{"id":49455519,"url":"https://github.com/shilo/pentatile","last_synced_at":"2026-05-01T06:01:16.889Z","repository":{"id":353911643,"uuid":"1220176984","full_name":"Shilo/PentaTile","owner":"Shilo","description":"Just paint your tiles. Intuitive Godot autotiling addon that takes the pain out of tilesets, with no manual terrain setup needed. Supports 5-archetype Penta and popular layouts. Paint with Godot's normal tools and PentaTile fills in the corners, edges, and transitions for you.","archived":false,"fork":false,"pushed_at":"2026-04-27T10:58:04.000Z","size":1602,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-27T12:28:22.034Z","etag":null,"topics":["2d","autotile","autotiling","blob-tiles","dual-grid","gamedev","gdscript","godot","godot-addon","godot-plugin","godot4","pixellab","tetra","tetra-tiles","tilemap","tileset","tilesetter","wang-tiles"],"latest_commit_sha":null,"homepage":"","language":"GDScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Shilo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-24T16:16:37.000Z","updated_at":"2026-04-27T10:58:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Shilo/PentaTile","commit_stats":null,"previous_names":["shilo/tetratile","shilo/pentatile"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/Shilo/PentaTile","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shilo%2FPentaTile","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shilo%2FPentaTile/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shilo%2FPentaTile/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shilo%2FPentaTile/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Shilo","download_url":"https://codeload.github.com/Shilo/PentaTile/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shilo%2FPentaTile/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32455254,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T22:27:22.272Z","status":"online","status_checked_at":"2026-04-30T02:00:05.929Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["2d","autotile","autotiling","blob-tiles","dual-grid","gamedev","gdscript","godot","godot-addon","godot-plugin","godot4","pixellab","tetra","tetra-tiles","tilemap","tileset","tilesetter","wang-tiles"],"created_at":"2026-04-30T05:04:06.409Z","updated_at":"2026-05-01T06:01:16.880Z","avatar_url":"https://github.com/Shilo.png","language":"GDScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# \u003cimg src=\"brand/penta_tile_icon.png\" width=\"32\" alt=\"PentaTile Icon\"\u003e PentaTile\n\n**Just paint your tiles.** Intuitive Godot autotiling addon that takes the pain out of tilesets, with no manual terrain setup needed. Supports [5-archetype **Penta**](#-what-is-a-penta-tileset) and [popular layouts](#-supported-layouts). Paint with Godot's normal tools and PentaTile fills in the corners, edges, and transitions for you.\n\n\u003cimg src=\"brand/penta_tile_logo.png\" width=\"188\" alt=\"PentaTile Logo\"\u003e\n\n## 📑 Table of Contents\n\n1. [Why PentaTile?](#-why-pentatile)\n2. [What is a Penta tileset?](#-what-is-a-penta-tileset)\n3. [Supported Layouts](#-supported-layouts)\n4. [The Penta-System Template](#-the-penta-system-template)\n5. [Layouts](#-layouts)\n6. [Comparison: PentaTile vs. TileMapDual](#-pentatile-vs-tilemapdual-api)\n7. [Choosing the Right Tool](#-choosing-the-right-tool)\n8. [Docs Site](#-docs-site)\n9. [Addon Layout](#-addon-layout)\n10. [Current API](#-current-api)\n11. [Demo](#-demo)\n12. [Authoring a Custom Layout](#-authoring-a-custom-layout)\n13. [Upgrading from 0.1.x](#-upgrading-from-01x)\n14. [Identity \u0026 Footprint](#-identity--footprint)\n15. [Implementation Notes](#-implementation-notes)\n16. [Roadmap](#-roadmap)\n17. [External Resources](#-external-resources)\n\n## 🚀 Why PentaTile?\n\n- **Reduced Tile Requirements:** Creating 47 tiles for a single terrain type is a time-consuming task. PentaTile's signature **Penta** layout scales the requirement from as few as one tile up to five (the progressive ONE through FIVE modes), lowering the barrier for creating custom game art while maintaining professional results.\n- **Efficient Visual Variation:** Authoring as few as 1–5 tiles per terrain makes iteration cheap. Instead of redrawing dozens of tiles for a single alternative set, you can quickly iterate on the small archetype set to add organic variety and reduce repetitive patterns.\n- **Native Integration:** Built as a single-class subclass of `TileMapLayer`, PentaTile hooks directly into Godot's native API. It listens to standard drawing commands and updates the visual layers in real-time without requiring a custom drawing interface.\n\n## 🍀 What is a Penta tileset?\n\n\u003cimg src=\"addons/penta_tile/layouts/penta_tile_layout_penta/five_horizontal.png\" width=\"256\" alt=\"Penta archetype reference: IsolatedCell (slot 0, source of synthesized OuterCorner) + Fill / Border / InnerCorner / OppositeCorners (slots 1-4)\"\u003e\n\nA **Penta tileset** is a 5-archetype autotile format. The five archetypes, **listed in canonical slot order**:\n\n1. **IsolatedCell** (slot 0) — a tile with all four edges and all four corners exposed; serves as the source for synthesizing OuterCorner.\n2. **Fill** (slot 1) — a tile with all four edges adjacent to the same terrain; the most common interior tile.\n3. **Border** (slot 2) — a tile on a straight terrain edge (one side adjacent to \"different terrain\").\n4. **InnerCorner** (slot 3) — a tile at the inside of an L-bend (two adjacent sides adjacent to \"different terrain\").\n5. **OppositeCorners** (slot 4) — a tile with two diagonally-opposite different-terrain corners.\n\n**OuterCorner** is implicit — synthesized from the corners of slot 0 (IsolatedCell) at load time. It does not occupy a dedicated slot.\n\n**Synthesis rule:** PentaTile supports a progressive 5-mode authoring scale (ONE through FIVE). Modes ONE through FOUR synthesize the missing archetypes from slot 0; mode FIVE provides all five archetypes hand-authored. Either way, every connectivity state at runtime resolves to one of the five archetypes above.\n\n**How \"Penta\" relates to other tileset codenames:**\n\n| Format | Tiles authored | Slot count | Year coined |\n| ------ | -------------- | ---------- | ----------- |\n| Wang   | 16 / 64 / 256  | varies     | ~1986       |\n| Blob   | 47             | 47         | ~2010       |\n| Penta  | 1–5            | 5          | 2026        |\n\n\"Penta\" is reserved for the 5-archetype format only — never for unrelated 5-tile arrangements. This rule is encoded as a project invariant in `CLAUDE.md` § Coined-Term Discipline.\n\n## 🧩 Supported Layouts\n\nAlready have tiles in a different format? No problem. PentaTile ships with a library of layouts covering virtually every popular autotiling convention out of the box:\n\n- **[Penta](#-the-penta-system-template)** (horizontal \u0026 vertical): the signature 1–5 tile authoring scale (modes ONE through FIVE)\n- **\u003ca href=\"https://www.youtube.com/watch?v=jEWFSv3ivTg\" target=\"_blank\" rel=\"noopener\"\u003eDual Grid ↗︎\u003c/a\u003e**: the popular 16-tile corner-mask format\n- **\u003ca href=\"https://www.boristhebrave.com/permanent/24/06/cr31/stagecast/wang/intro.html\" target=\"_blank\" rel=\"noopener\"\u003eWang ↗︎\u003c/a\u003e** (2-edge \u0026 2-corner): the classic edge/corner-color system\n- **\u003ca href=\"https://www.boristhebrave.com/2021/11/14/classification-of-tilesets/\" target=\"_blank\" rel=\"noopener\"\u003e47-tile Blob ↗︎\u003c/a\u003e**: the full Godot/Wang blob set\n- **\u003ca href=\"https://www.tilesetter.org/docs/generating_tilesets\" target=\"_blank\" rel=\"noopener\"\u003eTilesetter ↗︎\u003c/a\u003e** (Wang 15 \u0026 Blob 47): atlases as exported by Tilesetter\n- **\u003ca href=\"https://www.pixellab.ai/docs/tools/create-tileset\" target=\"_blank\" rel=\"noopener\"\u003ePixelLab ↗︎\u003c/a\u003e** (top-down \u0026 side-scroller): native image outputs from the PixelLab Aseprite extension\n- **Minimal 3x3**: the 9-tile match-sides format used by RPG Maker A2 and legacy Godot 3.x\n\nWhatever convention your art was drawn for, PentaTile can paint with it. And if your favorite isn't built in, you can plug in a custom layout of your own.\n\n## 🎨 The Penta-System Template\n\n\u003cimg src=\"addons/penta_tile/layouts/penta_tile_layout_penta/five_horizontal.png\" width=\"256\" alt=\"Penta Horizontal Tileset Template\"\u003e\n\nA **Penta** atlas is a horizontal or vertical strip of 1–5 tiles (the 5-mode authoring scale: ONE, TWO, THREE, FOUR, FIVE). The slots, in canonical order:\n\n1.  **IsolatedCell** (slot 0, always authored — also the source for synthesizing OuterCorner via render-time rotation)\n2.  **Fill** (slot 1, authored at TWO mode and above)\n3.  **Border** (slot 2, authored at THREE mode and above)\n4.  **InnerCorner** (slot 3, authored at FOUR mode and above)\n5.  **OppositeCorners** (slot 4, authored at FIVE mode)\n\nModes ONE through FOUR synthesize the missing archetypes from slot 0 at load time via `PentaTileSynthesis`. The two disconnected diagonal states (masks 6 and 9) resolve to the **OppositeCorners** archetype — synthesized in modes ONE..FOUR or hand-authored in mode FIVE. Single-layer dispatch only; no internal overlay layer (Phase 2 deleted that path in favor of the synthesized OppositeCorners archetype).\n\n## 🧱 Layouts\n\nPentaTile ships **8 built-in layouts**. Drop a `PentaTileMapLayer` into a scene, attach one of these layout Resources, and either bring your own atlas or use the layout's bundled fallback PNG (no `tile_set` needed) for instant prototyping.\n\n| Layout | Class | Atlas grid | Tile count | Mask | Convention source |\n|--------|-------|-----------|-----------|------|-------------------|\n| **Penta** (FOUR mode shown) | `PentaTileLayoutPenta` | strip 1×N (HORIZONTAL) or N×1 (VERTICAL) | 1–5 (modes ONE through FIVE) | 4-bit corner | Native — the addon's signature 5-archetype convention |\n| **DualGrid 16** | `PentaTileLayoutDualGrid16` | 4×4 | 16 | 4-bit corner (TL=1/TR=2/BL=4/BR=8) | \u003ca href=\"https://www.youtube.com/watch?v=jEWFSv3ivTg\" target=\"_blank\" rel=\"noopener\"\u003eDual Grid ↗︎\u003c/a\u003e |\n| **Wang 2-Edge** | `PentaTileLayoutWang2Edge` | 4×4 | 16 (single-grid) | 4-bit edge (N=1/E=2/S=4/W=8, CR31) | \u003ca href=\"https://www.boristhebrave.com/permanent/24/06/cr31/stagecast/wang/intro.html\" target=\"_blank\" rel=\"noopener\"\u003eCR31 / BorisTheBrave Wang ↗︎\u003c/a\u003e |\n| **Wang 2-Corner** | `PentaTileLayoutWang2Corner` | 4×4 | 16 (single-grid) | 4-bit corner (NE=1/SE=2/SW=4/NW=8, CR31) | Same — different bit naming, same silhouettes as DualGrid 16 |\n| **Minimal 3×3** | `PentaTileLayoutMinimal3x3` | 3×3 | 9 (single-grid) | 4-bit edge (T=1/E=2/B=4/W=8, open-side collapse) | RPG Maker A2 / legacy Godot 3.x |\n| **Blob 47 (Godot)** | `PentaTileLayoutBlob47Godot` | 7×7 (47 tiles + gaps) | 47 | 8-bit Moore mask (256 → 47 collapse) | \u003ca href=\"https://www.boristhebrave.com/2021/11/14/classification-of-tilesets/\" target=\"_blank\" rel=\"noopener\"\u003eBorisTheBrave 47-blob ↗︎\u003c/a\u003e |\n| **PixelLab Top-Down** | `PentaTileLayoutPixelLabTopDown` | 8×8 | 64 (single-grid; 16 archetypes × variation banks) | 4-bit corner; first-cell row-major pick | \u003ca href=\"https://www.pixellab.ai/docs/tools/create-tileset\" target=\"_blank\" rel=\"noopener\"\u003ePixelLab Aseprite plugin ↗︎\u003c/a\u003e top-down output |\n| **PixelLab Side-Scroller** | `PentaTileLayoutPixelLabSideScroller` | 8×8 | 64 (single-grid; 16 archetypes × variation banks) | 4-bit corner; first-cell row-major pick | PixelLab Aseprite plugin side-scroller output |\n\nVariation handling on PixelLab layouts is currently **first-cell row-major pick** (deterministic). Per-cell deterministic-hash variation-bank selection is on the v0.3+ backlog (`VAR-PIXEL-01`, design-coupled with `VAR-01` Y-axis variation).\n\nTilesetter (Wang 15 + Blob 47 in Tilesetter's atlas conventions) is on the v0.3+ backlog (`TBT-01-DEFERRED` / `TBT-02-DEFERRED`) — the Tilesetter primary-source slot tables were not located during plan-phase research; deferred rather than empirically fingerprinted.\n\nCustom layouts are supported via subclassing — see [Authoring a Custom Layout](#-authoring-a-custom-layout) (experimental).\n\n## 📚 Docs Site\n\nThe MkDocs source lives in `docs/`, with configuration in `mkdocs.yml`.\n\n```bash\npython -m pip install -r requirements-docs.txt\nmkdocs serve\n```\n\nThe GitHub release zip intentionally packages only `addons/penta_tile/`; docs,\ntests, and planning artifacts stay in the repository.\n\n## ⚔️ PentaTile vs. TileMapDual API\n\n\u003ca href=\"https://github.com/pablogila/TileMapDual\" target=\"_blank\" rel=\"noopener\"\u003eTileMapDual ↗︎\u003c/a\u003e is an established solution for Dual Grid systems in Godot. **PentaTile** takes a narrower scope, focusing on standard orthogonal grids with a minimal authoring surface.\n\n| Area             | PentaTile                                                                  | TileMapDual                                                            |\n| ---------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------- |\n| Public node      | `PentaTileMapLayer`                                                        | `TileMapDual` plus supporting addon classes                            |\n| Drawing API      | Native `TileMapLayer.set_cell()` / editor painting                         | Native painting plus custom helpers such as `draw_cell(cell, terrain)` |\n| Update hook      | `_update_cells(coords, forced_cleanup)` directly recomputes affected masks | `_update_cells()` forwards into display/cache/watcher systems          |\n| Terrain model    | Binary occupied/empty terrain for V1                                       | Terrain peering bits and terrain rules                                 |\n| Tile requirement | 1–5 tiles per Penta layout (or the layout's native count: 9, 16, 47…)      | 15-16 tile dual-grid/Wang-style sets                                   |\n| Internal state   | No persistent coordinate cache; direct 4-bit sampling                      | Tile caches, terrain rule tries, watchers, signals                     |\n| TileSet setup    | Strict atlas order, no terrain metadata required                           | Terrain metadata and optional editor autotile setup                    |\n| Grid scope       | Square orthogonal V1                                                       | Broader grid-shape handling                                            |\n| Collisions       | Generated visual layers can use TileSet physics polygons                   | Display layers copy collision-related properties from the parent       |\n\nPentaTile is smaller because it focuses on a specific subset of the multi-terrain/general-grid flexibility offered by TileMapDual.\n\n## ⚖️ Choosing the Right Tool\n\n### Why choose PentaTile?\n\n- **Scalability of Variations:** Because the **Penta** layout authoring scale starts at one tile and tops out at five, creating multiple visual variations is significantly faster and more manageable.\n- **Engine Purity:** PentaTile acts as a lightweight extension of the native `TileMapLayer`. It allows you to use Godot's native painting tools as intended, with the system handling the transformation logic automatically.\n- **Direct Logic:** It uses direct bitwise math to determine rotations and flips, keeping the runtime path short and easy to reason about.\n\n### Why choose \u003ca href=\"https://github.com/pablogila/TileMapDual\" target=\"_blank\" rel=\"noopener\"\u003eTileMapDual ↗︎\u003c/a\u003e?\n\n- **Complex Transitions:** For projects requiring complex \"Grass-to-Sand-to-Rock\" multi-terrain blending, TileMapDual is designed to handle that specific complexity.\n- **Standard Templates:** If you are already working with 16-tile Dual Grid (Wang) tilesets, TileMapDual provides a direct solution for those templates.\n\n## 🛠️ Addon Layout\n\n```text\naddons/penta_tile/\n  plugin.cfg\n  penta_tile_map_layer.gd                  # core PentaTileMapLayer node\n  penta_tile_synthesis.gd                  # synthesis machinery for Penta layouts\n  penta_tile_atlas_slot.gd                 # slot resource (atlas_coords + transform_flags)\n  layouts/\n    penta_tile_layout.gd                   # base PentaTileLayout\n    penta_tile_layout_penta.gd             # Penta family (1–5 modes, horizontal \u0026 vertical)\n    penta_tile_layout_dual_grid_16.gd      # Dual-grid 16-tile corner-mask\n    penta_tile_layout_wang_2_edge.gd       # Wang 2-edge\n    penta_tile_layout_wang_2_corner.gd     # Wang 2-corner\n    penta_tile_layout_minimal_3x3.gd       # Minimal 3x3\n    penta_tile_layout_penta/               # bundled per-mode PNGs (one_horizontal.png … five_vertical.png)\n  demo/\n    penta_tile_demo.tscn\n    demo_runtime_painter.gd\n    penta_layout_*.tres                    # demo layout resources\nbrand/                                     # project icon + logo (referenced by project.godot + @icon)\ntools/\n  _generate_bitmasks.py                    # internal tooling — regenerates bundled bitmask PNGs\n  mkdocs_hooks.py                          # MkDocs build hooks\ntests/\n  run_tests.ps1                            # Windows local test runner\n  run_tests.sh                             # Linux / CI test runner\n  determinism_test.gd                      # PENTA-SYNTH-06 baseline check\n  baselines/                               # captured hash + tile-map data\ndocs/\n  index.md                                 # MkDocs source\n```\n\nThe release zip archives only `addons/penta_tile/`, so the root `brand/`,\n`tools/`, `tests/`, and `docs/` directories are repository tooling and do not\nship inside the addon package. The `layouts/penta_tile_layout_penta/` PNG\nbundle ships the canonical Penta templates for each axis × mode combination.\n\n## 🔌 Current API\n\n`PentaTileMapLayer` extends `TileMapLayer`.\n\nUse the native TileMapLayer API:\n\n- `set_cell()`\n- `erase_cell()`\n- editor painting tools\n- `tile_set`\n- inherited TileMapLayer rendering/physics properties where applicable\n\nAdditional exported properties:\n\n| Property                      | Purpose                                                                                                                            |\n| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |\n| `layout`                      | A `PentaTileLayout` resource — pick one of the bundled subclasses (Penta, DualGrid16, Wang2Edge, Wang2Corner, Min3x3) or a custom. |\n| `atlas_source_id`             | Atlas source to read from. `-1` uses the first source in the TileSet.                                                              |\n| `logic_layer_opacity`         | Opacity for the hidden/editable logic layer. Defaults to `0.0`.                                                                    |\n| `visual_z_index_offset`       | Z index applied to generated internal visual layers.                                                                               |\n| `generated_collision_enabled` | Enables collisions on generated visual layers when the TileSet tiles have physics polygons.                                        |\n| `logic_collision_enabled`     | Enables collisions on the source logic layer. Defaults to `false` to avoid hidden full-cell colliders.                             |\n\n**Penta-specific layout properties** (on `PentaTileLayoutPenta`):\n\n| Property     | Purpose                                                                                                    |\n| ------------ | ---------------------------------------------------------------------------------------------------------- |\n| `axis`       | `HORIZONTAL` (slots along X) or `VERTICAL` (slots along Y).                                                |\n| `tile_count` | `AUTO` / `AUTO_STRIP` / `ONE..FIVE`. AUTO detects from atlas size; explicit modes pin the authoring scale. `AUTO_STRIP` detects per strip — each row (HORIZONTAL) or column (VERTICAL) of the source atlas can have its own mode count. Per-cell strip dispatch picks the strip from the first non-empty TL/TR/BL/BR neighbor's source atlas coords; mixed-strip neighbors at a tile-boundary may render visually wrong (proper terrain transitions are deferred to a future milestone). |\n\nPublic helper:\n\n| Method      | Purpose                                                               |\n| ----------- | --------------------------------------------------------------------- |\n| `rebuild()` | Clears and regenerates all visual cells from the current logic cells. |\n\n## 🧪 Demo\n\nOpen `res://addons/penta_tile/demo/penta_tile_demo.tscn`.\n\nThe demo is a **side-by-side spatial-grid showcase** of all 8 actually-shipped layouts (see [Layouts](#-layouts)):\n\n- 8 `PentaTileMapLayer` instances arranged in a 2×4 grid, each labeled with its layout name\n- Every instance has `tile_set = null` and `layout = \u003cbundled-resource\u003e` — the fallback `TileSet` is generated at runtime from each layout's `bitmask_template` via `get_fallback_tile_set()`\n- No authored TileSet anywhere in the demo — proves the prototyping UX end-to-end\n- Runtime drag-paint: left mouse button paints into whichever instance the cursor is over; right mouse button erases. Cross-instance gutters block paint between layers.\n- No platformer player — the demo is a pure layout showcase. (`get_fallback_tile_set()` ships zero physics layers, so there is nothing for a player to collide with on the bundled fallback art.)\n\n## 🛠️ Authoring a Custom Layout\n\n\u003e **Experimental** (`@experimental` per Phase 4 doc-comment sweep).\n\u003e The base class `PentaTileLayout` is annotated as experimental in the codebase — its virtual surface may evolve before the addon hits 1.0. Use the built-in 8 layouts where they fit; subclass only when a convention is genuinely missing.\n\nA custom layout subclasses `PentaTileLayout` and overrides three virtuals:\n\n| Virtual | Returns | Purpose |\n|---------|---------|---------|\n| `compute_mask(coord, sample_fn)` | `int` | Sample neighbors via `sample_fn(coord_offset)` (returns the source-atlas-coord at that cell, or `Vector2i(-1,-1)` if empty), pack the bits into your layout's mask integer (corner-mask, edge-mask, 8-bit Moore, etc.). |\n| `mask_to_atlas(mask)` | `PentaTileAtlasSlot` (or `null` to skip) | Map the mask integer to an atlas cell — return a `PentaTileAtlasSlot` with `atlas_coords: Vector2i`, `transform_flags: int = 0` (bit-pack rotations via `_pack_alternative()`), and `alternative_tile: int = 0`. |\n| `get_fallback_tile_set()` | `TileSet` (or `null`) | Optional. Returns a runtime-generated TileSet from `bitmask_template`. The base implementation handles the common case; override only if the layout needs a non-uniform atlas grid. |\n\n**Minimal example** (a hypothetical 1-tile \"always-fill\" layout):\n\n```gdscript\n@tool\n@experimental(\"Custom layouts are an experimental v0.2 feature; the virtual surface may change before 1.0.\")\nclass_name MyAlwaysFillLayout\nextends PentaTileLayout\n\nfunc compute_mask(_coord: Vector2i, _sample_fn: Callable) -\u003e int:\n    return 1  # always the same mask\n\nfunc mask_to_atlas(_mask: int) -\u003e PentaTileAtlasSlot:\n    var slot := PentaTileAtlasSlot.new()\n    slot.atlas_coords = Vector2i(0, 0)\n    return slot\n```\n\nAuthoring tips:\n\n- Use `_pack_alternative(alt_id, transform_flags)` to OR rotation flags (`TRANSFORM_FLIP_H | FLIP_V | TRANSPOSE`) into the alternative-tile int. The helper asserts `alt_id \u003c 4096` to guard the bit-collision pitfall.\n- For single-grid layouts, return a dispatched slot for `mask == 0` instead of `null` (Critical Pitfall #9 — isolated cells must render).\n- Co-locate a `bitmask_template: Texture2D` PNG next to the script for inspector preview + fallback support.\n- Override `is_dual_grid()` to return `true` if the layout uses dual-grid composition (DualGrid16, Penta) vs `false` for single-grid (Wang2Edge, Wang2Corner, Min3x3, Blob47, PixelLab*).\n\nSee `addons/penta_tile/layouts/penta_tile_layout_minimal_3x3.gd` for a compact reference subclass (~50 LOC).\n\n## ⬆️ Upgrading from 0.1.x\n\nPentaTile v0.2.0 is a hard breaking-change release with no backwards-compatibility shims (per the addon's [no-compat policy](../../CLAUDE.md#breaking-changes-policy-hard-rule)). The full breakage list is in [CHANGELOG.md](CHANGELOG.md); the key migrations:\n\n| v0.1 surface | v0.2 replacement |\n|--------------|------------------|\n| Project name `TetraTile` | `PentaTile` (entire repo renamed; class prefixes, addon folder, plugin id, custom data layer keys all renamed) |\n| `addons/tetra_tile/` | `addons/penta_tile/` |\n| `TetraTileMapLayer` class | `PentaTileMapLayer` |\n| `atlas_contract: PentaTileAtlasContract` @export on the layer | `layout: PentaTileLayout` @export directly on the layer (contract wrapper deleted) |\n| Separate `PentaTileLayoutPentaHorizontal` / `PentaTileLayoutPentaVertical` classes | Single `PentaTileLayoutPenta` class with `axis: Axis` and `tile_count: TileCountMode` enums |\n| 4-tile binary atlas only | 8 layouts: Penta (1-5 modes), DualGrid16, Wang2Edge, Wang2Corner, Min3x3, Blob47Godot, PixelLab Top-Down, PixelLab Side-Scroller |\n| `template_image: Texture2D` on the layout | `bitmask_template: Texture2D` (renamed; same image now serves both inspector preview AND fallback TileSet source) |\n| `fallback_tile_set: TileSet` @export on the layout | Hidden — `get_fallback_tile_set()` virtual generates one at runtime from `bitmask_template` |\n| `decoder_image: Texture2D` (speculative) | Deleted (no consumer; YAGNI per no-forward-compat policy) |\n| `addons/penta_tile/templates/` folder | Deleted; bundled bitmask PNGs co-located next to layout `.gd` files |\n| Slot ordering `0=Fill, 1=InnerCorner, 2=Border, 3=OuterCorner` | New ordering `0=IsolatedCell, 1=Fill, 2=Border, 3=InnerCorner, 4=OppositeCorners`; OuterCorner is implicit (synthesized from slot 0) |\n| Runtime `_overlay_layer` for masks 6 / 9 | Deleted; Penta synthesizes the OppositeCorners archetype at load time, single-layer dispatch only |\n| Demo: platformer player + authored ground.tres | Spatial-grid showcase of all 8 layouts using bundled fallbacks; no player |\n\nMigration path: rename addon folder, replace `atlas_contract` with `layout`, swap layout subclass references (Penta H/V → `PentaTileLayoutPenta(axis=...)`), rename `template_image` → `bitmask_template` if you authored your own layouts. v0.1 atlases are **not** bit-compatible with the new slot ordering (slot 3 is now InnerCorner, was OuterCorner) — re-author atlases if you painted against the old slot table.\n\n## 🔍 Identity \u0026 Footprint\n\nPentaTile's per-cell paint path is **4 stack frames deep** and traverses zero persistent caches, zero watchers, zero signal fanout, zero rule-trie walks, and zero property-copy hops. The audit (against TileMapDual v5.0.2, commit `9ff1e24f`) confirms 16 of 16 anti-pattern register items absent — the 6 CLAUDE.md \"Identity Guardrails\" rejects (terrain peering, multi-terrain transitions, watcher/signal-fanout, coordinate caches, parallel paint APIs, EditorInspectorPlugin polish) and the 10 AP-1..AP-10 entries from PITFALLS.md. Cumulative runtime LOC (2884 PentaTile vs 2126 TileMapDual) is reported as signal, not as a fail criterion (per [D-05-11](.planning/phases/05-demo-refresh-documentation-release/05-CONTEXT.md)). Identity at v0.2.0 is **hot-path minimalism + anti-pattern absence**, not raw LOC delta.\n\n[Full audit: `.planning/phases/05-demo-refresh-documentation-release/05-LOC-AUDIT.md`](.planning/phases/05-demo-refresh-documentation-release/05-LOC-AUDIT.md)\n\nPentaTile's identity is **hot-path minimalism + anti-pattern absence**, not raw LOC delta vs TileMapDual (per [D-05-11](.planning/phases/05-demo-refresh-documentation-release/05-CONTEXT.md)). The runtime path stays short:\n\n```\n_update_cells(coords) → layout.compute_mask(coord, sample_fn) → layout.mask_to_atlas(mask) → set_cell(coord, source_id, atlas_coords)\n```\n\nThe addon explicitly does NOT include:\n\n- Terrain peering metadata or terrain-rule tries\n- Watcher / signal-fanout systems\n- Persistent coordinate caches\n- Parallel paint APIs alongside `set_cell()`\n- `EditorInspectorPlugin` polish\n\n## 📝 Implementation Notes\n\nMask bits use:\n\n| Bit | Quadrant     |\n| --- | ------------ |\n| `1` | Top-left     |\n| `2` | Top-right    |\n| `4` | Bottom-left  |\n| `8` | Bottom-right |\n\nThe diagonal masks are `6` and `9`. Both resolve to the **OppositeCorners** archetype (slot 4 in a Penta atlas). PentaTile anchors mask 9 (`TL+BR`, \"\\\\\" diagonal) as the unrotated case (`_ROTATE_0`) and mask 6 (`TR+BL`, \"/\" diagonal) as `TRANSFORM_FLIP_H` of the same archetype. In modes ONE through FOUR the OppositeCorners art is synthesized from slot 0 corners by `PentaTileSynthesis`; in mode FIVE it is hand-authored. Single-layer dispatch only — no internal overlay layer.\n\nThe logic layer is hidden with `self_modulate.a`, not `visible = false`, because Godot may force cleanup behavior when a `TileMapLayer` is disabled, hidden, removed, or missing a TileSet.\n\n## 🗺️ Roadmap\n\n**v0.2.0 (current):** layout library — 8 built-in layouts (Penta, DualGrid16, Wang2Edge, Wang2Corner, Min3x3, Blob47Godot, PixelLab Top-Down, PixelLab Side-Scroller), bundled-fallback prototyping (no authored TileSet needed), full doc-comment sweep, demo refresh, GitHub release.\n\n**v0.3+ backlog** (deferred from v0.2 for design-coupling or scope reasons):\n\n- **Tilesetter Wang 15 + Blob 47** layouts (`TBT-01-DEFERRED`, `TBT-02-DEFERRED`) — primary-source slot tables not located in v0.2 plan-phase research; revisit when a Tilesetter export sample is available\n- **PixelLab variation-bank pick** (`VAR-PIXEL-01`) — currently first-cell row-major; deterministic-hash bank selection deferred (design-coupled with Y-axis variation)\n- **Y-axis variation** (`VAR-01`) via deterministic per-cell hash + `TileData.probability` weights\n- **Top tiles** (`TOP-01`) — designated top-edge visuals for platformer caps\n- **Multi-terrain in one tileset** (`MULTITERR-01..05`) — independent autotiling for multiple terrains within one atlas\n- **RPG Maker A2 / A4 subtile compositor** — quarter-tile composition (separate pipeline)\n- **PentaBake** — edit-time utility to procedurally compose a fifth archetype tile\n- **Tileset converter** — Wang/blob/single-tile inputs → PentaTile atlas\n- **Editor line/rect/bucket tool preview during drag** — visible preview when a `layout` is bound (Phase 6, far-future)\n- **Shader fallback** — single-pass shader for diagonal compositing\n- **Outer transition tiles** (`TERRAIN-01`) — multi-terrain transitions (grass→dirt etc.)\n\nFull deferred-features inventory is in [`.planning/REQUIREMENTS.md`](.planning/REQUIREMENTS.md) § \"v2 Requirements\".\n\n## 🔗 External Resources\n\n- \u003ca href=\"https://github.com/dandeliondino/godot-4-tileset-terrains-docs\" target=\"_blank\" rel=\"noopener\"\u003eGodot 4 Autotilling Documentation ↗︎\u003c/a\u003e - A detailed guide and starter project for understanding Godot 4's native terrain system.\n- \u003ca href=\"https://www.youtube.com/watch?v=jEWFSv3ivTg\" target=\"_blank\" rel=\"noopener\"\u003eThe Dual Grid Concept ↗︎\u003c/a\u003e - A brilliant deep dive into how offset grid math solves the 47-tile problem.\n- \u003ca href=\"https://www.youtube.com/watch?v=aWcCNGen0cM\" target=\"_blank\" rel=\"noopener\"\u003eDrawing Only 5 Tiles ↗︎\u003c/a\u003e - The inspiration for PentaTile's minimalism, showing how to achieve high-end results with a tiny asset footprint.\n- \u003ca href=\"https://github.com/dandeliondino/tile_bit_tools\" target=\"_blank\" rel=\"noopener\"\u003eTileBitTools (Godot 4 inspector plugin) ↗︎\u003c/a\u003e - Design inspiration for PentaTile's layout-Resource architecture. PentaTile re-implements every layout from each format's primary reference (BorisTheBrave for 47-blob; Tilesetter manual for Tilesetter Wang/Blob); no code or data is copied from TBT.\n- \u003ca href=\"https://excaliburjs.com/blog/Dual%20Tilemap%20Autotiling%20Technique/\" target=\"_blank\" rel=\"noopener\"\u003eDual Tilemap Autotiling Technique (Excalibur.js) ↗︎\u003c/a\u003e - Codifies the 5-archetype dual-grid set: \u003ccode\u003eFilled\u003c/code\u003e, \u003ccode\u003eEdge\u003c/code\u003e, \u003ccode\u003eInnerCorner\u003c/code\u003e, \u003ccode\u003eOuterCorner\u003c/code\u003e, \u003ccode\u003eOppositeCorners\u003c/code\u003e. Source for PentaTile's \"Opposite Corners\" archetype name. Companion code: \u003ca href=\"https://github.com/jyoung4242/dual-grid-auto-tiling\" target=\"_blank\" rel=\"noopener\"\u003ejyoung4242/dual-grid-auto-tiling ↗︎\u003c/a\u003e.\n- \u003ca href=\"https://youtu.be/Uxeo9c-PX-w?t=305\" target=\"_blank\" rel=\"noopener\"\u003eOskar Stålberg — dual-grid implementation walkthrough (5:05) ↗︎\u003c/a\u003e - The dual-grid talk that popularized this technique; the deep-link jumps straight to the tile-implementation breakdown.\n- \u003ca href=\"https://www.youtube.com/watch?v=buKQjkad2I0\" target=\"_blank\" rel=\"noopener\"\u003eProgramming Terrain Generation for my Farming Game ↗︎\u003c/a\u003e - Devlog showing dual-grid / 5-tile autotiling applied in a real game project.\n- \u003ca href=\"https://www.rpgmakerweb.com/blog/classic-tutorial-how-autotiles-work\" target=\"_blank\" rel=\"noopener\"\u003eClassic Tutorial: How Autotiles Work (RPG Maker) ↗︎\u003c/a\u003e - Explains RPG Maker's A2 autotile internals — each tile composed from 4 mini-tiles of 24×24 px. Background reading for the eventual \u003ccode\u003eRPGM-01/02\u003c/code\u003e subtile compositor (v0.3+).\n\n## 🙏 Attributions\n\n- \u003ca href=\"https://kenney.nl/assets/pico-8-platformer\" target=\"_blank\" rel=\"noopener\"\u003eKenney's Pico-8 Platformer ↗︎\u003c/a\u003e - Asset pack used for the demo ground texture (CC0).\n- The pixel-art Godot robot mascot in PentaTile's brand assets is an original drawing inspired by \u003ca href=\"https://godotengine.org/press/\" target=\"_blank\" rel=\"noopener\"\u003eGodot's official icon ↗︎\u003c/a\u003e and \u003ca href=\"https://toongoat.itch.io/godot-pixel-art-emoji-pack\" target=\"_blank\" rel=\"noopener\"\u003eKrad's Godot Pixel Art Emoji Pack ↗︎\u003c/a\u003e. It's a \"powered by Godot\" nod, not PentaTile branding — the Godot logo and name are trademarks of the Godot Foundation, and PentaTile claims no ownership of either.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshilo%2Fpentatile","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshilo%2Fpentatile","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshilo%2Fpentatile/lists"}