/** * Base Visualizer Class * Abstract base for all audio visualizers */ import * as THREE from 'three'; export class BaseVisualizer { constructor(scene) { this.scene = scene; this.mesh = null; this.isVisible = false; } /** * Initialize geometry, materials, and mesh * Override in subclasses */ init() { throw new Error('init() must be implemented by subclass'); } /** * Update visualizer with audio data * @param {Object} bands - { low, mid, high } frequency bands (0-1) * @param {number} time - Animation time */ update(bands, time) { throw new Error('update() must be implemented by subclass'); } /** * Add visualizer to scene */ show() { if (this.mesh && !this.isVisible) { this.scene.add(this.mesh); this.isVisible = true; } } /** * Remove visualizer from scene */ hide() { if (this.mesh && this.isVisible) { this.scene.remove(this.mesh); this.isVisible = false; } } /** * Cleanup resources */ dispose() { this.hide(); if (this.mesh) { this.mesh.geometry?.dispose(); if (this.mesh.material) { if (Array.isArray(this.mesh.material)) { this.mesh.material.forEach(m => m.dispose()); } else { this.mesh.material.dispose(); } } } } /** * Helper: Create shader material with common settings */ createShaderMaterial(vertexShader, fragmentShader, uniforms = {}) { return new THREE.ShaderMaterial({ vertexShader, fragmentShader, uniforms: { uTime: { value: 0 }, uLow: { value: 0 }, uMid: { value: 0 }, uHigh: { value: 0 }, ...uniforms }, transparent: true, blending: THREE.AdditiveBlending, depthWrite: false }); } } export default BaseVisualizer;