parent
8aa6bdc284
commit
d3e912f41a
|
|
@ -30,8 +30,8 @@ diag:button{
|
||||||
local selection = sprite.selection
|
local selection = sprite.selection
|
||||||
local image = cel.image:clone()
|
local image = cel.image:clone()
|
||||||
|
|
||||||
for x = 0, selection.bounds.width do
|
for x = 0, selection.bounds.width - 1 do
|
||||||
for y = 0, selection.bounds.height do
|
for y = 0, selection.bounds.height - 1 do
|
||||||
local xSel = x + selection.origin.x
|
local xSel = x + selection.origin.x
|
||||||
local ySel = y + selection.origin.y
|
local ySel = y + selection.origin.y
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
local sprite = app.editor.sprite
|
||||||
|
local cel = app.cel
|
||||||
|
|
||||||
|
function Shift(dx, dy)
|
||||||
|
if sprite.selection.isEmpty then
|
||||||
|
sprite.selection:selectAll()
|
||||||
|
end
|
||||||
|
|
||||||
|
local selection = sprite.selection
|
||||||
|
local image = cel.image:clone()
|
||||||
|
|
||||||
|
for it in image:pixels(selection) do
|
||||||
|
local color = Color(it())
|
||||||
|
local position = Point(it.x, it.y) -- gets the position
|
||||||
|
|
||||||
|
if not selection:contains(position.x + cel.position.x, position.y + cel.position.y) then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
color.red = math.min(255, math.max(0, color.red + dx))
|
||||||
|
color.green = math.min(255, math.max(0, color.green + dy))
|
||||||
|
|
||||||
|
it(color.rgbaPixel)
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
cel.image = image
|
||||||
|
app.refresh()
|
||||||
|
end
|
||||||
|
|
||||||
|
local diag = Dialog{
|
||||||
|
title = "Shift Displacement Map"
|
||||||
|
}
|
||||||
|
|
||||||
|
diag
|
||||||
|
:button{
|
||||||
|
text="↑",
|
||||||
|
onclick=function()
|
||||||
|
Shift(0,1)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
:newrow()
|
||||||
|
:button{
|
||||||
|
text="←",
|
||||||
|
onclick=function()
|
||||||
|
Shift(1,0)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
:button{
|
||||||
|
text="→",
|
||||||
|
onclick=function()
|
||||||
|
Shift(-1,0)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
:newrow()
|
||||||
|
:button{
|
||||||
|
text="↓",
|
||||||
|
onclick=function()
|
||||||
|
Shift(0,-1)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
diag:show{wait=false}
|
||||||
|
|
@ -5,113 +5,133 @@
|
||||||
-- TODO: Handling of sizes != 127 doesn't work properly and rounds differently from the real shader. Ah well.
|
-- TODO: Handling of sizes != 127 doesn't work properly and rounds differently from the real shader. Ah well.
|
||||||
|
|
||||||
local scale = 4
|
local scale = 4
|
||||||
|
local hasOobPixels = false
|
||||||
|
|
||||||
-- This script requires UI
|
-- This script requires UI
|
||||||
if not app.isUIAvailable then
|
if not app.isUIAvailable then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local getOffsetPixel = function(x, y, image, rect)
|
local sprite = app.editor.sprite
|
||||||
local posX = x - rect.x
|
|
||||||
local posY = y - rect.y
|
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
|
||||||
|
|
||||||
if posX < 0 or posX >= image.width or posY < 0 or posY >= image.height then
|
|
||||||
return image.spec.transparentColor
|
return image.spec.transparentColor
|
||||||
end
|
end
|
||||||
|
|
||||||
return image:getPixel(posX, posY)
|
|
||||||
end
|
|
||||||
|
|
||||||
local pixelValueToColor = function(sprite, value)
|
|
||||||
return Color(value)
|
|
||||||
end
|
|
||||||
|
|
||||||
local applyDisplacementMap = function(width, height, size, displacement, displacementRect, target, targetRect)
|
function applyDisplacementMap(width, height, displacement, target)
|
||||||
-- print(Color(displacement:getPixel(17, 15)).red)
|
local image = target.image:clone()
|
||||||
local image = target:clone()
|
|
||||||
image:resize(width, height)
|
image:resize(width, height)
|
||||||
image:clear()
|
image:clear()
|
||||||
|
|
||||||
|
local displacement_size = dialog.data["displacement_size"]
|
||||||
|
|
||||||
for x = 0, width - 1 do
|
for x = 0, width - 1 do
|
||||||
for y = 0, height - 1 do
|
for y = 0, height - 1 do
|
||||||
local value = getOffsetPixel(x, y, displacement, displacementRect)
|
if not displacement.bounds:contains(Rectangle{x,y,1,1}) then
|
||||||
local color = pixelValueToColor(sprite, value)
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
if color.alpha ~= 0 then
|
local color = Color(displacement.image:getPixel(x - displacement.bounds.x,y - displacement.bounds.y))
|
||||||
local offset_x = (color.red - 128) / 127 * size
|
|
||||||
local offset_y = (color.green - 128) / 127 * size
|
|
||||||
|
|
||||||
local colorValue = getOffsetPixel(x + offset_x, y + offset_y, target, targetRect)
|
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)
|
image:drawPixel(x, y, colorValue)
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
end
|
||||||
return image
|
return image
|
||||||
end
|
end
|
||||||
|
|
||||||
local dialog = nil
|
|
||||||
|
|
||||||
local sprite = app.editor.sprite
|
|
||||||
local spriteChanged = sprite.events:on("change",
|
|
||||||
function(ev)
|
|
||||||
dialog:repaint()
|
|
||||||
end)
|
|
||||||
|
|
||||||
local layers = {}
|
local layers = {}
|
||||||
for i,layer in ipairs(sprite.layers) do
|
for i,layer in ipairs(sprite.layers) do
|
||||||
table.insert(layers, 1, layer.name)
|
table.insert(layers, 1, layer.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
local findLayer = function(sprite, name)
|
function findLayer(_sprite, name)
|
||||||
for i, layer in ipairs(sprite.layers) do
|
for i,layer in ipairs(_sprite.layers) do
|
||||||
if layer.name == name then
|
if layer.name == name then
|
||||||
return layer
|
return layer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local applyOffset = function(dx, dy)
|
|
||||||
local cel = app.cel
|
|
||||||
local image = cel.image:clone()
|
|
||||||
local sprite = app.editor.sprite
|
|
||||||
local selection = sprite.selection
|
|
||||||
|
|
||||||
for x = selection.bounds.x, selection.bounds.x + selection.bounds.width - 1 do
|
|
||||||
for y = selection.bounds.y, selection.bounds.y + selection.bounds.height - 1 do
|
|
||||||
local xImg = x - cel.position.x
|
|
||||||
local yImg = y - cel.position.y
|
|
||||||
if xImg >= 0 and xImg < image.width and yImg >= 0 and yImg < image.height then
|
|
||||||
local pixelValue = image:getPixel(xImg, yImg)
|
|
||||||
local color = Color(pixelValue)
|
|
||||||
|
|
||||||
-- Offset R and G channel
|
|
||||||
color.red = math.min(255, math.max(0, color.red + dx))
|
|
||||||
color.green = math.min(255, math.max(0, color.green + dy))
|
|
||||||
|
|
||||||
image:drawPixel(xImg, yImg, app.pixelColor.rgba(color.red, color.green, color.blue, color.alpha))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
cel.image = image
|
|
||||||
dialog:repaint()
|
|
||||||
end
|
|
||||||
|
|
||||||
dialog = Dialog{
|
|
||||||
title = "Displacement map preview",
|
|
||||||
onclose = function(ev)
|
|
||||||
sprite.events:off(spriteChanged)
|
|
||||||
end}
|
|
||||||
|
|
||||||
dialog:canvas{
|
dialog:canvas{
|
||||||
id = "canvas",
|
id = "canvas",
|
||||||
width = sprite.width * scale,
|
width = sprite.width * scale,
|
||||||
height = sprite.height * scale,
|
height = sprite.height * scale,
|
||||||
onpaint = function(ev)
|
onpaint = function(ev)
|
||||||
local context = ev.context
|
local context = ev.context
|
||||||
|
hasOobPixels = false
|
||||||
|
|
||||||
local layerDisplacement = findLayer(sprite, dialog.data["displacement-select"])
|
local layerDisplacement = findLayer(sprite, dialog.data["displacement-select"])
|
||||||
local layerTarget = findLayer(sprite, dialog.data["reference-select"])
|
local layerTarget = findLayer(sprite, dialog.data["reference-select"])
|
||||||
|
|
@ -139,9 +159,8 @@ dialog:canvas{
|
||||||
-- Apply displacement map and draw
|
-- Apply displacement map and draw
|
||||||
local image = applyDisplacementMap(
|
local image = applyDisplacementMap(
|
||||||
sprite.width, sprite.height,
|
sprite.width, sprite.height,
|
||||||
dialog.data["size"],
|
celDisplacement,
|
||||||
celDisplacement.image, celDisplacement.bounds,
|
celTarget)
|
||||||
celTarget.image, celTarget.bounds)
|
|
||||||
|
|
||||||
context:drawImage(
|
context:drawImage(
|
||||||
-- srcImage
|
-- srcImage
|
||||||
|
|
@ -154,6 +173,10 @@ dialog:canvas{
|
||||||
0, 0,
|
0, 0,
|
||||||
-- dstSize
|
-- dstSize
|
||||||
image.width * scale, image.height * scale)
|
image.width * scale, image.height * scale)
|
||||||
|
dialog:modify{
|
||||||
|
id = "oob-pixels-warn",
|
||||||
|
visible = hasOobPixels
|
||||||
|
}
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,7 +208,7 @@ dialog:combobox{
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog:slider{
|
dialog:slider{
|
||||||
id = "size",
|
id = "displacement_size",
|
||||||
label = "displacement size",
|
label = "displacement size",
|
||||||
min = 127, --We dont support non 127 atm
|
min = 127, --We dont support non 127 atm
|
||||||
max = 127,
|
max = 127,
|
||||||
|
|
@ -195,35 +218,51 @@ dialog:slider{
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog:button{
|
-- Out of Bounds marking
|
||||||
id = "moveDown",
|
dialog:separator()
|
||||||
text = "Down",
|
|
||||||
|
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)
|
onclick = function(ev)
|
||||||
applyOffset(0, -1)
|
dialog:repaint()
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog:button{
|
dialog:check{
|
||||||
id = "moveUp",
|
id = "mark-oob-checkerboard",
|
||||||
text = "Up",
|
label = "Checkerboard Pattern",
|
||||||
|
selected = false,
|
||||||
|
hexpand = false,
|
||||||
onclick = function(ev)
|
onclick = function(ev)
|
||||||
applyOffset(0, 1)
|
dialog:repaint()
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog:button{
|
dialog:number{
|
||||||
id = "moveLeft",
|
id = "frame-size",
|
||||||
text = "Left",
|
label = "Frame Size",
|
||||||
onclick = function(ev)
|
text = "32",
|
||||||
applyOffset(1, 0)
|
hexpand = false,
|
||||||
|
onchange = function(ev)
|
||||||
|
dialog:repaint()
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog:button{
|
dialog:color{
|
||||||
id = "moveRight",
|
id = "mark-oob-color",
|
||||||
text = "Right",
|
label = "Out-of-Bounds Pixels Color",
|
||||||
onclick = function(ev)
|
color = Color{r = 255, g = 0, b = 0},
|
||||||
applyOffset(-1, 0)
|
onchange = function(ev)
|
||||||
|
dialog:repaint()
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue