@motionscript/browser 0.1.0-alpha.7 → 0.1.0-alpha.8

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.
Files changed (148) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/NOTICE +15 -14
  3. package/README.md +66 -43
  4. package/dist/asset-bytes.d.ts +7 -4
  5. package/dist/asset-bytes.d.ts.map +1 -1
  6. package/dist/asset-bytes.js +9 -4
  7. package/dist/asset-bytes.js.map +1 -1
  8. package/dist/audio/bus-graph.d.ts.map +1 -1
  9. package/dist/audio/bus-graph.js +15 -6
  10. package/dist/audio/bus-graph.js.map +1 -1
  11. package/dist/audio/effect-graph.d.ts +62 -0
  12. package/dist/audio/effect-graph.d.ts.map +1 -0
  13. package/dist/audio/effect-graph.js +234 -0
  14. package/dist/audio/effect-graph.js.map +1 -0
  15. package/dist/audio/index.d.ts.map +1 -1
  16. package/dist/audio/index.js +2 -3
  17. package/dist/audio/index.js.map +1 -1
  18. package/dist/audio/mixer.d.ts +4 -4
  19. package/dist/audio/mixer.d.ts.map +1 -1
  20. package/dist/audio/mixer.js +18 -38
  21. package/dist/audio/mixer.js.map +1 -1
  22. package/dist/audio/player.d.ts +12 -6
  23. package/dist/audio/player.d.ts.map +1 -1
  24. package/dist/audio/player.js +49 -62
  25. package/dist/audio/player.js.map +1 -1
  26. package/dist/browser/THIRD_PARTY_LICENSES.txt +379 -27
  27. package/dist/browser/audio.js +1 -1
  28. package/dist/browser/chunks/chunk-IILVN4SK.js +2 -0
  29. package/dist/browser/chunks/chunk-IILVN4SK.js.map +7 -0
  30. package/dist/browser/chunks/chunk-KLXMRKTL.js +2 -0
  31. package/dist/browser/chunks/chunk-L5UFQACH.js +163 -0
  32. package/dist/browser/chunks/chunk-L5UFQACH.js.map +7 -0
  33. package/dist/browser/chunks/mediabunny-mp3-encoder-ZCIHVVTJ.js +14 -0
  34. package/dist/browser/chunks/mediabunny-mp3-encoder-ZCIHVVTJ.js.map +7 -0
  35. package/dist/browser/index.js +51 -1343
  36. package/dist/browser/index.js.map +4 -4
  37. package/dist/browser/manifest.json +8 -8
  38. package/dist/browser/ms_renderer_bg.wasm +0 -0
  39. package/dist/canvaskit-renderer.d.ts +8 -0
  40. package/dist/canvaskit-renderer.d.ts.map +1 -0
  41. package/dist/canvaskit-renderer.js +23 -0
  42. package/dist/canvaskit-renderer.js.map +1 -0
  43. package/dist/concat.d.ts +33 -7
  44. package/dist/concat.d.ts.map +1 -1
  45. package/dist/concat.js +117 -72
  46. package/dist/concat.js.map +1 -1
  47. package/dist/deliver.d.ts +10 -0
  48. package/dist/deliver.d.ts.map +1 -0
  49. package/dist/deliver.js +22 -0
  50. package/dist/deliver.js.map +1 -0
  51. package/dist/dynamic-nodes.d.ts.map +1 -1
  52. package/dist/dynamic-nodes.js +2 -3
  53. package/dist/dynamic-nodes.js.map +1 -1
  54. package/dist/engine.d.ts +37 -9
  55. package/dist/engine.d.ts.map +1 -1
  56. package/dist/engine.js +41 -32
  57. package/dist/engine.js.map +1 -1
  58. package/dist/export-audio.d.ts +36 -0
  59. package/dist/export-audio.d.ts.map +1 -0
  60. package/dist/export-audio.js +56 -0
  61. package/dist/export-audio.js.map +1 -0
  62. package/dist/exporter.d.ts +125 -14
  63. package/dist/exporter.d.ts.map +1 -1
  64. package/dist/exporter.js +133 -89
  65. package/dist/exporter.js.map +1 -1
  66. package/dist/formats.d.ts +36 -0
  67. package/dist/formats.d.ts.map +1 -0
  68. package/dist/formats.js +83 -0
  69. package/dist/formats.js.map +1 -0
  70. package/dist/frame-loop.d.ts +190 -0
  71. package/dist/frame-loop.d.ts.map +1 -0
  72. package/dist/frame-loop.js +304 -0
  73. package/dist/frame-loop.js.map +1 -0
  74. package/dist/getter.d.ts +4 -1
  75. package/dist/getter.d.ts.map +1 -1
  76. package/dist/getter.js +7 -6
  77. package/dist/getter.js.map +1 -1
  78. package/dist/index.d.ts +12 -11
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +17 -21
  81. package/dist/index.js.map +1 -1
  82. package/dist/mix-timeline-audio.d.ts +5 -5
  83. package/dist/mix-timeline-audio.d.ts.map +1 -1
  84. package/dist/mix-timeline-audio.js +10 -12
  85. package/dist/mix-timeline-audio.js.map +1 -1
  86. package/dist/parallel/export-parallel.d.ts +32 -0
  87. package/dist/parallel/export-parallel.d.ts.map +1 -0
  88. package/dist/parallel/export-parallel.js +114 -0
  89. package/dist/parallel/export-parallel.js.map +1 -0
  90. package/dist/parallel/governor.d.ts +57 -0
  91. package/dist/parallel/governor.d.ts.map +1 -0
  92. package/dist/parallel/governor.js +79 -0
  93. package/dist/parallel/governor.js.map +1 -0
  94. package/dist/parallel/lane.d.ts +45 -0
  95. package/dist/parallel/lane.d.ts.map +1 -0
  96. package/dist/parallel/lane.js +2 -0
  97. package/dist/parallel/lane.js.map +1 -0
  98. package/dist/parallel/lanes.d.ts +20 -0
  99. package/dist/parallel/lanes.d.ts.map +1 -0
  100. package/dist/parallel/lanes.js +152 -0
  101. package/dist/parallel/lanes.js.map +1 -0
  102. package/dist/parallel/plan.d.ts +23 -0
  103. package/dist/parallel/plan.d.ts.map +1 -0
  104. package/dist/parallel/plan.js +30 -0
  105. package/dist/parallel/plan.js.map +1 -0
  106. package/dist/parallel/run-lanes.d.ts +38 -0
  107. package/dist/parallel/run-lanes.d.ts.map +1 -0
  108. package/dist/parallel/run-lanes.js +164 -0
  109. package/dist/parallel/run-lanes.js.map +1 -0
  110. package/dist/parallel/session.d.ts +9 -0
  111. package/dist/parallel/session.d.ts.map +1 -0
  112. package/dist/parallel/session.js +79 -0
  113. package/dist/parallel/session.js.map +1 -0
  114. package/dist/render-context.d.ts +2 -0
  115. package/dist/render-context.d.ts.map +1 -1
  116. package/dist/render-context.js +8 -4
  117. package/dist/render-context.js.map +1 -1
  118. package/dist/renderer.d.ts +22 -0
  119. package/dist/renderer.d.ts.map +1 -0
  120. package/dist/renderer.js +23 -0
  121. package/dist/renderer.js.map +1 -0
  122. package/dist/screenshot.d.ts +4 -4
  123. package/dist/screenshot.d.ts.map +1 -1
  124. package/dist/screenshot.js +3 -3
  125. package/dist/screenshot.js.map +1 -1
  126. package/dist/still.d.ts +17 -19
  127. package/dist/still.d.ts.map +1 -1
  128. package/dist/still.js +27 -34
  129. package/dist/still.js.map +1 -1
  130. package/dist/ticker.d.ts +22 -8
  131. package/dist/ticker.d.ts.map +1 -1
  132. package/dist/ticker.js +55 -20
  133. package/dist/ticker.js.map +1 -1
  134. package/package.json +9 -10
  135. package/dist/browser/chunks/GLTFLoader-PGXD4V5B.js +0 -2
  136. package/dist/browser/chunks/GLTFLoader-PGXD4V5B.js.map +0 -7
  137. package/dist/browser/chunks/SkeletonUtils-XYDMTGTM.js +0 -2
  138. package/dist/browser/chunks/chunk-2MAFJWBZ.js +0 -2
  139. package/dist/browser/chunks/chunk-2MAFJWBZ.js.map +0 -7
  140. package/dist/browser/chunks/chunk-CTLBXZSU.js +0 -4119
  141. package/dist/browser/chunks/chunk-CTLBXZSU.js.map +0 -7
  142. package/dist/browser/chunks/chunk-FQ32PDIU.js +0 -2
  143. package/dist/browser/chunks/chunk-FQ32PDIU.js.map +0 -7
  144. package/dist/browser/chunks/chunk-FY3BTKY4.js +0 -2
  145. package/dist/browser/chunks/chunk-FY3BTKY4.js.map +0 -7
  146. package/dist/browser/chunks/three.module-Z4EOVUWF.js +0 -2
  147. package/dist/browser/chunks/three.module-Z4EOVUWF.js.map +0 -7
  148. /package/dist/browser/chunks/{SkeletonUtils-XYDMTGTM.js.map → chunk-KLXMRKTL.js.map} +0 -0
package/dist/ticker.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Ticker } from "@motionscript/core";
2
2
  /**
3
- * Ticker backed by a Web Audio AudioContext. Clip time advances in
4
- * lock-step with `audioContext.currentTime`, so any AudioBufferSourceNodes
5
- * scheduled on the same context play perfectly in sync.
3
+ * Ticker backed by a Web Audio AudioContext. Clip time follows
4
+ * `audioContext.currentTime` (within a few ms, see `startLoop`), so any
5
+ * AudioBufferSourceNodes scheduled on the same context play in sync.
6
6
  *
7
7
  * When paused we suspend the context (audio stops, currentTime freezes);
8
8
  * when playing we resume and a rAF loop publishes ticks at the target fps.
@@ -12,8 +12,9 @@ import { Ticker } from "@motionscript/core";
12
12
  * graph to schedule against even on scenes that contain no audio nodes.
13
13
  */
14
14
  export class WebTicker extends Ticker {
15
- context;
15
+ contextOf;
16
16
  ownsContext;
17
+ _context = null;
17
18
  fps;
18
19
  playStartCtxTime = 0;
19
20
  seekOffset = 0;
@@ -23,19 +24,31 @@ export class WebTicker extends Ticker {
23
24
  /** Bumped by every `seek`, so the loop can tell that a listener or tick callback moved the playhead under it. */
24
25
  seekEpoch = 0;
25
26
  silentSource = null;
27
+ /**
28
+ * `context` may be a getter, called on the first `play`: a session hands
29
+ * over its audio sink's, which is created on first use, so a session that
30
+ * never plays never opens the audio device.
31
+ */
26
32
  constructor(opts) {
27
33
  super();
28
- this.context = opts.context ?? new AudioContext();
29
- this.ownsContext = !opts.context;
34
+ const { context } = opts;
35
+ this.contextOf = typeof context === "function" ? context : () => context ?? new AudioContext();
36
+ this.ownsContext = !context;
30
37
  this.fps = opts.fps;
31
38
  }
32
39
  getContext() {
33
- return this.context;
40
+ this._context ??= this.contextOf();
41
+ return this._context;
42
+ }
43
+ get context() {
44
+ return this.getContext();
34
45
  }
35
46
  seek(t) {
36
47
  const clamped = Math.max(0, Math.min(t, this.duration));
37
48
  this.seekOffset = clamped;
38
- this.playStartCtxTime = this.context.currentTime;
49
+ // The clock is read only while playing; `play` anchors it afresh.
50
+ if (this.isPlaying)
51
+ this.playStartCtxTime = this.context.currentTime;
39
52
  this.seekEpoch++;
40
53
  this.setCurrentTime(clamped);
41
54
  }
@@ -71,8 +84,8 @@ export class WebTicker extends Ticker {
71
84
  this.seekOffset = this.currentTime;
72
85
  this.stopLoop();
73
86
  this.stopSilentTicker();
74
- if (this.context.state === "running") {
75
- this.context.suspend();
87
+ if (this._context?.state === "running") {
88
+ this._context.suspend();
76
89
  }
77
90
  super.pause();
78
91
  }
@@ -111,8 +124,15 @@ export class WebTicker extends Ticker {
111
124
  return { time, ended: this.reverse ? time <= 0 : time >= this.duration };
112
125
  }
113
126
  /**
114
- * Drives ticks off `requestAnimationFrame`, firing `tick()` only when a full frame interval
115
- * has elapsed — and once more at the end of the run, whether or not it landed on a boundary.
127
+ * Drives ticks off `requestAnimationFrame`, firing `tick()` when the playhead reaches another
128
+ * project frame — and once more at the end of the run, whether or not it landed on a boundary.
129
+ *
130
+ * `context.currentTime` moves in whole render quanta handed over in bursts: at 48 kHz on a
131
+ * 60 Hz display it advances 8, 10.67, 18.67 or 21.33 ms between animation frames, and a
132
+ * playhead read straight off it skipped up to a third of the frames. So the playhead advances
133
+ * by each animation frame's own interval, pulled 5% of the way to the audio clock every frame
134
+ * and snapped to it when they disagree by more than 50 ms (a stall, a seek). It stays within
135
+ * a few ms of the audio and moves one frame per frame.
116
136
  *
117
137
  * A listener may `seek` (a host looping wraps to 0 when it hears the end), so the end is
118
138
  * re-read after the listeners and the tick callbacks have run: a run that was seeked out of
@@ -121,30 +141,45 @@ export class WebTicker extends Ticker {
121
141
  startLoop() {
122
142
  if (this.rafId !== null)
123
143
  return;
124
- const frameDt = 1 / this.fps;
125
- let lastTickTime = this.currentTime;
126
- const loop = async () => {
144
+ const frameOf = (t) => Math.floor(t * this.fps + 1e-6);
145
+ let lastFrame = frameOf(this.currentTime);
146
+ let steady = null;
147
+ const steadied = (now) => {
148
+ const audio = this.position();
149
+ let time = audio.time;
150
+ if (!audio.ended && steady && steady.epoch === this.seekEpoch) {
151
+ const step = ((now - steady.at) / 1000) * this.speed;
152
+ const predicted = this.reverse ? steady.time - step : steady.time + step;
153
+ const drift = audio.time - predicted;
154
+ const next = predicted + drift * 0.05;
155
+ if (Math.abs(drift) <= 0.05 && next > 0 && next < this.duration)
156
+ time = next;
157
+ }
158
+ steady = { time, at: now, epoch: this.seekEpoch };
159
+ return { time, ended: audio.ended };
160
+ };
161
+ const loop = async (now) => {
127
162
  let epoch = this.seekEpoch;
128
- const position = this.position();
163
+ const position = steadied(now);
129
164
  let ended = position.ended;
130
165
  // This loop runs at the display's refresh rate, which on a 120 or 144Hz panel is two
131
166
  // or three times the rate anything is rendered at — so the clock advances every time
132
167
  // round (the elapsed maths in `play`/`pause` depend on it being exact) while listeners
133
168
  // hear about it once per frame. The end of the run always reports, or the final
134
169
  // position would never reach a host. See `Ticker.setCurrentTime`.
135
- const crossed = Math.abs(position.time - lastTickTime) >= frameDt;
170
+ const crossed = frameOf(position.time) !== lastFrame;
136
171
  const report = crossed || ended;
137
172
  this.setCurrentTime(position.time, report);
138
173
  const reread = () => {
139
174
  if (this.seekEpoch === epoch)
140
175
  return;
141
176
  epoch = this.seekEpoch;
142
- lastTickTime = this.currentTime;
177
+ lastFrame = frameOf(this.currentTime);
143
178
  ended = this.position().ended;
144
179
  };
145
180
  reread();
146
181
  if (report) {
147
- lastTickTime = this.currentTime;
182
+ lastFrame = frameOf(this.currentTime);
148
183
  // Await so that a slow tick (asset load on a clip
149
184
  // boundary) doesn't queue a second concurrent tick.
150
185
  await this.tick();
@@ -172,7 +207,7 @@ export class WebTicker extends Ticker {
172
207
  this.stopLoop();
173
208
  this.stopSilentTicker();
174
209
  if (this.ownsContext) {
175
- this.context.close();
210
+ void this._context?.close();
176
211
  }
177
212
  super.dispose();
178
213
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ticker.js","sourceRoot":"","sources":["../src/ticker.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ticker } from \"@motionscript/core\";\r\n\r\n/**\r\n * Ticker backed by a Web Audio AudioContext. Clip time advances in\r\n * lock-step with `audioContext.currentTime`, so any AudioBufferSourceNodes\r\n * scheduled on the same context play perfectly in sync.\r\n *\r\n * When paused we suspend the context (audio stops, currentTime freezes);\r\n * when playing we resume and a rAF loop publishes ticks at the target fps.\r\n *\r\n * A single silent buffer covering the full timeline is started on play(),\r\n * which keeps the context active and gives the browser a stable audio\r\n * graph to schedule against even on scenes that contain no audio nodes.\r\n */\r\nexport class WebTicker extends Ticker {\r\n private context: AudioContext;\r\n private ownsContext: boolean;\r\n private fps: number;\r\n\r\n private playStartCtxTime: number = 0;\r\n private seekOffset: number = 0;\r\n private speed: number = 1;\r\n private reverse: boolean = false;\r\n\r\n private rafId: number | null = null;\r\n /** Bumped by every `seek`, so the loop can tell that a listener or tick callback moved the playhead under it. */\r\n private seekEpoch: number = 0;\r\n private silentSource: AudioBufferSourceNode | null = null;\r\n\r\n constructor(opts: { context?: AudioContext; fps: number }) {\r\n super();\r\n this.context = opts.context ?? new AudioContext();\r\n this.ownsContext = !opts.context;\r\n this.fps = opts.fps;\r\n }\r\n\r\n getContext(): AudioContext {\r\n return this.context;\r\n }\r\n\r\n seek(t: number): void {\r\n const clamped = Math.max(0, Math.min(t, this.duration));\r\n this.seekOffset = clamped;\r\n this.playStartCtxTime = this.context.currentTime;\r\n this.seekEpoch++;\r\n this.setCurrentTime(clamped);\r\n }\r\n\r\n /**\r\n * Anchors elapsed-time tracking to the current `context.currentTime`, starts the silent ticker\r\n * + rAF tick loop and asks a suspended context to resume.\r\n *\r\n * Synchronous, as {@link Ticker.play} is: a `pause` or `seek` in the same turn must find a\r\n * running clock to stop. A suspended context's `currentTime` is frozen, so the clock holds at\r\n * the anchor until the resume lands — outside a user gesture that is never.\r\n */\r\n play(speed: number = 1, reverse: boolean = false): void {\r\n this.speed = speed;\r\n this.reverse = reverse;\r\n this.seekOffset = this.currentTime;\r\n\r\n if (this.context.state === \"suspended\") {\r\n this.context.resume().then(\r\n () => {\r\n // A pause that arrived while the resume was pending could not suspend it.\r\n if (!this.isPlaying && this.context.state === \"running\") void this.context.suspend();\r\n },\r\n (error) => {\r\n console.error(\"[WebTicker] The audio context could not resume:\", error);\r\n this.pause();\r\n },\r\n );\r\n }\r\n\r\n this.playStartCtxTime = this.context.currentTime;\r\n this.startSilentTicker();\r\n super.play(speed, reverse);\r\n this.startLoop();\r\n }\r\n\r\n /** Stops the tick loop and silent ticker, then suspends the context — freezing `currentTime` so elapsed-time math stays correct across the pause. */\r\n pause(): void {\r\n this.seekOffset = this.currentTime;\r\n this.stopLoop();\r\n this.stopSilentTicker();\r\n if (this.context.state === \"running\") {\r\n this.context.suspend();\r\n }\r\n super.pause();\r\n }\r\n\r\n /** (Re)starts the silent buffer source covering the remaining timeline — keeps the AudioContext active and gives a stable graph for other AudioBufferSourceNodes to schedule against, per the class-level note. */\r\n private startSilentTicker(): void {\r\n this.stopSilentTicker();\r\n const remaining = Math.max(0.001, this.duration - this.seekOffset);\r\n const sampleRate = this.context.sampleRate;\r\n const buffer = this.context.createBuffer(1, Math.ceil(remaining * sampleRate), sampleRate);\r\n const source = this.context.createBufferSource();\r\n source.buffer = buffer;\r\n source.connect(this.context.destination);\r\n source.start();\r\n this.silentSource = source;\r\n }\r\n\r\n private stopSilentTicker(): void {\r\n if (this.silentSource) {\r\n try {\r\n this.silentSource.onended = null;\r\n this.silentSource.stop();\r\n this.silentSource.disconnect();\r\n } catch {\r\n // already stopped\r\n }\r\n this.silentSource = null;\r\n }\r\n }\r\n\r\n /** Where the playhead is now, from elapsed `context.currentTime` (so it stays sample-accurate), and whether that is the end of the run. */\r\n private position(): { time: number; ended: boolean } {\r\n const elapsed = (this.context.currentTime - this.playStartCtxTime) * this.speed;\r\n const next = this.reverse\r\n ? this.seekOffset - elapsed\r\n : this.seekOffset + elapsed;\r\n const time = Math.max(0, Math.min(next, this.duration));\r\n return { time, ended: this.reverse ? time <= 0 : time >= this.duration };\r\n }\r\n\r\n /**\r\n * Drives ticks off `requestAnimationFrame`, firing `tick()` only when a full frame interval\r\n * has elapsed — and once more at the end of the run, whether or not it landed on a boundary.\r\n *\r\n * A listener may `seek` (a host looping wraps to 0 when it hears the end), so the end is\r\n * re-read after the listeners and the tick callbacks have run: a run that was seeked out of\r\n * its end carries on, and one that was not pauses itself.\r\n */\r\n private startLoop(): void {\r\n if (this.rafId !== null) return;\r\n const frameDt = 1 / this.fps;\r\n let lastTickTime = this.currentTime;\r\n\r\n const loop = async () => {\r\n let epoch = this.seekEpoch;\r\n const position = this.position();\r\n let ended = position.ended;\r\n // This loop runs at the display's refresh rate, which on a 120 or 144Hz panel is two\r\n // or three times the rate anything is rendered at — so the clock advances every time\r\n // round (the elapsed maths in `play`/`pause` depend on it being exact) while listeners\r\n // hear about it once per frame. The end of the run always reports, or the final\r\n // position would never reach a host. See `Ticker.setCurrentTime`.\r\n const crossed = Math.abs(position.time - lastTickTime) >= frameDt;\r\n const report = crossed || ended;\r\n this.setCurrentTime(position.time, report);\r\n\r\n const reread = () => {\r\n if (this.seekEpoch === epoch) return;\r\n epoch = this.seekEpoch;\r\n lastTickTime = this.currentTime;\r\n ended = this.position().ended;\r\n };\r\n reread();\r\n\r\n if (report) {\r\n lastTickTime = this.currentTime;\r\n // Await so that a slow tick (asset load on a clip\r\n // boundary) doesn't queue a second concurrent tick.\r\n await this.tick();\r\n reread();\r\n }\r\n\r\n // If pause() cleared the rafId while we were awaiting tick(),\r\n // do not resume the loop.\r\n if (this.rafId === null) return;\r\n if (ended) {\r\n this.pause();\r\n return;\r\n }\r\n this.rafId = requestAnimationFrame(loop);\r\n };\r\n\r\n this.rafId = requestAnimationFrame(loop);\r\n }\r\n\r\n private stopLoop(): void {\r\n if (this.rafId !== null) {\r\n cancelAnimationFrame(this.rafId);\r\n this.rafId = null;\r\n }\r\n }\r\n\r\n dispose(): void {\r\n this.stopLoop();\r\n this.stopSilentTicker();\r\n if (this.ownsContext) {\r\n this.context.close();\r\n }\r\n super.dispose();\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"ticker.js","sourceRoot":"","sources":["../src/ticker.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ticker } from \"@motionscript/core\";\r\n\r\n/**\r\n * Ticker backed by a Web Audio AudioContext. Clip time follows\r\n * `audioContext.currentTime` (within a few ms, see `startLoop`), so any\r\n * AudioBufferSourceNodes scheduled on the same context play in sync.\r\n *\r\n * When paused we suspend the context (audio stops, currentTime freezes);\r\n * when playing we resume and a rAF loop publishes ticks at the target fps.\r\n *\r\n * A single silent buffer covering the full timeline is started on play(),\r\n * which keeps the context active and gives the browser a stable audio\r\n * graph to schedule against even on scenes that contain no audio nodes.\r\n */\r\nexport class WebTicker extends Ticker {\r\n private readonly contextOf: () => AudioContext;\r\n private readonly ownsContext: boolean;\r\n private _context: AudioContext | null = null;\r\n private fps: number;\r\n\r\n private playStartCtxTime: number = 0;\r\n private seekOffset: number = 0;\r\n private speed: number = 1;\r\n private reverse: boolean = false;\r\n\r\n private rafId: number | null = null;\r\n /** Bumped by every `seek`, so the loop can tell that a listener or tick callback moved the playhead under it. */\r\n private seekEpoch: number = 0;\r\n private silentSource: AudioBufferSourceNode | null = null;\r\n\r\n /**\r\n * `context` may be a getter, called on the first `play`: a session hands\r\n * over its audio sink's, which is created on first use, so a session that\r\n * never plays never opens the audio device.\r\n */\r\n constructor(opts: { context?: AudioContext | (() => AudioContext); fps: number }) {\r\n super();\r\n const { context } = opts;\r\n this.contextOf = typeof context === \"function\" ? context : () => context ?? new AudioContext();\r\n this.ownsContext = !context;\r\n this.fps = opts.fps;\r\n }\r\n\r\n getContext(): AudioContext {\r\n this._context ??= this.contextOf();\r\n return this._context;\r\n }\r\n\r\n private get context(): AudioContext {\r\n return this.getContext();\r\n }\r\n\r\n seek(t: number): void {\r\n const clamped = Math.max(0, Math.min(t, this.duration));\r\n this.seekOffset = clamped;\r\n // The clock is read only while playing; `play` anchors it afresh.\r\n if (this.isPlaying) this.playStartCtxTime = this.context.currentTime;\r\n this.seekEpoch++;\r\n this.setCurrentTime(clamped);\r\n }\r\n\r\n /**\r\n * Anchors elapsed-time tracking to the current `context.currentTime`, starts the silent ticker\r\n * + rAF tick loop and asks a suspended context to resume.\r\n *\r\n * Synchronous, as {@link Ticker.play} is: a `pause` or `seek` in the same turn must find a\r\n * running clock to stop. A suspended context's `currentTime` is frozen, so the clock holds at\r\n * the anchor until the resume lands — outside a user gesture that is never.\r\n */\r\n play(speed: number = 1, reverse: boolean = false): void {\r\n this.speed = speed;\r\n this.reverse = reverse;\r\n this.seekOffset = this.currentTime;\r\n\r\n if (this.context.state === \"suspended\") {\r\n this.context.resume().then(\r\n () => {\r\n // A pause that arrived while the resume was pending could not suspend it.\r\n if (!this.isPlaying && this.context.state === \"running\") void this.context.suspend();\r\n },\r\n (error) => {\r\n console.error(\"[WebTicker] The audio context could not resume:\", error);\r\n this.pause();\r\n },\r\n );\r\n }\r\n\r\n this.playStartCtxTime = this.context.currentTime;\r\n this.startSilentTicker();\r\n super.play(speed, reverse);\r\n this.startLoop();\r\n }\r\n\r\n /** Stops the tick loop and silent ticker, then suspends the context — freezing `currentTime` so elapsed-time math stays correct across the pause. */\r\n pause(): void {\r\n this.seekOffset = this.currentTime;\r\n this.stopLoop();\r\n this.stopSilentTicker();\r\n if (this._context?.state === \"running\") {\r\n this._context.suspend();\r\n }\r\n super.pause();\r\n }\r\n\r\n /** (Re)starts the silent buffer source covering the remaining timeline — keeps the AudioContext active and gives a stable graph for other AudioBufferSourceNodes to schedule against, per the class-level note. */\r\n private startSilentTicker(): void {\r\n this.stopSilentTicker();\r\n const remaining = Math.max(0.001, this.duration - this.seekOffset);\r\n const sampleRate = this.context.sampleRate;\r\n const buffer = this.context.createBuffer(1, Math.ceil(remaining * sampleRate), sampleRate);\r\n const source = this.context.createBufferSource();\r\n source.buffer = buffer;\r\n source.connect(this.context.destination);\r\n source.start();\r\n this.silentSource = source;\r\n }\r\n\r\n private stopSilentTicker(): void {\r\n if (this.silentSource) {\r\n try {\r\n this.silentSource.onended = null;\r\n this.silentSource.stop();\r\n this.silentSource.disconnect();\r\n } catch {\r\n // already stopped\r\n }\r\n this.silentSource = null;\r\n }\r\n }\r\n\r\n /** Where the playhead is now, from elapsed `context.currentTime` (so it stays sample-accurate), and whether that is the end of the run. */\r\n private position(): { time: number; ended: boolean } {\r\n const elapsed = (this.context.currentTime - this.playStartCtxTime) * this.speed;\r\n const next = this.reverse\r\n ? this.seekOffset - elapsed\r\n : this.seekOffset + elapsed;\r\n const time = Math.max(0, Math.min(next, this.duration));\r\n return { time, ended: this.reverse ? time <= 0 : time >= this.duration };\r\n }\r\n\r\n /**\r\n * Drives ticks off `requestAnimationFrame`, firing `tick()` when the playhead reaches another\r\n * project frame — and once more at the end of the run, whether or not it landed on a boundary.\r\n *\r\n * `context.currentTime` moves in whole render quanta handed over in bursts: at 48 kHz on a\r\n * 60 Hz display it advances 8, 10.67, 18.67 or 21.33 ms between animation frames, and a\r\n * playhead read straight off it skipped up to a third of the frames. So the playhead advances\r\n * by each animation frame's own interval, pulled 5% of the way to the audio clock every frame\r\n * and snapped to it when they disagree by more than 50 ms (a stall, a seek). It stays within\r\n * a few ms of the audio and moves one frame per frame.\r\n *\r\n * A listener may `seek` (a host looping wraps to 0 when it hears the end), so the end is\r\n * re-read after the listeners and the tick callbacks have run: a run that was seeked out of\r\n * its end carries on, and one that was not pauses itself.\r\n */\r\n private startLoop(): void {\r\n if (this.rafId !== null) return;\r\n const frameOf = (t: number) => Math.floor(t * this.fps + 1e-6);\r\n let lastFrame = frameOf(this.currentTime);\r\n let steady: { time: number; at: number; epoch: number } | null = null;\r\n const steadied = (now: number): { time: number; ended: boolean } => {\r\n const audio = this.position();\r\n let time = audio.time;\r\n if (!audio.ended && steady && steady.epoch === this.seekEpoch) {\r\n const step = ((now - steady.at) / 1000) * this.speed;\r\n const predicted = this.reverse ? steady.time - step : steady.time + step;\r\n const drift = audio.time - predicted;\r\n const next = predicted + drift * 0.05;\r\n if (Math.abs(drift) <= 0.05 && next > 0 && next < this.duration) time = next;\r\n }\r\n steady = { time, at: now, epoch: this.seekEpoch };\r\n return { time, ended: audio.ended };\r\n };\r\n\r\n const loop = async (now: number) => {\r\n let epoch = this.seekEpoch;\r\n const position = steadied(now);\r\n let ended = position.ended;\r\n // This loop runs at the display's refresh rate, which on a 120 or 144Hz panel is two\r\n // or three times the rate anything is rendered at — so the clock advances every time\r\n // round (the elapsed maths in `play`/`pause` depend on it being exact) while listeners\r\n // hear about it once per frame. The end of the run always reports, or the final\r\n // position would never reach a host. See `Ticker.setCurrentTime`.\r\n const crossed = frameOf(position.time) !== lastFrame;\r\n const report = crossed || ended;\r\n this.setCurrentTime(position.time, report);\r\n\r\n const reread = () => {\r\n if (this.seekEpoch === epoch) return;\r\n epoch = this.seekEpoch;\r\n lastFrame = frameOf(this.currentTime);\r\n ended = this.position().ended;\r\n };\r\n reread();\r\n\r\n if (report) {\r\n lastFrame = frameOf(this.currentTime);\r\n // Await so that a slow tick (asset load on a clip\r\n // boundary) doesn't queue a second concurrent tick.\r\n await this.tick();\r\n reread();\r\n }\r\n\r\n // If pause() cleared the rafId while we were awaiting tick(),\r\n // do not resume the loop.\r\n if (this.rafId === null) return;\r\n if (ended) {\r\n this.pause();\r\n return;\r\n }\r\n this.rafId = requestAnimationFrame(loop);\r\n };\r\n\r\n this.rafId = requestAnimationFrame(loop);\r\n }\r\n\r\n private stopLoop(): void {\r\n if (this.rafId !== null) {\r\n cancelAnimationFrame(this.rafId);\r\n this.rafId = null;\r\n }\r\n }\r\n\r\n dispose(): void {\r\n this.stopLoop();\r\n this.stopSilentTicker();\r\n if (this.ownsContext) {\r\n void this._context?.close();\r\n }\r\n super.dispose();\r\n }\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@motionscript/browser",
3
- "version": "0.1.0-alpha.7",
3
+ "version": "0.1.0-alpha.8",
4
4
  "type": "module",
5
- "description": "Browser rendering backend for Motion Script, powered by Skia/CanvasKit.",
5
+ "description": "Motion Script in the browser: WebGPU rendering through @motionscript/renderer, video and still export, audio playback.",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/motionscript-dev/motionscript.git",
@@ -15,6 +15,7 @@
15
15
  "sideEffects": false,
16
16
  "keywords": [],
17
17
  "license": "Apache-2.0",
18
+ "author": "Motionscript",
18
19
  "publishConfig": {
19
20
  "access": "public"
20
21
  },
@@ -37,11 +38,9 @@
37
38
  }
38
39
  },
39
40
  "peerDependencies": {
40
- "@motionscript/canvaskit": "^0.1.0-alpha.7",
41
- "@motionscript/core": "^0.1.0-alpha.7"
41
+ "@motionscript/core": "^0.1.0-alpha.8"
42
42
  },
43
43
  "devDependencies": {
44
- "@types/three": "0.185.1",
45
44
  "@vitest/browser": "4.1.7",
46
45
  "@vitest/browser-playwright": "^4.1.7",
47
46
  "concurrently": "^9.0.0",
@@ -50,22 +49,22 @@
50
49
  "eslint-plugin-import-x": "^4.16.1",
51
50
  "playwright": "^1.60.0",
52
51
  "rolldown": "1.0.3",
53
- "three": "0.185.1",
54
52
  "tsc-alias": "^1.8.16",
55
53
  "typescript": "^6.0.3",
56
54
  "typescript-eslint": "^8.48.0",
57
55
  "vitest": "^4.1.5",
58
- "@motionscript/canvaskit": "0.1.0-alpha.7",
59
- "@motionscript/core": "0.1.0-alpha.7"
56
+ "@motionscript/core": "0.1.0-alpha.8"
60
57
  },
61
58
  "dependencies": {
59
+ "@mediabunny/mp3-encoder": "^1.49.0",
62
60
  "esbuild-wasm": "^0.28.1",
63
61
  "mediabunny": "^1.40.1",
64
- "@motionscript/skia-render": "^0.1.0-alpha.7"
62
+ "@motionscript/renderer": "^0.1.0-alpha.8"
65
63
  },
66
64
  "motionscriptBrowser": {
67
65
  "assets": {
68
- "esbuild.wasm": "esbuild-wasm/esbuild.wasm"
66
+ "esbuild.wasm": "esbuild-wasm/esbuild.wasm",
67
+ "ms_renderer_bg.wasm": "@motionscript/renderer/ms_renderer_bg.wasm"
69
68
  }
70
69
  },
71
70
  "scripts": {
@@ -1,2 +0,0 @@
1
- import{c as bt}from"./chunk-FQ32PDIU.js";import{$d as rt,$f as Rt,Ed as tt,Gd as G,Gf as de,If as he,Kb as Qe,Kf as pe,Lb as te,Lf as mt,Pf as gt,Qd as nt,Qf as me,Rd as ae,Sd as ce,Tb as $e,Td as ue,Ub as V,Ud as Y,Vb as F,Wf as Tt,Zd as P,_d as st,_f as At,ae as it,ag as yt,bc as k,bg as Et,cc as M,ce as ot,cg as _t,dd as et,de as le,ed as ne,ee as at,fd as se,gd as N,id as re,ig as B,je as ct,ke as ut,l as Xe,ld as ie,le as lt,me as ft,mg as xt,n as Ve,ne as dt,nf as fe,oa as j,oe as ht,of as S,pa as qe,qa as Ye,ra as $,sa as We,td as z,ua as Ze,wa as X,wd as oe,xa as Je,xd as q,yf as pt,yg as wt,za as ee,zd as O}from"./chunk-CTLBXZSU.js";import"./chunk-FY3BTKY4.js";function ge(f,t){if(t===$e)return console.warn("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles."),f;if(t===F||t===V){let e=f.getIndex();if(e===null){let i=[],a=f.getAttribute("position");if(a!==void 0){for(let o=0;o<a.count;o++)i.push(o);f.setIndex(i),e=f.getIndex()}else return console.error("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Undefined position attribute. Processing not possible."),f}let s=e.count-2,n=[];if(t===F)for(let i=1;i<=s;i++)n.push(e.getX(0)),n.push(e.getX(i)),n.push(e.getX(i+1));else for(let i=0;i<s;i++)i%2===0?(n.push(e.getX(i)),n.push(e.getX(i+1)),n.push(e.getX(i+2))):(n.push(e.getX(i+2)),n.push(e.getX(i+1)),n.push(e.getX(i)));n.length/3!==s&&console.error("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unable to generate correct amount of triangles.");let r=f.clone();return r.setIndex(n),r.clearGroups(),r}else return console.error("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unknown draw mode:",t),f}var St=class extends gt{constructor(t){super(t),this.dracoLoader=null,this.ktx2Loader=null,this.meshoptDecoder=null,this.pluginCallbacks=[],this.register(function(e){return new xe(e)}),this.register(function(e){return new we(e)}),this.register(function(e){return new ve(e)}),this.register(function(e){return new He(e)}),this.register(function(e){return new ke(e)}),this.register(function(e){return new Se(e)}),this.register(function(e){return new Me(e)}),this.register(function(e){return new Ie(e)}),this.register(function(e){return new Ne(e)}),this.register(function(e){return new _e(e)}),this.register(function(e){return new Le(e)}),this.register(function(e){return new be(e)}),this.register(function(e){return new Ce(e)}),this.register(function(e){return new Oe(e)}),this.register(function(e){return new ye(e)}),this.register(function(e){return new W(e,g.EXT_MESHOPT_COMPRESSION)}),this.register(function(e){return new W(e,g.KHR_MESHOPT_COMPRESSION)}),this.register(function(e){return new Pe(e)})}load(t,e,s,n){let r=this,i;if(this.resourcePath!=="")i=this.resourcePath;else if(this.path!==""){let c=B.extractUrlBase(t);i=B.resolveURL(c,this.path)}else i=B.extractUrlBase(t);this.manager.itemStart(t);let a=function(c){n?n(c):console.error(c),r.manager.itemError(t),r.manager.itemEnd(t)},o=new me(this.manager);o.setPath(this.path),o.setResponseType("arraybuffer"),o.setRequestHeader(this.requestHeader),o.setWithCredentials(this.withCredentials),o.load(t,function(c){try{r.parse(c,i,function(u){e(u),r.manager.itemEnd(t)},a)}catch(u){a(u)}},s,a)}setDRACOLoader(t){return this.dracoLoader=t,this}setKTX2Loader(t){return this.ktx2Loader=t,this}setMeshoptDecoder(t){return this.meshoptDecoder=t,this}register(t){return this.pluginCallbacks.indexOf(t)===-1&&this.pluginCallbacks.push(t),this}unregister(t){return this.pluginCallbacks.indexOf(t)!==-1&&this.pluginCallbacks.splice(this.pluginCallbacks.indexOf(t),1),this}parse(t,e,s,n){let r,i={},a={},o=new TextDecoder;if(typeof t=="string")r=JSON.parse(t);else if(t instanceof ArrayBuffer)if(o.decode(new Uint8Array(t,0,4))===Ot){try{i[g.KHR_BINARY_GLTF]=new Be(t)}catch(l){n&&n(l);return}r=JSON.parse(i[g.KHR_BINARY_GLTF].content)}else r=JSON.parse(o.decode(t));else r=t;if(r.asset===void 0||r.asset.version[0]<2){n&&n(new Error("THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported."));return}let c=new je(r,{path:e||this.resourcePath||"",crossOrigin:this.crossOrigin,requestHeader:this.requestHeader,manager:this.manager,ktx2Loader:this.ktx2Loader,meshoptDecoder:this.meshoptDecoder});c.fileLoader.setRequestHeader(this.requestHeader);for(let u=0;u<this.pluginCallbacks.length;u++){let l=this.pluginCallbacks[u](c);l.name||console.error("THREE.GLTFLoader: Invalid plugin found: missing name"),a[l.name]=l,i[l.name]=!0}if(r.extensionsUsed)for(let u=0;u<r.extensionsUsed.length;++u){let l=r.extensionsUsed[u],d=r.extensionsRequired||[];switch(l){case g.KHR_MATERIALS_UNLIT:i[l]=new Ee;break;case g.KHR_DRACO_MESH_COMPRESSION:i[l]=new De(r,this.dracoLoader);break;case g.KHR_TEXTURE_TRANSFORM:i[l]=new Ue;break;case g.KHR_MESH_QUANTIZATION:i[l]=new Fe;break;default:d.indexOf(l)>=0&&a[l]===void 0&&console.warn('THREE.GLTFLoader: Unknown extension "'+l+'".')}}c.setExtensions(i),c.setPlugins(a),c.parse(s,n)}parseAsync(t,e){let s=this;return new Promise(function(n,r){s.parse(t,e,n,r)})}};function Ct(){let f={};return{get:function(t){return f[t]},add:function(t,e){f[t]=e},remove:function(t){delete f[t]},removeAll:function(){f={}}}}function _(f,t,e){let s=f.json.materials[t];return s.extensions&&s.extensions[e]?s.extensions[e]:null}var g={KHR_BINARY_GLTF:"KHR_binary_glTF",KHR_DRACO_MESH_COMPRESSION:"KHR_draco_mesh_compression",KHR_LIGHTS_PUNCTUAL:"KHR_lights_punctual",KHR_MATERIALS_CLEARCOAT:"KHR_materials_clearcoat",KHR_MATERIALS_DISPERSION:"KHR_materials_dispersion",KHR_MATERIALS_IOR:"KHR_materials_ior",KHR_MATERIALS_SHEEN:"KHR_materials_sheen",KHR_MATERIALS_SPECULAR:"KHR_materials_specular",KHR_MATERIALS_TRANSMISSION:"KHR_materials_transmission",KHR_MATERIALS_IRIDESCENCE:"KHR_materials_iridescence",KHR_MATERIALS_ANISOTROPY:"KHR_materials_anisotropy",KHR_MATERIALS_UNLIT:"KHR_materials_unlit",KHR_MATERIALS_VOLUME:"KHR_materials_volume",KHR_TEXTURE_BASISU:"KHR_texture_basisu",KHR_TEXTURE_TRANSFORM:"KHR_texture_transform",KHR_MESH_QUANTIZATION:"KHR_mesh_quantization",KHR_MATERIALS_EMISSIVE_STRENGTH:"KHR_materials_emissive_strength",EXT_MATERIALS_BUMP:"EXT_materials_bump",EXT_TEXTURE_WEBP:"EXT_texture_webp",EXT_TEXTURE_AVIF:"EXT_texture_avif",EXT_MESHOPT_COMPRESSION:"EXT_meshopt_compression",KHR_MESHOPT_COMPRESSION:"KHR_meshopt_compression",EXT_MESH_GPU_INSTANCING:"EXT_mesh_gpu_instancing"},ye=class{constructor(t){this.parser=t,this.name=g.KHR_LIGHTS_PUNCTUAL,this.cache={refs:{},uses:{}}}_markDefs(){let t=this.parser,e=this.parser.json.nodes||[];for(let s=0,n=e.length;s<n;s++){let r=e[s];r.extensions&&r.extensions[this.name]&&r.extensions[this.name].light!==void 0&&t._addNodeRef(this.cache,r.extensions[this.name].light)}}_loadLight(t){let e=this.parser,s="light:"+t,n=e.cache.get(s);if(n)return n;let r=e.json,o=((r.extensions&&r.extensions[this.name]||{}).lights||[])[t],c,u=new O(16777215);o.color!==void 0&&u.setRGB(o.color[0],o.color[1],o.color[2],M);let l=o.range!==void 0?o.range:0;switch(o.type){case"directional":c=new _t(u),c.target.position.set(0,0,-1),c.add(c.target);break;case"point":c=new yt(u),c.distance=l;break;case"spot":c=new Rt(u),c.distance=l,o.spot=o.spot||{},o.spot.innerConeAngle=o.spot.innerConeAngle!==void 0?o.spot.innerConeAngle:0,o.spot.outerConeAngle=o.spot.outerConeAngle!==void 0?o.spot.outerConeAngle:Math.PI/4,c.angle=o.spot.outerConeAngle,c.penumbra=1-o.spot.innerConeAngle/o.spot.outerConeAngle,c.target.position.set(0,0,-1),c.add(c.target);break;default:throw new Error("THREE.GLTFLoader: Unexpected light type: "+o.type)}return c.position.set(0,0,0),I(c,o),o.intensity!==void 0&&(c.intensity=o.intensity),c.name=e.createUniqueName(o.name||"light_"+t),n=Promise.resolve(c),e.cache.add(s,n),n}getDependency(t,e){if(t==="light")return this._loadLight(e)}createNodeAttachment(t){let e=this,s=this.parser,r=s.json.nodes[t],a=(r.extensions&&r.extensions[this.name]||{}).light;return a===void 0?null:this._loadLight(a).then(function(o){return s._getNodeRef(e.cache,a,o)})}},Ee=class{constructor(){this.name=g.KHR_MATERIALS_UNLIT}getMaterialType(){return P}extendParams(t,e,s){let n=[];t.color=new O(1,1,1),t.opacity=1;let r=e.pbrMetallicRoughness;if(r){if(Array.isArray(r.baseColorFactor)){let i=r.baseColorFactor;t.color.setRGB(i[0],i[1],i[2],M),t.opacity=i[3]}r.baseColorTexture!==void 0&&n.push(s.assignTexture(t,"map",r.baseColorTexture,k))}return Promise.all(n)}},_e=class{constructor(t){this.parser=t,this.name=g.KHR_MATERIALS_EMISSIVE_STRENGTH}extendMaterialParams(t,e){let s=_(this.parser,t,this.name);return s===null||s.emissiveStrength!==void 0&&(e.emissiveIntensity=s.emissiveStrength),Promise.resolve()}},xe=class{constructor(t){this.parser=t,this.name=g.KHR_MATERIALS_CLEARCOAT}getMaterialType(t){return _(this.parser,t,this.name)!==null?S:null}extendMaterialParams(t,e){let s=_(this.parser,t,this.name);if(s===null)return Promise.resolve();let n=[];if(s.clearcoatFactor!==void 0&&(e.clearcoat=s.clearcoatFactor),s.clearcoatTexture!==void 0&&n.push(this.parser.assignTexture(e,"clearcoatMap",s.clearcoatTexture)),s.clearcoatRoughnessFactor!==void 0&&(e.clearcoatRoughness=s.clearcoatRoughnessFactor),s.clearcoatRoughnessTexture!==void 0&&n.push(this.parser.assignTexture(e,"clearcoatRoughnessMap",s.clearcoatRoughnessTexture)),s.clearcoatNormalTexture!==void 0&&(n.push(this.parser.assignTexture(e,"clearcoatNormalMap",s.clearcoatNormalTexture)),s.clearcoatNormalTexture.scale!==void 0)){let r=s.clearcoatNormalTexture.scale;e.clearcoatNormalScale=new ne(r,r)}return Promise.all(n)}},we=class{constructor(t){this.parser=t,this.name=g.KHR_MATERIALS_DISPERSION}getMaterialType(t){return _(this.parser,t,this.name)!==null?S:null}extendMaterialParams(t,e){let s=_(this.parser,t,this.name);return s===null||(e.dispersion=s.dispersion!==void 0?s.dispersion:0),Promise.resolve()}},be=class{constructor(t){this.parser=t,this.name=g.KHR_MATERIALS_IRIDESCENCE}getMaterialType(t){return _(this.parser,t,this.name)!==null?S:null}extendMaterialParams(t,e){let s=_(this.parser,t,this.name);if(s===null)return Promise.resolve();let n=[];return s.iridescenceFactor!==void 0&&(e.iridescence=s.iridescenceFactor),s.iridescenceTexture!==void 0&&n.push(this.parser.assignTexture(e,"iridescenceMap",s.iridescenceTexture)),s.iridescenceIor!==void 0&&(e.iridescenceIOR=s.iridescenceIor),e.iridescenceThicknessRange===void 0&&(e.iridescenceThicknessRange=[100,400]),s.iridescenceThicknessMinimum!==void 0&&(e.iridescenceThicknessRange[0]=s.iridescenceThicknessMinimum),s.iridescenceThicknessMaximum!==void 0&&(e.iridescenceThicknessRange[1]=s.iridescenceThicknessMaximum),s.iridescenceThicknessTexture!==void 0&&n.push(this.parser.assignTexture(e,"iridescenceThicknessMap",s.iridescenceThicknessTexture)),Promise.all(n)}},Se=class{constructor(t){this.parser=t,this.name=g.KHR_MATERIALS_SHEEN}getMaterialType(t){return _(this.parser,t,this.name)!==null?S:null}extendMaterialParams(t,e){let s=_(this.parser,t,this.name);if(s===null)return Promise.resolve();let n=[];if(e.sheenColor=new O(0,0,0),e.sheenRoughness=0,e.sheen=1,s.sheenColorFactor!==void 0){let r=s.sheenColorFactor;e.sheenColor.setRGB(r[0],r[1],r[2],M)}return s.sheenRoughnessFactor!==void 0&&(e.sheenRoughness=s.sheenRoughnessFactor),s.sheenColorTexture!==void 0&&n.push(this.parser.assignTexture(e,"sheenColorMap",s.sheenColorTexture,k)),s.sheenRoughnessTexture!==void 0&&n.push(this.parser.assignTexture(e,"sheenRoughnessMap",s.sheenRoughnessTexture)),Promise.all(n)}},Me=class{constructor(t){this.parser=t,this.name=g.KHR_MATERIALS_TRANSMISSION}getMaterialType(t){return _(this.parser,t,this.name)!==null?S:null}extendMaterialParams(t,e){let s=_(this.parser,t,this.name);if(s===null)return Promise.resolve();let n=[];return s.transmissionFactor!==void 0&&(e.transmission=s.transmissionFactor),s.transmissionTexture!==void 0&&n.push(this.parser.assignTexture(e,"transmissionMap",s.transmissionTexture)),Promise.all(n)}},Ie=class{constructor(t){this.parser=t,this.name=g.KHR_MATERIALS_VOLUME}getMaterialType(t){return _(this.parser,t,this.name)!==null?S:null}extendMaterialParams(t,e){let s=_(this.parser,t,this.name);if(s===null)return Promise.resolve();let n=[];e.thickness=s.thicknessFactor!==void 0?s.thicknessFactor:0,s.thicknessTexture!==void 0&&n.push(this.parser.assignTexture(e,"thicknessMap",s.thicknessTexture)),e.attenuationDistance=s.attenuationDistance||1/0;let r=s.attenuationColor||[1,1,1];return e.attenuationColor=new O().setRGB(r[0],r[1],r[2],M),Promise.all(n)}},Ne=class{constructor(t){this.parser=t,this.name=g.KHR_MATERIALS_IOR}getMaterialType(t){return _(this.parser,t,this.name)!==null?S:null}extendMaterialParams(t,e){let s=_(this.parser,t,this.name);return s===null||(e.ior=s.ior!==void 0?s.ior:1.5,e.ior===0&&(e.ior=1e3)),Promise.resolve()}},Le=class{constructor(t){this.parser=t,this.name=g.KHR_MATERIALS_SPECULAR}getMaterialType(t){return _(this.parser,t,this.name)!==null?S:null}extendMaterialParams(t,e){let s=_(this.parser,t,this.name);if(s===null)return Promise.resolve();let n=[];e.specularIntensity=s.specularFactor!==void 0?s.specularFactor:1,s.specularTexture!==void 0&&n.push(this.parser.assignTexture(e,"specularIntensityMap",s.specularTexture));let r=s.specularColorFactor||[1,1,1];return e.specularColor=new O().setRGB(r[0],r[1],r[2],M),s.specularColorTexture!==void 0&&n.push(this.parser.assignTexture(e,"specularColorMap",s.specularColorTexture,k)),Promise.all(n)}},Oe=class{constructor(t){this.parser=t,this.name=g.EXT_MATERIALS_BUMP}getMaterialType(t){return _(this.parser,t,this.name)!==null?S:null}extendMaterialParams(t,e){let s=_(this.parser,t,this.name);if(s===null)return Promise.resolve();let n=[];return e.bumpScale=s.bumpFactor!==void 0?s.bumpFactor:1,s.bumpTexture!==void 0&&n.push(this.parser.assignTexture(e,"bumpMap",s.bumpTexture)),Promise.all(n)}},Ce=class{constructor(t){this.parser=t,this.name=g.KHR_MATERIALS_ANISOTROPY}getMaterialType(t){return _(this.parser,t,this.name)!==null?S:null}extendMaterialParams(t,e){let s=_(this.parser,t,this.name);if(s===null)return Promise.resolve();let n=[];return s.anisotropyStrength!==void 0&&(e.anisotropy=s.anisotropyStrength),s.anisotropyRotation!==void 0&&(e.anisotropyRotation=s.anisotropyRotation),s.anisotropyTexture!==void 0&&n.push(this.parser.assignTexture(e,"anisotropyMap",s.anisotropyTexture)),Promise.all(n)}},ve=class{constructor(t){this.parser=t,this.name=g.KHR_TEXTURE_BASISU}loadTexture(t){let e=this.parser,s=e.json,n=s.textures[t];if(!n.extensions||!n.extensions[this.name])return null;let r=n.extensions[this.name],i=e.options.ktx2Loader;if(!i){if(s.extensionsRequired&&s.extensionsRequired.indexOf(this.name)>=0)throw new Error("THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures");return null}return e.loadTextureImage(t,r.source,i)}},He=class{constructor(t){this.parser=t,this.name=g.EXT_TEXTURE_WEBP}loadTexture(t){let e=this.name,s=this.parser,n=s.json,r=n.textures[t];if(!r.extensions||!r.extensions[e])return null;let i=r.extensions[e],a=n.images[i.source],o=s.textureLoader;if(a.uri){let c=s.options.manager.getHandler(a.uri);c!==null&&(o=c)}return s.loadTextureImage(t,i.source,o)}},ke=class{constructor(t){this.parser=t,this.name=g.EXT_TEXTURE_AVIF}loadTexture(t){let e=this.name,s=this.parser,n=s.json,r=n.textures[t];if(!r.extensions||!r.extensions[e])return null;let i=r.extensions[e],a=n.images[i.source],o=s.textureLoader;if(a.uri){let c=s.options.manager.getHandler(a.uri);c!==null&&(o=c)}return s.loadTextureImage(t,i.source,o)}},W=class{constructor(t,e){this.name=e,this.parser=t}loadBufferView(t){let e=this.parser.json,s=e.bufferViews[t];if(s.extensions&&s.extensions[this.name]){let n=s.extensions[this.name],r=this.parser.getDependency("buffer",n.buffer),i=this.parser.options.meshoptDecoder;if(!i||!i.supported){if(e.extensionsRequired&&e.extensionsRequired.indexOf(this.name)>=0)throw new Error("THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files");return null}return r.then(function(a){let o=n.byteOffset||0,c=n.byteLength||0,u=n.count,l=n.byteStride,d=new Uint8Array(a,o,c);return i.decodeGltfBufferAsync?i.decodeGltfBufferAsync(u,l,d,n.mode,n.filter).then(function(h){return h.buffer}):i.ready.then(function(){let h=new ArrayBuffer(u*l);return i.decodeGltfBuffer(new Uint8Array(h),u,l,d,n.mode,n.filter),h})})}else return null}},Pe=class{constructor(t){this.name=g.EXT_MESH_GPU_INSTANCING,this.parser=t}createNodeMesh(t){let e=this.parser.json,s=e.nodes[t];if(!s.extensions||!s.extensions[this.name]||s.mesh===void 0)return null;let n=e.meshes[s.mesh];for(let c of n.primitives)if(c.mode!==w.TRIANGLES&&c.mode!==w.TRIANGLE_STRIP&&c.mode!==w.TRIANGLE_FAN&&c.mode!==void 0)return null;let i=s.extensions[this.name].attributes,a=[],o={};for(let c in i)a.push(this.parser.getDependency("accessor",i[c]).then(u=>(o[c]=u,o[c])));return a.length<1?null:(a.push(this.parser.createNodeMesh(t)),Promise.all(a).then(c=>{let u=c.pop(),l=u.isGroup?u.children:[u],d=c[0].count,h=[];for(let p of l){let A=new z,m=new N,T=new se,E=new N(1,1,1),y=new at(p.geometry,p.material,d);for(let R=0;R<d;R++)o.TRANSLATION&&m.fromBufferAttribute(o.TRANSLATION,R),o.ROTATION&&T.fromBufferAttribute(o.ROTATION,R),o.SCALE&&E.fromBufferAttribute(o.SCALE,R),y.setMatrixAt(R,A.compose(m,T,E));for(let R in o)if(R==="_COLOR_0"){let x=o[R];y.instanceColor=new le(x.array,x.itemSize,x.normalized)}else R!=="TRANSLATION"&&R!=="ROTATION"&&R!=="SCALE"&&p.geometry.setAttribute(R,o[R]);oe.prototype.copy.call(y,p),this.parser.assignFinalMaterial(y),h.push(y)}return u.isGroup?(u.clear(),u.add(...h),u):h[0]}))}},Ot="glTF",K=12,Mt={JSON:1313821514,BIN:5130562},Be=class{constructor(t){this.name=g.KHR_BINARY_GLTF,this.content=null,this.body=null;let e=new DataView(t,0,K),s=new TextDecoder;if(this.header={magic:s.decode(new Uint8Array(t.slice(0,4))),version:e.getUint32(4,!0),length:e.getUint32(8,!0)},this.header.magic!==Ot)throw new Error("THREE.GLTFLoader: Unsupported glTF-Binary header.");if(this.header.version<2)throw new Error("THREE.GLTFLoader: Legacy binary file detected.");let n=this.header.length-K,r=new DataView(t,K),i=0;for(;i<n;){let a=r.getUint32(i,!0);i+=4;let o=r.getUint32(i,!0);if(i+=4,o===Mt.JSON){let c=new Uint8Array(t,K+i,a);this.content=s.decode(c)}else if(o===Mt.BIN){let c=K+i;this.body=t.slice(c,c+a)}i+=a}if(this.content===null)throw new Error("THREE.GLTFLoader: JSON content not found.")}},De=class{constructor(t,e){if(!e)throw new Error("THREE.GLTFLoader: No DRACOLoader instance provided.");this.name=g.KHR_DRACO_MESH_COMPRESSION,this.json=t,this.dracoLoader=e,this.dracoLoader.preload()}decodePrimitive(t,e){let s=this.json,n=this.dracoLoader,r=t.extensions[this.name].bufferView,i=t.extensions[this.name].attributes,a={},o={},c={};for(let u in i){let l=Ge[u]||u.toLowerCase();a[l]=i[u]}for(let u in t.attributes){let l=Ge[u]||u.toLowerCase();if(i[u]!==void 0){let d=s.accessors[t.attributes[u]],h=D[d.componentType];c[l]=h.name,o[l]=d.normalized===!0}}return e.getDependency("bufferView",r).then(function(u){return new Promise(function(l,d){n.decodeDracoFile(u,function(h){for(let p in h.attributes){let A=h.attributes[p],m=o[p];m!==void 0&&(A.normalized=m)}l(h)},a,c,M,d)})})}},Ue=class{constructor(){this.name=g.KHR_TEXTURE_TRANSFORM}extendTexture(t,e){return(e.texCoord===void 0||e.texCoord===t.channel)&&e.offset===void 0&&e.rotation===void 0&&e.scale===void 0||(t=t.clone(),e.texCoord!==void 0&&(t.channel=e.texCoord),e.offset!==void 0&&t.offset.fromArray(e.offset),e.rotation!==void 0&&(t.rotation=e.rotation),e.scale!==void 0&&t.repeat.fromArray(e.scale),t.needsUpdate=!0),t}},Fe=class{constructor(){this.name=g.KHR_MESH_QUANTIZATION}},Z=class extends pt{constructor(t,e,s,n){super(t,e,s,n)}copySampleValue_(t){let e=this.resultBuffer,s=this.sampleValues,n=this.valueSize,r=t*n*3+n;for(let i=0;i!==n;i++)e[i]=s[r+i];return e}interpolate_(t,e,s,n){let r=this.resultBuffer,i=this.sampleValues,a=this.valueSize,o=a*2,c=a*3,u=n-e,l=(s-e)/u,d=l*l,h=d*l,p=t*c,A=p-c,m=-2*h+3*d,T=h-d,E=1-m,y=T-d+l;for(let R=0;R!==a;R++){let x=i[A+R+a],L=i[A+R+o]*u,b=i[p+R+a],U=i[p+R]*u;r[R]=E*x+y*L+m*b+T*U}return r}},vt=new se,ze=class extends Z{interpolate_(t,e,s,n){let r=super.interpolate_(t,e,s,n);return vt.fromArray(r).normalize().toArray(r),r}},w={FLOAT:5126,FLOAT_MAT3:35675,FLOAT_MAT4:35676,FLOAT_VEC2:35664,FLOAT_VEC3:35665,FLOAT_VEC4:35666,LINEAR:9729,REPEAT:10497,SAMPLER_2D:35678,POINTS:0,LINES:1,LINE_LOOP:2,LINE_STRIP:3,TRIANGLES:4,TRIANGLE_STRIP:5,TRIANGLE_FAN:6,UNSIGNED_BYTE:5121,UNSIGNED_SHORT:5123},D={5120:Int8Array,5121:Uint8Array,5122:Int16Array,5123:Uint16Array,5125:Uint32Array,5126:Float32Array},It={9728:$,9729:X,9984:We,9985:Je,9986:Ze,9987:ee},Nt={33071:qe,33648:Ye,10497:j},Te={SCALAR:1,VEC2:2,VEC3:3,VEC4:4,MAT2:4,MAT3:9,MAT4:16},Ge={POSITION:"position",NORMAL:"normal",TANGENT:"tangent",TEXCOORD_0:"uv",TEXCOORD_1:"uv1",TEXCOORD_2:"uv2",TEXCOORD_3:"uv3",COLOR_0:"color",WEIGHTS_0:"skinWeight",JOINTS_0:"skinIndex"},C={scale:"scale",translation:"position",rotation:"quaternion",weights:"morphTargetInfluences"},Ht={CUBICSPLINE:void 0,LINEAR:te,STEP:Qe},Ae={OPAQUE:"OPAQUE",MASK:"MASK",BLEND:"BLEND"};function kt(f){return f.DefaultMaterial===void 0&&(f.DefaultMaterial=new fe({color:16777215,emissive:0,metalness:1,roughness:1,transparent:!1,depthTest:!0,side:Xe})),f.DefaultMaterial}function v(f,t,e){for(let s in e.extensions)f[s]===void 0&&(t.userData.gltfExtensions=t.userData.gltfExtensions||{},t.userData.gltfExtensions[s]=e.extensions[s])}function I(f,t){t.extras!==void 0&&(typeof t.extras=="object"?Object.assign(f.userData,t.extras):console.warn("THREE.GLTFLoader: Ignoring primitive type .extras, "+t.extras))}function Pt(f,t,e){let s=!1,n=!1,r=!1;for(let c=0,u=t.length;c<u;c++){let l=t[c];if(l.POSITION!==void 0&&(s=!0),l.NORMAL!==void 0&&(n=!0),l.COLOR_0!==void 0&&(r=!0),s&&n&&r)break}if(!s&&!n&&!r)return Promise.resolve(f);let i=[],a=[],o=[];for(let c=0,u=t.length;c<u;c++){let l=t[c];if(s){let d=l.POSITION!==void 0?e.getDependency("accessor",l.POSITION):f.attributes.position;i.push(d)}if(n){let d=l.NORMAL!==void 0?e.getDependency("accessor",l.NORMAL):f.attributes.normal;a.push(d)}if(r){let d=l.COLOR_0!==void 0?e.getDependency("accessor",l.COLOR_0):f.attributes.color;o.push(d)}}return Promise.all([Promise.all(i),Promise.all(a),Promise.all(o)]).then(function(c){let u=c[0],l=c[1],d=c[2];return s&&(f.morphAttributes.position=u),n&&(f.morphAttributes.normal=l),r&&(f.morphAttributes.color=d),f.morphTargetsRelative=!0,f})}function Bt(f,t){if(f.updateMorphTargets(),t.weights!==void 0)for(let e=0,s=t.weights.length;e<s;e++)f.morphTargetInfluences[e]=t.weights[e];if(t.extras&&Array.isArray(t.extras.targetNames)){let e=t.extras.targetNames;if(f.morphTargetInfluences.length===e.length){f.morphTargetDictionary={};for(let s=0,n=e.length;s<n;s++)f.morphTargetDictionary[e[s]]=s}else console.warn("THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.")}}function Dt(f){let t,e=f.extensions&&f.extensions[g.KHR_DRACO_MESH_COMPRESSION];if(e?t="draco:"+e.bufferView+":"+e.indices+":"+Re(e.attributes):t=f.indices+":"+Re(f.attributes)+":"+f.mode,f.targets!==void 0)for(let s=0,n=f.targets.length;s<n;s++)t+=":"+Re(f.targets[s]);return t}function Re(f){let t="",e=Object.keys(f).sort();for(let s=0,n=e.length;s<n;s++)t+=e[s]+":"+f[e[s]]+";";return t}function Ke(f){switch(f){case Int8Array:return 1/127;case Uint8Array:return 1/255;case Int16Array:return 1/32767;case Uint16Array:return 1/65535;default:throw new Error("THREE.GLTFLoader: Unsupported normalized accessor component type.")}}function Ut(f){return f.search(/\.jpe?g($|\?)/i)>0||f.search(/^data\:image\/jpeg/)===0?"image/jpeg":f.search(/\.webp($|\?)/i)>0||f.search(/^data\:image\/webp/)===0?"image/webp":f.search(/\.ktx2($|\?)/i)>0||f.search(/^data\:image\/ktx2/)===0?"image/ktx2":"image/png"}var Ft=new z,je=class{constructor(t={},e={}){this.json=t,this.extensions={},this.plugins={},this.options=e,this.cache=new Ct,this.associations=new Map,this.primitiveCache={},this.nodeCache={},this.meshCache={refs:{},uses:{}},this.cameraCache={refs:{},uses:{}},this.lightCache={refs:{},uses:{}},this.sourceCache={},this.textureCache={},this.nodeNamesUsed={};let s=!1,n=-1,r=!1,i=-1;if(typeof navigator<"u"&&typeof navigator.userAgent<"u"){let a=navigator.userAgent;s=/^((?!chrome|android).)*safari/i.test(a)===!0;let o=a.match(/Version\/(\d+)/);n=s&&o?parseInt(o[1],10):-1,r=a.indexOf("Firefox")>-1,i=r?a.match(/Firefox\/([0-9]+)\./)[1]:-1}typeof createImageBitmap>"u"||s&&n<17||r&&i<98?this.textureLoader=new Tt(this.options.manager):this.textureLoader=new xt(this.options.manager),this.textureLoader.setCrossOrigin(this.options.crossOrigin),this.textureLoader.setRequestHeader(this.options.requestHeader),this.fileLoader=new me(this.options.manager),this.fileLoader.setResponseType("arraybuffer"),this.options.crossOrigin==="use-credentials"&&this.fileLoader.setWithCredentials(!0)}setExtensions(t){this.extensions=t}setPlugins(t){this.plugins=t}parse(t,e){let s=this,n=this.json,r=this.extensions;this.cache.removeAll(),this.nodeCache={},this._invokeAll(function(i){return i._markDefs&&i._markDefs()}),Promise.all(this._invokeAll(function(i){return i.beforeRoot&&i.beforeRoot()})).then(function(){return Promise.all([s.getDependencies("scene"),s.getDependencies("animation"),s.getDependencies("camera")])}).then(function(i){let a={scene:i[0][n.scene||0],scenes:i[0],animations:i[1],cameras:i[2],asset:n.asset,parser:s,userData:{}};return v(r,a,n),I(a,n),Promise.all(s._invokeAll(function(o){return o.afterRoot&&o.afterRoot(a)})).then(function(){for(let o of a.scenes)o.updateMatrixWorld();t(a)})}).catch(e)}_markDefs(){let t=this.json.nodes||[],e=this.json.skins||[],s=this.json.meshes||[];for(let n=0,r=e.length;n<r;n++){let i=e[n].joints;for(let a=0,o=i.length;a<o;a++)t[i[a]].isBone=!0}for(let n=0,r=t.length;n<r;n++){let i=t[n];i.mesh!==void 0&&(this._addNodeRef(this.meshCache,i.mesh),i.skin!==void 0&&(s[i.mesh].isSkinnedMesh=!0)),i.camera!==void 0&&this._addNodeRef(this.cameraCache,i.camera)}}_addNodeRef(t,e){e!==void 0&&(t.refs[e]===void 0&&(t.refs[e]=t.uses[e]=0),t.refs[e]++)}_getNodeRef(t,e,s){if(t.refs[e]<=1)return s;let n=s.clone(),r=(i,a)=>{let o=this.associations.get(i);o!=null&&this.associations.set(a,o);for(let[c,u]of i.children.entries())r(u,a.children[c])};return r(s,n),n.name+="_instance_"+t.uses[e]++,n}_invokeOne(t){let e=Object.values(this.plugins);e.push(this);for(let s=0;s<e.length;s++){let n=t(e[s]);if(n)return n}return null}_invokeAll(t){let e=Object.values(this.plugins);e.unshift(this);let s=[];for(let n=0;n<e.length;n++){let r=t(e[n]);r&&s.push(r)}return s}getDependency(t,e){let s=t+":"+e,n=this.cache.get(s);if(!n){switch(t){case"scene":n=this.loadScene(e);break;case"node":n=this._invokeOne(function(r){return r.loadNode&&r.loadNode(e)});break;case"mesh":n=this._invokeOne(function(r){return r.loadMesh&&r.loadMesh(e)});break;case"accessor":n=this.loadAccessor(e);break;case"bufferView":n=this._invokeOne(function(r){return r.loadBufferView&&r.loadBufferView(e)});break;case"buffer":n=this.loadBuffer(e);break;case"material":n=this._invokeOne(function(r){return r.loadMaterial&&r.loadMaterial(e)});break;case"texture":n=this._invokeOne(function(r){return r.loadTexture&&r.loadTexture(e)});break;case"skin":n=this.loadSkin(e);break;case"animation":n=this._invokeOne(function(r){return r.loadAnimation&&r.loadAnimation(e)});break;case"camera":n=this.loadCamera(e);break;default:if(n=this._invokeOne(function(r){return r!=this&&r.getDependency&&r.getDependency(t,e)}),!n)throw new Error("Unknown type: "+t);break}this.cache.add(s,n)}return n}getDependencies(t){let e=this.cache.get(t);if(!e){let s=this,n=this.json[t+(t==="mesh"?"es":"s")]||[];e=Promise.all(n.map(function(r,i){return s.getDependency(t,i)})),this.cache.add(t,e)}return e}loadBuffer(t){let e=this.json.buffers[t],s=this.fileLoader;if(e.type&&e.type!=="arraybuffer")throw new Error("THREE.GLTFLoader: "+e.type+" buffer type is not supported.");if(e.uri===void 0&&t===0)return Promise.resolve(this.extensions[g.KHR_BINARY_GLTF].body);let n=this.options;return new Promise(function(r,i){s.load(B.resolveURL(e.uri,n.path),r,void 0,function(){i(new Error('THREE.GLTFLoader: Failed to load buffer "'+e.uri+'".'))})})}loadBufferView(t){let e=this.json.bufferViews[t];return this.getDependency("buffer",e.buffer).then(function(s){let n=e.byteLength||0,r=e.byteOffset||0;return s.slice(r,r+n)})}loadAccessor(t){let e=this,s=this.json,n=this.json.accessors[t];if(n.bufferView===void 0&&n.sparse===void 0){let i=Te[n.type],a=D[n.componentType],o=n.normalized===!0,c=new a(n.count*i);return Promise.resolve(new G(c,i,o))}let r=[];return n.bufferView!==void 0?r.push(this.getDependency("bufferView",n.bufferView)):r.push(null),n.sparse!==void 0&&(r.push(this.getDependency("bufferView",n.sparse.indices.bufferView)),r.push(this.getDependency("bufferView",n.sparse.values.bufferView))),Promise.all(r).then(function(i){let a=i[0],o=Te[n.type],c=D[n.componentType],u=c.BYTES_PER_ELEMENT,l=u*o,d=n.byteOffset||0,h=n.bufferView!==void 0?s.bufferViews[n.bufferView].byteStride:void 0,p=n.normalized===!0,A,m;if(h&&h!==l){let T=Math.floor(d/h),E="InterleavedBuffer:"+n.bufferView+":"+n.componentType+":"+T+":"+n.count,y=e.cache.get(E);y||(A=new c(a,T*h,n.count*h/u),y=new ce(A,h/u),e.cache.add(E,y)),m=new ue(y,o,d%h/u,p)}else a===null?A=new c(n.count*o):A=new c(a,d,n.count*o),m=new G(A,o,p);if(n.sparse!==void 0){let T=Te.SCALAR,E=D[n.sparse.indices.componentType],y=n.sparse.indices.byteOffset||0,R=n.sparse.values.byteOffset||0,x=new E(i[1],y,n.sparse.count*T),L=new c(i[2],R,n.sparse.count*o);a!==null&&(m=new G(m.array.slice(),m.itemSize,m.normalized)),m.normalized=!1;for(let b=0,U=x.length;b<U;b++){let H=x[b];if(m.setX(H,L[b*o]),o>=2&&m.setY(H,L[b*o+1]),o>=3&&m.setZ(H,L[b*o+2]),o>=4&&m.setW(H,L[b*o+3]),o>=5)throw new Error("THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.")}m.normalized=p}return m})}loadTexture(t){let e=this.json,s=this.options,r=e.textures[t].source,i=e.images[r],a=this.textureLoader;if(i.uri){let o=s.manager.getHandler(i.uri);o!==null&&(a=o)}return this.loadTextureImage(t,r,a)}loadTextureImage(t,e,s){let n=this,r=this.json,i=r.textures[t],a=r.images[e],o=(a.uri||a.bufferView)+":"+i.sampler;if(this.textureCache[o])return this.textureCache[o];let c=this.loadImageSource(e,s).then(function(u){u.flipY=!1,u.name=i.name||a.name||"",u.name===""&&typeof a.uri=="string"&&a.uri.startsWith("data:image/")===!1&&(u.name=a.uri);let d=(r.samplers||{})[i.sampler]||{};return u.magFilter=It[d.magFilter]||X,u.minFilter=It[d.minFilter]||ee,u.wrapS=Nt[d.wrapS]||j,u.wrapT=Nt[d.wrapT]||j,u.generateMipmaps=!u.isCompressedTexture&&u.minFilter!==$&&u.minFilter!==X,n.associations.set(u,{textures:t}),u}).catch(function(){return null});return this.textureCache[o]=c,c}loadImageSource(t,e){let s=this,n=this.json,r=this.options;if(this.sourceCache[t]!==void 0)return this.sourceCache[t].then(l=>l.clone());let i=n.images[t],a=self.URL||self.webkitURL,o=i.uri||"",c=!1;if(i.bufferView!==void 0)o=s.getDependency("bufferView",i.bufferView).then(function(l){c=!0;let d=new Blob([l],{type:i.mimeType});return o=a.createObjectURL(d),o});else if(i.uri===void 0)throw new Error("THREE.GLTFLoader: Image "+t+" is missing URI and bufferView");let u=Promise.resolve(o).then(function(l){return new Promise(function(d,h){let p=d;e.isImageBitmapLoader===!0&&(p=function(A){let m=new ie(A);m.needsUpdate=!0,d(m)}),e.load(B.resolveURL(l,r.path),p,void 0,h)})}).then(function(l){return c===!0&&a.revokeObjectURL(o),I(l,i),l.userData.mimeType=i.mimeType||Ut(i.uri),l}).catch(function(l){throw console.error("THREE.GLTFLoader: Couldn't load texture",o),l});return this.sourceCache[t]=u,u}assignTexture(t,e,s,n){let r=this;return this.getDependency("texture",s.index).then(function(i){if(!i)return null;if(s.texCoord!==void 0&&s.texCoord>0&&(i=i.clone(),i.channel=s.texCoord),r.extensions[g.KHR_TEXTURE_TRANSFORM]){let a=s.extensions!==void 0?s.extensions[g.KHR_TEXTURE_TRANSFORM]:void 0;if(a){let o=r.associations.get(i);i=r.extensions[g.KHR_TEXTURE_TRANSFORM].extendTexture(i,a),r.associations.set(i,o)}}return n!==void 0&&(i.colorSpace=n),t[e]=i,i})}assignFinalMaterial(t){let e=t.geometry,s=t.material,n=e.attributes.tangent===void 0,r=e.attributes.color!==void 0,i=e.attributes.normal===void 0;if(t.isPoints){let a="PointsMaterial:"+s.uuid,o=this.cache.get(a);o||(o=new dt,Y.prototype.copy.call(o,s),o.color.copy(s.color),o.map=s.map,o.sizeAttenuation=!1,this.cache.add(a,o)),s=o}else if(t.isLine){let a="LineBasicMaterial:"+s.uuid,o=this.cache.get(a);o||(o=new ct,Y.prototype.copy.call(o,s),o.color.copy(s.color),o.map=s.map,this.cache.add(a,o)),s=o}if(n||r||i){let a="ClonedMaterial:"+s.uuid+":";n&&(a+="derivative-tangents:"),r&&(a+="vertex-colors:"),i&&(a+="flat-shading:");let o=this.cache.get(a);o||(o=s.clone(),r&&(o.vertexColors=!0),i&&(o.flatShading=!0),n&&(o.normalScale&&(o.normalScale.y*=-1),o.clearcoatNormalScale&&(o.clearcoatNormalScale.y*=-1)),this.cache.add(a,o),this.associations.set(o,this.associations.get(s))),s=o}t.material=s}getMaterialType(){return fe}loadMaterial(t){let e=this,s=this.json,n=this.extensions,r=s.materials[t],i,a={},o=r.extensions||{},c=[];if(o[g.KHR_MATERIALS_UNLIT]){let l=n[g.KHR_MATERIALS_UNLIT];i=l.getMaterialType(),c.push(l.extendParams(a,r,e))}else{let l=r.pbrMetallicRoughness||{};if(a.color=new O(1,1,1),a.opacity=1,Array.isArray(l.baseColorFactor)){let d=l.baseColorFactor;a.color.setRGB(d[0],d[1],d[2],M),a.opacity=d[3]}l.baseColorTexture!==void 0&&c.push(e.assignTexture(a,"map",l.baseColorTexture,k)),a.metalness=l.metallicFactor!==void 0?l.metallicFactor:1,a.roughness=l.roughnessFactor!==void 0?l.roughnessFactor:1,l.metallicRoughnessTexture!==void 0&&(c.push(e.assignTexture(a,"metalnessMap",l.metallicRoughnessTexture)),c.push(e.assignTexture(a,"roughnessMap",l.metallicRoughnessTexture))),i=this._invokeOne(function(d){return d.getMaterialType&&d.getMaterialType(t)}),c.push(Promise.all(this._invokeAll(function(d){return d.extendMaterialParams&&d.extendMaterialParams(t,a)})))}r.doubleSided===!0&&(a.side=Ve);let u=r.alphaMode||Ae.OPAQUE;if(u===Ae.BLEND?(a.transparent=!0,a.depthWrite=!1):(a.transparent=!1,u===Ae.MASK&&(a.alphaTest=r.alphaCutoff!==void 0?r.alphaCutoff:.5)),r.normalTexture!==void 0&&i!==P&&(c.push(e.assignTexture(a,"normalMap",r.normalTexture)),a.normalScale=new ne(1,1),r.normalTexture.scale!==void 0)){let l=r.normalTexture.scale;a.normalScale.set(l,l)}if(r.occlusionTexture!==void 0&&i!==P&&(c.push(e.assignTexture(a,"aoMap",r.occlusionTexture)),r.occlusionTexture.strength!==void 0&&(a.aoMapIntensity=r.occlusionTexture.strength)),r.emissiveFactor!==void 0&&i!==P){let l=r.emissiveFactor;a.emissive=new O().setRGB(l[0],l[1],l[2],M)}return r.emissiveTexture!==void 0&&i!==P&&c.push(e.assignTexture(a,"emissiveMap",r.emissiveTexture,k)),Promise.all(c).then(function(){let l=new i(a);return r.name&&(l.name=r.name),I(l,r),e.associations.set(l,{materials:t}),r.extensions&&v(n,l,r),l})}createUniqueName(t){let e=wt.sanitizeNodeName(t||"");return e in this.nodeNamesUsed?e+"_"+ ++this.nodeNamesUsed[e]:(this.nodeNamesUsed[e]=0,e)}loadGeometries(t){let e=this,s=this.extensions,n=this.primitiveCache;function r(a){return s[g.KHR_DRACO_MESH_COMPRESSION].decodePrimitive(a,e).then(function(o){return Lt(o,a,e)})}let i=[];for(let a=0,o=t.length;a<o;a++){let c=t[a],u=Dt(c),l=n[u];if(l)i.push(l.promise);else{let d;c.extensions&&c.extensions[g.KHR_DRACO_MESH_COMPRESSION]?d=r(c):d=Lt(new ae,c,e),n[u]={primitive:c,promise:d},i.push(d)}}return Promise.all(i)}loadMesh(t){let e=this,s=this.json,n=this.extensions,r=s.meshes[t],i=r.primitives,a=[];for(let o=0,c=i.length;o<c;o++){let u=i[o].material===void 0?kt(this.cache):this.getDependency("material",i[o].material);a.push(u)}return a.push(e.loadGeometries(i)),Promise.all(a).then(function(o){let c=o.slice(0,o.length-1),u=o[o.length-1],l=[];for(let h=0,p=u.length;h<p;h++){let A=u[h],m=i[h],T,E=c[h];if(m.mode===w.TRIANGLES||m.mode===w.TRIANGLE_STRIP||m.mode===w.TRIANGLE_FAN||m.mode===void 0)T=r.isSkinnedMesh===!0?new rt(A,E):new st(A,E),T.isSkinnedMesh===!0&&T.normalizeSkinWeights(),m.mode===w.TRIANGLE_STRIP?T.geometry=ge(T.geometry,V):m.mode===w.TRIANGLE_FAN&&(T.geometry=ge(T.geometry,F));else if(m.mode===w.LINES)T=new lt(A,E);else if(m.mode===w.LINE_STRIP)T=new ut(A,E);else if(m.mode===w.LINE_LOOP)T=new ft(A,E);else if(m.mode===w.POINTS)T=new ht(A,E);else throw new Error("THREE.GLTFLoader: Primitive mode unsupported: "+m.mode);Object.keys(T.geometry.morphAttributes).length>0&&Bt(T,r),T.name=e.createUniqueName(r.name||"mesh_"+t),I(T,r),m.extensions&&v(n,T,m),e.assignFinalMaterial(T),l.push(T)}for(let h=0,p=l.length;h<p;h++)e.associations.set(l[h],{meshes:t,primitives:h});if(l.length===1)return r.extensions&&v(n,l[0],r),l[0];let d=new q;r.extensions&&v(n,d,r),e.associations.set(d,{meshes:t});for(let h=0,p=l.length;h<p;h++)d.add(l[h]);return d})}loadCamera(t){let e,s=this.json.cameras[t],n=s[s.type];if(!n){console.warn("THREE.GLTFLoader: Missing camera parameters.");return}return s.type==="perspective"?e=new At(et.radToDeg(n.yfov),n.aspectRatio||1,n.znear||1,n.zfar||2e6):s.type==="orthographic"&&(e=new Et(-n.xmag,n.xmag,n.ymag,-n.ymag,n.znear,n.zfar)),s.name&&(e.name=this.createUniqueName(s.name)),I(e,s),Promise.resolve(e)}loadSkin(t){let e=this.json.skins[t],s=[];for(let n=0,r=e.joints.length;n<r;n++)s.push(this._loadNodeShallow(e.joints[n]));return e.inverseBindMatrices!==void 0?s.push(this.getDependency("accessor",e.inverseBindMatrices)):s.push(null),Promise.all(s).then(function(n){let r=n.pop(),i=n,a=[],o=[];for(let c=0,u=i.length;c<u;c++){let l=i[c];if(l){a.push(l);let d=new z;r!==null&&d.fromArray(r.array,c*16),o.push(d)}else console.warn('THREE.GLTFLoader: Joint "%s" could not be found.',e.joints[c])}return new ot(a,o)})}loadAnimation(t){let e=this.json,s=this,n=e.animations[t],r=n.name?n.name:"animation_"+t,i=[],a=[],o=[],c=[],u=[];for(let l=0,d=n.channels.length;l<d;l++){let h=n.channels[l],p=n.samplers[h.sampler],A=h.target,m=A.node,T=n.parameters!==void 0?n.parameters[p.input]:p.input,E=n.parameters!==void 0?n.parameters[p.output]:p.output;A.node!==void 0&&(i.push(this.getDependency("node",m)),a.push(this.getDependency("accessor",T)),o.push(this.getDependency("accessor",E)),c.push(p),u.push(A))}return Promise.all([Promise.all(i),Promise.all(a),Promise.all(o),Promise.all(c),Promise.all(u)]).then(function(l){let d=l[0],h=l[1],p=l[2],A=l[3],m=l[4],T=[];for(let y=0,R=d.length;y<R;y++){let x=d[y],L=h[y],b=p[y],U=A[y],H=m[y];if(x===void 0)continue;x.updateMatrix&&x.updateMatrix();let J=s._createAnimationTracks(x,L,b,U,H);if(J)for(let Q=0;Q<J.length;Q++)T.push(J[Q])}let E=new mt(r,void 0,T);return I(E,n),E})}createNodeMesh(t){let e=this.json,s=this,n=e.nodes[t];return n.mesh===void 0?null:s.getDependency("mesh",n.mesh).then(function(r){let i=s._getNodeRef(s.meshCache,n.mesh,r);return n.weights!==void 0&&i.traverse(function(a){if(a.isMesh)for(let o=0,c=n.weights.length;o<c;o++)a.morphTargetInfluences[o]=n.weights[o]}),i})}loadNode(t){let e=this.json,s=this,n=e.nodes[t],r=s._loadNodeShallow(t),i=[],a=n.children||[];for(let c=0,u=a.length;c<u;c++)i.push(s.getDependency("node",a[c]));let o=n.skin===void 0?Promise.resolve(null):s.getDependency("skin",n.skin);return Promise.all([r,Promise.all(i),o]).then(function(c){let u=c[0],l=c[1],d=c[2];d!==null&&u.traverse(function(h){h.isSkinnedMesh&&h.bind(d,Ft)});for(let h=0,p=l.length;h<p;h++)u.add(l[h]);if(u.userData.pivot!==void 0&&l.length>0){let h=u.userData.pivot,p=l[0];u.pivot=new N().fromArray(h),u.position.x-=h[0],u.position.y-=h[1],u.position.z-=h[2],p.position.set(0,0,0),delete u.userData.pivot}return u})}_loadNodeShallow(t){let e=this.json,s=this.extensions,n=this;if(this.nodeCache[t]!==void 0)return this.nodeCache[t];let r=e.nodes[t],i=r.name?n.createUniqueName(r.name):"",a=[],o=n._invokeOne(function(c){return c.createNodeMesh&&c.createNodeMesh(t)});return o&&a.push(o),r.camera!==void 0&&a.push(n.getDependency("camera",r.camera).then(function(c){return n._getNodeRef(n.cameraCache,r.camera,c)})),n._invokeAll(function(c){return c.createNodeAttachment&&c.createNodeAttachment(t)}).forEach(function(c){a.push(c)}),this.nodeCache[t]=Promise.all(a).then(function(c){let u;if(r.isBone===!0?u=new it:c.length>1?u=new q:c.length===1?u=c[0]:u=new oe,u!==c[0])for(let l=0,d=c.length;l<d;l++)u.add(c[l]);if(r.name&&(u.userData.name=r.name,u.name=i),I(u,r),r.extensions&&v(s,u,r),r.matrix!==void 0){let l=new z;l.fromArray(r.matrix),u.applyMatrix4(l)}else r.translation!==void 0&&u.position.fromArray(r.translation),r.rotation!==void 0&&u.quaternion.fromArray(r.rotation),r.scale!==void 0&&u.scale.fromArray(r.scale);if(!n.associations.has(u))n.associations.set(u,{});else if(r.mesh!==void 0&&n.meshCache.refs[r.mesh]>1){let l=n.associations.get(u);n.associations.set(u,{...l})}return n.associations.get(u).nodes=t,u}),this.nodeCache[t]}loadScene(t){let e=this.extensions,s=this.json.scenes[t],n=this,r=new q;s.name&&(r.name=n.createUniqueName(s.name)),I(r,s),s.extensions&&v(e,r,s);let i=s.nodes||[],a=[];for(let o=0,c=i.length;o<c;o++)a.push(n.getDependency("node",i[o]));return Promise.all(a).then(function(o){for(let u=0,l=o.length;u<l;u++){let d=o[u];d.parent!==null?r.add(bt(d)):r.add(d)}let c=u=>{let l=new Map;for(let[d,h]of n.associations)(d instanceof Y||d instanceof ie)&&l.set(d,h);return u.traverse(d=>{let h=n.associations.get(d);h!=null&&l.set(d,h)}),l};return n.associations=c(r),r})}_createAnimationTracks(t,e,s,n,r){let i=[],a=t.name?t.name:t.uuid,o=[];function c(h){h.morphTargetInfluences&&o.push(h.name?h.name:h.uuid)}C[r.path]===C.weights?(c(t),t.isGroup&&t.children.forEach(c)):o.push(a);let u;switch(C[r.path]){case C.weights:u=de;break;case C.rotation:u=he;break;case C.translation:case C.scale:u=pe;break;default:s.itemSize===1?u=de:u=pe;break}let l=n.interpolation!==void 0?Ht[n.interpolation]:te,d=this._getArrayFromAccessor(s);for(let h=0,p=o.length;h<p;h++){let A=new u(o[h]+"."+C[r.path],e.array,d,l);n.interpolation==="CUBICSPLINE"&&this._createCubicSplineTrackInterpolant(A),i.push(A)}return i}_getArrayFromAccessor(t){let e=t.array;if(t.normalized){let s=Ke(e.constructor),n=new Float32Array(e.length);for(let r=0,i=e.length;r<i;r++)n[r]=e[r]*s;e=n}return e}_createCubicSplineTrackInterpolant(t){t.createInterpolant=function(s){let n=this instanceof he?ze:Z;return new n(this.times,this.values,this.getValueSize()/3,s)},t.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline=!0}};function zt(f,t,e){let s=t.attributes,n=new tt;if(s.POSITION!==void 0){let a=e.json.accessors[s.POSITION],o=a.min,c=a.max;if(o!==void 0&&c!==void 0){if(n.set(new N(o[0],o[1],o[2]),new N(c[0],c[1],c[2])),a.normalized){let u=Ke(D[a.componentType]);n.min.multiplyScalar(u),n.max.multiplyScalar(u)}}else{console.warn("THREE.GLTFLoader: Missing min/max properties for accessor POSITION.");return}}else return;let r=t.targets;if(r!==void 0){let a=new N,o=new N;for(let c=0,u=r.length;c<u;c++){let l=r[c];if(l.POSITION!==void 0){let d=e.json.accessors[l.POSITION],h=d.min,p=d.max;if(h!==void 0&&p!==void 0){if(o.setX(Math.max(Math.abs(h[0]),Math.abs(p[0]))),o.setY(Math.max(Math.abs(h[1]),Math.abs(p[1]))),o.setZ(Math.max(Math.abs(h[2]),Math.abs(p[2]))),d.normalized){let A=Ke(D[d.componentType]);o.multiplyScalar(A)}a.max(o)}else console.warn("THREE.GLTFLoader: Missing min/max properties for accessor POSITION.")}}n.expandByVector(a)}f.boundingBox=n;let i=new nt;n.getCenter(i.center),i.radius=n.min.distanceTo(n.max)/2,f.boundingSphere=i}function Lt(f,t,e){let s=t.attributes,n=[];function r(i,a){return e.getDependency("accessor",i).then(function(o){f.setAttribute(a,o)})}for(let i in s){let a=Ge[i]||i.toLowerCase();a in f.attributes||n.push(r(s[i],a))}if(t.indices!==void 0&&!f.index){let i=e.getDependency("accessor",t.indices).then(function(a){f.setIndex(a)});n.push(i)}return re.workingColorSpace!==M&&"COLOR_0"in s&&console.warn(`THREE.GLTFLoader: Converting vertex colors from "srgb-linear" to "${re.workingColorSpace}" not supported.`),I(f,t),zt(f,t,e),Promise.all(n).then(function(){return t.targets!==void 0?Pt(f,t.targets,e):f})}export{St as GLTFLoader};
2
- //# sourceMappingURL=GLTFLoader-PGXD4V5B.js.map