mirror of
https://github.com/DramaticShape/DramaticShapeVoxelMod.git
synced 2026-08-12 09:10:49 +02:00
update 2d shiny sprites and shiny animation
This commit is contained in:
+275
-5
@@ -136,6 +136,13 @@ end
|
||||
local models = {} -- "<tileset>:<index>" -> prebuilt local quads
|
||||
local frontSets = {} -- tileset id -> { [tile] = true } or false
|
||||
|
||||
-- The tileset's side-door art: the 2x2 tile grid of one doorway cell
|
||||
-- (data/voxel_heights.lua `sideDoors`), or nil when the tileset names none.
|
||||
function Buildings.sideDoorCell(tilesetId)
|
||||
local s = profile()
|
||||
return s and s.sideDoors and s.sideDoors[tilesetId] or nil
|
||||
end
|
||||
|
||||
-- The tileset's front-only tiles as a set (data/voxel_heights.lua
|
||||
-- `frontOnly`): the doorways, shop signs and painted lettering that belong
|
||||
-- on a facade and on no other face of the same building. nil when the
|
||||
@@ -252,6 +259,82 @@ end
|
||||
-- topRows (placement is still by `tiles` alone); they exist so the MODEL
|
||||
-- is built from the complete drawing and the tower rises to its real
|
||||
-- height instead of folding as two half-buildings.
|
||||
-- Composite one doorway cell PAST the end of the sprite, at indices
|
||||
-- W*H .. W*H+255, and hand back its base. The side-door pass paints with
|
||||
-- sprite indices like everything else -- `emit` resolves a voxel's colour
|
||||
-- through sp.ax/sp.ay and knows nothing about where the index came from --
|
||||
-- so the door only has to BE in the sprite arrays to travel the rest of
|
||||
-- the pipeline untouched. Appending rather than drawing into the grid is
|
||||
-- the point: the drawing itself must not change, or the silhouette flood,
|
||||
-- the taper and every measured band would be read off art the tileset
|
||||
-- never placed here.
|
||||
--
|
||||
-- Nothing else walks past W*H (measure's shadeTexel scan and the pane
|
||||
-- flood both stop there), so the block is invisible to measurement and
|
||||
-- visible only to the code that asks for it by index.
|
||||
--
|
||||
-- WHICH OF THE 256 TEXELS ARE THE DOOR. A doorway cell is not a doorway
|
||||
-- edge to edge: the tileset draws it as a cell OF A FACADE, so its outer
|
||||
-- ring is the wall beside and above the frame, and its last row is the
|
||||
-- black threshold the building stands on with the door's own step cut into
|
||||
-- it. Painting all 16x16 onto a flank would stamp a one-pixel border of
|
||||
-- front-wall art around every door.
|
||||
--
|
||||
-- The front facade tells the ring from the door by flooding: the wall
|
||||
-- around the door is one region with the whole facade, far too big to be a
|
||||
-- pane, and only what the black frame SEALS sinks. The same test, bounded
|
||||
-- to the block: flood the left, right and top edges through their own
|
||||
-- shade class, and what the flood reaches is context -- left unpainted, so
|
||||
-- the flank keeps the texel it already had. Not the bottom edge, because
|
||||
-- the bottom edge is the ground: the step under the door is sealed there
|
||||
-- on the drawn facade too, and it recesses with the rest of the doorway.
|
||||
--
|
||||
-- What is painted then splits the way a facade's does: black is frame and
|
||||
-- stays flush with the wall, everything it seals sinks a voxel behind it.
|
||||
local function readDoor(sp, data, perRow, cell)
|
||||
local base = sp.W * sp.H
|
||||
local black = {}
|
||||
for dy = 0, 15 do
|
||||
local row = cell[math.floor(dy / 8) + 1]
|
||||
for dx = 0, 15 do
|
||||
local tile = row[math.floor(dx / 8) + 1]
|
||||
local px = (tile % perRow) * 8 + dx % 8
|
||||
local py = math.floor(tile / perRow) * 8 + dy % 8
|
||||
local k = dy * 16 + dx
|
||||
local i = base + k
|
||||
sp.ax[i], sp.ay[i] = px, py
|
||||
local r, g, b, a = data:getPixel(px, py)
|
||||
sp.col[i] = shadeOf(r, g, b, a)
|
||||
sp.inside[i] = true
|
||||
black[k] = sp.col[i] == BLACK
|
||||
end
|
||||
end
|
||||
|
||||
local context, stack = {}, {}
|
||||
local function seed(dx, dy, cls)
|
||||
if dx < 0 or dx > 15 or dy < 0 or dy > 15 then return end
|
||||
local k = dy * 16 + dx
|
||||
if context[k] or black[k] ~= cls then return end
|
||||
context[k] = true
|
||||
stack[#stack + 1] = k
|
||||
end
|
||||
for dy = 0, 15 do
|
||||
seed(0, dy, black[dy * 16])
|
||||
seed(15, dy, black[dy * 16 + 15])
|
||||
end
|
||||
for dx = 0, 15 do seed(dx, 0, black[dx]) end
|
||||
while #stack > 0 do
|
||||
local k = table.remove(stack)
|
||||
local dx, dy, cls = k % 16, math.floor(k / 16), black[k]
|
||||
seed(dx + 1, dy, cls)
|
||||
seed(dx - 1, dy, cls)
|
||||
seed(dx, dy + 1, cls)
|
||||
seed(dx, dy - 1, cls)
|
||||
end
|
||||
|
||||
sp.door = { base = base, black = black, context = context }
|
||||
end
|
||||
|
||||
local function read(t, data, perRow, frontOnly)
|
||||
local tiles = t.tiles
|
||||
if t.topRows then
|
||||
@@ -1087,11 +1170,113 @@ local function deskSetModel(sp, pr, t)
|
||||
W = W, ytop = ytop, zmin = 0, zmax = D - 1 }
|
||||
end
|
||||
|
||||
-- ------- the doorway a gate house is entered by from a side the drawing
|
||||
-- never shows it on (data/voxel_heights.lua `sideDoors`, and `sideDoorsAt`
|
||||
-- below for how the placements are found).
|
||||
--
|
||||
-- One cell of the tileset's own doorway art, standing on the ground of the
|
||||
-- face the player walks into, and hung by the SAME rule the drawn facade
|
||||
-- hangs its own door by: the art's black frame stays flush with the wall
|
||||
-- and everything it seals sinks a voxel behind it (`measure`'s pane pass,
|
||||
-- applied here by hand because the art is not in the drawing to be flooded
|
||||
-- with it). So a side door and a front door are the same depth of the same
|
||||
-- opening, and the jamb faces the recess exposes come out of the mesher for
|
||||
-- free, wearing the frame's own texels.
|
||||
--
|
||||
-- ORIENTATION IS NOT FREE. A flank quad carries one texel and the mesher
|
||||
-- picks it per voxel, so which art column lands at which world coordinate
|
||||
-- is decided HERE and nowhere else -- and a face is read from outside, so
|
||||
-- the art's own left-to-right runs with the viewer's, not with the world's:
|
||||
-- facing east at a west wall, south is to your right (+z); facing west at
|
||||
-- an east wall, north is (-z); facing south at a north wall, west is (-x).
|
||||
-- Two of the three are mirrored against the axis, which is the same reason
|
||||
-- `backMap` exists -- a wall seen from behind IS the drawing mirrored.
|
||||
local DOOR = 16
|
||||
|
||||
local function sideDoors(at, sp, doors, W)
|
||||
if not (doors and #doors > 0 and sp.door) then return at end
|
||||
local art = sp.door
|
||||
|
||||
-- The face's OUTER surface, walked in from the box edge until the wall
|
||||
-- answers. A drawing inset from its own grid (B03's outer columns are
|
||||
-- terrain, not building) stands its flank a column or two in, and a door
|
||||
-- pinned to the box edge would hang in the air beside it.
|
||||
local function faceX(from, step, off)
|
||||
for k = 0, W - 1 do
|
||||
local x = from + step * k
|
||||
for y = 0, DOOR - 1 do
|
||||
for z = off, off + DOOR - 1 do
|
||||
if at(x, y, z) then return x end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local list = {}
|
||||
for _, d in ipairs(doors) do
|
||||
local face = 0 -- north: the facade's own z origin
|
||||
if d.side == "w" then face = faceX(0, 1, d.at)
|
||||
elseif d.side == "e" then face = faceX(W - 1, -1, d.at) end
|
||||
if face then
|
||||
list[#list + 1] = { side = d.side, off = d.at, face = face }
|
||||
end
|
||||
end
|
||||
if #list == 0 then return at end
|
||||
|
||||
-- art column at (x, z) for door `e`, or nil when the voxel is not in it
|
||||
local function column(e, x, z)
|
||||
if e.side == "n" then
|
||||
if (z == 0 or z == 1) and x >= e.off and x < e.off + DOOR then
|
||||
return e.off + DOOR - 1 - x, z == 0
|
||||
end
|
||||
elseif z >= e.off and z < e.off + DOOR then
|
||||
if e.side == "w" then
|
||||
if x == e.face then return z - e.off, true end
|
||||
if x == e.face + 1 then return z - e.off, false end
|
||||
else
|
||||
if x == e.face then return e.off + DOOR - 1 - z, true end
|
||||
if x == e.face - 1 then return e.off + DOOR - 1 - z, false end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
return function(x, y, z)
|
||||
local v = at(x, y, z)
|
||||
-- no wall here is the end of it: a door is hung ON the building, and
|
||||
-- nothing about it may add geometry the drawing does not stand up
|
||||
if v == nil or y >= DOOR then return v end
|
||||
for _, e in ipairs(list) do
|
||||
local c, outer = column(e, x, z)
|
||||
if c then
|
||||
-- art row 0 is the door's head, so the ground row is its last
|
||||
local k = (DOOR - 1 - y) * DOOR + c
|
||||
-- the art's own ring: wall, not door. The flank keeps its texel.
|
||||
if art.context[k] then return v end
|
||||
if art.black[k] then
|
||||
-- the frame, flush with the wall; behind it the wall stands on
|
||||
if outer then return art.base + k end
|
||||
return v
|
||||
end
|
||||
-- and what the frame seals sinks: the face voxel goes, the one
|
||||
-- behind it wears the art. (Written long: `outer and nil or i`
|
||||
-- returns i for BOTH, nil being false to `and`.)
|
||||
if outer then return nil end
|
||||
return art.base + k
|
||||
end
|
||||
end
|
||||
return v
|
||||
end
|
||||
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)
|
||||
-- are trimmed to its underside so nothing pokes through the surface. A
|
||||
-- gate's side doors are hung on the finished lookup, last of all, because
|
||||
-- they answer to the FACE rather than to any band of the drawing.
|
||||
local function model(sp, pr, t, doors)
|
||||
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
|
||||
@@ -1209,6 +1394,8 @@ local function model(sp, pr, t)
|
||||
return pr.interior[i]
|
||||
end
|
||||
|
||||
at = sideDoors(at, sp, doors, W)
|
||||
|
||||
return { at = at, W = W, ytop = ytop,
|
||||
zmin = ledge0 and -2 or 0,
|
||||
zmax = math.max(rz1, ledge0 and (D + 1) or 0) }
|
||||
@@ -1420,6 +1607,76 @@ local function matches(S, t, tx, ty)
|
||||
return true
|
||||
end
|
||||
|
||||
-- ------- which of a placement's faces a gate is entered by
|
||||
--
|
||||
-- Read off the MAP, not authored: a gate entrance is a warp that lands in a
|
||||
-- gate house, and the face is whichever one of this placement the warp cell
|
||||
-- stands against. Thirty-odd doors fall out of two lines of geometry, and
|
||||
-- none of them can drift out of step with a map edit the way a hand list
|
||||
-- would. Three tests, each of them load-bearing:
|
||||
--
|
||||
-- the destination is a GATE tileset -- what makes a building a gate house
|
||||
-- rather than a house with a back door. GATE and FOREST_GATE both, so
|
||||
-- the Viridian Forest pair count; the Safari rest houses are on GATE
|
||||
-- too and are excluded by the next test, their warps being drawn doors
|
||||
-- already.
|
||||
-- the cell is not already a door tile -- a south entrance IS drawn, as a
|
||||
-- doorway block in the facade, and lib/Structures.lua folds it up into
|
||||
-- the front face. Adding a second one there would fight it.
|
||||
-- the cell is WALKABLE -- the ROM gives an unreachable twin warp to
|
||||
-- several gates (a fence cell beside the real opening on Route 7 west
|
||||
-- and Route 16 east, a tree beside Route 6's), and a door on the wall
|
||||
-- behind a fence is a door into nothing. The reachable cells are the
|
||||
-- entrance, and two of them side by side are the gate's real two-cell
|
||||
-- opening, which comes out as the double door it always was.
|
||||
--
|
||||
-- The south face is skipped whether or not it is drawn: it is the one face
|
||||
-- the drawing states in full, so anything it needs it already has.
|
||||
local function sideDoorsAt(map, tileset, tx, ty, bw, bh)
|
||||
local defs = _G.Game and Game.data and Game.data.maps
|
||||
local warps = map.def and map.def.warps
|
||||
if not (defs and warps and warps[1]) then return nil end
|
||||
if not Buildings.sideDoorCell(tileset.id) then return nil end
|
||||
|
||||
local out = nil
|
||||
for _, w in ipairs(warps) do
|
||||
local dest = defs[w.destMap]
|
||||
if dest and dest.tileset and dest.tileset:find("GATE", 1, true)
|
||||
and map:isWalkableCell(w.x, w.y)
|
||||
and not map:isDoorTileCell(w.x, w.y) then
|
||||
-- the cell in the model's own pixels: a cell is two tiles, a tile
|
||||
-- eight pixels, and the placement's origin is (tx, ty) in tiles
|
||||
local lx, lz = w.x * 16 - tx * 8, w.y * 16 - ty * 8
|
||||
local side = nil
|
||||
if lz == -16 and lx >= 0 and lx < bw * 8 then side = "n"
|
||||
elseif lx == -16 and lz >= 0 and lz < bh * 8 then side = "w"
|
||||
elseif lx == bw * 8 and lz >= 0 and lz < bh * 8 then side = "e" end
|
||||
if side then
|
||||
out = out or {}
|
||||
out[#out + 1] = { side = side, at = side == "n" and lx or lz }
|
||||
end
|
||||
end
|
||||
end
|
||||
if out then
|
||||
table.sort(out, function(a, b)
|
||||
if a.side ~= b.side then return a.side < b.side end
|
||||
return a.at < b.at
|
||||
end)
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
-- The model cache key's door half. A template's doors belong to the
|
||||
-- PLACEMENT -- the same 6x4 block is the gate on four routes and the warps
|
||||
-- sit at different rows of it on each -- so two placements of one drawing
|
||||
-- are two models, and only placements that agree share one.
|
||||
local function doorKey(doors)
|
||||
if not doors then return "" end
|
||||
local parts = {}
|
||||
for i, d in ipairs(doors) do parts[i] = d.side .. d.at end
|
||||
return "#" .. table.concat(parts, ",")
|
||||
end
|
||||
|
||||
-- Find every placement of every template for this map's tileset, build one
|
||||
-- model per template, and stamp it. Returns nothing; the quads land in
|
||||
-- S.objectQuads and the tiles are claimed so the volume path never boxes a
|
||||
@@ -1440,6 +1697,9 @@ function Buildings.build(S, map, data, perRow)
|
||||
if type(t.tiles) == "table" and #t.tiles > 0 then
|
||||
local bh, bw = #t.tiles, #t.tiles[1]
|
||||
local first = t.tiles[1][1]
|
||||
-- the model this placement stamps. Not hoisted out of the loops any
|
||||
-- more: a template's doors belong to the placement, so two hits of
|
||||
-- one drawing on the same map can be two models (see doorKey).
|
||||
local built = nil
|
||||
for ty = 0, th - bh do
|
||||
Budget.tick()
|
||||
@@ -1465,8 +1725,13 @@ function Buildings.build(S, map, data, perRow)
|
||||
end
|
||||
end
|
||||
if free and matches(S, t, tx, ty) then
|
||||
if not built then
|
||||
local key = tileset.id .. ":" .. index
|
||||
do
|
||||
-- keyed per PLACEMENT once a gate's doors are in play (see
|
||||
-- doorKey): the drawing is shared, the openings are not
|
||||
local doors = not t.claimOnly
|
||||
and sideDoorsAt(map, tileset, tx, ty, bw, bh)
|
||||
or nil
|
||||
local key = tileset.id .. ":" .. index .. doorKey(doors)
|
||||
if not models[key] then
|
||||
if t.claimOnly then
|
||||
-- claim the cells, stamp nothing: the drawing here is
|
||||
@@ -1478,8 +1743,13 @@ function Buildings.build(S, map, data, perRow)
|
||||
else
|
||||
local sp = read(t, data, perRow,
|
||||
Buildings.frontOnly(tileset.id))
|
||||
if doors then
|
||||
readDoor(sp, data, perRow,
|
||||
Buildings.sideDoorCell(tileset.id))
|
||||
end
|
||||
local pr = measure(sp, t)
|
||||
models[key] = emit(model(sp, pr, t), sp, atlasW, atlasH)
|
||||
models[key] = emit(model(sp, pr, t, doors), sp,
|
||||
atlasW, atlasH)
|
||||
end
|
||||
end
|
||||
built = models[key]
|
||||
|
||||
+88
-1
@@ -2004,7 +2004,9 @@ local function stairCell(S, map, data, cx, cy, s)
|
||||
local atlasW = map.tileset.imageWidth or 128
|
||||
local atlasH = map.tileset.imageHeight or 48
|
||||
local quads = S.objectQuads
|
||||
local down = s.class == "stair_down_e" or s.class == "stair_down_w"
|
||||
local north = s.class == "stair_down_n"
|
||||
local down = north or s.class == "stair_down_e"
|
||||
or s.class == "stair_down_w"
|
||||
local east = s.class == "stair_e" or s.class == "stair_down_e"
|
||||
local mx, mz = cx * 16, cy * 16
|
||||
local h = s.h or 16
|
||||
@@ -2053,6 +2055,90 @@ local function stairCell(S, map, data, cx, cy, s)
|
||||
end
|
||||
end
|
||||
|
||||
-- A flight running INTO the map instead of across it. The drawing is
|
||||
-- the same staircase seen head-on rather than from the side, and that
|
||||
-- changes which axis of the art means what: a drawn ROW is a step here,
|
||||
-- and -- because looking down a well is looking along its depth -- drawn
|
||||
-- row IS depth row, 1:1 across the cell's 16.
|
||||
--
|
||||
-- The Centers' steps state their own band table and it lands exactly:
|
||||
-- 4 white rows, 1 black, 3 grey, 1 black, 3 checker, 4 black = 16. So
|
||||
-- an even four-step division puts a black NOSING on the southmost row of
|
||||
-- every band (15, 11, 7, 3) and leaves the rows behind it as that step's
|
||||
-- tread. Nothing is authored but the RISE, which no head-on drawing can
|
||||
-- state; the depths, the treads and the nosings are all measured.
|
||||
--
|
||||
-- A nosing is drawn as one row because it is seen nearly edge-on, so
|
||||
-- un-projected it has real height and no depth: its row lies flat as the
|
||||
-- tread's front lip AND stands as the riser under it. That is the one
|
||||
-- texel in the flight used twice, and using it twice is what a nosing is.
|
||||
--
|
||||
-- The well's own walls come free as well: the drawing's first and last
|
||||
-- COLUMNS are its black side walls, and its top band is the darkness the
|
||||
-- flight leaves by, which is what the far end wants to wear.
|
||||
--
|
||||
-- Every quad here is split at the cell's own 8px seam, in x and in rows
|
||||
-- both: `uv` resolves ONE tile per corner, and these four tiles are not
|
||||
-- neighbours in the atlas, so a quad that spans a seam interpolates
|
||||
-- between two unrelated corners of the sheet.
|
||||
if north then
|
||||
local runD = 16 / STAIR_STEPS
|
||||
local HALVES = { { 0.2, 7.9, 0, 8 }, { 8.1, 15.8, 8, 16 } }
|
||||
for i = 0, STAIR_STEPS - 1 do
|
||||
local a0 = 16 - (i + 1) * runD -- band i, in art rows
|
||||
local a1 = a0 + runD
|
||||
local yTop = -(i + 1) * rise
|
||||
local z0b, z1b = mz + a0, mz + a1
|
||||
|
||||
for _, H in ipairs(HALVES) do
|
||||
local ax0, ax1, wx0, wx1 = H[1], H[2], mx + H[3], mx + H[4]
|
||||
|
||||
-- the tread: the whole band, drawn row = depth row, so the nosing
|
||||
-- lies on its front lip exactly where the artist drew it
|
||||
face({ wx0, yTop, z0b }, { wx1, yTop, z0b },
|
||||
{ wx1, yTop, z1b }, { wx0, yTop, z1b },
|
||||
ax0, a1, ax1, a0, STAIR_SHADE.wellTread)
|
||||
|
||||
-- the riser under that lip. It faces NORTH -- a flight descending
|
||||
-- away from you turns its risers away with it, and they close the
|
||||
-- steps from below rather than being looked at. One art row tall,
|
||||
-- so it needs none of `banded`'s row splitting; written straight
|
||||
-- keeps the geometry flush at the seam while the art stays inside
|
||||
-- its tile
|
||||
local ry = -i * rise
|
||||
face({ wx1, yTop, z1b }, { wx0, yTop, z1b },
|
||||
{ wx0, ry, z1b }, { wx1, ry, z1b },
|
||||
ax1, a1 - 1, ax0, a1, STAIR_SHADE.riser)
|
||||
|
||||
-- the deep end, closing the opening this flight is cut into: from
|
||||
-- the floor of the well up to the top of the wall band beside it,
|
||||
-- in the drawing's own black top rows
|
||||
if i == STAIR_STEPS - 1 then
|
||||
face({ wx1, -h, mz }, { wx0, -h, mz },
|
||||
{ wx0, h, mz }, { wx1, h, mz },
|
||||
ax1, 3.9, ax0, 0.1, STAIR_SHADE.wellEnd)
|
||||
end
|
||||
end
|
||||
|
||||
-- the well's side walls above this tread, wearing the drawing's own
|
||||
-- black edge columns -- the excavation is walled in its own texels
|
||||
local function sideWall(px, sx0, sx1, inward)
|
||||
local c
|
||||
if inward then -- west wall, faces E
|
||||
c = { { px, yTop, z1b }, { px, yTop, z0b },
|
||||
{ px, 0, z0b }, { px, 0, z1b } }
|
||||
else -- east wall, faces W
|
||||
c = { { px, yTop, z0b }, { px, yTop, z1b },
|
||||
{ px, 0, z1b }, { px, 0, z0b } }
|
||||
end
|
||||
face(c[1], c[2], c[3], c[4], sx0, a1, sx1, a0, STAIR_SHADE.wellN)
|
||||
end
|
||||
sideWall(mx, 0.1, 1.3, true)
|
||||
sideWall(mx + 16, 14.7, 15.9, false)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
for i = 0, STAIR_STEPS - 1 do
|
||||
local sx0 = east and (i * runW) or (16 - (i + 1) * runW)
|
||||
local sx1 = sx0 + runW
|
||||
@@ -2152,6 +2238,7 @@ function Structures.buildStairs(S, map, x0, x1, y0, y1)
|
||||
-- box or floor it. A rising flight stands on the map's common
|
||||
-- floor; a stairwell IS the hole, so nothing is painted under it
|
||||
local down = s.class == "stair_down_e" or s.class == "stair_down_w"
|
||||
or s.class == "stair_down_n"
|
||||
for dy = 0, 1 do
|
||||
for dx = 0, 1 do
|
||||
local tk = keyOf(cx * 2 + dx, cy * 2 + dy)
|
||||
|
||||
@@ -125,6 +125,12 @@ local FALLBACK_HEIGHTS = {
|
||||
stair_w = 16,
|
||||
stair_down_e = 16,
|
||||
stair_down_w = 16,
|
||||
-- a stairwell descending toward the BACK of the map, drawn head-on
|
||||
-- instead of from the side (the Centers' Cable Club steps). Its own
|
||||
-- class because the art reading is not the east/west one turned: there
|
||||
-- a drawn COLUMN is a step and a drawn row is height, here a drawn ROW
|
||||
-- is a step and drawn row = depth row, 1:1 down the well
|
||||
stair_down_n = 16,
|
||||
}
|
||||
|
||||
-- class -> how the mesher draws it (see the header). The last three are
|
||||
@@ -215,6 +221,7 @@ local ART = {
|
||||
stair_w = "stair",
|
||||
stair_down_e = "stair",
|
||||
stair_down_w = "stair",
|
||||
stair_down_n = "stair",
|
||||
}
|
||||
|
||||
local spec = nil -- the loaded data file, or false when absent
|
||||
|
||||
+15
-2
@@ -503,12 +503,22 @@ local active = false
|
||||
-- which is exactly the old behaviour minus the reflections.
|
||||
local DEPTH_FORMATS = { "depth24", "depth24stencil8", "depth32f", "depth16" }
|
||||
|
||||
-- dpiscale = 1, for the same reason PixelCanvas pins it and for one more:
|
||||
-- newCanvas otherwise takes the WINDOW's scale, and every canvas bound
|
||||
-- together must agree on PIXEL dimensions. The colour canvas beside this one
|
||||
-- comes from PixelCanvas at scale 1, so on any surface whose scale is not 1
|
||||
-- -- Android's density is routinely 2.625, and a retina Mac's is 2 -- this
|
||||
-- one came back 2.625x larger and the pair would not bind. beginScene then
|
||||
-- dropped the readable depth for the session (see below), depthReadable()
|
||||
-- went false, and the water pass never ran at all: the reflections were
|
||||
-- missing on every high-density display, with nothing in the log to say so,
|
||||
-- because a canvas that will not BIND is not a canvas the driver refused.
|
||||
local function newDepth(w, h)
|
||||
if not (love.graphics and love.graphics.newCanvas) then return nil end
|
||||
local c = nil
|
||||
for _, format in ipairs(DEPTH_FORMATS) do
|
||||
local ok, made = pcall(love.graphics.newCanvas, w, h,
|
||||
{ format = format, readable = true })
|
||||
{ format = format, readable = true, dpiscale = 1 })
|
||||
if ok and made then c = made break end
|
||||
end
|
||||
if not c then return nil end
|
||||
@@ -1344,7 +1354,10 @@ end
|
||||
function Voxel3D.beginWater(paint)
|
||||
if not (active and canvas and held and held.depth) then return nil end
|
||||
if not held.mirror then
|
||||
local ok, c = pcall(love.graphics.newCanvas, held.w, held.h)
|
||||
-- through PixelCanvas, because this one is bound WITH held.depth a few
|
||||
-- lines down and the two must agree on pixel dimensions -- the same
|
||||
-- scale trap newDepth documents
|
||||
local ok, c = PixelCanvas.new(held.w, held.h)
|
||||
if not (ok and c) then return nil end
|
||||
pcall(c.setFilter, c, "nearest", "nearest")
|
||||
pcall(c.setWrap, c, "clamp", "clamp")
|
||||
|
||||
Reference in New Issue
Block a user