mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-29 15:47:26 +00:00
Displacement map prototype (#26709)
Requires https://github.com/space-wizards/RobustToolbox/pull/5023
This uses the new engine features (above) to add a displacement map shader. This allows deforming a sprite based on another sprite.
Primary use case is automatically adapting human clothing sprites to different species, something we want to make species like Vox a reality.
A basic example of wiring this up with Vox has been added. The system is however incredibly simple and **will** need more work by a content developer to select and toggle displacement maps when appropriate. I am leaving that to somebody else. For example right now the displacement map is applied even if a species already has custom-fit sprites for a piece of clothing, such as the grey jumpsuit for Vox.
Basic Aseprite plugins to help with authoring displacement maps have also been made.
(cherry picked from commit 2f7d0dedbd)
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
local sprite = app.editor.sprite
|
||||
local cel = app.cel
|
||||
|
||||
if sprite.selection.isEmpty then
|
||||
print("You need to select something sorry")
|
||||
return
|
||||
end
|
||||
|
||||
local diag = Dialog{
|
||||
title = "Flip Displacement Map"
|
||||
}
|
||||
|
||||
diag:check{
|
||||
id = "horizontal",
|
||||
label = "flip horizontal?"
|
||||
}
|
||||
|
||||
diag:check{
|
||||
id = "vertical",
|
||||
label = "flip vertical?"
|
||||
}
|
||||
|
||||
diag:button{
|
||||
text = "ok",
|
||||
focus = true,
|
||||
onclick = function(ev)
|
||||
local horizontal = diag.data["horizontal"]
|
||||
local vertical = diag.data["vertical"]
|
||||
|
||||
local selection = sprite.selection
|
||||
local image = cel.image:clone()
|
||||
|
||||
for x = 0, selection.bounds.width do
|
||||
for y = 0, selection.bounds.height do
|
||||
local xSel = x + selection.origin.x
|
||||
local ySel = y + selection.origin.y
|
||||
|
||||
local xImg = xSel - cel.position.x
|
||||
local yImg = ySel - cel.position.y
|
||||
|
||||
if xImg < 0 or xImg >= image.width or yImg < 0 or yImg >= image.height then
|
||||
goto continue
|
||||
end
|
||||
|
||||
local imgValue = image:getPixel(xImg, yImg)
|
||||
local color = Color(imgValue)
|
||||
|
||||
if horizontal then
|
||||
color.red = 128 + -(color.red - 128)
|
||||
end
|
||||
|
||||
if vertical then
|
||||
color.green = 128 + -(color.green - 128)
|
||||
end
|
||||
|
||||
image:drawPixel(
|
||||
xImg,
|
||||
yImg,
|
||||
app.pixelColor.rgba(color.red, color.green, color.blue, color.alpha))
|
||||
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
cel.image = image
|
||||
|
||||
diag:close()
|
||||
end
|
||||
}
|
||||
|
||||
diag:button{
|
||||
text = "cancel",
|
||||
onclick = function(ev)
|
||||
diag:close()
|
||||
end
|
||||
}
|
||||
|
||||
diag:show()
|
||||
@@ -0,0 +1,135 @@
|
||||
-- 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
|
||||
|
||||
-- This script requires UI
|
||||
if not app.isUIAvailable then
|
||||
return
|
||||
end
|
||||
|
||||
local getOffsetPixel = function(x, y, image, rect)
|
||||
local posX = x - rect.x
|
||||
local posY = y - rect.y
|
||||
|
||||
if posX < 0 or posX >= image.width or posY < 0 or posY >= image.height then
|
||||
return image.spec.transparentColor
|
||||
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)
|
||||
-- print(Color(displacement:getPixel(17, 15)).red)
|
||||
local image = target:clone()
|
||||
image:resize(width, height)
|
||||
image:clear()
|
||||
|
||||
for x = 0, width - 1 do
|
||||
for y = 0, height - 1 do
|
||||
local value = getOffsetPixel(x, y, displacement, displacementRect)
|
||||
local color = pixelValueToColor(sprite, value)
|
||||
|
||||
if color.alpha ~= 0 then
|
||||
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)
|
||||
image:drawPixel(x, y, colorValue)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return image
|
||||
end
|
||||
|
||||
local dialog = nil
|
||||
|
||||
local sprite = app.editor.sprite
|
||||
local spriteChanged = sprite.events:on("change",
|
||||
function(ev)
|
||||
dialog:repaint()
|
||||
end)
|
||||
|
||||
local layers = {}
|
||||
for i,layer in ipairs(sprite.layers) do
|
||||
table.insert(layers, 1, layer.name)
|
||||
end
|
||||
|
||||
local findLayer = function(sprite, name)
|
||||
for i, layer in ipairs(sprite.layers) do
|
||||
if layer.name == name then
|
||||
return layer
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
dialog = Dialog{
|
||||
title = "Displacement map preview",
|
||||
onclose = function(ev)
|
||||
sprite.events:off(spriteChanged)
|
||||
end}
|
||||
|
||||
dialog:canvas{
|
||||
id = "canvas",
|
||||
width = sprite.width * scale,
|
||||
height = sprite.height * scale,
|
||||
onpaint = function(ev)
|
||||
local context = ev.context
|
||||
|
||||
local layerDisplacement = findLayer(sprite, dialog.data["displacement-select"])
|
||||
local layerTarget = findLayer(sprite, dialog.data["reference-select"])
|
||||
-- print(layerDisplacement.name)
|
||||
-- print(layerTarget.name)
|
||||
local celDisplacement = layerDisplacement:cel(1)
|
||||
local celTarget = layerTarget:cel(1)
|
||||
local image = applyDisplacementMap(
|
||||
sprite.width, sprite.height,
|
||||
dialog.data["size"],
|
||||
celDisplacement.image, celDisplacement.bounds,
|
||||
celTarget.image, celTarget.bounds)
|
||||
|
||||
context:drawImage(image, 0, 0, image.width, image.height, 0, 0, image.width * scale, context.width, context.height)
|
||||
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:slider{
|
||||
id = "size",
|
||||
label = "displacement size",
|
||||
min = 1,
|
||||
max = 127,
|
||||
value = 127,
|
||||
onchange = function(ev)
|
||||
dialog:repaint()
|
||||
end
|
||||
}
|
||||
|
||||
dialog:show{wait = false}
|
||||
Reference in New Issue
Block a user