@oeave/bakery3 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +562 -2
- package/dist/animation-B1h0Ryvj.d.ts +97 -0
- package/dist/bake/index.d.ts +369 -0
- package/dist/bake/index.js +9 -0
- package/dist/bake/index.js.map +1 -0
- package/dist/bake-DZ-CJR6f.d.ts +1364 -0
- package/dist/catalog/index.d.ts +100 -0
- package/dist/catalog/index.js +154 -0
- package/dist/catalog/index.js.map +1 -0
- package/dist/catalog.gen-BM-aNf7n.d.ts +733 -0
- package/dist/chunk-3G5QL4F4.js +145 -0
- package/dist/chunk-3G5QL4F4.js.map +1 -0
- package/dist/chunk-4E5VV4QY.js +12 -0
- package/dist/chunk-4E5VV4QY.js.map +1 -0
- package/dist/chunk-62M6XXNX.js +142 -0
- package/dist/chunk-62M6XXNX.js.map +1 -0
- package/dist/chunk-6FYI6AJM.js +1157 -0
- package/dist/chunk-6FYI6AJM.js.map +1 -0
- package/dist/chunk-6NYR73Y7.js +832 -0
- package/dist/chunk-6NYR73Y7.js.map +1 -0
- package/dist/chunk-APWUCEEB.js +118 -0
- package/dist/chunk-APWUCEEB.js.map +1 -0
- package/dist/chunk-HNMSWQU7.js +1709 -0
- package/dist/chunk-HNMSWQU7.js.map +1 -0
- package/dist/chunk-JFYUDERE.js +1412 -0
- package/dist/chunk-JFYUDERE.js.map +1 -0
- package/dist/chunk-KNUAOILG.js +551 -0
- package/dist/chunk-KNUAOILG.js.map +1 -0
- package/dist/chunk-KZLVTSBI.js +191 -0
- package/dist/chunk-KZLVTSBI.js.map +1 -0
- package/dist/chunk-LAKXC4WR.js +2028 -0
- package/dist/chunk-LAKXC4WR.js.map +1 -0
- package/dist/chunk-NXBAZGNB.js +1107 -0
- package/dist/chunk-NXBAZGNB.js.map +1 -0
- package/dist/chunk-QRCT5UGZ.js +3286 -0
- package/dist/chunk-QRCT5UGZ.js.map +1 -0
- package/dist/chunk-S4AJTLLN.js +23 -0
- package/dist/chunk-S4AJTLLN.js.map +1 -0
- package/dist/chunk-UWBP7B54.js +92 -0
- package/dist/chunk-UWBP7B54.js.map +1 -0
- package/dist/chunk-XK35ANPJ.js +346 -0
- package/dist/chunk-XK35ANPJ.js.map +1 -0
- package/dist/chunk-Z7IYVH22.js +4781 -0
- package/dist/chunk-Z7IYVH22.js.map +1 -0
- package/dist/chunk-ZEBVAIJJ.js +156 -0
- package/dist/chunk-ZEBVAIJJ.js.map +1 -0
- package/dist/devtools/index.d.ts +536 -0
- package/dist/devtools/index.js +15 -0
- package/dist/devtools/index.js.map +1 -0
- package/dist/environments/index.d.ts +93 -0
- package/dist/environments/index.js +382 -0
- package/dist/environments/index.js.map +1 -0
- package/dist/hotspots/index.d.ts +111 -0
- package/dist/hotspots/index.js +285 -0
- package/dist/hotspots/index.js.map +1 -0
- package/dist/index.d.ts +975 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/node/index.cjs +5240 -0
- package/dist/node/index.cjs.map +1 -0
- package/dist/node/index.d.cts +4927 -0
- package/dist/node/index.d.ts +597 -0
- package/dist/node/index.js +1328 -0
- package/dist/node/index.js.map +1 -0
- package/dist/prepare-BtjY4G3q.d.ts +112 -0
- package/dist/presets/index.d.ts +562 -0
- package/dist/presets/index.js +14 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/r3f/index.d.ts +159 -0
- package/dist/r3f/index.js +592 -0
- package/dist/r3f/index.js.map +1 -0
- package/dist/room-FS26KAPQ.js +9 -0
- package/dist/room-FS26KAPQ.js.map +1 -0
- package/dist/rooms.gen-DItzBR9k.d.ts +1462 -0
- package/dist/session-VCIQEO26.js +9 -0
- package/dist/session-VCIQEO26.js.map +1 -0
- package/dist/shapes/index.d.ts +222 -0
- package/dist/shapes/index.js +836 -0
- package/dist/shapes/index.js.map +1 -0
- package/dist/testRun-20OARnQr.d.ts +1139 -0
- package/dist/timeline-ChwgD7bT.d.ts +470 -0
- package/dist/tsl/index.d.ts +165 -0
- package/dist/tsl/index.js +310 -0
- package/dist/tsl/index.js.map +1 -0
- package/package.json +170 -4
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import { Camera, Scene, Object3D } from 'three';
|
|
2
|
+
import { A as AnimationSpec } from './animation-B1h0Ryvj.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Easing, in one implementation.
|
|
6
|
+
*
|
|
7
|
+
* The named easings are the CSS cubic-bezier control points, not hand-rolled
|
|
8
|
+
* polynomials, so `'ease-in-out'` here means what it means in every stylesheet
|
|
9
|
+
* and in gsap: a motion tuned in CSS comes back from the render as the same
|
|
10
|
+
* curve.
|
|
11
|
+
*
|
|
12
|
+
* This is also the only place easing exists. The renderer never sees a curve:
|
|
13
|
+
* the SDK samples the timeline at every output frame and ships the numbers,
|
|
14
|
+
* which is what keeps the browser preview and the rendered frame agreeing on
|
|
15
|
+
* where things are.
|
|
16
|
+
*/
|
|
17
|
+
/** A CSS easing: one of the four names, or the four control points of a
|
|
18
|
+
* cubic-bezier, `[x1, y1, x2, y2]`, exactly as a stylesheet writes them. */
|
|
19
|
+
type Easing = 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out' | [number, number, number, number];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Orbits, and the lookAt → quaternion conversion the wire format needs.
|
|
23
|
+
*
|
|
24
|
+
* A camera move between two points has two honest answers and they look
|
|
25
|
+
* nothing alike. Lerping the positions drives the camera through the middle
|
|
26
|
+
* of the product; interpolating in spherical coordinates around the thing
|
|
27
|
+
* being looked at swings around it at a constant distance. The second one is
|
|
28
|
+
* what everybody means by "orbit the camera 90°", so it is a first-class path
|
|
29
|
+
* type rather than something you have to build out of forty keyframes.
|
|
30
|
+
*
|
|
31
|
+
* Angles here are in radians and are not wrapped. An orbit of +370° is a full
|
|
32
|
+
* turn plus ten degrees, not minus ten: wrapping it into ±180° would silently
|
|
33
|
+
* reverse a turntable.
|
|
34
|
+
*/
|
|
35
|
+
type Vec3 = [number, number, number];
|
|
36
|
+
/** glTF order (x, y, z, w), matching the protocol's `Quat` and three's own
|
|
37
|
+
* `Quaternion.toArray()`. Nothing on this side of the wire reorders. */
|
|
38
|
+
type Quat = [number, number, number, number];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* What can be animated, how to read it off the live object, and how to put it
|
|
42
|
+
* back.
|
|
43
|
+
*
|
|
44
|
+
* One table, because three consumers must agree exactly: the panel (which
|
|
45
|
+
* offers a list), `seek()` (which drives the live three scene, so the browser
|
|
46
|
+
* is the preview) and `bake()` (which writes the numbers the renderer plays).
|
|
47
|
+
* A property that reads differently from how it writes is a preview that lies
|
|
48
|
+
* about the render.
|
|
49
|
+
*
|
|
50
|
+
* Everything is duck-typed rather than `instanceof Mesh`. three is a peer
|
|
51
|
+
* dependency with no runtime import here, and duck typing is what three does
|
|
52
|
+
* internally anyway, so this keeps working across a version that adds a type.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/** Values are always arrays, scalars included. One interpolation path, one
|
|
56
|
+
* storage shape, and `size` is then simply `value.length`. */
|
|
57
|
+
type PropertyValue = number[];
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A timeline you author in keyframes and ship as frames.
|
|
61
|
+
*
|
|
62
|
+
* const tl = createTimeline({ duration: 5, fps: 30, camera, scene });
|
|
63
|
+
*
|
|
64
|
+
* tl.push({ object: 'camera', orbitY: 90 });
|
|
65
|
+
* tl.to(mesh, { color: '#ff2d95' }, { at: 1, duration: 2, easing: 'ease-in-out' });
|
|
66
|
+
*
|
|
67
|
+
* tl.seek(2.5); // the live scene is the preview
|
|
68
|
+
* await bakery3.renderVideo({ timeline: tl });
|
|
69
|
+
*
|
|
70
|
+
* It looks like gsap and differs from it in two ways, because a shot has a
|
|
71
|
+
* known length rather than being a queue of tweens:
|
|
72
|
+
*
|
|
73
|
+
* - `at` defaults to 0 and `duration` to the rest of the timeline, so
|
|
74
|
+
* `tl.push({ object: 'camera', orbitY: 15 })` on a five-second timeline is
|
|
75
|
+
* a five-second move. Nothing appends itself to the end.
|
|
76
|
+
* - `seek()` drives your actual three objects. There is no shadow copy of
|
|
77
|
+
* the scene, so what you scrub to is what gets rendered.
|
|
78
|
+
*
|
|
79
|
+
* Everything that interpolates lives here, in easing.ts and in orbit.ts. The
|
|
80
|
+
* renderer sees none of it: `bake()` samples this evaluator at every output
|
|
81
|
+
* frame and ships the numbers.
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
/** `'camera'` rather than the camera object, because a live R3F camera is
|
|
85
|
+
* frequently swapped out from under the application and an id that survives
|
|
86
|
+
* that is worth more than a reference that does not. */
|
|
87
|
+
type TrackTarget = 'camera' | Object3D;
|
|
88
|
+
/** One value, at one time, on one track. Times are seconds from the start of
|
|
89
|
+
* the timeline, never relative to the keyframe before, so retiming one
|
|
90
|
+
* keyframe cannot cascade through the rest of the lane. */
|
|
91
|
+
type Keyframe = {
|
|
92
|
+
time: number;
|
|
93
|
+
value: PropertyValue;
|
|
94
|
+
/** Governs the segment ending here: the ease passed to the `.to()` that
|
|
95
|
+
* created this keyframe. */
|
|
96
|
+
easing?: Easing;
|
|
97
|
+
path?: 'line' | 'orbit';
|
|
98
|
+
/**
|
|
99
|
+
* Where an orbit segment ends, in spherical coordinates about `center`.
|
|
100
|
+
*
|
|
101
|
+
* Carried alongside the position rather than instead of it, so a consumer
|
|
102
|
+
* that does not understand orbits (the panel's diamonds, a JSON reader)
|
|
103
|
+
* still sees the right endpoint. `yaw` is unwrapped: +370° means a full turn
|
|
104
|
+
* plus ten degrees.
|
|
105
|
+
*/
|
|
106
|
+
orbit?: {
|
|
107
|
+
center: Vec3;
|
|
108
|
+
radius: number;
|
|
109
|
+
yaw: number;
|
|
110
|
+
pitch: number;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
/** One property of one target, over time: a lane in the panel and a channel
|
|
114
|
+
* in the bake. One track per (target, property) pair, always: two lanes
|
|
115
|
+
* driving one property would fight over the live object on every `seek()`. */
|
|
116
|
+
type Track = {
|
|
117
|
+
id: string;
|
|
118
|
+
target: TrackTarget;
|
|
119
|
+
property: string;
|
|
120
|
+
size: number;
|
|
121
|
+
/** What the panel shows. Falls back to the object's name or type. */
|
|
122
|
+
label: string;
|
|
123
|
+
keyframes: Keyframe[];
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* What a panel can listen for.
|
|
127
|
+
*
|
|
128
|
+
* `time` fires on every `seek()`, sixty times a second while playing, so a
|
|
129
|
+
* listener must be cheap. `change` is structural (a keyframe, the duration,
|
|
130
|
+
* the fps) and also fires the `time` listeners, because every readout that
|
|
131
|
+
* watches the playhead also wants to know the lanes moved underneath it.
|
|
132
|
+
*/
|
|
133
|
+
type TimelineEvent = 'change' | 'time' | 'play' | 'pause';
|
|
134
|
+
/** Everything a timeline needs to know before anything is authored on it.
|
|
135
|
+
* All optional: a timeline with no scene and no camera is still a valid
|
|
136
|
+
* document, which is what makes one testable without a renderer. */
|
|
137
|
+
type TimelineOptions = {
|
|
138
|
+
/** Seconds. Default 5. */
|
|
139
|
+
duration?: number;
|
|
140
|
+
/** Frames per second the bake samples at. Default 30. */
|
|
141
|
+
fps?: number;
|
|
142
|
+
camera?: Camera;
|
|
143
|
+
scene?: Scene;
|
|
144
|
+
/**
|
|
145
|
+
* What camera orbits turn around and lookAt tracks default to. Defaults to
|
|
146
|
+
* the center of the scene's bounding box, which is what "orbit the product"
|
|
147
|
+
* usually means.
|
|
148
|
+
*/
|
|
149
|
+
lookAt?: Vec3;
|
|
150
|
+
loop?: boolean;
|
|
151
|
+
/** The host's orbit-controls target, when it has one. Read on keyframe
|
|
152
|
+
* capture so the After Effects flow (frame it with the mouse, press the
|
|
153
|
+
* button) records the point you were actually orbiting. */
|
|
154
|
+
controlsTarget?: () => Vec3;
|
|
155
|
+
};
|
|
156
|
+
/** Where a `.to()`, `.fromTo()` or `.orbit()` sits on the timeline. */
|
|
157
|
+
type SegmentOptions = {
|
|
158
|
+
/** Seconds from the start of the timeline. Default 0. */
|
|
159
|
+
at?: number;
|
|
160
|
+
/** Seconds. Default: the rest of the timeline. */
|
|
161
|
+
duration?: number;
|
|
162
|
+
easing?: Easing;
|
|
163
|
+
};
|
|
164
|
+
/** A camera swing. Everything is relative to where the camera already is,
|
|
165
|
+
* except `radius`: an orbit is a nudge from the pose you framed, not a jump
|
|
166
|
+
* to coordinates you would have to work out first. */
|
|
167
|
+
type OrbitOptions = SegmentOptions & {
|
|
168
|
+
/** Degrees about the up axis. Not wrapped: -540 is one and a half turns. */
|
|
169
|
+
yaw?: number;
|
|
170
|
+
/** Degrees of elevation change. */
|
|
171
|
+
pitch?: number;
|
|
172
|
+
/** Absolute end distance from the center. */
|
|
173
|
+
radius?: number;
|
|
174
|
+
/** Distance change, relative to where the camera starts. */
|
|
175
|
+
dolly?: number;
|
|
176
|
+
lookAt?: Vec3 | Object3D;
|
|
177
|
+
};
|
|
178
|
+
/** The one-call shorthand: `tl.push({ object: 'camera', orbitY: +15 })`. */
|
|
179
|
+
type PushSpec = SegmentOptions & {
|
|
180
|
+
object: TrackTarget;
|
|
181
|
+
/** Degrees, horizontal. Positive swings the camera to its right. */
|
|
182
|
+
orbitY?: number;
|
|
183
|
+
/** Degrees, vertical. Positive lifts the camera. */
|
|
184
|
+
orbitX?: number;
|
|
185
|
+
/** Distance change, relative to where the camera starts. */
|
|
186
|
+
dolly?: number;
|
|
187
|
+
lookAt?: Vec3 | Object3D;
|
|
188
|
+
} & Record<string, unknown>;
|
|
189
|
+
/** Anything `.to()` accepts as a value: a number, a vector, a hex color, or
|
|
190
|
+
* a three Vector3/Color/Euler read straight off the scene. */
|
|
191
|
+
type AuthoredValue = number | number[] | string | {
|
|
192
|
+
x: number;
|
|
193
|
+
y: number;
|
|
194
|
+
z: number;
|
|
195
|
+
} | {
|
|
196
|
+
r: number;
|
|
197
|
+
g: number;
|
|
198
|
+
b: number;
|
|
199
|
+
};
|
|
200
|
+
/** Keyframes in, frames out. The file header has the two places this differs
|
|
201
|
+
* from gsap. */
|
|
202
|
+
declare class Timeline {
|
|
203
|
+
private _duration;
|
|
204
|
+
private _fps;
|
|
205
|
+
private _time;
|
|
206
|
+
private _tracks;
|
|
207
|
+
private _playing;
|
|
208
|
+
private _raf;
|
|
209
|
+
private _lastTick;
|
|
210
|
+
private _listeners;
|
|
211
|
+
private _center;
|
|
212
|
+
private _nextId;
|
|
213
|
+
/** Playback only. A bake is always exactly one pass of the duration, so
|
|
214
|
+
* looping in the panel never changes what a video contains. */
|
|
215
|
+
loop: boolean;
|
|
216
|
+
private _camera?;
|
|
217
|
+
private _scene?;
|
|
218
|
+
/** The host's orbit-controls target, when it has one. */
|
|
219
|
+
controlsTarget?: () => Vec3;
|
|
220
|
+
constructor(options?: TimelineOptions);
|
|
221
|
+
/**
|
|
222
|
+
* What `seek()` drives and `captureCameraKeyframe()` reads. Settable after
|
|
223
|
+
* construction because an R3F camera is frequently swapped out from under
|
|
224
|
+
* the application. A timeline from `bakery3.timeline()` that was given no
|
|
225
|
+
* camera reads the client's attached one each time it needs it.
|
|
226
|
+
*/
|
|
227
|
+
get camera(): Camera | undefined;
|
|
228
|
+
set camera(camera: Camera | undefined);
|
|
229
|
+
/** Used to find the orbit center, and to check at bake time that every
|
|
230
|
+
* animated object is actually in what gets exported. Read from the
|
|
231
|
+
* client's attachment, like `camera`, when it was not given. */
|
|
232
|
+
get scene(): Scene | undefined;
|
|
233
|
+
set scene(scene: Scene | undefined);
|
|
234
|
+
get duration(): number;
|
|
235
|
+
/**
|
|
236
|
+
* Setting this moves the end, not the keyframes.
|
|
237
|
+
*
|
|
238
|
+
* Existing keyframes keep their absolute times, because the common reason to
|
|
239
|
+
* change the duration is "I need two more seconds at the end", and silently
|
|
240
|
+
* rescaling every ease you already tuned would be the opposite of that.
|
|
241
|
+
* `scaleToFit()` is the other intent, spelled out.
|
|
242
|
+
*/
|
|
243
|
+
set duration(seconds: number);
|
|
244
|
+
get fps(): number;
|
|
245
|
+
set fps(value: number);
|
|
246
|
+
get time(): number;
|
|
247
|
+
get playing(): boolean;
|
|
248
|
+
get tracks(): readonly Track[];
|
|
249
|
+
/** Frames a bake at the current settings will produce. */
|
|
250
|
+
get frameCount(): number;
|
|
251
|
+
/** Proportionally rescale every keyframe into a new duration. */
|
|
252
|
+
scaleToFit(seconds: number): this;
|
|
253
|
+
/**
|
|
254
|
+
* The point orbits turn around.
|
|
255
|
+
*
|
|
256
|
+
* Resolved lazily so a timeline created before the model finished loading
|
|
257
|
+
* still centers on it. `createTimeline()` at mount is the normal case, and
|
|
258
|
+
* an empty scene's bounding box is the origin, which would be wrong for
|
|
259
|
+
* every model that is not at the origin.
|
|
260
|
+
*/
|
|
261
|
+
get center(): Vec3;
|
|
262
|
+
set center(value: Vec3);
|
|
263
|
+
/**
|
|
264
|
+
* Tween to a value from wherever the target is at `at`.
|
|
265
|
+
*
|
|
266
|
+
* "Wherever it is at `at`" means the timeline's own value if something else
|
|
267
|
+
* already animates that property, and the live object's value if not, so
|
|
268
|
+
* chaining two `.to()` calls on one property does what it looks like.
|
|
269
|
+
*/
|
|
270
|
+
to(target: TrackTarget, props: Record<string, AuthoredValue>, options?: SegmentOptions): this;
|
|
271
|
+
/** Both ends stated. Nothing is read off the live object. */
|
|
272
|
+
fromTo(target: TrackTarget, from: Record<string, AuthoredValue>, to: Record<string, AuthoredValue>, options?: SegmentOptions): this;
|
|
273
|
+
/**
|
|
274
|
+
* Swing the camera around what it is looking at.
|
|
275
|
+
*
|
|
276
|
+
* The lookAt point is pinned for the whole segment. That is the difference
|
|
277
|
+
* between an orbit and a camera drifting sideways past the product, and it
|
|
278
|
+
* is why this writes a `lookAt` track as well as a `position` one.
|
|
279
|
+
*/
|
|
280
|
+
orbit(camera: TrackTarget, options?: OrbitOptions): this;
|
|
281
|
+
/**
|
|
282
|
+
* One-call shorthand:
|
|
283
|
+
*
|
|
284
|
+
* tl.push({ object: 'camera', orbitY: +15 });
|
|
285
|
+
*
|
|
286
|
+
* The camera starts where it is now and ends fifteen degrees round its
|
|
287
|
+
* current lookAt, holding that lookAt, over the whole timeline. Any other
|
|
288
|
+
* key is treated as if it had been passed to `.to()`.
|
|
289
|
+
*/
|
|
290
|
+
push(spec: PushSpec): this;
|
|
291
|
+
/**
|
|
292
|
+
* Save a property where it is right now.
|
|
293
|
+
*
|
|
294
|
+
* The After Effects flow: put the playhead somewhere, pose the scene, press
|
|
295
|
+
* the button. Both arguments default to exactly that: the playhead, and the
|
|
296
|
+
* live value.
|
|
297
|
+
*/
|
|
298
|
+
keyframe(target: TrackTarget, property: string, time?: number, value?: AuthoredValue): Keyframe;
|
|
299
|
+
/**
|
|
300
|
+
* The camera, as the developer framed it.
|
|
301
|
+
*
|
|
302
|
+
* Position and the point it is aimed at, because a camera keyframe that
|
|
303
|
+
* records only position leaves the render free to aim it somewhere else.
|
|
304
|
+
* The aim comes from the host's orbit controls when there are any (that is
|
|
305
|
+
* the point you were dragging around), and is otherwise projected along the
|
|
306
|
+
* camera's own forward axis, never assumed to be the origin.
|
|
307
|
+
*/
|
|
308
|
+
captureCameraKeyframe(time?: number, lookAt?: Vec3): void;
|
|
309
|
+
removeKeyframe(trackId: string, index: number): void;
|
|
310
|
+
/** Retime one keyframe, clamped into the timeline. A keyframe dragged past
|
|
311
|
+
* the end would still be baked, at a frame nobody renders, so it is pinned
|
|
312
|
+
* to the duration rather than allowed to leave the shot. */
|
|
313
|
+
moveKeyframe(trackId: string, index: number, time: number): void;
|
|
314
|
+
/** Re-curve the segment ending at this keyframe, the same convention `.to()`
|
|
315
|
+
* writes, so the panel's dropdown and the authoring API mean the same thing
|
|
316
|
+
* by "the ease on this diamond". */
|
|
317
|
+
setEasing(trackId: string, index: number, easing: Easing): void;
|
|
318
|
+
/** Drop a lane and everything on it. The live object keeps whatever value it
|
|
319
|
+
* last had: removing a track stops driving a property, it does not restore
|
|
320
|
+
* what the property was before the timeline touched it. */
|
|
321
|
+
removeTrack(trackId: string): void;
|
|
322
|
+
/**
|
|
323
|
+
* Re-attach tracks to the scene as it is now, and report what could not be.
|
|
324
|
+
*
|
|
325
|
+
* An application that rebuilds part of its graph (a configurator swapping a
|
|
326
|
+
* variant, R3F remounting a subtree) leaves tracks pointing at Object3Ds
|
|
327
|
+
* that are no longer in the scene, even though an object that is for every
|
|
328
|
+
* purpose "the same one" is standing right there. Rather than fail at
|
|
329
|
+
* export, look for that object: first by the identity you pinned
|
|
330
|
+
* (`userData.bakery3Id`, which is exactly what survives a rebuild), then by
|
|
331
|
+
* an unambiguous name match.
|
|
332
|
+
*
|
|
333
|
+
* Nothing is dropped silently. Every track that could not be healed is
|
|
334
|
+
* returned, so the caller can say what happened; that is the difference
|
|
335
|
+
* between repairing a timeline and quietly losing a motion you authored.
|
|
336
|
+
*/
|
|
337
|
+
reconcile(scene: Object3D): {
|
|
338
|
+
healed: Array<{
|
|
339
|
+
trackId: string;
|
|
340
|
+
property: string;
|
|
341
|
+
label: string;
|
|
342
|
+
}>;
|
|
343
|
+
stale: Array<{
|
|
344
|
+
trackId: string;
|
|
345
|
+
property: string;
|
|
346
|
+
label: string;
|
|
347
|
+
reason: string;
|
|
348
|
+
}>;
|
|
349
|
+
};
|
|
350
|
+
/** Find or create a lane. Public because the panel's "add track" picker
|
|
351
|
+
* needs to create one without also writing a keyframe. */
|
|
352
|
+
track(target: TrackTarget, property: string): Track;
|
|
353
|
+
/**
|
|
354
|
+
* Evaluate every track and drive the live scene.
|
|
355
|
+
*
|
|
356
|
+
* The same evaluator `bake()` samples, which is the whole design: what you
|
|
357
|
+
* scrub to and what the renderer traces cannot drift apart, because there is
|
|
358
|
+
* only one of them.
|
|
359
|
+
*/
|
|
360
|
+
seek(time: number): this;
|
|
361
|
+
play(): this;
|
|
362
|
+
pause(): this;
|
|
363
|
+
/** Pause and rewind. */
|
|
364
|
+
stop(): this;
|
|
365
|
+
/** Stop the clock and forget every listener. The scene is left exactly where
|
|
366
|
+
* the playhead left it: a timeline being torn down should not also move
|
|
367
|
+
* your camera back. */
|
|
368
|
+
dispose(): void;
|
|
369
|
+
private tick;
|
|
370
|
+
/** The value of one track at a time, without touching the scene. Exposed
|
|
371
|
+
* because the bake samples it. */
|
|
372
|
+
valueAt(track: Track, time: number): PropertyValue | undefined;
|
|
373
|
+
/** What the timeline says everything is at `time`, for a panel readout or a
|
|
374
|
+
* test: the same numbers `seek()` applies. */
|
|
375
|
+
sample(time: number): Array<{
|
|
376
|
+
track: Track;
|
|
377
|
+
value: PropertyValue;
|
|
378
|
+
}>;
|
|
379
|
+
/** The camera pose at a time: position and the rotation the wire format
|
|
380
|
+
* carries. Shared by `seek()` and `bake()` so the preview cannot aim
|
|
381
|
+
* somewhere the render does not. */
|
|
382
|
+
cameraPoseAt(time: number): {
|
|
383
|
+
position: Vec3;
|
|
384
|
+
lookAt: Vec3;
|
|
385
|
+
quaternion: Quat;
|
|
386
|
+
} | undefined;
|
|
387
|
+
private apply;
|
|
388
|
+
/**
|
|
389
|
+
* Every track, sampled at every output frame. The wire format is flat
|
|
390
|
+
* samples, so the renderer needs no evaluator of its own.
|
|
391
|
+
*/
|
|
392
|
+
bake(options?: {
|
|
393
|
+
scene?: Scene;
|
|
394
|
+
fps?: number;
|
|
395
|
+
duration?: number;
|
|
396
|
+
}): AnimationSpec;
|
|
397
|
+
/** The timeline as a storable document. See `TimelineJSON`, and `fromJSON`
|
|
398
|
+
* for how stored ids find their live objects again. */
|
|
399
|
+
toJSON(): TimelineJSON;
|
|
400
|
+
/**
|
|
401
|
+
* Rebuild a timeline against a live scene.
|
|
402
|
+
*
|
|
403
|
+
* `resolve` maps a stored object id back to a live object. An id that does
|
|
404
|
+
* not resolve is refused rather than skipped: a track that silently
|
|
405
|
+
* disappears is a video missing a motion you authored, and you would find
|
|
406
|
+
* out by watching the finished render.
|
|
407
|
+
*/
|
|
408
|
+
static fromJSON(json: TimelineJSON, context?: {
|
|
409
|
+
resolve?: (objectId: string) => Object3D | undefined;
|
|
410
|
+
camera?: Camera;
|
|
411
|
+
scene?: Scene;
|
|
412
|
+
}): Timeline;
|
|
413
|
+
/** Subscribe. Returns the unsubscribe, so a panel's teardown is one call and
|
|
414
|
+
* a forgotten listener cannot keep a disposed scene alive. */
|
|
415
|
+
on(event: TimelineEvent, listener: (payload: never) => void): () => void;
|
|
416
|
+
private emit;
|
|
417
|
+
/** The live value of a property, read through the shared property table. */
|
|
418
|
+
live(target: TrackTarget, property: string): PropertyValue | undefined;
|
|
419
|
+
private find;
|
|
420
|
+
private normalizeTarget;
|
|
421
|
+
private span;
|
|
422
|
+
/** Make sure a segment has something to start from at `at`. */
|
|
423
|
+
private anchor;
|
|
424
|
+
private insert;
|
|
425
|
+
private requireCamera;
|
|
426
|
+
/**
|
|
427
|
+
* The camera, when one is needed. A client's timeline that has none by then
|
|
428
|
+
* refuses, because the move it was asked for would otherwise be authored
|
|
429
|
+
* from a made-up pose, or silently drive nothing. A standalone timeline
|
|
430
|
+
* with no camera is a document that can still be read and sampled.
|
|
431
|
+
*/
|
|
432
|
+
private cameraOrThrow;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* A timeline as a document.
|
|
436
|
+
*
|
|
437
|
+
* Targets travel as object ids rather than references, so this survives a page
|
|
438
|
+
* reload, a database column and a git diff. `version` is here so a stored
|
|
439
|
+
* timeline can be migrated rather than silently misread, the same reason the
|
|
440
|
+
* manifest carries `protocolVersion`.
|
|
441
|
+
*/
|
|
442
|
+
type TimelineJSON = {
|
|
443
|
+
version: 1;
|
|
444
|
+
duration: number;
|
|
445
|
+
fps: number;
|
|
446
|
+
center?: Vec3;
|
|
447
|
+
tracks: Array<{
|
|
448
|
+
target: {
|
|
449
|
+
type: 'camera';
|
|
450
|
+
} | {
|
|
451
|
+
type: 'object';
|
|
452
|
+
objectId: string;
|
|
453
|
+
};
|
|
454
|
+
property: string;
|
|
455
|
+
label?: string;
|
|
456
|
+
keyframes: Keyframe[];
|
|
457
|
+
}>;
|
|
458
|
+
};
|
|
459
|
+
/**
|
|
460
|
+
* A timeline, standing on its own.
|
|
461
|
+
*
|
|
462
|
+
* `bakery3.timeline()` is the same call with the attached scene and camera
|
|
463
|
+
* filled in, and is what an application should normally use. This one exists
|
|
464
|
+
* for the cases where there is no Bakery3 instance yet (a test, a timeline
|
|
465
|
+
* built at module scope and handed to the panel), because authoring a camera
|
|
466
|
+
* move should not need an API key.
|
|
467
|
+
*/
|
|
468
|
+
declare function createTimeline(options?: TimelineOptions): Timeline;
|
|
469
|
+
|
|
470
|
+
export { type AuthoredValue as A, type Easing as E, type Keyframe as K, type OrbitOptions as O, type PushSpec as P, type SegmentOptions as S, Timeline as T, type TimelineOptions as a, type Track as b, createTimeline as c, type TrackTarget as d, type TimelineEvent as e, type TimelineJSON as f };
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { float, vec3, vec4 } from 'three/tsl';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @oeave/bakery3/tsl: the renderer's shading vocabulary, usable from TSL.
|
|
5
|
+
*
|
|
6
|
+
* Two things happen here, and importing this module is what turns both on:
|
|
7
|
+
*
|
|
8
|
+
* 1. Registration. TSL's anonymous singletons (`positionWorld`, `time`,
|
|
9
|
+
* `cameraPosition`) carry no recognizable structure, so the extractor can
|
|
10
|
+
* only know them by being introduced. This module imports them from
|
|
11
|
+
* `three/tsl` and registers them by uuid. It is its own entry point
|
|
12
|
+
* because it needs three r170 (one of the nodes it imports is not
|
|
13
|
+
* exported before that); the core SDK keeps working for anyone older.
|
|
14
|
+
*
|
|
15
|
+
* 2. Renderer-only nodes. The renderer has procedural textures and shading
|
|
16
|
+
* inputs a browser cannot compute: traced ambient occlusion, shade-time
|
|
17
|
+
* bevel, pointiness, the classic noise textures. Each function below
|
|
18
|
+
* returns a real TSL node that draws an approximation in the WebGPU
|
|
19
|
+
* viewport and carries a marker the extractor serializes exactly for the
|
|
20
|
+
* render. The viewport is the sketch; the render is the real one.
|
|
21
|
+
* `check()` reports their use as info, never as an error.
|
|
22
|
+
*
|
|
23
|
+
* Everything accepts plain numbers for its dials, and a TSL expression for
|
|
24
|
+
* its coordinate input. Dials are baked into the manifest, so a dial driven
|
|
25
|
+
* by another node is not accepted, rather than accepted and ignored.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
type TSLExpr = ReturnType<typeof float>;
|
|
29
|
+
type Coord = TSLExpr | null | undefined;
|
|
30
|
+
/** A float node: a mask, a factor, a texture's fac output. */
|
|
31
|
+
type FloatNode = ReturnType<typeof float>;
|
|
32
|
+
/** A three-component node: a color or a normal. */
|
|
33
|
+
type Vec3Node = ReturnType<typeof vec3>;
|
|
34
|
+
/** A color with alpha. */
|
|
35
|
+
type Vec4Node = ReturnType<typeof vec4>;
|
|
36
|
+
type NoiseTextureOptions = {
|
|
37
|
+
scale?: number;
|
|
38
|
+
detail?: number;
|
|
39
|
+
roughness?: number;
|
|
40
|
+
lacunarity?: number;
|
|
41
|
+
distortion?: number;
|
|
42
|
+
dimensions?: 2 | 3 | 4;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* The renderer's fractal noise texture. Preview: MaterialX fractal noise,
|
|
46
|
+
* the same character on a different lattice.
|
|
47
|
+
*/
|
|
48
|
+
declare function bakeryNoiseTexture(coord?: Coord, options?: NoiseTextureOptions): FloatNode;
|
|
49
|
+
type VoronoiTextureOptions = {
|
|
50
|
+
scale?: number;
|
|
51
|
+
randomness?: number;
|
|
52
|
+
feature?: 'F1' | 'F2' | 'SMOOTH_F1' | 'DISTANCE_TO_EDGE';
|
|
53
|
+
metric?: 'EUCLIDEAN' | 'MANHATTAN' | 'CHEBYCHEV';
|
|
54
|
+
dimensions?: 2 | 3 | 4;
|
|
55
|
+
};
|
|
56
|
+
/** The renderer's Voronoi texture (distance output). Preview: MaterialX Worley. */
|
|
57
|
+
declare function bakeryVoronoiTexture(coord?: Coord, options?: VoronoiTextureOptions): FloatNode;
|
|
58
|
+
type WaveTextureOptions = {
|
|
59
|
+
scale?: number;
|
|
60
|
+
distortion?: number;
|
|
61
|
+
detail?: number;
|
|
62
|
+
detailScale?: number;
|
|
63
|
+
detailRoughness?: number;
|
|
64
|
+
phase?: number;
|
|
65
|
+
type?: 'BANDS' | 'RINGS';
|
|
66
|
+
direction?: 'X' | 'Y' | 'Z' | 'DIAGONAL';
|
|
67
|
+
profile?: 'SIN' | 'SAW' | 'TRI';
|
|
68
|
+
};
|
|
69
|
+
/** The renderer's wave texture. Preview: a sine along the chosen axis. */
|
|
70
|
+
declare function bakeryWaveTexture(coord?: Coord, options?: WaveTextureOptions): FloatNode;
|
|
71
|
+
type MagicTextureOptions = {
|
|
72
|
+
scale?: number;
|
|
73
|
+
distortion?: number;
|
|
74
|
+
depth?: number;
|
|
75
|
+
};
|
|
76
|
+
/** The renderer's interference "magic" texture. Preview: interfering sines,
|
|
77
|
+
* the flavor and not the math. */
|
|
78
|
+
declare function bakeryMagicTexture(coord?: Coord, options?: MagicTextureOptions): FloatNode;
|
|
79
|
+
type GradientType = 'LINEAR' | 'QUADRATIC' | 'EASING' | 'DIAGONAL' | 'SPHERICAL' | 'QUADRATIC_SPHERE' | 'RADIAL';
|
|
80
|
+
type GradientTextureOptions = {
|
|
81
|
+
type?: GradientType;
|
|
82
|
+
};
|
|
83
|
+
/** The renderer's gradient texture. Preview: the linear ramp. */
|
|
84
|
+
declare function bakeryGradientTexture(coord?: Coord, options?: GradientTextureOptions): FloatNode;
|
|
85
|
+
type BrickTextureOptions = {
|
|
86
|
+
offset?: number;
|
|
87
|
+
offsetFrequency?: number;
|
|
88
|
+
squash?: number;
|
|
89
|
+
squashFrequency?: number;
|
|
90
|
+
scale?: number;
|
|
91
|
+
mortarSize?: number;
|
|
92
|
+
mortarSmooth?: number;
|
|
93
|
+
bias?: number;
|
|
94
|
+
brickWidth?: number;
|
|
95
|
+
rowHeight?: number;
|
|
96
|
+
};
|
|
97
|
+
/** The renderer's brick texture (fac output, the mortar mask). Preview: a grid. */
|
|
98
|
+
declare function bakeryBrickTexture(coord?: Coord, options?: BrickTextureOptions): FloatNode;
|
|
99
|
+
/** White noise, a hash of the coordinate. Preview: cell noise. */
|
|
100
|
+
declare function bakeryWhiteNoise(coord?: Coord, options?: {
|
|
101
|
+
dimensions?: 2 | 3 | 4;
|
|
102
|
+
}): FloatNode;
|
|
103
|
+
/**
|
|
104
|
+
* Traced ambient occlusion. The browser has no rays, so the preview is plain
|
|
105
|
+
* white, which is exactly what an unoccluded surface reads as. The render
|
|
106
|
+
* traces the real thing.
|
|
107
|
+
*/
|
|
108
|
+
declare function bakeryAmbientOcclusion(options?: {
|
|
109
|
+
distance?: number;
|
|
110
|
+
samples?: number;
|
|
111
|
+
onlyLocal?: boolean;
|
|
112
|
+
}): FloatNode;
|
|
113
|
+
/**
|
|
114
|
+
* Shade-time edge rounding. Preview: the unrounded normal, so geometry looks
|
|
115
|
+
* crisp in the viewport and gets the worn edge in the render.
|
|
116
|
+
*/
|
|
117
|
+
declare function bakeryBevel(options?: {
|
|
118
|
+
radius?: number;
|
|
119
|
+
samples?: number;
|
|
120
|
+
}): Vec3Node;
|
|
121
|
+
/** Surface curvature: concave 0, flat 0.5, convex 1. Preview: flat 0.5. */
|
|
122
|
+
declare function bakeryPointiness(): FloatNode;
|
|
123
|
+
/** 1 on triangle edges. Preview: 0, no wires in the viewport. */
|
|
124
|
+
declare function bakeryWireframe(options?: {
|
|
125
|
+
size?: number;
|
|
126
|
+
pixelSize?: boolean;
|
|
127
|
+
}): FloatNode;
|
|
128
|
+
/** Physically correct fresnel. Preview: Schlick's approximation, close enough
|
|
129
|
+
* that the viewport and the render agree on where the rim light lives. */
|
|
130
|
+
declare function bakeryFresnel(ior?: number): FloatNode;
|
|
131
|
+
/** A facing/fresnel layer weight. Preview: the facing term it is built from. */
|
|
132
|
+
declare function bakeryLayerWeight(blend?: number, options?: {
|
|
133
|
+
output?: 'FRESNEL' | 'FACING';
|
|
134
|
+
}): FloatNode;
|
|
135
|
+
type ColorRampStop = {
|
|
136
|
+
position: number;
|
|
137
|
+
color: [number, number, number] | [number, number, number, number];
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* A color ramp. Preview: the same ramp, rebuilt from mix chains; for LINEAR
|
|
141
|
+
* and CONSTANT interpolation the viewport matches the render.
|
|
142
|
+
*/
|
|
143
|
+
declare function bakeryColorRamp(fac: TSLExpr, stops: ColorRampStop[], options?: {
|
|
144
|
+
interpolation?: 'LINEAR' | 'CONSTANT';
|
|
145
|
+
}): Vec4Node;
|
|
146
|
+
/** Blackbody temperature to color. Preview: a two-anchor tint, warm below
|
|
147
|
+
* and cool above. */
|
|
148
|
+
declare function bakeryBlackbody(kelvin?: number): Vec3Node;
|
|
149
|
+
/** A gamma curve. The preview is the same math. */
|
|
150
|
+
declare function bakeryGamma(color: TSLExpr, gamma?: number): Vec3Node;
|
|
151
|
+
/** Brightness/contrast. Preview: the same formula the render uses. */
|
|
152
|
+
declare function bakeryBrightnessContrast(color: TSLExpr, options?: {
|
|
153
|
+
brightness?: number;
|
|
154
|
+
contrast?: number;
|
|
155
|
+
}): Vec3Node;
|
|
156
|
+
/** Hue/saturation/value. Preview: value and saturation only; a hue rotation
|
|
157
|
+
* in the viewport is not worth a hand-rolled HSV conversion. */
|
|
158
|
+
declare function bakeryHueSaturation(color: TSLExpr, options?: {
|
|
159
|
+
hue?: number;
|
|
160
|
+
saturation?: number;
|
|
161
|
+
value?: number;
|
|
162
|
+
fac?: number;
|
|
163
|
+
}): Vec3Node;
|
|
164
|
+
|
|
165
|
+
export { type BrickTextureOptions, type ColorRampStop, type GradientTextureOptions, type GradientType, type MagicTextureOptions, type NoiseTextureOptions, type VoronoiTextureOptions, type WaveTextureOptions, bakeryAmbientOcclusion, bakeryBevel, bakeryBlackbody, bakeryBrickTexture, bakeryBrightnessContrast, bakeryColorRamp, bakeryFresnel, bakeryGamma, bakeryGradientTexture, bakeryHueSaturation, bakeryLayerWeight, bakeryMagicTexture, bakeryNoiseTexture, bakeryPointiness, bakeryVoronoiTexture, bakeryWaveTexture, bakeryWhiteNoise, bakeryWireframe };
|