DramaticShapeVoxelMod archival mirror · read-only
752653e243a05e32b5c005ff836d628076daaa18

update oak's pc

DramaticShape · 2026-08-01 · parent 6887f5d951

update oak's pc

Changed files

data/voxel_heights.lua   |  49 +++++++++++++----
 lib/Buildings.lua        | 143 ++++++++++++++++++++++++++++++++++++++++++++++++
 tools/building_voxels.py | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 364 insertions(+), 27 deletions(-)

Diff

diff --git a/data/voxel_heights.lua b/data/voxel_heights.lua
index 7ee4004..cbf4fda 100644
--- a/data/voxel_heights.lua
+++ b/data/voxel_heights.lua
@@ -3270,16 +3270,21 @@ return {
         roofRows = 19, roofBack = 16, roofFront = 0, roofCycle = { 2, 13 },
         slab = 3, frontEave = 0, ledge = nil, depth = 2,
       },
-      -- F02: the computer bank on the lab's west table (OAKS_LAB cell
-      -- 0,1) -- the one FACADE-ONLY template: rows 0-14 are machines
-      -- drawn face-on (monitor and keys, the centre console, the right
-      -- cabinet) over the lab-table fascia and base bands, and nothing
-      -- is drawn from above. roofRows = 0, so the whole drawing
-      -- extrudes -- the house facade treatment with no roof band; the
-      -- roof fields are inert and the recess pass sinks every framed
-      -- pane (screens, keys, the cabinet's inset face) into relief on
-      -- its own. Same drawing, same grid, stands in the Hall of Fame
-      -- on the GYM atlas -- registered there below.
+      -- F02: the computer desk on the lab's west side (OAKS_LAB cell
+      -- 0,1) -- the one DESK-SET template: the pipeline's region
+      -- classification at PART granularity (see Buildings
+      -- deskSetModel). The desk is the sibling lab table (fascia rows
+      -- 16-18, base 19-21); on it stand a monitor over its keyboard
+      -- (left), a computer tower over a keyboard and mouse (middle),
+      -- and a sheet of paper LYING FLAT (right). Upright parts anchor
+      -- their drawn bottom row to the desk's top plane and wear their
+      -- own drawn tops as lids; flat parts lie one voxel proud at
+      -- drawn row = depth row -- the same 1:1 the tabletop itself is
+      -- drawn with. The roof fields are inert (roofRows = 0 keeps the
+      -- recess scan over the whole drawing, which is what sinks the
+      -- monitor's screen and the tower's slots). Same drawing, same
+      -- grid, stands in the Hall of Fame on the GYM atlas --
+      -- registered there below.
       {
         id = "lab_computers",
         tiles = {
@@ -3289,6 +3294,16 @@ return {
         },
         roofRows = 0, roofBack = 0, roofFront = 0, roofCycle = { 0, 0 },
         slab = 0, frontEave = 0, ledge = nil, depth = 2,
+        desk = { fascia = { 16, 18 }, base = { 19, 21 } },
+        parts = {
+          { kind = "upright", x = { 2, 13 }, top = { 0, 2 },
+            facade = { 3, 10 }, depth = 4 },              -- the monitor
+          { kind = "flat", x = { 1, 13 }, rows = { 11, 14 } },  -- keyboard
+          { kind = "upright", x = { 14, 21 }, top = { 0, 3 },
+            facade = { 4, 10 }, depth = 6 },              -- the tower
+          { kind = "flat", x = { 14, 21 }, rows = { 11, 14 } }, -- keys+mouse
+          { kind = "flat", x = { 22, 30 }, rows = { 1, 14 } },  -- the paper
+        },
       },
       -- F03: the empty north table beside it (OAKS_LAB cell 2,1): the
       -- starter table's band table verbatim on a grid two tiles
@@ -3307,8 +3322,8 @@ return {
 
     GYM = {
       -- F02 again: the Hall of Fame's recording machine is the lab's
-      -- computer bank drawing, tile for tile, on the GYM atlas (one
-      -- placement: HALL_OF_FAME cell 4,1). Same band table as the DOJO
+      -- computer desk drawing, tile for tile, on the GYM atlas (one
+      -- placement: HALL_OF_FAME cell 4,1). Same part table as the DOJO
       -- entry above.
       {
         id = "lab_computers",
@@ -3319,6 +3334,16 @@ return {
         },
         roofRows = 0, roofBack = 0, roofFront = 0, roofCycle = { 0, 0 },
         slab = 0, frontEave = 0, ledge = nil, depth = 2,
+        desk = { fascia = { 16, 18 }, base = { 19, 21 } },
+        parts = {
+          { kind = "upright", x = { 2, 13 }, top = { 0, 2 },
+            facade = { 3, 10 }, depth = 4 },              -- the monitor
+          { kind = "flat", x = { 1, 13 }, rows = { 11, 14 } },  -- keyboard
+          { kind = "upright", x = { 14, 21 }, top = { 0, 3 },
+            facade = { 4, 10 }, depth = 6 },              -- the tower
+          { kind = "flat", x = { 14, 21 }, rows = { 11, 14 } }, -- keys+mouse
+          { kind = "flat", x = { 22, 30 }, rows = { 1, 14 } },  -- the paper
+        },
       },
     },
   },
diff --git a/lib/Buildings.lua b/lib/Buildings.lua
index 644cb09..d378ec7 100644
--- a/lib/Buildings.lua
+++ b/lib/Buildings.lua
@@ -307,11 +307,154 @@ end
 
 -- ----------------------------------------------------------------- build --
 
+-- A desk with separately-classified objects on it (a template's `parts`
+-- list): the methodology's region classification at part granularity.
+-- Upright parts anchor their drawn bottom row to the desk's top plane
+-- and wear their own drawn tops as lids; flat parts (a keyboard, a
+-- sheet of paper) lie one voxel proud at drawn row = depth row -- the
+-- same 1:1 the tabletop itself is drawn with, so an object's height ON
+-- the drawing is its position ON the desk. The desk is the lab-table
+-- slab + base; its lid is the one synthesized surface in the model
+-- (the objects cover every drawn pixel of the tabletop), continued
+-- from the sibling tables' pattern in the drawing's own shades.
+-- tools/building_voxels.py `build_desk_set` is the reference twin.
+local function deskSetModel(sp, pr, t)
+  local W, H, D = sp.W, sp.H, pr.D
+  local ground = pr.ground
+  local col, inside = sp.col, sp.inside
+  local vox = {}
+  local function key(x, y, z) return (y * D + z) * W + x end
+  local function put(x, y, z, i) vox[key(x, y, z)] = i end
+
+  -- de-outline walk bounded to the part, so a part's side faces show
+  -- its own material and never the neighbour's (the sprite-wide walk
+  -- the facade path uses would cross the black seam between units)
+  local function interiorAt(sx, sy, lo, hi)
+    local i = sy * W + sx
+    if col[i] ~= BLACK then return sx end
+    local step = sx < math.floor((lo + hi) / 2) and 1 or -1
+    for d = 1, 3 do
+      local nx = sx + step * d
+      if nx >= lo and nx <= hi then
+        local ni = sy * W + nx
+        if inside[ni] and col[ni] ~= BLACK then return nx end
+      end
+    end
+    return sx
+  end
+
+  local f0, f1 = t.desk.fascia[1], t.desk.fascia[2]
+  local b0, b1 = t.desk.base[1], t.desk.base[2]
+  local plane = (b1 - b0 + 1) + (f1 - f0 + 1)
+
+  -- the base band, extruded exactly like every lab table's
+  for sy = b0, b1 do
+    Budget.tick()
+    local y = ground - 1 - sy
+    for sx = 0, W - 1 do
+      if inside[sy * W + sx] then
+        local ix = interiorAt(sx, sy, 0, W - 1)
+        for z = 0, D - 1 do
+          local px = (z == 0 or z == D - 1) and sx or ix
+          put(sx, y, z, sy * W + px)
+        end
+      end
+    end
+  end
+  for i in pairs(pr.recess) do
+    local sy = math.floor(i / W)
+    if sy >= b0 and sy <= b1 then
+      vox[key(i % W, ground - 1 - sy, D - 1)] = nil
+    end
+  end
+
+  -- the slab: fascia rows wrap every side; the lid continues the
+  -- sibling tables' top -- black rim, white highlight courses along
+  -- the north and west, grey field
+  for sy = f0, f1 do
+    Budget.tick()
+    local y = plane - 1 - (sy - f0)
+    for sx = 0, W - 1 do
+      for z = 0, D - 1 do put(sx, y, z, sy * W + sx) end
+    end
+  end
+  for sx = 0, W - 1 do
+    for z = 0, D - 1 do
+      local shade = GREY
+      if sx == 0 or sx == W - 1 or z == 0 or z == D - 1 then
+        shade = BLACK
+      elseif sx == 1 or z == 1 then
+        shade = WHITE
+      end
+      put(sx, plane - 1, z, pr.shadeTexel[shade])
+    end
+  end
+
+  local ytop = plane
+  for _, p in ipairs(t.parts) do
+    Budget.tick()
+    local x0, x1 = p.x[1], p.x[2]
+    if p.kind == "flat" then
+      for sy = p.rows[1], p.rows[2] do
+        if sy >= 0 and sy < D then
+          for sx = x0, x1 do
+            if inside[sy * W + sx] then put(sx, plane, sy, sy * W + sx) end
+          end
+        end
+      end
+    else
+      local tr0, tr1 = p.top[1], p.top[2]
+      local fr0, fr1 = p.facade[1], p.facade[2]
+      local pd = p.depth
+      local ytp = plane + (fr1 - fr0)
+      if ytp > ytop then ytop = ytp end
+      for sx = x0, x1 do
+        -- the lid: the part's drawn top laid across its depth from the
+        -- back, last row continuing forward; the front lid row is the
+        -- facade's own top row -- the drawn front-top edge
+        for z = 0, pd - 1 do
+          local front = z == pd - 1
+          local sy = front and fr0 or math.min(tr0 + z, tr1)
+          while sy <= tr1 and not inside[sy * W + sx] do sy = sy + 1 end
+          local ok = sy <= tr1 or (front and inside[fr0 * W + sx])
+          if ok then
+            put(sx, ytp, z, (front and fr0 or sy) * W + sx)
+          end
+        end
+        -- the body: facade rows anchored to the desk's top plane
+        for sy = fr0 + 1, fr1 do
+          local y = plane + (fr1 - sy)
+          local i = sy * W + sx
+          if inside[i] then
+            local ix = interiorAt(sx, sy, x0, x1)
+            for z = 0, pd - 1 do
+              if z == pd - 1 then
+                if not pr.recess[i] then put(sx, y, z, i) end
+              elseif z == 0 then
+                put(sx, y, z, i)
+              else
+                put(sx, y, z, sy * W + ix)
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+
+  return { at = function(x, y, z)
+             if x < 0 or x >= W or y < 0 or z < 0 or z >= D then return nil end
+             return vox[key(x, y, z)]
+           end,
+           W = W, ytop = ytop, zmin = 0, zmax = D - 1 }
+end
+
 -- The voxel model as a lookup: `at(x, y, z)` is the index of the sprite
 -- pixel that voxel wears, or nil. Build ORDER is expressed as lookup
 -- order -- roof first, so it overwrites the walls it intersects, and walls
 -- are trimmed to its underside so nothing pokes through the surface.
 local function model(sp, pr, t)
+  if t.parts then return deskSetModel(sp, pr, t) end
   local W, H, D = sp.W, sp.H, pr.D
   local slab, roofRows = t.slab, t.roofRows
   local top, ytop, ground = pr.top, pr.ytop, pr.ground
diff --git a/tools/building_voxels.py b/tools/building_voxels.py
index a38f8c5..6cb00b7 100644
--- a/tools/building_voxels.py
+++ b/tools/building_voxels.py
@@ -569,15 +569,21 @@ TEMPLATES = {
         roof_rows=19, roof_back=16, roof_front=0, roof_cycle=(2, 13),
         slab=3, front_eave=0, ledge=None, tileset="gym", depth=2,
     ),
-    # F02: the computer bank on Oak's lab's west table -- and the Hall
-    # of Fame's recording machine, the same drawing on the GYM atlas
-    # (one placement each; both registered in voxel_heights). The one
-    # FACADE-ONLY template: rows 0-14 are machines drawn face-on over
-    # the lab-table fascia and base bands, nothing is drawn from above,
-    # so roof_rows=0 and the whole drawing extrudes -- the house facade
-    # treatment with no roof band. The recess pass turns every framed
-    # pane (the monitor, its keys, the right cabinet's inset face) into
-    # relief on its own.
+    # F02: the computer desk in Oak's lab -- and the Hall of Fame's
+    # recording machine, the same drawing on the GYM atlas (one
+    # placement each; both registered in voxel_heights). The one
+    # DESK-SET template: the methodology's region classification at
+    # part granularity. The desk is the sibling lab table (fascia rows
+    # 16-18, base 19-21); on it stand a monitor over its keyboard
+    # (left), a computer tower over a keyboard and mouse (middle), and
+    # a sheet of paper LYING FLAT (right). Upright parts anchor their
+    # drawn bottom row to the desk's top plane and wear their own drawn
+    # tops as lids; flat parts lie one voxel proud with drawn row =
+    # depth row -- the same 1:1 the tabletop itself is drawn with, so
+    # an object's height ON the drawing is its position ON the desk.
+    # The desk's own top is the one synthesized surface (the objects
+    # cover every pixel of it), continued from the sibling tables'
+    # pattern in the drawing's own shades.
     "lab_computers": dict(
         tiles=[
             [91, 92, 93, 94],
@@ -586,6 +592,16 @@ TEMPLATES = {
         ],
         roof_rows=0, roof_back=0, roof_front=0, roof_cycle=(0, 0),
         slab=0, front_eave=0, ledge=None, tileset="gym", depth=2,
+        desk=dict(fascia=(16, 18), base=(19, 21)),
+        parts=[
+            dict(kind="upright", x=(2, 13), top=(0, 2), facade=(3, 10),
+                 depth=4),                                   # the monitor
+            dict(kind="flat", x=(1, 13), rows=(11, 14)),     # its keyboard
+            dict(kind="upright", x=(14, 21), top=(0, 3), facade=(4, 10),
+                 depth=6),                                   # the tower
+            dict(kind="flat", x=(14, 21), rows=(11, 14)),    # keys + mouse
+            dict(kind="flat", x=(22, 30), rows=(1, 14)),     # the paper
+        ],
     ),
     # F03: the empty north table beside it -- the starter table's band
     # table verbatim on a grid two tiles narrower (one placement).
@@ -741,10 +757,122 @@ def profile(sp, t):
 
 
 # --------------------------------------------------------------- stage 3 --
+def build_desk_set(sp, pr, t):
+    """A desk with separately-classified objects on it (the `parts` list):
+    upright parts stand on the desk's top plane wearing their own drawn
+    tops as lids, flat parts lie one voxel proud at drawn row = depth row,
+    and the desk itself is the lab-table slab + base with a synthesized
+    lid (the objects cover every drawn pixel of the tabletop)."""
+    W, H, D = pr["W"], pr["H"], pr["D"]
+    inside = pr["inside"]
+    col, src = sp["col"], sp["src"]
+    ground = pr["ground"]
+    vox = {}
+
+    def put(x, y, z, sx, sy):
+        vox[(x, y, z)] = (col[sy][sx], src[sy][sx])
+
+    shade_px = {}
+    for sy in range(H):
+        for sx in range(W):
+            if inside(sx, sy):
+                shade_px.setdefault(col[sy][sx], (sx, sy))
+
+    def interior(sx, sy, lo, hi):
+        if col[sy][sx] != BLACK:
+            return sx
+        step = 1 if sx < (lo + hi) // 2 else -1
+        for d in range(1, 4):
+            nx = sx + step * d
+            if lo <= nx <= hi and inside(nx, sy) and col[sy][nx] != BLACK:
+                return nx
+        return sx
+
+    f0, f1 = t["desk"]["fascia"]
+    b0, b1 = t["desk"]["base"]
+    plane = (b1 - b0 + 1) + (f1 - f0 + 1)       # the desk's top plane
+
+    # the base band, extruded exactly like every lab table's
+    for sy in range(b0, b1 + 1):
+        y = ground - 1 - sy
+        for sx in range(W):
+            if not inside(sx, sy):
+                continue
+            ix = interior(sx, sy, 0, W - 1)
+            for z in range(D):
+                put(sx, y, z, sx if z in (0, D - 1) else ix, sy)
+    for sx, sy in pr["recess"]:
+        if b0 <= sy <= b1:
+            vox.pop((sx, ground - 1 - sy, D - 1), None)
+
+    # the slab: the fascia rows wrap every side; the lid is the one
+    # synthesized surface in the model -- the drawing never paints the
+    # tabletop (its objects cover it), so the lid continues the sibling
+    # tables' pattern in the drawing's own shades: black rim, white
+    # highlight courses along the north and west, grey field
+    for i, sy in enumerate(range(f0, f1 + 1)):
+        y = plane - 1 - i
+        for sx in range(W):
+            for z in range(D):
+                put(sx, y, z, sx, sy)
+    for sx in range(W):
+        for z in range(D):
+            if sx in (0, W - 1) or z in (0, D - 1):
+                shade = BLACK
+            elif sx == 1 or z == 1:
+                shade = WHITE
+            else:
+                shade = GREY
+            px = shade_px.get(shade) or shade_px[BLACK]
+            put(sx, plane - 1, z, px[0], px[1])
+
+    for p in t["parts"]:
+        x0, x1 = p["x"]
+        if p["kind"] == "flat":
+            r0, r1 = p["rows"]
+            for sy in range(r0, r1 + 1):
+                if not 0 <= sy < D:
+                    continue
+                for sx in range(x0, x1 + 1):
+                    if inside(sx, sy):
+                        put(sx, plane, sy, sx, sy)
+            continue
+        tr0, tr1 = p["top"]
+        fr0, fr1 = p["facade"]
+        pd = p["depth"]
+        ytp = plane + (fr1 - fr0)
+        for sx in range(x0, x1 + 1):
+            # the lid: the part's drawn top laid across its depth from
+            # the back, last row continuing forward; the front lid row
+            # is the facade's own top row -- the drawn front-top edge
+            for z in range(pd):
+                sy = fr0 if z == pd - 1 else min(tr0 + z, tr1)
+                while sy <= tr1 and not inside(sx, sy):
+                    sy += 1
+                if sy > tr1 and not (z == pd - 1 and inside(sx, fr0)):
+                    continue
+                put(sx, ytp, z, sx, fr0 if z == pd - 1 else sy)
+            # the body: facade rows anchored to the desk's top plane
+            for sy in range(fr0 + 1, fr1 + 1):
+                y = plane + (fr1 - sy)
+                if not inside(sx, sy):
+                    continue
+                ix = interior(sx, sy, x0, x1)
+                for z in range(pd):
+                    if z == pd - 1:
+                        if (sx, sy) not in pr["recess"]:
+                            put(sx, y, z, sx, sy)
+                    else:
+                        put(sx, y, z, sx if z == 0 else ix, sy)
+    return vox
+
+
 def build(sp, pr, t):
     """The voxel model. Order is load-bearing: walls, ledge, recesses, then
     the roof solid overwrites what it intersects and the walls are trimmed
     to the roof's underside."""
+    if t.get("parts"):
+        return build_desk_set(sp, pr, t)
     W, H, D = pr["W"], pr["H"], pr["D"]
     inside, top, ytop = pr["inside"], pr["top"], pr["ytop"]
     col, src = sp["col"], sp["src"]
@@ -869,12 +997,13 @@ def verify(vox, sp, pr, t):
     for (x, y, z), _ in vox.items():
         assert y <= T(x), f"voxel pokes through the roof at {x},{y},{z}"
 
-    # A facade-only template (roof_rows == 0: the lab computer bank) has
-    # no roof band, so there is no surface to hold level or slope and no
-    # coverage to demand of it; the poke-through assert above and the
-    # facade asserts below are what it answers to. Everything with a roof
-    # band answers the full set.
-    if t["roof_rows"] == 0:
+    # A desk set answers its own asserts; a facade-only template (roof_rows
+    # == 0) has no roof band, so there is no surface to hold level or slope
+    # and no coverage to demand of it; everything with a roof band answers
+    # the full set.
+    if t.get("parts"):
+        verify_desk_set(vox, pr, t)
+    elif t["roof_rows"] == 0:
         # its one geometric intent: a straight extrusion, so each column
         # of the drawing is solid from its lowest voxel to its top. NOT
         # from the ground -- the drawing's base band is inset like every
@@ -899,6 +1028,46 @@ def verify(vox, sp, pr, t):
     return shell
 
 
+def verify_desk_set(vox, pr, t):
+    W, D = pr["W"], pr["D"]
+    f0, f1 = t["desk"]["fascia"]
+    b0, b1 = t["desk"]["base"]
+    plane = (b1 - b0 + 1) + (f1 - f0 + 1)
+
+    # the slab is a full solid under everything on it
+    for x in range(W):
+        for z in range(D):
+            for y in range(b1 - b0 + 1, plane):
+                assert (x, y, z) in vox, f"slab hole at {x},{y},{z}"
+
+    # each part stands where authored -- upright bodies solid from the
+    # desk to their own drawn top (bar the front layer, whose panes
+    # sink) -- and nothing stands anywhere else
+    tops = {}
+    for p in t["parts"]:
+        x0, x1 = p["x"]
+        if p["kind"] == "flat":
+            r0, r1 = p["rows"]
+            for x in range(x0, x1 + 1):
+                for z in range(max(r0, 0), min(r1, D - 1) + 1):
+                    tops[(x, z)] = max(tops.get((x, z), 0), plane)
+        else:
+            fr0, fr1 = p["facade"]
+            ytp = plane + (fr1 - fr0)
+            for x in range(x0, x1 + 1):
+                for z in range(p["depth"]):
+                    tops[(x, z)] = max(tops.get((x, z), 0), ytp)
+                    if z == p["depth"] - 1:
+                        continue
+                    ys = [y for y in range(plane, ytp + 1)
+                          if (x, y, z) in vox]
+                    assert ys == list(range(ys[0], ys[0] + len(ys))) \
+                        if ys else True, f"part column hole at {x},{z}"
+    for (x, y, z) in vox:
+        assert y <= tops.get((x, z), plane - 1), \
+            f"voxel above its part at {x},{y},{z}"
+
+
 def verify_roof(vox, pr, t):
     W, H, D = pr["W"], pr["H"], pr["D"]
     ytop, top, slab = pr["ytop"], pr["top"], t["slab"]