reze-engine 0.3.10 → 0.3.12

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.
@@ -0,0 +1,266 @@
1
+ import { Vec3 } from "./math";
2
+ const MAX_PARTICLES = 30000;
3
+ export const DEFAULT_PARTICLE_CONFIG = {
4
+ particleCount: 5000, // Reduced from 23000 for better performance
5
+ particleSize: 0.1, // Smaller particles
6
+ fadeSpeed: 0.008, // Faster fade = particles die sooner
7
+ explosionForce: 0.6, // Smaller explosion radius
8
+ hoverDuration: 1.2, // Shorter hover
9
+ gravity: 0.00265,
10
+ friction: 0.95494,
11
+ rocketSpeed: 1.0,
12
+ rocketSize: 0.4, // Smaller rocket trail
13
+ };
14
+ export class Firework {
15
+ constructor(startX, config) {
16
+ this.startX = startX;
17
+ this.config = config;
18
+ this.phase = "rocket";
19
+ this.timer = 0;
20
+ this.colors = [];
21
+ this.rocketAlive = true;
22
+ this.rocketLifetime = 1.0;
23
+ this.justExploded = false; // Flag for triggering audio
24
+ // Explosion particle data
25
+ this.particles = null;
26
+ this.baseColors = null;
27
+ // Generate color palette (mono, dual, or tri-color)
28
+ const rand = Math.random();
29
+ const baseHue = Math.random();
30
+ if (rand < 0.33) {
31
+ // MONO (1 Color)
32
+ this.colors.push(this.hslToRgb(baseHue, 1.0, 0.6));
33
+ }
34
+ else if (rand < 0.66) {
35
+ // DUAL (2 Colors - Complementary)
36
+ this.colors.push(this.hslToRgb(baseHue, 1.0, 0.6));
37
+ this.colors.push(this.hslToRgb((baseHue + 0.5) % 1.0, 1.0, 0.5));
38
+ }
39
+ else {
40
+ // TRI (3 Colors - Triad)
41
+ this.colors.push(this.hslToRgb(baseHue, 1.0, 0.6));
42
+ this.colors.push(this.hslToRgb((baseHue + 0.33) % 1.0, 1.0, 0.6));
43
+ this.colors.push(this.hslToRgb((baseHue + 0.66) % 1.0, 1.0, 0.6));
44
+ }
45
+ this.pos = new Vec3(startX, -50, (Math.random() - 0.5) * 30);
46
+ this.vel = new Vec3((Math.random() - 0.5) * 0.5, config.rocketSpeed * (0.9 + Math.random() * 0.3), (Math.random() - 0.5) * 0.5);
47
+ this.targetY = 10 + Math.random() * 20; // Higher explosion point
48
+ }
49
+ hslToRgb(h, s, l) {
50
+ let r, g, b;
51
+ if (s === 0) {
52
+ r = g = b = l;
53
+ }
54
+ else {
55
+ const hue2rgb = (p, q, t) => {
56
+ if (t < 0)
57
+ t += 1;
58
+ if (t > 1)
59
+ t -= 1;
60
+ if (t < 1 / 6)
61
+ return p + (q - p) * 6 * t;
62
+ if (t < 1 / 2)
63
+ return q;
64
+ if (t < 2 / 3)
65
+ return p + (q - p) * (2 / 3 - t) * 6;
66
+ return p;
67
+ };
68
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
69
+ const p = 2 * l - q;
70
+ r = hue2rgb(p, q, h + 1 / 3);
71
+ g = hue2rgb(p, q, h);
72
+ b = hue2rgb(p, q, h - 1 / 3);
73
+ }
74
+ return new Vec3(r, g, b);
75
+ }
76
+ update(deltaTime) {
77
+ if (this.phase === "rocket") {
78
+ this.pos.x += this.vel.x;
79
+ this.pos.y += this.vel.y;
80
+ this.pos.z += this.vel.z;
81
+ this.vel.y *= 0.99;
82
+ this.rocketLifetime -= deltaTime * 0.5;
83
+ if (this.vel.y < 0.2 || this.pos.y >= this.targetY || this.rocketLifetime <= 0) {
84
+ this.explode();
85
+ }
86
+ }
87
+ else if (this.phase === "explode") {
88
+ this.timer += deltaTime;
89
+ if (!this.particles)
90
+ return;
91
+ const isHovering = this.timer < this.config.hoverDuration;
92
+ const gravityFactor = this.smoothstep(this.timer, this.config.hoverDuration, this.config.hoverDuration + 0.5);
93
+ let aliveCount = 0;
94
+ const positions = this.particles.positions;
95
+ const velocities = this.particles.velocities;
96
+ const colors = this.particles.colors;
97
+ const lifetimes = this.particles.lifetimes;
98
+ for (let i = 0; i < this.particles.count; i++) {
99
+ if (lifetimes[i] > 0) {
100
+ aliveCount++;
101
+ const i3 = i * 3;
102
+ // Update position
103
+ positions[i3] += velocities[i3];
104
+ positions[i3 + 1] += velocities[i3 + 1];
105
+ positions[i3 + 2] += velocities[i3 + 2];
106
+ if (isHovering) {
107
+ // Hovering phase - apply friction
108
+ velocities[i3] *= this.config.friction;
109
+ velocities[i3 + 1] *= this.config.friction;
110
+ velocities[i3 + 2] *= this.config.friction;
111
+ }
112
+ else {
113
+ // Falling phase - apply gravity and fade
114
+ velocities[i3 + 1] -= this.config.gravity * gravityFactor;
115
+ velocities[i3] *= 0.98;
116
+ velocities[i3 + 1] *= 0.98;
117
+ velocities[i3 + 2] *= 0.98;
118
+ lifetimes[i] -= this.config.fadeSpeed;
119
+ }
120
+ // Update color alpha
121
+ const alpha = Math.max(0, lifetimes[i]);
122
+ if (this.baseColors) {
123
+ colors[i3] = this.baseColors[i3] * alpha * 1.5;
124
+ colors[i3 + 1] = this.baseColors[i3 + 1] * alpha * 1.5;
125
+ colors[i3 + 2] = this.baseColors[i3 + 2] * alpha * 1.5;
126
+ }
127
+ }
128
+ }
129
+ if (aliveCount === 0) {
130
+ this.phase = "dead";
131
+ }
132
+ }
133
+ }
134
+ smoothstep(x, edge0, edge1) {
135
+ const t = Math.max(0, Math.min(1, (x - edge0) / (edge1 - edge0)));
136
+ return t * t * (3 - 2 * t);
137
+ }
138
+ explode() {
139
+ this.phase = "explode";
140
+ this.timer = 0;
141
+ this.rocketAlive = false;
142
+ this.justExploded = true; // Mark for audio trigger
143
+ const particleCount = Math.min(this.config.particleCount, MAX_PARTICLES);
144
+ const positions = new Float32Array(particleCount * 3);
145
+ const velocities = new Float32Array(particleCount * 3);
146
+ const colors = new Float32Array(particleCount * 3);
147
+ const lifetimes = new Float32Array(particleCount);
148
+ this.baseColors = new Float32Array(particleCount * 3);
149
+ const baseSpeed = this.config.explosionForce * (0.8 + Math.random() * 0.4);
150
+ for (let i = 0; i < particleCount; i++) {
151
+ const i3 = i * 3;
152
+ // Set initial position to explosion center
153
+ positions[i3] = this.pos.x;
154
+ positions[i3 + 1] = this.pos.y;
155
+ positions[i3 + 2] = this.pos.z;
156
+ // Generate spherical velocity distribution
157
+ const theta = Math.random() * Math.PI * 2;
158
+ const phi = Math.acos(2 * Math.random() - 1);
159
+ const speed = baseSpeed * (0.8 + Math.random() * 0.4);
160
+ velocities[i3] = speed * Math.sin(phi) * Math.cos(theta);
161
+ velocities[i3 + 1] = speed * Math.sin(phi) * Math.sin(theta);
162
+ velocities[i3 + 2] = speed * Math.cos(phi);
163
+ // Pick color from palette and vary brightness
164
+ const targetColor = this.colors[Math.floor(Math.random() * this.colors.length)];
165
+ const brightness = 0.5 + Math.random() * 0.8;
166
+ this.baseColors[i3] = targetColor.x * brightness;
167
+ this.baseColors[i3 + 1] = targetColor.y * brightness;
168
+ this.baseColors[i3 + 2] = targetColor.z * brightness;
169
+ colors[i3] = this.baseColors[i3];
170
+ colors[i3 + 1] = this.baseColors[i3 + 1];
171
+ colors[i3 + 2] = this.baseColors[i3 + 2];
172
+ lifetimes[i] = 1.0;
173
+ }
174
+ this.particles = {
175
+ positions,
176
+ velocities,
177
+ colors,
178
+ lifetimes,
179
+ count: particleCount,
180
+ };
181
+ }
182
+ isDead() {
183
+ return this.phase === "dead";
184
+ }
185
+ isRocket() {
186
+ return this.phase === "rocket" && this.rocketAlive;
187
+ }
188
+ getRocketPosition() {
189
+ return this.pos;
190
+ }
191
+ getRocketColor() {
192
+ return this.colors[0];
193
+ }
194
+ getParticleData() {
195
+ return this.particles;
196
+ }
197
+ hasJustExploded() {
198
+ return this.justExploded;
199
+ }
200
+ clearExplosionFlag() {
201
+ this.justExploded = false;
202
+ }
203
+ }
204
+ export class ParticleSystem {
205
+ constructor(config) {
206
+ this.fireworks = [];
207
+ this.config = { ...DEFAULT_PARTICLE_CONFIG };
208
+ this.maxConcurrentFireworks = 5; // Limit concurrent fireworks for performance
209
+ // Auto-launch settings
210
+ this.autoLaunch = false;
211
+ this.launchInterval = 3000; // ms
212
+ this.lastLaunchTime = 0;
213
+ this.nextLaunchDelay = 0;
214
+ if (config) {
215
+ this.config = { ...this.config, ...config };
216
+ }
217
+ }
218
+ update(deltaTime) {
219
+ // Update existing fireworks
220
+ for (let i = this.fireworks.length - 1; i >= 0; i--) {
221
+ this.fireworks[i].update(deltaTime);
222
+ if (this.fireworks[i].isDead()) {
223
+ this.fireworks.splice(i, 1);
224
+ }
225
+ }
226
+ // Auto-launch
227
+ if (this.autoLaunch) {
228
+ const now = performance.now();
229
+ if (now - this.lastLaunchTime > this.nextLaunchDelay) {
230
+ this.lastLaunchTime = now;
231
+ this.nextLaunchDelay = this.launchInterval + Math.random() * 1000;
232
+ this.spawnFirework();
233
+ }
234
+ }
235
+ }
236
+ spawnFirework(x) {
237
+ // Don't spawn if we're at the limit (for performance)
238
+ if (this.fireworks.length >= this.maxConcurrentFireworks) {
239
+ return;
240
+ }
241
+ const startX = x !== undefined ? x : (Math.random() - 0.5) * 5;
242
+ this.fireworks.push(new Firework(startX, this.config));
243
+ }
244
+ getFireworks() {
245
+ return this.fireworks;
246
+ }
247
+ setAutoLaunch(enabled) {
248
+ this.autoLaunch = enabled;
249
+ if (enabled) {
250
+ this.lastLaunchTime = performance.now();
251
+ this.nextLaunchDelay = 0;
252
+ }
253
+ }
254
+ setConfig(config) {
255
+ this.config = { ...this.config, ...config };
256
+ }
257
+ getConfig() {
258
+ return { ...this.config };
259
+ }
260
+ setMaxConcurrentFireworks(max) {
261
+ this.maxConcurrentFireworks = Math.max(1, max);
262
+ }
263
+ getMaxConcurrentFireworks() {
264
+ return this.maxConcurrentFireworks;
265
+ }
266
+ }
package/dist/physics.d.ts CHANGED
@@ -72,8 +72,8 @@ export declare class Physics {
72
72
  private createAmmoRigidbodies;
73
73
  private createAmmoJoints;
74
74
  private normalizeAngle;
75
- reset(boneWorldMatrices: Float32Array, boneInverseBindMatrices: Float32Array): void;
76
- step(dt: number, boneWorldMatrices: Float32Array, boneInverseBindMatrices: Float32Array): void;
75
+ reset(boneWorldMatrices: Mat4[], boneInverseBindMatrices: Float32Array): void;
76
+ step(dt: number, boneWorldMatrices: Mat4[], boneInverseBindMatrices: Float32Array): void;
77
77
  private computeBodyOffsets;
78
78
  private positionBodiesFromBones;
79
79
  private syncFromBones;
@@ -1 +1 @@
1
- {"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../src/physics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../src/physics.ts"],"names":[],"mappings":"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"}
package/dist/physics.js CHANGED
@@ -336,7 +336,7 @@ export class Physics {
336
336
  if (!this.ammoInitialized || !this.ammo || !this.dynamicsWorld) {
337
337
  return;
338
338
  }
339
- const boneCount = boneWorldMatrices.length / 16;
339
+ const boneCount = boneWorldMatrices.length;
340
340
  const Ammo = this.ammo;
341
341
  // Ensure body offsets are computed
342
342
  if (!this.rigidbodiesInitialized) {
@@ -351,9 +351,8 @@ export class Physics {
351
351
  if (!ammoBody || rb.boneIndex < 0 || rb.boneIndex >= boneCount)
352
352
  continue;
353
353
  const boneIdx = rb.boneIndex;
354
- const worldMatIdx = boneIdx * 16;
355
354
  // Get bone world matrix
356
- const boneWorldMat = new Mat4(boneWorldMatrices.subarray(worldMatIdx, worldMatIdx + 16));
355
+ const boneWorldMat = boneWorldMatrices[boneIdx];
357
356
  // Compute body world matrix: bodyWorld = boneWorld × bodyOffsetMatrix
358
357
  // (like babylon-mmd: bodyWorldMatrix = bodyOffsetMatrix.multiplyToRef(bodyWorldMatrix))
359
358
  const bodyOffsetMatrix = rb.bodyOffsetMatrix || rb.bodyOffsetMatrixInverse.inverse();
@@ -394,7 +393,7 @@ export class Physics {
394
393
  if (!this.ammoInitialized || !this.ammo || !this.dynamicsWorld) {
395
394
  return;
396
395
  }
397
- const boneCount = boneWorldMatrices.length / 16;
396
+ const boneCount = boneWorldMatrices.length;
398
397
  if (this.firstFrame) {
399
398
  if (!this.rigidbodiesInitialized) {
400
399
  this.computeBodyOffsets(boneInverseBindMatrices, boneCount);
@@ -451,8 +450,7 @@ export class Physics {
451
450
  if (!ammoBody || rb.boneIndex < 0 || rb.boneIndex >= boneCount)
452
451
  continue;
453
452
  const boneIdx = rb.boneIndex;
454
- const worldMatIdx = boneIdx * 16;
455
- const boneWorldMat = new Mat4(boneWorldMatrices.subarray(worldMatIdx, worldMatIdx + 16));
453
+ const boneWorldMat = boneWorldMatrices[boneIdx];
456
454
  // nodeWorld = boneWorld × shapeLocal (not shapeLocal × boneWorld)
457
455
  const bodyOffsetMatrix = rb.bodyOffsetMatrix || rb.bodyOffsetMatrixInverse.inverse();
458
456
  const nodeWorldMatrix = boneWorldMat.multiply(bodyOffsetMatrix);
@@ -494,8 +492,7 @@ export class Physics {
494
492
  rb.boneIndex >= 0 &&
495
493
  rb.boneIndex < boneCount) {
496
494
  const boneIdx = rb.boneIndex;
497
- const worldMatIdx = boneIdx * 16;
498
- const boneWorldMat = new Mat4(boneWorldMatrices.subarray(worldMatIdx, worldMatIdx + 16));
495
+ const boneWorldMat = boneWorldMatrices[boneIdx];
499
496
  // nodeWorld = boneWorld × shapeLocal (not shapeLocal × boneWorld)
500
497
  const bodyOffsetMatrix = rb.bodyOffsetMatrix || rb.bodyOffsetMatrixInverse.inverse();
501
498
  const nodeWorldMatrix = boneWorldMat.multiply(bodyOffsetMatrix);
@@ -539,7 +536,6 @@ export class Physics {
539
536
  // Only dynamic rigidbodies drive bones (Static/Kinematic follow bones)
540
537
  if (rb.type === RigidbodyType.Dynamic && rb.boneIndex >= 0 && rb.boneIndex < boneCount) {
541
538
  const boneIdx = rb.boneIndex;
542
- const worldMatIdx = boneIdx * 16;
543
539
  const transform = ammoBody.getWorldTransform();
544
540
  const origin = transform.getOrigin();
545
541
  const rotation = transform.getRotation();
@@ -550,7 +546,7 @@ export class Physics {
550
546
  const boneWorldMat = nodeWorldMatrix.multiply(rb.bodyOffsetMatrixInverse);
551
547
  const values = boneWorldMat.values;
552
548
  if (!isNaN(values[0]) && !isNaN(values[15]) && Math.abs(values[0]) < 1e6 && Math.abs(values[15]) < 1e6) {
553
- boneWorldMatrices.set(values, worldMatIdx);
549
+ boneWorldMatrices[boneIdx].values.set(values);
554
550
  }
555
551
  else {
556
552
  console.warn(`[Physics] Invalid bone world matrix for rigidbody ${i} (${rb.name}), skipping update`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reze-engine",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "description": "A WebGPU-based MMD model renderer",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",