create-definedmotion 0.1.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.
Files changed (98) hide show
  1. package/bin/index.js +3 -0
  2. package/package.json +37 -0
  3. package/src/cli.js +100 -0
  4. package/template/.editorconfig +9 -0
  5. package/template/.prettierignore +6 -0
  6. package/template/.prettierrc.yaml +10 -0
  7. package/template/_gitignore +10 -0
  8. package/template/build/entitlements.mac.plist +12 -0
  9. package/template/build/icon.icns +0 -0
  10. package/template/build/icon.ico +0 -0
  11. package/template/build/icon.png +0 -0
  12. package/template/electron-builder.yml +43 -0
  13. package/template/electron.vite.config.ts +50 -0
  14. package/template/eslint.config.mjs +24 -0
  15. package/template/package-lock.json +10299 -0
  16. package/template/package.json +64 -0
  17. package/template/resources/icon.png +0 -0
  18. package/template/src/assets/audio/fadeSound.mp3 +0 -0
  19. package/template/src/assets/audio/fadeSound2.mp3 +0 -0
  20. package/template/src/assets/audio/interstellar.mp3 +0 -0
  21. package/template/src/assets/audio/keyboard1.mp3 +0 -0
  22. package/template/src/assets/audio/keyboard2.mp3 +0 -0
  23. package/template/src/assets/audio/keyboard3.mp3 +0 -0
  24. package/template/src/assets/audio/tick_sound.mp3 +0 -0
  25. package/template/src/assets/base.css +67 -0
  26. package/template/src/assets/electron.svg +10 -0
  27. package/template/src/assets/fonts/Geo-Regular.woff +0 -0
  28. package/template/src/assets/fonts/Montserrat-Italic-VariableFont_wght.woff2 +0 -0
  29. package/template/src/assets/fonts/Montserrat-Medium.ttf +0 -0
  30. package/template/src/assets/fonts/Montserrat-Medium.woff +0 -0
  31. package/template/src/assets/fonts/Montserrat-VariableFont_wght.woff2 +0 -0
  32. package/template/src/assets/fonts/glitch.ttf +0 -0
  33. package/template/src/assets/hdri/indoor1.hdr +0 -0
  34. package/template/src/assets/hdri/metro1.hdr +0 -0
  35. package/template/src/assets/hdri/outdoor1.hdr +0 -0
  36. package/template/src/assets/hdri/photo-studio1.hdr +0 -0
  37. package/template/src/assets/hdri/photo-studio2.hdr +0 -0
  38. package/template/src/assets/hdri/photo-studio3.hdr +0 -0
  39. package/template/src/assets/objects/keyboardScene/ibm-keyboard.glb +0 -0
  40. package/template/src/assets/wavy-lines.svg +25 -0
  41. package/template/src/entry.ts +20 -0
  42. package/template/src/example_scenes/alternativesScene.ts +88 -0
  43. package/template/src/example_scenes/dependencyScene.ts +116 -0
  44. package/template/src/example_scenes/fourierMachineScene.ts +108 -0
  45. package/template/src/example_scenes/fourierSeriesScene.ts +678 -0
  46. package/template/src/example_scenes/keyboardScene.ts +447 -0
  47. package/template/src/example_scenes/surfaceScene.ts +88 -0
  48. package/template/src/example_scenes/tutorials/easy1.ts +59 -0
  49. package/template/src/example_scenes/tutorials/easy2.ts +141 -0
  50. package/template/src/example_scenes/tutorials/easy3.ts +133 -0
  51. package/template/src/example_scenes/tutorials/medium1.ts +154 -0
  52. package/template/src/example_scenes/vectorField.ts +209 -0
  53. package/template/src/example_scenes/visulizingFunctions.ts +246 -0
  54. package/template/src/main/index.ts +101 -0
  55. package/template/src/main/rendering.ts +219 -0
  56. package/template/src/main/storage.ts +35 -0
  57. package/template/src/preload/index.d.ts +8 -0
  58. package/template/src/preload/index.ts +36 -0
  59. package/template/src/renderer/index.html +17 -0
  60. package/template/src/renderer/src/App.svelte +130 -0
  61. package/template/src/renderer/src/app.css +24 -0
  62. package/template/src/renderer/src/env.d.ts +2 -0
  63. package/template/src/renderer/src/lib/animation/animations.ts +214 -0
  64. package/template/src/renderer/src/lib/animation/captureCanvas.ts +85 -0
  65. package/template/src/renderer/src/lib/animation/helpers.ts +7 -0
  66. package/template/src/renderer/src/lib/animation/interpolations.ts +155 -0
  67. package/template/src/renderer/src/lib/animation/protocols.ts +79 -0
  68. package/template/src/renderer/src/lib/audio/loader.ts +104 -0
  69. package/template/src/renderer/src/lib/fonts/Roboto_Regular.json +1 -0
  70. package/template/src/renderer/src/lib/fonts/montserrat-medium.json +1 -0
  71. package/template/src/renderer/src/lib/fonts/montserrat.json +1 -0
  72. package/template/src/renderer/src/lib/general/helpers.ts +77 -0
  73. package/template/src/renderer/src/lib/general/onDestory.ts +10 -0
  74. package/template/src/renderer/src/lib/mathHelpers/vectors.ts +18 -0
  75. package/template/src/renderer/src/lib/rendering/bumpMaps/noise.ts +84 -0
  76. package/template/src/renderer/src/lib/rendering/helpers.ts +35 -0
  77. package/template/src/renderer/src/lib/rendering/lighting3d.ts +387 -0
  78. package/template/src/renderer/src/lib/rendering/materials.ts +6 -0
  79. package/template/src/renderer/src/lib/rendering/objects/import.ts +148 -0
  80. package/template/src/renderer/src/lib/rendering/objects2d.ts +489 -0
  81. package/template/src/renderer/src/lib/rendering/objects3d.ts +89 -0
  82. package/template/src/renderer/src/lib/rendering/protocols.ts +21 -0
  83. package/template/src/renderer/src/lib/rendering/setup.ts +71 -0
  84. package/template/src/renderer/src/lib/rendering/svg/drawing.ts +213 -0
  85. package/template/src/renderer/src/lib/rendering/svg/parsing.ts +717 -0
  86. package/template/src/renderer/src/lib/rendering/svg/rastered.ts +42 -0
  87. package/template/src/renderer/src/lib/rendering/svgObjects.ts +1137 -0
  88. package/template/src/renderer/src/lib/scene/helpers.ts +89 -0
  89. package/template/src/renderer/src/lib/scene/sceneClass.ts +648 -0
  90. package/template/src/renderer/src/lib/shaders/background_gradient/frag.glsl +12 -0
  91. package/template/src/renderer/src/lib/shaders/background_gradient/vert.glsl +6 -0
  92. package/template/src/renderer/src/lib/shaders/hdri_blur/frag.glsl +45 -0
  93. package/template/src/renderer/src/lib/shaders/hdri_blur/vert.glsl +5 -0
  94. package/template/src/renderer/src/main.ts +9 -0
  95. package/template/svelte.config.mjs +7 -0
  96. package/template/tsconfig.json +4 -0
  97. package/template/tsconfig.node.json +10 -0
  98. package/template/tsconfig.web.json +32 -0
@@ -0,0 +1,648 @@
1
+ import { captureCanvasFrame, triggerEncoder } from '../animation/captureCanvas'
2
+ import {
3
+ createAnim,
4
+ type DependencyUpdater,
5
+ type InternalAnimation,
6
+ type UserAnimation
7
+ } from '../animation/protocols'
8
+ import { generateID, logCameraState } from '../general/helpers'
9
+ import { sleep } from '../rendering/helpers'
10
+ import { createScene } from '../rendering/setup'
11
+ import * as THREE from 'three'
12
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
13
+ import { easeConstant } from '../animation/interpolations'
14
+ import { animationFPSThrottle, renderSkip } from '../../../../entry'
15
+ import { addDestroyFunction } from '../general/onDestory'
16
+ import { ticksToMillis } from '../animation/helpers'
17
+ import { AudioInScene, loadAllAudio, playAudio, registerAudio } from '../audio/loader'
18
+
19
+ export enum SpaceSetting {
20
+ ThreeDim,
21
+ TwoDim
22
+ }
23
+
24
+ export enum HotReloadSetting {
25
+ TraceFromStart,
26
+ BeginFromCurrent
27
+ }
28
+
29
+ type SceneInstruction = (tick: number) => any
30
+
31
+ export let globalContainerRef: HTMLElement
32
+
33
+ export const setGlobalContainerRef = (ref: HTMLElement) => {
34
+ globalContainerRef = ref
35
+ }
36
+
37
+ export class AnimatedScene {
38
+ scene: THREE.Scene
39
+ camera: THREE.PerspectiveCamera | THREE.OrthographicCamera
40
+ renderer: THREE.WebGLRenderer
41
+ private controls: OrbitControls
42
+ private container: HTMLElement
43
+
44
+ sceneRenderTick: number = 0
45
+ private sceneCalculationTick: number = 0
46
+ totalSceneTicks: number = 0
47
+ private sceneAnimations: InternalAnimation[] = []
48
+ private sceneDependencies: DependencyUpdater[] = []
49
+ private sceneInstructions: Map<number, SceneInstruction[]> = new Map()
50
+ private planedSounds: Map<number, AudioInScene[]> = new Map()
51
+
52
+ private pixelsWidth
53
+ private pixelsHeight
54
+
55
+ playEffectFunction: () => any = () => {}
56
+
57
+ isPlaying = false
58
+
59
+ private initialSceneChildren: THREE.Object3D[] = []
60
+ private initialCameraState: {
61
+ position: THREE.Vector3
62
+ rotation: THREE.Euler
63
+ zoom?: number
64
+ left?: number
65
+ right?: number
66
+ top?: number
67
+ bottom?: number
68
+ }
69
+ private initialRendererState: {
70
+ clearColor: THREE.Color
71
+ clearAlpha: number
72
+ shadowMapEnabled: boolean
73
+ }
74
+
75
+ private zoom = 30
76
+ farLimitRender = 1000
77
+
78
+ private buildFunction: (scene: this) => any
79
+
80
+ private traceFromStart: boolean
81
+
82
+ private controlsAnimationFrameId: number | null = null
83
+ private animationFrameId: number | null = null
84
+
85
+ private isBuilding = false
86
+ private isRendering = false
87
+ private doNotPlayAudio = false
88
+ private renderingAudioGather: AudioInScene[] = []
89
+
90
+ constructor(
91
+ pixelsWidth: number,
92
+ pixelsHeight: number,
93
+ spaceSetting: SpaceSetting = SpaceSetting.ThreeDim,
94
+ hotReloadSetting: HotReloadSetting = HotReloadSetting.TraceFromStart,
95
+ buildFunctionGiven: (scene: AnimatedScene) => any
96
+ ) {
97
+ this.container = globalContainerRef
98
+ this.pixelsHeight = pixelsHeight
99
+ this.pixelsWidth = pixelsWidth
100
+ this.traceFromStart = hotReloadSetting === HotReloadSetting.TraceFromStart
101
+
102
+ const threeDim = spaceSetting === SpaceSetting.ThreeDim
103
+
104
+ const { scene, camera, renderer, controls } = createScene(
105
+ globalContainerRef,
106
+ pixelsWidth,
107
+ pixelsHeight,
108
+ threeDim,
109
+ this.zoom,
110
+ this.farLimitRender
111
+ )
112
+
113
+ this.buildFunction = async () => {
114
+ await buildFunctionGiven(this)
115
+ this.end()
116
+ }
117
+
118
+ this.attachScreenSizeListener(globalContainerRef, threeDim)
119
+ // Store initial state
120
+ this.initialSceneChildren = [...scene.children]
121
+ this.initialCameraState = this.captureCameraState(camera)
122
+ this.initialRendererState = {
123
+ clearColor: renderer.getClearColor(new THREE.Color()),
124
+ clearAlpha: renderer.getClearAlpha(),
125
+ shadowMapEnabled: renderer.shadowMap.enabled
126
+ }
127
+
128
+ this.scene = scene
129
+ this.camera = camera
130
+ this.renderer = renderer
131
+ this.controls = controls
132
+
133
+ this.startControls()
134
+
135
+ addDestroyFunction(() => this.onDestroy())
136
+ }
137
+
138
+ onDestroy() {
139
+ this.stopControls()
140
+ if (this.animationFrameId) cancelAnimationFrame(this.animationFrameId)
141
+ }
142
+
143
+ add = (...elements: THREE.Mesh[] | THREE.Group[] | THREE.Object3D[]) => {
144
+ elements.forEach((e) => this.scene.add(e))
145
+ }
146
+
147
+ do(instruction: SceneInstruction) {
148
+ this.appendInstruction(instruction, this.sceneCalculationTick)
149
+ }
150
+
151
+ addAnim(...animations: UserAnimation[]) {
152
+ const longest = Math.max(...animations.map((a) => a.interpolation.length))
153
+ for (const animation of animations) {
154
+ this.appendAnimation(animation)
155
+ }
156
+ this.sceneCalculationTick += longest
157
+ }
158
+
159
+ insertAnimAt(tick: number, ...animations: UserAnimation[]) {
160
+ for (const animation of animations) {
161
+ const internalAnimation: InternalAnimation = {
162
+ startTick: tick,
163
+ endTick: tick + animation.interpolation.length - 1,
164
+ updater: animation.updater,
165
+ interpolation: animation.interpolation
166
+ }
167
+
168
+ this.sceneAnimations.push(internalAnimation)
169
+ }
170
+ }
171
+
172
+ addSequentialBackgroundAnims(...sequentialAnimations: UserAnimation[]) {
173
+ let padding = 0
174
+ for (const animation of sequentialAnimations) {
175
+ this.appendAnimation(animation, padding)
176
+ padding += animation.interpolation.length
177
+ }
178
+ }
179
+
180
+ onEachTick(updater: DependencyUpdater) {
181
+ this.sceneDependencies.push(updater)
182
+ }
183
+
184
+ end() {
185
+ this.totalSceneTicks = this.sceneCalculationTick + 1
186
+ }
187
+
188
+ registerAudio(audioPath: string) {
189
+ registerAudio(audioPath)
190
+ }
191
+
192
+ playAudio(audioPath: string, volume: number = 1) {
193
+ if (this.isBuilding) {
194
+ const listForFrame = this.planedSounds.get(this.sceneCalculationTick)
195
+
196
+ if (!listForFrame) {
197
+ this.planedSounds.set(this.sceneCalculationTick, [
198
+ {
199
+ audioPath,
200
+ atFrame: this.sceneCalculationTick,
201
+ volume
202
+ }
203
+ ])
204
+ } else {
205
+ listForFrame.push({
206
+ audioPath,
207
+ atFrame: this.sceneCalculationTick,
208
+ volume
209
+ })
210
+ }
211
+ } else if (this.isRendering) {
212
+ // Handle rendering scenerio soon
213
+ this.renderingAudioGather.push({
214
+ audioPath,
215
+ volume,
216
+ atFrame: Math.round(this.sceneRenderTick / renderSkip)
217
+ })
218
+ } else if (this.isPlaying && this.doNotPlayAudio === false) {
219
+ playAudio(audioPath, volume)
220
+ }
221
+ }
222
+
223
+ addWait(duration: number) {
224
+ this.addAnim(createAnim(easeConstant(0, duration), () => {}))
225
+ }
226
+
227
+ async jumpToFrameAtIndex(index: number, notSize: boolean = false) {
228
+ this.doNotPlayAudio = true
229
+ this.resetComponents(notSize)
230
+ this.isBuilding = true
231
+ await this.buildFunction(this)
232
+ this.isBuilding = false
233
+
234
+ await loadAllAudio()
235
+
236
+ if (index > this.totalSceneTicks - 1) {
237
+ index = 0
238
+ }
239
+
240
+ if (this.traceFromStart) {
241
+ await this.traceToFrameIndex(index, false)
242
+ } else {
243
+ const allInstructionUntilNow = this.getSceneInstructionsUpToIndex(index - 1)
244
+ for (let i = 0; i < allInstructionUntilNow.length; i++) {
245
+ await allInstructionUntilNow[i].instruction(allInstructionUntilNow[i].key)
246
+ }
247
+ await this.traceCurrentFrame(index, false, false)
248
+ }
249
+
250
+ this.renderCurrentFrame()
251
+ this.sceneRenderTick = index
252
+ await this.playEffectFunction()
253
+
254
+ // console.log('INSTRUCTIONS', this.sceneInstructions)
255
+
256
+ this.doNotPlayAudio = false
257
+ }
258
+
259
+ getAspectRatio() {
260
+ return this.pixelsWidth / this.pixelsHeight
261
+ }
262
+
263
+ private syncControlsWithCamera() {
264
+ // Get the direction vector (works for both camera types)
265
+ const direction = new THREE.Vector3(0, 0, -1)
266
+
267
+ // Use the appropriate transformation based on camera type
268
+ if (this.camera.type === 'OrthographicCamera') {
269
+ direction.transformDirection(this.camera.matrixWorld)
270
+ } else {
271
+ direction.applyQuaternion(this.camera.quaternion)
272
+ }
273
+
274
+ // Calculate the new target (same for both camera types)
275
+ const targetDistance = this.controls.target.distanceTo(this.controls.object.position)
276
+ const newTarget = this.camera.position.clone().add(direction.multiplyScalar(targetDistance))
277
+ this.controls.target.copy(newTarget)
278
+
279
+ // Reset the internal state
280
+ this.controls.update()
281
+ }
282
+
283
+ private startControls() {
284
+ this.controls.enabled = true
285
+ let animateCounter = 0
286
+
287
+ let isInteracting = false
288
+
289
+ // Add these event listeners
290
+ this.controls.addEventListener('start', () => (isInteracting = true))
291
+ this.controls.addEventListener('end', () => (isInteracting = false))
292
+ // Animation loop
293
+ const animate = () => {
294
+ if (this.isPlaying) return
295
+ this.controlsAnimationFrameId = requestAnimationFrame(animate)
296
+
297
+ if (isInteracting) {
298
+ this.controls.update()
299
+ } else {
300
+ //Set current camera state to the controls so its correct when we later interact
301
+ // Get the camera's forward direction.
302
+ const camDirection = new THREE.Vector3()
303
+ this.camera.getWorldDirection(camDirection)
304
+
305
+ // Compute the current distance between camera and controls target.
306
+ const distance = this.controls.target.distanceTo(this.camera.position)
307
+
308
+ // Define the new target using the same distance.
309
+ const newTarget = new THREE.Vector3()
310
+ .copy(this.camera.position)
311
+ .add(camDirection.multiplyScalar(distance))
312
+
313
+ // Update the controls with the new target.
314
+ this.controls.target.copy(newTarget)
315
+ this.controls.update()
316
+ }
317
+
318
+ this.renderCurrentFrame()
319
+ animateCounter++
320
+
321
+ if (animateCounter % 10 === 0) {
322
+ logCameraState(this.camera)
323
+ }
324
+ }
325
+ animate()
326
+ }
327
+
328
+ private stopControls() {
329
+ this.controls.enabled = false
330
+ if (this.controlsAnimationFrameId !== null) {
331
+ cancelAnimationFrame(this.controlsAnimationFrameId)
332
+ }
333
+ }
334
+
335
+ private attachScreenSizeListener(container: HTMLElement, threeDim: boolean) {
336
+ const internalAspect = this.pixelsWidth / this.pixelsHeight
337
+ // Resize handler
338
+ const handleResize = () => {
339
+ const newWidth = container.clientWidth
340
+ const newHeight = container.clientHeight
341
+
342
+ if (threeDim && this.camera instanceof THREE.PerspectiveCamera) {
343
+ this.camera.aspect = internalAspect
344
+ } else if (this.camera instanceof THREE.OrthographicCamera) {
345
+ this.camera.left = -this.zoom * internalAspect
346
+ this.camera.right = this.zoom * internalAspect
347
+ }
348
+
349
+ this.camera.updateProjectionMatrix()
350
+ this.renderer.setSize(newWidth, newHeight)
351
+ this.renderer.render(this.scene, this.camera)
352
+ }
353
+ window.addEventListener('resize', handleResize)
354
+ }
355
+
356
+ pause() {
357
+ this.isPlaying = false
358
+ if (this.animationFrameId) cancelAnimationFrame(this.animationFrameId)
359
+
360
+ this.syncControlsWithCamera()
361
+
362
+ this.startControls()
363
+ }
364
+
365
+ async render() {
366
+ this.isRendering = true
367
+ this.isPlaying = true
368
+ this.stopControls()
369
+ const renderName = generateID(10)
370
+
371
+ const cpu_free_time = 5
372
+ const div = this.container
373
+ const originalPosition = div.style.position
374
+ const originalTop = div.style.top
375
+ const originalLeft = div.style.left
376
+ const originalZIndex = div.style.zIndex
377
+ // Set to position absolute
378
+ div.style.position = 'absolute'
379
+ div.style.top = '0' // Or whatever values you need
380
+ div.style.left = '0'
381
+ div.style.zIndex = '999' // Optional, to ensure it's on top
382
+ div.style.opacity = '0'
383
+
384
+ this.renderer.setSize(this.pixelsWidth, this.pixelsHeight, true)
385
+
386
+ window.scrollTo(0, 0)
387
+ const startFrame = 0
388
+ await this.jumpToFrameAtIndex(startFrame, true)
389
+ for (let i = startFrame; i < this.totalSceneTicks; i++) {
390
+ this.sceneRenderTick = i
391
+ //To not trace start frame twice
392
+
393
+ await this.traceCurrentFrame(this.sceneRenderTick, true, i === startFrame)
394
+
395
+ if (this.sceneRenderTick % renderSkip === 0) {
396
+ this.renderCurrentFrame()
397
+ await captureCanvasFrame(
398
+ Math.round(this.sceneRenderTick / renderSkip),
399
+ renderName,
400
+ this.renderer
401
+ )
402
+ }
403
+ await this.playEffectFunction()
404
+ if (i % 10 === 0) {
405
+ await sleep(cpu_free_time)
406
+ }
407
+ }
408
+
409
+ triggerEncoder(this.pixelsWidth, this.pixelsHeight, this.renderingAudioGather)
410
+
411
+ this.renderingAudioGather = []
412
+ this.isRendering = false
413
+
414
+ div.style.opacity = '1'
415
+
416
+ // Restore original positioning
417
+ div.style.position = originalPosition
418
+ div.style.top = originalTop
419
+ div.style.left = originalLeft
420
+ div.style.zIndex = originalZIndex
421
+
422
+ this.renderer.setSize(this.container.clientWidth, this.container.clientHeight)
423
+ await this.jumpToFrameAtIndex(0)
424
+ this.renderCurrentFrame()
425
+
426
+ this.isPlaying = false
427
+ this.startControls()
428
+ }
429
+
430
+ play() {
431
+ this.playSequenceOfAnimation(0, this.totalSceneTicks - 1)
432
+ }
433
+
434
+ async playSequenceOfAnimation(fromFrame: number, toFrame: number) {
435
+ this.isPlaying = true
436
+ this.stopControls()
437
+ await this.jumpToFrameAtIndex(fromFrame)
438
+ logCameraState(this.camera)
439
+
440
+ let currentFrame = fromFrame
441
+ let numberCalledAnimate = 0
442
+ const animate = async (trace: boolean) => {
443
+ if (!this.isPlaying) return
444
+ if (currentFrame <= toFrame) {
445
+ if (numberCalledAnimate % animationFPSThrottle === 0) {
446
+ this.sceneRenderTick = currentFrame
447
+ //To not apply trace twice if we just jumped to startframe (and thus tranced it)
448
+ await this.traceCurrentFrame(this.sceneRenderTick, true, !trace)
449
+ this.renderCurrentFrame()
450
+ currentFrame++
451
+ await this.playEffectFunction()
452
+ }
453
+ numberCalledAnimate++
454
+ this.animationFrameId = requestAnimationFrame(async () => await animate(true))
455
+ } else {
456
+ await this.jumpToFrameAtIndex(0)
457
+ currentFrame = 0
458
+ this.animationFrameId = requestAnimationFrame(async () => await animate(false))
459
+ }
460
+ }
461
+
462
+ this.animationFrameId = requestAnimationFrame(() => animate(false))
463
+ }
464
+
465
+ renderCurrentFrame() {
466
+ //ANALYZE THIS LINE
467
+ this.camera.updateProjectionMatrix()
468
+ this.renderer.render(this.scene, this.camera)
469
+ }
470
+
471
+ private async traceToFrameIndex(index: number, withAudio: boolean) {
472
+ //Trace all actions
473
+ for (let traceTick = 0; traceTick <= index; traceTick++) {
474
+ await this.traceCurrentFrame(traceTick, withAudio, false)
475
+ }
476
+ }
477
+
478
+ private async traceCurrentFrame(index: number, withAudio: boolean, onlyAudio: boolean) {
479
+ if (withAudio) {
480
+ const soundsForTick = this.planedSounds.get(index)
481
+ if (soundsForTick) {
482
+ for (const sound of soundsForTick) {
483
+ this.playAudio(sound.audioPath, sound.volume)
484
+ }
485
+ }
486
+ }
487
+ if (onlyAudio) return
488
+ //Trace all actions
489
+ const frameInstructions = this.sceneInstructions.get(index)
490
+ if (frameInstructions) {
491
+ for (let i = 0; i < frameInstructions.length; i++) {
492
+ await frameInstructions[i](index)
493
+ }
494
+ }
495
+ const animationsForFrame = this.getActiveAnimationsForTick(index)
496
+ for (let a = 0; a < animationsForFrame.length; a++) {
497
+ const localInterpolationIndex = index - animationsForFrame[a].startTick
498
+ await animationsForFrame[a].updater(
499
+ animationsForFrame[a].interpolation[localInterpolationIndex],
500
+ index,
501
+ localInterpolationIndex === animationsForFrame[a].interpolation.length - 1
502
+ )
503
+ }
504
+
505
+ for (let d = 0; d < this.sceneDependencies.length; d++) {
506
+ await this.sceneDependencies[d](index, ticksToMillis(index))
507
+ }
508
+ }
509
+
510
+ private getActiveAnimationsForTick(sceneTick: number): InternalAnimation[] {
511
+ return this.sceneAnimations.filter(
512
+ (anim) => anim.startTick <= sceneTick && anim.endTick >= sceneTick
513
+ )
514
+ }
515
+
516
+ private appendAnimation(userAnimation: UserAnimation, paddedTick: number = 0) {
517
+ const internalAnimation: InternalAnimation = {
518
+ startTick: paddedTick + this.sceneCalculationTick,
519
+ endTick: paddedTick + this.sceneCalculationTick + userAnimation.interpolation.length - 1,
520
+ updater: userAnimation.updater,
521
+ interpolation: userAnimation.interpolation
522
+ }
523
+
524
+ this.sceneAnimations.push(internalAnimation)
525
+ }
526
+
527
+ private appendInstruction(instruction: SceneInstruction, atTick: number) {
528
+ // Check if the map already has an entry for this tick.
529
+ if (this.sceneInstructions.has(atTick)) {
530
+ // If yes, append the new instruction to the existing list.
531
+ this.sceneInstructions.get(atTick)!.push(instruction)
532
+ } else {
533
+ // Otherwise, create a new list with this instruction and add it to the map.
534
+ this.sceneInstructions.set(atTick, [instruction])
535
+ }
536
+ }
537
+
538
+ // Replace recreateComponents with reset logic
539
+ private resetComponents(notSize: boolean) {
540
+ this.resetSceneVars()
541
+ this.resetScene()
542
+ this.resetCamera()
543
+ this.resetRenderer(notSize)
544
+ }
545
+
546
+ private resetSceneVars() {
547
+ this.sceneRenderTick = 0
548
+ this.sceneCalculationTick = 0
549
+ this.totalSceneTicks = 0
550
+ this.sceneAnimations = []
551
+ this.sceneDependencies = []
552
+ this.sceneInstructions = new Map()
553
+ this.planedSounds = new Map()
554
+ }
555
+
556
+ private captureCameraState(camera: THREE.Camera) {
557
+ const state: typeof this.initialCameraState = {
558
+ position: camera.position.clone(),
559
+ rotation: camera.rotation.clone()
560
+ }
561
+
562
+ if (camera instanceof THREE.OrthographicCamera) {
563
+ state.zoom = camera.zoom
564
+ state.left = camera.left
565
+ state.right = camera.right
566
+ state.top = camera.top
567
+ state.bottom = camera.bottom
568
+ } else if (camera instanceof THREE.PerspectiveCamera) {
569
+ state.zoom = camera.zoom
570
+ }
571
+
572
+ return state
573
+ }
574
+
575
+ private resetScene() {
576
+ // Remove all non-initial objects
577
+ const currentChildren = [...this.scene.children]
578
+ currentChildren.forEach((child) => {
579
+ if (!this.initialSceneChildren.includes(child)) {
580
+ this.scene.remove(child)
581
+ // Dispose geometry and materials if needed
582
+ if (child instanceof THREE.Mesh) {
583
+ child.geometry?.dispose()
584
+ if (Array.isArray(child.material)) {
585
+ child.material.forEach((m) => m.dispose())
586
+ } else {
587
+ child.material?.dispose()
588
+ }
589
+ }
590
+ }
591
+ })
592
+ }
593
+
594
+ private resetCamera() {
595
+ const cam = this.camera
596
+ cam.position.copy(this.initialCameraState.position)
597
+ cam.rotation.copy(this.initialCameraState.rotation)
598
+
599
+ if (cam instanceof THREE.OrthographicCamera) {
600
+ cam.zoom = this.initialCameraState.zoom!
601
+ cam.left = this.initialCameraState.left!
602
+ cam.right = this.initialCameraState.right!
603
+ cam.top = this.initialCameraState.top!
604
+ cam.bottom = this.initialCameraState.bottom!
605
+ cam.updateProjectionMatrix()
606
+ }
607
+
608
+ if (cam instanceof THREE.PerspectiveCamera) {
609
+ cam.zoom = this.initialCameraState.zoom!
610
+ }
611
+ }
612
+
613
+ private resetRenderer(notSize: boolean) {
614
+ if (!notSize) {
615
+ this.renderer.setSize(this.container.clientWidth, this.container.clientHeight)
616
+ }
617
+
618
+ this.renderer.setClearColor(
619
+ this.initialRendererState.clearColor,
620
+ this.initialRendererState.clearAlpha
621
+ )
622
+ this.renderer.shadowMap.enabled = this.initialRendererState.shadowMapEnabled
623
+ }
624
+
625
+ private getSceneInstructionsUpToIndex(
626
+ index: number
627
+ ): Array<{ key: number; instruction: SceneInstruction }> {
628
+ // Filter keys that are less than or equal to the provided index and sort them in ascending order.
629
+ const sortedKeys = Array.from(this.sceneInstructions.keys())
630
+ .filter((key) => key <= index)
631
+ .sort((a, b) => a - b)
632
+
633
+ // Create a result array to hold objects that couple each key with its corresponding instruction.
634
+ const coupledInstructions: Array<{ key: number; instruction: SceneInstruction }> = []
635
+
636
+ // For each key, retrieve its instructions and push an object for each instruction.
637
+ sortedKeys.forEach((key) => {
638
+ const instructions = this.sceneInstructions.get(key)
639
+ if (instructions) {
640
+ instructions.forEach((instruction) => {
641
+ coupledInstructions.push({ key, instruction })
642
+ })
643
+ }
644
+ })
645
+
646
+ return coupledInstructions
647
+ }
648
+ }
@@ -0,0 +1,12 @@
1
+ uniform vec3 topColor;
2
+ uniform vec3 bottomColor;
3
+ uniform float opacity;
4
+
5
+ varying vec3 vWorldPosition;
6
+
7
+ void main() {
8
+ vec3 direction = normalize(vWorldPosition);
9
+ float gradient = (direction.y + 1.0) * 0.5;
10
+ vec3 color = mix(bottomColor, topColor, gradient);
11
+ gl_FragColor = vec4(color, opacity);
12
+ }
@@ -0,0 +1,6 @@
1
+ varying vec3 vWorldPosition;
2
+ void main() {
3
+ vec4 worldPosition = modelMatrix * vec4(position, 1.0);
4
+ vWorldPosition = worldPosition.xyz;
5
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
6
+ }
@@ -0,0 +1,45 @@
1
+ varying vec2 vUv;
2
+ uniform sampler2D uTexture;
3
+ uniform float uBlurAmount;
4
+ uniform vec2 uTextureSize;
5
+ uniform float sigma; // Missing uniform declaration
6
+ uniform float opacity;
7
+ uniform float uSaturation;
8
+
9
+ // Add these at the top before calculateWeight()
10
+ const float PI = 3.141592653589793;
11
+
12
+ float calculateWeight(float x) {
13
+ return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * PI) * sigma);
14
+ }
15
+
16
+ void main() {
17
+ vec2 texelSize = (1.0 / uTextureSize) * uBlurAmount;
18
+ vec4 totalColor = vec4(0.0);
19
+
20
+ // Generate weights
21
+ float weights[11];
22
+ float weightSum = 0.0;
23
+
24
+ // Calculate raw weights
25
+ for(int i = 0; i < 11; i++) {
26
+ float x = float(i - 5);
27
+ weights[i] = calculateWeight(x);
28
+ weightSum += weights[i];
29
+ }
30
+
31
+ // Normalize 1D weights
32
+ for(int i = 0; i < 11; i++) {
33
+ weights[i] /= weightSum;
34
+ }
35
+
36
+ for(int x = -5; x <= 5; x++) {
37
+ for(int y = -5; y <= 5; y++) {
38
+ vec2 offset = vec2(float(x), float(y)) * texelSize;
39
+ float weight = weights[x + 5] * weights[y + 5];
40
+ totalColor += texture2D(uTexture, vUv + offset) * weight;
41
+ }
42
+ }
43
+
44
+ gl_FragColor = totalColor * opacity;
45
+ }