Feature: medieval musicians

This commit is contained in:
Dejvino 2025-11-21 20:38:37 +01:00
parent a5548d8044
commit 47e11a497c
5 changed files with 149 additions and 4 deletions

View File

@ -13,12 +13,12 @@ function updateCamera() {
// Base Camera Position in front of the TV
const baseX = 0;
const baseY = 1.6;
const baseZ = 10.0;
const baseZ = -10.0;
// Base LookAt target (Center of the screen)
const baseTargetX = 0;
const baseTargetY = 1.6;
const baseTargetZ = -10.0;
const baseTargetZ = -30.0;
// Camera Position Offsets (Drift)
const camOffsetX = Math.sin(globalTime * 3.1) * camAmplitude;

View File

@ -1 +1,101 @@
// This file will contain the Three.js code for creating and animating the medieval musicians on the stage.
import * as THREE from 'three';
import { state } from '../state.js';
import { SceneFeature } from './SceneFeature.js';
import sceneFeatureManager from './SceneFeatureManager.js';
import musiciansTextureUrl from '/textures/musician1.png';
export class MedievalMusicians extends SceneFeature {
constructor() {
super();
this.musicians = [];
sceneFeatureManager.register(this);
}
init() {
// --- Stage dimensions for positioning ---
const stageHeight = 1.5;
const stageDepth = 5;
const length = 40;
// --- Billboard Properties ---
const musicianHeight = 2.5;
const musicianWidth = 2.5;
// Load the texture and create the material inside the callback
state.loader.load(musiciansTextureUrl, (texture) => {
// 1. Draw texture to canvas to process it
const image = texture.image;
const canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
const context = canvas.getContext('2d');
context.drawImage(image, 0, 0);
// 2. Get the key color from the top-left pixel
const keyPixelData = context.getImageData(0, 0, 1, 1).data;
const keyColor = { r: keyPixelData[0], g: keyPixelData[1], b: keyPixelData[2] };
// 3. Process the entire canvas to make background transparent
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const threshold = 20; // Adjust this for more/less color tolerance
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const distance = Math.sqrt(Math.pow(r - keyColor.r, 2) + Math.pow(g - keyColor.g, 2) + Math.pow(b - keyColor.b, 2));
if (distance < threshold) {
data[i + 3] = 0; // Set alpha to 0 (transparent)
}
}
context.putImageData(imageData, 0, 0);
// 4. Create a new texture from the modified canvas
const processedTexture = new THREE.CanvasTexture(canvas);
// 5. Create a standard material with the new texture
const material = new THREE.MeshStandardMaterial({
map: processedTexture,
side: THREE.DoubleSide,
transparent: true,
roughness: 0.7,
metalness: 0.1,
});
// 6. Create and position the musicians
const geometry = new THREE.PlaneGeometry(musicianWidth, musicianHeight);
const musicianPositions = [
new THREE.Vector3(-2, stageHeight + musicianHeight / 2, -length / 2 + stageDepth / 2 - 1),
new THREE.Vector3(0, stageHeight + musicianHeight / 2, -length / 2 + stageDepth / 2 - 1.5),
new THREE.Vector3(2.5, stageHeight + musicianHeight / 2, -length / 2 + stageDepth / 2 - 1.2),
];
musicianPositions.forEach(pos => {
const musician = new THREE.Mesh(geometry, material);
musician.position.copy(pos);
state.scene.add(musician);
this.musicians.push(musician);
});
});
}
update(deltaTime) {
// Billboard effect: make each musician face the camera
if (this.musicians.length > 0) {
const cameraPosition = new THREE.Vector3();
state.camera.getWorldPosition(cameraPosition);
this.musicians.forEach(musician => {
// We only want to rotate on the Y axis to keep them upright
musician.lookAt(cameraPosition.x, musician.position.y, cameraPosition.z);
});
}
}
}
new MedievalMusicians();

View File

@ -6,6 +6,8 @@ import sceneFeatureManager from './SceneFeatureManager.js';
import { RoomWalls } from './room-walls.js';
import { LightBall } from './light-ball.js';
import { Pews } from './pews.js';
import { Stage } from './stage.js';
import { MedievalMusicians } from './medieval-musicians.js';
// Scene Features ^^^
// --- Scene Modeling Function ---

View File

@ -1 +1,44 @@
// This file will contain the Three.js code for creating the stage at the front of the cathedral.
import * as THREE from 'three';
import { state } from '../state.js';
import { SceneFeature } from './SceneFeature.js';
import sceneFeatureManager from './SceneFeatureManager.js';
import woodTextureUrl from '/textures/wood.png';
export class Stage extends SceneFeature {
constructor() {
super();
sceneFeatureManager.register(this);
}
init() {
// --- Dimensions from room-walls.js for positioning ---
const length = 40;
const naveWidth = 12;
// --- Stage Properties ---
const stageWidth = naveWidth - 1; // Slightly narrower than the nave
const stageHeight = 1.5;
const stageDepth = 5;
// --- Material ---
const woodTexture = state.loader.load(woodTextureUrl);
woodTexture.wrapS = THREE.RepeatWrapping;
woodTexture.wrapT = THREE.RepeatWrapping;
woodTexture.repeat.set(stageWidth / 2, stageDepth / 2);
const woodMaterial = new THREE.MeshStandardMaterial({
map: woodTexture,
roughness: 0.8,
metalness: 0.1,
});
// --- Create Stage Mesh ---
const stageGeo = new THREE.BoxGeometry(stageWidth, stageHeight, stageDepth);
const stageMesh = new THREE.Mesh(stageGeo, woodMaterial);
stageMesh.castShadow = true;
stageMesh.receiveShadow = true;
stageMesh.position.set(0, stageHeight / 2, -length / 2 + stageDepth / 2);
state.scene.add(stageMesh);
}
}
new Stage();

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 KiB