@joshtol/emotive-engine 3.3.4 → 3.3.5
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/dist/emotive-mascot-3d.js +1 -1
- package/dist/emotive-mascot-3d.js.map +1 -1
- package/dist/emotive-mascot-3d.umd.js +1 -1
- package/dist/emotive-mascot-3d.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/3d/Core3DManager.js +9 -8
- package/src/3d/ThreeRenderer.js +19 -11
- package/src/3d/effects/CrystalSoul.js +7 -9
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@joshtol/emotive-engine",
|
|
4
|
-
"version": "3.3.
|
|
4
|
+
"version": "3.3.5",
|
|
5
5
|
"description": "Open-source animation engine for AI-controlled emotional visualizations with musical time synchronization",
|
|
6
6
|
"main": "dist/emotive-mascot.umd.js",
|
|
7
7
|
"module": "dist/mascot.js",
|
package/src/3d/Core3DManager.js
CHANGED
|
@@ -291,6 +291,8 @@ export class Core3DManager {
|
|
|
291
291
|
this.glowColor = [1.0, 1.0, 1.0]; // RGB
|
|
292
292
|
this.glowColorHex = '#FFFFFF'; // Hex color for luminance normalization
|
|
293
293
|
this.glowIntensity = 1.0;
|
|
294
|
+
// OPTIMIZATION: Cache normalized color to avoid recalculating every frame
|
|
295
|
+
this._normalizedGlowColor = null;
|
|
294
296
|
this.coreGlowEnabled = true; // Toggle to enable/disable core glow
|
|
295
297
|
this.glowIntensityOverride = null; // Manual override for testing
|
|
296
298
|
this.intensityCalibrationOffset = 0; // Universal filter calibration offset
|
|
@@ -419,6 +421,9 @@ export class Core3DManager {
|
|
|
419
421
|
this.glowColor = rgb;
|
|
420
422
|
// Store hex color for bloom luminance normalization
|
|
421
423
|
this.glowColorHex = emotionData.visual.glowColor;
|
|
424
|
+
// OPTIMIZATION: Pre-compute normalized color (avoids recalculating every frame)
|
|
425
|
+
const normalized = normalizeRGBLuminance(rgb, 0.30);
|
|
426
|
+
this._normalizedGlowColor = [normalized.r, normalized.g, normalized.b];
|
|
422
427
|
|
|
423
428
|
// Calculate intensity using universal filter based on color luminance
|
|
424
429
|
// This ensures consistent visibility across all emotions regardless of color brightness
|
|
@@ -1832,11 +1837,9 @@ export class Core3DManager {
|
|
|
1832
1837
|
this.customMaterial.uniforms.glowIntensity.value = effectiveGlowIntensity;
|
|
1833
1838
|
}
|
|
1834
1839
|
|
|
1835
|
-
//
|
|
1840
|
+
// OPTIMIZATION: Use pre-computed normalized color (calculated when emotion changes)
|
|
1836
1841
|
// This ensures yellow (joy) doesn't wash out the soul while blue (sadness) stays visible
|
|
1837
|
-
|
|
1838
|
-
const normalized = normalizeRGBLuminance(this.glowColor, 0.30);
|
|
1839
|
-
const normalizedColor = [normalized.r, normalized.g, normalized.b];
|
|
1842
|
+
const normalizedColor = this._normalizedGlowColor || [1, 1, 1];
|
|
1840
1843
|
|
|
1841
1844
|
// Update emotion color on outer shell (luminance-normalized)
|
|
1842
1845
|
this.customMaterial.uniforms.emotionColor.value.setRGB(
|
|
@@ -1848,12 +1851,10 @@ export class Core3DManager {
|
|
|
1848
1851
|
this.customMaterial.uniforms.blinkIntensity.value = blinkPulse;
|
|
1849
1852
|
}
|
|
1850
1853
|
}
|
|
1851
|
-
// Update inner core color and animation (also use normalized color)
|
|
1854
|
+
// Update inner core color and animation (also use cached normalized color)
|
|
1852
1855
|
// Only update if core glow is enabled
|
|
1853
|
-
// IMPORTANT: Use glowColor (RGB), not glowColorHex - glowColor has undertone saturation applied
|
|
1854
1856
|
if (this.coreGlowEnabled) {
|
|
1855
|
-
const
|
|
1856
|
-
const normalizedCoreColor = [normalizedCore.r, normalizedCore.g, normalizedCore.b];
|
|
1857
|
+
const normalizedCoreColor = this._normalizedGlowColor || [1, 1, 1];
|
|
1857
1858
|
this.updateCrystalInnerCore(normalizedCoreColor, deltaTime);
|
|
1858
1859
|
}
|
|
1859
1860
|
}
|
package/src/3d/ThreeRenderer.js
CHANGED
|
@@ -143,6 +143,13 @@ export class ThreeRenderer {
|
|
|
143
143
|
this._zAxis = new THREE.Vector3(0, 0, 1);
|
|
144
144
|
this._cameraToMesh = new THREE.Vector3();
|
|
145
145
|
this._cameraDir = new THREE.Vector3();
|
|
146
|
+
|
|
147
|
+
// OPTIMIZATION: Reusable temp vector for soul position projection (avoids allocation per frame)
|
|
148
|
+
this._soulPosTemp = new THREE.Vector3();
|
|
149
|
+
// OPTIMIZATION: Cached soul mesh reference (avoids scene.traverse every frame)
|
|
150
|
+
this._cachedSoulMesh = null;
|
|
151
|
+
// OPTIMIZATION: Reusable Vector2 for drawing buffer size queries
|
|
152
|
+
this._drawingBufferSize = new THREE.Vector2();
|
|
146
153
|
}
|
|
147
154
|
|
|
148
155
|
/**
|
|
@@ -1494,14 +1501,15 @@ export class ThreeRenderer {
|
|
|
1494
1501
|
// === STEP 0: Render soul (layer 2) to texture for refraction sampling ===
|
|
1495
1502
|
// OPTIMIZATION: Skip soul pass entirely if geometry doesn't have a soul
|
|
1496
1503
|
if (this.soulRenderTarget && hasSoul) {
|
|
1497
|
-
//
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1504
|
+
// OPTIMIZATION: Use cached soul mesh reference instead of traversing every frame
|
|
1505
|
+
if (!this._cachedSoulMesh) {
|
|
1506
|
+
this.scene.traverse(obj => {
|
|
1507
|
+
if (obj.name === 'crystalSoul') this._cachedSoulMesh = obj;
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
const soulMesh = this._cachedSoulMesh;
|
|
1502
1511
|
|
|
1503
1512
|
this.renderer.setRenderTarget(this.soulRenderTarget);
|
|
1504
|
-
this.renderer.setClearColor(0x000000, 0);
|
|
1505
1513
|
this.renderer.clear();
|
|
1506
1514
|
|
|
1507
1515
|
// Render only soul layer (layer 2)
|
|
@@ -1520,17 +1528,17 @@ export class ThreeRenderer {
|
|
|
1520
1528
|
}
|
|
1521
1529
|
// Compute soul's screen center position for refraction sampling
|
|
1522
1530
|
if (this.coreMesh.material.uniforms.soulScreenCenter && soulMesh) {
|
|
1523
|
-
|
|
1524
|
-
|
|
1531
|
+
// OPTIMIZATION: Reuse pooled vector instead of cloning every frame
|
|
1532
|
+
this._soulPosTemp.copy(soulMesh.position);
|
|
1533
|
+
this._soulPosTemp.project(this.camera);
|
|
1525
1534
|
// Convert from NDC (-1 to 1) to UV (0 to 1)
|
|
1526
|
-
const soulScreenU = (
|
|
1527
|
-
const soulScreenV = (
|
|
1535
|
+
const soulScreenU = (this._soulPosTemp.x + 1.0) * 0.5;
|
|
1536
|
+
const soulScreenV = (this._soulPosTemp.y + 1.0) * 0.5;
|
|
1528
1537
|
this.coreMesh.material.uniforms.soulScreenCenter.value.set(soulScreenU, soulScreenV);
|
|
1529
1538
|
}
|
|
1530
1539
|
}
|
|
1531
1540
|
|
|
1532
1541
|
this.renderer.setRenderTarget(null);
|
|
1533
|
-
this.renderer.setClearColor(0x000000, 0);
|
|
1534
1542
|
}
|
|
1535
1543
|
|
|
1536
1544
|
// === STEP 1: Render main scene (layer 0) through bloom to screen ===
|
|
@@ -291,7 +291,7 @@ export class CrystalSoul {
|
|
|
291
291
|
uniforms: {
|
|
292
292
|
time: { value: 0 },
|
|
293
293
|
emotionColor: { value: new THREE.Color(1, 1, 1) },
|
|
294
|
-
energyIntensity: { value:
|
|
294
|
+
energyIntensity: { value: 0.8 }, // Fixed value - no per-frame update needed
|
|
295
295
|
driftEnabled: { value: 1.0 },
|
|
296
296
|
driftSpeed: { value: 0.5 },
|
|
297
297
|
crossWaveEnabled: { value: 1.0 },
|
|
@@ -420,17 +420,15 @@ export class CrystalSoul {
|
|
|
420
420
|
uniforms.time.value += deltaTime / 1000;
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
-
// Update emotion color
|
|
423
|
+
// Update emotion color only if changed (avoid unnecessary GPU uniform sync)
|
|
424
424
|
if (uniforms.emotionColor && glowColor) {
|
|
425
|
-
uniforms.emotionColor.value
|
|
426
|
-
|
|
427
|
-
|
|
425
|
+
const current = uniforms.emotionColor.value;
|
|
426
|
+
if (current.r !== glowColor[0] || current.g !== glowColor[1] || current.b !== glowColor[2]) {
|
|
427
|
+
current.setRGB(glowColor[0], glowColor[1], glowColor[2]);
|
|
428
|
+
}
|
|
428
429
|
}
|
|
429
430
|
|
|
430
|
-
//
|
|
431
|
-
if (uniforms.energyIntensity) {
|
|
432
|
-
uniforms.energyIntensity.value = 0.8;
|
|
433
|
-
}
|
|
431
|
+
// Note: energyIntensity is fixed at 0.8 (set in constructor, no per-frame update needed)
|
|
434
432
|
|
|
435
433
|
// Apply breathing scale
|
|
436
434
|
if (this.mesh) {
|