Feature: blackout to emphasize music drops and calm sections
This commit is contained in:
parent
8b2d1aed53
commit
72a2fe7de2
@ -103,6 +103,11 @@ export class ConfigUI extends SceneFeature {
|
||||
rightContainer.appendChild(row);
|
||||
};
|
||||
|
||||
// Blackout Toggle
|
||||
createToggle('BLACKOUT', 'blackout', (enabled) => {
|
||||
state.blackoutMode = enabled;
|
||||
});
|
||||
|
||||
// Torches Toggle
|
||||
createToggle('Stage Torches', 'torchesEnabled');
|
||||
|
||||
@ -475,6 +480,7 @@ export class ConfigUI extends SceneFeature {
|
||||
lightBarsEnabled: true,
|
||||
laserColorMode: 'RUNNING',
|
||||
guestCount: 150,
|
||||
blackout: false,
|
||||
djHat: 'None'
|
||||
};
|
||||
for (const key in defaults) {
|
||||
|
||||
@ -186,6 +186,11 @@ export class MusicConsole extends SceneFeature {
|
||||
const beatIntensity = state.music.beatIntensity;
|
||||
|
||||
this.lights.forEach(light => {
|
||||
if (state.blackoutMode) {
|
||||
light.mesh.material.color.copy(light.offColor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (time > light.nextToggle) {
|
||||
// Toggle state
|
||||
light.active = !light.active;
|
||||
@ -207,7 +212,7 @@ export class MusicConsole extends SceneFeature {
|
||||
|
||||
// Update Front LED Array
|
||||
if (this.frontLedMesh) {
|
||||
if (!state.config.consoleRGBEnabled) {
|
||||
if (!state.config.consoleRGBEnabled || state.blackoutMode) {
|
||||
this.frontLedMesh.visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ import sceneFeatureManager from './SceneFeatureManager.js';
|
||||
export class MusicVisualizer extends SceneFeature {
|
||||
constructor() {
|
||||
super();
|
||||
this.lastStateChangeTime = 0;
|
||||
this.lastBlackoutMode = false;
|
||||
sceneFeatureManager.register(this);
|
||||
}
|
||||
|
||||
@ -34,6 +36,39 @@ export class MusicVisualizer extends SceneFeature {
|
||||
// This creates a very sharp spike for the torch flame effect
|
||||
const measureProgress = (time % state.music.measureDuration) / state.music.measureDuration;
|
||||
state.music.measurePulse = measureProgress < 0.2 ? Math.sin(measureProgress * Math.PI * 5) : 0;
|
||||
|
||||
// --- Auto-Blackout Logic ---
|
||||
// If blackout is active, monitor for loud events (The Drop) to disable it.
|
||||
if (state.config.blackout) {
|
||||
if (state.blackoutMode !== this.lastBlackoutMode) {
|
||||
this.lastStateChangeTime = time;
|
||||
this.lastBlackoutMode = state.blackoutMode;
|
||||
}
|
||||
|
||||
const timeInState = time - this.lastStateChangeTime;
|
||||
|
||||
// Dynamic Thresholds
|
||||
// Exit blackout: Start high 0.8 and decrease over time
|
||||
let loudThreshold = Math.max(0.4, 0.8 - (timeInState * 0.05));
|
||||
|
||||
// Enter blackout: Start low and increase over time
|
||||
let quietThreshold = Math.min(0.2, 0.05 + (timeInState * 0.01));
|
||||
|
||||
const beatThreshold = 0.8;
|
||||
|
||||
if (state.blackoutMode) {
|
||||
if (state.music.loudness > loudThreshold && state.music.beatIntensity > beatThreshold) {
|
||||
state.blackoutMode = false;
|
||||
}
|
||||
} else {
|
||||
if (state.music.loudness < quietThreshold) {
|
||||
state.blackoutMode = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state.blackoutMode = false;
|
||||
this.lastBlackoutMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -257,6 +257,10 @@ export class ProjectionScreen extends SceneFeature {
|
||||
const beat = (state.music && state.music.beatIntensity) ? state.music.beatIntensity : 0.0;
|
||||
state.screenLight.intensity = state.originalScreenIntensity * (0.5 + beat * 0.5);
|
||||
|
||||
if (state.blackoutMode) {
|
||||
state.screenLight.intensity = 0;
|
||||
}
|
||||
|
||||
// Update color buffer
|
||||
const colors = state.config.lightBarColors;
|
||||
const colorCount = colors ? Math.min(colors.length, 16) : 0;
|
||||
@ -277,6 +281,10 @@ export class ProjectionScreen extends SceneFeature {
|
||||
if (s.mesh.material.uniforms.u_colorCount) {
|
||||
s.mesh.material.uniforms.u_colorCount.value = colorCount;
|
||||
}
|
||||
if (s.mesh.material.uniforms.u_opacity) {
|
||||
const targetOpacity = state.blackoutMode ? 0.1 : state.screenOpacity;
|
||||
s.mesh.material.uniforms.u_opacity.value = targetOpacity;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -463,6 +471,11 @@ export function updateScreenEffect() {
|
||||
material.uniforms.u_time.value = state.clock.getElapsedTime();
|
||||
material.uniforms.u_effect_type.value = state.screenEffect.type;
|
||||
material.uniforms.u_effect_strength.value = strength;
|
||||
|
||||
if (material.uniforms.u_opacity) {
|
||||
const targetOpacity = state.blackoutMode ? 0.1 : (state.screenOpacity !== undefined ? state.screenOpacity : 0.7);
|
||||
material.uniforms.u_opacity.value = targetOpacity;
|
||||
}
|
||||
|
||||
if (progress >= 1.0) {
|
||||
material.uniforms.u_effect_type.value = 0.0;
|
||||
|
||||
@ -14,6 +14,8 @@ export class StageLasers extends SceneFeature {
|
||||
this.stateTimer = 0;
|
||||
this.initialSilenceSeconds = 10;
|
||||
this.currentCycleMode = 'RUNNING';
|
||||
this.dummyColor = new THREE.Color();
|
||||
this.areLasersVisible = true;
|
||||
sceneFeatureManager.register(this);
|
||||
}
|
||||
|
||||
@ -103,14 +105,20 @@ export class StageLasers extends SceneFeature {
|
||||
|
||||
update(deltaTime) {
|
||||
if (!state.partyStarted) {
|
||||
this.lasers.forEach(l => l.mesh.visible = false);
|
||||
if (this.areLasersVisible) {
|
||||
this.lasers.forEach(l => l.mesh.visible = false);
|
||||
this.areLasersVisible = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const time = state.clock.getElapsedTime();
|
||||
|
||||
if (!state.config.lasersEnabled) {
|
||||
this.lasers.forEach(l => l.mesh.visible = false);
|
||||
if (!state.config.lasersEnabled || state.blackoutMode) {
|
||||
if (this.areLasersVisible) {
|
||||
this.lasers.forEach(l => l.mesh.visible = false);
|
||||
this.areLasersVisible = false;
|
||||
}
|
||||
if (state.laserData) state.laserData.count = 0;
|
||||
return;
|
||||
}
|
||||
@ -156,6 +164,7 @@ export class StageLasers extends SceneFeature {
|
||||
if (this.stateTimer <= 0) {
|
||||
this.activationState = 'COOLDOWN';
|
||||
this.stateTimer = 4.0; // Cooldown duration
|
||||
isActive = false;
|
||||
}
|
||||
} else if (this.activationState === 'COOLDOWN') {
|
||||
this.stateTimer -= deltaTime;
|
||||
@ -166,10 +175,18 @@ export class StageLasers extends SceneFeature {
|
||||
}
|
||||
|
||||
if (!isActive) {
|
||||
this.lasers.forEach(l => l.mesh.visible = false);
|
||||
if (this.areLasersVisible) {
|
||||
this.lasers.forEach(l => l.mesh.visible = false);
|
||||
this.areLasersVisible = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.areLasersVisible) {
|
||||
this.lasers.forEach(l => l.mesh.visible = true);
|
||||
this.areLasersVisible = true;
|
||||
}
|
||||
|
||||
// --- Pattern Logic ---
|
||||
if (time - this.lastPatternChange > 8) { // Change every 8 seconds
|
||||
this.pattern = (this.pattern + 1) % 4;
|
||||
@ -192,8 +209,6 @@ export class StageLasers extends SceneFeature {
|
||||
}
|
||||
|
||||
this.lasers.forEach(l => {
|
||||
l.mesh.visible = !['IDLE', 'COOLDOWN'].includes(this.activationState);
|
||||
|
||||
let currentIntensity = intensity;
|
||||
let flareScale = 1.0;
|
||||
|
||||
@ -235,8 +250,8 @@ export class StageLasers extends SceneFeature {
|
||||
} 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();
|
||||
this.dummyColor.setHSL(hue, 1.0, 0.5);
|
||||
colorHex = this.dummyColor.getHex();
|
||||
}
|
||||
|
||||
l.mesh.material.color.set(colorHex);
|
||||
@ -279,7 +294,10 @@ export class StageLasers extends SceneFeature {
|
||||
}
|
||||
|
||||
onPartyEnd() {
|
||||
this.lasers.forEach(l => l.mesh.visible = false);
|
||||
if (this.areLasersVisible) {
|
||||
this.lasers.forEach(l => l.mesh.visible = false);
|
||||
this.areLasersVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -114,6 +114,13 @@ export class StageLightBars extends SceneFeature {
|
||||
update(deltaTime) {
|
||||
if (!state.partyStarted) return;
|
||||
if (!state.config.lightBarsEnabled) return;
|
||||
|
||||
if (state.blackoutMode) {
|
||||
this.bars.forEach(bar => {
|
||||
bar.material.emissiveIntensity = 0;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const time = state.clock.getElapsedTime();
|
||||
const beatIntensity = state.music ? state.music.beatIntensity : 0;
|
||||
|
||||
@ -136,7 +136,10 @@ export class StageLights extends SceneFeature {
|
||||
this.focusPoint.lerp(this.targetFocusPoint, deltaTime * 2.0);
|
||||
|
||||
// Update each light
|
||||
const intensity = state.music ? 20 + state.music.beatIntensity * 150 : 50;
|
||||
let intensity = state.music ? 20 + state.music.beatIntensity * 150 : 50;
|
||||
if (state.blackoutMode) {
|
||||
intensity = 0;
|
||||
}
|
||||
|
||||
const spread = 0.2 + (state.music ? state.music.beatIntensity * 0.4 : 0);
|
||||
const bounce = state.music ? state.music.beatIntensity * 0.5 : 0;
|
||||
|
||||
@ -104,7 +104,7 @@ export class StageTorches extends SceneFeature {
|
||||
}
|
||||
|
||||
update(deltaTime) {
|
||||
const enabled = state.config.torchesEnabled;
|
||||
const enabled = state.config.torchesEnabled && !state.blackoutMode;
|
||||
this.torches.forEach(torch => {
|
||||
if (torch.group.visible !== enabled) torch.group.visible = enabled;
|
||||
});
|
||||
|
||||
@ -14,6 +14,7 @@ export function initState() {
|
||||
laserColorMode: 'RUNNING', // 'SINGLE', 'RANDOM', 'RUNNING', 'ANY'
|
||||
lightBarColors: ['#ff00ff', '#00ffff', '#ffff00'], // Default neon colors
|
||||
guestCount: 150,
|
||||
blackout: false,
|
||||
djHat: 'None' // 'None', 'Santa', 'Top Hat'
|
||||
};
|
||||
try {
|
||||
@ -61,6 +62,7 @@ export function initState() {
|
||||
debugLight: false, // Turn on light helpers
|
||||
debugCamera: false, // Turn on camera helpers
|
||||
partyStarted: false,
|
||||
blackoutMode: false,
|
||||
|
||||
// Feature Configuration
|
||||
config: config,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user