Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions src/ExplosionSim.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ export class ExplosionSim {
// 3-step cel-shading quantization
float stepped = floor(NdotL * 3.0 + 0.5) / 3.0;

// Height-based + global temperature color ramp
float h = clamp(vWorldPos.y / 12.0, 0.0, 1.0);
// Height-based + global temperature color ramp (world Y 0..40)
float h = clamp(vWorldPos.y / 38.0, 0.0, 1.0);
float t = clamp(uGlobalTemp * (0.5 + 0.5 * h), 0.0, 1.0);

// Smooth color transitions
Expand Down Expand Up @@ -96,9 +96,10 @@ export class ExplosionSim {
});

this.mc = new MarchingCubes(this.N, this.celMat, false, false, 200000);
this.mc.isolation = 0.12;
this.mc.scale.set(12, 12, 12);
this.mc.position.set(0, 0, 0);
this.mc.isolation = 0.14;
// Non-square: 16 wide × 40 tall — world Y goes 0 → 40 (grid bottom = ground)
this.mc.scale.set(8, 20, 8);
this.mc.position.set(0, 20, 0);
this.scene.add(this.mc);
}

Expand Down Expand Up @@ -165,16 +166,16 @@ export class ExplosionSim {
_updateTimeline(t) {
const s = this.solver, N = this.N;

// Detonation — inject massive initial energy
// Detonation — narrow initial fireball near ground
if (!this.detonated && t > 0.05) {
this.detonated = true;
s.injectSphere(N / 2, N * 0.12, N / 2, N * 0.15, 80, 50);
s.injectSphere(N / 2, N * 0.08, N / 2, N * 0.09, 80, 40);
}

// Sustained fireball energy
if (t > 0.1 && t < 3.0) {
const strength = (1 - remap(t, 0.1, 3.0)) * 20;
s.injectSphere(N / 2, N * 0.12, N / 2, N * 0.08, strength, strength * 0.5);
// Sustained narrow stem injection at base
if (t > 0.1 && t < 4.5) {
const strength = (1 - remap(t, 0.1, 4.5)) * 22;
s.injectSphere(N / 2, N * 0.07, N / 2, N * 0.05, strength, strength * 0.4);
}

// Nachlauf-Stem (Mushroom stem injection)
Expand All @@ -190,11 +191,14 @@ export class ExplosionSim {
s.vortexStrength = easeOutCubic(vp) * 8.0;
s.vortexRadius = N * (0.1 + easeOutCubic(vp) * 0.15);

s.turbulenceStrength = 1.0 + 2.0 * clamp01(remap(t, 0.5, 3.0));
// Entrainment: inward pull creates narrow stem below the cap
s.entrainStrength = t > 1.0 ? Math.min(3.0, (t - 1.0) * 1.2) : 0;

s.turbulenceStrength = 0.8 + 1.5 * clamp01(remap(t, 0.5, 3.0));
s.cooling = 0.998 - remap(t, 5, 20) * 0.008;

// Global temperature for coloring
this.globalTemp = 1.0 - remap(t, 1.0, 12.0) * 0.85;
this.globalTemp = 1.0 - remap(t, 1.0, 14.0) * 0.86;
this.celMat.uniforms.uGlobalTemp.value = this.globalTemp;
}

Expand Down
19 changes: 18 additions & 1 deletion src/FluidSolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export class FluidSolver {
this.turbulenceTime = 0;
this.jacobiIters = 15;
this.cooling = 0.995; // temperature decay per step
this.entrainStrength = 0; // inward pull to form narrow stem
}

step() {
Expand Down Expand Up @@ -148,7 +149,13 @@ export class FluidSolver {
// 5. Cool temperature
for (let i = 0; i < N*N*N; i++) {
g.temp[i] *= this.cooling;
// No density dissipation — let advection+boundaries handle mass loss naturally
}

// 6. Open top: let gas flow out instead of being killed at the ceiling
for (let k = 0; k < N; k++)
for (let i = 0; i < N; i++) {
g.density[g.idx(i, N-1, k)] = g.density[g.idx(i, N-2, k)] * 0.90;
g.temp[g.idx(i, N-1, k)] = g.temp[g.idx(i, N-2, k)] * 0.90;
}
}

Expand Down Expand Up @@ -190,6 +197,16 @@ export class FluidSolver {
vz[idx] += vPol_r * nz * dt;
}

// --- Entrainment: inward pull at low altitude → narrow stem ---
if (this.entrainStrength > 0 && j < N * 0.50) {
const edx = i - cx, edz = k - cz;
const er = Math.sqrt(edx * edx + edz * edz) + 0.001;
const altFrac = j / (N * 0.50);
const inward = this.entrainStrength * Math.max(0, 1 - altFrac);
vx[idx] -= (edx / er) * inward * dt;
vz[idx] -= (edz / er) * inward * dt;
}

// --- Curl-Noise Turbulence (Kolmogorov micro-scale) ---
if (this.turbulenceStrength > 0) {
const s = 0.15, t2 = this.turbulenceTime;
Expand Down
20 changes: 10 additions & 10 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ renderer.setClearColor(0x0a0a0f);

// ---- Scene ----
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x0a0a0f, 0.005);
scene.fog = new THREE.FogExp2(0x0a0a0f, 0.003);

// ---- Camera ----
const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 500);
camera.position.set(18, 8, 24);
camera.lookAt(0, 5, 0);
const camera = new THREE.PerspectiveCamera(48, window.innerWidth / window.innerHeight, 0.1, 600);
camera.position.set(22, 10, 32);
camera.lookAt(0, 10, 0);

// ---- Post-processing (Sobel outlines + bloom) ----
let postProcess = null;
Expand All @@ -44,8 +44,8 @@ restartBtn.addEventListener('click', () => explosion.reset());
const clock = new THREE.Clock();
let frameCount = 0, fpsTime = 0;

const camBase = new THREE.Vector3(18, 8, 24);
const camLook = new THREE.Vector3(0, 5, 0);
const camBase = new THREE.Vector3(22, 10, 32);
const camLook = new THREE.Vector3(0, 10, 0);

// ---- Flash overlay (DOM-based for compatibility) ----
const flashEl = document.createElement('div');
Expand All @@ -58,12 +58,12 @@ function animate() {
explosion.update(dt);
const t = explosion.time;

// Camera: slow pull-back + rise with the cloud
const pull = 1 + Math.min(t * 0.012, 0.2);
// Camera: slow pull-back + rise tracking the mushroom cloud
const pull = 1 + Math.min(t * 0.010, 0.18);
camera.position.x = camBase.x * pull;
camera.position.y = camBase.y + Math.min(t * 0.25, 4);
camera.position.y = camBase.y + Math.min(t * 0.30, 8);
camera.position.z = camBase.z * pull;
camLook.y = 5 + Math.min(t * 0.35, 5);
camLook.y = 10 + Math.min(t * 0.90, 18); // follows cap to world-y ≈ 28
camera.lookAt(camLook);

// Flash overlay
Expand Down