Files
tbd-station-14/Tools/SS14 Aseprite Plugins/Displacement Map Visualizer.lua
Southbridge 5ddf503331 Updated Aseprite Tools (#39358)
Modified aesprite tools
2025-08-28 17:44:50 -04:00

270 lines
6.7 KiB
Lua

-- Displacement Map Visualizer
--
-- This script will create a little preview window that will test a displacement map.
--
-- TODO: Handling of sizes != 127 doesn't work properly and rounds differently from the real shader. Ah well.
local scale = 4
local hasOobPixels = false
-- This script requires UI
if not app.isUIAvailable then
return
end
local sprite = app.editor.sprite
local spriteChanged = sprite.events:on("change",
function()
dialog:repaint()
end
)
dialog = Dialog{
title = "Displacement map preview",
onclose = function(ev)
sprite.events:off(spriteChanged)
end
}
function isOutOfBounds(x,y, dx, dy)
local size = dialog.data["frame-size"]
-- I messed around in Desmos for 2 hours trying to find a function that could do all of this at once
-- but I am sadly not a math major
-- This works by checking to see if we've wrapped around from say 31 to 01 which indicates that we've gone over
-- the edges of a sprite's bounds.
if dx > 0 and math.fmod(x+dx, size) < math.fmod(x, size) then
return true
end
-- gotta add size here in case we go from 0 -> -1, since mod -1 is just -1 not 31
if dx < 0 and math.fmod(x+size+dx, size) > math.fmod(x, size) then
return true
end
if dy > 0 and math.fmod(y+dy, size) < math.fmod(y, size) then
return true
end
if dy < 0 and math.fmod(y+size+dy, size) > math.fmod(y, size) then
return true
end
return false
end
function getOobColor(x,y)
if dialog.data["mark-oob-checkerboard"] then -- requested by Emogarbage :3
local size = dialog.data["frame-size"]
if (math.sin(math.pi*x*8.0/size) > 0) == (math.cos(math.pi*y*8.0/size) > 0) then
return Color{r=0, g=0, b=0, a=255}
end
end
return dialog.data["mark-oob-color"]
end
function getOffsetPixel(x, y, dx, dy, image, bounds)
if isOutOfBounds(x,y,dx,dy,image) then
hasOobPixels = true
if dialog.data["mark-oob"] then
return getOobColor(x,y)
end
end
local adj_x = x - bounds.x
local adj_y = y - bounds.y
if (image.bounds:contains(Rectangle{adj_x+dx, adj_y+dy, 1, 1})) then
return image:getPixel(adj_x+dx, adj_y+dy)
end
return image.spec.transparentColor
end
function applyDisplacementMap(width, height, displacement, target)
local image = target.image:clone()
image:resize(width, height)
image:clear()
local displacement_size = dialog.data["displacement_size"]
for x = 0, width - 1 do
for y = 0, height - 1 do
if not displacement.bounds:contains(Rectangle{x,y,1,1}) then
goto continue
end
local color = Color(displacement.image:getPixel(x - displacement.bounds.x,y - displacement.bounds.y))
if color.alpha == 0 then
goto continue
end
local dx = (color.red - 128) / 127 * displacement_size
local dy = (color.green - 128) / 127 * displacement_size
local colorValue = getOffsetPixel(x, y, dx, dy, target.image, target.bounds)
image:drawPixel(x, y, colorValue)
::continue::
end
end
return image
end
local layers = {}
for i,layer in ipairs(sprite.layers) do
table.insert(layers, 1, layer.name)
end
function findLayer(_sprite, name)
for i,layer in ipairs(_sprite.layers) do
if layer.name == name then
return layer
end
end
return nil
end
dialog:canvas{
id = "canvas",
width = sprite.width * scale,
height = sprite.height * scale,
onpaint = function(ev)
local context = ev.context
hasOobPixels = false
local layerDisplacement = findLayer(sprite, dialog.data["displacement-select"])
local layerTarget = findLayer(sprite, dialog.data["reference-select"])
local layerBackground = findLayer(sprite, dialog.data["background-select"])
-- print(layerDisplacement.name)
-- print(layerTarget.name)
local celDisplacement = layerDisplacement:cel(1)
local celTarget = layerTarget:cel(1)
local celBackground = layerBackground:cel(1)
-- Draw background
context:drawImage(
-- srcImage
celBackground.image,
-- srcPos
0, 0,
-- srcSize
celBackground.image.width, celBackground.image.height,
-- dstPos
celBackground.position.x * scale, celBackground.position.y * scale,
-- dstSize
celBackground.image.width * scale, celBackground.image.height * scale)
-- Apply displacement map and draw
local image = applyDisplacementMap(
sprite.width, sprite.height,
celDisplacement,
celTarget)
context:drawImage(
-- srcImage
image,
-- srcPos
0, 0,
-- srcSize
image.width, image.height,
-- dstPos
0, 0,
-- dstSize
image.width * scale, image.height * scale)
dialog:modify{
id = "oob-pixels-warn",
visible = hasOobPixels
}
end
}
dialog:combobox{
id = "displacement-select",
label = "displacement layer",
options = layers,
onchange = function(ev)
dialog:repaint()
end
}
dialog:combobox{
id = "reference-select",
label = "reference layer",
options = layers,
onchange = function(ev)
dialog:repaint()
end
}
dialog:combobox{
id = "background-select",
label = "background layer",
options = layers,
onchange = function(ev)
dialog:repaint()
end
}
dialog:slider{
id = "displacement_size",
label = "displacement size",
min = 127, --We dont support non 127 atm
max = 127,
value = 127,
onchange = function(ev)
dialog:repaint()
end
}
-- Out of Bounds marking
dialog:separator()
dialog:label{
id = "oob-pixels-warn",
text = "Warning: Out-of-bounds displacements detected!",
visible = false
}
dialog:check{
id = "mark-oob",
label = "Mark Out-of-Bounds Displacements",
selected = false,
hexpand = false,
onclick = function(ev)
dialog:repaint()
end
}
dialog:check{
id = "mark-oob-checkerboard",
label = "Checkerboard Pattern",
selected = false,
hexpand = false,
onclick = function(ev)
dialog:repaint()
end
}
dialog:number{
id = "frame-size",
label = "Frame Size",
text = "32",
hexpand = false,
onchange = function(ev)
dialog:repaint()
end
}
dialog:color{
id = "mark-oob-color",
label = "Out-of-Bounds Pixels Color",
color = Color{r = 255, g = 0, b = 0},
onchange = function(ev)
dialog:repaint()
end
}
dialog:show{wait = false}