Feature: stage light bars
This commit is contained in:
parent
edbed229b3
commit
7296715a5e
@ -123,6 +123,68 @@ export class ConfigUI extends SceneFeature {
|
||||
// Gameboy Toggle
|
||||
createToggle('Gameboy', 'gameboyEnabled');
|
||||
|
||||
// --- Light Bar Colors ---
|
||||
const colorContainer = document.createElement('div');
|
||||
Object.assign(colorContainer.style, {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '5px',
|
||||
marginTop: '5px',
|
||||
paddingTop: '5px',
|
||||
borderTop: '1px solid #444'
|
||||
});
|
||||
|
||||
const colorLabel = document.createElement('label');
|
||||
colorLabel.innerText = 'Stage Light Bars';
|
||||
colorContainer.appendChild(colorLabel);
|
||||
|
||||
const colorControls = document.createElement('div');
|
||||
colorControls.style.display = 'flex';
|
||||
colorControls.style.gap = '5px';
|
||||
|
||||
const colorPicker = document.createElement('input');
|
||||
colorPicker.type = 'color';
|
||||
colorPicker.value = '#ff00ff';
|
||||
colorPicker.style.width = '40px';
|
||||
colorPicker.style.border = 'none';
|
||||
colorPicker.style.cursor = 'pointer';
|
||||
|
||||
const addColorBtn = document.createElement('button');
|
||||
addColorBtn.innerText = '+';
|
||||
addColorBtn.style.cursor = 'pointer';
|
||||
addColorBtn.onclick = () => {
|
||||
state.config.lightBarColors.push(colorPicker.value);
|
||||
saveConfig();
|
||||
updateColorList();
|
||||
const lightBars = sceneFeatureManager.features.find(f => f.constructor.name === 'StageLightBars');
|
||||
if (lightBars) lightBars.refreshColors();
|
||||
};
|
||||
|
||||
const clearColorsBtn = document.createElement('button');
|
||||
clearColorsBtn.innerText = 'Clear';
|
||||
clearColorsBtn.style.cursor = 'pointer';
|
||||
clearColorsBtn.onclick = () => {
|
||||
state.config.lightBarColors = [];
|
||||
saveConfig();
|
||||
updateColorList();
|
||||
const lightBars = sceneFeatureManager.features.find(f => f.constructor.name === 'StageLightBars');
|
||||
if (lightBars) lightBars.refreshColors();
|
||||
};
|
||||
|
||||
colorControls.appendChild(colorPicker);
|
||||
colorControls.appendChild(addColorBtn);
|
||||
colorControls.appendChild(clearColorsBtn);
|
||||
colorContainer.appendChild(colorControls);
|
||||
|
||||
const colorList = document.createElement('div');
|
||||
colorList.style.display = 'flex';
|
||||
colorList.style.flexWrap = 'wrap';
|
||||
colorList.style.gap = '4px';
|
||||
colorList.style.marginTop = '4px';
|
||||
this.colorList = colorList;
|
||||
colorContainer.appendChild(colorList);
|
||||
rightContainer.appendChild(colorContainer);
|
||||
|
||||
// Guest Count Input
|
||||
const guestRow = document.createElement('div');
|
||||
guestRow.style.display = 'flex';
|
||||
@ -378,6 +440,7 @@ export class ConfigUI extends SceneFeature {
|
||||
document.body.appendChild(leftContainer);
|
||||
document.body.appendChild(rightContainer);
|
||||
this.updateStatus();
|
||||
this.updateColorList();
|
||||
|
||||
// Restore poster
|
||||
MediaStorage.getPoster().then(file => {
|
||||
@ -388,6 +451,22 @@ export class ConfigUI extends SceneFeature {
|
||||
});
|
||||
}
|
||||
|
||||
updateColorList() {
|
||||
if (!this.colorList) return;
|
||||
this.colorList.innerHTML = '';
|
||||
state.config.lightBarColors.forEach(color => {
|
||||
const swatch = document.createElement('div');
|
||||
Object.assign(swatch.style, {
|
||||
width: '15px',
|
||||
height: '15px',
|
||||
backgroundColor: color,
|
||||
border: '1px solid #fff',
|
||||
borderRadius: '2px'
|
||||
});
|
||||
this.colorList.appendChild(swatch);
|
||||
});
|
||||
}
|
||||
|
||||
updateStatus() {
|
||||
if (!this.songLabel) return;
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ import { DJ } from './dj.js';
|
||||
import { ProjectionScreen } from './projection-screen.js';
|
||||
import { StageLasers } from './stage-lasers.js';
|
||||
import { ConfigUI } from './config-ui.js';
|
||||
import { StageLightBars } from './stage-light-bars.js';
|
||||
// Scene Features ^^^
|
||||
|
||||
// --- Scene Modeling Function ---
|
||||
|
||||
109
party-stage/src/scene/stage-light-bars.js
Normal file
109
party-stage/src/scene/stage-light-bars.js
Normal file
@ -0,0 +1,109 @@
|
||||
import * as THREE from 'three';
|
||||
import { state } from '../state.js';
|
||||
import { SceneFeature } from './SceneFeature.js';
|
||||
import sceneFeatureManager from './SceneFeatureManager.js';
|
||||
|
||||
export class StageLightBars extends SceneFeature {
|
||||
constructor() {
|
||||
super();
|
||||
this.bars = [];
|
||||
sceneFeatureManager.register(this);
|
||||
}
|
||||
|
||||
init() {
|
||||
// Shared Geometry/Material setup
|
||||
// We use individual materials to allow different colors per bar if needed,
|
||||
// or we can update them dynamically.
|
||||
|
||||
// 1. Stage Front Edge Bar
|
||||
// Stage is approx width 11, height 1.5, centered at z=-17.5, depth 5.
|
||||
// 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)
|
||||
// Left Front
|
||||
this.createBar(new THREE.Vector3(-5.45, 0.7, -14.8), new THREE.Vector3(0.1, 1.5, 0.1));
|
||||
// Right Front
|
||||
this.createBar(new THREE.Vector3(5.45, 0.7, -14.8), new THREE.Vector3(0.1, 1.5, 0.1));
|
||||
|
||||
// 3. Overhead Beam Bars
|
||||
// Beam is at y=9, z=-14, length 24.
|
||||
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)
|
||||
this.createBar(new THREE.Vector3(-11.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.applyColors();
|
||||
}
|
||||
|
||||
createBar(position, size) {
|
||||
const geometry = new THREE.BoxGeometry(size.x, size.y, size.z);
|
||||
const material = new THREE.MeshStandardMaterial({
|
||||
color: 0xffffff,
|
||||
emissive: 0xffffff,
|
||||
emissiveIntensity: 1.0,
|
||||
roughness: 0.4,
|
||||
metalness: 0.8
|
||||
});
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
mesh.position.copy(position);
|
||||
mesh.castShadow = true; // Maybe cast shadow?
|
||||
state.scene.add(mesh);
|
||||
|
||||
this.bars.push({ mesh, material });
|
||||
}
|
||||
|
||||
applyColors() {
|
||||
const colors = state.config.lightBarColors;
|
||||
if (!colors || colors.length === 0) {
|
||||
// Default off or white if list is empty
|
||||
this.bars.forEach(bar => {
|
||||
bar.material.color.setHex(0x111111);
|
||||
bar.material.emissive.setHex(0x000000);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.bars.forEach((bar, index) => {
|
||||
const colorHex = colors[index % colors.length];
|
||||
const color = new THREE.Color(colorHex);
|
||||
bar.material.color.copy(color);
|
||||
bar.material.emissive.copy(color);
|
||||
});
|
||||
}
|
||||
|
||||
update(deltaTime) {
|
||||
// Check if colors changed (simple length check or reference check could work,
|
||||
// but here we can just re-apply if needed or rely on ConfigUI calling a refresh.
|
||||
// For simplicity, we'll assume ConfigUI updates state and we might poll or be notified.
|
||||
// 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.
|
||||
|
||||
if (!state.partyStarted) return;
|
||||
|
||||
const time = state.clock.getElapsedTime();
|
||||
let beatIntensity = 0;
|
||||
if (state.music) {
|
||||
beatIntensity = state.music.beatIntensity;
|
||||
}
|
||||
|
||||
// Pulsate
|
||||
const baseIntensity = 0.1;
|
||||
const pulse = Math.sin(time * 2.0) * 0.3 + 0.3; // Breathing
|
||||
const beatFlash = beatIntensity * 2.0; // Sharp flash on beat
|
||||
|
||||
const totalIntensity = baseIntensity + pulse + beatFlash;
|
||||
|
||||
this.bars.forEach(bar => {
|
||||
bar.material.emissiveIntensity = totalIntensity;
|
||||
});
|
||||
}
|
||||
|
||||
refreshColors() {
|
||||
this.applyColors();
|
||||
}
|
||||
}
|
||||
|
||||
new StageLightBars();
|
||||
@ -10,6 +10,7 @@ export function initState() {
|
||||
consoleRGBEnabled: true,
|
||||
consoleEnabled: true,
|
||||
gameboyEnabled: false,
|
||||
lightBarColors: ['#ff00ff', '#00ffff', '#ffff00'], // Default neon colors
|
||||
guestCount: 150,
|
||||
djHat: 'None' // 'None', 'Santa', 'Top Hat'
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user