mirror of
https://github.com/Dadido3/noita-mapcap.git
synced 2024-11-18 17:17:31 +00:00
Add animation capture mode
This commit is contained in:
parent
3fd0d970b7
commit
d3edf29a80
2
.gitignore
vendored
2
.gitignore
vendored
@ -109,3 +109,5 @@ $RECYCLE.BIN/
|
|||||||
/bin/stitch/*.dzi
|
/bin/stitch/*.dzi
|
||||||
/bin/stitch/*_files/
|
/bin/stitch/*_files/
|
||||||
/files/magic-numbers/generated.xml
|
/files/magic-numbers/generated.xml
|
||||||
|
|
||||||
|
/bin/stitch/captures/*
|
@ -81,6 +81,10 @@ After a few minutes the file `output.png` will be created.
|
|||||||
- `Spiral`: Will capture the world in a spiral.
|
- `Spiral`: Will capture the world in a spiral.
|
||||||
The center starting point of the spiral can either be your current viewport, the world center or some custom coordinates.
|
The center starting point of the spiral can either be your current viewport, the world center or some custom coordinates.
|
||||||
|
|
||||||
|
- `Animation`: Will capture an image sequence.
|
||||||
|
This will capture whatever you see frame by frame and stores it in the output folder by frame number.
|
||||||
|
You can't stitch the resulting images, but instead you can use something like ffmpeg to render the sequence into a video file.
|
||||||
|
|
||||||
### Advanced mod settings
|
### Advanced mod settings
|
||||||
|
|
||||||
- `World seed`: If non empty, this will set the next new game to this seed.
|
- `World seed`: If non empty, this will set the next new game to this seed.
|
||||||
|
@ -166,6 +166,37 @@ local function captureScreenshot(pos, ensureLoaded, dontOverwrite, ctx, outputPi
|
|||||||
MonitorStandby.ResetTimer()
|
MonitorStandby.ResetTimer()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Captures a screenshot of the current viewport.
|
||||||
|
---This is used to capture animations, therefore the resulting image may not be suitable for stitching.
|
||||||
|
---@param outputPixelScale number? The resulting image pixel to world pixel ratio.
|
||||||
|
---@param frameNumber integer The frame number of the animation.
|
||||||
|
local function captureScreenshotAnimation(outputPixelScale, frameNumber)
|
||||||
|
if outputPixelScale == 0 or outputPixelScale == nil then
|
||||||
|
outputPixelScale = Coords:PixelScale()
|
||||||
|
end
|
||||||
|
|
||||||
|
local rectTopLeft, rectBottomRight = ScreenCapture.GetRect()
|
||||||
|
if not rectTopLeft or not rectBottomRight then
|
||||||
|
error(string.format("couldn't determine capturing rectangle"))
|
||||||
|
end
|
||||||
|
if Coords:InternalRectSize() ~= rectBottomRight - rectTopLeft then
|
||||||
|
error(string.format("internal rectangle size seems to have changed from %s to %s", Coords:InternalRectSize(), rectBottomRight - rectTopLeft))
|
||||||
|
end
|
||||||
|
|
||||||
|
local topLeftWorld, bottomRightWorld = Coords:ToWorld(rectTopLeft), Coords:ToWorld(rectBottomRight)
|
||||||
|
|
||||||
|
---We will use this to get our fame number into the filename.
|
||||||
|
---@type Vec2
|
||||||
|
local outputTopLeft = Vec2(frameNumber, 0)
|
||||||
|
|
||||||
|
if not ScreenCapture.Capture(rectTopLeft, rectBottomRight, outputTopLeft, (bottomRightWorld - topLeftWorld) * outputPixelScale) then
|
||||||
|
error(string.format("failed to capture screenshot"))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reset monitor and PC standby every screenshot.
|
||||||
|
MonitorStandby.ResetTimer()
|
||||||
|
end
|
||||||
|
|
||||||
---Map capture process runner context error handler callback. Just rolls off the tongue.
|
---Map capture process runner context error handler callback. Just rolls off the tongue.
|
||||||
---@param err string
|
---@param err string
|
||||||
---@param scope "init"|"do"|"end"
|
---@param scope "init"|"do"|"end"
|
||||||
@ -750,6 +781,56 @@ function Capture:StartCapturingPlayerPath(interval, outputPixelScale)
|
|||||||
self.PlayerPathCapturingCtx:Run(handleInit, handleDo, handleEnd, handleErr)
|
self.PlayerPathCapturingCtx:Run(handleInit, handleDo, handleEnd, handleErr)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Starts to capture an animation.
|
||||||
|
---This stores sequences of images that can't be stitched, but can be rendered into a video instead.
|
||||||
|
---Use `Capture.MapCapturingCtx` to stop, control or view the process.
|
||||||
|
---@param outputPixelScale number? -- The resulting image pixel to world pixel ratio.
|
||||||
|
function Capture:StartCapturingAnimation(outputPixelScale)
|
||||||
|
|
||||||
|
---Queries the mod settings for the live capture parameters.
|
||||||
|
---@return integer interval -- The interval length in frames.
|
||||||
|
local function querySettings()
|
||||||
|
local interval = 1--tonumber(ModSettingGet("noita-mapcap.live-interval")) or 30
|
||||||
|
return interval
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create file that signals that there are files in the output directory.
|
||||||
|
local file = io.open("mods/noita-mapcap/output/nonempty", "a")
|
||||||
|
if file ~= nil then file:close() end
|
||||||
|
|
||||||
|
---Process main callback.
|
||||||
|
---@param ctx ProcessRunnerCtx
|
||||||
|
local function handleDo(ctx)
|
||||||
|
Modification.SetCameraFree(false)
|
||||||
|
|
||||||
|
local frame = 0
|
||||||
|
|
||||||
|
repeat
|
||||||
|
local interval = querySettings()
|
||||||
|
|
||||||
|
-- Wait until we are allowed to take a new screenshot.
|
||||||
|
local delayFrames = 0
|
||||||
|
repeat
|
||||||
|
wait(0)
|
||||||
|
delayFrames = delayFrames + 1
|
||||||
|
until ctx:IsStopping() or delayFrames >= interval
|
||||||
|
|
||||||
|
captureScreenshotAnimation(outputPixelScale, frame)
|
||||||
|
|
||||||
|
frame = frame + 1
|
||||||
|
until ctx:IsStopping()
|
||||||
|
end
|
||||||
|
|
||||||
|
---Process end callback.
|
||||||
|
---@param ctx ProcessRunnerCtx
|
||||||
|
local function handleEnd(ctx)
|
||||||
|
Modification.SetCameraFree()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Run process, if there is no other running right now.
|
||||||
|
self.MapCapturingCtx:Run(nil, handleDo, handleEnd, mapCapturingCtxErrHandler)
|
||||||
|
end
|
||||||
|
|
||||||
---Starts the capturing process based on user/mod settings.
|
---Starts the capturing process based on user/mod settings.
|
||||||
function Capture:StartCapturing()
|
function Capture:StartCapturing()
|
||||||
Message:CatchException("Capture:StartCapturing", function()
|
Message:CatchException("Capture:StartCapturing", function()
|
||||||
@ -762,6 +843,8 @@ function Capture:StartCapturing()
|
|||||||
if mode == "live" then
|
if mode == "live" then
|
||||||
self:StartCapturingLive(outputPixelScale)
|
self:StartCapturingLive(outputPixelScale)
|
||||||
self:StartCapturingPlayerPath(5, outputPixelScale) -- Capture player path with an interval of 5 frames.
|
self:StartCapturingPlayerPath(5, outputPixelScale) -- Capture player path with an interval of 5 frames.
|
||||||
|
elseif mode == "animation" then
|
||||||
|
self:StartCapturingAnimation(outputPixelScale)
|
||||||
elseif mode == "area" then
|
elseif mode == "area" then
|
||||||
local area = ModSettingGet("noita-mapcap.area")
|
local area = ModSettingGet("noita-mapcap.area")
|
||||||
if area == "custom" then
|
if area == "custom" then
|
||||||
|
@ -120,7 +120,7 @@ function Check:Regular(interval)
|
|||||||
-- This is not perfect, as it doesn't take rounding and cropping into account, so the actual captured area may be a few pixels smaller.
|
-- This is not perfect, as it doesn't take rounding and cropping into account, so the actual captured area may be a few pixels smaller.
|
||||||
local mode = ModSettingGet("noita-mapcap.capture-mode")
|
local mode = ModSettingGet("noita-mapcap.capture-mode")
|
||||||
local captureGridSize = tonumber(ModSettingGet("noita-mapcap.grid-size"))
|
local captureGridSize = tonumber(ModSettingGet("noita-mapcap.grid-size"))
|
||||||
if mode ~= "live" and (Coords.VirtualResolution.x < captureGridSize or Coords.VirtualResolution.y < captureGridSize) then
|
if (mode ~= "live" and mode ~= "animation") and (Coords.VirtualResolution.x < captureGridSize or Coords.VirtualResolution.y < captureGridSize) then
|
||||||
Message:ShowGeneralSettingsProblem(
|
Message:ShowGeneralSettingsProblem(
|
||||||
"The virtual resolution is smaller than the capture grid size.",
|
"The virtual resolution is smaller than the capture grid size.",
|
||||||
"This means that you will get black areas in your final stitched image.",
|
"This means that you will get black areas in your final stitched image.",
|
||||||
|
@ -67,9 +67,9 @@ modSettings = {
|
|||||||
{
|
{
|
||||||
id = "capture-mode",
|
id = "capture-mode",
|
||||||
ui_name = "Mode",
|
ui_name = "Mode",
|
||||||
ui_description = "How the mod captures:\n- Live: Capture as you play along.\n- Area: Capture a defined area of the world.\n- Spiral: Capture in a spiral around a starting point indefinitely.",
|
ui_description = "How the mod captures:\n- Live: Capture as you play along.\n- Area: Capture a defined area of the world.\n- Spiral: Capture in a spiral around a starting point indefinitely.\n- Animation: Capture the screen frame by frame.",
|
||||||
value_default = "live",
|
value_default = "live",
|
||||||
values = { { "live", "Live" }, { "area", "Area" }, { "spiral", "Spiral" } },
|
values = { { "live", "Live" }, { "area", "Area" }, { "spiral", "Spiral" }, { "animation", "Animation"} },
|
||||||
scope = MOD_SETTING_SCOPE_RUNTIME,
|
scope = MOD_SETTING_SCOPE_RUNTIME,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -143,7 +143,7 @@ modSettings = {
|
|||||||
value_default = "512",
|
value_default = "512",
|
||||||
allowed_characters = "0123456789",
|
allowed_characters = "0123456789",
|
||||||
scope = MOD_SETTING_SCOPE_RUNTIME,
|
scope = MOD_SETTING_SCOPE_RUNTIME,
|
||||||
show_fn = function() return modSettings:GetNextValue("capture-mode") ~= "live" end,
|
show_fn = function() return modSettings:GetNextValue("capture-mode") == "area" or modSettings:GetNextValue("capture-mode") == "spiral" end,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id = "pixel-scale",
|
id = "pixel-scale",
|
||||||
@ -167,7 +167,7 @@ modSettings = {
|
|||||||
value_display_multiplier = 1,
|
value_display_multiplier = 1,
|
||||||
value_display_formatting = " $0 frames",
|
value_display_formatting = " $0 frames",
|
||||||
scope = MOD_SETTING_SCOPE_RUNTIME,
|
scope = MOD_SETTING_SCOPE_RUNTIME,
|
||||||
show_fn = function() return modSettings:GetNextValue("capture-mode") ~= "live" end,
|
show_fn = function() return modSettings:GetNextValue("capture-mode") == "area" or modSettings:GetNextValue("capture-mode") == "spiral" end,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id = "custom-resolution-live",
|
id = "custom-resolution-live",
|
||||||
|
Loading…
Reference in New Issue
Block a user