@opsydyn/effect-three 0.2.0 → 0.4.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/CHANGELOG.md +21 -1
- package/README.md +14 -1
- package/dist/{index-01kwfekm.js → index-h3vf50fn.js} +2418 -85
- package/dist/index-h3vf50fn.js.map +53 -0
- package/dist/index-ttwy0znv.js +145 -0
- package/dist/index-ttwy0znv.js.map +12 -0
- package/dist/index.js +543 -149
- package/dist/index.js.map +8 -6
- package/dist/kernel/behaviours.d.ts.map +1 -1
- package/dist/kernel/boxSelect.d.ts +22 -0
- package/dist/kernel/boxSelect.d.ts.map +1 -0
- package/dist/kernel/build.d.ts +197 -6
- package/dist/kernel/build.d.ts.map +1 -1
- package/dist/kernel/cameraMotion.d.ts +3 -1
- package/dist/kernel/cameraMotion.d.ts.map +1 -1
- package/dist/kernel/cameraShake.d.ts +33 -0
- package/dist/kernel/cameraShake.d.ts.map +1 -0
- package/dist/kernel/describeScene.d.ts +50 -0
- package/dist/kernel/describeScene.d.ts.map +1 -0
- package/dist/kernel/description.d.ts +240 -2
- package/dist/kernel/description.d.ts.map +1 -1
- package/dist/kernel/diff.d.ts +18 -1
- package/dist/kernel/diff.d.ts.map +1 -1
- package/dist/kernel/equality.d.ts +13 -1
- package/dist/kernel/equality.d.ts.map +1 -1
- package/dist/kernel/gizmo.d.ts +69 -0
- package/dist/kernel/gizmo.d.ts.map +1 -0
- package/dist/kernel/history.d.ts +61 -0
- package/dist/kernel/history.d.ts.map +1 -0
- package/dist/kernel/index.d.ts +13 -2
- package/dist/kernel/index.d.ts.map +1 -1
- package/dist/kernel/index.js +116 -3
- package/dist/kernel/index.js.map +1 -1
- package/dist/kernel/inspect.d.ts +85 -0
- package/dist/kernel/inspect.d.ts.map +1 -0
- package/dist/kernel/lineGeometry.d.ts +25 -0
- package/dist/kernel/lineGeometry.d.ts.map +1 -0
- package/dist/kernel/lockstep.d.ts +114 -0
- package/dist/kernel/lockstep.d.ts.map +1 -0
- package/dist/kernel/loop.d.ts.map +1 -1
- package/dist/kernel/materials.d.ts.map +1 -1
- package/dist/kernel/pivot.d.ts +54 -0
- package/dist/kernel/pivot.d.ts.map +1 -0
- package/dist/kernel/pointBuffers.d.ts +17 -0
- package/dist/kernel/pointBuffers.d.ts.map +1 -0
- package/dist/kernel/pointer.d.ts +12 -0
- package/dist/kernel/pointer.d.ts.map +1 -1
- package/dist/kernel/renderKit.d.ts +21 -1
- package/dist/kernel/renderKit.d.ts.map +1 -1
- package/dist/kernel/renderer.d.ts +6 -0
- package/dist/kernel/renderer.d.ts.map +1 -1
- package/dist/kernel/resources.d.ts.map +1 -1
- package/dist/kernel/sceneChanges.d.ts +87 -0
- package/dist/kernel/sceneChanges.d.ts.map +1 -0
- package/dist/kernel/sceneGraph.d.ts +6 -1
- package/dist/kernel/sceneGraph.d.ts.map +1 -1
- package/dist/kernel/seeded.d.ts +9 -0
- package/dist/kernel/seeded.d.ts.map +1 -0
- package/dist/kernel/shots.d.ts +26 -0
- package/dist/kernel/shots.d.ts.map +1 -0
- package/dist/kernel/tape.d.ts +38 -0
- package/dist/kernel/tape.d.ts.map +1 -0
- package/dist/kernel/trail.d.ts +31 -0
- package/dist/kernel/trail.d.ts.map +1 -0
- package/dist/kernel/viewportGizmo.d.ts +25 -0
- package/dist/kernel/viewportGizmo.d.ts.map +1 -0
- package/dist/runtime/Inspector.d.ts +20 -0
- package/dist/runtime/Inspector.d.ts.map +1 -0
- package/dist/runtime/SceneFile.d.ts +448 -0
- package/dist/runtime/SceneFile.d.ts.map +1 -0
- package/dist/runtime/SceneSchema.d.ts +614 -5
- package/dist/runtime/SceneSchema.d.ts.map +1 -1
- package/dist/runtime/errors.d.ts +9 -0
- package/dist/runtime/errors.d.ts.map +1 -1
- package/dist/runtime/index.d.ts +3 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/webgl/index.d.ts.map +1 -1
- package/dist/webgl/index.js +228 -3
- package/dist/webgl/index.js.map +6 -4
- package/dist/webgl/line.d.ts +4 -0
- package/dist/webgl/line.d.ts.map +1 -0
- package/dist/webgl/points.d.ts +4 -0
- package/dist/webgl/points.d.ts.map +1 -0
- package/dist/webgpu/index.d.ts.map +1 -1
- package/dist/webgpu/index.js +206 -26
- package/dist/webgpu/index.js.map +6 -4
- package/dist/webgpu/line.d.ts +4 -0
- package/dist/webgpu/line.d.ts.map +1 -0
- package/dist/webgpu/points.d.ts +4 -0
- package/dist/webgpu/points.d.ts.map +1 -0
- package/package.json +2 -1
- package/src/kernel/behaviours.ts +5 -0
- package/src/kernel/bodySources.ts +1 -1
- package/src/kernel/boxSelect.ts +73 -0
- package/src/kernel/build.ts +430 -0
- package/src/kernel/cameraMotion.ts +3 -0
- package/src/kernel/cameraShake.ts +87 -0
- package/src/kernel/describeScene.ts +224 -0
- package/src/kernel/description.ts +266 -1
- package/src/kernel/diff.ts +46 -1
- package/src/kernel/equality.ts +148 -0
- package/src/kernel/gizmo.ts +478 -0
- package/src/kernel/history.ts +118 -0
- package/src/kernel/index.ts +102 -0
- package/src/kernel/inspect.ts +214 -0
- package/src/kernel/lineGeometry.ts +75 -0
- package/src/kernel/lockstep.ts +237 -0
- package/src/kernel/loop.ts +5 -0
- package/src/kernel/materials.ts +3 -0
- package/src/kernel/pivot.ts +418 -0
- package/src/kernel/pointBuffers.ts +115 -0
- package/src/kernel/pointer.ts +63 -12
- package/src/kernel/renderKit.ts +23 -1
- package/src/kernel/renderer.ts +6 -0
- package/src/kernel/resources.ts +69 -0
- package/src/kernel/sceneChanges.ts +265 -0
- package/src/kernel/sceneGraph.ts +461 -9
- package/src/kernel/seeded.ts +18 -0
- package/src/kernel/shots.ts +167 -0
- package/src/kernel/tape.ts +124 -0
- package/src/kernel/trail.ts +170 -0
- package/src/kernel/viewportGizmo.ts +194 -0
- package/src/runtime/Inspector.ts +43 -0
- package/src/runtime/SceneFile.ts +46 -0
- package/src/runtime/SceneSchema.ts +305 -4
- package/src/runtime/errors.ts +6 -0
- package/src/runtime/index.ts +3 -1
- package/src/webgl/index.ts +4 -0
- package/src/webgl/line.ts +66 -0
- package/src/webgl/points.ts +205 -0
- package/src/webgpu/index.ts +4 -0
- package/src/webgpu/line.ts +67 -0
- package/src/webgpu/points.ts +193 -0
- package/dist/index-01kwfekm.js.map +0 -40
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a scene contains, for an assistant (roadmap, "Beyond parity" 3): its camera, its
|
|
3
|
+
* things with their kind, size, colour and place, its lights, the box they fill and its
|
|
4
|
+
* staging, as data (`describeScene`) and as a few lines of text (`formatSummary`) an
|
|
5
|
+
* agent reads before it acts. An agent that reads the Model can describe a scene
|
|
6
|
+
* without three.js, so sizes come from the geometry's parameters and the node's scale:
|
|
7
|
+
* close enough to place things beside one another and to frame a shot.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import type {
|
|
12
|
+
GeometryRef,
|
|
13
|
+
Rgb,
|
|
14
|
+
SceneDescription,
|
|
15
|
+
SceneNode,
|
|
16
|
+
ShapeGeometry,
|
|
17
|
+
Vec3,
|
|
18
|
+
} from './description.ts';
|
|
19
|
+
import type { Box } from './orbit.ts';
|
|
20
|
+
|
|
21
|
+
export type ThingSummary = {
|
|
22
|
+
readonly key: string;
|
|
23
|
+
/** The node's kind, with its geometry's for a mesh: `Mesh (Box)`. */
|
|
24
|
+
readonly kind: string;
|
|
25
|
+
/** In its parent's space; for things at the top, the world's. */
|
|
26
|
+
readonly position: Vec3;
|
|
27
|
+
/** Width, height and depth, from the geometry and scale; `null` when not known. */
|
|
28
|
+
readonly size: Vec3 | null;
|
|
29
|
+
readonly colour: Rgb | null;
|
|
30
|
+
/** The group it is in, `null` at the top. */
|
|
31
|
+
readonly parent: string | null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type LightSummary = {
|
|
35
|
+
readonly key: string;
|
|
36
|
+
readonly kind: string;
|
|
37
|
+
readonly colour: Rgb;
|
|
38
|
+
readonly intensity: number;
|
|
39
|
+
readonly position: Vec3 | null;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type SceneSummary = {
|
|
43
|
+
readonly camera: {
|
|
44
|
+
readonly kind: 'Perspective' | 'Orthographic';
|
|
45
|
+
readonly position: Vec3;
|
|
46
|
+
readonly lookAt: Vec3;
|
|
47
|
+
};
|
|
48
|
+
readonly things: ReadonlyArray<ThingSummary>;
|
|
49
|
+
readonly lights: ReadonlyArray<LightSummary>;
|
|
50
|
+
/** The box the top-level things of known size fill, `null` with none. */
|
|
51
|
+
readonly bounds: Box | null;
|
|
52
|
+
readonly staging: ReadonlyArray<string>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** Width, height and depth of a shape, unscaled; `undefined` for text and shapes. */
|
|
56
|
+
const extentOf = (geometry: ShapeGeometry): Vec3 | undefined => {
|
|
57
|
+
switch (geometry._tag) {
|
|
58
|
+
case 'Box':
|
|
59
|
+
case 'RoundedBox':
|
|
60
|
+
return [geometry.width, geometry.height, geometry.depth];
|
|
61
|
+
case 'Sphere':
|
|
62
|
+
return [geometry.radius * 2, geometry.radius * 2, geometry.radius * 2];
|
|
63
|
+
case 'Cone':
|
|
64
|
+
return [geometry.radius * 2, geometry.height, geometry.radius * 2];
|
|
65
|
+
case 'Cylinder': {
|
|
66
|
+
const across = Math.max(geometry.radiusTop, geometry.radiusBottom) * 2;
|
|
67
|
+
return [across, geometry.height, across];
|
|
68
|
+
}
|
|
69
|
+
case 'Capsule':
|
|
70
|
+
return [geometry.radius * 2, geometry.height + geometry.radius * 2, geometry.radius * 2];
|
|
71
|
+
case 'Torus': {
|
|
72
|
+
const across = (geometry.radius + geometry.tube) * 2;
|
|
73
|
+
return [across, across, geometry.tube * 2];
|
|
74
|
+
}
|
|
75
|
+
case 'Plane':
|
|
76
|
+
return [geometry.width, geometry.height, 0];
|
|
77
|
+
case 'Circle':
|
|
78
|
+
return [geometry.radius * 2, geometry.radius * 2, 0];
|
|
79
|
+
case 'Ring':
|
|
80
|
+
return [geometry.outerRadius * 2, geometry.outerRadius * 2, 0];
|
|
81
|
+
default:
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const scaled = (extent: Vec3, scale: Vec3): Vec3 => [
|
|
87
|
+
extent[0] * Math.abs(scale[0]),
|
|
88
|
+
extent[1] * Math.abs(scale[1]),
|
|
89
|
+
extent[2] * Math.abs(scale[2]),
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
/** The shape a geometry reference draws: its own, a shared one's, or none (lent). */
|
|
93
|
+
const shapeOf = (
|
|
94
|
+
geometry: GeometryRef,
|
|
95
|
+
description: SceneDescription,
|
|
96
|
+
): ShapeGeometry | undefined => {
|
|
97
|
+
if (geometry._tag === 'LentGeometry') return undefined;
|
|
98
|
+
if (geometry._tag !== 'SharedGeometry') return geometry;
|
|
99
|
+
return description.resources.geometries.find((entry) => entry.id === geometry.id)?.geometry;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const sizeOf = (node: SceneNode, description: SceneDescription): Vec3 | null => {
|
|
103
|
+
if (node._tag !== 'Mesh') return null;
|
|
104
|
+
const shape = shapeOf(node.geometry, description);
|
|
105
|
+
const extent = shape === undefined ? undefined : extentOf(shape);
|
|
106
|
+
return extent === undefined ? null : scaled(extent, node.scale);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const colourOf = (node: SceneNode): Rgb | null =>
|
|
110
|
+
node._tag === 'Mesh' && 'color' in node.material ? node.material.color : null;
|
|
111
|
+
|
|
112
|
+
const kindOf = (node: SceneNode): string =>
|
|
113
|
+
node._tag === 'Mesh' ? `Mesh (${node.geometry._tag})` : node._tag;
|
|
114
|
+
|
|
115
|
+
const isLight = (node: SceneNode): boolean => node._tag.endsWith('Light');
|
|
116
|
+
|
|
117
|
+
const lightOf = (node: SceneNode): ReadonlyArray<LightSummary> => {
|
|
118
|
+
if (!isLight(node) || !('color' in node) || !('intensity' in node)) return [];
|
|
119
|
+
return [
|
|
120
|
+
{
|
|
121
|
+
key: node.key,
|
|
122
|
+
kind: node._tag,
|
|
123
|
+
colour: node.color,
|
|
124
|
+
intensity: node.intensity,
|
|
125
|
+
position: 'position' in node ? node.position : null,
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const thingsIn = (
|
|
131
|
+
nodes: ReadonlyArray<SceneNode>,
|
|
132
|
+
parent: string | null,
|
|
133
|
+
description: SceneDescription,
|
|
134
|
+
): ReadonlyArray<ThingSummary> =>
|
|
135
|
+
nodes.flatMap((node) => [
|
|
136
|
+
...(isLight(node)
|
|
137
|
+
? []
|
|
138
|
+
: [
|
|
139
|
+
{
|
|
140
|
+
key: node.key,
|
|
141
|
+
kind: kindOf(node),
|
|
142
|
+
position: 'position' in node ? node.position : ([0, 0, 0] as const),
|
|
143
|
+
size: sizeOf(node, description),
|
|
144
|
+
colour: colourOf(node),
|
|
145
|
+
parent,
|
|
146
|
+
},
|
|
147
|
+
]),
|
|
148
|
+
...(node._tag === 'Group' ? thingsIn(node.children, node.key, description) : []),
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
const lightsIn = (nodes: ReadonlyArray<SceneNode>): ReadonlyArray<LightSummary> =>
|
|
152
|
+
nodes.flatMap((node) => [
|
|
153
|
+
...lightOf(node),
|
|
154
|
+
...(node._tag === 'Group' ? lightsIn(node.children) : []),
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
/** The box top-level things of known size fill, each taken as a box round its place. */
|
|
158
|
+
export const thingsBounds = (things: ReadonlyArray<ThingSummary>): Box | null => {
|
|
159
|
+
const sized = things.filter((thing) => thing.parent === null && thing.size !== null);
|
|
160
|
+
if (sized.length === 0) return null;
|
|
161
|
+
const edge = (axis: 0 | 1 | 2, sign: 1 | -1): ReadonlyArray<number> =>
|
|
162
|
+
sized.map((thing) => thing.position[axis] + sign * ((thing.size?.[axis] ?? 0) / 2));
|
|
163
|
+
const low = (axis: 0 | 1 | 2) => Math.min(...edge(axis, -1));
|
|
164
|
+
const high = (axis: 0 | 1 | 2) => Math.max(...edge(axis, 1));
|
|
165
|
+
return { min: [low(0), low(1), low(2)], max: [high(0), high(1), high(2)] };
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const stagingOf = (description: SceneDescription): ReadonlyArray<string> => [
|
|
169
|
+
...(description.environment === null ? [] : ['environment lighting']),
|
|
170
|
+
...(description.sky === null ? [] : ['sky']),
|
|
171
|
+
...(description.render.shadows === 'off' ? [] : [`${description.render.shadows} shadows`]),
|
|
172
|
+
...(description.contactShadows === null ? [] : ['contact shadows']),
|
|
173
|
+
...description.render.effects.map((effect) => effect._tag.toLowerCase()),
|
|
174
|
+
...(description.physics === null ? [] : ['physics']),
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
/** What `description` contains, for an assistant to read. */
|
|
178
|
+
export const describeScene = (description: SceneDescription): SceneSummary => {
|
|
179
|
+
const things = thingsIn(description.children, null, description);
|
|
180
|
+
return {
|
|
181
|
+
camera: {
|
|
182
|
+
kind: description.camera._tag,
|
|
183
|
+
position: description.camera.position,
|
|
184
|
+
lookAt: description.camera.lookAt,
|
|
185
|
+
},
|
|
186
|
+
things,
|
|
187
|
+
lights: lightsIn(description.children),
|
|
188
|
+
bounds: thingsBounds(things),
|
|
189
|
+
staging: stagingOf(description),
|
|
190
|
+
};
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const number = (value: number): string => String(Math.round(value * 100) / 100);
|
|
194
|
+
const point = (at: Vec3): string => `(${at.map(number).join(', ')})`;
|
|
195
|
+
const hex = (colour: Rgb): string => `#${colour.toString(16).padStart(6, '0')}`;
|
|
196
|
+
|
|
197
|
+
const thingLine = (thing: ThingSummary): string =>
|
|
198
|
+
[
|
|
199
|
+
`- ${thing.key}: ${thing.kind}`,
|
|
200
|
+
thing.size === null ? '' : `, ${thing.size.map(number).join(' × ')}`,
|
|
201
|
+
thing.colour === null ? '' : `, ${hex(thing.colour)}`,
|
|
202
|
+
`, at ${point(thing.position)}`,
|
|
203
|
+
thing.parent === null ? '' : ` in ${thing.parent}`,
|
|
204
|
+
].join('');
|
|
205
|
+
|
|
206
|
+
const lightLine = (light: LightSummary): string =>
|
|
207
|
+
`- ${light.key}: ${light.kind}, ${hex(light.colour)} × ${number(light.intensity)}${
|
|
208
|
+
light.position === null ? '' : ` at ${point(light.position)}`
|
|
209
|
+
}`;
|
|
210
|
+
|
|
211
|
+
/** The summary as a few lines of text, for a prompt or a tool's answer. */
|
|
212
|
+
export const formatSummary = (summary: SceneSummary): string =>
|
|
213
|
+
[
|
|
214
|
+
`Camera: ${summary.camera.kind.toLowerCase()} at ${point(summary.camera.position)}, looking at ${point(summary.camera.lookAt)}`,
|
|
215
|
+
`Things (${summary.things.length})${
|
|
216
|
+
summary.bounds === null
|
|
217
|
+
? ''
|
|
218
|
+
: `, within ${point(summary.bounds.min)} to ${point(summary.bounds.max)}`
|
|
219
|
+
}:`,
|
|
220
|
+
...summary.things.map(thingLine),
|
|
221
|
+
`Lights (${summary.lights.length}):`,
|
|
222
|
+
...summary.lights.map(lightLine),
|
|
223
|
+
`Staging: ${summary.staging.length === 0 ? 'none' : summary.staging.join(', ')}`,
|
|
224
|
+
].join('\n');
|
|
@@ -30,6 +30,17 @@ export type Rotation =
|
|
|
30
30
|
readonly axis: Axis;
|
|
31
31
|
readonly radiansPerSecond: number;
|
|
32
32
|
readonly phase: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Faces the scene's camera every frame, as Drei's `Billboard`: the node turns so its
|
|
36
|
+
* world rotation is the camera's. A locked axis stays at 0 in the node's own Euler
|
|
37
|
+
* angles (Drei keeps its previous value, which starts at 0).
|
|
38
|
+
*/
|
|
39
|
+
| {
|
|
40
|
+
readonly _tag: 'Billboard';
|
|
41
|
+
readonly lockX: boolean;
|
|
42
|
+
readonly lockY: boolean;
|
|
43
|
+
readonly lockZ: boolean;
|
|
33
44
|
};
|
|
34
45
|
|
|
35
46
|
/** Box geometry parameters. */
|
|
@@ -90,6 +101,36 @@ export type CapsuleGeometry = {
|
|
|
90
101
|
readonly radialSegments: number;
|
|
91
102
|
};
|
|
92
103
|
|
|
104
|
+
/**
|
|
105
|
+
* A box with rounded edges and corners, centred, as Drei's `RoundedBox`: a rounded
|
|
106
|
+
* rectangle extruded and bevelled, with normals creased at `creaseAngle`.
|
|
107
|
+
*/
|
|
108
|
+
export type RoundedBoxGeometry = {
|
|
109
|
+
readonly _tag: 'RoundedBox';
|
|
110
|
+
readonly width: number;
|
|
111
|
+
readonly height: number;
|
|
112
|
+
readonly depth: number;
|
|
113
|
+
readonly radius: number;
|
|
114
|
+
/** Segments in each rounded corner of the outline. */
|
|
115
|
+
readonly smoothness: number;
|
|
116
|
+
/** Segments across the bevel, which Drei doubles. */
|
|
117
|
+
readonly bevelSegments: number;
|
|
118
|
+
readonly steps: number;
|
|
119
|
+
/** Faces meeting at more than this angle (radians) keep a hard edge. */
|
|
120
|
+
readonly creaseAngle: number;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* A photo backdrop, as Drei's `Backdrop`: a floor that sweeps up into a wall behind it,
|
|
125
|
+
* one unit each way (scale it to size). The floor lies along +Z towards the viewer, the
|
|
126
|
+
* wall rises at its back, and `floor` extends the flat front.
|
|
127
|
+
*/
|
|
128
|
+
export type BackdropGeometry = {
|
|
129
|
+
readonly _tag: 'Backdrop';
|
|
130
|
+
readonly floor: number;
|
|
131
|
+
readonly segments: number;
|
|
132
|
+
};
|
|
133
|
+
|
|
93
134
|
/** A flat ring (annulus) in the XY plane, facing +Z. */
|
|
94
135
|
export type RingGeometry = {
|
|
95
136
|
readonly _tag: 'Ring';
|
|
@@ -150,7 +191,9 @@ export type ShapeGeometry =
|
|
|
150
191
|
| ConeGeometry
|
|
151
192
|
| CapsuleGeometry
|
|
152
193
|
| RingGeometry
|
|
153
|
-
| CircleGeometry
|
|
194
|
+
| CircleGeometry
|
|
195
|
+
| RoundedBoxGeometry
|
|
196
|
+
| BackdropGeometry;
|
|
154
197
|
|
|
155
198
|
/** Which faces a material draws. */
|
|
156
199
|
export type MaterialSide = 'front' | 'back' | 'double';
|
|
@@ -165,6 +208,11 @@ export type BasicMaterial = {
|
|
|
165
208
|
readonly transparent: boolean;
|
|
166
209
|
readonly side: MaterialSide;
|
|
167
210
|
readonly wireframe: boolean;
|
|
211
|
+
/**
|
|
212
|
+
* Whether it is hidden behind what is nearer, as usual; `false` draws it over
|
|
213
|
+
* everything (and writes no depth), as three's gizmos and helpers are drawn.
|
|
214
|
+
*/
|
|
215
|
+
readonly depthTest: boolean;
|
|
168
216
|
};
|
|
169
217
|
|
|
170
218
|
/** A matte (Lambertian) material: cheap per-vertex lighting, no highlights. */
|
|
@@ -342,6 +390,24 @@ export type GroupNode = {
|
|
|
342
390
|
* not tracked (or no session presents). `null` for the group's own transform.
|
|
343
391
|
*/
|
|
344
392
|
readonly follow: XrSpace | null;
|
|
393
|
+
/**
|
|
394
|
+
* Levels of detail: the group shows one child at a time, chosen by the camera's
|
|
395
|
+
* distance (Drei's `Detailed`, three's `LOD`), or `null` to show them all.
|
|
396
|
+
*/
|
|
397
|
+
readonly detail: Detail | null;
|
|
398
|
+
/** A ribbon behind the group as it moves (Drei's `Trail`), or `null` for none. */
|
|
399
|
+
readonly trail: Trail | null;
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Levels of detail for a group's children, in order: child `i` shows from
|
|
404
|
+
* `distances[i]` (scene units from the camera, divided by its zoom) until the next level's
|
|
405
|
+
* distance. `hysteresis` (a fraction) keeps a shown level until the camera is that much
|
|
406
|
+
* nearer than its distance, so the switch does not flicker at the boundary.
|
|
407
|
+
*/
|
|
408
|
+
export type Detail = {
|
|
409
|
+
readonly distances: ReadonlyArray<number>;
|
|
410
|
+
readonly hysteresis: number;
|
|
345
411
|
};
|
|
346
412
|
|
|
347
413
|
/** The joints of a tracked hand, as WebXR names them (`XRHandJoint`). */
|
|
@@ -411,6 +477,47 @@ export type MeshNode = {
|
|
|
411
477
|
readonly receiveShadow: boolean;
|
|
412
478
|
/** A rigid body simulating the mesh, when the scene has `physics`; `null` for none. */
|
|
413
479
|
readonly body: Body | null;
|
|
480
|
+
/** Lines along the mesh's hard edges (Drei's `Edges`), or `null` for none. */
|
|
481
|
+
readonly edges: Edges | null;
|
|
482
|
+
/** A ribbon behind the mesh as it moves (Drei's `Trail`), or `null` for none. */
|
|
483
|
+
readonly trail: Trail | null;
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* A trail behind a moving node, as Drei's `Trail`: `length × 10` points of where it has
|
|
488
|
+
* been, a new one every `interval` steps (60 a second of scene time) when it has moved at
|
|
489
|
+
* least `stride`, `decay` of them at a time; drawn as a ribbon facing the camera, a tenth
|
|
490
|
+
* of `width` wide in scene units, narrowing (`taper`) and fading (`fade`) to the tail.
|
|
491
|
+
* `local` follows the node's own position rather than its world position, as Drei's
|
|
492
|
+
* does; the ribbon is drawn in the scene's space either way.
|
|
493
|
+
*/
|
|
494
|
+
export type Trail = {
|
|
495
|
+
readonly width: number;
|
|
496
|
+
readonly length: number;
|
|
497
|
+
readonly decay: number;
|
|
498
|
+
readonly local: boolean;
|
|
499
|
+
readonly stride: number;
|
|
500
|
+
readonly interval: number;
|
|
501
|
+
readonly color: Rgb;
|
|
502
|
+
readonly taper: 'none' | 'linear' | 'quadratic';
|
|
503
|
+
readonly fade: boolean;
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Lines along a mesh's edges where its faces meet at more than `threshold` degrees
|
|
508
|
+
* (three's `EdgesGeometry`, as Drei's `Edges`), drawn as a line (`width` pixels, or scene
|
|
509
|
+
* units with `worldUnits`) by the render kit. Never picked: hits go to the mesh.
|
|
510
|
+
*/
|
|
511
|
+
export type Edges = {
|
|
512
|
+
readonly threshold: number;
|
|
513
|
+
readonly color: Rgb;
|
|
514
|
+
readonly width: number;
|
|
515
|
+
readonly worldUnits: boolean;
|
|
516
|
+
readonly dashed: boolean;
|
|
517
|
+
readonly dashSize: number;
|
|
518
|
+
readonly gapSize: number;
|
|
519
|
+
readonly opacity: number;
|
|
520
|
+
readonly transparent: boolean;
|
|
414
521
|
};
|
|
415
522
|
|
|
416
523
|
/**
|
|
@@ -656,6 +763,13 @@ export type BorrowedNode = {
|
|
|
656
763
|
readonly animation: Animation | null;
|
|
657
764
|
/** A rigid body whose colliders are the model's meshes, once it is lent; or `null`. */
|
|
658
765
|
readonly body: Body | null;
|
|
766
|
+
/**
|
|
767
|
+
* Every mesh in what it shows casts (receives) shadows, as Drei's `Clone` shorthands;
|
|
768
|
+
* `false` leaves each mesh's own setting. Set on the lent object itself where it is
|
|
769
|
+
* shown as lent rather than cloned.
|
|
770
|
+
*/
|
|
771
|
+
readonly castShadow: boolean;
|
|
772
|
+
readonly receiveShadow: boolean;
|
|
659
773
|
};
|
|
660
774
|
|
|
661
775
|
/** Where a label's origin is along its width: an edge, the middle, or a fraction of it. */
|
|
@@ -862,8 +976,106 @@ export type ShaderGridNode = {
|
|
|
862
976
|
readonly side: MaterialSide;
|
|
863
977
|
};
|
|
864
978
|
|
|
979
|
+
/**
|
|
980
|
+
* A line through points, as Drei's `Line` (three's fat lines, `Line2`): `width` pixels
|
|
981
|
+
* wide on screen, or scene units with `worldUnits`; one colour, or one per point; solid
|
|
982
|
+
* or dashed. With `segments` each pair of points is its own segment (Drei's `Segments`).
|
|
983
|
+
* A render kit draws it; without one it is an empty group.
|
|
984
|
+
*/
|
|
985
|
+
export type LineNode = {
|
|
986
|
+
readonly _tag: 'Line';
|
|
987
|
+
readonly key: string;
|
|
988
|
+
readonly position: Vec3;
|
|
989
|
+
readonly rotation: Rotation;
|
|
990
|
+
readonly scale: Vec3;
|
|
991
|
+
readonly transition: Transition;
|
|
992
|
+
readonly behaviour: Behave | null;
|
|
993
|
+
/** Makes the line pickable, within its width. */
|
|
994
|
+
readonly interactive: boolean;
|
|
995
|
+
readonly points: ReadonlyArray<Vec3>;
|
|
996
|
+
readonly segments: boolean;
|
|
997
|
+
readonly color: Rgb;
|
|
998
|
+
/** A colour for each point, blended along the line, or `null` for `color` throughout. */
|
|
999
|
+
readonly vertexColors: ReadonlyArray<Rgb> | null;
|
|
1000
|
+
readonly width: number;
|
|
1001
|
+
readonly worldUnits: boolean;
|
|
1002
|
+
readonly dashed: boolean;
|
|
1003
|
+
readonly dashSize: number;
|
|
1004
|
+
readonly gapSize: number;
|
|
1005
|
+
readonly dashScale: number;
|
|
1006
|
+
readonly dashOffset: number;
|
|
1007
|
+
readonly opacity: number;
|
|
1008
|
+
readonly transparent: boolean;
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Points, drawn by the render kit as round or square dots, Drei's sparkles or its
|
|
1013
|
+
* stars. The look says which, and carries everything needed to draw them: given
|
|
1014
|
+
* points, or a seed that scatters them the same way every time.
|
|
1015
|
+
*/
|
|
1016
|
+
export type PointsNode = {
|
|
1017
|
+
readonly _tag: 'Points';
|
|
1018
|
+
readonly key: string;
|
|
1019
|
+
readonly position: Vec3;
|
|
1020
|
+
readonly rotation: Rotation;
|
|
1021
|
+
readonly scale: Vec3;
|
|
1022
|
+
readonly transition: Transition;
|
|
1023
|
+
readonly behaviour: Behave | null;
|
|
1024
|
+
readonly look: PointLook;
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* What points look like:
|
|
1029
|
+
*
|
|
1030
|
+
* - `Dots`: Drei's `Points` with its `PointMaterial` (three's `PointsMaterial`, `round`
|
|
1031
|
+
* to circles): `size` pixels across, smaller with distance under `sizeAttenuation`;
|
|
1032
|
+
* one colour or one a point, one size or a factor a point.
|
|
1033
|
+
* - `Sparkles`: Drei's `Sparkles`: `count` glowing motes scattered through a box of
|
|
1034
|
+
* `scale`, drifting by `noise` at `speed`.
|
|
1035
|
+
* - `Stars`: Drei's `Stars`: `count` stars in a shell from `radius` to `radius + depth`,
|
|
1036
|
+
* `factor` sizing them, twinkling at `speed`, `fade` softening their edges.
|
|
1037
|
+
*/
|
|
1038
|
+
export type PointLook =
|
|
1039
|
+
| {
|
|
1040
|
+
readonly _tag: 'Dots';
|
|
1041
|
+
readonly positions: ReadonlyArray<Vec3>;
|
|
1042
|
+
readonly colors: ReadonlyArray<Rgb> | null;
|
|
1043
|
+
readonly sizes: ReadonlyArray<number> | null;
|
|
1044
|
+
readonly color: Rgb;
|
|
1045
|
+
readonly size: number;
|
|
1046
|
+
readonly sizeAttenuation: boolean;
|
|
1047
|
+
readonly round: boolean;
|
|
1048
|
+
readonly opacity: number;
|
|
1049
|
+
readonly transparent: boolean;
|
|
1050
|
+
}
|
|
1051
|
+
| {
|
|
1052
|
+
readonly _tag: 'Sparkles';
|
|
1053
|
+
readonly count: number;
|
|
1054
|
+
readonly speed: number;
|
|
1055
|
+
readonly opacity: number;
|
|
1056
|
+
readonly scale: Vec3;
|
|
1057
|
+
/** Each mote's size, or `null` for Drei's: random, up to 1. */
|
|
1058
|
+
readonly size: number | null;
|
|
1059
|
+
readonly color: Rgb;
|
|
1060
|
+
readonly noise: Vec3;
|
|
1061
|
+
readonly seed: number;
|
|
1062
|
+
}
|
|
1063
|
+
| {
|
|
1064
|
+
readonly _tag: 'Stars';
|
|
1065
|
+
readonly radius: number;
|
|
1066
|
+
readonly depth: number;
|
|
1067
|
+
readonly count: number;
|
|
1068
|
+
readonly factor: number;
|
|
1069
|
+
readonly saturation: number;
|
|
1070
|
+
readonly fade: boolean;
|
|
1071
|
+
readonly speed: number;
|
|
1072
|
+
readonly seed: number;
|
|
1073
|
+
};
|
|
1074
|
+
|
|
865
1075
|
/** Any node in a scene. Keys are unique across the whole scene. */
|
|
866
1076
|
export type SceneNode =
|
|
1077
|
+
| PointsNode
|
|
1078
|
+
| LineNode
|
|
867
1079
|
| GridNode
|
|
868
1080
|
| ShaderGridNode
|
|
869
1081
|
| GroupNode
|
|
@@ -902,6 +1114,8 @@ export type PerspectiveCamera = {
|
|
|
902
1114
|
* (`autoRotated(camera, detail)`). A new position or `lookAt` turns on from there.
|
|
903
1115
|
*/
|
|
904
1116
|
readonly autoRotate: number;
|
|
1117
|
+
/** Shakes the rendered camera (Drei's `CameraShake`), or `null` for none. */
|
|
1118
|
+
readonly shake: CameraShake | null;
|
|
905
1119
|
};
|
|
906
1120
|
|
|
907
1121
|
/**
|
|
@@ -919,6 +1133,28 @@ export type OrthographicCamera = {
|
|
|
919
1133
|
readonly damping: number;
|
|
920
1134
|
/** As on `PerspectiveCamera`. */
|
|
921
1135
|
readonly autoRotate: number;
|
|
1136
|
+
/** As on `PerspectiveCamera`. */
|
|
1137
|
+
readonly shake: CameraShake | null;
|
|
1138
|
+
};
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Camera shake, as Drei's `CameraShake`: yaw, pitch and roll of up to `max*` radians,
|
|
1142
|
+
* from noise at `*Frequency`, scaled by `intensity` squared (0 to 1). With `decay` the
|
|
1143
|
+
* intensity falls by `decayRate` a second from when the shake began, so a decaying shake
|
|
1144
|
+
* is a one-off: it begins when it first appears or changes, and `trigger` (a count the
|
|
1145
|
+
* Model bumps) begins the same shake again. Reduced motion holds the camera still.
|
|
1146
|
+
*/
|
|
1147
|
+
export type CameraShake = {
|
|
1148
|
+
readonly intensity: number;
|
|
1149
|
+
readonly decay: boolean;
|
|
1150
|
+
readonly decayRate: number;
|
|
1151
|
+
readonly maxYaw: number;
|
|
1152
|
+
readonly maxPitch: number;
|
|
1153
|
+
readonly maxRoll: number;
|
|
1154
|
+
readonly yawFrequency: number;
|
|
1155
|
+
readonly pitchFrequency: number;
|
|
1156
|
+
readonly rollFrequency: number;
|
|
1157
|
+
readonly trigger: number;
|
|
922
1158
|
};
|
|
923
1159
|
|
|
924
1160
|
/** The single active camera. */
|
|
@@ -990,6 +1226,33 @@ export type ViewRect = {
|
|
|
990
1226
|
*/
|
|
991
1227
|
export type View = { readonly key: string; readonly camera: Camera; readonly rect: ViewRect };
|
|
992
1228
|
|
|
1229
|
+
/** Where a HUD's origin sits on the canvas: a corner, an edge's middle, or the centre. */
|
|
1230
|
+
export type HudAlignment =
|
|
1231
|
+
| 'top-left'
|
|
1232
|
+
| 'top-center'
|
|
1233
|
+
| 'top-right'
|
|
1234
|
+
| 'center-left'
|
|
1235
|
+
| 'center-center'
|
|
1236
|
+
| 'center-right'
|
|
1237
|
+
| 'bottom-left'
|
|
1238
|
+
| 'bottom-center'
|
|
1239
|
+
| 'bottom-right';
|
|
1240
|
+
|
|
1241
|
+
/**
|
|
1242
|
+
* Nodes drawn over the scene, Drei's `Hud`, as data: a scene of their own (keys apart
|
|
1243
|
+
* from the scene's), drawn after it with only its depth cleared, through an orthographic
|
|
1244
|
+
* camera measured in CSS pixels, y up, whose origin sits `margin` pixels in from the
|
|
1245
|
+
* `alignment`'s edges (Drei's `GizmoHelper` places its gizmo so). Pointer events reach
|
|
1246
|
+
* its interactive meshes before anything behind them; their points and rays are in the
|
|
1247
|
+
* HUD's own space. Scene lighting and fog do not reach it.
|
|
1248
|
+
*/
|
|
1249
|
+
export type Hud = {
|
|
1250
|
+
readonly alignment: HudAlignment;
|
|
1251
|
+
/** CSS pixels in from the aligned edges, `[x, y]`: unused for a centred direction. */
|
|
1252
|
+
readonly margin: readonly [number, number];
|
|
1253
|
+
readonly children: ReadonlyArray<SceneNode>;
|
|
1254
|
+
};
|
|
1255
|
+
|
|
993
1256
|
/**
|
|
994
1257
|
* A small scene rendered into a texture that materials use as `map: id`: Drei's
|
|
995
1258
|
* `RenderTexture`, as data. Its nodes have their own key namespace and may use the
|
|
@@ -1174,6 +1437,8 @@ export type SceneDescription = {
|
|
|
1174
1437
|
readonly views: ReadonlyArray<View>;
|
|
1175
1438
|
/** Scenes rendered into textures for materials to use. */
|
|
1176
1439
|
readonly renderTextures: ReadonlyArray<RenderTexture>;
|
|
1440
|
+
/** Nodes drawn over the scene, or `null` for none. */
|
|
1441
|
+
readonly hud: Hud | null;
|
|
1177
1442
|
/** Points to show HTML at, placed every frame the scene draws. */
|
|
1178
1443
|
readonly overlays: ReadonlyArray<Overlay>;
|
|
1179
1444
|
/** Image-based lighting and background, or `null` for none. */
|
package/src/kernel/diff.ts
CHANGED
|
@@ -31,6 +31,11 @@ import {
|
|
|
31
31
|
labelMaterialEquals,
|
|
32
32
|
labelTypeEquals,
|
|
33
33
|
xrSpaceEquals,
|
|
34
|
+
lineLookEquals,
|
|
35
|
+
edgesEquals,
|
|
36
|
+
trailEquals,
|
|
37
|
+
pointLookEquals,
|
|
38
|
+
detailEquals,
|
|
34
39
|
shaderGridLookEquals,
|
|
35
40
|
instancesEqual,
|
|
36
41
|
materialRefEquals,
|
|
@@ -81,8 +86,18 @@ export type Field =
|
|
|
81
86
|
| 'text'
|
|
82
87
|
/** A shader grid's look: anything but where it is. */
|
|
83
88
|
| 'grid'
|
|
89
|
+
/** A line's points or look: anything but where it is. */
|
|
90
|
+
| 'line'
|
|
91
|
+
/** A mesh's edge lines. */
|
|
92
|
+
| 'edges'
|
|
93
|
+
/** A group's or mesh's trail. */
|
|
94
|
+
| 'trail'
|
|
95
|
+
/** Points' look: what they are and where, within the node. */
|
|
96
|
+
| 'look'
|
|
84
97
|
/** The tracked space a group follows. */
|
|
85
|
-
| 'follow'
|
|
98
|
+
| 'follow'
|
|
99
|
+
/** A group's levels of detail. */
|
|
100
|
+
| 'detail';
|
|
86
101
|
|
|
87
102
|
/** A single change to apply to the hosted scene graph. */
|
|
88
103
|
export type Op =
|
|
@@ -91,6 +106,9 @@ export type Op =
|
|
|
91
106
|
/** A render texture was created, resized or its scene changed (its own operations). */
|
|
92
107
|
| { readonly _tag: 'UpdateRenderTexture'; readonly id: string; readonly ops: ReadonlyArray<Op> }
|
|
93
108
|
| { readonly _tag: 'RemoveRenderTexture'; readonly id: string }
|
|
109
|
+
/** The HUD was created or its scene changed (its own operations). */
|
|
110
|
+
| { readonly _tag: 'UpdateHud'; readonly ops: ReadonlyArray<Op> }
|
|
111
|
+
| { readonly _tag: 'RemoveHud' }
|
|
94
112
|
| { readonly _tag: 'SetRenderSettings'; readonly settings: RenderSettings }
|
|
95
113
|
| { readonly _tag: 'DefineResources'; readonly resources: ResourceDefinitions }
|
|
96
114
|
| { readonly _tag: 'Create'; readonly node: SceneNode; readonly parent: ParentKey }
|
|
@@ -131,6 +149,8 @@ export const changedFields = (
|
|
|
131
149
|
if (prev.interactive !== next.interactive) fields.push('interactive');
|
|
132
150
|
if (!bodyEquals(prev.body, next.body)) fields.push('body');
|
|
133
151
|
if (!xrSpaceEquals(prev.follow, next.follow)) fields.push('follow');
|
|
152
|
+
if (!detailEquals(prev.detail, next.detail)) fields.push('detail');
|
|
153
|
+
if (!trailEquals(prev.trail, next.trail)) fields.push('trail');
|
|
134
154
|
return fields;
|
|
135
155
|
}
|
|
136
156
|
case 'Mesh': {
|
|
@@ -146,6 +166,8 @@ export const changedFields = (
|
|
|
146
166
|
if (prev.castShadow !== next.castShadow) fields.push('castShadow');
|
|
147
167
|
if (prev.receiveShadow !== next.receiveShadow) fields.push('receiveShadow');
|
|
148
168
|
if (!bodyEquals(prev.body, next.body)) fields.push('body');
|
|
169
|
+
if (!edgesEquals(prev.edges, next.edges)) fields.push('edges');
|
|
170
|
+
if (!trailEquals(prev.trail, next.trail)) fields.push('trail');
|
|
149
171
|
return fields;
|
|
150
172
|
}
|
|
151
173
|
case 'Instances': {
|
|
@@ -175,6 +197,8 @@ export const changedFields = (
|
|
|
175
197
|
if (!animationEquals(prev.animation, next.animation)) fields.push('animation');
|
|
176
198
|
if (prev.interactive !== next.interactive) fields.push('interactive');
|
|
177
199
|
if (!bodyEquals(prev.body, next.body)) fields.push('body');
|
|
200
|
+
if (prev.castShadow !== next.castShadow) fields.push('castShadow');
|
|
201
|
+
if (prev.receiveShadow !== next.receiveShadow) fields.push('receiveShadow');
|
|
178
202
|
return fields;
|
|
179
203
|
}
|
|
180
204
|
case 'Label': {
|
|
@@ -233,6 +257,27 @@ export const changedFields = (
|
|
|
233
257
|
if (!Object.is(prev.shadowBias, next.shadowBias)) fields.push('shadowBias');
|
|
234
258
|
return fields;
|
|
235
259
|
}
|
|
260
|
+
case 'Points': {
|
|
261
|
+
if (prev._tag !== 'Points') return undefined;
|
|
262
|
+
if (!behaviourEquals(prev.behaviour, next.behaviour)) fields.push('behaviour');
|
|
263
|
+
if (!vec3Equals(prev.position, next.position)) fields.push('position');
|
|
264
|
+
if (!rotationEquals(prev.rotation, next.rotation)) fields.push('rotation');
|
|
265
|
+
if (!vec3Equals(prev.scale, next.scale)) fields.push('scale');
|
|
266
|
+
if (!transitionEquals(prev.transition, next.transition)) fields.push('transition');
|
|
267
|
+
if (!pointLookEquals(prev.look, next.look)) fields.push('look');
|
|
268
|
+
return fields;
|
|
269
|
+
}
|
|
270
|
+
case 'Line': {
|
|
271
|
+
if (prev._tag !== 'Line') return undefined;
|
|
272
|
+
if (!behaviourEquals(prev.behaviour, next.behaviour)) fields.push('behaviour');
|
|
273
|
+
if (!vec3Equals(prev.position, next.position)) fields.push('position');
|
|
274
|
+
if (!rotationEquals(prev.rotation, next.rotation)) fields.push('rotation');
|
|
275
|
+
if (!vec3Equals(prev.scale, next.scale)) fields.push('scale');
|
|
276
|
+
if (!transitionEquals(prev.transition, next.transition)) fields.push('transition');
|
|
277
|
+
if (prev.interactive !== next.interactive) fields.push('interactive');
|
|
278
|
+
if (!lineLookEquals(prev, next)) fields.push('line');
|
|
279
|
+
return fields;
|
|
280
|
+
}
|
|
236
281
|
case 'ShaderGrid': {
|
|
237
282
|
if (prev._tag !== 'ShaderGrid') return undefined;
|
|
238
283
|
if (!vec3Equals(prev.position, next.position)) fields.push('position');
|