Source Code
src/pages/projects/proc_gen.rs
use leptos::*;
use leptos_meta::{Meta, Title};
use super::data::ALL_PROJECTS;
#[component]
pub fn ProcGen() -> impl IntoView {
let project = ALL_PROJECTS
.iter()
.find(|p| p.slug == "proc-gen")
.unwrap();
let skills_view = project
.skills
.iter()
.map(|&s| view! { <li>{s}</li> })
.collect::<Vec<_>>();
view! {
<Title text="Procedural Generation – Peter Pinto"/>
<Meta name="description" content="A planet-scale procedural generation engine using the H3 hexagonal grid, adaptive LOD refinement, and a trait-based deterministic simulation system built on Bevy."/>
<div class="page">
<span class="eyebrow">"Projects"</span>
<h1>"Procedural " <em style="font-style:italic; color: var(--accent)">"Generation"</em></h1>
<p class="lead">
"A planet-scale hexagonal grid engine with adaptive level-of-detail refinement,
Perlin noise terrain, and a trait-based simulation system — designed for
deterministic, networked ecosystem simulation."
</p>
<ul class="skills-list" style="margin-top: 1.5rem;">
{skills_view}
</ul>
<hr class="divider"/>
// ── H3 Grid ───────────────────────────────────────────
<section class="project-section">
<span class="eyebrow">"Foundation"</span>
<h2>"H3 Hexagonal Grid"</h2>
<p>
"The planet surface is partitioned using "
<a href="https://h3geo.org" target="_blank" rel="noopener noreferrer" class="prose-link">"Uber's H3"</a>
" hierarchical hexagonal grid system. Unlike square or triangular grids,
H3's hexagons have uniform neighbour distances and no pole singularities —
every cell on the sphere has the same geometric relationship to its neighbours.
The hierarchy runs from resolution 0 (122 base cells covering the globe) up
to resolution 15 (sub-metre cells), where each hex subdivides into 7 children."
</p>
</section>
<hr class="divider"/>
// ── Adaptive LOD ──────────────────────────────────────
<section class="project-section">
<span class="eyebrow">"Rendering"</span>
<h2>"Adaptive LOD Refinement"</h2>
<p>
"Cell resolution is managed dynamically based on camera proximity. Cells
near the camera are in "
<code class="inline-code">"Refining"</code>
" state — they spawn their 7 children and despawn themselves. Cells the
camera moves away from enter "
<code class="inline-code">"Coarsening"</code>
" state — their parent is respawned and they are removed. The transition
is driven by Bevy ECS systems on a custom "
<code class="inline-code">"SimulationUpdate"</code>
" schedule that runs independently of the render loop."
</p>
<p style="margin-top: 1rem;">
"The result is a globe that renders only as much geometry as the current
viewpoint demands — a low-resolution wireframe from orbit that resolves
to fine-grained detail on the ground without ever loading the full grid
into memory."
</p>
</section>
<hr class="divider"/>
// ── Terrain ───────────────────────────────────────────
<section class="project-section">
<span class="eyebrow">"World Generation"</span>
<h2>"Terrain"</h2>
<p>
"Terrain height is sampled using multi-octave Perlin noise from the "
<code class="inline-code">"noise"</code>
" crate. Each H3 cell's geographic centroid is converted to lat/lng
coordinates, which are fed into the noise function to produce a stable,
deterministic height value. Higher octaves add fine surface detail without
altering coarse topology — this means the terrain is consistent regardless
of which resolution level samples it."
</p>
</section>
<hr class="divider"/>
// ── Simulation ────────────────────────────────────────
<section class="project-section">
<span class="eyebrow">"Simulation"</span>
<h2>"Trait-Based Simulation System"</h2>
<p>
"Simulation logic is plugged in via a "
<code class="inline-code">"Simulation"</code>
" trait. Each implementation receives a cell's current state and its
neighbours' states and returns an updated state. This keeps the simulation
logic completely decoupled from the grid management code — swapping in a
different ecosystem model is a one-line change."
</p>
<p style="margin-top: 1rem;">
"When the LOD changes, simulation data is preserved through an
aggregate/subdivide pattern: child cells inherit subdivided values from
their parent, and when cells coarsen, children aggregate back up to
produce a consistent parent state. No simulation history is lost across
resolution transitions."
</p>
</section>
<hr class="divider"/>
// ── Determinism ───────────────────────────────────────
<section class="project-section">
<span class="eyebrow">"Design"</span>
<h2>"Determinism"</h2>
<p>
"Every system that touches simulation state is audited for determinism.
HashMaps have been replaced with BTreeMaps where iteration order matters.
Bevy query ordering is explicitly controlled via system ordering constraints.
A "
<code class="inline-code">"DETERMINISM_ANALYSIS.md"</code>
" tracks every known nondeterministic pattern and its resolution status —
floating-point variance, query iteration order, and hash randomisation
are each addressed individually."
</p>
</section>
</div>
}
}