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,77 @@
1
+ import type { AnimatedScene } from '../scene/sceneClass'
2
+ import * as THREE from 'three'
3
+
4
+ const frameValueString = 'frameValueIndex'
5
+
6
+ export const generateID = (numCharacters: number = 10) =>
7
+ Math.random().toString(numCharacters).substr(2, 9)
8
+
9
+ export const updateStateInUrl = (stateValue: number) => {
10
+ const url = new URL(window.location.href)
11
+ url.searchParams.set(frameValueString, stateValue.toString())
12
+ window.history.replaceState(null, '', url.toString())
13
+ }
14
+
15
+ export const setStateInScene = async (scene: AnimatedScene) => {
16
+ const url = new URL(window.location.href)
17
+ const stateParam = url.searchParams.get(frameValueString)
18
+
19
+ if (stateParam) {
20
+ const stateValue = parseInt(stateParam, 10)
21
+
22
+ if (!isNaN(stateValue)) {
23
+ console.log('Restored state:', stateValue)
24
+ await scene.jumpToFrameAtIndex(stateValue)
25
+ return
26
+ } else {
27
+ console.error('Invalid state parameter in URL')
28
+ }
29
+ }
30
+ await scene.jumpToFrameAtIndex(0)
31
+ }
32
+
33
+ let lastStateText = ''
34
+
35
+ export const logCameraState = (
36
+ camera: THREE.PerspectiveCamera | THREE.OrthographicCamera
37
+ ): void => {
38
+ // Clone all values to prevent reference-related issues
39
+ const position = camera.position.clone()
40
+ const rotation = camera.rotation.clone()
41
+ const quaternion = camera.quaternion.clone()
42
+
43
+ // Format numbers to 7 significant digits and create code-ready string
44
+ const stateCode = `
45
+ scene.camera.position.set(
46
+ ${position.x.toPrecision(7)},
47
+ ${position.y.toPrecision(7)},
48
+ ${position.z.toPrecision(7)}
49
+ );
50
+
51
+ scene.camera.rotation.set(
52
+ ${rotation.x.toPrecision(7)},
53
+ ${rotation.y.toPrecision(7)},
54
+ ${rotation.z.toPrecision(7)}
55
+ );
56
+
57
+ scene.camera.quaternion.set(
58
+ ${quaternion.x.toPrecision(7)},
59
+ ${quaternion.y.toPrecision(7)},
60
+ ${quaternion.z.toPrecision(7)},
61
+ ${quaternion.w.toPrecision(7)}
62
+ );
63
+
64
+ scene.camera.rotation.order = '${rotation.order}';
65
+ `
66
+
67
+ // Only update DOM if state changed
68
+ if (stateCode !== lastStateText) {
69
+ lastStateText = stateCode
70
+ const output = document.getElementById('cameraPositionTextID')
71
+ if (output) {
72
+ output.textContent = stateCode
73
+ // Add CSS to preserve whitespace and line breaks
74
+ output.style.whiteSpace = 'pre'
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,10 @@
1
+ let allDestroyFunctionsToCall: (() => any)[] = []
2
+
3
+ export const addDestroyFunction = (func: () => any) => allDestroyFunctionsToCall.push(func)
4
+
5
+ export const callAllDestroyFunctions = async () => {
6
+ for (let i = 0; i < allDestroyFunctionsToCall.length; i++) {
7
+ await allDestroyFunctionsToCall[i]()
8
+ }
9
+ allDestroyFunctionsToCall = []
10
+ }
@@ -0,0 +1,18 @@
1
+ // NON TESTED FUNCTIONS!
2
+
3
+ /**
4
+ * Generate `num` evenly‑spaced numbers from `start` to `end` (inclusive).
5
+ */
6
+ export function linspace(start: number, end: number, num: number): number[] {
7
+ if (num < 2) return num === 1 ? [start] : []
8
+ const step = (end - start) / (num - 1)
9
+ return Array.from({ length: num }, (_v, i) => start + step * i)
10
+ }
11
+
12
+ /**
13
+ * Like numpy.arange: start (inclusive) to stop (exclusive) by step.
14
+ */
15
+ export function arange(start: number, stop: number, step = 1): number[] {
16
+ const length = Math.max(Math.ceil((stop - start) / step), 0)
17
+ return Array.from({ length }, (_v, i) => start + step * i)
18
+ }
@@ -0,0 +1,84 @@
1
+ import * as THREE from 'three'
2
+
3
+ export interface BumpMapOptions {
4
+ /** Width of the bump map canvas (default: 256) */
5
+ width?: number
6
+ /** Height of the bump map canvas (default: 256) */
7
+ height?: number
8
+ /**
9
+ * Noise algorithm to use; currently supports:
10
+ * - "random": pure random noise (default)
11
+ * - "perlin": placeholder (requires additional implementation)
12
+ */
13
+ noiseAlgorithm?: 'random' | 'perlin'
14
+ /**
15
+ * Intensity multiplier for the noise values.
16
+ * Higher values result in a wider contrast between low and high bumps (default: 1).
17
+ */
18
+ intensity?: number
19
+ }
20
+
21
+ /**
22
+ * Generates a bump map as a THREE.CanvasTexture.
23
+ *
24
+ * @param options - Customization options for the bump map
25
+ * @returns A THREE.CanvasTexture that can be assigned as a bumpMap in a material.
26
+ */
27
+ export function createBumpMap(options?: BumpMapOptions): THREE.CanvasTexture {
28
+ // Set default options
29
+ const { width = 256, height = 256, noiseAlgorithm = 'random', intensity = 1 } = options || {}
30
+
31
+ // Create a canvas element and get its 2d rendering context
32
+ const canvas: HTMLCanvasElement = document.createElement('canvas')
33
+ canvas.width = width
34
+ canvas.height = height
35
+ const ctx = canvas.getContext('2d')
36
+
37
+ if (!ctx) {
38
+ throw new Error('Could not get canvas 2D context.')
39
+ }
40
+
41
+ // Create an ImageData object to manipulate pixel values directly
42
+ const imageData = ctx.createImageData(width, height)
43
+ const data = imageData.data
44
+
45
+ // Depending on the noise algorithm choose how to fill the canvas
46
+ if (noiseAlgorithm === 'random') {
47
+ // For each pixel, set a random grayscale value
48
+ for (let i = 0; i < width * height; i++) {
49
+ // Compute the index in the data array. Each pixel takes 4 values (RGBA)
50
+ const index = i * 4
51
+ // A random value in [0, 255] multiplied by the intensity factor,
52
+ // clamped to the [0, 255] range.
53
+ const value = Math.min(255, Math.floor(Math.random() * 255 * intensity))
54
+ data[index] = value // R
55
+ data[index + 1] = value // G
56
+ data[index + 2] = value // B
57
+ data[index + 3] = 255 // A (opaque)
58
+ }
59
+ } else if (noiseAlgorithm === 'perlin') {
60
+ // Placeholder for perlin noise: you would implement or integrate
61
+ // a Perlin noise function here. For now, we'll default to random noise.
62
+ console.warn('Perlin noise algorithm not implemented. Falling back to random noise.')
63
+ for (let i = 0; i < width * height; i++) {
64
+ const index = i * 4
65
+ const value = Math.min(255, Math.floor(Math.random() * 255 * intensity))
66
+ data[index] = value
67
+ data[index + 1] = value
68
+ data[index + 2] = value
69
+ data[index + 3] = 255
70
+ }
71
+ }
72
+
73
+ // Put the generated pixel data back into the canvas
74
+ ctx.putImageData(imageData, 0, 0)
75
+
76
+ // Create a THREE.CanvasTexture from the canvas and return it.
77
+ const bumpTexture = new THREE.CanvasTexture(canvas)
78
+
79
+ // Optionally, if you need specific properties such as wrapping:
80
+ // bumpTexture.wrapS = THREE.RepeatWrapping;
81
+ // bumpTexture.wrapT = THREE.RepeatWrapping;
82
+
83
+ return bumpTexture
84
+ }
@@ -0,0 +1,35 @@
1
+ import * as THREE from 'three'
2
+
3
+ export const COLORS = {
4
+ red: 0xff0000,
5
+ green: 0x00ff00,
6
+ blue: 0x0000ff,
7
+ white: 0xffffff,
8
+ black: 0x000000,
9
+ yellow: 0xffff00,
10
+ cyan: 0x00ffff,
11
+ magenta: 0xff00ff,
12
+ silver: 0xc0c0c0,
13
+ gray: 0x808080,
14
+ grey: 0x808080,
15
+ orange: 0xffa500,
16
+ pink: 0xffc0cb,
17
+ purple: 0x800080,
18
+ brown: 0xa52a2a,
19
+ gold: 0xffd700,
20
+ indigo: 0x4b0082,
21
+ violet: 0xee82ee,
22
+ maroon: 0x800000,
23
+ teal: 0x008080,
24
+ lime: 0x00ff00,
25
+ olive: 0x808000,
26
+ navy: 0x000080
27
+ }
28
+
29
+ export function sleep(ms: number) {
30
+ return new Promise((resolve) => setTimeout(resolve, ms))
31
+ }
32
+
33
+ export const hexColor = (hex: string): THREE.Color => {
34
+ return new THREE.Color(hex)
35
+ }
@@ -0,0 +1,387 @@
1
+ import * as THREE from 'three'
2
+ import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
3
+ import _photoStudio1 from '$assets/hdri/photo-studio1.hdr?url'
4
+ import _photoStudio2 from '$assets/hdri/photo-studio2.hdr?url'
5
+ import _photoStudio3 from '$assets/hdri/photo-studio3.hdr?url'
6
+
7
+ import _outdoor1 from '$assets/hdri/outdoor1.hdr?url'
8
+ import _indoor1 from '$assets/hdri/indoor1.hdr?url'
9
+
10
+ import _metro1 from '$assets/hdri/metro1.hdr?url'
11
+
12
+ import { AnimatedScene } from '../scene/sceneClass'
13
+ import vert_blur_hdri from '../shaders/hdri_blur/vert.glsl?raw'
14
+ import frag_blur_hdri from '../shaders/hdri_blur/frag.glsl?raw'
15
+
16
+ import vert_background_gradient from '../shaders/background_gradient/vert.glsl?raw'
17
+ import frag_background_gradient from '../shaders/background_gradient/frag.glsl?raw'
18
+ import { COLORS } from './helpers'
19
+
20
+ /**
21
+ * Configuration options for the scene lighting
22
+ */
23
+ interface LightingOptions {
24
+ /** Whether to add ambient light (default: true) */
25
+ addAmbient?: boolean
26
+ /** Whether to add directional lights (default: true) */
27
+ addDirectional?: boolean
28
+ /** Whether to add spot lights (default: true) */
29
+ addSpot?: boolean
30
+ /** Whether to add light helper objects (default: false) */
31
+ addHelpers?: boolean
32
+ /** Preset color scheme (default: 'cool') */
33
+ colorScheme?: 'cool' | 'warm' | 'contrast' | 'studio' | 'dramatic'
34
+ /** Base intensity multiplier for all lights (default: 1.0) */
35
+ intensity?: number
36
+ }
37
+
38
+ /**
39
+ * Collection of lights created by the lighting function
40
+ */
41
+ interface LightCollection {
42
+ ambient: THREE.AmbientLight | null
43
+ directional: THREE.DirectionalLight[]
44
+ spot: THREE.SpotLight[]
45
+ helpers: THREE.Object3D[]
46
+ }
47
+
48
+ /**
49
+ * Color scheme definition
50
+ */
51
+ interface ColorScheme {
52
+ ambient: number
53
+ key: number
54
+ fill: number
55
+ rim: number
56
+ }
57
+
58
+ /**
59
+ * Adds multiple light sources to create a visually appealing lighting setup
60
+ * Scaled appropriately for a standard THREE.GridHelper scene
61
+ *
62
+ * @param scene - The Three.js scene to add lights to
63
+ * @param options - Optional configuration settings
64
+ * @returns Collection of created lights for further manipulation
65
+ */
66
+ export function addSceneLighting(
67
+ scene: THREE.Scene,
68
+ options: LightingOptions = {}
69
+ ): LightCollection {
70
+ // Set default options
71
+ const config: Required<LightingOptions> = {
72
+ addAmbient: true,
73
+ addDirectional: true,
74
+ addSpot: true,
75
+ addHelpers: false,
76
+ colorScheme: 'cool',
77
+ intensity: 1.0,
78
+ ...options
79
+ }
80
+
81
+ // Store all created lights for reference
82
+ const lights: LightCollection = {
83
+ ambient: null,
84
+ directional: [],
85
+ spot: [],
86
+ helpers: []
87
+ }
88
+
89
+ // Define color schemes
90
+ const colorSchemes: Record<Required<LightingOptions>['colorScheme'], ColorScheme> = {
91
+ cool: {
92
+ ambient: 0x445570,
93
+ key: 0xffffff,
94
+ fill: 0x6495ed, // cornflower blue
95
+ rim: 0x00ffff // cyan
96
+ },
97
+ warm: {
98
+ ambient: 0x553322,
99
+ key: 0xffcc88,
100
+ fill: 0xff8844,
101
+ rim: 0xff3333
102
+ },
103
+ contrast: {
104
+ ambient: 0x222222,
105
+ key: 0xffffff,
106
+ fill: 0x00aaff,
107
+ rim: 0xff0088
108
+ },
109
+ studio: {
110
+ ambient: 0xaaaaaa,
111
+ key: 0xffffff,
112
+ fill: 0xdddddd,
113
+ rim: 0x8888ff
114
+ },
115
+ dramatic: {
116
+ ambient: 0x111122,
117
+ key: 0xffffaa,
118
+ fill: 0x2222ff,
119
+ rim: 0xff00ff
120
+ }
121
+ }
122
+
123
+ // Get the selected color scheme
124
+ const colors = colorSchemes[config.colorScheme]
125
+
126
+ // Add ambient light
127
+ if (config.addAmbient) {
128
+ const ambient = new THREE.AmbientLight(colors.ambient, 0.5 * config.intensity)
129
+ scene.add(ambient)
130
+ lights.ambient = ambient
131
+ }
132
+
133
+ // Add directional lights (key, fill, rim setup)
134
+ if (config.addDirectional) {
135
+ // Key light (main light)
136
+ const keyLight = new THREE.DirectionalLight(colors.key, 1.0 * config.intensity)
137
+ // Position scaled for standard grid (10x10)
138
+ keyLight.position.set(5, 7, 5)
139
+ keyLight.castShadow = true
140
+
141
+ // Configure shadows for better quality
142
+ keyLight.shadow.mapSize.width = 2048
143
+ keyLight.shadow.mapSize.height = 2048
144
+ keyLight.shadow.camera.near = 0.5
145
+ keyLight.shadow.camera.far = 30
146
+ keyLight.shadow.camera.left = -10
147
+ keyLight.shadow.camera.right = 10
148
+ keyLight.shadow.camera.top = 10
149
+ keyLight.shadow.camera.bottom = -10
150
+ keyLight.shadow.bias = -0.0001
151
+
152
+ scene.add(keyLight)
153
+ lights.directional.push(keyLight)
154
+
155
+ // Fill light (secondary light to fill shadows)
156
+ const fillLight = new THREE.DirectionalLight(colors.fill, 0.6 * config.intensity)
157
+ fillLight.position.set(-5, 4, -3)
158
+ scene.add(fillLight)
159
+ lights.directional.push(fillLight)
160
+
161
+ // Rim light (edge highlighting from behind)
162
+ const rimLight = new THREE.DirectionalLight(colors.rim, 0.8 * config.intensity)
163
+ rimLight.position.set(2, 3, -8)
164
+ scene.add(rimLight)
165
+ lights.directional.push(rimLight)
166
+
167
+ // Add helpers if requested
168
+ if (config.addHelpers) {
169
+ const keyHelper = new THREE.DirectionalLightHelper(keyLight, 1)
170
+ const fillHelper = new THREE.DirectionalLightHelper(fillLight, 1)
171
+ const rimHelper = new THREE.DirectionalLightHelper(rimLight, 1)
172
+
173
+ scene.add(keyHelper)
174
+ scene.add(fillHelper)
175
+ scene.add(rimHelper)
176
+
177
+ lights.helpers.push(keyHelper, fillHelper, rimHelper)
178
+ }
179
+ }
180
+
181
+ // Add spot lights for additional emphasis
182
+ if (config.addSpot) {
183
+ // Spot from above
184
+ const topSpot = new THREE.SpotLight(
185
+ colors.key,
186
+ 0.8 * config.intensity,
187
+ 30, // distance
188
+ Math.PI / 6, // angle
189
+ 0.5, // penumbra
190
+ 1 // decay
191
+ )
192
+ topSpot.position.set(0, 15, 0)
193
+ topSpot.castShadow = true
194
+
195
+ // Configure spotlight shadows
196
+ topSpot.shadow.mapSize.width = 1024
197
+ topSpot.shadow.mapSize.height = 1024
198
+ topSpot.shadow.camera.near = 1
199
+ topSpot.shadow.camera.far = 30
200
+
201
+ scene.add(topSpot)
202
+ lights.spot.push(topSpot)
203
+
204
+ // Accent spot
205
+ const accentSpot = new THREE.SpotLight(
206
+ colors.rim,
207
+ 0.7 * config.intensity,
208
+ 20,
209
+ Math.PI / 8,
210
+ 0.6,
211
+ 1.5
212
+ )
213
+ accentSpot.position.set(-7, 4, -7)
214
+ scene.add(accentSpot)
215
+ lights.spot.push(accentSpot)
216
+
217
+ // Add helpers if requested
218
+ if (config.addHelpers) {
219
+ const topSpotHelper = new THREE.SpotLightHelper(topSpot)
220
+ const accentSpotHelper = new THREE.SpotLightHelper(accentSpot)
221
+
222
+ scene.add(topSpotHelper)
223
+ scene.add(accentSpotHelper)
224
+
225
+ lights.helpers.push(topSpotHelper, accentSpotHelper)
226
+ }
227
+ }
228
+
229
+ // Return all created lights for further manipulation
230
+ return lights
231
+ }
232
+
233
+ /**
234
+ * Creates a scene with standard grid, axes, and lighting
235
+ *
236
+ * @param scene - The Three.js scene to set up
237
+ * @param lightingOptions - Optional lighting configuration
238
+ * @returns The created lights collection
239
+ */
240
+ export function setupStandardScene(
241
+ scene: THREE.Scene,
242
+ lightingOptions: LightingOptions = {}
243
+ ): LightCollection {
244
+ // Add grid
245
+ const gridHelper = new THREE.GridHelper(10, 10)
246
+ scene.add(gridHelper)
247
+
248
+ // Add axes
249
+ const axesHelper = new THREE.AxesHelper(5)
250
+ scene.add(axesHelper)
251
+
252
+ // Add lights and return them
253
+ return addSceneLighting(scene, lightingOptions)
254
+ }
255
+
256
+ export interface HDRIData {
257
+ texture: THREE.DataTexture
258
+ material: THREE.Material
259
+ }
260
+
261
+ export const loadHDRIData = async (
262
+ path: HDRIs | string,
263
+ blurAmount: number,
264
+ opacity: number = 1
265
+ ): Promise<HDRIData> => {
266
+ const rgbeLoader: RGBELoader = new RGBELoader()
267
+
268
+ return new Promise((resolve, reject) => {
269
+ rgbeLoader.setDataType(THREE.FloatType).load(
270
+ path as any,
271
+ (texture: THREE.DataTexture): void => {
272
+ const blurredMaterial = new THREE.ShaderMaterial({
273
+ uniforms: {
274
+ uTexture: { value: texture },
275
+ uBlurAmount: { value: blurAmount }, // Increase for more blur
276
+ uTextureSize: {
277
+ value: new THREE.Vector2(texture.image.width, texture.image.height)
278
+ },
279
+ sigma: { value: 3.0 }, // Add this uniform
280
+ opacity: { value: opacity }
281
+ },
282
+ vertexShader: vert_blur_hdri,
283
+ fragmentShader: frag_blur_hdri,
284
+ side: THREE.BackSide,
285
+ transparent: true
286
+ })
287
+
288
+ resolve({
289
+ texture,
290
+ material: blurredMaterial
291
+ })
292
+ },
293
+ (_) => {},
294
+ // Optional error callback
295
+ (error): void => {
296
+ console.error('Error loading HDRI:', error)
297
+ reject(error)
298
+ }
299
+ )
300
+ })
301
+ }
302
+
303
+ export enum HDRIs {
304
+ photoStudio1 = _photoStudio1 as any,
305
+ photoStudio2 = _photoStudio2 as any,
306
+ photoStudio3 = _photoStudio3 as any,
307
+ outdoor1 = _outdoor1 as any,
308
+ indoor1 = _indoor1 as any,
309
+ metro1 = _metro1 as any
310
+ }
311
+
312
+ export async function addHDRI(scene: AnimatedScene, hdriData: HDRIData, lightingIntensity = 1.0) {
313
+ // Create PMREM Generator for converting equirectangular HDRI to cubemap
314
+ const pmremGenerator: THREE.PMREMGenerator = new THREE.PMREMGenerator(scene.renderer)
315
+ pmremGenerator.compileEquirectangularShader()
316
+
317
+ // Process the HDRI texture
318
+ const envMap: THREE.Texture = pmremGenerator.fromEquirectangular(hdriData.texture).texture
319
+
320
+ // Create background sphere
321
+ const geometry = new THREE.SphereGeometry(scene.farLimitRender / 2, 40, 40)
322
+
323
+ const backgroundSphere = new THREE.Mesh(geometry, hdriData.material)
324
+ backgroundSphere.renderOrder = -1 // Render before other objects
325
+
326
+ // Attach to camera
327
+ scene.scene.add(backgroundSphere)
328
+
329
+ // Apply to scene environment (for reflections)
330
+ scene.scene.environment = envMap
331
+
332
+ scene.scene.environmentIntensity = lightingIntensity
333
+
334
+ // Clean up resources
335
+ // texture.dispose()
336
+ pmremGenerator.dispose()
337
+ }
338
+
339
+ export function addBackgroundGradient({
340
+ scene,
341
+ topColor, // Sky blue
342
+ bottomColor, // White
343
+ backgroundOpacity = 1.0,
344
+ lightingIntensity = 1.0,
345
+ addLighting = true
346
+ }: {
347
+ scene: AnimatedScene
348
+ topColor: THREE.ColorRepresentation
349
+ bottomColor: THREE.ColorRepresentation
350
+ backgroundOpacity?: number
351
+ lightingIntensity?: number
352
+ radius?: number
353
+ addLighting?: boolean
354
+ }): void {
355
+ // Convert ColorRepresentation to Color instances
356
+ const top = new THREE.Color(topColor)
357
+ const bottom = new THREE.Color(bottomColor)
358
+
359
+ const sphereRadius = scene.farLimitRender / 2.2
360
+
361
+ // Create background sphere
362
+ const geometry = new THREE.SphereGeometry(sphereRadius, 40, 40)
363
+ const gradientMaterial = new THREE.ShaderMaterial({
364
+ vertexShader: vert_background_gradient,
365
+ fragmentShader: frag_background_gradient,
366
+ uniforms: {
367
+ topColor: { value: top },
368
+ bottomColor: { value: bottom },
369
+ opacity: { value: backgroundOpacity }
370
+ },
371
+ side: THREE.BackSide,
372
+ transparent: backgroundOpacity < 1.0
373
+ })
374
+
375
+ const backgroundSphere = new THREE.Mesh(geometry, gradientMaterial)
376
+ backgroundSphere.renderOrder = -1
377
+ scene.scene.add(backgroundSphere)
378
+
379
+ // Add hemisphere lighting that matches the gradient colors
380
+ if (addLighting) {
381
+ const hemisphereLight = new THREE.HemisphereLight(top, bottom, lightingIntensity)
382
+
383
+ // Optional: Position the light if needed, though HemisphereLight works from everywhere
384
+ hemisphereLight.position.set(0, 1, 0)
385
+ scene.scene.add(hemisphereLight)
386
+ }
387
+ }
@@ -0,0 +1,6 @@
1
+ import * as THREE from "three"
2
+ import { COLORS } from "./helpers"
3
+
4
+ export const getRegularMetal = (color: number = COLORS.white) => {
5
+ return new THREE.MeshStandardMaterial({ color: color, roughness: 0.2, metalness: 1 })
6
+ }