Last active
February 7, 2024 00:24
-
-
Save vurpo/80741c5d03c98cc957975dc6929af61f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
obs = obslua | |
output_size = {x = 50, y = 50, width = 1820, height = 980} | |
transition_time = 0.2 | |
tic_size = {x = 240, y = 136} | |
reorder = true | |
function vec2(x,y) | |
local vec = obs.vec2() | |
vec.x = x | |
vec.y = y | |
return vec | |
end | |
-- Description displayed in the Scripts dialog window | |
function script_description() | |
return [[automatic tic-80 arranger]] | |
end | |
function script_properties() | |
props = obs.obs_properties_create() | |
obs.obs_properties_add_int(props, "x", "Output area X", 0, 1000000, 1) | |
obs.obs_properties_add_int(props, "y", "Output area Y", 0, 1000000, 1) | |
obs.obs_properties_add_int(props, "w", "Output area width", 0, 1000000, 1) | |
obs.obs_properties_add_int(props, "h", "Output area height", 0, 1000000, 1) | |
obs.obs_properties_add_int(props, "t", "Transition time", 0, 1000000, 1) | |
obs.obs_properties_add_int(props, "tic_w", "TIC-80 aspect ratio (width)", 0, 1000000, 1) | |
obs.obs_properties_add_int(props, "tic_h", "TIC-80 aspect ratio (height)", 0, 1000000, 1) | |
obs.obs_properties_add_bool(props, "reorder", "Reorder sources") | |
return props | |
end | |
function script_defaults(settings) | |
obs.obs_data_set_default_int(settings, "x", 0) | |
obs.obs_data_set_default_int(settings, "y", 0) | |
obs.obs_data_set_default_int(settings, "w", 1920) | |
obs.obs_data_set_default_int(settings, "h", 1080) | |
obs.obs_data_set_default_int(settings, "t", 200) | |
obs.obs_data_set_default_int(settings, "tic_w", 240) | |
obs.obs_data_set_default_int(settings, "tic_h", 136) | |
obs.obs_data_set_default_bool(settings, "reorder", true) | |
end | |
function script_update(settings) | |
output_size.x = obs.obs_data_get_int(settings, "x") | |
output_size.y = obs.obs_data_get_int(settings, "y") | |
output_size.width = obs.obs_data_get_int(settings, "w") | |
output_size.height = obs.obs_data_get_int(settings, "h") | |
transition_time = obs.obs_data_get_int(settings, "t")/1000 | |
tic_size.x = obs.obs_data_get_int(settings, "tic_w") | |
tic_size.y = obs.obs_data_get_int(settings, "tic_h") | |
reorder = obs.obs_data_get_bool(settings, "reorder") | |
end | |
function distance(x, y) | |
return math.abs(x-y) | |
end | |
function clamp(x, a, b) | |
if x<a then | |
return a | |
elseif x>b then | |
return b | |
else | |
return x | |
end | |
end | |
function inOutCubic(t, b, c, d) | |
t = t / d * 2 | |
if t < 1 then | |
return c / 2 * t * t * t + b | |
else | |
t = t - 2 | |
return c / 2 * (t * t * t + 2) + b | |
end | |
end | |
function lerp(a, b, t) | |
return a + (b - a) * clamp(t, 0, 1) | |
end | |
function find_num_of_rows(num_screens) | |
local best_rows = 0 | |
local best_ratio = 0 | |
-- try all possible number of rows, save the one that has the closest ratio to the output | |
for rows=1,num_screens do | |
screens_per_row = math.ceil(num_screens/rows) | |
ratio = (tic_size.x*screens_per_row)/(tic_size.y*rows) | |
if distance(ratio, output_size.width/output_size.height) < distance(best_ratio, output_size.width/output_size.height) or best_rows == 0 then | |
best_rows = rows | |
best_ratio = ratio | |
end | |
end | |
return best_rows | |
end | |
function len(t) | |
local count = 0 | |
for _ in pairs(t) do count = count + 1 end | |
return count | |
end | |
function calculate_screen_arrangement(screens) | |
local num_rows = find_num_of_rows(len(screens)) | |
local scenes_per_row = math.ceil(len(screens)/num_rows) | |
local screen_width = output_size.width / scenes_per_row | |
local screen_height = output_size.height / num_rows | |
local arrangement = {} | |
local j=0 | |
for i,_ in pairs(screens) do | |
arrangement[i] = { | |
x = output_size.x + (j%scenes_per_row)*screen_width, | |
y = output_size.y + (math.floor(j/scenes_per_row))*screen_height, | |
width = screen_width, | |
height = screen_height | |
} | |
j = j+1 | |
end | |
return arrangement | |
end | |
transition_from = {} | |
arrangement = {} | |
previously_visible = {} | |
transitioned_at = 0 | |
function contains(set, item) | |
if set[item] ~= nil then return true | |
else return false end | |
end | |
function different(list1, list2) | |
if len(list1) ~= len(list2) then return true end | |
for i, _ in pairs(list1) do | |
if not contains(list2, i) then | |
return true | |
end | |
end | |
return false | |
end | |
lowest = nil | |
highest = nil | |
function script_tick(seconds) | |
local current_scene_as_source = obs.obs_frontend_get_current_scene() | |
if current_scene_as_source then | |
local current_scene = obs.obs_scene_from_source(current_scene_as_source) | |
-- find how many are visible | |
local new_visible = {} | |
local all_sources = {} | |
for i=1,100 do | |
local scene_item = obs.obs_scene_find_source(current_scene, tostring(i)) | |
if scene_item and obs.obs_sceneitem_visible(scene_item) then | |
new_visible[i] = true | |
end | |
if scene_item then | |
all_sources[i] = true | |
order = obs.obs_sceneitem_get_order_position(scene_item) | |
if lowest == nil or lowest > order then lowest = order end | |
if highest == nil or highest < order then highest = order end | |
end | |
end | |
if different(previously_visible, new_visible) then | |
transition_from = arrangement | |
transitioned_at = os.clock() | |
if reorder then | |
if len(new_visible) < len(all_sources)-len(new_visible) then -- there's fewer visible ones, so raise them instead | |
for i in pairs(all_sources) do | |
if contains(new_visible, i) then | |
local scene_item = obs.obs_scene_find_source(current_scene, tostring(i)) | |
if scene_item then | |
obs.obs_sceneitem_set_order_position(scene_item, highest) | |
end | |
end | |
end | |
else -- there's more visible ones than invisible | |
for i in pairs(all_sources) do | |
if not contains(new_visible, i) then | |
local scene_item = obs.obs_scene_find_source(current_scene, tostring(i)) | |
if scene_item then | |
obs.obs_sceneitem_set_order_position(scene_item, lowest) | |
end | |
end | |
end | |
end | |
end | |
print("there are "..len(new_visible).." visible ones") | |
end | |
previously_visible = new_visible | |
arrangement = calculate_screen_arrangement(new_visible) | |
t = inOutCubic(os.clock()-transitioned_at, 0, 1, transition_time) | |
for i=1,100 do | |
i_0 = i-1 | |
local scene_item = obs.obs_scene_find_source(current_scene, tostring(i)) | |
if scene_item then | |
obs.obs_sceneitem_set_bounds_type(scene_item, obs.OBS_BOUNDS_SCALE_INNER) | |
local new_pos = arrangement[i] | |
if new_pos then | |
local old_pos = transition_from[i] | |
if old_pos then | |
obs.obs_sceneitem_set_pos(scene_item, vec2( | |
lerp(old_pos.x, new_pos.x, t), | |
lerp(old_pos.y, new_pos.y, t) | |
)) | |
obs.obs_sceneitem_set_bounds(scene_item, vec2( | |
lerp(old_pos.width, new_pos.width, t), | |
lerp(old_pos.height, new_pos.height, t) | |
)) | |
else | |
obs.obs_sceneitem_set_pos(scene_item, vec2(new_pos.x, new_pos.y)) | |
obs.obs_sceneitem_set_bounds(scene_item, vec2(new_pos.width, new_pos.height)) | |
end | |
end | |
end | |
end | |
obs.obs_source_release(current_scene_as_source) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment