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.
- package/bin/index.js +3 -0
- package/package.json +37 -0
- package/src/cli.js +100 -0
- package/template/.editorconfig +9 -0
- package/template/.prettierignore +6 -0
- package/template/.prettierrc.yaml +10 -0
- package/template/_gitignore +10 -0
- package/template/build/entitlements.mac.plist +12 -0
- package/template/build/icon.icns +0 -0
- package/template/build/icon.ico +0 -0
- package/template/build/icon.png +0 -0
- package/template/electron-builder.yml +43 -0
- package/template/electron.vite.config.ts +50 -0
- package/template/eslint.config.mjs +24 -0
- package/template/package-lock.json +10299 -0
- package/template/package.json +64 -0
- package/template/resources/icon.png +0 -0
- package/template/src/assets/audio/fadeSound.mp3 +0 -0
- package/template/src/assets/audio/fadeSound2.mp3 +0 -0
- package/template/src/assets/audio/interstellar.mp3 +0 -0
- package/template/src/assets/audio/keyboard1.mp3 +0 -0
- package/template/src/assets/audio/keyboard2.mp3 +0 -0
- package/template/src/assets/audio/keyboard3.mp3 +0 -0
- package/template/src/assets/audio/tick_sound.mp3 +0 -0
- package/template/src/assets/base.css +67 -0
- package/template/src/assets/electron.svg +10 -0
- package/template/src/assets/fonts/Geo-Regular.woff +0 -0
- package/template/src/assets/fonts/Montserrat-Italic-VariableFont_wght.woff2 +0 -0
- package/template/src/assets/fonts/Montserrat-Medium.ttf +0 -0
- package/template/src/assets/fonts/Montserrat-Medium.woff +0 -0
- package/template/src/assets/fonts/Montserrat-VariableFont_wght.woff2 +0 -0
- package/template/src/assets/fonts/glitch.ttf +0 -0
- package/template/src/assets/hdri/indoor1.hdr +0 -0
- package/template/src/assets/hdri/metro1.hdr +0 -0
- package/template/src/assets/hdri/outdoor1.hdr +0 -0
- package/template/src/assets/hdri/photo-studio1.hdr +0 -0
- package/template/src/assets/hdri/photo-studio2.hdr +0 -0
- package/template/src/assets/hdri/photo-studio3.hdr +0 -0
- package/template/src/assets/objects/keyboardScene/ibm-keyboard.glb +0 -0
- package/template/src/assets/wavy-lines.svg +25 -0
- package/template/src/entry.ts +20 -0
- package/template/src/example_scenes/alternativesScene.ts +88 -0
- package/template/src/example_scenes/dependencyScene.ts +116 -0
- package/template/src/example_scenes/fourierMachineScene.ts +108 -0
- package/template/src/example_scenes/fourierSeriesScene.ts +678 -0
- package/template/src/example_scenes/keyboardScene.ts +447 -0
- package/template/src/example_scenes/surfaceScene.ts +88 -0
- package/template/src/example_scenes/tutorials/easy1.ts +59 -0
- package/template/src/example_scenes/tutorials/easy2.ts +141 -0
- package/template/src/example_scenes/tutorials/easy3.ts +133 -0
- package/template/src/example_scenes/tutorials/medium1.ts +154 -0
- package/template/src/example_scenes/vectorField.ts +209 -0
- package/template/src/example_scenes/visulizingFunctions.ts +246 -0
- package/template/src/main/index.ts +101 -0
- package/template/src/main/rendering.ts +219 -0
- package/template/src/main/storage.ts +35 -0
- package/template/src/preload/index.d.ts +8 -0
- package/template/src/preload/index.ts +36 -0
- package/template/src/renderer/index.html +17 -0
- package/template/src/renderer/src/App.svelte +130 -0
- package/template/src/renderer/src/app.css +24 -0
- package/template/src/renderer/src/env.d.ts +2 -0
- package/template/src/renderer/src/lib/animation/animations.ts +214 -0
- package/template/src/renderer/src/lib/animation/captureCanvas.ts +85 -0
- package/template/src/renderer/src/lib/animation/helpers.ts +7 -0
- package/template/src/renderer/src/lib/animation/interpolations.ts +155 -0
- package/template/src/renderer/src/lib/animation/protocols.ts +79 -0
- package/template/src/renderer/src/lib/audio/loader.ts +104 -0
- package/template/src/renderer/src/lib/fonts/Roboto_Regular.json +1 -0
- package/template/src/renderer/src/lib/fonts/montserrat-medium.json +1 -0
- package/template/src/renderer/src/lib/fonts/montserrat.json +1 -0
- package/template/src/renderer/src/lib/general/helpers.ts +77 -0
- package/template/src/renderer/src/lib/general/onDestory.ts +10 -0
- package/template/src/renderer/src/lib/mathHelpers/vectors.ts +18 -0
- package/template/src/renderer/src/lib/rendering/bumpMaps/noise.ts +84 -0
- package/template/src/renderer/src/lib/rendering/helpers.ts +35 -0
- package/template/src/renderer/src/lib/rendering/lighting3d.ts +387 -0
- package/template/src/renderer/src/lib/rendering/materials.ts +6 -0
- package/template/src/renderer/src/lib/rendering/objects/import.ts +148 -0
- package/template/src/renderer/src/lib/rendering/objects2d.ts +489 -0
- package/template/src/renderer/src/lib/rendering/objects3d.ts +89 -0
- package/template/src/renderer/src/lib/rendering/protocols.ts +21 -0
- package/template/src/renderer/src/lib/rendering/setup.ts +71 -0
- package/template/src/renderer/src/lib/rendering/svg/drawing.ts +213 -0
- package/template/src/renderer/src/lib/rendering/svg/parsing.ts +717 -0
- package/template/src/renderer/src/lib/rendering/svg/rastered.ts +42 -0
- package/template/src/renderer/src/lib/rendering/svgObjects.ts +1137 -0
- package/template/src/renderer/src/lib/scene/helpers.ts +89 -0
- package/template/src/renderer/src/lib/scene/sceneClass.ts +648 -0
- package/template/src/renderer/src/lib/shaders/background_gradient/frag.glsl +12 -0
- package/template/src/renderer/src/lib/shaders/background_gradient/vert.glsl +6 -0
- package/template/src/renderer/src/lib/shaders/hdri_blur/frag.glsl +45 -0
- package/template/src/renderer/src/lib/shaders/hdri_blur/vert.glsl +5 -0
- package/template/src/renderer/src/main.ts +9 -0
- package/template/svelte.config.mjs +7 -0
- package/template/tsconfig.json +4 -0
- package/template/tsconfig.node.json +10 -0
- 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,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
|
+
}
|