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.
- package/README.md +297 -0
- package/dist/index.html +83 -0
- package/dist/static/image/arrow.6f27b59f.png +0 -0
- package/dist/static/image/blocksAtlasLatest.7850afa3.png +0 -0
- package/dist/static/image/blocksAtlasLegacy.5c76823d.png +0 -0
- package/dist/static/image/itemsAtlasLatest.36036f95.png +0 -0
- package/dist/static/image/itemsAtlasLegacy.dcb1b58d.png +0 -0
- package/dist/static/image/tipped_arrow.6f27b59f.png +0 -0
- package/dist/static/js/365.f05233ab.js +8462 -0
- package/dist/static/js/365.f05233ab.js.LICENSE.txt +52 -0
- package/dist/static/js/async/738.efa27644.js +1 -0
- package/dist/static/js/index.092ec5be.js +56 -0
- package/dist/static/js/lib-polyfill.98986ac5.js +1 -0
- package/dist/static/js/lib-react.5c9129e0.js +2 -0
- package/dist/static/js/lib-react.5c9129e0.js.LICENSE.txt +39 -0
- package/package.json +104 -0
- package/src/assets/destroy_stage_0.png +0 -0
- package/src/assets/destroy_stage_1.png +0 -0
- package/src/assets/destroy_stage_2.png +0 -0
- package/src/assets/destroy_stage_3.png +0 -0
- package/src/assets/destroy_stage_4.png +0 -0
- package/src/assets/destroy_stage_5.png +0 -0
- package/src/assets/destroy_stage_6.png +0 -0
- package/src/assets/destroy_stage_7.png +0 -0
- package/src/assets/destroy_stage_8.png +0 -0
- package/src/assets/destroy_stage_9.png +0 -0
- package/src/examples/README.md +146 -0
- package/src/examples/appViewerExample.ts +205 -0
- package/src/examples/initialMenuStart.ts +161 -0
- package/src/graphicsBackend/appViewer.ts +297 -0
- package/src/graphicsBackend/config.ts +119 -0
- package/src/graphicsBackend/index.ts +10 -0
- package/src/graphicsBackend/playerState.ts +61 -0
- package/src/graphicsBackend/types.ts +143 -0
- package/src/index.ts +97 -0
- package/src/lib/DebugGui.ts +190 -0
- package/src/lib/animationController.ts +85 -0
- package/src/lib/buildSharedConfig.mjs +1 -0
- package/src/lib/cameraBobbing.ts +94 -0
- package/src/lib/canvas2DOverlay.example.ts +361 -0
- package/src/lib/canvas2DOverlay.quickstart.ts +242 -0
- package/src/lib/canvas2DOverlay.ts +381 -0
- package/src/lib/cleanupDecorator.ts +29 -0
- package/src/lib/createPlayerObject.ts +55 -0
- package/src/lib/frameTimingCollector.ts +164 -0
- package/src/lib/guiRenderer.ts +283 -0
- package/src/lib/items.ts +140 -0
- package/src/lib/mesherlogReader.ts +131 -0
- package/src/lib/moreBlockDataGenerated.json +714 -0
- package/src/lib/preflatMap.json +1741 -0
- package/src/lib/simpleUtils.ts +40 -0
- package/src/lib/smoothSwitcher.ts +168 -0
- package/src/lib/spiral.ts +29 -0
- package/src/lib/ui/newStats.ts +120 -0
- package/src/lib/utils/proxy.ts +23 -0
- package/src/lib/utils/skins.ts +63 -0
- package/src/lib/utils.ts +76 -0
- package/src/lib/workerProxy.ts +342 -0
- package/src/lib/worldrendererCommon.ts +1088 -0
- package/src/mesher/mesher.ts +253 -0
- package/src/mesher/models.ts +769 -0
- package/src/mesher/modelsGeometryCommon.ts +142 -0
- package/src/mesher/shared.ts +80 -0
- package/src/mesher/standaloneRenderer.ts +270 -0
- package/src/mesher/test/a.ts +3 -0
- package/src/mesher/test/mesherTester.ts +76 -0
- package/src/mesher/test/playground.ts +19 -0
- package/src/mesher/test/test-perf.ts +74 -0
- package/src/mesher/test/tests.test.ts +56 -0
- package/src/mesher/world.ts +294 -0
- package/src/mesher/worldConstants.ts +1 -0
- package/src/modules/index.ts +11 -0
- package/src/modules/starfield.ts +313 -0
- package/src/modules/types.ts +110 -0
- package/src/playerState/playerState.ts +78 -0
- package/src/playerState/types.ts +36 -0
- package/src/playground/allEntitiesDebug.ts +170 -0
- package/src/playground/baseScene.ts +587 -0
- package/src/playground/mobileControls.tsx +268 -0
- package/src/playground/playground.html +83 -0
- package/src/playground/playground.ts +11 -0
- package/src/playground/playgroundUi.tsx +140 -0
- package/src/playground/reactUtils.ts +71 -0
- package/src/playground/scenes/allEntities.ts +13 -0
- package/src/playground/scenes/entities.ts +37 -0
- package/src/playground/scenes/floorRandom.ts +33 -0
- package/src/playground/scenes/frequentUpdates.ts +148 -0
- package/src/playground/scenes/geometryExport.ts +142 -0
- package/src/playground/scenes/index.ts +12 -0
- package/src/playground/scenes/lightingStarfield.ts +40 -0
- package/src/playground/scenes/main.ts +313 -0
- package/src/playground/scenes/railsCobweb.ts +14 -0
- package/src/playground/scenes/rotationIssue.ts +7 -0
- package/src/playground/scenes/slabsOptimization.ts +16 -0
- package/src/playground/scenes/transparencyIssue.ts +11 -0
- package/src/playground/shared.ts +79 -0
- package/src/resourcesManager/index.ts +5 -0
- package/src/resourcesManager/resourcesManager.ts +314 -0
- package/src/shims/minecraftData.ts +41 -0
- package/src/sign-renderer/index.html +21 -0
- package/src/sign-renderer/index.ts +216 -0
- package/src/sign-renderer/noop.js +1 -0
- package/src/sign-renderer/playground.ts +38 -0
- package/src/sign-renderer/tests.test.ts +69 -0
- package/src/sign-renderer/vite.config.ts +10 -0
- package/src/three/appShared.ts +75 -0
- package/src/three/bannerRenderer.ts +275 -0
- package/src/three/cameraShake.ts +120 -0
- package/src/three/cinimaticScript.ts +350 -0
- package/src/three/documentRenderer.ts +491 -0
- package/src/three/entities.ts +1580 -0
- package/src/three/entity/EntityMesh.ts +707 -0
- package/src/three/entity/animations.js +171 -0
- package/src/three/entity/armorModels.json +204 -0
- package/src/three/entity/armorModels.ts +36 -0
- package/src/three/entity/entities.json +6230 -0
- package/src/three/entity/exportedModels.js +38 -0
- package/src/three/entity/externalTextures.json +1 -0
- package/src/three/entity/models/allay.obj +325 -0
- package/src/three/entity/models/arrow.obj +60 -0
- package/src/three/entity/models/axolotl.obj +509 -0
- package/src/three/entity/models/blaze.obj +601 -0
- package/src/three/entity/models/boat.obj +417 -0
- package/src/three/entity/models/camel.obj +1061 -0
- package/src/three/entity/models/cat.obj +509 -0
- package/src/three/entity/models/chicken.obj +371 -0
- package/src/three/entity/models/cod.obj +371 -0
- package/src/three/entity/models/creeper.obj +279 -0
- package/src/three/entity/models/dolphin.obj +371 -0
- package/src/three/entity/models/ender_dragon.obj +2993 -0
- package/src/three/entity/models/enderman.obj +325 -0
- package/src/three/entity/models/endermite.obj +187 -0
- package/src/three/entity/models/fox.obj +463 -0
- package/src/three/entity/models/frog.obj +739 -0
- package/src/three/entity/models/ghast.obj +463 -0
- package/src/three/entity/models/goat.obj +601 -0
- package/src/three/entity/models/guardian.obj +1015 -0
- package/src/three/entity/models/horse.obj +1061 -0
- package/src/three/entity/models/llama.obj +509 -0
- package/src/three/entity/models/minecart.obj +233 -0
- package/src/three/entity/models/parrot.obj +509 -0
- package/src/three/entity/models/piglin.obj +739 -0
- package/src/three/entity/models/pillager.obj +371 -0
- package/src/three/entity/models/rabbit.obj +555 -0
- package/src/three/entity/models/sheep.obj +555 -0
- package/src/three/entity/models/shulker.obj +141 -0
- package/src/three/entity/models/sniffer.obj +693 -0
- package/src/three/entity/models/spider.obj +509 -0
- package/src/three/entity/models/tadpole.obj +95 -0
- package/src/three/entity/models/turtle.obj +371 -0
- package/src/three/entity/models/vex.obj +325 -0
- package/src/three/entity/models/villager.obj +509 -0
- package/src/three/entity/models/warden.obj +463 -0
- package/src/three/entity/models/witch.obj +647 -0
- package/src/three/entity/models/wolf.obj +509 -0
- package/src/three/entity/models/zombie_villager.obj +463 -0
- package/src/three/entity/objModels.js +1 -0
- package/src/three/fireworks.ts +661 -0
- package/src/three/fireworksRenderer.ts +434 -0
- package/src/three/globals.d.ts +7 -0
- package/src/three/graphicsBackend.ts +274 -0
- package/src/three/graphicsBackendOffThread.ts +107 -0
- package/src/three/hand.ts +89 -0
- package/src/three/holdingBlock.ts +926 -0
- package/src/three/index.ts +20 -0
- package/src/three/itemMesh.ts +427 -0
- package/src/three/modules.d.ts +14 -0
- package/src/three/panorama.ts +308 -0
- package/src/three/panoramaShared.ts +1 -0
- package/src/three/renderSlot.ts +82 -0
- package/src/three/skyboxRenderer.ts +406 -0
- package/src/three/starField.ts +13 -0
- package/src/three/threeJsMedia.ts +731 -0
- package/src/three/threeJsMethods.ts +15 -0
- package/src/three/threeJsParticles.ts +160 -0
- package/src/three/threeJsSound.ts +95 -0
- package/src/three/threeJsUtils.ts +90 -0
- package/src/three/waypointSprite.ts +435 -0
- package/src/three/waypoints.ts +163 -0
- package/src/three/world/cursorBlock.ts +172 -0
- package/src/three/world/vr.ts +257 -0
- package/src/three/worldGeometryExport.ts +259 -0
- package/src/three/worldGeometryHandler.ts +279 -0
- package/src/three/worldRendererThree.ts +1381 -0
- package/src/worldView/index.ts +6 -0
- package/src/worldView/types.ts +66 -0
- 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
|
+
}
|