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,661 @@
|
|
|
1
|
+
import * as THREE from 'three'
|
|
2
|
+
import { createCanvas } from '../lib/utils'
|
|
3
|
+
|
|
4
|
+
// Shader code
|
|
5
|
+
const vertexShader = `
|
|
6
|
+
precision highp float;
|
|
7
|
+
attribute vec3 position;
|
|
8
|
+
attribute vec4 color;
|
|
9
|
+
attribute vec3 velocity;
|
|
10
|
+
attribute float adjustSize;
|
|
11
|
+
attribute float mass;
|
|
12
|
+
|
|
13
|
+
uniform mat4 modelViewMatrix;
|
|
14
|
+
uniform mat4 projectionMatrix;
|
|
15
|
+
uniform float size;
|
|
16
|
+
|
|
17
|
+
varying vec4 vColor;
|
|
18
|
+
|
|
19
|
+
void main() {
|
|
20
|
+
vColor = color;
|
|
21
|
+
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
|
|
22
|
+
gl_PointSize = size * adjustSize * (300.0 / length(mvPosition.xyz));
|
|
23
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
24
|
+
}
|
|
25
|
+
`
|
|
26
|
+
|
|
27
|
+
const fragmentShader = `
|
|
28
|
+
precision highp float;
|
|
29
|
+
uniform sampler2D texture;
|
|
30
|
+
varying vec4 vColor;
|
|
31
|
+
|
|
32
|
+
void main() {
|
|
33
|
+
vec4 texColor = texture2D(texture, gl_PointCoord);
|
|
34
|
+
gl_FragColor = vColor * texColor;
|
|
35
|
+
}
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
// Configuration interfaces
|
|
39
|
+
export interface FireworkLaunchOptions {
|
|
40
|
+
/** Position to launch the firework from */
|
|
41
|
+
position?: THREE.Vector3
|
|
42
|
+
/** Particle size (100-600, default: 300) */
|
|
43
|
+
particleSize?: number
|
|
44
|
+
/** Force rich fireworks type (with trails) */
|
|
45
|
+
forceRich?: boolean
|
|
46
|
+
/** Force basic fireworks type (no trails) */
|
|
47
|
+
forceBasic?: boolean
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface FireworksManagerConfig {
|
|
51
|
+
/** Maximum number of active fireworks at once */
|
|
52
|
+
maxActiveFireworks?: number
|
|
53
|
+
/** Default particle size for fireworks */
|
|
54
|
+
defaultParticleSize?: number
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Constants
|
|
58
|
+
export const FIREWORKS_CONFIG = {
|
|
59
|
+
textureSize: 128,
|
|
60
|
+
gravity: new THREE.Vector3(0, -0.005, 0),
|
|
61
|
+
friction: 0.998,
|
|
62
|
+
defaultParticleSize: 300,
|
|
63
|
+
maxActiveFireworks: 5,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Utility functions
|
|
67
|
+
const getOffsetXYZ = (i: number) => {
|
|
68
|
+
const offset = 3
|
|
69
|
+
const index = i * offset
|
|
70
|
+
return { x: index, y: index + 1, z: index + 2 }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const getOffsetRGBA = (i: number) => {
|
|
74
|
+
const offset = 4
|
|
75
|
+
const index = i * offset
|
|
76
|
+
return { r: index, g: index + 1, b: index + 2, a: index + 3 }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const getRandomNum = (max = 0, min = 0) => Math.floor(Math.random() * (max + 1 - min)) + min
|
|
80
|
+
|
|
81
|
+
// Texture generation
|
|
82
|
+
const drawRadialGradation = (ctx: OffscreenCanvasRenderingContext2D, canvasRadius: number, canvasW: number, canvasH: number) => {
|
|
83
|
+
ctx.save()
|
|
84
|
+
const gradient = ctx.createRadialGradient(canvasRadius, canvasRadius, 0, canvasRadius, canvasRadius, canvasRadius)
|
|
85
|
+
gradient.addColorStop(0, 'rgba(255,255,255,1.0)')
|
|
86
|
+
gradient.addColorStop(0.5, 'rgba(255,255,255,0.5)')
|
|
87
|
+
gradient.addColorStop(1, 'rgba(255,255,255,0)')
|
|
88
|
+
ctx.fillStyle = gradient
|
|
89
|
+
ctx.fillRect(0, 0, canvasW, canvasH)
|
|
90
|
+
ctx.restore()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const createFireworksTexture = () => {
|
|
94
|
+
const diameter = FIREWORKS_CONFIG.textureSize
|
|
95
|
+
const canvas = createCanvas(diameter, diameter)
|
|
96
|
+
const ctx = canvas.getContext('2d')!
|
|
97
|
+
const canvasRadius = diameter / 2
|
|
98
|
+
|
|
99
|
+
drawRadialGradation(ctx, canvasRadius, canvas.width, canvas.height)
|
|
100
|
+
const texture = new THREE.Texture(canvas)
|
|
101
|
+
texture.needsUpdate = true
|
|
102
|
+
return texture
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Point mesh creation
|
|
106
|
+
const getPointMesh = (num: number, vels: THREE.Vector3[], type: 'seed' | 'trail' | 'default', texture: THREE.Texture, particleSize = FIREWORKS_CONFIG.defaultParticleSize) => {
|
|
107
|
+
const bufferGeometry = new THREE.BufferGeometry()
|
|
108
|
+
const vertices: number[] = []
|
|
109
|
+
const velocities: number[] = []
|
|
110
|
+
const colors: number[] = []
|
|
111
|
+
const adjustSizes: number[] = []
|
|
112
|
+
const masses: number[] = []
|
|
113
|
+
|
|
114
|
+
const colorType = Math.random() > 0.3 ? 'single' : 'multiple'
|
|
115
|
+
const singleColor = getRandomNum(100, 20) * 0.01
|
|
116
|
+
const multipleColor = () => getRandomNum(100, 1) * 0.01
|
|
117
|
+
|
|
118
|
+
let rgbType: 'red' | 'green' | 'blue' = 'red'
|
|
119
|
+
const rgbTypeDice = Math.random()
|
|
120
|
+
if (rgbTypeDice > 0.66) {
|
|
121
|
+
rgbType = 'red'
|
|
122
|
+
} else if (rgbTypeDice > 0.33) {
|
|
123
|
+
rgbType = 'green'
|
|
124
|
+
} else {
|
|
125
|
+
rgbType = 'blue'
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (let i = 0; i < num; i++) {
|
|
129
|
+
const pos = new THREE.Vector3(0, 0, 0)
|
|
130
|
+
vertices.push(pos.x, pos.y, pos.z)
|
|
131
|
+
velocities.push(vels[i].x, vels[i].y, vels[i].z)
|
|
132
|
+
|
|
133
|
+
if (type === 'seed') {
|
|
134
|
+
let size = vels[i].y ** 2 * 0.04
|
|
135
|
+
if (i === 0) size *= 1.1
|
|
136
|
+
adjustSizes.push(size)
|
|
137
|
+
masses.push(size * 0.017)
|
|
138
|
+
colors.push(1, 1, 1, 1)
|
|
139
|
+
} else if (type === 'trail') {
|
|
140
|
+
const size = Math.random() * 0.1 + 0.1
|
|
141
|
+
adjustSizes.push(size)
|
|
142
|
+
masses.push(size * 0.017)
|
|
143
|
+
colors.push(1, 1, 1, 1)
|
|
144
|
+
} else {
|
|
145
|
+
const size = getRandomNum(particleSize, 10) * 0.001
|
|
146
|
+
adjustSizes.push(size)
|
|
147
|
+
masses.push(size * 0.017)
|
|
148
|
+
|
|
149
|
+
if (colorType === 'multiple') {
|
|
150
|
+
colors.push(multipleColor(), multipleColor(), multipleColor(), 1)
|
|
151
|
+
} else {
|
|
152
|
+
switch (rgbType) {
|
|
153
|
+
case 'red':
|
|
154
|
+
colors.push(singleColor, 0.1, 0.1, 1)
|
|
155
|
+
break
|
|
156
|
+
case 'green':
|
|
157
|
+
colors.push(0.1, singleColor, 0.1, 1)
|
|
158
|
+
break
|
|
159
|
+
case 'blue':
|
|
160
|
+
colors.push(0.1, 0.1, singleColor, 1)
|
|
161
|
+
break
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
bufferGeometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3))
|
|
168
|
+
bufferGeometry.setAttribute('velocity', new THREE.Float32BufferAttribute(velocities, 3))
|
|
169
|
+
bufferGeometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 4))
|
|
170
|
+
bufferGeometry.setAttribute('adjustSize', new THREE.Float32BufferAttribute(adjustSizes, 1))
|
|
171
|
+
bufferGeometry.setAttribute('mass', new THREE.Float32BufferAttribute(masses, 1))
|
|
172
|
+
|
|
173
|
+
const shaderMaterial = new THREE.RawShaderMaterial({
|
|
174
|
+
uniforms: {
|
|
175
|
+
size: { value: FIREWORKS_CONFIG.textureSize },
|
|
176
|
+
texture: { value: texture },
|
|
177
|
+
},
|
|
178
|
+
transparent: true,
|
|
179
|
+
depthWrite: false,
|
|
180
|
+
blending: THREE.AdditiveBlending,
|
|
181
|
+
vertexShader,
|
|
182
|
+
fragmentShader,
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
return new THREE.Points(bufferGeometry, shaderMaterial)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Particle mesh classes
|
|
189
|
+
export class ParticleMesh {
|
|
190
|
+
particleNum: number
|
|
191
|
+
timerStartFading: number
|
|
192
|
+
mesh: THREE.Points
|
|
193
|
+
|
|
194
|
+
constructor (num: number, vels: THREE.Vector3[], type: 'seed' | 'trail' | 'default', texture: THREE.Texture, particleSize?: number) {
|
|
195
|
+
this.particleNum = num
|
|
196
|
+
this.timerStartFading = 10
|
|
197
|
+
this.mesh = getPointMesh(num, vels, type, texture, particleSize)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
update (gravity: THREE.Vector3) {
|
|
201
|
+
if (this.timerStartFading > 0) this.timerStartFading -= 0.3
|
|
202
|
+
|
|
203
|
+
const position = this.mesh.geometry.attributes.position as THREE.BufferAttribute
|
|
204
|
+
const velocity = this.mesh.geometry.attributes.velocity as THREE.BufferAttribute
|
|
205
|
+
const color = this.mesh.geometry.attributes.color as THREE.BufferAttribute
|
|
206
|
+
const mass = this.mesh.geometry.attributes.mass as THREE.BufferAttribute
|
|
207
|
+
|
|
208
|
+
const decrementRandom = () => (Math.random() > 0.5 ? 0.98 : 0.96)
|
|
209
|
+
const decrementByVel = (v: number) => (Math.random() > 0.5 ? 0 : (1 - v) * 0.1)
|
|
210
|
+
|
|
211
|
+
for (let i = 0; i < this.particleNum; i++) {
|
|
212
|
+
const { x, y, z } = getOffsetXYZ(i)
|
|
213
|
+
|
|
214
|
+
velocity.array[y] += gravity.y - mass.array[i]
|
|
215
|
+
velocity.array[x] *= FIREWORKS_CONFIG.friction
|
|
216
|
+
velocity.array[z] *= FIREWORKS_CONFIG.friction
|
|
217
|
+
velocity.array[y] *= FIREWORKS_CONFIG.friction
|
|
218
|
+
|
|
219
|
+
position.array[x] += velocity.array[x]
|
|
220
|
+
position.array[y] += velocity.array[y]
|
|
221
|
+
position.array[z] += velocity.array[z]
|
|
222
|
+
|
|
223
|
+
const { a } = getOffsetRGBA(i)
|
|
224
|
+
if (this.timerStartFading <= 0) {
|
|
225
|
+
color.array[a] *= decrementRandom() - decrementByVel(color.array[a])
|
|
226
|
+
if (color.array[a] < 0.001) color.array[a] = 0
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
position.needsUpdate = true
|
|
231
|
+
velocity.needsUpdate = true
|
|
232
|
+
color.needsUpdate = true
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
disposeAll () {
|
|
236
|
+
this.mesh.geometry.dispose()
|
|
237
|
+
;(this.mesh.material as THREE.Material).dispose()
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export class ParticleSeedMesh extends ParticleMesh {
|
|
242
|
+
constructor (num: number, vels: THREE.Vector3[], texture: THREE.Texture) {
|
|
243
|
+
super(num, vels, 'seed', texture)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
update (gravity: THREE.Vector3) {
|
|
247
|
+
const position = this.mesh.geometry.attributes.position as THREE.BufferAttribute
|
|
248
|
+
const velocity = this.mesh.geometry.attributes.velocity as THREE.BufferAttribute
|
|
249
|
+
const color = this.mesh.geometry.attributes.color as THREE.BufferAttribute
|
|
250
|
+
const mass = this.mesh.geometry.attributes.mass as THREE.BufferAttribute
|
|
251
|
+
|
|
252
|
+
const decrementRandom = () => (Math.random() > 0.3 ? 0.99 : 0.96)
|
|
253
|
+
const decrementByVel = (v: number) => (Math.random() > 0.3 ? 0 : (1 - v) * 0.1)
|
|
254
|
+
const shake = () => (Math.random() > 0.5 ? 0.05 : -0.05)
|
|
255
|
+
const dice = () => Math.random() > 0.1
|
|
256
|
+
const _f = FIREWORKS_CONFIG.friction * 0.98
|
|
257
|
+
|
|
258
|
+
for (let i = 0; i < this.particleNum; i++) {
|
|
259
|
+
const { x, y, z } = getOffsetXYZ(i)
|
|
260
|
+
|
|
261
|
+
velocity.array[y] += gravity.y - mass.array[i]
|
|
262
|
+
velocity.array[x] *= _f
|
|
263
|
+
velocity.array[z] *= _f
|
|
264
|
+
velocity.array[y] *= _f
|
|
265
|
+
|
|
266
|
+
position.array[x] += velocity.array[x]
|
|
267
|
+
position.array[y] += velocity.array[y]
|
|
268
|
+
position.array[z] += velocity.array[z]
|
|
269
|
+
|
|
270
|
+
if (dice()) position.array[x] += shake()
|
|
271
|
+
if (dice()) position.array[z] += shake()
|
|
272
|
+
|
|
273
|
+
const { a } = getOffsetRGBA(i)
|
|
274
|
+
color.array[a] *= decrementRandom() - decrementByVel(color.array[a])
|
|
275
|
+
if (color.array[a] < 0.001) color.array[a] = 0
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
position.needsUpdate = true
|
|
279
|
+
velocity.needsUpdate = true
|
|
280
|
+
color.needsUpdate = true
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export class ParticleTailMesh extends ParticleMesh {
|
|
285
|
+
constructor (num: number, vels: THREE.Vector3[], texture: THREE.Texture) {
|
|
286
|
+
super(num, vels, 'trail', texture)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
update (gravity: THREE.Vector3) {
|
|
290
|
+
const position = this.mesh.geometry.attributes.position as THREE.BufferAttribute
|
|
291
|
+
const velocity = this.mesh.geometry.attributes.velocity as THREE.BufferAttribute
|
|
292
|
+
const color = this.mesh.geometry.attributes.color as THREE.BufferAttribute
|
|
293
|
+
const mass = this.mesh.geometry.attributes.mass as THREE.BufferAttribute
|
|
294
|
+
|
|
295
|
+
const decrementRandom = () => (Math.random() > 0.3 ? 0.98 : 0.95)
|
|
296
|
+
const shake = () => (Math.random() > 0.5 ? 0.05 : -0.05)
|
|
297
|
+
const dice = () => Math.random() > 0.2
|
|
298
|
+
|
|
299
|
+
for (let i = 0; i < this.particleNum; i++) {
|
|
300
|
+
const { x, y, z } = getOffsetXYZ(i)
|
|
301
|
+
|
|
302
|
+
velocity.array[y] += gravity.y - mass.array[i]
|
|
303
|
+
velocity.array[x] *= FIREWORKS_CONFIG.friction
|
|
304
|
+
velocity.array[z] *= FIREWORKS_CONFIG.friction
|
|
305
|
+
velocity.array[y] *= FIREWORKS_CONFIG.friction
|
|
306
|
+
|
|
307
|
+
position.array[x] += velocity.array[x]
|
|
308
|
+
position.array[y] += velocity.array[y]
|
|
309
|
+
position.array[z] += velocity.array[z]
|
|
310
|
+
|
|
311
|
+
if (dice()) position.array[x] += shake()
|
|
312
|
+
if (dice()) position.array[z] += shake()
|
|
313
|
+
|
|
314
|
+
const { a } = getOffsetRGBA(i)
|
|
315
|
+
color.array[a] *= decrementRandom()
|
|
316
|
+
if (color.array[a] < 0.001) color.array[a] = 0
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
position.needsUpdate = true
|
|
320
|
+
velocity.needsUpdate = true
|
|
321
|
+
color.needsUpdate = true
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Fireworks classes
|
|
326
|
+
export class BasicFireworks {
|
|
327
|
+
meshGroup: THREE.Group
|
|
328
|
+
isExplode: boolean
|
|
329
|
+
petalsNum: number
|
|
330
|
+
life: number
|
|
331
|
+
seed: ParticleSeedMesh
|
|
332
|
+
flowerSizeRate: number
|
|
333
|
+
flower?: ParticleMesh
|
|
334
|
+
texture: THREE.Texture
|
|
335
|
+
particleSize: number
|
|
336
|
+
|
|
337
|
+
constructor (texture: THREE.Texture, particleSize = FIREWORKS_CONFIG.defaultParticleSize, startPosition?: THREE.Vector3) {
|
|
338
|
+
this.meshGroup = new THREE.Group()
|
|
339
|
+
this.isExplode = false
|
|
340
|
+
this.texture = texture
|
|
341
|
+
this.particleSize = particleSize
|
|
342
|
+
|
|
343
|
+
const max = 400
|
|
344
|
+
const min = 150
|
|
345
|
+
this.petalsNum = getRandomNum(max, min)
|
|
346
|
+
this.life = 150
|
|
347
|
+
this.seed = this.getSeed(startPosition)
|
|
348
|
+
this.meshGroup.add(this.seed.mesh)
|
|
349
|
+
this.flowerSizeRate = THREE.MathUtils.mapLinear(this.petalsNum, min, max, 0.4, 0.7)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
getSeed (startPosition?: THREE.Vector3): ParticleSeedMesh {
|
|
353
|
+
const num = 40
|
|
354
|
+
const vels: THREE.Vector3[] = []
|
|
355
|
+
|
|
356
|
+
for (let i = 0; i < num; i++) {
|
|
357
|
+
const vx = 0
|
|
358
|
+
const vy = i === 0 ? Math.random() * 2.5 + 0.9 : Math.random() * 2 + 0.4
|
|
359
|
+
const vz = 0
|
|
360
|
+
vels.push(new THREE.Vector3(vx, vy, vz))
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const pm = new ParticleSeedMesh(num, vels, this.texture)
|
|
364
|
+
if (startPosition) {
|
|
365
|
+
pm.mesh.position.set(startPosition.x, startPosition.y, startPosition.z)
|
|
366
|
+
} else {
|
|
367
|
+
const x = Math.random() * 80 - 40
|
|
368
|
+
const y = -50
|
|
369
|
+
const z = Math.random() * 80 - 40
|
|
370
|
+
pm.mesh.position.set(x, y, z)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return pm
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
explode (pos: THREE.Vector3) {
|
|
377
|
+
this.isExplode = true
|
|
378
|
+
this.flower = this.getFlower(pos)
|
|
379
|
+
this.meshGroup.add(this.flower.mesh)
|
|
380
|
+
this.meshGroup.remove(this.seed.mesh)
|
|
381
|
+
this.seed.disposeAll()
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
getFlower (pos: THREE.Vector3): ParticleMesh {
|
|
385
|
+
const num = this.petalsNum
|
|
386
|
+
const vels: THREE.Vector3[] = []
|
|
387
|
+
let radius: number
|
|
388
|
+
const dice = Math.random()
|
|
389
|
+
|
|
390
|
+
if (dice > 0.5) {
|
|
391
|
+
for (let i = 0; i < num; i++) {
|
|
392
|
+
radius = getRandomNum(120, 60) * 0.01
|
|
393
|
+
const theta = THREE.MathUtils.degToRad(Math.random() * 180)
|
|
394
|
+
const phi = THREE.MathUtils.degToRad(Math.random() * 360)
|
|
395
|
+
const vx = Math.sin(theta) * Math.cos(phi) * radius
|
|
396
|
+
const vy = Math.sin(theta) * Math.sin(phi) * radius
|
|
397
|
+
const vz = Math.cos(theta) * radius
|
|
398
|
+
const vel = new THREE.Vector3(vx, vy, vz)
|
|
399
|
+
vel.multiplyScalar(this.flowerSizeRate)
|
|
400
|
+
vels.push(vel)
|
|
401
|
+
}
|
|
402
|
+
} else {
|
|
403
|
+
const zStep = 180 / num
|
|
404
|
+
const trad = (360 * (Math.random() * 20 + 1)) / num
|
|
405
|
+
const xStep = trad
|
|
406
|
+
const yStep = trad
|
|
407
|
+
radius = getRandomNum(120, 60) * 0.01
|
|
408
|
+
|
|
409
|
+
for (let i = 0; i < num; i++) {
|
|
410
|
+
const sphereRate = Math.sin(THREE.MathUtils.degToRad(zStep * i))
|
|
411
|
+
const vz = Math.cos(THREE.MathUtils.degToRad(zStep * i)) * radius
|
|
412
|
+
const vx = Math.cos(THREE.MathUtils.degToRad(xStep * i)) * sphereRate * radius
|
|
413
|
+
const vy = Math.sin(THREE.MathUtils.degToRad(yStep * i)) * sphereRate * radius
|
|
414
|
+
const vel = new THREE.Vector3(vx, vy, vz)
|
|
415
|
+
vel.multiplyScalar(this.flowerSizeRate)
|
|
416
|
+
vels.push(vel)
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const particleMesh = new ParticleMesh(num, vels, 'default', this.texture, this.particleSize)
|
|
421
|
+
particleMesh.mesh.position.set(pos.x, pos.y, pos.z)
|
|
422
|
+
return particleMesh
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
update (gravity: THREE.Vector3) {
|
|
426
|
+
if (this.isExplode) {
|
|
427
|
+
this.flower!.update(gravity)
|
|
428
|
+
if (this.life > 0) this.life -= 1
|
|
429
|
+
} else {
|
|
430
|
+
this.drawTail()
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
drawTail () {
|
|
435
|
+
this.seed.update(FIREWORKS_CONFIG.gravity)
|
|
436
|
+
|
|
437
|
+
const position = this.seed.mesh.geometry.attributes.position as THREE.BufferAttribute
|
|
438
|
+
const velocity = this.seed.mesh.geometry.attributes.velocity as THREE.BufferAttribute
|
|
439
|
+
|
|
440
|
+
let count = 0
|
|
441
|
+
|
|
442
|
+
// Check if the y-axis speed is down for all particles
|
|
443
|
+
for (let i = 0, l = velocity.array.length; i < l; i++) {
|
|
444
|
+
const v = velocity.array[i]
|
|
445
|
+
const index = i % 3
|
|
446
|
+
if (index === 1 && v > 0) {
|
|
447
|
+
count++
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const isComplete = count === 0
|
|
452
|
+
if (!isComplete) return
|
|
453
|
+
|
|
454
|
+
const { x, y, z } = this.seed.mesh.position
|
|
455
|
+
const flowerPos = new THREE.Vector3(x, y, z)
|
|
456
|
+
let highestPos = 0
|
|
457
|
+
let offsetPos: THREE.Vector3 | undefined
|
|
458
|
+
|
|
459
|
+
for (let i = 0, l = position.array.length; i < l; i++) {
|
|
460
|
+
const p = position.array[i]
|
|
461
|
+
const index = i % 3
|
|
462
|
+
if (index === 1 && p > highestPos) {
|
|
463
|
+
highestPos = p
|
|
464
|
+
offsetPos = new THREE.Vector3(position.array[i - 1], p, position.array[i + 2])
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (offsetPos) {
|
|
469
|
+
flowerPos.add(offsetPos)
|
|
470
|
+
this.explode(flowerPos)
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export class RichFireworks extends BasicFireworks {
|
|
476
|
+
tailMeshGroup: THREE.Group
|
|
477
|
+
tails: ParticleTailMesh[]
|
|
478
|
+
|
|
479
|
+
constructor (texture: THREE.Texture, particleSize = FIREWORKS_CONFIG.defaultParticleSize, startPosition?: THREE.Vector3) {
|
|
480
|
+
super(texture, particleSize, startPosition)
|
|
481
|
+
|
|
482
|
+
const max = 150
|
|
483
|
+
const min = 100
|
|
484
|
+
this.petalsNum = getRandomNum(max, min)
|
|
485
|
+
this.flowerSizeRate = THREE.MathUtils.mapLinear(this.petalsNum, min, max, 0.4, 0.7)
|
|
486
|
+
this.tailMeshGroup = new THREE.Group()
|
|
487
|
+
this.tails = []
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
explode (pos: THREE.Vector3) {
|
|
491
|
+
this.isExplode = true
|
|
492
|
+
this.flower = this.getFlower(pos)
|
|
493
|
+
this.tails = this.getTail()
|
|
494
|
+
this.meshGroup.add(this.flower.mesh)
|
|
495
|
+
this.meshGroup.add(this.tailMeshGroup)
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
getTail (): ParticleTailMesh[] {
|
|
499
|
+
const tails: ParticleTailMesh[] = []
|
|
500
|
+
const num = 20
|
|
501
|
+
const petalColor = this.flower!.mesh.geometry.attributes.color as THREE.BufferAttribute
|
|
502
|
+
|
|
503
|
+
for (let i = 0; i < this.petalsNum; i++) {
|
|
504
|
+
const vels: THREE.Vector3[] = []
|
|
505
|
+
for (let j = 0; j < num; j++) {
|
|
506
|
+
vels.push(new THREE.Vector3(0, 0, 0))
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const tail = new ParticleTailMesh(num, vels, this.texture)
|
|
510
|
+
const { r, g, b, a } = getOffsetRGBA(i)
|
|
511
|
+
|
|
512
|
+
const petalR = petalColor.array[r]
|
|
513
|
+
const petalG = petalColor.array[g]
|
|
514
|
+
const petalB = petalColor.array[b]
|
|
515
|
+
const petalA = petalColor.array[a]
|
|
516
|
+
|
|
517
|
+
const position = tail.mesh.geometry.attributes.position as THREE.BufferAttribute
|
|
518
|
+
const color = tail.mesh.geometry.attributes.color as THREE.BufferAttribute
|
|
519
|
+
|
|
520
|
+
for (let k = 0; k < position.count; k++) {
|
|
521
|
+
const rgba = getOffsetRGBA(k)
|
|
522
|
+
color.array[rgba.r] = petalR
|
|
523
|
+
color.array[rgba.g] = petalG
|
|
524
|
+
color.array[rgba.b] = petalB
|
|
525
|
+
color.array[rgba.a] = petalA
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const { x, y, z } = this.flower!.mesh.position
|
|
529
|
+
tail.mesh.position.set(x, y, z)
|
|
530
|
+
tails.push(tail)
|
|
531
|
+
this.tailMeshGroup.add(tail.mesh)
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return tails
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
update (gravity: THREE.Vector3) {
|
|
538
|
+
if (this.isExplode) {
|
|
539
|
+
this.flower!.update(gravity)
|
|
540
|
+
|
|
541
|
+
const flowerGeometry = this.flower!.mesh.geometry.attributes.position as THREE.BufferAttribute
|
|
542
|
+
|
|
543
|
+
for (let i = 0, l = this.tails.length; i < l; i++) {
|
|
544
|
+
const tail = this.tails[i]
|
|
545
|
+
tail.update(gravity)
|
|
546
|
+
|
|
547
|
+
const { x, y, z } = getOffsetXYZ(i)
|
|
548
|
+
const flowerPos = new THREE.Vector3(
|
|
549
|
+
flowerGeometry.array[x],
|
|
550
|
+
flowerGeometry.array[y],
|
|
551
|
+
flowerGeometry.array[z]
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
const position = tail.mesh.geometry.attributes.position as THREE.BufferAttribute
|
|
555
|
+
const velocity = tail.mesh.geometry.attributes.velocity as THREE.BufferAttribute
|
|
556
|
+
|
|
557
|
+
for (let k = 0; k < position.count; k++) {
|
|
558
|
+
const offset = getOffsetXYZ(k)
|
|
559
|
+
const desiredVelocity = new THREE.Vector3()
|
|
560
|
+
const tailPos = new THREE.Vector3(position.array[offset.x], position.array[offset.y], position.array[offset.z])
|
|
561
|
+
const tailVel = new THREE.Vector3(velocity.array[offset.x], velocity.array[offset.y], velocity.array[offset.z])
|
|
562
|
+
desiredVelocity.subVectors(flowerPos, tailPos)
|
|
563
|
+
const steer = desiredVelocity.sub(tailVel)
|
|
564
|
+
steer.normalize()
|
|
565
|
+
steer.multiplyScalar(Math.random() * 0.0003 * this.life)
|
|
566
|
+
velocity.array[offset.x] += steer.x
|
|
567
|
+
velocity.array[offset.y] += steer.y
|
|
568
|
+
velocity.array[offset.z] += steer.z
|
|
569
|
+
}
|
|
570
|
+
velocity.needsUpdate = true
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (this.life > 0) this.life -= 1.2
|
|
574
|
+
} else {
|
|
575
|
+
this.drawTail()
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Manager class for handling multiple fireworks
|
|
581
|
+
export class FireworksManager {
|
|
582
|
+
fireworksInstances: Array<BasicFireworks | RichFireworks>
|
|
583
|
+
scene: THREE.Scene
|
|
584
|
+
texture: THREE.Texture
|
|
585
|
+
particleSize: number
|
|
586
|
+
maxFireworks: number
|
|
587
|
+
|
|
588
|
+
constructor (scene: THREE.Scene, config?: FireworksManagerConfig) {
|
|
589
|
+
this.fireworksInstances = []
|
|
590
|
+
this.scene = scene
|
|
591
|
+
this.texture = createFireworksTexture()
|
|
592
|
+
this.particleSize = config?.defaultParticleSize ?? FIREWORKS_CONFIG.defaultParticleSize
|
|
593
|
+
this.maxFireworks = config?.maxActiveFireworks ?? FIREWORKS_CONFIG.maxActiveFireworks
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
launchFirework (options?: FireworkLaunchOptions) {
|
|
597
|
+
if (this.fireworksInstances.length >= this.maxFireworks) return
|
|
598
|
+
|
|
599
|
+
const particleSize = options?.particleSize ?? this.particleSize
|
|
600
|
+
const position = options?.position
|
|
601
|
+
|
|
602
|
+
let fw: BasicFireworks | RichFireworks
|
|
603
|
+
if (options?.forceRich) {
|
|
604
|
+
fw = new RichFireworks(this.texture, particleSize, position)
|
|
605
|
+
} else if (options?.forceBasic) {
|
|
606
|
+
fw = new BasicFireworks(this.texture, particleSize, position)
|
|
607
|
+
} else {
|
|
608
|
+
fw = Math.random() > 0.5 ? new BasicFireworks(this.texture, particleSize, position) : new RichFireworks(this.texture, particleSize, position)
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
this.fireworksInstances.push(fw)
|
|
612
|
+
this.scene.add(fw.meshGroup)
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
update () {
|
|
616
|
+
const explodedIndexList: number[] = []
|
|
617
|
+
|
|
618
|
+
for (let i = this.fireworksInstances.length - 1; i >= 0; i--) {
|
|
619
|
+
const instance = this.fireworksInstances[i]
|
|
620
|
+
instance.update(FIREWORKS_CONFIG.gravity)
|
|
621
|
+
if (instance.isExplode) explodedIndexList.push(i)
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
for (let i = 0, l = explodedIndexList.length; i < l; i++) {
|
|
625
|
+
const index = explodedIndexList[i]
|
|
626
|
+
const instance = this.fireworksInstances[index]
|
|
627
|
+
if (!instance) continue
|
|
628
|
+
|
|
629
|
+
instance.meshGroup.remove(instance.seed.mesh)
|
|
630
|
+
instance.seed.disposeAll()
|
|
631
|
+
|
|
632
|
+
if (instance.life <= 0) {
|
|
633
|
+
this.scene.remove(instance.meshGroup)
|
|
634
|
+
if (instance instanceof RichFireworks && instance.tailMeshGroup) {
|
|
635
|
+
for (const v of instance.tails) {
|
|
636
|
+
v.disposeAll()
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
instance.flower?.disposeAll()
|
|
640
|
+
this.fireworksInstances.splice(index, 1)
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
clear () {
|
|
646
|
+
for (const instance of this.fireworksInstances) {
|
|
647
|
+
this.scene.remove(instance.meshGroup)
|
|
648
|
+
instance.seed.disposeAll()
|
|
649
|
+
if (instance.flower) instance.flower.disposeAll()
|
|
650
|
+
if (instance instanceof RichFireworks) {
|
|
651
|
+
for (const v of instance.tails) v.disposeAll()
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
this.fireworksInstances = []
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
dispose () {
|
|
658
|
+
this.clear()
|
|
659
|
+
this.texture.dispose()
|
|
660
|
+
}
|
|
661
|
+
}
|