@opsydyn/effect-three 0.3.0 → 0.6.4
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 +56 -0
- package/README.md +15 -1
- package/dist/{index-8cf8t6by.js → index-re3rrfwd.js} +2935 -104
- package/dist/index-re3rrfwd.js.map +56 -0
- package/dist/index-ttwy0znv.js +145 -0
- package/dist/index-ttwy0znv.js.map +12 -0
- package/dist/index.js +557 -147
- package/dist/index.js.map +8 -6
- package/dist/kernel/bodies.d.ts +20 -1
- package/dist/kernel/bodies.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 +165 -7
- package/dist/kernel/build.d.ts.map +1 -1
- package/dist/kernel/cameraShake.d.ts.map +1 -1
- package/dist/kernel/describeScene.d.ts +50 -0
- package/dist/kernel/describeScene.d.ts.map +1 -0
- package/dist/kernel/description.d.ts +223 -1
- package/dist/kernel/description.d.ts.map +1 -1
- package/dist/kernel/diff.d.ts +15 -0
- package/dist/kernel/diff.d.ts.map +1 -1
- package/dist/kernel/equality.d.ts +11 -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 +19 -6
- package/dist/kernel/index.d.ts.map +1 -1
- package/dist/kernel/index.js +124 -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/paintOrder.d.ts +53 -0
- package/dist/kernel/paintOrder.d.ts.map +1 -0
- package/dist/kernel/physics.d.ts +39 -0
- package/dist/kernel/physics.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 +31 -1
- package/dist/kernel/pointer.d.ts.map +1 -1
- package/dist/kernel/preview.d.ts +32 -0
- package/dist/kernel/preview.d.ts.map +1 -0
- 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/sceneChanges.d.ts +87 -0
- package/dist/kernel/sceneChanges.d.ts.map +1 -0
- package/dist/kernel/sceneGraph.d.ts +9 -1
- package/dist/kernel/sceneGraph.d.ts.map +1 -1
- package/dist/kernel/sceneIndex.d.ts +5 -0
- package/dist/kernel/sceneIndex.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/timeline.d.ts +60 -0
- package/dist/kernel/timeline.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 +462 -0
- package/dist/runtime/SceneFile.d.ts.map +1 -0
- package/dist/runtime/SceneSchema.d.ts +404 -0
- 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 +4 -2
- package/src/kernel/bodies.ts +299 -17
- package/src/kernel/boxSelect.ts +73 -0
- package/src/kernel/build.ts +374 -1
- package/src/kernel/cameraShake.ts +3 -13
- package/src/kernel/describeScene.ts +224 -0
- package/src/kernel/description.ts +243 -0
- package/src/kernel/diff.ts +39 -0
- package/src/kernel/equality.ts +123 -0
- package/src/kernel/gizmo.ts +478 -0
- package/src/kernel/history.ts +118 -0
- package/src/kernel/index.ts +110 -1
- 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 +7 -0
- package/src/kernel/materials.ts +3 -0
- package/src/kernel/paintOrder.ts +274 -0
- package/src/kernel/physics.ts +86 -1
- package/src/kernel/pivot.ts +418 -0
- package/src/kernel/pointBuffers.ts +115 -0
- package/src/kernel/pointer.ts +98 -17
- package/src/kernel/preview.ts +467 -0
- package/src/kernel/renderKit.ts +23 -1
- package/src/kernel/renderer.ts +6 -0
- package/src/kernel/sceneChanges.ts +265 -0
- package/src/kernel/sceneGraph.ts +354 -11
- package/src/kernel/sceneIndex.ts +8 -0
- package/src/kernel/seeded.ts +18 -0
- package/src/kernel/shots.ts +167 -0
- package/src/kernel/tape.ts +124 -0
- package/src/kernel/timeline.ts +93 -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 +319 -0
- 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-8cf8t6by.js.map +0 -41
|
@@ -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');
|
|
@@ -208,6 +208,11 @@ export type BasicMaterial = {
|
|
|
208
208
|
readonly transparent: boolean;
|
|
209
209
|
readonly side: MaterialSide;
|
|
210
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;
|
|
211
216
|
};
|
|
212
217
|
|
|
213
218
|
/** A matte (Lambertian) material: cheap per-vertex lighting, no highlights. */
|
|
@@ -390,6 +395,8 @@ export type GroupNode = {
|
|
|
390
395
|
* distance (Drei's `Detailed`, three's `LOD`), or `null` to show them all.
|
|
391
396
|
*/
|
|
392
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;
|
|
393
400
|
};
|
|
394
401
|
|
|
395
402
|
/**
|
|
@@ -470,6 +477,47 @@ export type MeshNode = {
|
|
|
470
477
|
readonly receiveShadow: boolean;
|
|
471
478
|
/** A rigid body simulating the mesh, when the scene has `physics`; `null` for none. */
|
|
472
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;
|
|
473
521
|
};
|
|
474
522
|
|
|
475
523
|
/**
|
|
@@ -577,12 +625,56 @@ export type Physics = {
|
|
|
577
625
|
readonly paused: boolean;
|
|
578
626
|
readonly timeStep: number;
|
|
579
627
|
readonly interpolate: boolean;
|
|
628
|
+
/**
|
|
629
|
+
* The step the Model lets the world reach, for a simulation that must run alike
|
|
630
|
+
* wherever it runs (lockstep, replays; ADR-014). The world advances toward it in fixed
|
|
631
|
+
* steps at scene time's pace (faster when far behind), stops there and reports
|
|
632
|
+
* `PhysicsReached`; whatever the description changes while it waits applies at that
|
|
633
|
+
* step, on every peer. Steps count from 0 when the world is made, and the world never
|
|
634
|
+
* goes back: a lower `until` holds it where it is. `null` (the default): the world runs
|
|
635
|
+
* on scene time alone, as react-three-rapier's does.
|
|
636
|
+
*/
|
|
637
|
+
readonly until: number | null;
|
|
638
|
+
/**
|
|
639
|
+
* Which run of the simulation this is: a new number throws the world away and starts
|
|
640
|
+
* another at step 0, for a peer that takes a session's record and replays it, or a
|
|
641
|
+
* replay starting over. `0` by default.
|
|
642
|
+
*/
|
|
643
|
+
readonly run: number;
|
|
644
|
+
/**
|
|
645
|
+
* A step the world also stops at on its way to `until`, to report there with a
|
|
646
|
+
* snapshot of itself (`PhysicsReached.snapshot`) for another world to start `from`. It
|
|
647
|
+
* does not wait there. `null` (the default): no snapshot. An engine that cannot take
|
|
648
|
+
* one reports there without.
|
|
649
|
+
*/
|
|
650
|
+
readonly snapshotAt: number | null;
|
|
651
|
+
/**
|
|
652
|
+
* Where a run starts: a snapshot and the step it was taken at. The world is made from
|
|
653
|
+
* it, holding the bodies and joints it held, and counts on from that step, going
|
|
654
|
+
* straight to `until`; bodies the description has and it lacks are added where
|
|
655
|
+
* described. Read only when a run starts; `null` (the default): from the description,
|
|
656
|
+
* at step 0.
|
|
657
|
+
*/
|
|
658
|
+
readonly from: PhysicsStart | null;
|
|
659
|
+
/**
|
|
660
|
+
* How many of its latest stops (each step it reported at) the world keeps a snapshot
|
|
661
|
+
* of, itself, for a run to start `from` without one in the description: to roll back
|
|
662
|
+
* a few steps and go again with an input that came late. `0` (the default): none.
|
|
663
|
+
*/
|
|
664
|
+
readonly keep: number;
|
|
580
665
|
/** Draw every collider and joint as lines, as react-three-rapier's `<Physics debug>`. */
|
|
581
666
|
readonly debug: boolean;
|
|
582
667
|
/** Joints between bodies, each active while both its bodies exist. */
|
|
583
668
|
readonly joints: ReadonlyArray<Joint>;
|
|
584
669
|
};
|
|
585
670
|
|
|
671
|
+
/**
|
|
672
|
+
* Where a run starts: the step, and a world's snapshot there (base64, as
|
|
673
|
+
* `PhysicsReached` carries it), or `null` for the one the world kept at that step itself
|
|
674
|
+
* (`keep`). A step it did not keep starts from the description, at step 0.
|
|
675
|
+
*/
|
|
676
|
+
export type PhysicsStart = { readonly step: number; readonly snapshot: string | null };
|
|
677
|
+
|
|
586
678
|
/** A body a joint holds: a node's, or one instance's of an instances node. */
|
|
587
679
|
export type BodyRef = { readonly key: string; readonly instance: string | null };
|
|
588
680
|
|
|
@@ -928,8 +1020,106 @@ export type ShaderGridNode = {
|
|
|
928
1020
|
readonly side: MaterialSide;
|
|
929
1021
|
};
|
|
930
1022
|
|
|
1023
|
+
/**
|
|
1024
|
+
* A line through points, as Drei's `Line` (three's fat lines, `Line2`): `width` pixels
|
|
1025
|
+
* wide on screen, or scene units with `worldUnits`; one colour, or one per point; solid
|
|
1026
|
+
* or dashed. With `segments` each pair of points is its own segment (Drei's `Segments`).
|
|
1027
|
+
* A render kit draws it; without one it is an empty group.
|
|
1028
|
+
*/
|
|
1029
|
+
export type LineNode = {
|
|
1030
|
+
readonly _tag: 'Line';
|
|
1031
|
+
readonly key: string;
|
|
1032
|
+
readonly position: Vec3;
|
|
1033
|
+
readonly rotation: Rotation;
|
|
1034
|
+
readonly scale: Vec3;
|
|
1035
|
+
readonly transition: Transition;
|
|
1036
|
+
readonly behaviour: Behave | null;
|
|
1037
|
+
/** Makes the line pickable, within its width. */
|
|
1038
|
+
readonly interactive: boolean;
|
|
1039
|
+
readonly points: ReadonlyArray<Vec3>;
|
|
1040
|
+
readonly segments: boolean;
|
|
1041
|
+
readonly color: Rgb;
|
|
1042
|
+
/** A colour for each point, blended along the line, or `null` for `color` throughout. */
|
|
1043
|
+
readonly vertexColors: ReadonlyArray<Rgb> | null;
|
|
1044
|
+
readonly width: number;
|
|
1045
|
+
readonly worldUnits: boolean;
|
|
1046
|
+
readonly dashed: boolean;
|
|
1047
|
+
readonly dashSize: number;
|
|
1048
|
+
readonly gapSize: number;
|
|
1049
|
+
readonly dashScale: number;
|
|
1050
|
+
readonly dashOffset: number;
|
|
1051
|
+
readonly opacity: number;
|
|
1052
|
+
readonly transparent: boolean;
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* Points, drawn by the render kit as round or square dots, Drei's sparkles or its
|
|
1057
|
+
* stars. The look says which, and carries everything needed to draw them: given
|
|
1058
|
+
* points, or a seed that scatters them the same way every time.
|
|
1059
|
+
*/
|
|
1060
|
+
export type PointsNode = {
|
|
1061
|
+
readonly _tag: 'Points';
|
|
1062
|
+
readonly key: string;
|
|
1063
|
+
readonly position: Vec3;
|
|
1064
|
+
readonly rotation: Rotation;
|
|
1065
|
+
readonly scale: Vec3;
|
|
1066
|
+
readonly transition: Transition;
|
|
1067
|
+
readonly behaviour: Behave | null;
|
|
1068
|
+
readonly look: PointLook;
|
|
1069
|
+
};
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* What points look like:
|
|
1073
|
+
*
|
|
1074
|
+
* - `Dots`: Drei's `Points` with its `PointMaterial` (three's `PointsMaterial`, `round`
|
|
1075
|
+
* to circles): `size` pixels across, smaller with distance under `sizeAttenuation`;
|
|
1076
|
+
* one colour or one a point, one size or a factor a point.
|
|
1077
|
+
* - `Sparkles`: Drei's `Sparkles`: `count` glowing motes scattered through a box of
|
|
1078
|
+
* `scale`, drifting by `noise` at `speed`.
|
|
1079
|
+
* - `Stars`: Drei's `Stars`: `count` stars in a shell from `radius` to `radius + depth`,
|
|
1080
|
+
* `factor` sizing them, twinkling at `speed`, `fade` softening their edges.
|
|
1081
|
+
*/
|
|
1082
|
+
export type PointLook =
|
|
1083
|
+
| {
|
|
1084
|
+
readonly _tag: 'Dots';
|
|
1085
|
+
readonly positions: ReadonlyArray<Vec3>;
|
|
1086
|
+
readonly colors: ReadonlyArray<Rgb> | null;
|
|
1087
|
+
readonly sizes: ReadonlyArray<number> | null;
|
|
1088
|
+
readonly color: Rgb;
|
|
1089
|
+
readonly size: number;
|
|
1090
|
+
readonly sizeAttenuation: boolean;
|
|
1091
|
+
readonly round: boolean;
|
|
1092
|
+
readonly opacity: number;
|
|
1093
|
+
readonly transparent: boolean;
|
|
1094
|
+
}
|
|
1095
|
+
| {
|
|
1096
|
+
readonly _tag: 'Sparkles';
|
|
1097
|
+
readonly count: number;
|
|
1098
|
+
readonly speed: number;
|
|
1099
|
+
readonly opacity: number;
|
|
1100
|
+
readonly scale: Vec3;
|
|
1101
|
+
/** Each mote's size, or `null` for Drei's: random, up to 1. */
|
|
1102
|
+
readonly size: number | null;
|
|
1103
|
+
readonly color: Rgb;
|
|
1104
|
+
readonly noise: Vec3;
|
|
1105
|
+
readonly seed: number;
|
|
1106
|
+
}
|
|
1107
|
+
| {
|
|
1108
|
+
readonly _tag: 'Stars';
|
|
1109
|
+
readonly radius: number;
|
|
1110
|
+
readonly depth: number;
|
|
1111
|
+
readonly count: number;
|
|
1112
|
+
readonly factor: number;
|
|
1113
|
+
readonly saturation: number;
|
|
1114
|
+
readonly fade: boolean;
|
|
1115
|
+
readonly speed: number;
|
|
1116
|
+
readonly seed: number;
|
|
1117
|
+
};
|
|
1118
|
+
|
|
931
1119
|
/** Any node in a scene. Keys are unique across the whole scene. */
|
|
932
1120
|
export type SceneNode =
|
|
1121
|
+
| PointsNode
|
|
1122
|
+
| LineNode
|
|
933
1123
|
| GridNode
|
|
934
1124
|
| ShaderGridNode
|
|
935
1125
|
| GroupNode
|
|
@@ -1080,6 +1270,33 @@ export type ViewRect = {
|
|
|
1080
1270
|
*/
|
|
1081
1271
|
export type View = { readonly key: string; readonly camera: Camera; readonly rect: ViewRect };
|
|
1082
1272
|
|
|
1273
|
+
/** Where a HUD's origin sits on the canvas: a corner, an edge's middle, or the centre. */
|
|
1274
|
+
export type HudAlignment =
|
|
1275
|
+
| 'top-left'
|
|
1276
|
+
| 'top-center'
|
|
1277
|
+
| 'top-right'
|
|
1278
|
+
| 'center-left'
|
|
1279
|
+
| 'center-center'
|
|
1280
|
+
| 'center-right'
|
|
1281
|
+
| 'bottom-left'
|
|
1282
|
+
| 'bottom-center'
|
|
1283
|
+
| 'bottom-right';
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* Nodes drawn over the scene, Drei's `Hud`, as data: a scene of their own (keys apart
|
|
1287
|
+
* from the scene's), drawn after it with only its depth cleared, through an orthographic
|
|
1288
|
+
* camera measured in CSS pixels, y up, whose origin sits `margin` pixels in from the
|
|
1289
|
+
* `alignment`'s edges (Drei's `GizmoHelper` places its gizmo so). Pointer events reach
|
|
1290
|
+
* its interactive meshes before anything behind them; their points and rays are in the
|
|
1291
|
+
* HUD's own space. Scene lighting and fog do not reach it.
|
|
1292
|
+
*/
|
|
1293
|
+
export type Hud = {
|
|
1294
|
+
readonly alignment: HudAlignment;
|
|
1295
|
+
/** CSS pixels in from the aligned edges, `[x, y]`: unused for a centred direction. */
|
|
1296
|
+
readonly margin: readonly [number, number];
|
|
1297
|
+
readonly children: ReadonlyArray<SceneNode>;
|
|
1298
|
+
};
|
|
1299
|
+
|
|
1083
1300
|
/**
|
|
1084
1301
|
* A small scene rendered into a texture that materials use as `map: id`: Drei's
|
|
1085
1302
|
* `RenderTexture`, as data. Its nodes have their own key namespace and may use the
|
|
@@ -1253,6 +1470,22 @@ export type XrGlide = {
|
|
|
1253
1470
|
readonly snapDegrees: number;
|
|
1254
1471
|
};
|
|
1255
1472
|
|
|
1473
|
+
/** What an asset is: a glTF or GLB model, a colour texture, an HDR panorama, a typeface font. */
|
|
1474
|
+
export type AssetKind = 'model' | 'texture' | 'environment' | 'font';
|
|
1475
|
+
|
|
1476
|
+
/**
|
|
1477
|
+
* An asset a scene asks for: its kind, the id it is lent under once loaded, and where it
|
|
1478
|
+
* comes from. `levels` are coarser versions, coarsest first, to show while `url` (the
|
|
1479
|
+
* finest) is on its way: each is lent as it arrives, if finer than what is shown, and
|
|
1480
|
+
* reported as a level of detail (ADR-015).
|
|
1481
|
+
*/
|
|
1482
|
+
export type Asset = {
|
|
1483
|
+
readonly kind: AssetKind;
|
|
1484
|
+
readonly id: string;
|
|
1485
|
+
readonly url: string;
|
|
1486
|
+
readonly levels: ReadonlyArray<string>;
|
|
1487
|
+
};
|
|
1488
|
+
|
|
1256
1489
|
export type SceneDescription = {
|
|
1257
1490
|
readonly camera: Camera;
|
|
1258
1491
|
/** Whether the scene claims the pointer's gestures from the page. */
|
|
@@ -1264,6 +1497,8 @@ export type SceneDescription = {
|
|
|
1264
1497
|
readonly views: ReadonlyArray<View>;
|
|
1265
1498
|
/** Scenes rendered into textures for materials to use. */
|
|
1266
1499
|
readonly renderTextures: ReadonlyArray<RenderTexture>;
|
|
1500
|
+
/** Nodes drawn over the scene, or `null` for none. */
|
|
1501
|
+
readonly hud: Hud | null;
|
|
1267
1502
|
/** Points to show HTML at, placed every frame the scene draws. */
|
|
1268
1503
|
readonly overlays: ReadonlyArray<Overlay>;
|
|
1269
1504
|
/** Image-based lighting and background, or `null` for none. */
|
|
@@ -1281,6 +1516,14 @@ export type SceneDescription = {
|
|
|
1281
1516
|
* changes: what Drei's `Bounds` measures, for `fitCamera`. Loaded models included.
|
|
1282
1517
|
*/
|
|
1283
1518
|
readonly measure: ReadonlyArray<string>;
|
|
1519
|
+
/**
|
|
1520
|
+
* Assets the scene asks for, each lent under its id once loaded: a `borrowed({ id })`
|
|
1521
|
+
* node shows a model, a material's `map: id` a texture, `environment({ map: id })` a
|
|
1522
|
+
* panorama and `text({ font: id })` a font. The host loads each once however many
|
|
1523
|
+
* scenes ask, cancels a load no scene asks for any more, and frees an asset when the
|
|
1524
|
+
* last scene stops asking (ADR-015); it reports each as loaded or failed.
|
|
1525
|
+
*/
|
|
1526
|
+
readonly assets: ReadonlyArray<Asset>;
|
|
1284
1527
|
readonly render: RenderSettings;
|
|
1285
1528
|
readonly resources: ResourceDefinitions;
|
|
1286
1529
|
readonly children: ReadonlyArray<SceneNode>;
|
package/src/kernel/diff.ts
CHANGED
|
@@ -31,6 +31,10 @@ import {
|
|
|
31
31
|
labelMaterialEquals,
|
|
32
32
|
labelTypeEquals,
|
|
33
33
|
xrSpaceEquals,
|
|
34
|
+
lineLookEquals,
|
|
35
|
+
edgesEquals,
|
|
36
|
+
trailEquals,
|
|
37
|
+
pointLookEquals,
|
|
34
38
|
detailEquals,
|
|
35
39
|
shaderGridLookEquals,
|
|
36
40
|
instancesEqual,
|
|
@@ -82,6 +86,14 @@ export type Field =
|
|
|
82
86
|
| 'text'
|
|
83
87
|
/** A shader grid's look: anything but where it is. */
|
|
84
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'
|
|
85
97
|
/** The tracked space a group follows. */
|
|
86
98
|
| 'follow'
|
|
87
99
|
/** A group's levels of detail. */
|
|
@@ -94,6 +106,9 @@ export type Op =
|
|
|
94
106
|
/** A render texture was created, resized or its scene changed (its own operations). */
|
|
95
107
|
| { readonly _tag: 'UpdateRenderTexture'; readonly id: string; readonly ops: ReadonlyArray<Op> }
|
|
96
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' }
|
|
97
112
|
| { readonly _tag: 'SetRenderSettings'; readonly settings: RenderSettings }
|
|
98
113
|
| { readonly _tag: 'DefineResources'; readonly resources: ResourceDefinitions }
|
|
99
114
|
| { readonly _tag: 'Create'; readonly node: SceneNode; readonly parent: ParentKey }
|
|
@@ -135,6 +150,7 @@ export const changedFields = (
|
|
|
135
150
|
if (!bodyEquals(prev.body, next.body)) fields.push('body');
|
|
136
151
|
if (!xrSpaceEquals(prev.follow, next.follow)) fields.push('follow');
|
|
137
152
|
if (!detailEquals(prev.detail, next.detail)) fields.push('detail');
|
|
153
|
+
if (!trailEquals(prev.trail, next.trail)) fields.push('trail');
|
|
138
154
|
return fields;
|
|
139
155
|
}
|
|
140
156
|
case 'Mesh': {
|
|
@@ -150,6 +166,8 @@ export const changedFields = (
|
|
|
150
166
|
if (prev.castShadow !== next.castShadow) fields.push('castShadow');
|
|
151
167
|
if (prev.receiveShadow !== next.receiveShadow) fields.push('receiveShadow');
|
|
152
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');
|
|
153
171
|
return fields;
|
|
154
172
|
}
|
|
155
173
|
case 'Instances': {
|
|
@@ -239,6 +257,27 @@ export const changedFields = (
|
|
|
239
257
|
if (!Object.is(prev.shadowBias, next.shadowBias)) fields.push('shadowBias');
|
|
240
258
|
return fields;
|
|
241
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
|
+
}
|
|
242
281
|
case 'ShaderGrid': {
|
|
243
282
|
if (prev._tag !== 'ShaderGrid') return undefined;
|
|
244
283
|
if (!vec3Equals(prev.position, next.position)) fields.push('position');
|