islaApocalypse/Core/Scripts/Constants.cs
beezm f9ea2da3f4 roads: enable Rugged+Trail carving + per-tier character (D-022)
All four road tiers now carve into the 3D world. Rugged and Trail were
generated, exported and parsed all along, but nothing ever consumed them —
which is why county roads visible on the 2D map did not exist in 3D.

Tier identity is now carried into the carve via a RoadSegment struct, so each
tier gets its own width, shoulder and grade-smoothing from one tunable block in
Constants: highways widest and holding a grade, trails narrow and hugging the
land. Rugged and Trail surface as dirt rather than asphalt.

Because tiers now have different reach, nearest-by-distance was no longer a
sound way to pick the governing road — a nearby footpath could shadow a highway
whose shoulder still covered the column. The carve now considers only roads
whose shoulder actually reaches, and takes the closest of those.

The per-chunk cull pad is derived from the widest shoulder plus a margin, so
widening a road cannot silently truncate it at chunk edges.

Untouched: density field, Marching Cubes interpolation/welding, chunk dims,
the .dat contract, the 2D A* generation, and mesher normals.

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

119 lines
4.6 KiB
C#

using Godot;
namespace IslaApocalypse.Core
{
public static class Constants
{
// Chunk Dimensions
public const int CHUNK_SIZE_X = 24;
public const int CHUNK_SIZE_Z = 24;
public const int CHUNK_HEIGHT = 256; // Our agreed-upon depth!
// World Generation
public const float ISO_LEVEL = 0.0f; // The threshold for Marching Cubes
public const float VOXEL_SCALE = 1.0f; // 1 Pixel = 1 Meter
// --- APPEARANCE TUNING ---------------------------------------------
// How many metres it takes for one surface material to fade into the
// next (grass -> dirt -> stone) on the rendered mesh.
//
// TUNE THIS BY EYE: bigger = softer, more gradual fade.
// 0.0 = hard switch, exactly like the old banded look
// 2.0 = default, a gentle two-metre blend
// 4.0+ = very soft, materials smear into each other
//
// Colour only. This does NOT affect terrain shape, collision, or what
// block is actually stored in a voxel.
public const float BLEND_BAND_METERS = 2.0f;
// --- ROAD CHARACTER BY TIER (D-022) ---------------------------------
// A road is carved to match how BUILT it is. One gradient, four steps:
// a highway is engineered, a trail is a footpath worn into the ground.
//
// Three knobs per tier:
// RoadRadius - half-width of the flat carriageway, in metres.
// ShoulderRadius - how far out the carve eases back to natural
// ground. Must always be LARGER than RoadRadius.
// GradeSmoothing - 0.0 hugs the land (follows every bump)
// 1.0 holds a grade (straight ramp between path
// points, cutting bumps and filling dips).
// SurfaceMaterial- what the carriageway is paved with.
//
// TUNE THESE BY EYE. They are starting values chosen to make the
// hierarchy visible, not final ones. A trail being bumpy is CORRECT —
// nobody drives a truck down a footpath.
//
// NOTE: these affect road ELEVATION (terrain shape) and paint, not the
// terrain noise. Tune the highway properly once vehicles can drive it.
public const float HIGHWAY_ROAD_RADIUS = 4.0f; // 8 m carriageway
public const float HIGHWAY_SHOULDER_RADIUS = 12.0f;
public const float HIGHWAY_GRADE_SMOOTHING = 1.0f; // fully engineered
public const float BRANCH_ROAD_RADIUS = 3.0f;
public const float BRANCH_SHOULDER_RADIUS = 9.0f;
public const float BRANCH_GRADE_SMOOTHING = 0.7f;
public const float RUGGED_ROAD_RADIUS = 2.0f;
public const float RUGGED_SHOULDER_RADIUS = 6.0f;
public const float RUGGED_GRADE_SMOOTHING = 0.4f;
public const float TRAIL_ROAD_RADIUS = 1.5f;
public const float TRAIL_SHOULDER_RADIUS = 4.0f;
public const float TRAIL_GRADE_SMOOTHING = 0.1f; // barely built at all
/// <summary>
/// The widest reach any road has. The per-chunk road cull pads by this
/// (plus a margin) so a road just outside a chunk still carves into it.
/// Computed from the values above, so raising a shoulder can never
/// silently outgrow the cull and truncate roads at chunk edges.
/// </summary>
public static readonly float MAX_SHOULDER_RADIUS = Mathf.Max(
Mathf.Max(HIGHWAY_SHOULDER_RADIUS, BRANCH_SHOULDER_RADIUS),
Mathf.Max(RUGGED_SHOULDER_RADIUS, TRAIL_SHOULDER_RADIUS));
/// <summary>Safety margin added to the road cull, in metres.</summary>
public const float ROAD_CULL_MARGIN = 3.0f;
/// <summary>
/// Looks up the carve settings for a road tier. One place to tune.
/// </summary>
public static RoadProfile GetRoadProfile(RoadTier tier)
{
switch (tier)
{
case RoadTier.Highway:
return new RoadProfile(HIGHWAY_ROAD_RADIUS, HIGHWAY_SHOULDER_RADIUS,
HIGHWAY_GRADE_SMOOTHING, BlockRegistry.ASPHALT);
case RoadTier.Branch:
return new RoadProfile(BRANCH_ROAD_RADIUS, BRANCH_SHOULDER_RADIUS,
BRANCH_GRADE_SMOOTHING, BlockRegistry.ASPHALT);
case RoadTier.Rugged:
return new RoadProfile(RUGGED_ROAD_RADIUS, RUGGED_SHOULDER_RADIUS,
RUGGED_GRADE_SMOOTHING, BlockRegistry.DIRT);
default: // Trail
return new RoadProfile(TRAIL_ROAD_RADIUS, TRAIL_SHOULDER_RADIUS,
TRAIL_GRADE_SMOOTHING, BlockRegistry.DIRT);
}
}
}
/// <summary>
/// How one tier of road is carved. Values come from Constants above.
/// </summary>
public struct RoadProfile
{
public float RoadRadius;
public float ShoulderRadius;
public float GradeSmoothing;
public byte SurfaceMaterial;
public RoadProfile(float roadRadius, float shoulderRadius, float gradeSmoothing, byte surfaceMaterial)
{
RoadRadius = roadRadius;
ShoulderRadius = shoulderRadius;
GradeSmoothing = gradeSmoothing;
SurfaceMaterial = surfaceMaterial;
}
}
}