diff --git a/plugins/obs-transitions/CMakeLists.txt b/plugins/obs-transitions/CMakeLists.txt index bbf58a0a9..b5b99a35a 100644 --- a/plugins/obs-transitions/CMakeLists.txt +++ b/plugins/obs-transitions/CMakeLists.txt @@ -2,6 +2,7 @@ project(obs-transitions) set(obs-transitions_SOURCES obs-transitions.c + transition-slide.c transition-swipe.c transition-fade.c transition-cut.c diff --git a/plugins/obs-transitions/data/locale/en-US.ini b/plugins/obs-transitions/data/locale/en-US.ini index 4740506bc..08e53cd15 100644 --- a/plugins/obs-transitions/data/locale/en-US.ini +++ b/plugins/obs-transitions/data/locale/en-US.ini @@ -1,6 +1,7 @@ FadeTransition="Fade" CutTransition="Cut" SwipeTransition="Swipe" +SlideTransition="Slide" Direction="Direction" Direction.Left="Left" Direction.Right="Right" diff --git a/plugins/obs-transitions/data/slide_transition.effect b/plugins/obs-transitions/data/slide_transition.effect new file mode 100644 index 000000000..91a9b8952 --- /dev/null +++ b/plugins/obs-transitions/data/slide_transition.effect @@ -0,0 +1,45 @@ +uniform float4x4 ViewProj; +uniform texture2d tex_a; +uniform texture2d tex_b; +uniform float2 tex_a_dir; +uniform float2 tex_b_dir; + + +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData VSDefault(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv; + return vert_out; +} + +float4 PSSlide(VertData v_in) : TARGET +{ + float2 tex_a_uv = v_in.uv + tex_a_dir; + float2 tex_b_uv = v_in.uv - tex_b_dir; + + return (tex_a_uv.x - saturate(tex_a_uv.x) != 0.0) || + (tex_a_uv.y - saturate(tex_a_uv.y) != 0.0) + ? tex_b.Sample(textureSampler, tex_b_uv) + : tex_a.Sample(textureSampler, tex_a_uv); +} + +technique Slide +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSSlide(v_in); + } +} diff --git a/plugins/obs-transitions/easings.h b/plugins/obs-transitions/easings.h new file mode 100644 index 000000000..23e0203c0 --- /dev/null +++ b/plugins/obs-transitions/easings.h @@ -0,0 +1,11 @@ +#pragma once + +static inline float cubic_ease_in_out(float t) +{ + if (t < 0.5f) { + return 4.0f * t * t * t; + } else { + float temp = (2.0f * t - 2.0f); + return (t - 1.0f) * temp * temp + 1.0f; + } +} diff --git a/plugins/obs-transitions/obs-transitions.c b/plugins/obs-transitions/obs-transitions.c index 09ce19787..5a1a86936 100644 --- a/plugins/obs-transitions/obs-transitions.c +++ b/plugins/obs-transitions/obs-transitions.c @@ -7,11 +7,13 @@ OBS_MODULE_USE_DEFAULT_LOCALE("obs-transitions", "en-US") extern struct obs_source_info cut_transition; extern struct obs_source_info fade_transition; extern struct obs_source_info swipe_transition; +extern struct obs_source_info slide_transition; bool obs_module_load(void) { obs_register_source(&cut_transition); obs_register_source(&fade_transition); obs_register_source(&swipe_transition); + obs_register_source(&slide_transition); return true; } diff --git a/plugins/obs-transitions/transition-slide.c b/plugins/obs-transitions/transition-slide.c new file mode 100644 index 000000000..d38147753 --- /dev/null +++ b/plugins/obs-transitions/transition-slide.c @@ -0,0 +1,165 @@ +#include +#include +#include "easings.h" + +#define S_DIRECTION "direction" + +struct slide_info { + obs_source_t *source; + + gs_effect_t *effect; + gs_eparam_t *a_param; + gs_eparam_t *b_param; + gs_eparam_t *tex_a_dir_param; + gs_eparam_t *tex_b_dir_param; + + struct vec2 dir; + bool slide_in; +}; + +static const char *slide_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return obs_module_text("SlideTransition"); +} + +static void slide_update(void *data, obs_data_t *settings) +{ + struct slide_info *slide = data; + const char *dir = obs_data_get_string(settings, S_DIRECTION); + + if (strcmp(dir, "right") == 0) + slide->dir = (struct vec2){ -1.0f, 0.0f }; + else if (strcmp(dir, "up") == 0) + slide->dir = (struct vec2){ 0.0f, 1.0f }; + else if (strcmp(dir, "down") == 0) + slide->dir = (struct vec2){ 0.0f, -1.0f }; + else /* left */ + slide->dir = (struct vec2){ 1.0f, 0.0f }; +} + +void *slide_create(obs_data_t *settings, obs_source_t *source) +{ + struct slide_info *slide; + gs_effect_t *effect; + + char *file = obs_module_file("slide_transition.effect"); + + obs_enter_graphics(); + effect = gs_effect_create_from_file(file, NULL); + obs_leave_graphics(); + + bfree(file); + + if (!effect) { + blog(LOG_ERROR, "Could not find slide_transition.effect"); + return NULL; + } + + slide = bzalloc(sizeof(*slide)); + + slide->source = source; + slide->effect = effect; + + slide->a_param = gs_effect_get_param_by_name(effect, "tex_a"); + slide->b_param = gs_effect_get_param_by_name(effect, "tex_b"); + + slide->tex_a_dir_param = + gs_effect_get_param_by_name(effect, "tex_a_dir"); + slide->tex_b_dir_param = + gs_effect_get_param_by_name(effect, "tex_b_dir"); + + obs_source_update(source, settings); + + return slide; +} + +void slide_destroy(void *data) +{ + struct slide_info *slide = data; + bfree(slide); +} + +static void slide_callback(void *data, gs_texture_t *a, gs_texture_t *b, + float t, uint32_t cx, uint32_t cy) +{ + struct slide_info *slide = data; + + struct vec2 tex_a_dir = slide->dir; + struct vec2 tex_b_dir = slide->dir; + + t = cubic_ease_in_out(t); + + vec2_mulf(&tex_a_dir, &tex_a_dir, t); + vec2_mulf(&tex_b_dir, &tex_b_dir, 1.0f - t); + + gs_effect_set_texture(slide->a_param, a); + gs_effect_set_texture(slide->b_param, b); + + gs_effect_set_vec2(slide->tex_a_dir_param, &tex_a_dir); + gs_effect_set_vec2(slide->tex_b_dir_param, &tex_b_dir); + + while (gs_effect_loop(slide->effect, "Slide")) + gs_draw_sprite(NULL, 0, cx, cy); +} + +void slide_video_render(void *data, gs_effect_t *effect) +{ + struct slide_info *slide = data; + obs_transition_video_render(slide->source, slide_callback); + UNUSED_PARAMETER(effect); +} + +static float mix_a(void *data, float t) +{ + UNUSED_PARAMETER(data); + return 1.0f - cubic_ease_in_out(t); +} + +static float mix_b(void *data, float t) +{ + UNUSED_PARAMETER(data); + return cubic_ease_in_out(t); +} + +bool slide_audio_render(void *data, uint64_t *ts_out, + struct obs_source_audio_mix *audio, uint32_t mixers, + size_t channels, size_t sample_rate) +{ + struct slide_info *slide = data; + return obs_transition_audio_render(slide->source, ts_out, + audio, mixers, channels, sample_rate, mix_a, mix_b); +} + +static obs_properties_t *slide_properties(void *data) +{ + obs_properties_t *ppts = obs_properties_create(); + obs_property_t *p; + + p = obs_properties_add_list(ppts, S_DIRECTION, + obs_module_text("Direction"), OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, obs_module_text("Direction.Left"), + "left"); + obs_property_list_add_string(p, obs_module_text("Direction.Right"), + "right"); + obs_property_list_add_string(p, obs_module_text("Direction.Up"), + "up"); + obs_property_list_add_string(p, obs_module_text("Direction.Down"), + "down"); + + UNUSED_PARAMETER(data); + return ppts; +} + +struct obs_source_info slide_transition = { + .id = "slide_transition", + .type = OBS_SOURCE_TYPE_TRANSITION, + .get_name = slide_get_name, + .create = slide_create, + .destroy = slide_destroy, + .update = slide_update, + .video_render = slide_video_render, + .audio_render = slide_audio_render, + .get_properties = slide_properties +}; diff --git a/plugins/obs-transitions/transition-swipe.c b/plugins/obs-transitions/transition-swipe.c index b73b4e48a..3bb5ce07d 100644 --- a/plugins/obs-transitions/transition-swipe.c +++ b/plugins/obs-transitions/transition-swipe.c @@ -1,5 +1,6 @@ #include #include +#include "easings.h" struct swipe_info { obs_source_t *source; @@ -16,16 +17,6 @@ struct swipe_info { #define S_DIRECTION "direction" #define S_SWIPE_IN "swipe_in" -static float get_easing(float t) -{ - if (t < 0.5f) { - return 4.0f * t * t * t; - } else { - float temp = (2.0f * t - 2.0f); - return (t - 1.0f) * temp * temp + 1.0f; - } -} - static const char *swipe_get_name(void *type_data) { UNUSED_PARAMETER(type_data); @@ -93,7 +84,7 @@ static void swipe_callback(void *data, gs_texture_t *a, gs_texture_t *b, if (swipe->swipe_in) vec2_neg(&swipe_val, &swipe_val); - t = get_easing(t); + t = cubic_ease_in_out(t); vec2_mulf(&swipe_val, &swipe_val, swipe->swipe_in ? 1.0f - t : t); @@ -115,13 +106,13 @@ static void swipe_video_render(void *data, gs_effect_t *effect) static float mix_a(void *data, float t) { UNUSED_PARAMETER(data); - return 1.0f - get_easing(t); + return 1.0f - cubic_ease_in_out(t); } static float mix_b(void *data, float t) { UNUSED_PARAMETER(data); - return get_easing(t); + return cubic_ease_in_out(t); } static bool swipe_audio_render(void *data, uint64_t *ts_out,