Compare commits
1 Commits
v7.0.0
...
feat/mnemo
| Author | SHA1 | Date | |
|---|---|---|---|
| 0dbce86cfc |
162
app.js
162
app.js
@@ -2933,6 +2933,168 @@ function updateAshStorm(delta, elapsed) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ═══════════════════════════════════════════
|
||||
// PROJECT MNEMOSYNE — HOLOGRAPHIC MEMORY ORBS
|
||||
// ═══════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* Spawn a glowing memory orb at the given position.
|
||||
* Used to visualize RAG retrievals and memory recalls in the Nexus.
|
||||
*
|
||||
* @param {THREE.Vector3} position - World position for the orb
|
||||
* @param {number} color - Hex color (default: 0x4af0c0 - cyan)
|
||||
* @param {number} size - Radius of the orb (default: 0.5)
|
||||
* @param {object} metadata - Optional metadata for the memory (source, timestamp, etc.)
|
||||
* @returns {THREE.Mesh} The created orb mesh
|
||||
*/
|
||||
function spawnMemoryOrb(position, color = 0x4af0c0, size = 0.5, metadata = {}) {
|
||||
// Create geometry with higher detail for better glow effect
|
||||
const geometry = new THREE.SphereGeometry(size, 32, 32);
|
||||
|
||||
// Use MeshStandardMaterial for PBR rendering with transmission
|
||||
const material = new THREE.MeshStandardMaterial({
|
||||
color: color,
|
||||
emissive: color,
|
||||
emissiveIntensity: 2.5, // High emissive for glow
|
||||
metalness: 0.3,
|
||||
roughness: 0.2,
|
||||
transparent: true,
|
||||
opacity: 0.85,
|
||||
envMapIntensity: 1.5
|
||||
});
|
||||
|
||||
const orb = new THREE.Mesh(geometry, material);
|
||||
orb.position.copy(position);
|
||||
orb.castShadow = true;
|
||||
orb.receiveShadow = true;
|
||||
|
||||
// Store metadata for interaction
|
||||
orb.userData = {
|
||||
type: 'memory_orb',
|
||||
pulse: 0,
|
||||
pulseSpeed: 0.002 + Math.random() * 0.001, // Slight variation
|
||||
originalScale: size,
|
||||
metadata: metadata,
|
||||
createdAt: Date.now()
|
||||
};
|
||||
|
||||
// Add point light for local illumination
|
||||
const light = new THREE.PointLight(color, 1.5, 8);
|
||||
light.position.set(0, 0, 0);
|
||||
orb.add(light);
|
||||
|
||||
// Add to scene if available
|
||||
if (typeof scene !== 'undefined' && scene) {
|
||||
scene.add(orb);
|
||||
}
|
||||
|
||||
// Add to memory orbs registry for animation
|
||||
if (typeof memoryOrbs !== 'undefined') {
|
||||
memoryOrbs.push(orb);
|
||||
}
|
||||
|
||||
console.info('[Mnemosyne] Memory orb spawned:', metadata.source || 'unknown');
|
||||
return orb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a memory orb from the scene.
|
||||
* @param {THREE.Mesh} orb - The orb to remove
|
||||
*/
|
||||
function removeMemoryOrb(orb) {
|
||||
if (!orb) return;
|
||||
|
||||
// Remove from scene
|
||||
if (orb.parent) {
|
||||
orb.parent.remove(orb);
|
||||
}
|
||||
|
||||
// Dispose geometry and material
|
||||
if (orb.geometry) orb.geometry.dispose();
|
||||
if (orb.material) orb.material.dispose();
|
||||
|
||||
// Remove from registry
|
||||
if (typeof memoryOrbs !== 'undefined') {
|
||||
const idx = memoryOrbs.indexOf(orb);
|
||||
if (idx > -1) memoryOrbs.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate all memory orbs (call from render loop).
|
||||
* @param {number} delta - Time since last frame
|
||||
*/
|
||||
function animateMemoryOrbs(delta) {
|
||||
if (typeof memoryOrbs === 'undefined') return;
|
||||
|
||||
for (const orb of memoryOrbs) {
|
||||
if (!orb.userData) continue;
|
||||
|
||||
// Pulse animation
|
||||
orb.userData.pulse += orb.userData.pulseSpeed * delta * 1000;
|
||||
const pulseFactor = 1 + Math.sin(orb.userData.pulse) * 0.1;
|
||||
orb.scale.setScalar(pulseFactor * orb.userData.originalScale);
|
||||
|
||||
// Gentle rotation
|
||||
orb.rotation.y += delta * 0.5;
|
||||
|
||||
// Fade based on age (optional: orbs fade after 30 seconds)
|
||||
const age = (Date.now() - orb.userData.createdAt) / 1000;
|
||||
if (age > 30) {
|
||||
const fadeStart = 30;
|
||||
const fadeDuration = 10;
|
||||
const fadeProgress = Math.min(1, (age - fadeStart) / fadeDuration);
|
||||
orb.material.opacity = 0.85 * (1 - fadeProgress);
|
||||
|
||||
if (fadeProgress >= 1) {
|
||||
removeMemoryOrb(orb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn memory orbs for RAG retrieval results.
|
||||
* @param {Array} results - Array of retrieval results with {content, score, source}
|
||||
* @param {THREE.Vector3} center - Center position to spawn orbs around
|
||||
*/
|
||||
function spawnRetrievalOrbs(results, center = new THREE.Vector3(0, 2, 0)) {
|
||||
if (!results || !Array.isArray(results)) return;
|
||||
|
||||
const colors = [0x4af0c0, 0x7b5cff, 0xffd700, 0xff4466, 0x00ff88];
|
||||
const radius = 3;
|
||||
|
||||
results.forEach((result, i) => {
|
||||
// Arrange in a spiral pattern
|
||||
const angle = (i / results.length) * Math.PI * 2;
|
||||
const height = (i / results.length) * 2 - 1;
|
||||
|
||||
const position = new THREE.Vector3(
|
||||
center.x + Math.cos(angle) * radius,
|
||||
center.y + height,
|
||||
center.z + Math.sin(angle) * radius
|
||||
);
|
||||
|
||||
// Color based on relevance score
|
||||
const colorIdx = Math.min(colors.length - 1, Math.floor(result.score * colors.length));
|
||||
const color = colors[colorIdx];
|
||||
|
||||
// Size based on score
|
||||
const size = 0.3 + result.score * 0.4;
|
||||
|
||||
spawnMemoryOrb(position, color, size, {
|
||||
source: result.source,
|
||||
score: result.score,
|
||||
contentPreview: result.content?.substring(0, 100) || ''
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize memory orbs registry
|
||||
const memoryOrbs = [];
|
||||
|
||||
init().then(() => {
|
||||
createAshStorm();
|
||||
createPortalTunnel();
|
||||
|
||||
Reference in New Issue
Block a user