diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index cd977a06..3bdda3ea 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -93,6 +93,8 @@ Shader.Shader.Size="Size" Shader.Shader.Size.Description="Size of the source, can be one of the following units:\n- '###.##%' which is relative to the parent source, filter or scene.\n- '###' to specify the size in pixels. (Default)\nIf the value can't be parsed, it will be treated as 100%." Shader.Shader.Size.Width="Width" Shader.Shader.Size.Height="Height" +Shader.Shader.Seed="Randomization Seed" +Shader.Shader.Seed.Description="Seed used for the Per-Instance, Per-Activation and Per-Frame random values.\nThe same seed will always produce identical results if the identical number of runs were made." Shader.Parameters="Shader Parameters" Shader.Parameters.Description="All the shader parameters that the loaded shader offers.\nMake sure to refresh these every now and then." Filter.Shader="Shader" diff --git a/source/filters/filter-shader.cpp b/source/filters/filter-shader.cpp index 7a694a78..3396014a 100644 --- a/source/filters/filter-shader.cpp +++ b/source/filters/filter-shader.cpp @@ -133,12 +133,23 @@ void shader_instance::video_render(gs_effect_t* effect) } } +void streamfx::filter::shader::shader_instance::activate() +{ + _fx->set_active(true); +} + +void streamfx::filter::shader::shader_instance::deactivate() +{ + _fx->set_active(false); +} + shader_factory::shader_factory() { _info.id = PREFIX "filter-shader"; _info.type = OBS_SOURCE_TYPE_FILTER; _info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW; + set_activity_tracking_enabled(true); finish_setup(); register_proxy("obs-stream-effects-filter-shader"); } diff --git a/source/filters/filter-shader.hpp b/source/filters/filter-shader.hpp index 19283ec9..2548dbbf 100644 --- a/source/filters/filter-shader.hpp +++ b/source/filters/filter-shader.hpp @@ -43,6 +43,9 @@ namespace streamfx::filter::shader { virtual void video_tick(float_t sec_since_last) override; virtual void video_render(gs_effect_t* effect) override; + + void activate() override; + void deactivate() override; }; class shader_factory : public obs::source_factory { diff --git a/source/gfx/shader/gfx-shader.cpp b/source/gfx/shader/gfx-shader.cpp index da027b49..4825618d 100644 --- a/source/gfx/shader/gfx-shader.cpp +++ b/source/gfx/shader/gfx-shader.cpp @@ -30,20 +30,26 @@ #define ST_SHADER_SIZE ST_SHADER ".Size" #define ST_SHADER_SIZE_WIDTH ST_SHADER_SIZE ".Width" #define ST_SHADER_SIZE_HEIGHT ST_SHADER_SIZE ".Height" +#define ST_SHADER_SEED ST_SHADER ".Seed" #define ST_PARAMETERS ST ".Parameters" gfx::shader::shader::shader(obs_source_t* self, shader_mode mode) - : _self(self), _mode(mode), _base_width(1), _base_height(1), + : _self(self), _mode(mode), _base_width(1), _base_height(1), _active(true), _shader(), _shader_file(), _shader_tech("Draw"), _shader_file_mt(), _shader_file_sz(), _shader_file_tick(0), _width_type(size_type::Percent), _width_value(1.0), _height_type(size_type::Percent), _height_value(1.0), - _time(0), _time_loop(0), _loops(0), _random(), _have_current_params(false), + _have_current_params(false), _time(0), _time_loop(0), _loops(0), _random(), _random_seed(0), _rt_up_to_date(false), _rt(std::make_shared(GS_RGBA, GS_ZS_NONE)) { - _random.seed(static_cast(time(NULL))); + // Intialize random values. + _random.seed(static_cast(_random_seed)); + for (size_t idx = 0; idx < 16; idx++) { + _random_values[idx] = + static_cast(static_cast(_random()) / static_cast(_random.max())); + } } gfx::shader::shader::~shader() {} @@ -180,21 +186,13 @@ void gfx::shader::shader::defaults(obs_data_t* data) obs_data_set_default_string(data, ST_SHADER_TECHNIQUE, ""); obs_data_set_default_string(data, ST_SHADER_SIZE_WIDTH, "100.0 %"); obs_data_set_default_string(data, ST_SHADER_SIZE_HEIGHT, "100.0 %"); + obs_data_set_default_int(data, ST_SHADER_SEED, static_cast(time(NULL))); } void gfx::shader::shader::properties(obs_properties_t* pr) { _have_current_params = false; - { - obs_properties_add_button2( - pr, ST_REFRESH, D_TRANSLATE(ST_REFRESH), - [](obs_properties_t* props, obs_property_t* prop, void* priv) { - return reinterpret_cast(priv)->on_refresh_properties(props, prop); - }, - this); - } - { auto grp = obs_properties_create(); obs_properties_add_group(pr, ST_SHADER, D_TRANSLATE(ST_SHADER), OBS_GROUP_NORMAL, grp); @@ -211,23 +209,20 @@ void gfx::shader::shader::properties(obs_properties_t* pr) auto p = obs_properties_add_path(grp, ST_SHADER_FILE, D_TRANSLATE(ST_SHADER_FILE), OBS_PATH_FILE, "*.*", path.c_str()); obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHADER_FILE))); - /*obs_property_set_modified_callback2( - p, - [](void* priv, obs_properties_t* props, obs_property_t* prop, obs_data_t* data) noexcept { - return reinterpret_cast(priv)->on_shader_or_technique_modified(props, prop, data); - }, - this);*/ } { auto p = obs_properties_add_list(grp, ST_SHADER_TECHNIQUE, D_TRANSLATE(ST_SHADER_TECHNIQUE), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHADER_TECHNIQUE))); - /*obs_property_set_modified_callback2( - p, - [](void* priv, obs_properties_t* props, obs_property_t* prop, obs_data_t* data) noexcept { - return reinterpret_cast(priv)->on_shader_or_technique_modified(props, prop, data); + } + + { + obs_properties_add_button2( + grp, ST_REFRESH, D_TRANSLATE(ST_REFRESH), + [](obs_properties_t* props, obs_property_t* prop, void* priv) { + return reinterpret_cast(priv)->on_refresh_properties(props, prop); }, - this);*/ + this); } if (_mode != shader_mode::Transition) { @@ -245,6 +240,12 @@ void gfx::shader::shader::properties(obs_properties_t* pr) obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHADER_SIZE))); } } + + { + auto p = obs_properties_add_int_slider(grp, ST_SHADER_SEED, D_TRANSLATE(ST_SHADER_SEED), + std::numeric_limits::min(), std::numeric_limits::max(), 1); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHADER_SEED))); + } } { auto grp = obs_properties_create(); @@ -360,6 +361,15 @@ void gfx::shader::shader::update(obs_data_t* data) _height_value = std::clamp(sz_y.second, 0.01, 8192.0); } + if (int32_t seed = static_cast(obs_data_get_int(data, ST_SHADER_SEED)); _random_seed != seed) { + _random_seed = seed; + _random.seed(static_cast(_random_seed)); + for (size_t idx = 0; idx < 16; idx++) { + _random_values[idx] = + static_cast(static_cast(_random()) / static_cast(_random.max())); + } + } + for (auto kv : _shader_params) { kv.second->update(data); } @@ -444,6 +454,12 @@ bool gfx::shader::shader::tick(float_t time) _loops = -_loops; } + // Recreate Per-Activation-Random values. + for (size_t idx = 0; idx < 8; idx++) { + _random_values[8 + idx] = + static_cast(static_cast(_random()) / static_cast(_random.max())); + } + return false; } @@ -456,17 +472,16 @@ void gfx::shader::shader::prepare_render() kv.second->assign(); } - // Time: (Current Time), (Zero), (Zero), (Random Value) + // float4 Time: (Current Time), (Zero), (Zero), (Random Value) if (gs::effect_parameter el = _shader.get_parameter("Time"); el != nullptr) { if (el.get_type() == gs::effect_parameter::type::Float4) { el.set_float4( _time, _time_loop, static_cast(_loops), - static_cast(static_cast(_random()) - / static_cast(std::numeric_limits::max()))); + static_cast(static_cast(_random()) / static_cast(_random.max()))); } } - // ViewSize: (Width), (Height), (1.0 / Width), (1.0 / Height) + // float4 ViewSize: (Width), (Height), (1.0 / Width), (1.0 / Height) if (auto el = _shader.get_parameter("ViewSize"); el != nullptr) { if (el.get_type() == gs::effect_parameter::type::Float4) { el.set_float4(static_cast(width()), static_cast(height()), @@ -474,6 +489,20 @@ void gfx::shader::shader::prepare_render() } } + // float4x4 Random: float4[Per-Instance Random], float4[Per-Activation Random], float4x2[Per-Frame Random] + if (auto el = _shader.get_parameter("Random"); el != nullptr) { + if (el.get_type() == gs::effect_parameter::type::Matrix) { + el.set_value(_random_values, 16); + } + } + + // int32 RandomSeed: Seed used for random generation + if (auto el = _shader.get_parameter("RandomSeed"); el != nullptr) { + if (el.get_type() == gs::effect_parameter::type::Integer) { + el.set_int(_random_seed); + } + } + _rt_up_to_date = false; } @@ -576,3 +605,14 @@ void gfx::shader::shader::set_transition_size(std::uint32_t w, std::uint32_t h) } } } + +void gfx::shader::shader::set_active(bool active) +{ + _active = active; + + // Recreate Per-Activation-Random values. + for (size_t idx = 0; idx < 4; idx++) { + _random_values[4 + idx] = + static_cast(static_cast(_random()) / static_cast(_random.max())); + } +} diff --git a/source/gfx/shader/gfx-shader.hpp b/source/gfx/shader/gfx-shader.hpp index 3f1e4a4a..e8f884ea 100644 --- a/source/gfx/shader/gfx-shader.hpp +++ b/source/gfx/shader/gfx-shader.hpp @@ -47,6 +47,7 @@ namespace gfx { shader_mode _mode; std::uint32_t _base_width; std::uint32_t _base_height; + bool _active; // Shader gs::effect _shader; @@ -64,11 +65,13 @@ namespace gfx { double_t _height_value; // Cache + bool _have_current_params; float_t _time; float_t _time_loop; int32_t _loops; std::mt19937_64 _random; - bool _have_current_params; + int32_t _random_seed; + float_t _random_values[16]; // 0..4 Per-Instance-Random, 4..8 Per-Activation-Random 9..15 Per-Frame-Random // Rendering bool _rt_up_to_date; @@ -121,6 +124,8 @@ namespace gfx { void set_transition_time(float_t t); void set_transition_size(std::uint32_t w, std::uint32_t h); + + void set_active(bool active); }; } // namespace shader } // namespace gfx diff --git a/source/sources/source-shader.cpp b/source/sources/source-shader.cpp index e6fbe574..cf93bc6e 100644 --- a/source/sources/source-shader.cpp +++ b/source/sources/source-shader.cpp @@ -88,12 +88,23 @@ void shader_instance::video_render(gs_effect_t* effect) _fx->render(); } +void streamfx::source::shader::shader_instance::activate() +{ + _fx->set_active(true); +} + +void streamfx::source::shader::shader_instance::deactivate() +{ + _fx->set_active(false); +} + shader_factory::shader_factory() { _info.id = PREFIX "source-shader"; _info.type = OBS_SOURCE_TYPE_INPUT; _info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW; + set_activity_tracking_enabled(true); finish_setup(); register_proxy("obs-stream-effects-source-shader"); } diff --git a/source/sources/source-shader.hpp b/source/sources/source-shader.hpp index 22e46072..eee9e2fd 100644 --- a/source/sources/source-shader.hpp +++ b/source/sources/source-shader.hpp @@ -42,6 +42,9 @@ namespace streamfx::source::shader { virtual void video_tick(float_t sec_since_last) override; virtual void video_render(gs_effect_t* effect) override; + + void activate() override; + void deactivate() override; }; class shader_factory : public obs::source_factory { diff --git a/source/transitions/transition-shader.cpp b/source/transitions/transition-shader.cpp index 14385849..f714ca91 100644 --- a/source/transitions/transition-shader.cpp +++ b/source/transitions/transition-shader.cpp @@ -110,9 +110,14 @@ bool shader_instance::audio_render(uint64_t* ts_out, obs_source_audio_mix* audio [](void*, float_t t) { return t; }); } -void shader_instance::transition_start() {} +void shader_instance::transition_start() { + _fx->set_active(true); +} -void shader_instance::transition_stop() {} +void shader_instance::transition_stop() +{ + _fx->set_active(false); +} shader_factory::shader_factory() { @@ -120,6 +125,7 @@ shader_factory::shader_factory() _info.type = OBS_SOURCE_TYPE_TRANSITION; _info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW; + //set_activity_tracking_enabled(true); // Handled via transition start/stop finish_setup(); register_proxy("obs-stream-effects-transition-shader"); }