islaApocalypse/Core/Scripts/MATH_MARCHING_CUBES.md
beezm d72ffc0183 docs: bring all READMEs current with actual code (post-sweep-10 truth pass)
Every README was read first, checked against the code in its directory, then
rewritten to describe what the code actually does now.

Corrected throughout: map size is config-driven (8192 default) not a fixed
4096; chunks are 24x24x256 not 32x32; road carving is implemented for all four
tiers, not a 'next step'; Data/ and Resources/ are empty, not populated.

Also fixed MATH_MARCHING_CUBES.md, which documented the density sign convention
exactly backwards — it claimed positive was underground, where the code treats
positive as above the surface and counts a corner inside when density < iso.
Getting that backwards inverts every normal, a bug this project has hit before.

The root README now carries accurate run steps: F5 runs the map generator (not
the game), F6 on Scenes/Main.tscn runs the 3D world, and ChunkRadius is a load
radius that should be dropped to 4-8 while iterating.

Docs only. No code changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 05:23:58 -04:00

3.5 KiB
Raw Blame History

📐 The Math of Marching Cubes (Smooth Voxels)

Overview

In a standard blocky game like Minecraft (Greedy Meshing), if a coordinate has a block, you draw a square. If it doesn't, you draw nothing.

In a smooth voxel game like 7 Days to Die, we use an algorithm called Marching Cubes. Instead of looking at a single block, the algorithm looks at the 8 corners of an invisible 3D grid cell (a cube) and asks: "Which of these 8 corners are underground, and which are in the air?"

Based on the answer, it draws a specific set of triangles through the invisible cube to create a smooth surface.


🔑 Key Concepts

1. Density (The "Float" Value)

In smooth voxels, a point in space isn't just "Solid" (1) or "Empty" (0). It has a continuous Density value.

Mind the sign — this project's convention is the opposite of many tutorials. Density here is essentially "how far above the surface am I?", computed as (y surfaceY) normalised by the local slope. So it counts upward, not downward:

  • 1.0 = High in the sky (Empty Space).
  • 0.5 = Slightly above ground (Air).
  • 0.0 = The exact surface of the ground.
  • -0.5 = Slightly underground (Dirt).
  • -1.0 = Deep underground (Solid Rock).

2. The Iso-Level (The Threshold)

The Iso-Level is the exact density value where the "skin" of the world is drawn. Here it is 0.0 (Constants.ISO_LEVEL).

  • If a corner's density is less than 0.0, it is "Inside" the terrain.
  • If a corner's density is greater than 0.0, it is "Outside" in the air.

This matches the code: MarchingCubes.GenerateMesh sets a corner's bit when cornerDensities[i] < isolevel. Getting this backwards inverts every surface normal and makes the world render inside-out — which is a real bug this project has hit before.

3. The Triangulation Table (The Magic Array)

A cube has 8 corners. Each corner can either be Inside or Outside. That means there are 2^8 = 256 possible combinations.

Instead of writing 256 if/else statements, the algorithm uses a hardcoded Lookup Table (an array of integers).

  • Example: If corner 0 is inside, but corners 1-7 are outside, the table instantly says: "Draw a single small triangle slicing off corner 0."
  • Example: If corners 0, 1, 2, and 3 are inside (the whole bottom half), the table says: "Draw a flat quad (two triangles) straight across the middle of the cube."

4. Linear Interpolation (Why it looks Smooth)

If the algorithm just drew triangles exactly halfway between the inside and outside corners, the terrain would still look a bit jagged (like a low-poly PS1 game).

To make it perfectly smooth, we Interpolate:

  • Corner A has a density of 1.0 (Very solid).
  • Corner B has a density of -0.1 (Just barely in the air).
  • Because -0.1 is much closer to our 0.0 Iso-Level than 1.0 is, the algorithm slides the triangle vertex much closer to Corner B. This creates gentle slopes and sharp cliffs dynamically.

⚙️ The Execution Loop (What our C# Script will do)

When the Server tells the Client to render a 16x16x64 chunk, the mesher does this:

  1. Loops through every X, Y, Z coordinate in the chunk.
  2. Checks the 8 corners of the current voxel.
  3. Creates an 8-bit integer (a byte) based on which corners are solid (e.g., 00001111).
  4. Plugs that byte into the Triangulation Table.
  5. The table spits out a list of edges.
  6. The script calculates the exact interpolated point on those edges.
  7. It connects those points into triangles and adds them to Godot's ArrayMesh.
  8. It Marches to the next cube and repeats.