update readme, mod card

This commit is contained in:
DramaticShape
2026-07-26 18:16:09 -04:00
parent 7046ead648
commit 39e855da34
4 changed files with 394 additions and 523 deletions
+349 -375
View File
@@ -1,6 +1,22 @@
# Changelog
## Unreleased
## 1.0.0
First release, ported from the engine-internal voxel branch onto the
`render_pipelines` mod API.
Interior furniture gets the shapes it depicts
(mods/DRAMATIC_SHAPE/tools/voxel-survey.md is the procedure that found and
verified these).
Buildings stop being boxes wearing their own elevation. A profiled
building is voxelized from its own sprite, band by band -- the pipeline
written up in `assets/docs/buidling_to_voxel/`.
Terrain meshing goes asynchronous, instanced and bounded. The first voxel
frame used to build every neighbourhood map synchronously (a ~2.4s
freeze), retain every map's analysis forever (gigabytes over a
cross-region trek), and string stray pixels along map seams.
Real shadows. The sun moves to the southeast and drops to 45 degrees, so
shadows fall northwest -- up and to the left on screen -- and run about as
@@ -8,6 +24,136 @@ long as the thing throwing them is tall.
### Added
- `voxel` render pipeline: 3D diorama overworld with extruded terrain,
depth-buffered occlusion, leaning sprite billboards, drop shadows and
contact AO. VOXEL options row and hotkey `3` (OFF / 15 / 35 / 50 / 75).
- `tiltshift` render pipeline: a `worldPresent` post-process giving the
miniature-photo look. T-SHIFT options row and hotkey `6` (OFF / 1 / 2 / 3).
- Carved voxel models for 67 overworld sprites, plus the
`tools/build_voxels.py` that produced them.
- `data/voxel_heights.lua`, the hand-authored tile shape profile.
- Furniture shape classes in the profile: `bed` (a low slab wearing its
top-down art), `table` and `desk` (boxes at their drawn height whose
faces fold the artwork up), `relief` (a prop drawn from above -- a
game console -- lying flat and extruding a few voxels inside its
outline), the standee pools `billboard` (10px) / `prop` (5px) /
`stool` (5px, and characters standing on its walkable cell sit at
seat height) / `cutout` (one voxel: pure profile, for the vase on the
table), and the stair archetypes `stair_e`/`stair_w` (a rising flight
of real steps) and `stair_down_e`/`stair_down_w` (a sunken stairwell
descending below the floor -- stairs that lead down). Separate pools
cluster separately, so touching drawings never merge into one cutout.
- Standee clusters split into per-pixel connected components, each
standing on its own feet in the depth band of the row it is drawn in:
two stools stacked in adjacent cells become two stools, and no
fragment of a drawing ever floats at its bounding-box height. A
`cutout` keeps only its largest component -- a cast shadow's drawn
edge is background, not a floating scrap.
- `bookcase` class: free-standing shelf drawings collapse in ranks onto
one-cell-deep boxes at their full drawn height, back rows becoming
hidden floor; the trim row above a rank -- undetected structure or a
row pinned `table`, since the same trim tiles cap other furniture --
is adopted as its cap. Pinned for Oak's Lab (`DOJO`).
- Oak's Lab tables pinned `table`: the starter-ball display and the
north tables stand at real table height, the display frame's black
corner brackets no longer auto-extract into standing prisms, and the
Poke Ball / Pokedex sprites ride the authored height onto the
tabletops.
- Pinned props drawn directly above a pinned box stand ON it: the PC
monitor on its desk, the flower pot on the dining table.
- Profile pins for `REDS_HOUSE_1` / `REDS_HOUSE_2` (Red's house and the
Copycat's, both floors): bed, stools, tables, PC desk with its
standing monitor, bookcases, TV standing on the floor behind the game
console's relief, potted plant, flower pot, both staircases, and the
wall/window band.
- `mods/DRAMATIC_SHAPE/tests/voxel_survey.lua`: screenshot-survey driver
behind the repeatable inspection procedure (SURVEY_MAP / SURVEY_SPOTS /
SURVEY_LEVELS / SHOT_DIR), documented in
mods/DRAMATIC_SHAPE/tools/voxel-survey.md.
- `lib/Buildings.lua`: a building archetype. Where the volume path folds a
whole drawing upright (roof, facade and sloped ends alike) into one box,
this classifies each BAND of the drawing by the 3D surface it depicts and
applies the matching operation: top-facing rows lay flat over the
footprint, the facade extrudes straight back, an awning band juts past
the walls, and the drawn taper at the ends becomes a stepped slope in
elevation. Every visible voxel carries a real texel of the drawing, so
the model recolours with the atlas.
- A `buildings` section in `data/voxel_heights.lua`. A building is matched
by its exact tile grid (the drawings are catalogued in `assets/docs/buildings/`),
so one entry covers every map that places the same art -- Red's house,
Blue's house, Bill's, the Copycat's and the two Fuchsia houses are one
seven-placement entry, and Oak's lab is a second. Only the band table is
authored; the silhouette, the taper rate, the eave height and every
window and doorway are measured off the pixels.
- The flat-roofed civic block and its sixteen relatives -- every Pokemon
Center and every Poke Mart, Fuchsia Gym, the museum, the Game Corner,
Celadon Mansion and its department store, the Power Plant, the Route 5
and Route 22 gates, Silph Co and five anonymous scenery blocks. They are
one architecture drawn at eleven different footprints, from 4x4 cells up
to Silph Co's 8x12, and they share one band table: their lattice is drawn
from straight above, so the measured taper comes out flat and the whole
band is depth under one level roof. No new roof mode was needed -- the
drawn profile was always the shape, and a drawing with no taper simply
yields a level one.
- Pewter's museum hall (`assets/docs/buildings/B24`). It is the one
sloped-roof building in this pass, and the only one so far whose roof
texture is not a plain repeat: the drawing states its own period by
repeating the whole lattice-and-course motif, rows 8..31 again at 32..55,
so the cycle is 24 rows and the roof carries its drawn courses across the
depth instead of a bare lattice. The band below them is the roof's
fascia, wider than the wall it covers, so it belongs to the roof and
lands on the south rim. Note the museum is TWO drawings: this hall and
the east entrance beside it, which is B18 and shares its drawing with the
Route 2 gate.
- The rest of the 2:1 sloped-roof buildings: both gym drawings (the
standard one at Cinnabar, Pewter, Vermilion and Viridian plus the
Fighting Dojo, and the wider Celadon / Cerulean / Saffron one), the 4x2
cottage that houses Mr Fuji, the Cubone house, Bill's grandpa, the Name
Rater and the Viridian school, Cerulean's three wide houses, the day
care, and three scenery blocks -- among them B01, which at 19 placements
is the commonest drawing in the game. Each one's roof band is pixel for
pixel one of the three already authored, so they take that sibling's band
table unchanged: 16 rows for Red's house's, 32 for Oak's lab's, 64 for
the museum's.
- The Safari Zone rest houses and the Victory Road entrance, the first
buildings outside the `OVERWORLD` tileset -- `buildings` is keyed by
tileset and until now only had the one section. The rest house's
corrugated roof repeats every 5 rows rather than the overworld lattice's
8, which is the point of `roofCycle` being authored per building.
- Route 10's scenery block, via a new `seal` field. Its drawing has no
black base course -- it ends on a row of light brick -- so the silhouette
flood climbed in from the south border through the mortar and hollowed
the wall out, leaving 72% of the sprite in 65 pieces. `seal` names the
sides a drawing runs off rather than closing, and the flood does not seed
there: sealed, it is 95% in one piece, and the model is its twin the
museum's. No other building sets it, and none changes by a voxel.
- Together these take the mod to 31 of the catalogue's 34 drawings and 144
of its 147 placements. The last three, and why each resists, are written
up in `assets/docs/buildings/REMAINING.md`.
- A roof no longer runs past the drawing's own silhouette. These sprites
are inset from their boxes, and the columns outside the inset carry no
roof: they now get none, and the fascia belongs to the outermost columns
the drawing actually paints. Before, they raised a four-voxel slab of
black kerb the full depth of the building, flanking its walls at ground
level. Nothing shipped hit this -- Red's house and Oak's lab are drawn
edge to edge -- so no existing model changes by a single voxel.
- Windows and doorways sink a voxel behind their frames, found rather than
listed: a pane is a non-black region the drawing seals off behind its own
black outline. A nested frame (the door's own little window) layers for
free.
- `tools/building_voxels.py`: the reference implementation of the same
algorithm, with the geometric asserts and isometric previews Stage 5 of
the methodology calls for. It and the runtime agree exactly on voxel and
shell counts for all 19 templates -- 96,617 / 12,866 for Red's house up
to 3,715,963 / 146,762 for Silph Co, which ship as 3,410 and 13,994
quads. Its slope asserts read the drawn columns only and stand down for a
roof with no taper, where the check is that the roof is level instead;
its previews fit the projection to the model rather than to a fixed
camera, so a building taller or deeper than the first two still lands on
the canvas.
- A sky at the 75-degree rung. Pitched that far over, the horizon comes
into frame and a good part of the picture is void, so the void gets
filled instead of reading as the black plate it does at every rung below.
@@ -30,12 +176,12 @@ long as the thing throwing them is tall.
- The mod's four controls sit on adjacent keys, and the two that never had
one now have a hotkey at all:
| key | control | was |
| --- | --- | --- |
| 3 | VOXEL, the camera ladder | 6 |
| 5 | V-GRID, the wireframe | menu only |
| 6 | T-SHIFT, the blur ladder | 9 |
| 7 | V-CURVE, the horizon bend | menu only |
| key | control |
| --- | --- |
| 3 | VOXEL, the camera ladder |
| 5 | V-GRID, the wireframe |
| 6 | T-SHIFT, the blur ladder |
| 7 | V-CURVE, the horizon bend |
Only 6 arrives by the documented route. `Game:keypressed` answers the
engine's own display keys first and returns -- 2 COLORS, 3 TILT, 4 ZOOM,
@@ -160,8 +306,203 @@ long as the thing throwing them is tall.
whatever the player left in `options.lua`, and the world view size (which
the light frustum is fitted to) swings 3x across that range.
- A `counter` class (8px, upright): half-cell furniture. One 8px band,
so exactly the drawing's bottom row stands up as the front and every
row above it rides the top face in drawn order. `table`'s 12px could
not be retuned for it; the houses share that class.
- `tilesets.POKECENTER` in `data/voxel_heights.lua`. Before it, the
detector merged the wall-touching counters and healing machines into
the wall band and towered them 3-6 blocks, flattened the machines'
near-black screens to void, read the pillar bases, plant pots and
machine bodies as ponds (the $14/$32/$48 stale-cache water fallback),
boxed each plant pair into one hedge cube, and extruded the lounge
seat -- a PERSON is drawn into its tile art -- into a monolith wearing
his face.
- The pins, by shape: the wall band, windows, poster, pillars and the
16px machine bodies are `wall`; the counters (with the nurse's tray)
are `counter` and the PC's desk is `table`; the machine screens and
the PC are `billboard`, standing on the pinned boxes below them; the
potted plants are `prop` standees like every other interior plant.
- The lounge couch with the man sitting on it is a `counter` box: its
bottom row stands up as the couch's front and the cushion and the man
ride the top face, each drawn exactly once.
He cannot be stood upright, and the reason is structural rather than a
tuning question. His skin pixels span two tile rows and stop dead at
the row 9/10 seam; folding two rows upright requires both to share a
class, which makes the box two tiles deep, and a fully folded box
repeats its north row across its whole top face. So every upright
arrangement puts his head on screen two or three times -- as a 16px
seat-back, on the front and twice more on the top; as a 32px bookcase,
a cabinet taller than the room's own walls. Dropping the seat in front
of him to floor level only changes which copy you see. Nor can he be a
standee: the drawing has no floor margin, so all three non-black
shades touch the cluster rim and the mask drains 307 of its 420
interior pixels -- 46% of him even segmented alone, because his skin
is the same light shade as the couch behind him.
- Survey evidence: full before/after passes of VIRIDIAN_POKECENTER at
15/35/50 degrees, plus spot-checks of CELADON_POKECENTER and
CELADON_HOTEL (shared tileset, both inherit correctly) and of
REDS_HOUSE_1F, OAKS_LAB and VIRIDIAN_CITY (unchanged -- the new class
is additive and no other tileset lists it).
### Changed
- Pinned props are segmented the way the art is authored: objects wear a
black outline, so background is the shades touching the cluster's edge
(white floor around a TV, grey tabletop around a vase) flooded in from
the aprons; the outline, its interior, paint whites and anything they
enclose survive -- pixel-perfect cutouts on any surface.
- Indoor structure analysis floods background from all four aprons and
accepts ground contact on any side (outdoors keeps the south-only rule
that protects roofs), so face-on furniture drawings voxelize per pixel
instead of rising as wall-height volumes.
- Profile-pinned standees are 10px deep (detected props stay 6px), so a
deliberate object like a TV keeps a body at shallow camera angles.
- Authored upright boxes fold their artwork up every face (flanks and
back wear the front stack darkened) and top faces keep the drawn
tabletop: face-on rows wear the row above the fold instead of
repeating their front art lying flat, and a run that folded entirely
tops with the furniture row drawn above it (a bookcase's shelf trim).
- Characters no longer ride a pinned stair tile's class height: stairs
are walked through at floor level, fixing the step-up onto thin air in
front of stairwells.
- A voxelized building is as tall as its facade plus its roof slab rather
than as tall as its drawing: the roof rows are DEPTH now, not height, so
Red's house is 36px over a 4x3-cell plot instead of a 48px cube.
- Round trees (the `cylinder` pin: lone canopies and the border tree
wall) stop being lathes -- the sprite wrapped around a 12-segment
column read as exactly that, art smeared on a barrel. Each cell is now
a real voxel hull: the canopy is segmented out of its cell as the
darkest-pixel outline plus everything it encloses (which also drops
the background grass that used to inflate every row to full width, and
the cast shadow under the ball), and each mask row runs its own span's
circular chord in depth -- the front view is the sprite pixel for
pixel, the plan view is the sprite's width profile turned in depth.
Dithered art with no closed outline (the tree wall) falls back to
light-shades-only flooding, per the methodology doc's boundary rule.
Sides de-outline like building extrusions so flanks read as canopy
rather than solid black, and dome caps keep their outline on the rim
while the interior samples the canopy a couple of rows deeper.
- A `post` standee pool, pinned for the overworld's vertical fence-post
cell (tiles 14/85 -- across every map the pair appears only as this
cell). The detector already turns HORIZONTAL fence runs (tile 57) into
per-post standees, but a vertical run of repeated cells trips its
scenery-repetition guard and fell to the volume path as a
fence-textured tower (Viridian's west line, Route 25).
`post` extracts every CELL as its own cluster -- pooled clustering
would stand the whole line up as one drawing-tall slab at one depth --
and classifies pixels the way the detector does (non-white is body)
rather than by the pinned-prop outline rule, which would strip the
posts to black skeletons; at the detector's own 6px depth, pinned and
detected fences look alike.
- Town signs move from the `billboard` pool to a new `signpost` pool: the
same per-pixel standing slab, but 2 voxels thin instead of 10. A sign
is a plate on a stick, and the standee body that keeps a TV from
vanishing at shallow angles read as a solid block of furniture here.
- The ground under a round tree matches the tree's own drawn background
instead of the map's commonest ground tile. The hull's segmentation
already knows which pixels are NOT the tree; those pixels are scored
against every flat ground tile the map places and the closest art
wins, per template -- so border trees drawn over checker grass stand
on checker grass even on a map that is mostly pale path (the old
fallback painted path under every mid-forest tree, which has no flat
neighbour to vote with). The drawn cast shadow stays out of the score:
no ground tile carries a shadow, and its darks would drag every match.
- Mesh builds stream in the background. `ChunkMesher` queues per-map
build jobs and `pump()` -- driven from the pipeline's update -- runs
them inside a few-millisecond frame budget (`lib/BuildBudget.lua`
suspends the build coroutine mid-loop when the slice is spent). The
camera tween holds at flat until the current map's terrain exists, so
toggling voxel mode shows a handful of flat frames instead of a frozen
one; neighbours pop in as they finish. Warp fades prefetch the
destination (the pipeline update ticks while the Transition covers the
screen, with a wider pump slice), so a door exit lands on terrain that
is already built.
- Vertex packing goes through FFI into one native buffer
(`Mesh:setVertices(ByteData)`) instead of a Lua table per vertex --
the headless table path remains for the pure `geometry()` API and its
suite.
- Round-tree hulls are carved once per (tileset, art, ground set) and
kept as stamps -- template plus cell offset, expanded during vertex
packing -- instead of materialized per-cell quad tables. A route's
border forest was ~500 quads x hundreds of cells of retained heap.
- Mesh and analysis caches evict down to the live neighbourhood (current
map + rendered neighbours, plus one set of history so a house
round-trip keeps the town warm). Evicted meshes are released
explicitly. Memory over Pallet -> Mt Moon: was ~2.9GB and monotonic,
now oscillates between ~90 and 200MB.
- `Voxel3D.SHADOW_KX/KZ` are -0.85 / -0.55, from +0.30 / +0.45: the sun
crosses to the southeast and drops from 62 degrees to 45. The bearing
leans WEST of northwest on purpose -- a character is drawn as a slab
leaning away from the camera, which covers the ground due north of its
feet, so a shadow thrown straight up-screen lands entirely underneath
the figure casting it and is never seen.
- `Voxel3D.SHADOW_ALPHA` 0.32 -> 0.40, a quarter darker.
- `FACE_SHADE` east 0.78 -> 0.84 and west 0.78 -> 0.72. The two were equal
because the old sun sat due northwest and they were symmetric about it;
under a southeastern sun east is a lit flank and west a shaded one.
- A character's shadow lookup runs off the UPRIGHT card the sun saw, not
the leaning slab the camera sees (`Voxel3D.draw`'s `sunModel`). Casting
the leaning slab instead would shrink every shadow to nothing as the
camera flattened toward top-down; looking up with the leaned position
put each sprite's own card across its front.
- The contact-shadow term in `ChunkMesher` was a one-directional stripe
keyed to a northwestern sun -- two neighbours, one corner, top faces
only. It is now the ambient occlusion above.
- The light frustum is fitted to the ground the CAMERA CAN SEE rather than
to a view-sized box around the focus, and both of its margins are now
asymmetric -- for opposite reasons. The camera sits south of its focus
and looks north, so the ground it sees runs far north and barely south;
the sun sits southeast, so the casters for that ground stand south and
east of it. Paying for a view-sized box plus caster margin on all four
sides covered about a third of what was on screen at 75 degrees, and
overpaid at 15.
- Shadows ease off at the frustum's rim instead of ending on it. Past the
low rungs the horizon is further out than any box worth paying for, and
a covered region that simply stops draws a hard line across the middle
distance where every shadow ends at once.
The Pokemon Center interiors. One `POKECENTER` group in
`data/voxel_heights.lua` plus one new class, and because
VIRIDIAN_POKECENTER places every tile the tileset's other maps use, the
one pin set covers all eleven Centers and the Celadon Hotel.
### Fixed
- The generic town-house tileset (`HOUSE` -- Blue's house, Daisy at her
table, and eighteen more homes, the schoolhouse and the trashed house
among them) is now pinned in `data/voxel_heights.lua` the way Red's
rooms already were: the dining table stops towering as a wall-height
volume and sits at table height with its front folded upright, stools
become seat-high boxes that characters sit on, the corner potted
plants become per-pixel standees instead of texture-smeared box
stacks, the bookcases get clean capped tops, and the wall band (with
its window, picture and the schoolhouse blackboard) stays one 16px
face. The schoolhouse's open book stands on the pinned tabletop as a
cutout, and the trashed house's ransacked table corner keeps table
height.
- Interior door mats lie flat again in Red's and the generic houses.
Their collision tile is $14, which the engine's stale-cache fallback
counts as water in every tileset, so the rug recessed into a pond lip;
a `ground` pin now overrides the water read.
- Stray pixels along map seams: ring props (border-tree hulls) whose
quad CENTER sat exactly on a neighbour body's edge line escaped the
strict point-in-rect mask and survived as fragments of otherwise
dropped trees. Object quads now keep/drop by their full extent,
boundary inclusive; props straddling the body edge also stay whole
instead of shedding their outer half.
- The one-step "ledge hop" when crossing a connection into a tree-ringed
map: the seam step stands the player one cell off the new map, where
`Map:cellTile` border-extends into the borderBlock -- a raised tile on
maps ringed with trees. Off-map ground now reads as height 0 (the
departed neighbour's flat walkway, which is what is actually rendered
there).
- Cycling palette modes with voxel mode on eventually killed the pipeline
outright: `attempt to call field 'atlasImageData' (a nil value) --
disabled for this session`. Nothing brought it back short of a restart.
@@ -297,373 +638,6 @@ long as the thing throwing them is tall.
Nothing showed the fault until the voxel wireframe drew the grid those
pixels were supposed to be sitting on.
### Changed
- `Voxel3D.SHADOW_KX/KZ` are -0.85 / -0.55, from +0.30 / +0.45: the sun
crosses to the southeast and drops from 62 degrees to 45. The bearing
leans WEST of northwest on purpose -- a character is drawn as a slab
leaning away from the camera, which covers the ground due north of its
feet, so a shadow thrown straight up-screen lands entirely underneath
the figure casting it and is never seen.
- `Voxel3D.SHADOW_ALPHA` 0.32 -> 0.40, a quarter darker.
- `FACE_SHADE` east 0.78 -> 0.84 and west 0.78 -> 0.72. The two were equal
because the old sun sat due northwest and they were symmetric about it;
under a southeastern sun east is a lit flank and west a shaded one.
- A character's shadow lookup runs off the UPRIGHT card the sun saw, not
the leaning slab the camera sees (`Voxel3D.draw`'s `sunModel`). Casting
the leaning slab instead would shrink every shadow to nothing as the
camera flattened toward top-down; looking up with the leaned position
put each sprite's own card across its front.
- The contact-shadow term in `ChunkMesher` was a one-directional stripe
keyed to a northwestern sun -- two neighbours, one corner, top faces
only. It is now the ambient occlusion above.
- The light frustum is fitted to the ground the CAMERA CAN SEE rather than
to a view-sized box around the focus, and both of its margins are now
asymmetric -- for opposite reasons. The camera sits south of its focus
and looks north, so the ground it sees runs far north and barely south;
the sun sits southeast, so the casters for that ground stand south and
east of it. Paying for a view-sized box plus caster margin on all four
sides covered about a third of what was on screen at 75 degrees, and
overpaid at 15.
- Shadows ease off at the frustum's rim instead of ending on it. Past the
low rungs the horizon is further out than any box worth paying for, and
a covered region that simply stops draws a hard line across the middle
distance where every shadow ends at once.
The Pokemon Center interiors. One `POKECENTER` group in
`data/voxel_heights.lua` plus one new class, and because
VIRIDIAN_POKECENTER places every tile the tileset's other maps use, the
one pin set covers all eleven Centers and the Celadon Hotel.
### Added
- A `counter` class (8px, upright): half-cell furniture. One 8px band,
so exactly the drawing's bottom row stands up as the front and every
row above it rides the top face in drawn order. `table`'s 12px could
not be retuned for it; the houses share that class.
- `tilesets.POKECENTER` in `data/voxel_heights.lua`. Before it, the
detector merged the wall-touching counters and healing machines into
the wall band and towered them 3-6 blocks, flattened the machines'
near-black screens to void, read the pillar bases, plant pots and
machine bodies as ponds (the $14/$32/$48 stale-cache water fallback),
boxed each plant pair into one hedge cube, and extruded the lounge
seat -- a PERSON is drawn into its tile art -- into a monolith wearing
his face.
- The pins, by shape: the wall band, windows, poster, pillars and the
16px machine bodies are `wall`; the counters (with the nurse's tray)
are `counter` and the PC's desk is `table`; the machine screens and
the PC are `billboard`, standing on the pinned boxes below them; the
potted plants are `prop` standees like every other interior plant.
- The lounge couch with the man sitting on it is a `counter` box: its
bottom row stands up as the couch's front and the cushion and the man
ride the top face, each drawn exactly once.
He cannot be stood upright, and the reason is structural rather than a
tuning question. His skin pixels span two tile rows and stop dead at
the row 9/10 seam; folding two rows upright requires both to share a
class, which makes the box two tiles deep, and a fully folded box
repeats its north row across its whole top face. So every upright
arrangement puts his head on screen two or three times -- as a 16px
seat-back, on the front and twice more on the top; as a 32px bookcase,
a cabinet taller than the room's own walls. Dropping the seat in front
of him to floor level only changes which copy you see. Nor can he be a
standee: the drawing has no floor margin, so all three non-black
shades touch the cluster rim and the mask drains 307 of its 420
interior pixels -- 46% of him even segmented alone, because his skin
is the same light shade as the couch behind him.
- Survey evidence: full before/after passes of VIRIDIAN_POKECENTER at
15/35/50 degrees, plus spot-checks of CELADON_POKECENTER and
CELADON_HOTEL (shared tileset, both inherit correctly) and of
REDS_HOUSE_1F, OAKS_LAB and VIRIDIAN_CITY (unchanged -- the new class
is additive and no other tileset lists it).
## 1.3.0
Terrain meshing goes asynchronous, instanced and bounded. The first voxel
frame used to build every neighbourhood map synchronously (a ~2.4s
freeze), retain every map's analysis forever (gigabytes over a
cross-region trek), and string stray pixels along map seams.
### Changed
- Mesh builds stream in the background. `ChunkMesher` queues per-map
build jobs and `pump()` -- driven from the pipeline's update -- runs
them inside a few-millisecond frame budget (`lib/BuildBudget.lua`
suspends the build coroutine mid-loop when the slice is spent). The
camera tween holds at flat until the current map's terrain exists, so
toggling voxel mode shows a handful of flat frames instead of a frozen
one; neighbours pop in as they finish. Warp fades prefetch the
destination (the pipeline update ticks while the Transition covers the
screen, with a wider pump slice), so a door exit lands on terrain that
is already built.
- Vertex packing goes through FFI into one native buffer
(`Mesh:setVertices(ByteData)`) instead of a Lua table per vertex --
the headless table path remains for the pure `geometry()` API and its
suite.
- Round-tree hulls are carved once per (tileset, art, ground set) and
kept as stamps -- template plus cell offset, expanded during vertex
packing -- instead of materialized per-cell quad tables. A route's
border forest was ~500 quads x hundreds of cells of retained heap.
- Mesh and analysis caches evict down to the live neighbourhood (current
map + rendered neighbours, plus one set of history so a house
round-trip keeps the town warm). Evicted meshes are released
explicitly. Memory over Pallet -> Mt Moon: was ~2.9GB and monotonic,
now oscillates between ~90 and 200MB.
### Fixed
- Stray pixels along map seams: ring props (border-tree hulls) whose
quad CENTER sat exactly on a neighbour body's edge line escaped the
strict point-in-rect mask and survived as fragments of otherwise
dropped trees. Object quads now keep/drop by their full extent,
boundary inclusive; props straddling the body edge also stay whole
instead of shedding their outer half.
- The one-step "ledge hop" when crossing a connection into a tree-ringed
map: the seam step stands the player one cell off the new map, where
`Map:cellTile` border-extends into the borderBlock -- a raised tile on
maps ringed with trees. Off-map ground now reads as height 0 (the
departed neighbour's flat walkway, which is what is actually rendered
there).
## 1.2.0
Buildings stop being boxes wearing their own elevation. A profiled
building is voxelized from its own sprite, band by band -- the pipeline
written up in `assets/docs/buidling_to_voxel/`.
### Added
- `lib/Buildings.lua`: a building archetype. Where the volume path folds a
whole drawing upright (roof, facade and sloped ends alike) into one box,
this classifies each BAND of the drawing by the 3D surface it depicts and
applies the matching operation: top-facing rows lay flat over the
footprint, the facade extrudes straight back, an awning band juts past
the walls, and the drawn taper at the ends becomes a stepped slope in
elevation. Every visible voxel carries a real texel of the drawing, so
the model recolours with the atlas.
- A `buildings` section in `data/voxel_heights.lua`. A building is matched
by its exact tile grid (the drawings are catalogued in `assets/docs/buildings/`),
so one entry covers every map that places the same art -- Red's house,
Blue's house, Bill's, the Copycat's and the two Fuchsia houses are one
seven-placement entry, and Oak's lab is a second. Only the band table is
authored; the silhouette, the taper rate, the eave height and every
window and doorway are measured off the pixels.
- The flat-roofed civic block and its sixteen relatives -- every Pokemon
Center and every Poke Mart, Fuchsia Gym, the museum, the Game Corner,
Celadon Mansion and its department store, the Power Plant, the Route 5
and Route 22 gates, Silph Co and five anonymous scenery blocks. They are
one architecture drawn at eleven different footprints, from 4x4 cells up
to Silph Co's 8x12, and they share one band table: their lattice is drawn
from straight above, so the measured taper comes out flat and the whole
band is depth under one level roof. No new roof mode was needed -- the
drawn profile was always the shape, and a drawing with no taper simply
yields a level one.
- Pewter's museum hall (`assets/docs/buildings/B24`). It is the one
sloped-roof building in this pass, and the only one so far whose roof
texture is not a plain repeat: the drawing states its own period by
repeating the whole lattice-and-course motif, rows 8..31 again at 32..55,
so the cycle is 24 rows and the roof carries its drawn courses across the
depth instead of a bare lattice. The band below them is the roof's
fascia, wider than the wall it covers, so it belongs to the roof and
lands on the south rim. Note the museum is TWO drawings: this hall and
the east entrance beside it, which is B18 and shares its drawing with the
Route 2 gate.
- The rest of the 2:1 sloped-roof buildings: both gym drawings (the
standard one at Cinnabar, Pewter, Vermilion and Viridian plus the
Fighting Dojo, and the wider Celadon / Cerulean / Saffron one), the 4x2
cottage that houses Mr Fuji, the Cubone house, Bill's grandpa, the Name
Rater and the Viridian school, Cerulean's three wide houses, the day
care, and three scenery blocks -- among them B01, which at 19 placements
is the commonest drawing in the game. Each one's roof band is pixel for
pixel one of the three already authored, so they take that sibling's band
table unchanged: 16 rows for Red's house's, 32 for Oak's lab's, 64 for
the museum's.
- The Safari Zone rest houses and the Victory Road entrance, the first
buildings outside the `OVERWORLD` tileset -- `buildings` is keyed by
tileset and until now only had the one section. The rest house's
corrugated roof repeats every 5 rows rather than the overworld lattice's
8, which is the point of `roofCycle` being authored per building.
- Route 10's scenery block, via a new `seal` field. Its drawing has no
black base course -- it ends on a row of light brick -- so the silhouette
flood climbed in from the south border through the mortar and hollowed
the wall out, leaving 72% of the sprite in 65 pieces. `seal` names the
sides a drawing runs off rather than closing, and the flood does not seed
there: sealed, it is 95% in one piece, and the model is its twin the
museum's. No other building sets it, and none changes by a voxel.
- Together these take the mod to 31 of the catalogue's 34 drawings and 144
of its 147 placements. The last three, and why each resists, are written
up in `assets/docs/buildings/REMAINING.md`.
- A roof no longer runs past the drawing's own silhouette. These sprites
are inset from their boxes, and the columns outside the inset carry no
roof: they now get none, and the fascia belongs to the outermost columns
the drawing actually paints. Before, they raised a four-voxel slab of
black kerb the full depth of the building, flanking its walls at ground
level. Nothing shipped hit this -- Red's house and Oak's lab are drawn
edge to edge -- so no existing model changes by a single voxel.
- Windows and doorways sink a voxel behind their frames, found rather than
listed: a pane is a non-black region the drawing seals off behind its own
black outline. A nested frame (the door's own little window) layers for
free.
- `tools/building_voxels.py`: the reference implementation of the same
algorithm, with the geometric asserts and isometric previews Stage 5 of
the methodology calls for. It and the runtime agree exactly on voxel and
shell counts for all 19 templates -- 96,617 / 12,866 for Red's house up
to 3,715,963 / 146,762 for Silph Co, which ship as 3,410 and 13,994
quads. Its slope asserts read the drawn columns only and stand down for a
roof with no taper, where the check is that the roof is level instead;
its previews fit the projection to the model rather than to a fixed
camera, so a building taller or deeper than the first two still lands on
the canvas.
### Changed
- A voxelized building is as tall as its facade plus its roof slab rather
than as tall as its drawing: the roof rows are DEPTH now, not height, so
Red's house is 36px over a 4x3-cell plot instead of a 48px cube.
- Round trees (the `cylinder` pin: lone canopies and the border tree
wall) stop being lathes -- the sprite wrapped around a 12-segment
column read as exactly that, art smeared on a barrel. Each cell is now
a real voxel hull: the canopy is segmented out of its cell as the
darkest-pixel outline plus everything it encloses (which also drops
the background grass that used to inflate every row to full width, and
the cast shadow under the ball), and each mask row runs its own span's
circular chord in depth -- the front view is the sprite pixel for
pixel, the plan view is the sprite's width profile turned in depth.
Dithered art with no closed outline (the tree wall) falls back to
light-shades-only flooding, per the methodology doc's boundary rule.
Sides de-outline like building extrusions so flanks read as canopy
rather than solid black, and dome caps keep their outline on the rim
while the interior samples the canopy a couple of rows deeper.
- A `post` standee pool, pinned for the overworld's vertical fence-post
cell (tiles 14/85 -- across every map the pair appears only as this
cell). The detector already turns HORIZONTAL fence runs (tile 57) into
per-post standees, but a vertical run of repeated cells trips its
scenery-repetition guard and fell to the volume path as a
fence-textured tower (Viridian's west line, Route 25).
`post` extracts every CELL as its own cluster -- pooled clustering
would stand the whole line up as one drawing-tall slab at one depth --
and classifies pixels the way the detector does (non-white is body)
rather than by the pinned-prop outline rule, which would strip the
posts to black skeletons; at the detector's own 6px depth, pinned and
detected fences look alike.
- Town signs move from the `billboard` pool to a new `signpost` pool: the
same per-pixel standing slab, but 2 voxels thin instead of 10. A sign
is a plate on a stick, and the standee body that keeps a TV from
vanishing at shallow angles read as a solid block of furniture here.
- The ground under a round tree matches the tree's own drawn background
instead of the map's commonest ground tile. The hull's segmentation
already knows which pixels are NOT the tree; those pixels are scored
against every flat ground tile the map places and the closest art
wins, per template -- so border trees drawn over checker grass stand
on checker grass even on a map that is mostly pale path (the old
fallback painted path under every mid-forest tree, which has no flat
neighbour to vote with). The drawn cast shadow stays out of the score:
no ground tile carries a shadow, and its darks would drag every match.
### Fixed
- The generic town-house tileset (`HOUSE` -- Blue's house, Daisy at her
table, and eighteen more homes, the schoolhouse and the trashed house
among them) is now pinned in `data/voxel_heights.lua` the way Red's
rooms already were: the dining table stops towering as a wall-height
volume and sits at table height with its front folded upright, stools
become seat-high boxes that characters sit on, the corner potted
plants become per-pixel standees instead of texture-smeared box
stacks, the bookcases get clean capped tops, and the wall band (with
its window, picture and the schoolhouse blackboard) stays one 16px
face. The schoolhouse's open book stands on the pinned tabletop as a
cutout, and the trashed house's ransacked table corner keeps table
height.
- Interior door mats lie flat again in Red's and the generic houses.
Their collision tile is $14, which the engine's stale-cache fallback
counts as water in every tileset, so the rug recessed into a pond lip;
a `ground` pin now overrides the water read.
## 1.1.0
Interior furniture gets the shapes it depicts
(mods/DRAMATIC_SHAPE/tools/voxel-survey.md is the procedure that found and
verified these).
### Added
- Furniture shape classes in the profile: `bed` (a low slab wearing its
top-down art), `table` and `desk` (boxes at their drawn height whose
faces fold the artwork up), `relief` (a prop drawn from above -- a
game console -- lying flat and extruding a few voxels inside its
outline), the standee pools `billboard` (10px) / `prop` (5px) /
`stool` (5px, and characters standing on its walkable cell sit at
seat height) / `cutout` (one voxel: pure profile, for the vase on the
table), and the stair archetypes `stair_e`/`stair_w` (a rising flight
of real steps) and `stair_down_e`/`stair_down_w` (a sunken stairwell
descending below the floor -- stairs that lead down). Separate pools
cluster separately, so touching drawings never merge into one cutout.
- Standee clusters split into per-pixel connected components, each
standing on its own feet in the depth band of the row it is drawn in:
two stools stacked in adjacent cells become two stools, and no
fragment of a drawing ever floats at its bounding-box height. A
`cutout` keeps only its largest component -- a cast shadow's drawn
edge is background, not a floating scrap.
- `bookcase` class: free-standing shelf drawings collapse in ranks onto
one-cell-deep boxes at their full drawn height, back rows becoming
hidden floor; the trim row above a rank -- undetected structure or a
row pinned `table`, since the same trim tiles cap other furniture --
is adopted as its cap. Pinned for Oak's Lab (`DOJO`).
- Oak's Lab tables pinned `table`: the starter-ball display and the
north tables stand at real table height, the display frame's black
corner brackets no longer auto-extract into standing prisms, and the
Poke Ball / Pokedex sprites ride the authored height onto the
tabletops.
- Pinned props drawn directly above a pinned box stand ON it: the PC
monitor on its desk, the flower pot on the dining table.
- Profile pins for `REDS_HOUSE_1` / `REDS_HOUSE_2` (Red's house and the
Copycat's, both floors): bed, stools, tables, PC desk with its
standing monitor, bookcases, TV standing on the floor behind the game
console's relief, potted plant, flower pot, both staircases, and the
wall/window band.
- `mods/DRAMATIC_SHAPE/tests/voxel_survey.lua`: screenshot-survey driver
behind the repeatable inspection procedure (SURVEY_MAP / SURVEY_SPOTS /
SURVEY_LEVELS / SHOT_DIR), documented in
mods/DRAMATIC_SHAPE/tools/voxel-survey.md.
### Changed
- Pinned props are segmented the way the art is authored: objects wear a
black outline, so background is the shades touching the cluster's edge
(white floor around a TV, grey tabletop around a vase) flooded in from
the aprons; the outline, its interior, paint whites and anything they
enclose survive -- pixel-perfect cutouts on any surface.
- Indoor structure analysis floods background from all four aprons and
accepts ground contact on any side (outdoors keeps the south-only rule
that protects roofs), so face-on furniture drawings voxelize per pixel
instead of rising as wall-height volumes.
- Profile-pinned standees are 10px deep (detected props stay 6px), so a
deliberate object like a TV keeps a body at shallow camera angles.
- Authored upright boxes fold their artwork up every face (flanks and
back wear the front stack darkened) and top faces keep the drawn
tabletop: face-on rows wear the row above the fold instead of
repeating their front art lying flat, and a run that folded entirely
tops with the furniture row drawn above it (a bookcase's shelf trim).
- Characters no longer ride a pinned stair tile's class height: stairs
are walked through at floor level, fixing the step-up onto thin air in
front of stairwells.
## 1.0.0
First release, ported from the engine-internal voxel branch onto the
`render_pipelines` mod API.
### Added
- `voxel` render pipeline: 3D diorama overworld with extruded terrain,
depth-buffered occlusion, leaning sprite billboards, drop shadows and
contact AO. VOXEL options row and hotkey `6` (OFF / 15 / 35 / 50).
- `tiltshift` render pipeline: a `worldPresent` post-process giving the
miniature-photo look. T-SHIFT options row and hotkey `9` (OFF / 1 / 2 / 3).
- Carved voxel models for 67 overworld sprites, plus the
`tools/build_voxels.py` that produced them.
- `data/voxel_heights.lua`, the hand-authored tile shape profile.
### Changed from the pre-mod version
- The level is no longer the mod's to keep. The engine owns the ladder, the
@@ -671,7 +645,7 @@ First release, ported from the engine-internal voxel branch onto the
keeps only the camera-angle tween.
- Persistence moved from `save.options.voxel` / `save.options.tiltshift` to
`save.options.pipelines.voxel` / `.tiltshift`.
- Hotkeys moved from `4`/`9` to `6`/`9`: the fork already uses `4` for
- Hotkeys moved from `4`/`9` to `3`/`6`: the fork already uses `4` for
survey zoom.
- The tilt-shift pass is a declared `worldPresent` stage rather than a call
spliced into the world draw, so it composes with any world pipeline