Skip to content

Claude/minecraft heightmap generator dhk a4#13

Merged
stephenkall merged 17 commits intomainfrom
claude/minecraft-heightmap-generator-DhkA4
May 8, 2026
Merged

Claude/minecraft heightmap generator dhk a4#13
stephenkall merged 17 commits intomainfrom
claude/minecraft-heightmap-generator-DhkA4

Conversation

@stephenkall
Copy link
Copy Markdown
Owner

@stephenkall stephenkall commented May 8, 2026

More options and diagnostics

stephenkall and others added 17 commits May 6, 2026 16:28
Feature 1: New STL prompt 'sea_level_offset' — carves an ocean basin N blocks
deep below sea level. Ocean cells are set to stl_zero = sea_level - offset,
all terrain is clamped to that floor, and h_min_global = stl_zero. The printed
basin is exactly offset * z_scale mm deep so resin fills flush to sea level.

Feature 2: Optional JSON polygon mask file. Each polygon's convex hull is
computed and all heightmap cells inside are forced to sea_level and marked as
ocean. Uses scipy ConvexHull + Delaunay.find_simplex for point-in-hull test.
World origin (min_cx * 16, min_cz * 16) is stored in checkpoint params after
stage_load so coordinate conversion survives resume.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Heightmap saturation: land heights were normalized against the 98th
percentile of all pixels including ocean cells (at sea_level=0). For flat
worlds, mountains occupy the top 5-15% of land, so they all clamped to
full red. Fix: exclude ocean pixels from land stats and use hi_pct=100.0
(full range normalization). Same fix applied to below-sea gradient.

Sea level offset: previous implementation deepened the ocean basin by
clamping heights in mesh.py. Correct semantics: pretend sea level is N
blocks lower, so shallow seafloor (cells > effective_sea_level) is exposed
as terrain. build_ocean_mask, remove_micro_islands, and apply_ocean_mask
all now use effective_sea_level = sea_level - offset. The printed ocean
basin naturally forms at the effective sea level floor; fill with resin
and its surface aligns with the real sea level on the model.

Config file: at startup, user can optionally provide a path to a
previously saved config.json to pre-fill all prompts. After answering
all prompts, config is saved to <out_dir>/config.json automatically.
Checkpoint detection now runs regardless of whether a config was loaded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Unattended mode: pass --config=path.json on the CLI (or enter a path at
the config prompt) to run fully non-interactively. All prompts are skipped
and values are taken directly from the JSON. Missing required fields
(save_path, out_dir) produce a clear error and exit. The checkpoint resume
prompt is also skipped when a config is loaded.

Config bug fix: checkpoint detection inside collect_params is now guarded
by `if saved is None:` again, so loading a config file is never silently
overridden by a stale checkpoint in the same output directory.

Smart checkpoint invalidation: after cp.load(), the new run's params are
compared against the stored ones. Only the affected downstream stages are
unmarked:
  - save_path / ground_only / detect_floating changed → unmark from loaded
  - ocean/masking params changed → unmark from processed
  - sigma/gamma/image dims changed → unmark image only
  - STL dims (z, base, max_verts) changed → unmark stl
  - tile dims changed → unmark mosaic + delete tile files

Keep intermediate files: heightmap_raw.npy, heightmap_work.npy, and
ocean_mask.npy are no longer deleted after a successful run. This allows
re-running with a different sigma/gamma to regenerate only image+STL+tiles
without re-parsing 1444 region files. stage_process restores the detected
sea_level into params during the resume (skip) path so stage_image always
has a correct sea level reference.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er reprocess

- ocean.py: skip polygons entirely outside heightmap bounds (negative mgrid dims)
- mc_to_stl.py: restore _min_cx/_min_cz from checkpoint on resume
- mc_to_stl.py: unmark image/stl/mosaic after stage_process completes fresh
  so stale outputs from a prior crashed/wrong run are always regenerated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…utput

Bug fix: _min_cx/_min_cz (block-coord origin for polygon masking) were lost
whenever stage_load resumed from cache, because cp.set_params() in main()
overwrote checkpoint state before stage_load had a chance to restore them.
Fix: preserve all _-prefixed internal keys from checkpoint before overwriting,
and restore _min_cx/_min_cz in stage_load's resume path.

Feature: generate_image() now also writes heightmap_gray.png alongside the
color PNG. Ocean = black, land = linear grayscale (darkest=lowest, white=highest).
stage_image regenerates if grayscale is missing (upgrade path).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The root cause: build_ocean_mask was called with effective_sea_level
(= sea_level - offset). On the Westeros/Essos map, ocean water sits at
Y=63 (standard MC sea level). With offset=2, effective=61, so ALL
surface water (Y=62-63) was excluded from the ocean mask and appeared
as flat green land in the image. Only deep water at Y≤61 (21%) was
masked blue — the polygon (ignore.json) appeared as the only ocean
because it forced cells into the mask explicitly.

Fix: decouple ocean DETECTION from ocean FLATTENING.
- build_ocean_mask and remove_micro_islands now use sea_level (original),
  so the full coastline is always detected correctly.
- apply_ocean_mask still uses effective_sea_level, so the STL basin is
  sea_level_offset blocks deeper than sea level (fill with resin).
- apply_polygon_masks now uses effective_sea_level for consistency.
- stage_image now passes sea_level (not eff_sl) to generate_image, so
  the colour reference matches the real coastline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ng + blue ocean

Replace red-green-blue elevation gradient with:
- Grayscale land (2nd-98th percentile normalised, lighter = higher)
- Directional hillshading replicating Unmined's CalcShading algorithm
  (3x3 weighted kernel, sun_angle=120deg, factor range ~0.66-1.34)
- Uniform steel-blue ocean overlay
- Drop unused gamma parameter (no longer needed for grayscale scheme)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rm grayscale

- Always resample to exact (out_w × out_h): previously zoom was skipped
  when scale >= 0.999, so images stayed at native block resolution even
  when max_px was configured for larger/smaller output
- Apply 3x3 median filter before rendering to remove isolated deep-pixel
  anomalies (cave mouths, ravines, missing chunks) that survived Gaussian
- Remove hillshading — color now maps purely to elevation with no
  directional bias, making the image a reliable working reference
- Both heightmap.png and heightmap_gray.png now use the same 2nd-98th
  percentile normalisation (gray previously used full min..max range)
- Remove unused _unmined_hillshade helper and gamma parameter

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- force_scan: bypass stale stored Heightmaps NBT by always scanning
  block sections (matches Unmined behaviour); disables chunk cache
- crop_poly: 4-corner quadrilateral in block coords filters region files
  and masks outside-polygon pixels to global min height
- image: use strict global min→black, max→white instead of 2nd-98th
  percentile, eliminating the mountain plateau at 255
- mesh: flip rows before meshing so north maps to Y=max (back of print
  bed), fixing the Y-axis mirror in single STL and mosaic tiles

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- image: new elevation color scheme (dark blue → sea green → red);
  ocean cells remain steel-blue; both PNG outputs support RGBA alpha
  when rectangular_crop=False (outside crop polygon → transparent)
- mesh: replace max_z_mm with z_exaggeration (float, default 1.0 = same
  scale as XY); actual Z = h_range * xy_scale * z_exaggeration
- config: unified sectioned JSON format with "configuration", "crop_area"
  (single polygon), and "sea_masking" (list of polygons) sections;
  backward compat reads old flat format automatically; polygon_json file
  is inlined into sea_masking on first run
- ocean: apply_polygon_masks accepts new [[x,z],...] format alongside
  legacy {"coordinates":...} dicts
- loader: _crop_to_polygon returns crop_mask (bool array); load_save
  returns (hm, meta, crop_mask) 3-tuple
- stage_load: saves crop_mask to .crop_mask.npy; reloads on resume
- stage_image: receives crop_mask; passes rectangular_crop flag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The sea-masking prompt was refactored to use input() directly in
interactive mode, leaving _ask_str with no callers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pressions

Problem: regions below sea level that are terra (not water) were marked as ocean.
Solution: add detect_water_blocks mode that scans block types during parsing.

- anvil.py: add _is_water() and _water_map_from_sections() to identify water blocks
- anvil.py: add parse_region_with_water() returning both heightmaps and water maps
- loader.py: extend _parse_worker() and load_save() to optionally build water maps
- loader.py: add _assemble_water_map() to stitch water chunks into full array
- ocean.py: add water_map parameter to build_ocean_mask(); filters candidates
- mc_to_stl.py: add detect_water_blocks prompt and parameter passing
- mc_to_stl.py: stage_load() and stage_process() handle water_map caching

When detect_water_blocks=True (opt-in), ocean detection requires BOTH:
  1. height <= sea_level AND
  2. block type is water

This prevents terra depressions (e.g. canyons) below sea level from being marked ocean.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…anations

- Add 'Water block detection' section explaining detect_water_blocks
- Add 'Force scan' section explaining how to fix stale chunk data
- Update 'Heightmap image' section with rectangular_crop explanation
- Update 'STL physical dimensions' with z_exaggeration examples and border_width
- Add 'Crop polygon and sea masking' section with format examples
- Rewrite 'Example config.json' with inline comments for each parameter
- Add 'Key parameter explanations' section summarizing all config options
- Update Tested worlds section to reflect WesterosEssos at 16k scale

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@stephenkall stephenkall merged commit 4061efa into main May 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant