Feature: torches on stage

This commit is contained in:
Dejvino 2025-11-21 23:44:02 +01:00
parent c944fb0f4d
commit cdd90a4c57
3 changed files with 118 additions and 0 deletions

View File

@ -9,6 +9,7 @@ import { Pews } from './pews.js';
import { Stage } from './stage.js';
import { MedievalMusicians } from './medieval-musicians.js';
import { PartyGuests } from './party-guests.js';
import { StageTorches } from './stage-torches.js';
// Scene Features ^^^
// --- Scene Modeling Function ---

View File

@ -0,0 +1,117 @@
import * as THREE from 'three';
import { state } from '../state.js';
import { SceneFeature } from './SceneFeature.js';
import sceneFeatureManager from './SceneFeatureManager.js';
import sparkTextureUrl from '/textures/spark.png';
export class StageTorches extends SceneFeature {
constructor() {
super();
this.torches = [];
sceneFeatureManager.register(this);
}
init() {
// --- Stage Dimensions for positioning ---
const length = 40;
const naveWidth = 12;
const stageWidth = naveWidth - 1;
const stageHeight = 1.5;
const stageDepth = 5;
const torchPositions = [
new THREE.Vector3(-stageWidth / 2, stageHeight, -length / 2 + 0.5),
new THREE.Vector3(stageWidth / 2, stageHeight, -length / 2 + 0.5),
new THREE.Vector3(-stageWidth / 2, stageHeight, -length / 2 + stageDepth - 0.5),
new THREE.Vector3(stageWidth / 2, stageHeight, -length / 2 + stageDepth - 0.5),
];
torchPositions.forEach(pos => {
const torch = this.createTorch(pos);
this.torches.push(torch);
state.scene.add(torch.group);
});
}
createTorch(position) {
const torchGroup = new THREE.Group();
torchGroup.position.copy(position);
// --- Torch Holder ---
const holderMaterial = new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.6, metalness: 0.5 });
const holderGeo = new THREE.CylinderGeometry(0.1, 0.15, 1.0, 12);
const holderMesh = new THREE.Mesh(holderGeo, holderMaterial);
holderMesh.position.y = 0.5;
holderMesh.castShadow = true;
holderMesh.receiveShadow = true;
torchGroup.add(holderMesh);
// --- Point Light ---
const pointLight = new THREE.PointLight(0xffaa44, 2.5, 8);
pointLight.position.y = 1.2;
pointLight.castShadow = true;
pointLight.shadow.mapSize.width = 128;
pointLight.shadow.mapSize.height = 128;
torchGroup.add(pointLight);
// --- Particle System for Fire ---
const particleCount = 50;
const particles = new THREE.BufferGeometry();
const positions = [];
const particleData = [];
const sparkTexture = state.loader.load(sparkTextureUrl);
const particleMaterial = new THREE.PointsMaterial({
map: sparkTexture,
color: 0xffaa00,
size: 0.5,
blending: THREE.AdditiveBlending,
transparent: true,
depthWrite: false,
});
for (let i = 0; i < particleCount; i++) {
positions.push(0, 1, 0);
particleData.push({
velocity: new THREE.Vector3((Math.random() - 0.5) * 0.2, Math.random() * 1.5, (Math.random() - 0.5) * 0.2),
life: Math.random() * 1.0,
});
}
particles.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
const particleSystem = new THREE.Points(particles, particleMaterial);
torchGroup.add(particleSystem);
return { group: torchGroup, light: pointLight, particles: particleSystem, particleData: particleData };
}
update(deltaTime) {
this.torches.forEach(torch => {
// --- Animate Particles ---
const positions = torch.particles.geometry.attributes.position.array;
for (let i = 0; i < torch.particleData.length; i++) {
const data = torch.particleData[i];
data.life -= deltaTime;
if (data.life <= 0) {
// Reset particle
positions[i * 3] = 0;
positions[i * 3 + 1] = 1;
positions[i * 3 + 2] = 0;
data.life = Math.random() * 1.0;
} else {
// Update position
positions[i * 3] += data.velocity.x * deltaTime;
positions[i * 3 + 1] += data.velocity.y * deltaTime;
positions[i * 3 + 2] += data.velocity.z * deltaTime;
}
}
torch.particles.geometry.attributes.position.needsUpdate = true;
// --- Flicker Light ---
const flicker = Math.random() * 0.5;
torch.light.intensity = 2.0 + flicker;
});
}
}
new StageTorches();

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B