Feature: Stage colors configurable and used everywhere
This commit is contained in:
parent
7296715a5e
commit
8bf82f5f8a
@ -1,4 +1,5 @@
|
|||||||
import { state } from '../state.js';
|
import { state } from '../state.js';
|
||||||
|
import * as THREE from 'three';
|
||||||
import { SceneFeature } from './SceneFeature.js';
|
import { SceneFeature } from './SceneFeature.js';
|
||||||
import sceneFeatureManager from './SceneFeatureManager.js';
|
import sceneFeatureManager from './SceneFeatureManager.js';
|
||||||
import { MediaStorage } from '../core/media-storage.js';
|
import { MediaStorage } from '../core/media-storage.js';
|
||||||
@ -108,6 +109,33 @@ export class ConfigUI extends SceneFeature {
|
|||||||
// Lasers Toggle
|
// Lasers Toggle
|
||||||
createToggle('Lasers', 'lasersEnabled');
|
createToggle('Lasers', 'lasersEnabled');
|
||||||
|
|
||||||
|
// Laser Color Mode
|
||||||
|
const laserModeRow = document.createElement('div');
|
||||||
|
laserModeRow.style.display = 'flex';
|
||||||
|
laserModeRow.style.alignItems = 'center';
|
||||||
|
laserModeRow.style.justifyContent = 'space-between';
|
||||||
|
|
||||||
|
const laserModeLabel = document.createElement('label');
|
||||||
|
laserModeLabel.innerText = 'Laser Colors';
|
||||||
|
|
||||||
|
const laserModeSelect = document.createElement('select');
|
||||||
|
['SINGLE', 'RANDOM', 'RUNNING', 'ANY'].forEach(mode => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = mode;
|
||||||
|
opt.innerText = mode.charAt(0) + mode.slice(1).toLowerCase();
|
||||||
|
if (mode === state.config.laserColorMode) opt.selected = true;
|
||||||
|
laserModeSelect.appendChild(opt);
|
||||||
|
});
|
||||||
|
laserModeSelect.onchange = (e) => {
|
||||||
|
state.config.laserColorMode = e.target.value;
|
||||||
|
saveConfig();
|
||||||
|
};
|
||||||
|
this.laserModeSelect = laserModeSelect;
|
||||||
|
|
||||||
|
laserModeRow.appendChild(laserModeLabel);
|
||||||
|
laserModeRow.appendChild(laserModeSelect);
|
||||||
|
rightContainer.appendChild(laserModeRow);
|
||||||
|
|
||||||
// Side Screens Toggle
|
// Side Screens Toggle
|
||||||
createToggle('Side Screens', 'sideScreensEnabled');
|
createToggle('Side Screens', 'sideScreensEnabled');
|
||||||
|
|
||||||
@ -123,6 +151,12 @@ export class ConfigUI extends SceneFeature {
|
|||||||
// Gameboy Toggle
|
// Gameboy Toggle
|
||||||
createToggle('Gameboy', 'gameboyEnabled');
|
createToggle('Gameboy', 'gameboyEnabled');
|
||||||
|
|
||||||
|
// Stage Light Bars Toggle
|
||||||
|
createToggle('Stage Light Bars', 'lightBarsEnabled', (enabled) => {
|
||||||
|
const lightBars = sceneFeatureManager.features.find(f => f.constructor.name === 'StageLightBars');
|
||||||
|
if (lightBars) lightBars.setVisibility(enabled);
|
||||||
|
});
|
||||||
|
|
||||||
// --- Light Bar Colors ---
|
// --- Light Bar Colors ---
|
||||||
const colorContainer = document.createElement('div');
|
const colorContainer = document.createElement('div');
|
||||||
Object.assign(colorContainer.style, {
|
Object.assign(colorContainer.style, {
|
||||||
@ -135,7 +169,7 @@ export class ConfigUI extends SceneFeature {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const colorLabel = document.createElement('label');
|
const colorLabel = document.createElement('label');
|
||||||
colorLabel.innerText = 'Stage Light Bars';
|
colorLabel.innerText = 'Stage colors';
|
||||||
colorContainer.appendChild(colorLabel);
|
colorContainer.appendChild(colorLabel);
|
||||||
|
|
||||||
const colorControls = document.createElement('div');
|
const colorControls = document.createElement('div');
|
||||||
@ -155,7 +189,7 @@ export class ConfigUI extends SceneFeature {
|
|||||||
addColorBtn.onclick = () => {
|
addColorBtn.onclick = () => {
|
||||||
state.config.lightBarColors.push(colorPicker.value);
|
state.config.lightBarColors.push(colorPicker.value);
|
||||||
saveConfig();
|
saveConfig();
|
||||||
updateColorList();
|
this.updateColorList();
|
||||||
const lightBars = sceneFeatureManager.features.find(f => f.constructor.name === 'StageLightBars');
|
const lightBars = sceneFeatureManager.features.find(f => f.constructor.name === 'StageLightBars');
|
||||||
if (lightBars) lightBars.refreshColors();
|
if (lightBars) lightBars.refreshColors();
|
||||||
};
|
};
|
||||||
@ -166,7 +200,23 @@ export class ConfigUI extends SceneFeature {
|
|||||||
clearColorsBtn.onclick = () => {
|
clearColorsBtn.onclick = () => {
|
||||||
state.config.lightBarColors = [];
|
state.config.lightBarColors = [];
|
||||||
saveConfig();
|
saveConfig();
|
||||||
updateColorList();
|
this.updateColorList();
|
||||||
|
const lightBars = sceneFeatureManager.features.find(f => f.constructor.name === 'StageLightBars');
|
||||||
|
if (lightBars) lightBars.refreshColors();
|
||||||
|
};
|
||||||
|
|
||||||
|
const randomizeColorsBtn = document.createElement('button');
|
||||||
|
randomizeColorsBtn.innerText = 'Randomize';
|
||||||
|
randomizeColorsBtn.style.cursor = 'pointer';
|
||||||
|
randomizeColorsBtn.onclick = () => {
|
||||||
|
const count = 2 + Math.floor(Math.random() * 5); // 2 to 6
|
||||||
|
const newColors = [];
|
||||||
|
for(let i=0; i<count; i++) {
|
||||||
|
newColors.push('#' + new THREE.Color().setHSL(Math.random(), 1.0, 0.5).getHexString());
|
||||||
|
}
|
||||||
|
state.config.lightBarColors = newColors;
|
||||||
|
saveConfig();
|
||||||
|
this.updateColorList();
|
||||||
const lightBars = sceneFeatureManager.features.find(f => f.constructor.name === 'StageLightBars');
|
const lightBars = sceneFeatureManager.features.find(f => f.constructor.name === 'StageLightBars');
|
||||||
if (lightBars) lightBars.refreshColors();
|
if (lightBars) lightBars.refreshColors();
|
||||||
};
|
};
|
||||||
@ -174,6 +224,7 @@ export class ConfigUI extends SceneFeature {
|
|||||||
colorControls.appendChild(colorPicker);
|
colorControls.appendChild(colorPicker);
|
||||||
colorControls.appendChild(addColorBtn);
|
colorControls.appendChild(addColorBtn);
|
||||||
colorControls.appendChild(clearColorsBtn);
|
colorControls.appendChild(clearColorsBtn);
|
||||||
|
colorControls.appendChild(randomizeColorsBtn);
|
||||||
colorContainer.appendChild(colorControls);
|
colorContainer.appendChild(colorControls);
|
||||||
|
|
||||||
const colorList = document.createElement('div');
|
const colorList = document.createElement('div');
|
||||||
@ -421,6 +472,8 @@ export class ConfigUI extends SceneFeature {
|
|||||||
consoleRGBEnabled: true,
|
consoleRGBEnabled: true,
|
||||||
consoleEnabled: true,
|
consoleEnabled: true,
|
||||||
gameboyEnabled: false,
|
gameboyEnabled: false,
|
||||||
|
lightBarsEnabled: true,
|
||||||
|
laserColorMode: 'RUNNING',
|
||||||
guestCount: 150,
|
guestCount: 150,
|
||||||
djHat: 'None'
|
djHat: 'None'
|
||||||
};
|
};
|
||||||
@ -432,6 +485,7 @@ export class ConfigUI extends SceneFeature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.guestInput) this.guestInput.value = defaults.guestCount;
|
if (this.guestInput) this.guestInput.value = defaults.guestCount;
|
||||||
|
if (this.laserModeSelect) this.laserModeSelect.value = defaults.laserColorMode;
|
||||||
if (this.hatSelect) this.hatSelect.value = defaults.djHat;
|
if (this.hatSelect) this.hatSelect.value = defaults.djHat;
|
||||||
this.updateStatus();
|
this.updateStatus();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -228,11 +228,20 @@ export class MusicConsole extends SceneFeature {
|
|||||||
const intensity = (wave1 + wave2 + wave3) / 3 * 0.5 + 0.5;
|
const intensity = (wave1 + wave2 + wave3) / 3 * 0.5 + 0.5;
|
||||||
|
|
||||||
// Color palette shifting
|
// Color palette shifting
|
||||||
const hue = (time * 0.1 + u * 0.3 + intensity * 0.2) % 1;
|
const palette = state.config.lightBarColors;
|
||||||
const sat = 0.9;
|
if (palette && palette.length > 0) {
|
||||||
const light = intensity * (0.1 + beatIntensity * 0.9); // Pulse brightness with beat
|
const drive = (time * 0.1 + u * 0.3 + intensity * 0.2);
|
||||||
|
const paletteIndex = Math.floor(((drive % 1) + 1) % 1 * palette.length);
|
||||||
color.setHSL(hue, sat, light);
|
color.set(palette[paletteIndex]);
|
||||||
|
|
||||||
|
const light = intensity * (0.1 + beatIntensity * 0.9);
|
||||||
|
color.multiplyScalar(light);
|
||||||
|
} else {
|
||||||
|
const hue = (time * 0.1 + u * 0.3 + intensity * 0.2) % 1;
|
||||||
|
const sat = 0.9;
|
||||||
|
const light = intensity * (0.1 + beatIntensity * 0.9); // Pulse brightness with beat
|
||||||
|
color.setHSL(hue, sat, light);
|
||||||
|
}
|
||||||
this.frontLedMesh.setColorAt(idx, color);
|
this.frontLedMesh.setColorAt(idx, color);
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,6 +64,8 @@ uniform float u_time;
|
|||||||
uniform float u_beat;
|
uniform float u_beat;
|
||||||
uniform float u_opacity;
|
uniform float u_opacity;
|
||||||
uniform vec2 u_resolution;
|
uniform vec2 u_resolution;
|
||||||
|
uniform vec3 u_colors[16];
|
||||||
|
uniform int u_colorCount;
|
||||||
varying vec2 vUv;
|
varying vec2 vUv;
|
||||||
|
|
||||||
vec3 hsv2rgb(vec3 c) {
|
vec3 hsv2rgb(vec3 c) {
|
||||||
@ -98,7 +100,24 @@ void main() {
|
|||||||
|
|
||||||
float hue = fract(u_time * 0.1 + d * 0.2);
|
float hue = fract(u_time * 0.1 + d * 0.2);
|
||||||
float val = 0.5 + 0.5 * sin(wave + beatWave);
|
float val = 0.5 + 0.5 * sin(wave + beatWave);
|
||||||
gl_FragColor = vec4(hsv2rgb(vec3(hue, 0.8, val)), mask);
|
|
||||||
|
vec3 finalColor;
|
||||||
|
if (u_colorCount > 0) {
|
||||||
|
float indexFloat = hue * float(u_colorCount);
|
||||||
|
int index = int(mod(indexFloat, float(u_colorCount)));
|
||||||
|
vec3 c = vec3(0.0);
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
if (i == index) {
|
||||||
|
c = u_colors[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finalColor = c * val;
|
||||||
|
} else {
|
||||||
|
finalColor = hsv2rgb(vec3(hue, 0.8, val));
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = vec4(finalColor, mask);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -110,6 +129,7 @@ export class ProjectionScreen extends SceneFeature {
|
|||||||
projectionScreenInstance = this;
|
projectionScreenInstance = this;
|
||||||
this.isVisualizerActive = false;
|
this.isVisualizerActive = false;
|
||||||
this.screens = [];
|
this.screens = [];
|
||||||
|
this.colorBuffer = new Float32Array(16 * 3);
|
||||||
sceneFeatureManager.register(this);
|
sceneFeatureManager.register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,11 +256,27 @@ export class ProjectionScreen extends SceneFeature {
|
|||||||
if (this.isVisualizerActive) {
|
if (this.isVisualizerActive) {
|
||||||
const beat = (state.music && state.music.beatIntensity) ? state.music.beatIntensity : 0.0;
|
const beat = (state.music && state.music.beatIntensity) ? state.music.beatIntensity : 0.0;
|
||||||
state.screenLight.intensity = state.originalScreenIntensity * (0.5 + beat * 0.5);
|
state.screenLight.intensity = state.originalScreenIntensity * (0.5 + beat * 0.5);
|
||||||
|
|
||||||
|
// Update color buffer
|
||||||
|
const colors = state.config.lightBarColors;
|
||||||
|
const colorCount = colors ? Math.min(colors.length, 16) : 0;
|
||||||
|
|
||||||
|
if (colorCount > 0) {
|
||||||
|
for (let i = 0; i < colorCount; i++) {
|
||||||
|
const c = new THREE.Color(colors[i]);
|
||||||
|
this.colorBuffer[i * 3] = c.r;
|
||||||
|
this.colorBuffer[i * 3 + 1] = c.g;
|
||||||
|
this.colorBuffer[i * 3 + 2] = c.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.screens.forEach(s => {
|
this.screens.forEach(s => {
|
||||||
if (s.mesh.material && s.mesh.material.uniforms && s.mesh.material.uniforms.u_time) {
|
if (s.mesh.material && s.mesh.material.uniforms && s.mesh.material.uniforms.u_time) {
|
||||||
s.mesh.material.uniforms.u_time.value = state.clock.getElapsedTime();
|
s.mesh.material.uniforms.u_time.value = state.clock.getElapsedTime();
|
||||||
s.mesh.material.uniforms.u_beat.value = beat;
|
s.mesh.material.uniforms.u_beat.value = beat;
|
||||||
|
if (s.mesh.material.uniforms.u_colorCount) {
|
||||||
|
s.mesh.material.uniforms.u_colorCount.value = colorCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -273,7 +309,9 @@ export class ProjectionScreen extends SceneFeature {
|
|||||||
u_time: { value: 0.0 },
|
u_time: { value: 0.0 },
|
||||||
u_beat: { value: 0.0 },
|
u_beat: { value: 0.0 },
|
||||||
u_opacity: { value: state.screenOpacity },
|
u_opacity: { value: state.screenOpacity },
|
||||||
u_resolution: { value: new THREE.Vector2(1, 1) } // Placeholder, set in applyMaterialToAll
|
u_resolution: { value: new THREE.Vector2(1, 1) }, // Placeholder, set in applyMaterialToAll
|
||||||
|
u_colors: { value: this.colorBuffer },
|
||||||
|
u_colorCount: { value: 0 }
|
||||||
},
|
},
|
||||||
vertexShader: screenVertexShader,
|
vertexShader: screenVertexShader,
|
||||||
fragmentShader: visualizerFragmentShader,
|
fragmentShader: visualizerFragmentShader,
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export class StageLasers extends SceneFeature {
|
|||||||
this.activationState = 'IDLE';
|
this.activationState = 'IDLE';
|
||||||
this.stateTimer = 0;
|
this.stateTimer = 0;
|
||||||
this.initialSilenceSeconds = 10;
|
this.initialSilenceSeconds = 10;
|
||||||
|
this.currentCycleMode = 'RUNNING';
|
||||||
sceneFeatureManager.register(this);
|
sceneFeatureManager.register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +95,8 @@ export class StageLasers extends SceneFeature {
|
|||||||
flare: flare,
|
flare: flare,
|
||||||
index: i,
|
index: i,
|
||||||
totalInBank: count,
|
totalInBank: count,
|
||||||
bankId: position.x < 0 ? 0 : (position.x > 0 ? 1 : 2) // 0:L, 1:R, 2:C
|
bankId: position.x < 0 ? 0 : (position.x > 0 ? 1 : 2), // 0:L, 1:R, 2:C
|
||||||
|
staticColorIndex: Math.floor(Math.random() * 100)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,6 +127,10 @@ export class StageLasers extends SceneFeature {
|
|||||||
if (time > this.initialSilenceSeconds && loudness > this.averageLoudness + 0.1) {
|
if (time > this.initialSilenceSeconds && loudness > this.averageLoudness + 0.1) {
|
||||||
this.activationState = 'WARMUP';
|
this.activationState = 'WARMUP';
|
||||||
this.stateTimer = 1.0; // Warmup duration
|
this.stateTimer = 1.0; // Warmup duration
|
||||||
|
|
||||||
|
// Pick a random mode for this activation cycle (used if config is 'ANY')
|
||||||
|
const modes = ['SINGLE', 'RANDOM', 'RUNNING'];
|
||||||
|
this.currentCycleMode = modes[Math.floor(Math.random() * modes.length)];
|
||||||
}
|
}
|
||||||
} else if (this.activationState === 'WARMUP') {
|
} else if (this.activationState === 'WARMUP') {
|
||||||
isActive = true;
|
isActive = true;
|
||||||
@ -172,8 +178,7 @@ export class StageLasers extends SceneFeature {
|
|||||||
|
|
||||||
// --- Color & Intensity ---
|
// --- Color & Intensity ---
|
||||||
const beat = state.music ? state.music.beatIntensity : 0;
|
const beat = state.music ? state.music.beatIntensity : 0;
|
||||||
const hue = (time * 0.1) % 1;
|
|
||||||
const color = new THREE.Color().setHSL(hue, 1.0, 0.5);
|
|
||||||
let intensity = 0.2 + beat * 0.6;
|
let intensity = 0.2 + beat * 0.6;
|
||||||
|
|
||||||
// Strobe Mode: Flash rapidly when beat intensity is high
|
// Strobe Mode: Flash rapidly when beat intensity is high
|
||||||
@ -205,9 +210,39 @@ export class StageLasers extends SceneFeature {
|
|||||||
flareScale = fade;
|
flareScale = fade;
|
||||||
}
|
}
|
||||||
|
|
||||||
l.mesh.material.color.copy(color);
|
let colorHex = 0x00ff00; // Default green
|
||||||
|
const palette = state.config.lightBarColors;
|
||||||
|
|
||||||
|
let mode = state.config.laserColorMode;
|
||||||
|
if (mode === 'ANY') {
|
||||||
|
mode = this.currentCycleMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (palette && palette.length > 0) {
|
||||||
|
if (mode === 'SINGLE') {
|
||||||
|
colorHex = palette[0];
|
||||||
|
} else if (mode === 'RANDOM') {
|
||||||
|
colorHex = palette[l.staticColorIndex % palette.length];
|
||||||
|
} else if (mode === 'RUNNING') {
|
||||||
|
const offset = Math.floor(time * 4); // Speed of running
|
||||||
|
const idx = (l.index + offset) % palette.length;
|
||||||
|
colorHex = palette[idx];
|
||||||
|
} else {
|
||||||
|
// Fallback to running if unknown
|
||||||
|
const idx = l.index % palette.length;
|
||||||
|
colorHex = palette[idx];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback if no palette: Cycle hue
|
||||||
|
const hue = (time * 0.1) % 1;
|
||||||
|
const c = new THREE.Color().setHSL(hue, 1.0, 0.5);
|
||||||
|
colorHex = c.getHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
l.mesh.material.color.set(colorHex);
|
||||||
|
l.flare.material.color.set(colorHex);
|
||||||
|
|
||||||
l.mesh.material.opacity = currentIntensity;
|
l.mesh.material.opacity = currentIntensity;
|
||||||
l.flare.material.color.copy(color);
|
|
||||||
l.flare.scale.setScalar(flareScale);
|
l.flare.scale.setScalar(flareScale);
|
||||||
|
|
||||||
// --- Movement Calculation ---
|
// --- Movement Calculation ---
|
||||||
|
|||||||
@ -7,6 +7,10 @@ export class StageLightBars extends SceneFeature {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.bars = [];
|
this.bars = [];
|
||||||
|
this.mode = 'STATIC'; // 'STATIC' or 'CHASE'
|
||||||
|
this.lastModeChange = 0;
|
||||||
|
this.chaseOffset = 0;
|
||||||
|
this.staticIndices = [];
|
||||||
sceneFeatureManager.register(this);
|
sceneFeatureManager.register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,25 +20,23 @@ export class StageLightBars extends SceneFeature {
|
|||||||
// or we can update them dynamically.
|
// or we can update them dynamically.
|
||||||
|
|
||||||
// 1. Stage Front Edge Bar
|
// 1. Stage Front Edge Bar
|
||||||
// Stage is approx width 11, height 1.5, centered at z=-17.5, depth 5.
|
this.createBar(new THREE.Vector3(0, 1.50, -14.95), new THREE.Vector3(11, 0.1, 0.1));
|
||||||
// Front edge is at z = -17.5 + 2.5 = -15.
|
|
||||||
this.createBar(new THREE.Vector3(0, 1.50, -14.8), new THREE.Vector3(11, 0.1, 0.1));
|
|
||||||
|
|
||||||
// 2. Stage Side Bars (Vertical)
|
// 2. Stage Side Bars (Vertical)
|
||||||
// Left Front
|
// Left Front
|
||||||
this.createBar(new THREE.Vector3(-5.45, 0.7, -14.8), new THREE.Vector3(0.1, 1.5, 0.1));
|
this.createBar(new THREE.Vector3(-5.45, 0.7, -14.95), new THREE.Vector3(0.1, 1.5, 0.1));
|
||||||
// Right Front
|
// Right Front
|
||||||
this.createBar(new THREE.Vector3(5.45, 0.7, -14.8), new THREE.Vector3(0.1, 1.5, 0.1));
|
this.createBar(new THREE.Vector3(5.45, 0.7, -14.95), new THREE.Vector3(0.1, 1.5, 0.1));
|
||||||
|
|
||||||
// 3. Overhead Beam Bars
|
// 3. Overhead Beam Bars
|
||||||
// Beam is at y=9, z=-14, length 24.
|
this.createBar(new THREE.Vector3(0, 8.5, -14), new THREE.Vector3(31, 0.1, 0.1));
|
||||||
this.createBar(new THREE.Vector3(0, 8.7, -14), new THREE.Vector3(24, 0.1, 0.1));
|
|
||||||
|
|
||||||
// 4. Vertical Truss Bars (Sides of the beam)
|
// 4. Vertical Truss Bars (Sides of the beam)
|
||||||
this.createBar(new THREE.Vector3(-11.9, 3, -14), new THREE.Vector3(0.2, 12, 0.2));
|
this.createBar(new THREE.Vector3(-15.9, 3, -14), new THREE.Vector3(0.2, 12, 0.2));
|
||||||
this.createBar(new THREE.Vector3(11.9, 3, -14), new THREE.Vector3(0.2, 12, 0.2));
|
this.createBar(new THREE.Vector3(15.9, 3, -14), new THREE.Vector3(0.2, 12, 0.2));
|
||||||
|
|
||||||
this.applyColors();
|
this.applyColors();
|
||||||
|
this.setVisibility(state.config.lightBarsEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
createBar(position, size) {
|
createBar(position, size) {
|
||||||
@ -57,40 +59,92 @@ export class StageLightBars extends SceneFeature {
|
|||||||
applyColors() {
|
applyColors() {
|
||||||
const colors = state.config.lightBarColors;
|
const colors = state.config.lightBarColors;
|
||||||
if (!colors || colors.length === 0) {
|
if (!colors || colors.length === 0) {
|
||||||
// Default off or white if list is empty
|
this.staticIndices = [];
|
||||||
|
} else {
|
||||||
|
// Default distribution (sequential)
|
||||||
|
this.staticIndices = this.bars.map((_, i) => i % colors.length);
|
||||||
|
}
|
||||||
|
this.updateBarColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBarColors() {
|
||||||
|
const colors = state.config.lightBarColors;
|
||||||
|
if (!colors || colors.length === 0) {
|
||||||
this.bars.forEach(bar => {
|
this.bars.forEach(bar => {
|
||||||
bar.material.color.setHex(0x111111);
|
bar.material.color.setHex(0x111111);
|
||||||
bar.material.emissive.setHex(0x000000);
|
bar.material.emissive.setHex(0x000000);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.bars.forEach((bar, index) => {
|
this.bars.forEach((bar, index) => {
|
||||||
const colorHex = colors[index % colors.length];
|
let colorIndex;
|
||||||
|
if (this.mode === 'CHASE') {
|
||||||
|
colorIndex = Math.floor(index + this.chaseOffset) % colors.length;
|
||||||
|
} else {
|
||||||
|
// Ensure staticIndices is populated
|
||||||
|
if (this.staticIndices.length <= index) {
|
||||||
|
this.staticIndices.push(index % colors.length);
|
||||||
|
}
|
||||||
|
colorIndex = this.staticIndices[index] % colors.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorIndex < 0) colorIndex += colors.length;
|
||||||
|
|
||||||
|
const colorHex = colors[colorIndex];
|
||||||
const color = new THREE.Color(colorHex);
|
const color = new THREE.Color(colorHex);
|
||||||
bar.material.color.copy(color);
|
bar.material.color.copy(color);
|
||||||
bar.material.emissive.copy(color);
|
bar.material.emissive.copy(color);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
update(deltaTime) {
|
redistributeColors() {
|
||||||
// Check if colors changed (simple length check or reference check could work,
|
const colors = state.config.lightBarColors;
|
||||||
// but here we can just re-apply if needed or rely on ConfigUI calling a refresh.
|
if (colors && colors.length > 0) {
|
||||||
// For simplicity, we'll assume ConfigUI updates state and we might poll or be notified.
|
this.staticIndices = this.bars.map(() => Math.floor(Math.random() * colors.length));
|
||||||
// Let's just re-apply in update if we want to be reactive to the UI instantly,
|
}
|
||||||
// though it's slightly expensive. Better: ConfigUI triggers a refresh.
|
}
|
||||||
// For now, we'll just animate intensity.
|
|
||||||
|
|
||||||
|
setVisibility(visible) {
|
||||||
|
this.bars.forEach(bar => {
|
||||||
|
bar.mesh.visible = visible;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaTime) {
|
||||||
if (!state.partyStarted) return;
|
if (!state.partyStarted) return;
|
||||||
|
if (!state.config.lightBarsEnabled) return;
|
||||||
|
|
||||||
const time = state.clock.getElapsedTime();
|
const time = state.clock.getElapsedTime();
|
||||||
let beatIntensity = 0;
|
const beatIntensity = state.music ? state.music.beatIntensity : 0;
|
||||||
if (state.music) {
|
const isBeat = beatIntensity > 0.8;
|
||||||
beatIntensity = state.music.beatIntensity;
|
|
||||||
|
// Mode Switching Logic
|
||||||
|
if (isBeat && time - this.lastModeChange > 4.0) {
|
||||||
|
// Chance to switch mode or redistribute
|
||||||
|
if (Math.random() < 0.3) {
|
||||||
|
this.mode = this.mode === 'STATIC' ? 'CHASE' : 'STATIC';
|
||||||
|
this.lastModeChange = time;
|
||||||
|
|
||||||
|
if (this.mode === 'STATIC') {
|
||||||
|
this.redistributeColors();
|
||||||
|
}
|
||||||
|
} else if (this.mode === 'STATIC' && Math.random() < 0.5) {
|
||||||
|
// Redistribute without switching mode
|
||||||
|
this.redistributeColors();
|
||||||
|
this.lastModeChange = time;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update Chase
|
||||||
|
if (this.mode === 'CHASE') {
|
||||||
|
this.chaseOffset += deltaTime * 4.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateBarColors();
|
||||||
|
|
||||||
// Pulsate
|
// Pulsate
|
||||||
const baseIntensity = 0.1;
|
const baseIntensity = 0.2;
|
||||||
const pulse = Math.sin(time * 2.0) * 0.3 + 0.3; // Breathing
|
const pulse = Math.sin(time * 2.0) * 0.3 + 0.3; // Breathing
|
||||||
const beatFlash = beatIntensity * 2.0; // Sharp flash on beat
|
const beatFlash = beatIntensity * 2.0; // Sharp flash on beat
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,8 @@ export function initState() {
|
|||||||
consoleRGBEnabled: true,
|
consoleRGBEnabled: true,
|
||||||
consoleEnabled: true,
|
consoleEnabled: true,
|
||||||
gameboyEnabled: false,
|
gameboyEnabled: false,
|
||||||
|
lightBarsEnabled: true,
|
||||||
|
laserColorMode: 'RUNNING', // 'SINGLE', 'RANDOM', 'RUNNING', 'ANY'
|
||||||
lightBarColors: ['#ff00ff', '#00ffff', '#ffff00'], // Default neon colors
|
lightBarColors: ['#ff00ff', '#00ffff', '#ffff00'], // Default neon colors
|
||||||
guestCount: 150,
|
guestCount: 150,
|
||||||
djHat: 'None' // 'None', 'Santa', 'Top Hat'
|
djHat: 'None' // 'None', 'Santa', 'Top Hat'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user