diff --git a/.modkitignore b/.modkitignore new file mode 100644 index 0000000..c40bb83 --- /dev/null +++ b/.modkitignore @@ -0,0 +1,30 @@ +# Files that stay in the repo and out of the distributed package. +# Exact relative paths, one per line -- modkit matches literally, no globs. + +# The SDK suite and the probes it grew out of. A shipped test that requires +# an engine module reads as a private require against the archive +# (CONTRIBUTING-mods.md "What the PR must contain", 2). +tests/dramatic_shape_test.lua +tests/voxel_anim_probe.lua +tests/voxel_door_probe.lua +tests/voxel_mem_probe.lua +tests/voxel_perf_probe.lua +tests/voxel_pipeline_probe.lua +tests/voxel_seam_shots.lua +tests/voxel_shadow_probe.lua +tests/voxel_survey.lua +tests/voxel_void_probe.lua + +# Authoring tools. These run by hand on a machine that has imported its own +# ROM, read the local cache, and emit assets/voxels/*.lua; the carved models +# are what a player installs, the carving is not. +tools/build_voxels.py +tools/building_images.py +tools/building_voxels.py +tools/voxel-survey.md +assets/docs/buidling_to_voxel/sprite_extract.py +assets/docs/buidling_to_voxel/voxel_build_verify.py + +# assets/docs/**.md stays in: data/voxel_heights.lua points a modder at the +# building catalogue, and the methodology is where the geometry's provenance +# is written down. diff --git a/CHANGELOG.md b/CHANGELOG.md index a2b074e..186eede 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 596adea..de48a41 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Dramatic Shape Voxel Mod +A mod for the [Pokémon Gen 1 Recompilation +Project](https://github.com/bryanthaboi/pokemon-gen1-recomp-project). + The overworld as a 3D diorama. Terrain is extruded into real geometry, occlusion comes from a depth buffer rather than a y-sort, characters stand as leaning sprite slabs, a shadow map throws real cast shadows across @@ -11,7 +14,10 @@ or scripts — it changes what the world *looks* like and nothing about what it *is*. Battles, menus and cutscenes are untouched; only the free-roam overworld draws differently. -## Using it +## Controls + +Every key is free-roam only, and each one is also a row on the OPTIONS +menu. | control | does | | --- | --- | @@ -20,146 +26,7 @@ overworld draws differently. | `6`, or the **T-SHIFT** options row | OFF → 1 → 2 → 3 → OFF (miniature blur) | | `7`, or the **V-CURVE** options row | OFF → 1 → 2 → 3 — bend the world over the horizon | -The four keys sit together, and two of them are taken off the engine: `3` -was **TILT** and `5` was **GBC FX**. Neither is reachable by key while this -mod is enabled, and both are still on the OPTIONS menu. Every key is -free-roam only, and a screen that handles its own keys keeps them, so -nothing here fires while a menu or a text box has focus. - -**`3` also turns TILT and GBC FX off**, on every press — including the one -that cycles the ladder back round to OFF. Both fight the diorama: TILT is -the flat fake of what this mode does for real, and GBC FX lays a -full-screen present pass over the top of it. Since `3` is now the only key -that reaches either, it is also the way back from having left one on. - -75 degrees is a rung the engine's flat **TILT** could not have: it projects -the world canvas as one rigid plane, which degenerates into a horizon line -down there, where real geometry only gets more of itself to show. - -That rung is also the one that gets a **sky**. Pitched that far over the -horizon comes into frame, so the void behind the diorama is filled rather -than left as the black plate it reads as at every rung below — enough to -stand the world under something without a skybox to draw. Outdoors only: a -house, a cave or a gym is a room with a ceiling, and the void past its -walls is the outside of a box, not open air. The colour follows the display -mode like everything else here — blue in the colour modes, grey under -GRAY, green under CLASSIC — and fades in with the camera tween rather than -snapping on at the keypress. - -V-CURVE is the *curved world* — every vertex pushed down by the square of -its horizontal distance from the camera's focus, so the ground under you -stays flat and the map bends away over a near horizon. It is not a fisheye: -a fisheye is a lens, a screen-space warp that bends straight lines -everywhere and resamples every pixel to do it. This bends the geometry, so -lines near the camera stay straight and the art stays crisp. - -Both pipeline levels persist in `save.options.pipelines`, and V-GRID and -V-CURVE in `save.options.modOptions.DRAMATIC_SHAPE` — the same place the mod -manager's own settings page for this mod reads, so each row is one setting -in two places. VOXEL and the engine's **TILT** are two answers to the same -question, so switching one on switches the other off. - -Without shader or depth-canvas support the rows still cycle but the world -stays 2D — the mode reports itself unavailable and the engine keeps the -vanilla path. - -## How it is built - -This is a *rendering pipeline* mod (see `docs/modding.md`). `main.lua` -registers two records in the engine's `render_pipelines` registry and -writes almost nothing else — the ladder, the options rows, persistence and -the free-roam gate are all engine plumbing driven from those records: - -- **`voxel`** — a `drawWorld` pipeline. Replaces the world pass with a - rendered 3D scene and returns it as one window-resolution canvas. -- **`tiltshift`** — a `worldPresent` pipeline. Post-processes the finished - world *before* the UI composites, so the diorama blurs and the text boxes - in front of it stay crisp. - -Everything else lives in `lib/`, loaded through the mod's own namespace -(`V.require`) rather than `package.path`, which a mod directory is not on: - -| module | role | -| --- | --- | -| `Voxel3D` | the 3D pass: shader, depth buffer, camera, projection | -| `ShadowMap` | the sun's own pass: what it cannot see is in shadow | -| `WorldCurve` | the curved world: the Animal Crossing horizon | -| `VoxelGrid` | the wireframe toggle | -| `ModSetting` | a setting of this mod's own: ladder, store, options row | -| `Mat4` | the 4x4 matrix math the camera and model transforms need | -| `VoxelState` | the camera angle and its ease; the engine owns the level | -| `VoxelScene` | assembles one frame: shadows, terrain, characters, grass | -| `ChunkMesher` | a map's block layer → a terrain mesh, cached per map | -| `Structures` | flood-fills drawn things and decides how tall each is | -| `Buildings` | a profiled building sprite → a voxel model, band by band | -| `TileShape` | resolves every tile to an extrusion class and art mode | -| `TerrainAtlas` | the texture geometry samples: SGB palette bakes, and the animated water/flower tiles | -| `SpriteBillboards` | a sprite frame → a leaning slab with side relief | -| `VoxelModels` | the carved character models in `assets/voxels/` | -| `TiltShift` | the two-pass separable blur and saturation lift | - -`data/voxel_heights.lua` is the hand-authored shape profile: listing a tile -there pins its height and art mode over the automatic detector, and its -`buildings` section names whole building sprites to voxelize band by band -(`assets/docs/buidling_to_voxel/` is the pipeline; `assets/docs/buildings/` catalogues the -drawings). Another mod can pin its own tiles by shipping the same file, and -can replace a character's model with `overrides/voxels/.lua` — models -resolve through the engine's asset search path before falling back to ours. - -`tools/build_voxels.py` is what carved `assets/voxels/` out of the -overworld sprite sheets. `tools/building_voxels.py` is the reference -implementation of the building voxelizer — it builds the same models -offline, asserts their geometry and renders isometric previews, and its -voxel and shell counts are what `lib/Buildings.lua` is checked against. - -## Engine seams it relies on - -The mod reaches into a few engine internals (hence `"permissions": -["engine_internals"]`). All but the last are read-only: - -- `Game:keypressed` — **wrapped, not read.** The only thing here that - changes engine behaviour, and the one seam worth arguing about. The - engine answers its own display keys and returns before a mod's pipeline - is offered the key, expressly so a pipeline cannot shadow one — so `3` - (TILT) and `5` (GBC FX) cannot be claimed the documented way, and V-GRID - and V-CURVE own no render pass and have no registry to claim from at all. - Only `6` arrives by the supported route. Polling the keyboard instead - would fire *alongside* the engine's handler rather than instead of it, so - `3` would cycle both this mode and TILT on one press. The wrapper - delegates everything it can: `Pipelines.hotkey` still applies its own - gate and ladder, the settings borrow the voxel pipeline's free-roam gate, - unclaimed keys pass straight through, and a screen with its own handler - keeps the keyboard. -- `Player:pose()` / `NPC:pose()` — the sheet, position, facing and step - phase a frame renders to, so 2D and 3D can never disagree about a pose. -- `SpriteRenderer:resolveImage()` and `SpriteRenderer.STAND/WALK` — the - live sprite image and frame tables, so palette modes and sprite-replacing - mods apply to the geometry with no second copy of the logic. -- `TileRenderer.recolorSample` — the exact shade cutoffs the 2D palette - shader uses, so baked terrain lands on the same colours as flat tiles. -- `TileRenderer.animFrame()` / `.atlasImageData()` / `.defaultAnimatedTiles()` - — the tile-animation clock, the pixels behind the atlas texture, and the - vanilla water/flower spec. A static mesh cannot overdraw animated cells - the way the tile layer does, so it animates the texture instead, and - these are what let it do that on the engine's own clock and colours. - - Only `defaultAnimatedTiles` is required. The other two are OPTIONAL and - read guarded, because an engine build is free not to carry them — this - one does not carry either. Missing `animFrame` freezes the tile clock at - step 0 (terrain stops animating in voxel mode; everything else is - unaffected). Missing `atlasImageData` costs nothing where the mod baked - the atlas itself, falls back to the tileset art where the engine is - drawing that art unmodified, and skips animating a RED++ per-map bake, - whose pixels are unreachable without the seam. None of the three may ever - be called unguarded: a throw inside `drawWorld` costs the player the whole - pipeline for the session, not one frame of moving water. - -## Tests - -```sh -luajit mods/DRAMATIC_SHAPE/tests/dramatic_shape_test.lua -``` - -Runs headless: the mod loads, both pipelines land in the registry with the -shape the engine dispatches on, the ladders and persistence round-trip, and -the whole thing stays inert on a machine with no 3D support. +Two of those keys are taken off the engine: `3` was **TILT** and `5` was +**GBC FX**. Neither is reachable by key while this mod is enabled, and both +are still on the OPTIONS menu. Pressing `3` also switches both off — they +fight the diorama, and it is the way back from having left one on. \ No newline at end of file diff --git a/mod.card b/mod.card index 9e5f37c..e53f6e2 100644 --- a/mod.card +++ b/mod.card @@ -2,8 +2,8 @@ -- and the manager detail pane; never by the loader's merge. return { summary = "The overworld as a 3D diorama: extruded terrain, real occlusion, voxel characters, tilt-shift miniature blur.", - author = "Pokemon Gen 1 Recompilation Project", - contact = "https://github.com/bryanthaboi/pokemon-gen1-recomp-project", + author = "DramaticShape", + contact = "https://github.com/DramaticShape/DRAMATIC_SHAPE", tags = { "graphics", "3d", "voxel", "render-pipeline", "presentation" }, differences = { changed = { @@ -14,7 +14,7 @@ return { "the VOXEL key (3) turns TILT and GBC FX off on every press -- both fight the diorama, and 3 is now the only key that reaches either", }, added = { - "VOXEL options row and hotkey 3 (OFF / 15 / 35 / 50 degrees)", + "VOXEL options row and hotkey 3 (OFF / 15 / 35 / 50 / 75 degrees)", "T-SHIFT options row and hotkey 6 (OFF / 1 / 2 / 3), the miniature blur", "V-GRID on hotkey 5 and V-CURVE on hotkey 7", "a sky behind the diorama at the 75-degree rung, outdoor maps only, coloured by the active palette mode", @@ -30,5 +30,5 @@ return { credits = { { who = "pret/pokered", for_ = "the tile and sprite data the geometry is derived from" }, }, - compat = { engine = ">=0.1.0 <2.0.0", modApi = 2 }, + compat = { engine = ">=0.1.28 <2.0.0", modApi = 2 }, }