mirror of
https://github.com/Insality/druid.git
synced 2025-06-27 10:27:47 +02:00
Test shaders
This commit is contained in:
parent
917a84fc94
commit
9a1cd795b8
37
druid/druid.script
Normal file
37
druid/druid.script
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
-- Place this script nearby with the gui component to able make requests
|
||||||
|
-- To the go namespace from GUI with events systems (cross context)
|
||||||
|
|
||||||
|
local event_queue = require("druid.event_queue")
|
||||||
|
|
||||||
|
---Usage: event_queue.request("druid.get_atlas_path", callback, gui.get_texture(self.node), msg.url())
|
||||||
|
---Pass texture name to get atlas info and sender url to check if the request is valid
|
||||||
|
local MESSAGE_GET_ATLAS_PATH = "druid.get_atlas_path"
|
||||||
|
|
||||||
|
|
||||||
|
---@param texture_name hash The name from gui.get_texture(node)
|
||||||
|
---@param sender hash Just msg.url from the caller
|
||||||
|
local function get_atlas_path(texture_name, sender)
|
||||||
|
local my_url = msg.url()
|
||||||
|
my_url.fragment = nil
|
||||||
|
|
||||||
|
local copy_url = msg.url(sender)
|
||||||
|
copy_url.fragment = nil
|
||||||
|
|
||||||
|
-- This check should works well
|
||||||
|
local is_my_url = my_url == copy_url
|
||||||
|
if not is_my_url then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return go.get(sender, "textures", { key = texture_name })
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function init(self)
|
||||||
|
event_queue.subscribe(MESSAGE_GET_ATLAS_PATH, get_atlas_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function final(self)
|
||||||
|
event_queue.unsubscribe(MESSAGE_GET_ATLAS_PATH, get_atlas_path)
|
||||||
|
end
|
84
druid/event_queue.lua
Normal file
84
druid/event_queue.lua
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
local event = require("event.event")
|
||||||
|
|
||||||
|
---@class event.queue
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local event_handlers = {}
|
||||||
|
local pending_callbacks = {}
|
||||||
|
|
||||||
|
|
||||||
|
---Request to handle a specified event and processes the queue of callbacks associated with it.
|
||||||
|
---If event has already been triggered, the callback will be executed immediately.
|
||||||
|
---If event not triggered yet, callback will be executed when event will be triggered.
|
||||||
|
---It triggered only once and then removed from the queue.
|
||||||
|
---@param event_name string The name of the event to trigger.
|
||||||
|
---@param callback fun() The callback function to execute upon triggering.
|
||||||
|
---@param ... any Additional arguments for the callback.
|
||||||
|
function M.request(event_name, callback, ...)
|
||||||
|
pending_callbacks[event_name] = pending_callbacks[event_name] or {}
|
||||||
|
table.insert(pending_callbacks[event_name], { event.create(callback), ... })
|
||||||
|
|
||||||
|
M.process_pending_callbacks(event_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Subscribes to a specified event and executes a callback when the event is triggered.
|
||||||
|
-- If the event has already been triggered, the callback will be executed immediately.
|
||||||
|
---@param event_name string The name of the event to subscribe to.
|
||||||
|
---@param callback fun() The function to call when the event is triggered.
|
||||||
|
function M.subscribe(event_name, callback)
|
||||||
|
event_handlers[event_name] = event_handlers[event_name] or event.create()
|
||||||
|
|
||||||
|
if event_handlers[event_name] then
|
||||||
|
event_handlers[event_name]:subscribe(callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
M.process_pending_callbacks(event_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Unsubscribes a callback function from a specified event.
|
||||||
|
---@param event_name string The name of the event to unsubscribe from.
|
||||||
|
---@param callback fun() The function to remove from the event's subscription list.
|
||||||
|
function M.unsubscribe(event_name, callback)
|
||||||
|
if event_handlers[event_name] then
|
||||||
|
event_handlers[event_name]:unsubscribe(callback)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Processes the queue for a given event name, executing callbacks and handling results.
|
||||||
|
---Processed callbacks are removed from the queue.
|
||||||
|
---@param event_name string The name of the event for which to process the queue.
|
||||||
|
function M.process_pending_callbacks(event_name)
|
||||||
|
local callbacks_to_process = pending_callbacks[event_name]
|
||||||
|
local event_handler = event_handlers[event_name]
|
||||||
|
|
||||||
|
if not callbacks_to_process or not event_handler then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Loop through the queue in reverse to prevent index errors during removal
|
||||||
|
for i = #callbacks_to_process, 1, -1 do
|
||||||
|
local callback_entry = callbacks_to_process[i]
|
||||||
|
-- Better to figure out how to make it without 2 unpacks, but ok for all our cases now
|
||||||
|
local args = { unpack(callback_entry, 2) }
|
||||||
|
|
||||||
|
-- Safely call the event handler and handle errors
|
||||||
|
local success, result = pcall(event_handler.trigger, event_handler, unpack(args))
|
||||||
|
|
||||||
|
if success and result then
|
||||||
|
local callback_function = callback_entry[1]
|
||||||
|
pcall(callback_function, result) -- Safely invoke the callback, catching any errors
|
||||||
|
table.remove(callbacks_to_process, i) -- Remove the processed callback from the queue
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clean up if the callback queue is empty
|
||||||
|
if #callbacks_to_process == 0 then
|
||||||
|
pending_callbacks[event_name] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return M
|
122
druid/helper.lua
122
druid/helper.lua
@ -12,6 +12,11 @@ local POSITION_X = hash("position.x")
|
|||||||
local SCALE_X = hash("scale.x")
|
local SCALE_X = hash("scale.x")
|
||||||
local SIZE_X = hash("size.x")
|
local SIZE_X = hash("size.x")
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
local function get_text_width(text_node)
|
local function get_text_width(text_node)
|
||||||
if text_node then
|
if text_node then
|
||||||
local text_metrics = M.get_text_metrics_from_node(text_node)
|
local text_metrics = M.get_text_metrics_from_node(text_node)
|
||||||
@ -540,4 +545,121 @@ function M.get_full_position(node, root)
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@class druid.animation_data
|
||||||
|
---@field frames table<number, table<string, number>> @List of frames with uv coordinates and size
|
||||||
|
---@field width number @Width of the animation
|
||||||
|
---@field height number @Height of the animation
|
||||||
|
---@field fps number @Frames per second
|
||||||
|
---@field current_frame number @Current frame
|
||||||
|
---@field node node @Node with flipbook animation
|
||||||
|
---@field v vector4 @Vector with UV coordinates and size
|
||||||
|
|
||||||
|
---@param node node
|
||||||
|
---@param atlas_path string @Path to the atlas
|
||||||
|
---@return druid.animation_data
|
||||||
|
function M.get_animation_data_from_node(node, atlas_path)
|
||||||
|
local atlas_data = resource.get_atlas(atlas_path)
|
||||||
|
local tex_info = resource.get_texture_info(atlas_data.texture)
|
||||||
|
local tex_w = tex_info.width
|
||||||
|
local tex_h = tex_info.height
|
||||||
|
|
||||||
|
local animation_data
|
||||||
|
|
||||||
|
local sprite_image_id = gui.get_flipbook(node)
|
||||||
|
for _, animation in ipairs(atlas_data.animations) do
|
||||||
|
if hash(animation.id) == sprite_image_id then
|
||||||
|
animation_data = animation
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assert(animation_data, "Unable to find image " .. sprite_image_id)
|
||||||
|
|
||||||
|
local frames = {}
|
||||||
|
for index = animation_data.frame_start, animation_data.frame_end - 1 do
|
||||||
|
local uvs = atlas_data.geometries[index].uvs
|
||||||
|
assert(#uvs == 8, "Sprite trim mode should be disabled for the images.")
|
||||||
|
|
||||||
|
-- UV texture coordinates
|
||||||
|
-- 1
|
||||||
|
-- ^ V
|
||||||
|
-- |
|
||||||
|
-- |
|
||||||
|
-- | U
|
||||||
|
-- 0-------> 1
|
||||||
|
|
||||||
|
-- uvs = {
|
||||||
|
-- 0, 0,
|
||||||
|
-- 0, height,
|
||||||
|
-- width, height,
|
||||||
|
-- width, 0
|
||||||
|
-- },
|
||||||
|
-- Point indeces (Point number {uv_index_x, uv_index_y})
|
||||||
|
-- geometries.indices = {0 (1,2), 1(3,4), 2(5,6), 0(1,2), 2(5,6), 3(7,8)}
|
||||||
|
-- 1------2
|
||||||
|
-- | / |
|
||||||
|
-- | A / |
|
||||||
|
-- | / B |
|
||||||
|
-- | / |
|
||||||
|
-- 0------3
|
||||||
|
|
||||||
|
local width = uvs[5] - uvs[1] -- Width of sprite region
|
||||||
|
local height = uvs[2] - uvs[4] -- Height of sprite region
|
||||||
|
local is_rotated = height < 0 -- In case of rotated sprite
|
||||||
|
|
||||||
|
local x_left = uvs[1]
|
||||||
|
local y_bottom = uvs[2]
|
||||||
|
local x_right = uvs[5]
|
||||||
|
local y_top = uvs[6]
|
||||||
|
|
||||||
|
-- Okay now it's correct for non rotated
|
||||||
|
local uv_coord = vmath.vector4(
|
||||||
|
x_left / tex_w,
|
||||||
|
(tex_h - y_bottom) / tex_h,
|
||||||
|
x_right / tex_w,
|
||||||
|
(tex_h - y_top) / tex_h
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_rotated then
|
||||||
|
-- In case the atlas has clockwise rotated sprite.
|
||||||
|
-- 0---------------1
|
||||||
|
-- | \ A |
|
||||||
|
-- | \ |
|
||||||
|
-- | \ |
|
||||||
|
-- | B \ |
|
||||||
|
-- 3---------------2
|
||||||
|
height = -height
|
||||||
|
|
||||||
|
uv_coord.x, uv_coord.y, uv_coord.z, uv_coord.w = uv_coord.y, uv_coord.z, uv_coord.w, uv_coord.x
|
||||||
|
|
||||||
|
-- Update uv_coord
|
||||||
|
--uv_coord = vmath.vector4(
|
||||||
|
-- u1 / tex_w,
|
||||||
|
-- (tex_h - v2) / tex_h,
|
||||||
|
-- u2 / tex_w,
|
||||||
|
-- (tex_h - v1) / tex_h
|
||||||
|
--)
|
||||||
|
end
|
||||||
|
|
||||||
|
local frame = {
|
||||||
|
uv_coord = uv_coord,
|
||||||
|
w = width,
|
||||||
|
h = height,
|
||||||
|
uv_rotated = is_rotated and vmath.vector4(0, 1, 0, 0) or vmath.vector4(1, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
table.insert(frames, frame)
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
frames = frames,
|
||||||
|
width = animation_data.width,
|
||||||
|
height = animation_data.height,
|
||||||
|
fps = animation_data.fps,
|
||||||
|
v = vmath.vector4(1, 1, animation_data.width, animation_data.height),
|
||||||
|
current_frame = 1,
|
||||||
|
node = node,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
84
druid/materials/gui_repeat/gui_repeat.fp
Normal file
84
druid/materials/gui_repeat/gui_repeat.fp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#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_perspective;
|
||||||
|
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;
|
||||||
|
}
|
43
druid/materials/gui_repeat/gui_repeat.material
Normal file
43
druid/materials/gui_repeat/gui_repeat.material
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
name: "repeat"
|
||||||
|
tags: "gui"
|
||||||
|
vertex_program: "/druid/materials/gui_repeat/gui_repeat.vp"
|
||||||
|
fragment_program: "/druid/materials/gui_repeat/gui_repeat.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: "perspective"
|
||||||
|
type: CONSTANT_TYPE_USER
|
||||||
|
value {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vertex_constants {
|
||||||
|
name: "uv_rotated"
|
||||||
|
type: CONSTANT_TYPE_USER
|
||||||
|
value {
|
||||||
|
x: 1.0
|
||||||
|
}
|
||||||
|
}
|
55
druid/materials/gui_repeat/gui_repeat.vp
Normal file
55
druid/materials/gui_repeat/gui_repeat.vp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#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]
|
||||||
|
vec4 perspective; // [perspective_x, perspective_y, zoom, 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_perspective;
|
||||||
|
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_perspective = perspective;
|
||||||
|
var_uv_rotated = uv_rotated;
|
||||||
|
|
||||||
|
float perspective_y = position.z;
|
||||||
|
|
||||||
|
float scale_x = 1.0 - abs(perspective.x);
|
||||||
|
float scale_y = 1.0 - abs(perspective_y);
|
||||||
|
|
||||||
|
mat4 transform = mat4(
|
||||||
|
scale_x, 0, 0, perspective.z,
|
||||||
|
0, scale_y, 0, perspective.w,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
perspective.x, perspective_y, 0, 1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
// Matrix Info = mat4(
|
||||||
|
// scale_x, skew_x, 0, offset_x,
|
||||||
|
// skew_y, scale_y, 0, offset_y,
|
||||||
|
// 0, 0, scale_z, offset_z,
|
||||||
|
// perspective_x, perspective_y, perspective_z, zoom
|
||||||
|
//)
|
||||||
|
|
||||||
|
gl_Position = view_proj * vec4(position.xyz, 1.0) * transform;
|
||||||
|
}
|
18
druid/materials/skew/gui_skew.fp
Normal file
18
druid/materials/skew/gui_skew.fp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#version 140
|
||||||
|
|
||||||
|
uniform sampler2D texture_sampler;
|
||||||
|
|
||||||
|
in vec2 var_texcoord0;
|
||||||
|
in vec4 var_color;
|
||||||
|
|
||||||
|
out vec4 color_out;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
lowp vec4 tex = texture(texture_sampler, var_texcoord0.xy);
|
||||||
|
if (tex.a < 0.5) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final color of stencil texture
|
||||||
|
color_out = tex * var_color;
|
||||||
|
}
|
8
druid/materials/skew/gui_skew.material
Normal file
8
druid/materials/skew/gui_skew.material
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
name: "repeat"
|
||||||
|
tags: "gui"
|
||||||
|
vertex_program: "/druid/materials/stencil/gui_stencil.vp"
|
||||||
|
fragment_program: "/druid/materials/stencil/gui_stencil.fp"
|
||||||
|
vertex_constants {
|
||||||
|
name: "view_proj"
|
||||||
|
type: CONSTANT_TYPE_VIEWPROJ
|
||||||
|
}
|
20
druid/materials/skew/gui_skew.vp
Normal file
20
druid/materials/skew/gui_skew.vp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#version 140
|
||||||
|
|
||||||
|
uniform vertex_inputs {
|
||||||
|
highp mat4 view_proj;
|
||||||
|
};
|
||||||
|
|
||||||
|
// positions are in world space
|
||||||
|
in mediump vec3 position;
|
||||||
|
in mediump vec2 texcoord0;
|
||||||
|
in lowp vec4 color;
|
||||||
|
|
||||||
|
out mediump vec2 var_texcoord0;
|
||||||
|
out lowp vec4 var_color;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
var_texcoord0 = texcoord0;
|
||||||
|
var_color = vec4(color.rgb * color.a, color.a);
|
||||||
|
gl_Position = view_proj * vec4(position.xyz, 1.0);
|
||||||
|
}
|
195
druid/widget/node_repeat/node_repeat.lua
Normal file
195
druid/widget/node_repeat/node_repeat.lua
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
local helper = require("druid.helper")
|
||||||
|
local event_queue = require("druid.event_queue")
|
||||||
|
|
||||||
|
---@class druid.node_repeat: druid.widget
|
||||||
|
---@field animation table
|
||||||
|
---@field node node
|
||||||
|
---@field params vector4
|
||||||
|
---@field time number
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M:init(node)
|
||||||
|
self.node = self:get_node(node)
|
||||||
|
self.animation = nil
|
||||||
|
gui.set_material(self.node, hash("gui_repeat"))
|
||||||
|
self.time = 0
|
||||||
|
self.margin = 0
|
||||||
|
|
||||||
|
self.params = gui.get(self.node, "params") --[[@as vector4]]
|
||||||
|
self:get_atlas_path(function(atlas_path)
|
||||||
|
self.is_inited = self:init_tiling_animation(atlas_path)
|
||||||
|
local repeat_x, repeat_y = self:get_repeat()
|
||||||
|
self:animate(repeat_x, repeat_y)
|
||||||
|
end)
|
||||||
|
|
||||||
|
--self.druid.events.on_node_property_changed:subscribe(self.on_node_property_changed, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
self:set_repeat(repeat_x, repeat_y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function M:get_repeat()
|
||||||
|
if not self.is_inited then
|
||||||
|
return 1, 1
|
||||||
|
end
|
||||||
|
local size_x = gui.get(self.node, helper.PROP_SIZE_X)
|
||||||
|
local size_y = gui.get(self.node, helper.PROP_SIZE_Y)
|
||||||
|
local scale_x = gui.get(self.node, helper.PROP_SCALE_X)
|
||||||
|
local scale_y = gui.get(self.node, helper.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
|
||||||
|
|
||||||
|
|
||||||
|
function M:get_atlas_path(callback)
|
||||||
|
event_queue.request("druid.get_atlas_path", callback, gui.get_texture(self.node), msg.url())
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@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 -- X factor
|
||||||
|
-- @param repeat_y -- Y factor
|
||||||
|
function M:animate(repeat_x, repeat_y)
|
||||||
|
if not self.is_inited then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local node = self.node
|
||||||
|
local animation = self.animation
|
||||||
|
|
||||||
|
local frame = animation.frames[1]
|
||||||
|
gui.set(node, "uv_coord", frame.uv_coord)
|
||||||
|
self:set_repeat(repeat_x, repeat_y)
|
||||||
|
|
||||||
|
if #animation.frames > 1 and animation.fps > 0 then
|
||||||
|
animation.handle =
|
||||||
|
timer.delay(1/animation.fps, true, function(self, 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
|
||||||
|
-- @param repeat_y
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
function M:set_perpective(perspective_x, perspective_y)
|
||||||
|
if perspective_x then
|
||||||
|
gui.set(self.node, "perspective.x", perspective_x)
|
||||||
|
end
|
||||||
|
|
||||||
|
if perspective_y then
|
||||||
|
gui.set(self.node, "perspective.y", perspective_y)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function M:set_perpective_offset(offset_x, offset_y)
|
||||||
|
if offset_x then
|
||||||
|
gui.set(self.node, "perspective.z", offset_x)
|
||||||
|
end
|
||||||
|
|
||||||
|
if offset_y then
|
||||||
|
gui.set(self.node, "perspective.w", offset_y)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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, helper.PROP_SCALE_X)
|
||||||
|
local current_scale_y = gui.get(self.node, helper.PROP_SCALE_Y)
|
||||||
|
local current_size_x = gui.get(self.node, helper.PROP_SIZE_X)
|
||||||
|
local current_size_y = gui.get(self.node, helper.PROP_SIZE_Y)
|
||||||
|
|
||||||
|
local delta_scale_x = scale / current_scale_x
|
||||||
|
local delta_scale_y = scale / current_scale_y
|
||||||
|
gui.set(self.node, helper.PROP_SCALE_X, scale)
|
||||||
|
gui.set(self.node, helper.PROP_SCALE_Y, scale)
|
||||||
|
gui.set(self.node, helper.PROP_SIZE_X, current_size_x / delta_scale_x)
|
||||||
|
gui.set(self.node, helper.PROP_SIZE_Y, current_size_y / delta_scale_y)
|
||||||
|
|
||||||
|
--self.druid:on_node_property_changed(self.node, "scale")
|
||||||
|
--self.druid:on_node_property_changed(self.node, "size")
|
||||||
|
|
||||||
|
--local repeat_x, repeat_y = self:get_repeat()
|
||||||
|
--self:set_repeat(repeat_x, repeat_y)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return M
|
@ -19,6 +19,7 @@ function M:init()
|
|||||||
|
|
||||||
self.min = 0
|
self.min = 0
|
||||||
self.max = 1
|
self.max = 1
|
||||||
|
self.step = 0.01
|
||||||
|
|
||||||
self.text_name = self.druid:new_text("text_name")
|
self.text_name = self.druid:new_text("text_name")
|
||||||
:set_text_adjust("scale_then_trim", 0.3)
|
:set_text_adjust("scale_then_trim", 0.3)
|
||||||
@ -63,7 +64,7 @@ end
|
|||||||
---@param value number
|
---@param value number
|
||||||
function M:set_value(value, is_instant)
|
function M:set_value(value, is_instant)
|
||||||
local diff = math.abs(self.max - self.min)
|
local diff = math.abs(self.max - self.min)
|
||||||
self.slider:set(value / diff, true)
|
self.slider:set((value - self.min) / diff, true)
|
||||||
|
|
||||||
local is_changed = self._value ~= value
|
local is_changed = self._value ~= value
|
||||||
if not is_changed then
|
if not is_changed then
|
||||||
|
Loading…
x
Reference in New Issue
Block a user