Several updates

- Add full map capture mode
- Show error on screen if screencapturing failed
- Add additional frame delay for jumps larger than the grid size
- Show progress for map capture mode
- Tweak streaming chunk target
- Update README.md
This commit is contained in:
David Vogel 2019-11-02 21:37:10 +01:00
parent c87a4d05d0
commit 8cf46232de
7 changed files with 166 additions and 13 deletions

View File

@ -22,7 +22,9 @@ A resulting image with close to 3 gigapixels can be [seen here](https://easyzoom
3. Unpack it into your mods folder, so that you get the following file structure `.../Noita/mods/noita-mapcap/mod.xml`.
4. Set your resolution to 1280x720, and use the `Windowed` mode. (Not `Fullscreen (Windowed)`!) If you have to use a different resolution, see advanced usage.
5. Enable the mod and restart Noita.
6. In the game you should see a `>> Start capturing map <<` text on the screen, click it.
6. In the game you should see text on screen.
- Either press `>> Start capturing map around view <<` to capture in a spiral around your current view.
- Or press `>> Start capturing full map <<` to capture the whole map.
7. The screen will jump around, and the game will take screenshots automatically.
- Screenshots are saved in `.../Noita/mods/noita-mapcap/output/`.
- Don't cover the game window.
@ -50,6 +52,8 @@ The following two formulae have to be true:
You can also change how much the tiles overlap by adjusting the `CAPTURE_GRID_SIZE` in `.../Noita/mods/noita-mapcap/files/capture.lua`. If you increase the grid size, you can capture more area per time. But on the other hand the stitcher may not be able to remove artifacts if the tiles don't overlap enough.
The rectangle for the full map capture mode is defined in `.../Noita/mods/noita-mapcap/files/capture.lua`.
## License
[MIT](LICENSE)

View File

@ -6,8 +6,14 @@
CAPTURE_PIXEL_SIZE = 1 -- Screen to virtual pixel ratio
CAPTURE_GRID_SIZE = 420 -- in ingame pixels. There will always be 3 to 6 images overlapping
CAPTURE_DELAY = 15 -- in frames
CAPTURE_BIGJUMP_DELAY = 20 -- in frames. Additional delay after doing a "larger than grid jump"
CAPTURE_FORCE_HP = 4 -- * 25HP
CAPTURE_LEFT = -25000 -- in ingame pixels. Left edge of the full map capture rectangle
CAPTURE_TOP = -36000 -- in ingame pixels. Top edge of the full map capture rectangle
CAPTURE_RIGHT = 25000 -- in ingame pixels. Right edge of the full map capture rectangle (Pixels are not included in the rectangle)
CAPTURE_BOTTOM = 36000 -- in ingame pixels. Bottom edge of the full map capture rectangle (Pixels are not included in the rectangle)
local function preparePlayer()
local playerEntity = getPlayer()
addEffectToEntity(playerEntity, "PROTECTION_ALL")
@ -25,9 +31,8 @@ local function resetPlayer()
setPlayerHP(CAPTURE_FORCE_HP)
end
function startCapturing()
function startCapturingSpiral()
local ox, oy = GameGetCameraPos()
--getPlayerPos()
ox, oy = math.floor(ox / CAPTURE_GRID_SIZE) * CAPTURE_GRID_SIZE, math.floor(oy / CAPTURE_GRID_SIZE) * CAPTURE_GRID_SIZE
local x, y = ox, oy
@ -47,7 +52,9 @@ function startCapturing()
wait(CAPTURE_DELAY - 1)
UiHide = true -- Hide UI while capturing the screenshot
wait(1)
TriggerCapture(rx, ry)
if not TriggerCapture(rx, ry) then
UiCaptureProblem = "Screen capture failed. Please restart Noita."
end
UiHide = false
end
x, y = x + CAPTURE_GRID_SIZE, y
@ -60,7 +67,9 @@ function startCapturing()
wait(CAPTURE_DELAY - 1)
UiHide = true
wait(1)
TriggerCapture(rx, ry)
if not TriggerCapture(rx, ry) then
UiCaptureProblem = "Screen capture failed. Please restart Noita."
end
UiHide = false
end
x, y = x, y + CAPTURE_GRID_SIZE
@ -74,7 +83,9 @@ function startCapturing()
wait(CAPTURE_DELAY - 1)
UiHide = true
wait(1)
TriggerCapture(rx, ry)
if not TriggerCapture(rx, ry) then
UiCaptureProblem = "Screen capture failed. Please restart Noita."
end
UiHide = false
end
x, y = x - CAPTURE_GRID_SIZE, y
@ -87,7 +98,9 @@ function startCapturing()
wait(CAPTURE_DELAY - 1)
UiHide = true
wait(1)
TriggerCapture(rx, ry)
if not TriggerCapture(rx, ry) then
UiCaptureProblem = "Screen capture failed. Please restart Noita."
end
UiHide = false
end
x, y = x, y - CAPTURE_GRID_SIZE
@ -96,3 +109,63 @@ function startCapturing()
end
)
end
function startCapturingHilbert()
local ox, oy = GameGetCameraPos()
-- Get size of the rectangle in grid/chunk coordinates
local gridLeft = math.floor(CAPTURE_LEFT / CAPTURE_GRID_SIZE)
local gridTop = math.floor(CAPTURE_TOP / CAPTURE_GRID_SIZE)
local gridRight = math.ceil(CAPTURE_RIGHT / CAPTURE_GRID_SIZE) + 1
local gridBottom = math.ceil(CAPTURE_BOTTOM / CAPTURE_GRID_SIZE) + 1
-- Size of the grid in chunks
local gridWidth = gridRight - gridLeft
local gridHeight = gridBottom - gridTop
-- Hilbert curve can only fit into a square, so get the longest side
local gridPOTSize = math.ceil(math.log(math.max(gridWidth, gridHeight)) / math.log(2))
-- Max size (Already rounded up to the next power of two)
local gridMaxSize = math.pow(2, gridPOTSize)
local t, tLimit = 0, gridMaxSize * gridMaxSize
UiProgress = {Progress = 0, Max = gridWidth * gridHeight}
preparePlayer()
GameSetCameraFree(true)
-- Coroutine to calculate next coordinate, and trigger screenshots
async(
function()
while t < tLimit do
local hx, hy = mapHilbert(t, gridPOTSize)
if hx < gridWidth and hy < gridHeight then
local x, y = (hx + gridLeft) * CAPTURE_GRID_SIZE, (hy + gridTop) * CAPTURE_GRID_SIZE
local rx, ry = x * CAPTURE_PIXEL_SIZE, y * CAPTURE_PIXEL_SIZE
if not fileExists(string.format("mods/noita-mapcap/output/%d,%d.png", rx, ry)) then
GameSetCameraPos(x, y)
-- "Larger than grid jump" delay
if math.abs(x - ox) > CAPTURE_GRID_SIZE or math.abs(y - oy) > CAPTURE_GRID_SIZE then
wait(CAPTURE_BIGJUMP_DELAY)
end
ox, oy = x, y
wait(CAPTURE_DELAY - 1)
UiHide = true -- Hide UI while capturing the screenshot
wait(1)
if not TriggerCapture(rx, ry) then
UiCaptureProblem = "Screen capture failed. Please restart Noita."
end
UiHide = false
end
UiProgress.Progress = UiProgress.Progress + 1
end
t = t + 1
end
end
)
end

48
files/hilbert.lua Normal file
View File

@ -0,0 +1,48 @@
-- Copyright (c) 2019 David Vogel
--
-- This software is released under the MIT License.
-- https://opensource.org/licenses/MIT
local function hilbertRotate(n, x, y, rx, ry)
if not ry then
if rx then
x = n - 1 - x
y = n - 1 - y
end
x, y = y, x
end
return x, y
end
-- Maps a variable t to a hilbert curve with the side length of 2^potSize (Power of two size)
function mapHilbert(t, potSize)
local size = math.pow(2, potSize)
local x, y = 0, 0
if t < 0 or t >= size * size then
error("Variable t is outside of the range")
end
for i = 0, potSize - 1, 1 do
local iPOT = math.pow(2, i)
local rx = bit.band(t, 2) == 2
local ry = bit.band(t, 1) == 1
if rx then
ry = not ry
end
x, y = hilbertRotate(iPOT, x, y, rx, ry)
if rx then
x = x + iPOT
end
if ry then
y = y + iPOT
end
t = math.floor(t / 4)
end
return x, y
end

View File

@ -10,6 +10,7 @@ dofile("data/scripts/perks/perk_list.lua")
dofile("mods/noita-mapcap/files/compatibility.lua")
dofile("mods/noita-mapcap/files/util.lua")
dofile("mods/noita-mapcap/files/hilbert.lua")
dofile("mods/noita-mapcap/files/external.lua")
dofile("mods/noita-mapcap/files/capture.lua")
dofile("mods/noita-mapcap/files/ui.lua")

View File

@ -1,12 +1,11 @@
<MagicNumbers VIRTUAL_RESOLUTION_X="1280"
VIRTUAL_RESOLUTION_Y="720"
STREAMING_CHUNK_TARGET="24"
STREAMING_CHUNK_TARGET="16"
DRAW_PARALLAX_BACKGROUND="0"
DEBUG_FREE_CAMERA_SPEED="10"
DEBUG_NO_LOGO_SPLASHES="1"
DEBUG_PAUSE_GRID_UPDATE="1"
DEBUG_PAUSE_BOX2D="1"
DEBUG_DISABLE_POSTFX_DITHERING="1"
DEBUG_NO_PAUSE_ON_WINDOW_FOCUS_LOST="1"
DEBUG_LUA="1">
DEBUG_NO_PAUSE_ON_WINDOW_FOCUS_LOST="1">
</MagicNumbers>

View File

@ -5,6 +5,8 @@
UiHide = false
local UiReduce = false
UiProgress = nil
UiCaptureProblem = nil
async_loop(
function()
@ -86,7 +88,7 @@ async_loop(
end
GuiTextCentered(modGUI, 0, 0, "You can freely look around and search a place to start capturing.")
GuiTextCentered(modGUI, 0, 0, "The mod will then take images in a spiral around your current view.")
GuiTextCentered(modGUI, 0, 0, "When started the mod will take pictures automatically.")
GuiTextCentered(modGUI, 0, 0, "Use ESC to pause, and close the game to stop the process.")
GuiTextCentered(
modGUI,
@ -102,8 +104,12 @@ async_loop(
'If you want to start a new map, you have to delete all images from the "output" folder!'
)
GuiTextCentered(modGUI, 0, 0, " ")
if GuiButton(modGUI, 0, 0, ">> Start capturing map <<", 1) then
startCapturing()
if GuiButton(modGUI, 0, 0, ">> Start capturing map around view <<", 1) then
startCapturingSpiral()
UiReduce = true
end
if GuiButton(modGUI, 0, 0, ">> Start capturing full map <<", 1) then
startCapturingHilbert()
UiReduce = true
end
GuiTextCentered(modGUI, 0, 0, " ")
@ -111,6 +117,20 @@ async_loop(
if not UiHide then
local x, y = GameGetCameraPos()
GuiTextCentered(modGUI, 0, 0, string.format("Coordinates: %d, %d", x, y))
if UiProgress then
GuiTextCentered(
modGUI,
0,
0,
progressBarString(
UiProgress,
{BarLength = 100, CharFull = "l", CharEmpty = ".", Format = "|%s| [%d / %d] [%1.2f%%]"}
)
)
end
if UiCaptureProblem then
GuiTextCentered(modGUI, 0, 0, string.format("A problem occurred while capturing: %s", UiCaptureProblem))
end
end
GuiLayoutEnd(modGUI)
end

View File

@ -97,3 +97,11 @@ function fileExists(fileName)
return false
end
end
function progressBarString(progress, look)
local factor = progress.Progress / progress.Max
local count = math.ceil(look.BarLength * factor)
local barString = string.rep(look.CharFull, count) .. string.rep(look.CharEmpty, look.BarLength - count)
return string.format(look.Format, barString, progress.Progress, progress.Max, factor * 100)
end