119 lines
4.4 KiB
JavaScript
119 lines
4.4 KiB
JavaScript
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);
|
|
|
|
// 1.5. Cauldron Handles
|
|
const handleRadius = 0.08;
|
|
const handleTube = 0.01;
|
|
const handleMaterial = new THREE.MeshPhongMaterial({ color: 0x333333, shininess: 50 });
|
|
|
|
const handleGeo = new THREE.TorusGeometry(handleRadius, handleTube, 8, 32);
|
|
|
|
const handleShiftX = 0.07;
|
|
const handleShiftY = 0.12;
|
|
|
|
const leftHandle = new THREE.Mesh(handleGeo, handleMaterial);
|
|
leftHandle.rotation.y = Math.PI / 2;
|
|
leftHandle.position.set(-cauldronRadius - handleRadius * 0.5 + handleShiftX, cauldronHeight * 0.5 - handleShiftY, 0);
|
|
leftHandle.castShadow = true;
|
|
cauldronGroup.add(leftHandle);
|
|
|
|
const rightHandle = new THREE.Mesh(handleGeo, handleMaterial);
|
|
rightHandle.rotation.y = -Math.PI / 2;
|
|
rightHandle.position.set(cauldronRadius + handleRadius * 0.5 - handleShiftX, cauldronHeight * 0.5 - handleShiftY, 0);
|
|
rightHandle.castShadow = true;
|
|
cauldronGroup.add(rightHandle);
|
|
|
|
// 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;
|
|
} |