diorama_shots.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
-- Driver: the DIORAMA mode's own frame, without a headset. -- -- The diorama is drawn through VoxelScene's `eyes` path, and that path only -- ever runs from lib/VR -- so with no OpenXR runtime on the machine there is -- nothing to look at and nothing to check. This builds ONE eye by hand (the -- same VRRig mapping the headset would have built), opens a diorama frame, -- and encodes the eye canvas straight to a PNG. -- -- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/diorama_shots.lua \ -- SHOT_DIR=<dir> lovec.exe . -- -- knobs (env): -- SHOT_DIR output directory under the LOVE save dir (default "diorama") -- DIO_MAP map id (default VIRIDIAN_CITY) -- DIO_SPOT "x,y[,facing]" (default 20,26,up) -- DIO_RUNG the voxel camera rung (default 5, the 75 one) -- -- It also prints whether each shader the mode touches actually COMPILED, -- which is the part a headless suite cannot answer: the cull is new source -- in the scene shader, the water shader and both forest ones, and a shader -- that will not build fails soft everywhere in this mod -- the picture just -- quietly loses a pass. return function(game) local U = dofile("tests/drivers/util.lua") local Pipelines = require("src.render.Pipelines") local ROOT = os.getenv("SHOT_DIR") or "diorama" local handle = game.mods.exports["DRAMATIC_SHAPE"] if not (handle and handle.lib) then print("[dio] DRAMATIC_SHAPE mod not loaded -- nothing to shoot") return end local V = handle.lib local Voxel = V.require("VoxelState") local Voxel3D = V.require("Voxel3D") local VoxelScene = V.require("VoxelScene") local VRRig = V.require("VRRig") local Diorama = V.require("Diorama") local Water = V.require("Water") local ChunkMesher = V.require("ChunkMesher") local DayNight = V.require("DayNight") local VR = V.require("VR") local Curve = V.require("WorldCurve") local MAP = os.getenv("DIO_MAP") or "VIRIDIAN_CITY" local SPOT = os.getenv("DIO_SPOT") or "20,26,up" local RUNG = math.floor(tonumber(os.getenv("DIO_RUNG")) or 5) local sx, sy, sf = SPOT:match("^(%-?%d+),%s*(%-?%d+),?%s*(%a*)$") sx, sy = tonumber(sx) or 20, tonumber(sy) or 26 if sf == "" then sf = "up" end require("src.world.OverworldController").rollEncounter = function() return nil end DayNight.setting:sync("day") U.teleport(game, MAP, sx, sy, sf) Pipelines.setLevel("voxel", RUNG) Pipelines.setLevel("tiltshift", 0) local function settle() for _ = 1, 900 do if ChunkMesher.pending() == 0 then break end U.wait(1) end for _ = 1, 300 do if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end U.wait(1) end U.wait(20) end settle() settle() print(("[dio] scene shader %s | grid variant %s | water %s") :format(tostring(Voxel3D.shader() ~= nil), tostring(Voxel3D.shader(true) ~= nil), tostring(Water.shader(false) ~= nil))) -- one eye, straight ahead from the resting head: the mapping lib/VR would -- have built for this rung, with no pose to read off a runtime local POSE = { pos = { 0, 0, 0 }, quat = { 0, 0, 0, 1 } } local FOV = { angleLeft = -0.8, angleRight = 0.8, angleUp = 0.7, angleDown = -0.7 } local function shoot(name, opts) local ow = game.overworld local vw, vh = 320, 288 pcall(function() vw, vh = game.renderer:worldViewSize() end) local cx = ow.camera.x + vw / 2 local cy = ow.camera.y + vh / 2 Diorama.begin(opts.mode or "diorama") Diorama.zoom = opts.zoom or 1 Diorama.yaw = opts.yaw or 0 -- the cut's SHAPE is the V-CURVE row's (box while flat, ball while -- bent), so the driver throws the row rather than asking for a shape Curve.setting:sync(opts.curve or 0) if opts.arena then Diorama.pillar(opts.arena) else Diorama.viewport(cx, cy, vh) end local pivot = VRRig.dioramaPivot(Diorama.cull.x, Diorama.cull.z) local anchor = VRRig.dioramaAnchor(Voxel.angle, Diorama.offset) -- the same framing lib/VR picks: the view ordinarily, the DISC while a -- fight is staged local frame = opts.arena and (Diorama.cull.r * 2.6) or vh local scale = VRRig.dioramaScale(frame, Voxel.FOCAL) -- the bend the diorama asks its eyes for (lib/VR does the same) local curveK = Curve.k(vh) local eyes = { { camera = VRRig.eyeCamera(POSE, FOV, pivot, anchor, scale, opts.yaw ~= 0 and opts.yaw or nil, curveK), w = 720, h = 720, slot = "vrL", adopt = false }, cx = pivot[1], cy = pivot[3], } VoxelScene.spriteLean = math.rad(75) local ok, out = pcall(VoxelScene.render, ow, 0, 0, vw, vh, VR.paletteFor, eyes) VoxelScene.spriteLean = nil if not (ok and out and out[1]) then print("[dio] " .. name .. ": no frame (" .. tostring(out) .. ")") Diorama.stop() return end local okE, err = pcall(function() love.filesystem.createDirectory(ROOT) local data = out[1]:newImageData() data:encode("png", ROOT .. "/" .. name .. ".png") end) print("[dio] " .. name .. (okE and " written" or (" ENCODE FAILED: " .. tostring(err)))) Diorama.stop() end shoot("box", { mode = "diorama" }) shoot("box-turned", { mode = "diorama", yaw = math.rad(35) }) shoot("box-tight", { mode = "diorama", zoom = 0.5 }) shoot("ball-3", { mode = "diorama", curve = 3 }) shoot("ball-4", { mode = "diorama", curve = 4 }) shoot("ball-5", { mode = "diorama", curve = 5 }) shoot("ball-wide", { mode = "diorama", curve = 5, zoom = 1.9 }) shoot("keyed", { mode = "diorama-mr" }) shoot("keyed-ball", { mode = "diorama-mr", curve = 3 }) do -- a fight's disc, cut about the arena the map would actually stage on local BattleArena = V.require("BattleArena") local ow = game.overworld local arena = BattleArena.find(ow.map, ow.player.cellX, ow.player.cellY, false) if arena then shoot("arena-disc", { mode = "diorama", arena = arena }) else print("[dio] no arena on this map -- disc shot skipped") end end Diorama.stop() print("[dio] done; PNGs are under the LOVE save directory / " .. ROOT) love.event.quit() end