Feature: bubling cauldron

This commit is contained in:
Dejvino 2025-11-19 23:43:52 +01:00
parent a98c058f3d
commit 499fc006f5
3 changed files with 103 additions and 0 deletions

View File

@ -3,6 +3,7 @@ import { updateDoor } from '../scene/door.js';
import { updateVcrDisplay } from '../scene/vcr-display.js'; import { updateVcrDisplay } from '../scene/vcr-display.js';
import { state } from '../state.js'; import { state } from '../state.js';
import { updateScreenEffect } from '../scene/magic-mirror.js' import { updateScreenEffect } from '../scene/magic-mirror.js'
import { updateCauldron } from '../scene/cauldron.js';
import { updateFire } from '../scene/fireplace.js'; import { updateFire } from '../scene/fireplace.js';
function updateCamera() { function updateCamera() {
@ -174,6 +175,7 @@ export function animate() {
// updatePictureFrame(); // updatePictureFrame();
updateScreenEffect(); updateScreenEffect();
updateFire(); updateFire();
updateCauldron();
// RENDER! // RENDER!
state.renderer.render(state.scene, state.camera); state.renderer.render(state.scene, state.camera);

View File

@ -0,0 +1,97 @@
import * as THREE from 'three';
import { state } from '../state.js';
let cauldronParticles;
let cauldronLight;
const particleCount = 8;
const particleVelocities = [];
export function createCauldron(x, y, z) {
const cauldronGroup = new THREE.Group();
const cauldronRadius = 0.2;
const cauldronHeight = 0.25;
// 1. Cauldron Body
const cauldronMaterial = new THREE.MeshPhongMaterial({ color: 0x111111, shininess: 80 });
// Use a sphere geometry cut in half for the bowl shape
const cauldronGeo = new THREE.SphereGeometry(cauldronRadius, 32, 16, 0, Math.PI * 2, 0, Math.PI / 2);
const cauldronMesh = new THREE.Mesh(cauldronGeo, cauldronMaterial);
cauldronMesh.castShadow = true;
cauldronMesh.rotation.z = Math.PI;
cauldronGroup.add(cauldronMesh);
// 2. Glowing Liquid Surface
const liquidColor = 0x00ff00; // Bright green
const liquidMaterial = new THREE.MeshPhongMaterial({
color: liquidColor,
emissive: liquidColor,
emissiveIntensity: 0.6,
shininess: 100
});
const liquidGeo = new THREE.CircleGeometry(cauldronRadius * 0.95, 32);
const liquidSurface = new THREE.Mesh(liquidGeo, liquidMaterial);
liquidSurface.rotation.x = -Math.PI / 2;
liquidSurface.position.y = -0.01; // Slightly below the rim
cauldronGroup.add(liquidSurface);
// 3. Bubbling Particles
const particleGeo = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
positions[i * 3] = (Math.random() - 0.5) * cauldronRadius * 1.5;
positions[i * 3 + 1] = (Math.random() - 0.5) * 0.05; // Start near the surface
positions[i * 3 + 2] = (Math.random() - 0.5) * cauldronRadius * 1.5;
particleVelocities.push((0.05 + Math.random() * 0.1) / 60); // Random upward velocity
}
particleGeo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const particleMaterial = new THREE.PointsMaterial({
color: liquidColor,
size: 0.015,
transparent: true,
blending: THREE.AdditiveBlending,
depthWrite: false,
opacity: 0.7
});
cauldronParticles = new THREE.Points(particleGeo, particleMaterial);
cauldronGroup.add(cauldronParticles);
// 4. Light from the cauldron
cauldronLight = new THREE.PointLight(liquidColor, 0.8, 3);
cauldronLight.position.y = 0.2;
cauldronLight.castShadow = true;
cauldronGroup.add(cauldronLight);
// Position and add to scene
cauldronGroup.position.set(x, y, z);
state.scene.add(cauldronGroup);
}
export function updateCauldron() {
if (!cauldronParticles || !cauldronLight) return;
// Animate Bubbles
const positions = cauldronParticles.geometry.attributes.position.array;
const bubbleMaxHeight = 0.1;
const overfly = Math.random() * bubbleMaxHeight;
for (let i = 0; i < particleCount; i++) {
positions[i * 3 + 1] += particleVelocities[i]; // Move bubble up
// Reset bubble if it goes too high
if (positions[i * 3 + 1] > bubbleMaxHeight + overfly) {
positions[i * 3 + 1] = (Math.random() - 0.5) * 0.05;
// Give it a new random X/Z position
positions[i * 3] = (Math.random() - 0.5) * 0.2 * 1.5;
positions[i * 3 + 2] = (Math.random() - 0.5) * 0.2 * 1.5;
}
}
cauldronParticles.geometry.attributes.position.needsUpdate = true;
// Flicker Light
const flicker = Math.random() * 0.02;
cauldronLight.intensity = 0.1 + flicker;
}

View File

@ -5,6 +5,7 @@ import { createBookshelf } from './bookshelf.js';
import { createMagicMirror } from './magic-mirror.js'; import { createMagicMirror } from './magic-mirror.js';
import { createFireplace } from './fireplace.js'; import { createFireplace } from './fireplace.js';
import { createTable } from './table.js'; import { createTable } from './table.js';
import { createCauldron } from './cauldron.js';
import { PictureFrame } from './PictureFrame.js'; import { PictureFrame } from './PictureFrame.js';
import painting1 from '/textures/painting1.jpg'; import painting1 from '/textures/painting1.jpg';
import painting2 from '/textures/painting2.jpg'; import painting2 from '/textures/painting2.jpg';
@ -78,6 +79,9 @@ export function createSceneObjects() {
createTable(-1.8, 0, -0.8, Math.PI / 2.3); createTable(-1.8, 0, -0.8, Math.PI / 2.3);
// Add cauldron on top of the table (Y = table height + cauldron radius)
createCauldron(-1.8, 0.5 + 0.2, -0.8);
// --- 8. Timber Frames --- // --- 8. Timber Frames ---
const beamThickness = 0.15; const beamThickness = 0.15;
const beamDepth = 0.2; const beamDepth = 0.2;