minecraft-renderer 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 (187) hide show
  1. package/README.md +297 -0
  2. package/dist/index.html +83 -0
  3. package/dist/static/image/arrow.6f27b59f.png +0 -0
  4. package/dist/static/image/blocksAtlasLatest.7850afa3.png +0 -0
  5. package/dist/static/image/blocksAtlasLegacy.5c76823d.png +0 -0
  6. package/dist/static/image/itemsAtlasLatest.36036f95.png +0 -0
  7. package/dist/static/image/itemsAtlasLegacy.dcb1b58d.png +0 -0
  8. package/dist/static/image/tipped_arrow.6f27b59f.png +0 -0
  9. package/dist/static/js/365.f05233ab.js +8462 -0
  10. package/dist/static/js/365.f05233ab.js.LICENSE.txt +52 -0
  11. package/dist/static/js/async/738.efa27644.js +1 -0
  12. package/dist/static/js/index.092ec5be.js +56 -0
  13. package/dist/static/js/lib-polyfill.98986ac5.js +1 -0
  14. package/dist/static/js/lib-react.5c9129e0.js +2 -0
  15. package/dist/static/js/lib-react.5c9129e0.js.LICENSE.txt +39 -0
  16. package/package.json +104 -0
  17. package/src/assets/destroy_stage_0.png +0 -0
  18. package/src/assets/destroy_stage_1.png +0 -0
  19. package/src/assets/destroy_stage_2.png +0 -0
  20. package/src/assets/destroy_stage_3.png +0 -0
  21. package/src/assets/destroy_stage_4.png +0 -0
  22. package/src/assets/destroy_stage_5.png +0 -0
  23. package/src/assets/destroy_stage_6.png +0 -0
  24. package/src/assets/destroy_stage_7.png +0 -0
  25. package/src/assets/destroy_stage_8.png +0 -0
  26. package/src/assets/destroy_stage_9.png +0 -0
  27. package/src/examples/README.md +146 -0
  28. package/src/examples/appViewerExample.ts +205 -0
  29. package/src/examples/initialMenuStart.ts +161 -0
  30. package/src/graphicsBackend/appViewer.ts +297 -0
  31. package/src/graphicsBackend/config.ts +119 -0
  32. package/src/graphicsBackend/index.ts +10 -0
  33. package/src/graphicsBackend/playerState.ts +61 -0
  34. package/src/graphicsBackend/types.ts +143 -0
  35. package/src/index.ts +97 -0
  36. package/src/lib/DebugGui.ts +190 -0
  37. package/src/lib/animationController.ts +85 -0
  38. package/src/lib/buildSharedConfig.mjs +1 -0
  39. package/src/lib/cameraBobbing.ts +94 -0
  40. package/src/lib/canvas2DOverlay.example.ts +361 -0
  41. package/src/lib/canvas2DOverlay.quickstart.ts +242 -0
  42. package/src/lib/canvas2DOverlay.ts +381 -0
  43. package/src/lib/cleanupDecorator.ts +29 -0
  44. package/src/lib/createPlayerObject.ts +55 -0
  45. package/src/lib/frameTimingCollector.ts +164 -0
  46. package/src/lib/guiRenderer.ts +283 -0
  47. package/src/lib/items.ts +140 -0
  48. package/src/lib/mesherlogReader.ts +131 -0
  49. package/src/lib/moreBlockDataGenerated.json +714 -0
  50. package/src/lib/preflatMap.json +1741 -0
  51. package/src/lib/simpleUtils.ts +40 -0
  52. package/src/lib/smoothSwitcher.ts +168 -0
  53. package/src/lib/spiral.ts +29 -0
  54. package/src/lib/ui/newStats.ts +120 -0
  55. package/src/lib/utils/proxy.ts +23 -0
  56. package/src/lib/utils/skins.ts +63 -0
  57. package/src/lib/utils.ts +76 -0
  58. package/src/lib/workerProxy.ts +342 -0
  59. package/src/lib/worldrendererCommon.ts +1088 -0
  60. package/src/mesher/mesher.ts +253 -0
  61. package/src/mesher/models.ts +769 -0
  62. package/src/mesher/modelsGeometryCommon.ts +142 -0
  63. package/src/mesher/shared.ts +80 -0
  64. package/src/mesher/standaloneRenderer.ts +270 -0
  65. package/src/mesher/test/a.ts +3 -0
  66. package/src/mesher/test/mesherTester.ts +76 -0
  67. package/src/mesher/test/playground.ts +19 -0
  68. package/src/mesher/test/test-perf.ts +74 -0
  69. package/src/mesher/test/tests.test.ts +56 -0
  70. package/src/mesher/world.ts +294 -0
  71. package/src/mesher/worldConstants.ts +1 -0
  72. package/src/modules/index.ts +11 -0
  73. package/src/modules/starfield.ts +313 -0
  74. package/src/modules/types.ts +110 -0
  75. package/src/playerState/playerState.ts +78 -0
  76. package/src/playerState/types.ts +36 -0
  77. package/src/playground/allEntitiesDebug.ts +170 -0
  78. package/src/playground/baseScene.ts +587 -0
  79. package/src/playground/mobileControls.tsx +268 -0
  80. package/src/playground/playground.html +83 -0
  81. package/src/playground/playground.ts +11 -0
  82. package/src/playground/playgroundUi.tsx +140 -0
  83. package/src/playground/reactUtils.ts +71 -0
  84. package/src/playground/scenes/allEntities.ts +13 -0
  85. package/src/playground/scenes/entities.ts +37 -0
  86. package/src/playground/scenes/floorRandom.ts +33 -0
  87. package/src/playground/scenes/frequentUpdates.ts +148 -0
  88. package/src/playground/scenes/geometryExport.ts +142 -0
  89. package/src/playground/scenes/index.ts +12 -0
  90. package/src/playground/scenes/lightingStarfield.ts +40 -0
  91. package/src/playground/scenes/main.ts +313 -0
  92. package/src/playground/scenes/railsCobweb.ts +14 -0
  93. package/src/playground/scenes/rotationIssue.ts +7 -0
  94. package/src/playground/scenes/slabsOptimization.ts +16 -0
  95. package/src/playground/scenes/transparencyIssue.ts +11 -0
  96. package/src/playground/shared.ts +79 -0
  97. package/src/resourcesManager/index.ts +5 -0
  98. package/src/resourcesManager/resourcesManager.ts +314 -0
  99. package/src/shims/minecraftData.ts +41 -0
  100. package/src/sign-renderer/index.html +21 -0
  101. package/src/sign-renderer/index.ts +216 -0
  102. package/src/sign-renderer/noop.js +1 -0
  103. package/src/sign-renderer/playground.ts +38 -0
  104. package/src/sign-renderer/tests.test.ts +69 -0
  105. package/src/sign-renderer/vite.config.ts +10 -0
  106. package/src/three/appShared.ts +75 -0
  107. package/src/three/bannerRenderer.ts +275 -0
  108. package/src/three/cameraShake.ts +120 -0
  109. package/src/three/cinimaticScript.ts +350 -0
  110. package/src/three/documentRenderer.ts +491 -0
  111. package/src/three/entities.ts +1580 -0
  112. package/src/three/entity/EntityMesh.ts +707 -0
  113. package/src/three/entity/animations.js +171 -0
  114. package/src/three/entity/armorModels.json +204 -0
  115. package/src/three/entity/armorModels.ts +36 -0
  116. package/src/three/entity/entities.json +6230 -0
  117. package/src/three/entity/exportedModels.js +38 -0
  118. package/src/three/entity/externalTextures.json +1 -0
  119. package/src/three/entity/models/allay.obj +325 -0
  120. package/src/three/entity/models/arrow.obj +60 -0
  121. package/src/three/entity/models/axolotl.obj +509 -0
  122. package/src/three/entity/models/blaze.obj +601 -0
  123. package/src/three/entity/models/boat.obj +417 -0
  124. package/src/three/entity/models/camel.obj +1061 -0
  125. package/src/three/entity/models/cat.obj +509 -0
  126. package/src/three/entity/models/chicken.obj +371 -0
  127. package/src/three/entity/models/cod.obj +371 -0
  128. package/src/three/entity/models/creeper.obj +279 -0
  129. package/src/three/entity/models/dolphin.obj +371 -0
  130. package/src/three/entity/models/ender_dragon.obj +2993 -0
  131. package/src/three/entity/models/enderman.obj +325 -0
  132. package/src/three/entity/models/endermite.obj +187 -0
  133. package/src/three/entity/models/fox.obj +463 -0
  134. package/src/three/entity/models/frog.obj +739 -0
  135. package/src/three/entity/models/ghast.obj +463 -0
  136. package/src/three/entity/models/goat.obj +601 -0
  137. package/src/three/entity/models/guardian.obj +1015 -0
  138. package/src/three/entity/models/horse.obj +1061 -0
  139. package/src/three/entity/models/llama.obj +509 -0
  140. package/src/three/entity/models/minecart.obj +233 -0
  141. package/src/three/entity/models/parrot.obj +509 -0
  142. package/src/three/entity/models/piglin.obj +739 -0
  143. package/src/three/entity/models/pillager.obj +371 -0
  144. package/src/three/entity/models/rabbit.obj +555 -0
  145. package/src/three/entity/models/sheep.obj +555 -0
  146. package/src/three/entity/models/shulker.obj +141 -0
  147. package/src/three/entity/models/sniffer.obj +693 -0
  148. package/src/three/entity/models/spider.obj +509 -0
  149. package/src/three/entity/models/tadpole.obj +95 -0
  150. package/src/three/entity/models/turtle.obj +371 -0
  151. package/src/three/entity/models/vex.obj +325 -0
  152. package/src/three/entity/models/villager.obj +509 -0
  153. package/src/three/entity/models/warden.obj +463 -0
  154. package/src/three/entity/models/witch.obj +647 -0
  155. package/src/three/entity/models/wolf.obj +509 -0
  156. package/src/three/entity/models/zombie_villager.obj +463 -0
  157. package/src/three/entity/objModels.js +1 -0
  158. package/src/three/fireworks.ts +661 -0
  159. package/src/three/fireworksRenderer.ts +434 -0
  160. package/src/three/globals.d.ts +7 -0
  161. package/src/three/graphicsBackend.ts +274 -0
  162. package/src/three/graphicsBackendOffThread.ts +107 -0
  163. package/src/three/hand.ts +89 -0
  164. package/src/three/holdingBlock.ts +926 -0
  165. package/src/three/index.ts +20 -0
  166. package/src/three/itemMesh.ts +427 -0
  167. package/src/three/modules.d.ts +14 -0
  168. package/src/three/panorama.ts +308 -0
  169. package/src/three/panoramaShared.ts +1 -0
  170. package/src/three/renderSlot.ts +82 -0
  171. package/src/three/skyboxRenderer.ts +406 -0
  172. package/src/three/starField.ts +13 -0
  173. package/src/three/threeJsMedia.ts +731 -0
  174. package/src/three/threeJsMethods.ts +15 -0
  175. package/src/three/threeJsParticles.ts +160 -0
  176. package/src/three/threeJsSound.ts +95 -0
  177. package/src/three/threeJsUtils.ts +90 -0
  178. package/src/three/waypointSprite.ts +435 -0
  179. package/src/three/waypoints.ts +163 -0
  180. package/src/three/world/cursorBlock.ts +172 -0
  181. package/src/three/world/vr.ts +257 -0
  182. package/src/three/worldGeometryExport.ts +259 -0
  183. package/src/three/worldGeometryHandler.ts +279 -0
  184. package/src/three/worldRendererThree.ts +1381 -0
  185. package/src/worldView/index.ts +6 -0
  186. package/src/worldView/types.ts +66 -0
  187. package/src/worldView/worldView.ts +424 -0
@@ -0,0 +1,434 @@
1
+ import * as THREE from 'three'
2
+ import { Vec3 } from 'vec3'
3
+ import type { WorldRendererThree } from './worldRendererThree'
4
+
5
+ interface FireworkExplosion {
6
+ id: string
7
+ position: Vec3
8
+ particles: FireworkParticle[]
9
+ startTime: number
10
+ duration: number
11
+ size: number
12
+ debugSphere?: THREE.Mesh
13
+ }
14
+
15
+ interface FireworkParticle {
16
+ position: Vec3
17
+ velocity: Vec3
18
+ color: THREE.Color
19
+ life: number
20
+ maxLife: number
21
+ mesh: THREE.Mesh
22
+ }
23
+
24
+ export class FireworksRenderer {
25
+ // Class constants
26
+ static readonly DEFAULT_PARTICLE_COUNT = 50
27
+ static readonly DEFAULT_PARTICLE_SIZE = 0.05 // Much smaller for realistic firework particles
28
+ static readonly DEFAULT_EXPLOSION_DURATION = 6000 // ms - increased duration
29
+ static readonly GRAVITY = -0.005 // Realistic gravity in units/second²
30
+ static readonly VELOCITY_FACTOR = 1.5 // Increased for proper explosion spread
31
+ static readonly TEST_INTERVAL = 1000 // 1 second
32
+
33
+ // Debug flags
34
+ static readonly DEBUG_SHOW_EXPLOSION_CENTER = true
35
+ static readonly DEBUG_SHOW_PARTICLE_TRAILS = false
36
+
37
+ private readonly explosions = new Map<string, FireworkExplosion>()
38
+ private readonly particleGeometry: THREE.BoxGeometry
39
+ private readonly particleMaterials: THREE.MeshBasicMaterial[] = []
40
+ private testModeTimer?: NodeJS.Timeout
41
+ private explosionCounter = 0
42
+
43
+ constructor(private readonly worldRenderer: WorldRendererThree) {
44
+ // Create reusable box geometry for pixelated particles
45
+ this.particleGeometry = new THREE.BoxGeometry(
46
+ FireworksRenderer.DEFAULT_PARTICLE_SIZE,
47
+ FireworksRenderer.DEFAULT_PARTICLE_SIZE,
48
+ FireworksRenderer.DEFAULT_PARTICLE_SIZE
49
+ )
50
+
51
+ // Pre-create materials with different colors for performance
52
+ this.createParticleMaterials()
53
+
54
+ // Start test mode
55
+ // this.startTestMode() // Disabled for debugging
56
+ }
57
+
58
+ private createParticleMaterials() {
59
+ const colors = [
60
+ 0xff_00_00, // Red
61
+ 0x00_ff_00, // Green
62
+ 0x00_00_ff, // Blue
63
+ 0xff_ff_00, // Yellow
64
+ 0xff_00_ff, // Magenta
65
+ 0x00_ff_ff, // Cyan
66
+ 0xff_a5_00, // Orange
67
+ 0x80_00_80, // Purple
68
+ 0xff_c0_cb, // Pink
69
+ 0xff_ff_ff, // White
70
+ ]
71
+
72
+ for (const color of colors) {
73
+ const material = new THREE.MeshBasicMaterial({
74
+ color,
75
+ transparent: true,
76
+ opacity: 1
77
+ })
78
+ this.particleMaterials.push(material)
79
+ }
80
+ }
81
+
82
+ private getRandomMaterial(): THREE.MeshBasicMaterial {
83
+ return this.particleMaterials[Math.floor(Math.random() * this.particleMaterials.length)]
84
+ }
85
+
86
+ private createExplosion(position: Vec3, size = 1, color?: number, duration = FireworksRenderer.DEFAULT_EXPLOSION_DURATION): FireworkExplosion {
87
+ const explosionId = `firework_${this.explosionCounter++}_${Date.now()}`
88
+ const particles: FireworkParticle[] = []
89
+ const particleCount = Math.floor(FireworksRenderer.DEFAULT_PARTICLE_COUNT * size)
90
+
91
+ // Create debug sphere if enabled
92
+ let debugSphere: THREE.Mesh | undefined
93
+ if (FireworksRenderer.DEBUG_SHOW_EXPLOSION_CENTER) {
94
+ const sphereRadius = 0.5 * size
95
+ const sphereGeometry = new THREE.SphereGeometry(sphereRadius, 8, 8)
96
+ const sphereMaterial = new THREE.MeshBasicMaterial({
97
+ color: 0xff_00_00,
98
+ wireframe: true,
99
+ transparent: true,
100
+ opacity: 0.8
101
+ })
102
+ debugSphere = new THREE.Mesh(sphereGeometry, sphereMaterial)
103
+ debugSphere.position.set(position.x, position.y, position.z)
104
+ debugSphere.renderOrder = 999 // Render before particles
105
+ this.worldRenderer.scene.add(debugSphere)
106
+ console.log(`Debug: Created explosion center sphere at (${position.x}, ${position.y}, ${position.z}) with radius ${sphereRadius}`)
107
+ }
108
+
109
+ for (let i = 0; i < particleCount; i++) {
110
+ // Create planar firework explosion (disk pattern)
111
+ const angle = Math.random() * Math.PI * 2 // Random angle in the plane
112
+ const distance = Math.sqrt(Math.random()) * FireworksRenderer.VELOCITY_FACTOR * size // Square root for uniform distribution
113
+
114
+ // Create a plane that faces the camera (or can be rotated)
115
+ // For now, let's make it explode in the XZ plane (horizontal disk)
116
+ const velocity = new Vec3(
117
+ distance * Math.cos(angle),
118
+ Math.random() * 0.5 - 0.25, // Small random Y variation for depth
119
+ distance * Math.sin(angle)
120
+ )
121
+
122
+ // DEBUG: Log first few particles to understand positioning
123
+ if (i < 5) {
124
+ console.log(`Debug: Particle ${i} - Initial pos: (${position.x}, ${position.y}, ${position.z}), Velocity: (${velocity.x.toFixed(3)}, ${velocity.y.toFixed(3)}, ${velocity.z.toFixed(3)}), Distance: ${distance.toFixed(3)}`)
125
+ console.log(`Debug: Particle ${i} - Velocity magnitude: ${velocity.toString()}, Expected max: ${(FireworksRenderer.VELOCITY_FACTOR * size).toFixed(3)}`)
126
+ }
127
+
128
+ // Create particle mesh
129
+ const material = color === undefined
130
+ ? this.getRandomMaterial().clone()
131
+ : new THREE.MeshBasicMaterial({ color, transparent: true, opacity: 1 })
132
+
133
+ const mesh = new THREE.Mesh(this.particleGeometry, material)
134
+ mesh.position.set(position.x, position.y, position.z)
135
+
136
+ // Make particles more visible
137
+ mesh.renderOrder = 1000 // Render on top
138
+
139
+ // DEBUG: Add particle size info to console
140
+ if (i < 5) {
141
+ console.log(`Debug: Particle ${i} mesh created at (${mesh.position.x}, ${mesh.position.y}, ${mesh.position.z}) with size ${FireworksRenderer.DEFAULT_PARTICLE_SIZE}`)
142
+ }
143
+
144
+ // Add to scene
145
+ this.worldRenderer.scene.add(mesh)
146
+
147
+ const particle: FireworkParticle = {
148
+ position: position.clone(),
149
+ velocity,
150
+ color: new THREE.Color(material.color),
151
+ life: 1,
152
+ maxLife: 1,
153
+ mesh
154
+ }
155
+
156
+ particles.push(particle)
157
+ }
158
+
159
+ const explosion: FireworkExplosion = {
160
+ id: explosionId,
161
+ position: position.clone(),
162
+ particles,
163
+ startTime: Date.now(),
164
+ duration,
165
+ size,
166
+ debugSphere
167
+ }
168
+
169
+ this.explosions.set(explosionId, explosion)
170
+ console.log(`Debug: Created firework explosion ${explosionId} at (${position.x}, ${position.y}, ${position.z}) with ${particleCount} particles, duration ${duration}ms`)
171
+ console.log(`Debug: Expected behavior - All particles should start at explosion center and move outward with velocities up to ${(FireworksRenderer.VELOCITY_FACTOR * size).toFixed(3)} units/second`)
172
+ console.log(`Debug: Debug sphere radius: ${(0.5 * size).toFixed(3)}, Particle size: ${FireworksRenderer.DEFAULT_PARTICLE_SIZE}`)
173
+ return explosion
174
+ }
175
+
176
+ private createExplosionFacingCamera(position: Vec3, size = 1, color?: number, duration = FireworksRenderer.DEFAULT_EXPLOSION_DURATION): FireworkExplosion {
177
+ const explosionId = `firework_${this.explosionCounter++}_${Date.now()}`
178
+ const particles: FireworkParticle[] = []
179
+ const particleCount = Math.floor(FireworksRenderer.DEFAULT_PARTICLE_COUNT * size)
180
+
181
+ // Create debug sphere if enabled
182
+ let debugSphere: THREE.Mesh | undefined
183
+ if (FireworksRenderer.DEBUG_SHOW_EXPLOSION_CENTER) {
184
+ const sphereRadius = 0.5 * size
185
+ const sphereGeometry = new THREE.SphereGeometry(sphereRadius, 8, 8)
186
+ const sphereMaterial = new THREE.MeshBasicMaterial({
187
+ color: 0xff_00_00,
188
+ wireframe: true,
189
+ transparent: true,
190
+ opacity: 0.8
191
+ })
192
+ debugSphere = new THREE.Mesh(sphereGeometry, sphereMaterial)
193
+ debugSphere.position.set(position.x, position.y, position.z)
194
+ debugSphere.renderOrder = 999 // Render before particles
195
+ this.worldRenderer.scene.add(debugSphere)
196
+ console.log(`Debug: Created camera-facing explosion center sphere at (${position.x}, ${position.y}, ${position.z}) with radius ${sphereRadius}`)
197
+ }
198
+
199
+ // Get camera direction to face the explosion plane towards camera
200
+ const cameraPos = this.worldRenderer.getCameraPosition()
201
+ const directionToCamera = new Vec3(
202
+ cameraPos.x - position.x,
203
+ cameraPos.y - position.y,
204
+ cameraPos.z - position.z
205
+ ).normalize()
206
+
207
+ // Create a plane perpendicular to the camera direction
208
+ for (let i = 0; i < particleCount; i++) {
209
+ const angle = Math.random() * Math.PI * 2
210
+ const distance = Math.sqrt(Math.random()) * FireworksRenderer.VELOCITY_FACTOR * size
211
+
212
+ // Create velocity in the plane perpendicular to camera direction
213
+ const velocity = new Vec3(
214
+ distance * Math.cos(angle),
215
+ distance * Math.sin(angle),
216
+ 0
217
+ )
218
+
219
+ // Rotate the velocity to face the camera
220
+ // This is a simplified rotation - in a full implementation you'd use quaternions
221
+ const rotatedVelocity = new Vec3(
222
+ velocity.x * directionToCamera.x - velocity.z * directionToCamera.z,
223
+ velocity.y,
224
+ velocity.x * directionToCamera.z + velocity.z * directionToCamera.x
225
+ )
226
+
227
+ // DEBUG: Log first few particles
228
+ if (i < 5) {
229
+ console.log(`Debug: Camera-facing Particle ${i} - Velocity: (${rotatedVelocity.x.toFixed(3)}, ${rotatedVelocity.y.toFixed(3)}, ${rotatedVelocity.z.toFixed(3)}), Distance: ${distance.toFixed(3)}`)
230
+ }
231
+
232
+ // Create particle mesh
233
+ const material = color === undefined
234
+ ? this.getRandomMaterial().clone()
235
+ : new THREE.MeshBasicMaterial({ color, transparent: true, opacity: 1 })
236
+
237
+ const mesh = new THREE.Mesh(this.particleGeometry, material)
238
+ mesh.position.set(position.x, position.y, position.z)
239
+
240
+ // Make particles more visible
241
+ mesh.renderOrder = 1000 // Render on top
242
+
243
+ // Add to scene
244
+ this.worldRenderer.scene.add(mesh)
245
+
246
+ const particle: FireworkParticle = {
247
+ position: position.clone(),
248
+ velocity: rotatedVelocity,
249
+ color: new THREE.Color(material.color),
250
+ life: 1,
251
+ maxLife: 1,
252
+ mesh
253
+ }
254
+
255
+ particles.push(particle)
256
+ }
257
+
258
+ const explosion: FireworkExplosion = {
259
+ id: explosionId,
260
+ position: position.clone(),
261
+ particles,
262
+ startTime: Date.now(),
263
+ duration,
264
+ size,
265
+ debugSphere
266
+ }
267
+
268
+ this.explosions.set(explosionId, explosion)
269
+ console.log(`Debug: Created camera-facing firework explosion ${explosionId} at (${position.x}, ${position.y}, ${position.z}) with ${particleCount} particles, duration ${duration}ms`)
270
+ return explosion
271
+ }
272
+
273
+ /**
274
+ * Create a firework explosion at the specified position
275
+ * @param position World position for the explosion
276
+ * @param size Size multiplier for the explosion (default: 1.0)
277
+ * @param color Optional specific color for all particles (default: random colors)
278
+ * @param duration Duration of the explosion in milliseconds (default: DEFAULT_EXPLOSION_DURATION)
279
+ */
280
+ explode(position: Vec3, size = 1, color?: number, duration?: number): string {
281
+ const explosion = this.createExplosion(position, size, color, duration)
282
+ return explosion.id
283
+ }
284
+
285
+ /**
286
+ * Create a firework explosion that faces the camera direction
287
+ * @param position World position for the explosion
288
+ * @param size Size multiplier for the explosion (default: 1.0)
289
+ * @param color Optional specific color for all particles (default: random colors)
290
+ * @param duration Duration of the explosion in milliseconds (default: DEFAULT_EXPLOSION_DURATION)
291
+ */
292
+ explodeFacingCamera(position: Vec3, size = 1, color?: number, duration?: number): string {
293
+ const explosion = this.createExplosionFacingCamera(position, size, color, duration)
294
+ return explosion.id
295
+ }
296
+
297
+ private startTestMode() {
298
+ const cameraPos = this.worldRenderer.getCameraPosition()
299
+ const fireworkPos = new Vec3(cameraPos.x, cameraPos.y + 10, cameraPos.z)
300
+ this.testModeTimer = setInterval(() => {
301
+ // Random size and color for variety
302
+ const size = 0.8 + Math.random() * 0.4 // 0.8 - 1.2
303
+ const colors = [0xff_00_00, 0x00_ff_00, 0x00_00_ff, 0xff_ff_00, 0xff_00_ff, 0x00_ff_ff, 0xff_a5_00]
304
+ const randomColor = colors[Math.floor(Math.random() * colors.length)]
305
+
306
+ console.log('fireworkPos', fireworkPos)
307
+ this.explodeFacingCamera(fireworkPos, size, randomColor)
308
+ }, FireworksRenderer.TEST_INTERVAL)
309
+ }
310
+
311
+ private updateParticle(particle: FireworkParticle, deltaTime: number, explosionDuration: number) {
312
+ // DEBUG: Log first few updates to see what's happening
313
+ const isFirstParticle = particle.mesh.position.x === particle.position.x &&
314
+ particle.mesh.position.y === particle.position.y &&
315
+ particle.mesh.position.z === particle.position.z
316
+
317
+ if (isFirstParticle && particle.life > 0.95) {
318
+ console.log(`Debug: First particle update - DeltaTime: ${deltaTime}ms (${(deltaTime / 1000).toFixed(3)}s), Duration: ${explosionDuration}`)
319
+ console.log(`Debug: Initial velocity: (${particle.velocity.x.toFixed(3)}, ${particle.velocity.y.toFixed(3)}, ${particle.velocity.z.toFixed(3)}) units/s`)
320
+ console.log(`Debug: Initial position: (${particle.position.x.toFixed(3)}, ${particle.position.y.toFixed(3)}, ${particle.position.z.toFixed(3)})`)
321
+ }
322
+
323
+ // Apply gravity
324
+ particle.velocity.y += FireworksRenderer.GRAVITY
325
+
326
+ // Update position
327
+ const oldPos = particle.position.clone()
328
+ // Convert deltaTime from milliseconds to seconds for proper physics
329
+ const deltaTimeSeconds = deltaTime / 1000
330
+ particle.position.add(particle.velocity.scaled(deltaTimeSeconds))
331
+ particle.mesh.position.set(particle.position.x, particle.position.y, particle.position.z)
332
+
333
+ // Update life and opacity
334
+ const oldLife = particle.life
335
+ particle.life -= deltaTime / explosionDuration
336
+ const opacity = Math.max(0, particle.life)
337
+ if (particle.mesh.material instanceof THREE.MeshBasicMaterial) {
338
+ particle.mesh.material.opacity = opacity
339
+ }
340
+
341
+ // DEBUG: Log movement for first few frames
342
+ if (isFirstParticle && oldLife > 0.95) {
343
+ const movement = particle.position.distanceTo(oldPos)
344
+ console.log(`Debug: Particle moved ${movement.toFixed(4)} units in ${(deltaTime / 1000).toFixed(3)}s, new pos: (${particle.position.x.toFixed(3)}, ${particle.position.y.toFixed(3)}, ${particle.position.z.toFixed(3)})`)
345
+ console.log(`Debug: Life: ${oldLife.toFixed(3)} -> ${particle.life.toFixed(3)}, Opacity: ${opacity.toFixed(3)}`)
346
+ }
347
+
348
+ return particle.life > 0
349
+ }
350
+
351
+ render() {
352
+ const currentTime = Date.now()
353
+ const deltaTime = 16.67 // Assume ~60fps for physics calculations
354
+
355
+ // DEBUG: Log render calls occasionally
356
+ if (this.explosions.size > 0 && Math.random() < 0.01) { // ~1% chance
357
+ console.log(`Debug: Render called with ${this.explosions.size} explosions, deltaTime: ${deltaTime}`)
358
+ }
359
+
360
+ for (const [explosionId, explosion] of this.explosions.entries()) {
361
+ const elapsed = currentTime - explosion.startTime
362
+
363
+ if (elapsed >= explosion.duration) {
364
+ // Remove expired explosion
365
+ console.log(`Debug: Removing expired explosion ${explosionId} after ${elapsed}ms`)
366
+ this.removeExplosion(explosionId)
367
+ continue
368
+ }
369
+
370
+ // DEBUG: Log explosion status every 2 seconds
371
+ if (elapsed % 2000 < 16) { // Every ~2 seconds
372
+ console.log(`Debug: Explosion ${explosionId} - Elapsed: ${elapsed}ms, Particles alive: ${explosion.particles.length}`)
373
+ }
374
+
375
+ // Update all particles in this explosion
376
+ explosion.particles = explosion.particles.filter(particle => this.updateParticle(particle, deltaTime, explosion.duration))
377
+
378
+ // If no particles left, remove explosion early
379
+ if (explosion.particles.length === 0) {
380
+ console.log(`Debug: Removing explosion ${explosionId} - no particles left`)
381
+ this.removeExplosion(explosionId)
382
+ }
383
+ }
384
+ }
385
+
386
+ private removeExplosion(explosionId: string) {
387
+ const explosion = this.explosions.get(explosionId)
388
+ if (!explosion) return
389
+
390
+ // Clean up particle meshes
391
+ for (const particle of explosion.particles) {
392
+ this.worldRenderer.scene.remove(particle.mesh)
393
+ if (particle.mesh.material instanceof THREE.Material) {
394
+ particle.mesh.material.dispose()
395
+ }
396
+ }
397
+
398
+ // Clean up debug sphere if it exists
399
+ if (explosion.debugSphere) {
400
+ this.worldRenderer.scene.remove(explosion.debugSphere)
401
+ explosion.debugSphere.geometry.dispose()
402
+ if (explosion.debugSphere.material instanceof THREE.Material) {
403
+ explosion.debugSphere.material.dispose()
404
+ }
405
+ console.log('Debug: Removed explosion center sphere')
406
+ }
407
+
408
+ this.explosions.delete(explosionId)
409
+ }
410
+
411
+ stopTestMode() {
412
+ if (this.testModeTimer) {
413
+ clearInterval(this.testModeTimer)
414
+ this.testModeTimer = undefined
415
+ }
416
+ }
417
+
418
+ destroy() {
419
+ this.stopTestMode()
420
+
421
+ // Clean up all explosions
422
+ for (const explosionId of this.explosions.keys()) {
423
+ this.removeExplosion(explosionId)
424
+ }
425
+
426
+ // Clean up materials
427
+ for (const material of this.particleMaterials) {
428
+ material.dispose()
429
+ }
430
+
431
+ // Clean up geometry
432
+ this.particleGeometry.dispose()
433
+ }
434
+ }
@@ -0,0 +1,7 @@
1
+ interface ObjectConstructor {
2
+ keys<T extends object>(obj: T): Array<StringKeys<T>>
3
+ entries<T extends object>(obj: T): Array<[StringKeys<T>, T[keyof T]]>
4
+ // todo review https://stackoverflow.com/questions/57390305/trying-to-get-fromentries-type-right
5
+ fromEntries<T extends Array<[string, any]>>(obj: T): Record<T[number][0], T[number][1]>
6
+ assign<T extends Record<string, any>, K extends Record<string, any>>(target: T, source: K): asserts target is T & K
7
+ }