mirror of
https://github.com/Insality/druid.git
synced 2025-09-27 18:12:19 +02:00
Update
This commit is contained in:
83
druid/custom/tiling_node/gui_tiling_node.fp
Normal file
83
druid/custom/tiling_node/gui_tiling_node.fp
Normal file
@@ -0,0 +1,83 @@
|
||||
#version 140
|
||||
|
||||
uniform sampler2D texture_sampler;
|
||||
|
||||
in vec2 var_texcoord0;
|
||||
in vec4 var_color;
|
||||
in vec4 var_uv;
|
||||
in vec4 var_repeat; // [repeat_x, repeat_y, anchor_x, anchor_y]
|
||||
in vec4 var_params; // [margin_x, margin_y, offset_x, offset_y]
|
||||
in vec4 var_uv_rotated;
|
||||
|
||||
out vec4 color_out;
|
||||
|
||||
void main() {
|
||||
vec2 pivot = var_repeat.zw;
|
||||
// Margin is a value between 0 and 1 that means offset/padding from the one image to another
|
||||
vec2 margin = var_params.xy;
|
||||
vec2 offset = var_params.zw;
|
||||
vec2 repeat = var_repeat.xy;
|
||||
|
||||
// Atlas UV to local UV [0, 1]
|
||||
float u = (var_texcoord0.x - var_uv.x) / (var_uv.z - var_uv.x);
|
||||
float v = (var_texcoord0.y - var_uv.y) / (var_uv.w - var_uv.y);
|
||||
|
||||
// Adjust local UV by the pivot point. So 0:0 will be at the pivot point of node
|
||||
u = u - (0.5 + pivot.x);
|
||||
v = v - (0.5 - pivot.y);
|
||||
|
||||
// If rotated, swap UV
|
||||
if (var_uv_rotated.y < 0.5) {
|
||||
float temp = u;
|
||||
u = v;
|
||||
v = temp;
|
||||
}
|
||||
|
||||
// Adjust repeat by the margin
|
||||
repeat.x = repeat.x / (1.0 + margin.x);
|
||||
repeat.y = repeat.y / (1.0 + margin.y);
|
||||
|
||||
// Repeat is a value between 0 and 1 that represents the number of times the texture is repeated in the atlas.
|
||||
float tile_u = fract(u * repeat.x);
|
||||
float tile_v = fract(v * repeat.y);
|
||||
|
||||
float tile_width = 1.0 / repeat.x;
|
||||
float tile_height = 1.0 / repeat.y;
|
||||
|
||||
// Adjust tile UV by the pivot point.
|
||||
// Not center is left top corner, need to adjust it to pivot point
|
||||
tile_u = fract(tile_u + pivot.x + 0.5);
|
||||
tile_v = fract(tile_v - pivot.y + 0.5);
|
||||
|
||||
// Apply offset
|
||||
tile_u = fract(tile_u + offset.x);
|
||||
tile_v = fract(tile_v + offset.y);
|
||||
|
||||
// Extend margins
|
||||
margin = margin * 0.5;
|
||||
tile_u = mix(0.0 - margin.x, 1.0 + margin.x, tile_u);
|
||||
tile_v = mix(0.0 - margin.y, 1.0 + margin.y, tile_v);
|
||||
float alpha = 0.0;
|
||||
// If the tile is outside the margins, make it transparent, without IF
|
||||
alpha = step(0.0, tile_u) * step(tile_u, 1.0) * step(0.0, tile_v) * step(tile_v, 1.0);
|
||||
|
||||
tile_u = clamp(tile_u, 0.0, 1.0); // Keep borders in the range 0-1
|
||||
tile_v = clamp(tile_v, 0.0, 1.0); // Keep borders in the range 0-1
|
||||
|
||||
if (var_uv_rotated.y < 0.5) {
|
||||
float temp = tile_u;
|
||||
tile_u = tile_v;
|
||||
tile_v = temp;
|
||||
}
|
||||
|
||||
// Remap local UV to the atlas UV
|
||||
vec2 uv = vec2(
|
||||
mix(var_uv.x, var_uv.z, tile_u), // Get texture coordinate from the atlas
|
||||
mix(var_uv.y, var_uv.w, tile_v) // Get texture coordinate from the atlas
|
||||
//mix(var_uv.x, var_uv.z, tile_u * var_uv_rotated.x + tile_v * var_uv_rotated.z),
|
||||
//mix(var_uv.y, var_uv.w, 1.0 - (tile_u * var_uv_rotated.y + tile_v * var_uv_rotated.x))
|
||||
);
|
||||
|
||||
lowp vec4 tex = texture(texture_sampler, uv);
|
||||
color_out = tex * var_color;
|
||||
}
|
37
druid/custom/tiling_node/gui_tiling_node.material
Normal file
37
druid/custom/tiling_node/gui_tiling_node.material
Normal file
@@ -0,0 +1,37 @@
|
||||
name: "repeat"
|
||||
tags: "gui"
|
||||
vertex_program: "/druid/custom/tiling_node/gui_tiling_node.vp"
|
||||
fragment_program: "/druid/custom/tiling_node/gui_tiling_node.fp"
|
||||
vertex_constants {
|
||||
name: "view_proj"
|
||||
type: CONSTANT_TYPE_VIEWPROJ
|
||||
}
|
||||
vertex_constants {
|
||||
name: "uv_coord"
|
||||
type: CONSTANT_TYPE_USER
|
||||
value {
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
}
|
||||
vertex_constants {
|
||||
name: "uv_repeat"
|
||||
type: CONSTANT_TYPE_USER
|
||||
value {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
}
|
||||
}
|
||||
vertex_constants {
|
||||
name: "params"
|
||||
type: CONSTANT_TYPE_USER
|
||||
value {
|
||||
}
|
||||
}
|
||||
vertex_constants {
|
||||
name: "uv_rotated"
|
||||
type: CONSTANT_TYPE_USER
|
||||
value {
|
||||
x: 1.0
|
||||
}
|
||||
}
|
40
druid/custom/tiling_node/gui_tiling_node.vp
Normal file
40
druid/custom/tiling_node/gui_tiling_node.vp
Normal file
@@ -0,0 +1,40 @@
|
||||
#version 140
|
||||
|
||||
in mediump vec3 position;
|
||||
in mediump vec2 texcoord0;
|
||||
in lowp vec4 color;
|
||||
|
||||
uniform vertex_inputs
|
||||
{
|
||||
highp mat4 view_proj;
|
||||
highp vec4 uv_coord;
|
||||
highp vec4 uv_repeat; // [repeat_x, repeat_y, pivot_x, pivot_y]
|
||||
vec4 uv_rotated;
|
||||
vec4 params; // [margin_x, margin_y, offset_x, offset_y]
|
||||
};
|
||||
|
||||
out mediump vec2 var_texcoord0;
|
||||
out lowp vec4 var_color;
|
||||
out highp vec4 var_uv;
|
||||
out highp vec4 var_repeat;
|
||||
out vec4 var_params;
|
||||
out vec4 var_uv_rotated;
|
||||
|
||||
void main()
|
||||
{
|
||||
var_texcoord0 = texcoord0;
|
||||
var_color = vec4(color.rgb * color.a, color.a);
|
||||
var_uv = uv_coord;
|
||||
var_repeat = uv_repeat;
|
||||
var_params = params;
|
||||
var_uv_rotated = uv_rotated;
|
||||
|
||||
mat4 transform = mat4(
|
||||
1.0, 0, 0, 0.0,
|
||||
0, 1.0, 0, 0.0,
|
||||
0, 0, 1, 0,
|
||||
0.0, position.z, 0, 1.0
|
||||
);
|
||||
|
||||
gl_Position = view_proj * vec4(position.xyz, 1.0) * transform;
|
||||
}
|
184
druid/custom/tiling_node/tiling_node.lua
Normal file
184
druid/custom/tiling_node/tiling_node.lua
Normal file
@@ -0,0 +1,184 @@
|
||||
local component = require("druid.component")
|
||||
local helper = require("druid.helper")
|
||||
local defer = require("event.defer")
|
||||
|
||||
---@class druid.tiling_node: druid.component
|
||||
---@field animation table
|
||||
---@field node node
|
||||
---@field params vector4
|
||||
---@field time number
|
||||
local M = component.create("tiling_node")
|
||||
|
||||
M.PROP_SIZE_X = hash("size.x")
|
||||
M.PROP_SIZE_Y = hash("size.y")
|
||||
M.PROP_SCALE_X = hash("scale.x")
|
||||
M.PROP_SCALE_Y = hash("scale.y")
|
||||
|
||||
|
||||
---@param node node|string
|
||||
function M:init(node)
|
||||
self.node = self:get_node(node)
|
||||
self.animation = nil
|
||||
self.time = 0
|
||||
self.margin = 0
|
||||
|
||||
self.params = gui.get(self.node, "params") --[[@as vector4]]
|
||||
|
||||
self.timer_no_init = timer.delay(0.1, false, function()
|
||||
print("The druid.script is not found, please add it nearby to the GUI collection", msg.url())
|
||||
end)
|
||||
|
||||
defer.push("druid.get_atlas_path", {
|
||||
texture_name = gui.get_texture(self.node),
|
||||
sender = msg.url(),
|
||||
}, self.on_get_atlas_path, self)
|
||||
end
|
||||
|
||||
|
||||
---@param atlas_path string
|
||||
function M:on_get_atlas_path(atlas_path)
|
||||
timer.cancel(self.timer_no_init)
|
||||
self.is_inited = self:init_tiling_animation(atlas_path)
|
||||
local repeat_x, repeat_y = self:get_repeat_count_from_node()
|
||||
self:start_animation(repeat_x, repeat_y)
|
||||
end
|
||||
|
||||
|
||||
---@param node node
|
||||
---@param property string
|
||||
function M:on_node_property_changed(node, property)
|
||||
if not self.is_inited or node ~= self.node then
|
||||
return
|
||||
end
|
||||
|
||||
if property == "size" or property == "scale" then
|
||||
local repeat_x, repeat_y = self:get_repeat_count_from_node()
|
||||
self:set_repeat(repeat_x, repeat_y)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function M:get_repeat_count_from_node()
|
||||
if not self.is_inited then
|
||||
return 1, 1
|
||||
end
|
||||
local size_x = gui.get(self.node, M.PROP_SIZE_X)
|
||||
local size_y = gui.get(self.node, M.PROP_SIZE_Y)
|
||||
local scale_x = gui.get(self.node, M.PROP_SCALE_X)
|
||||
local scale_y = gui.get(self.node, M.PROP_SCALE_Y)
|
||||
|
||||
local repeat_x = (size_x / self.animation.width) / scale_x
|
||||
local repeat_y = (size_y / self.animation.height) / scale_y
|
||||
|
||||
return repeat_x, repeat_y
|
||||
end
|
||||
|
||||
|
||||
---@param atlas_path string
|
||||
---@return boolean
|
||||
function M:init_tiling_animation(atlas_path)
|
||||
if not atlas_path then
|
||||
print("No atlas path found for node", gui.get_id(self.node), gui.get_texture(self.node))
|
||||
print("Probably you should add druid.script at window collection to access resources")
|
||||
return false
|
||||
end
|
||||
|
||||
self.animation = helper.get_animation_data_from_node(self.node, atlas_path)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
-- Start our repeat shader work
|
||||
---@param repeat_x number X factor
|
||||
---@param repeat_y number Y factor
|
||||
function M:start_animation(repeat_x, repeat_y)
|
||||
if not self.is_inited then
|
||||
return
|
||||
end
|
||||
|
||||
self:set_repeat(repeat_x, repeat_y)
|
||||
|
||||
local node = self.node
|
||||
local animation = self.animation
|
||||
local frame = animation.frames[1]
|
||||
gui.set(node, "uv_coord", frame.uv_coord)
|
||||
|
||||
if #animation.frames > 1 and animation.fps > 0 then
|
||||
animation.handle =
|
||||
timer.delay(1/animation.fps, true, function(_, handle, time_elapsed)
|
||||
local next_rame = animation.frames[animation.current_frame]
|
||||
gui.set(node, "uv_coord", next_rame.uv_coord)
|
||||
|
||||
animation.current_frame = animation.current_frame + 1
|
||||
if animation.current_frame > #animation.frames then
|
||||
animation.current_frame = 1
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function M:final()
|
||||
local animation = self.animation
|
||||
if animation.handle then
|
||||
timer.cancel(animation.handle)
|
||||
animation.handle = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---Update repeat factor values
|
||||
---@param repeat_x number? X factor
|
||||
---@param repeat_y number? Y factor
|
||||
function M:set_repeat(repeat_x, repeat_y)
|
||||
local animation = self.animation
|
||||
animation.v.x = repeat_x or animation.v.x
|
||||
animation.v.y = repeat_y or animation.v.y
|
||||
|
||||
local anchor = helper.get_pivot_offset(gui.get_pivot(self.node))
|
||||
animation.v.z = anchor.x
|
||||
animation.v.w = anchor.y
|
||||
|
||||
gui.set(self.node, "uv_repeat", animation.v)
|
||||
end
|
||||
|
||||
|
||||
---@param offset_perc_x number? X offset
|
||||
---@param offset_perc_y number? Y offset
|
||||
function M:set_offset(offset_perc_x, offset_perc_y)
|
||||
self.params.z = offset_perc_x or self.params.z
|
||||
self.params.w = offset_perc_y or self.params.w
|
||||
gui.set(self.node, "params", self.params)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---@param margin_x number? X margin
|
||||
---@param margin_y number? Y margin
|
||||
function M:set_margin(margin_x, margin_y)
|
||||
self.params.x = margin_x or self.params.x
|
||||
self.params.y = margin_y or self.params.y
|
||||
gui.set(self.node, "params", self.params)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---@param scale number
|
||||
function M:set_scale(scale)
|
||||
local current_scale_x = gui.get(self.node, M.PROP_SCALE_X)
|
||||
local current_scale_y = gui.get(self.node, M.PROP_SCALE_Y)
|
||||
local current_size_x = gui.get(self.node, M.PROP_SIZE_X)
|
||||
local current_size_y = gui.get(self.node, M.PROP_SIZE_Y)
|
||||
|
||||
local delta_scale_x = scale / current_scale_x
|
||||
local delta_scale_y = scale / current_scale_y
|
||||
gui.set(self.node, M.PROP_SCALE_X, scale)
|
||||
gui.set(self.node, M.PROP_SCALE_Y, scale)
|
||||
gui.set(self.node, M.PROP_SIZE_X, current_size_x / delta_scale_x)
|
||||
gui.set(self.node, M.PROP_SIZE_Y, current_size_y / delta_scale_y)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
return M
|
Reference in New Issue
Block a user