From bde4e278bf786ce0765ecb9343eb3b6568017286 Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Wed, 7 Aug 2019 17:14:44 +0200 Subject: [PATCH] gfx-effect-source: Further work on rewrite This partially implements most behavior to an acceptable degree for use right now. --- source/gfx/gfx-effect-source.cpp | 801 +++++++++++++++++++++++++++---- source/gfx/gfx-effect-source.hpp | 221 +++++++-- 2 files changed, 901 insertions(+), 121 deletions(-) diff --git a/source/gfx/gfx-effect-source.cpp b/source/gfx/gfx-effect-source.cpp index c9e2310a..f7d45844 100644 --- a/source/gfx/gfx-effect-source.cpp +++ b/source/gfx/gfx-effect-source.cpp @@ -20,142 +20,688 @@ #include #include #include +#include #include +#include "obs/gs/gs-helper.hpp" #include "strings.hpp" #define ST "Shader" -#define ST_FILE "Shader.File" +#define ST_FILE ST ".File" +#define ST_TECHNIQUE ST ".Technique" -gfx::effect_source::parameter::parameter(std::shared_ptr effect, std::string name) - : effect(effect), name(name), description(""), formulae(""), visible(true) +static std::vector static_parameters{ + "ViewProj", + "Time", + "Random", +}; + +gfx::effect_source::parameter::parameter(std::shared_ptr effect, + std::shared_ptr param) + : _effect(effect), _param(param), _description(""), _formulae(""), _visible(true) { if (!effect) throw std::invalid_argument("effect"); - if (!effect->has_parameter(name)) - throw std::invalid_argument("name"); - param = effect->get_parameter(name); + if (!effect) + throw std::invalid_argument("param"); + if (!effect->has_parameter(param->get_name(), param->get_type())) + throw std::invalid_argument("param"); + _name = _param->get_name(); + _visible_name = _name; + if (param->has_annotation("name", gs::effect_parameter::type::String)) { + param->get_annotation("name")->get_default_string(_visible_name); + } if (param->has_annotation("description", gs::effect_parameter::type::String)) { - param->get_annotation("description")->get_default_string(description); + param->get_annotation("description")->get_default_string(_description); } if (param->has_annotation("formulae", gs::effect_parameter::type::String)) { - param->get_annotation("formulae")->get_default_string(formulae); + param->get_annotation("formulae")->get_default_string(_formulae); } if (param->has_annotation("visible", gs::effect_parameter::type::Boolean)) { - param->get_annotation("visible")->get_default_bool(visible); + param->get_annotation("visible")->get_default_bool(_visible); } else { - visible = true; + _visible = true; } } -gfx::effect_source::bool_parameter::bool_parameter(std::shared_ptr effect, std::string name) - : parameter(effect, name) +gfx::effect_source::parameter::~parameter() {} + +void gfx::effect_source::parameter::properties(obs_properties_t* props) {} + +void gfx::effect_source::parameter::remove_properties(obs_properties_t* props) {} + +void gfx::effect_source::parameter::update(obs_data_t* data) {} + +void gfx::effect_source::parameter::tick(float_t time) {} + +void gfx::effect_source::parameter::prepare() {} + +void gfx::effect_source::parameter::assign() {} + +std::shared_ptr gfx::effect_source::parameter::get_param() +{ + return _param; +} + +std::shared_ptr + gfx::effect_source::parameter::create(std::shared_ptr effect, + std::shared_ptr param) +{ + if (!effect) + throw std::invalid_argument("effect"); + if (!effect) + throw std::invalid_argument("param"); + if (!effect->has_parameter(param->get_name(), param->get_type())) + throw std::invalid_argument("param"); + + switch (param->get_type()) { + case gs::effect_parameter::type::Boolean: + return std::make_shared(effect, param); + case gs::effect_parameter::type::Float: + case gs::effect_parameter::type::Float2: + case gs::effect_parameter::type::Float3: + case gs::effect_parameter::type::Float4: + case gs::effect_parameter::type::Integer: + case gs::effect_parameter::type::Integer2: + case gs::effect_parameter::type::Integer3: + case gs::effect_parameter::type::Integer4: + return std::make_shared(effect, param); + case gs::effect_parameter::type::Matrix: + return std::make_shared(effect, param); + case gs::effect_parameter::type::Texture: + return std::make_shared(effect, param); + } + + return nullptr; +} + +gfx::effect_source::bool_parameter::bool_parameter(std::shared_ptr effect, + std::shared_ptr param) + : parameter(effect, param) { if (param->get_type() != gs::effect_parameter::type::Boolean) throw std::bad_cast(); - param->get_default_bool(value); + param->get_default_bool(_value); } -gfx::effect_source::value_parameter::value_parameter(std::shared_ptr effect, std::string name) - : parameter(effect, name) +void gfx::effect_source::bool_parameter::properties(obs_properties_t* props) +{ + auto p = obs_properties_add_bool(props, _name.c_str(), _visible_name.c_str()); + obs_property_set_long_description(p, _description.c_str()); + obs_property_set_visible(p, _visible); +} + +void gfx::effect_source::bool_parameter::remove_properties(obs_properties_t* props) +{ + obs_properties_remove_by_name(props, _name.c_str()); +} + +void gfx::effect_source::bool_parameter::update(obs_data_t* data) +{ + _value = obs_data_get_bool(data, _name.c_str()); +} + +void gfx::effect_source::bool_parameter::tick(float_t time) {} + +void gfx::effect_source::bool_parameter::prepare() {} + +void gfx::effect_source::bool_parameter::assign() +{ + _param->set_bool(_value); +} + +gfx::effect_source::value_parameter::value_parameter(std::shared_ptr effect, + std::shared_ptr param) + : parameter(effect, param) { std::shared_ptr min = param->get_annotation("minimum"); std::shared_ptr max = param->get_annotation("maximum"); + std::shared_ptr stp = param->get_annotation("step"); + + bool is_int = false; switch (param->get_type()) { case gs::effect_parameter::type::Float: - param->get_default_float(value.f[0]); + param->get_default_float(_value.f[0]); if (min) - min->get_default_float(minimum.f[0]); + min->get_default_float(_minimum.f[0]); if (max) - max->get_default_float(maximum.f[0]); + max->get_default_float(_maximum.f[0]); + if (stp) + stp->get_default_float(_step.f[0]); break; case gs::effect_parameter::type::Float2: - param->get_default_float2(value.f[0], value.f[1]); + param->get_default_float2(_value.f[0], _value.f[1]); if (min) - min->get_default_float2(minimum.f[0], minimum.f[1]); + min->get_default_float2(_minimum.f[0], _minimum.f[1]); if (max) - max->get_default_float2(maximum.f[0], maximum.f[1]); + max->get_default_float2(_maximum.f[0], _maximum.f[1]); + if (stp) + stp->get_default_float2(_step.f[0], _step.f[1]); break; case gs::effect_parameter::type::Float3: - param->get_default_float3(value.f[0], value.f[1], value.f[2]); + param->get_default_float3(_value.f[0], _value.f[1], _value.f[2]); if (min) - min->get_default_float3(minimum.f[0], minimum.f[1], minimum.f[2]); + min->get_default_float3(_minimum.f[0], _minimum.f[1], _minimum.f[2]); if (max) - max->get_default_float3(maximum.f[0], maximum.f[1], maximum.f[2]); + max->get_default_float3(_maximum.f[0], _maximum.f[1], _maximum.f[2]); + if (stp) + stp->get_default_float3(_step.f[0], _step.f[1], _step.f[2]); break; case gs::effect_parameter::type::Float4: - param->get_default_float4(value.f[0], value.f[1], value.f[2], value.f[3]); + param->get_default_float4(_value.f[0], _value.f[1], _value.f[2], _value.f[3]); if (min) - min->get_default_float4(minimum.f[0], minimum.f[1], minimum.f[2], minimum.f[3]); + min->get_default_float4(_minimum.f[0], _minimum.f[1], _minimum.f[2], _minimum.f[3]); if (max) - max->get_default_float4(maximum.f[0], maximum.f[1], maximum.f[2], maximum.f[3]); + max->get_default_float4(_maximum.f[0], _maximum.f[1], _maximum.f[2], _maximum.f[3]); + if (stp) + stp->get_default_float4(_step.f[0], _step.f[1], _step.f[2], _step.f[3]); break; case gs::effect_parameter::type::Integer: - param->get_default_int(value.i[0]); + param->get_default_int(_value.i[0]); if (min) - min->get_default_int(minimum.i[0]); + min->get_default_int(_minimum.i[0]); if (max) - max->get_default_int(maximum.i[0]); + max->get_default_int(_maximum.i[0]); + if (stp) + stp->get_default_int(_step.i[0]); + is_int = true; break; case gs::effect_parameter::type::Integer2: - param->get_default_int2(value.i[0], value.i[1]); + param->get_default_int2(_value.i[0], _value.i[1]); if (min) - min->get_default_int2(minimum.i[0], minimum.i[1]); + min->get_default_int2(_minimum.i[0], _minimum.i[1]); if (max) - max->get_default_int2(maximum.i[0], maximum.i[1]); + max->get_default_int2(_maximum.i[0], _maximum.i[1]); + if (stp) + stp->get_default_int2(_step.i[0], _step.i[1]); + is_int = true; break; case gs::effect_parameter::type::Integer3: - param->get_default_int3(value.i[0], value.i[1], value.i[2]); + param->get_default_int3(_value.i[0], _value.i[1], _value.i[2]); if (min) - min->get_default_int3(minimum.i[0], minimum.i[1], minimum.i[2]); + min->get_default_int3(_minimum.i[0], _minimum.i[1], _minimum.i[2]); if (max) - max->get_default_int3(maximum.i[0], maximum.i[1], maximum.i[2]); + max->get_default_int3(_maximum.i[0], _maximum.i[1], _maximum.i[2]); + if (stp) + stp->get_default_int3(_step.i[0], _step.i[1], _step.i[2]); + is_int = true; break; case gs::effect_parameter::type::Integer4: - param->get_default_int4(value.i[0], value.i[1], value.i[2], value.i[3]); + param->get_default_int4(_value.i[0], _value.i[1], _value.i[2], _value.i[3]); if (min) - min->get_default_int4(minimum.i[0], minimum.i[1], minimum.i[2], minimum.i[3]); + min->get_default_int4(_minimum.i[0], _minimum.i[1], _minimum.i[2], _minimum.i[3]); if (max) - max->get_default_int4(maximum.i[0], maximum.i[1], maximum.i[2], maximum.i[3]); + max->get_default_int4(_maximum.i[0], _maximum.i[1], _maximum.i[2], _maximum.i[3]); + if (stp) + stp->get_default_int4(_step.i[0], _step.i[1], _step.i[2], _step.i[3]); + is_int = true; break; default: throw std::bad_cast(); } + + if (!min) { + if (is_int) { + _minimum.i[0] = _minimum.i[1] = _minimum.i[2] = _minimum.i[3] = std::numeric_limits::min(); + } else { + _minimum.f[0] = _minimum.f[1] = _minimum.f[2] = _minimum.f[3] = -167772.16f; + } + } + if (!max) { + if (is_int) { + _maximum.i[0] = _maximum.i[1] = _maximum.i[2] = _maximum.i[3] = std::numeric_limits::max(); + } else { + _maximum.f[0] = _maximum.f[1] = _maximum.f[2] = _maximum.f[3] = +167772.16f; + } + } + if (!stp) { + if (is_int) { + _step.i[0] = _step.i[1] = _step.i[2] = _step.i[3] = 1; + } else { + _step.f[0] = _step.f[1] = _step.f[2] = _step.f[3] = 0.01f; + } + } + + std::shared_ptr mode = param->get_annotation("mode"); + if (mode && (mode->get_type() == gs::effect_parameter::type::String)) { + std::string mode_str = mode->get_default_string(); + if (strcmpi(mode_str.c_str(), "slider")) { + _mode = value_mode::SLIDER; + } else { + _mode = value_mode::INPUT; + } + } + + for (size_t idx = 0; idx < 4; idx++) { + std::stringstream name_sstr; + std::stringstream ui_sstr; + + name_sstr << _name << '[' << idx << ']'; + ui_sstr << _visible_name << '[' << idx << ']'; + + _cache.name[idx] = name_sstr.str(); + _cache.visible_name[idx] = ui_sstr.str(); + } } -gfx::effect_source::matrix_parameter::matrix_parameter(std::shared_ptr effect, std::string name) - : parameter(effect, name) +void gfx::effect_source::value_parameter::properties(obs_properties_t* props) +{ + auto grp = props; + if (!util::are_property_groups_broken()) { + grp = obs_properties_create(); + auto p = obs_properties_add_group(props, _name.c_str(), _visible_name.c_str(), OBS_GROUP_NORMAL, grp); + obs_property_set_long_description(p, _description.c_str()); + obs_property_set_visible(p, _visible); + } + + bool is_int = false; + size_t limit = 0; + + switch (_param->get_type()) { + case gs::effect_parameter::type::Integer: + is_int = true; + limit = 1; + break; + case gs::effect_parameter::type::Integer2: + is_int = true; + limit = 2; + break; + case gs::effect_parameter::type::Integer3: + is_int = true; + limit = 3; + break; + case gs::effect_parameter::type::Integer4: + is_int = true; + limit = 4; + break; + case gs::effect_parameter::type::Float: + is_int = false; + limit = 1; + break; + case gs::effect_parameter::type::Float2: + is_int = false; + limit = 2; + break; + case gs::effect_parameter::type::Float3: + is_int = false; + limit = 3; + break; + case gs::effect_parameter::type::Float4: + is_int = false; + limit = 4; + break; + } + + for (size_t idx = 0; idx < limit; idx++) { + if (is_int) { + if (_mode == value_mode::INPUT) { + auto p = obs_properties_add_int(grp, _cache.name[idx].c_str(), _cache.visible_name[idx].c_str(), + _minimum.i[idx], _maximum.i[idx], _step.i[idx]); + obs_property_set_visible(p, _visible); + } else { + auto p = obs_properties_add_int_slider(grp, _cache.name[idx].c_str(), _cache.visible_name[idx].c_str(), + _minimum.i[idx], _maximum.i[idx], _step.i[idx]); + obs_property_set_visible(p, _visible); + } + } else { + if (_mode == value_mode::INPUT) { + auto p = obs_properties_add_float(grp, _cache.name[idx].c_str(), _cache.visible_name[idx].c_str(), + _minimum.f[idx], _maximum.f[idx], _step.f[idx]); + obs_property_set_visible(p, _visible); + } else { + auto p = + obs_properties_add_float_slider(grp, _cache.name[idx].c_str(), _cache.visible_name[idx].c_str(), + _minimum.f[idx], _maximum.f[idx], _step.f[idx]); + obs_property_set_visible(p, _visible); + } + } + } +} + +void gfx::effect_source::value_parameter::remove_properties(obs_properties_t* props) +{ + bool is_int = false; + size_t limit = 0; + + switch (_param->get_type()) { + case gs::effect_parameter::type::Integer: + is_int = true; + limit = 1; + break; + case gs::effect_parameter::type::Integer2: + is_int = true; + limit = 2; + break; + case gs::effect_parameter::type::Integer3: + is_int = true; + limit = 3; + break; + case gs::effect_parameter::type::Integer4: + is_int = true; + limit = 4; + break; + case gs::effect_parameter::type::Float: + is_int = false; + limit = 1; + break; + case gs::effect_parameter::type::Float2: + is_int = false; + limit = 2; + break; + case gs::effect_parameter::type::Float3: + is_int = false; + limit = 3; + break; + case gs::effect_parameter::type::Float4: + is_int = false; + limit = 4; + break; + } + + for (size_t idx = 0; idx < limit; idx++) { + obs_properties_remove_by_name(props, _cache.name[idx].c_str()); + } +} + +void gfx::effect_source::value_parameter::update(obs_data_t* data) +{ + bool is_int = false; + size_t limit = 0; + + switch (_param->get_type()) { + case gs::effect_parameter::type::Integer: + is_int = true; + limit = 1; + break; + case gs::effect_parameter::type::Integer2: + is_int = true; + limit = 2; + break; + case gs::effect_parameter::type::Integer3: + is_int = true; + limit = 3; + break; + case gs::effect_parameter::type::Integer4: + is_int = true; + limit = 4; + break; + case gs::effect_parameter::type::Float: + is_int = false; + limit = 1; + break; + case gs::effect_parameter::type::Float2: + is_int = false; + limit = 2; + break; + case gs::effect_parameter::type::Float3: + is_int = false; + limit = 3; + break; + case gs::effect_parameter::type::Float4: + is_int = false; + limit = 4; + break; + } + + for (size_t idx = 0; idx < limit; idx++) { + if (is_int) { + _value.i[idx] = obs_data_get_int(data, _cache.name[idx].c_str()); + } else { + _value.f[idx] = static_cast(obs_data_get_double(data, _cache.name[idx].c_str())); + } + } +} + +void gfx::effect_source::value_parameter::tick(float_t time) {} + +void gfx::effect_source::value_parameter::prepare() {} + +void gfx::effect_source::value_parameter::assign() +{ + switch (_param->get_type()) { + case gs::effect_parameter::type::Integer: + _param->set_int(_value.i[0]); + break; + case gs::effect_parameter::type::Integer2: + _param->set_int2(_value.i[0], _value.i[1]); + break; + case gs::effect_parameter::type::Integer3: + _param->set_int3(_value.i[0], _value.i[1], _value.i[2]); + break; + case gs::effect_parameter::type::Integer4: + _param->set_int4(_value.i[0], _value.i[1], _value.i[2], _value.i[3]); + break; + case gs::effect_parameter::type::Float: + _param->set_float(_value.f[0]); + break; + case gs::effect_parameter::type::Float2: + _param->set_float2(_value.f[0], _value.f[1]); + break; + case gs::effect_parameter::type::Float3: + _param->set_float3(_value.f[0], _value.f[1], _value.f[2]); + break; + case gs::effect_parameter::type::Float4: + _param->set_float4(_value.f[0], _value.f[1], _value.f[2], _value.f[3]); + break; + } +} + +gfx::effect_source::matrix_parameter::matrix_parameter(std::shared_ptr effect, + std::shared_ptr param) + : parameter(effect, param) { std::shared_ptr min = param->get_annotation("minimum"); std::shared_ptr max = param->get_annotation("maximum"); + std::shared_ptr stp = param->get_annotation("step"); - param->get_default_matrix(value); + param->get_default_matrix(_value); if (min) - min->get_default_matrix(minimum); + min->get_default_matrix(_minimum); + else + _minimum = matrix4{vec4{-167772.16f, -167772.16f, -167772.16f, -167772.16f}, + vec4{-167772.16f, -167772.16f, -167772.16f, -167772.16f}, + vec4{-167772.16f, -167772.16f, -167772.16f, -167772.16f}, + vec4{-167772.16f, -167772.16f, -167772.16f, -167772.16f}}; + if (max) - max->get_default_matrix(maximum); + max->get_default_matrix(_maximum); + else + _maximum = matrix4{vec4{-167772.16f, -167772.16f, -167772.16f, -167772.16f}, + vec4{-167772.16f, -167772.16f, -167772.16f, -167772.16f}, + vec4{-167772.16f, -167772.16f, -167772.16f, -167772.16f}, + vec4{-167772.16f, -167772.16f, -167772.16f, -167772.16f}}; + + if (stp) + stp->get_default_matrix(_step); + else + _step = matrix4{vec4{0.01f, 0.01f, 0.01f, 0.01f}, vec4{0.01f, 0.01f, 0.01f, 0.01f}, + vec4{0.01f, 0.01f, 0.01f, 0.01f}, vec4{0.01f, 0.01f, 0.01f, 0.01f}}; + + std::shared_ptr mode = param->get_annotation("mode"); + if (mode && (mode->get_type() == gs::effect_parameter::type::String)) { + std::string mode_str = mode->get_default_string(); + if (strcmpi(mode_str.c_str(), "slider")) { + _mode = value_mode::SLIDER; + } else { + _mode = value_mode::INPUT; + } + } + + for (size_t x = 0; x < 4; x++) { + for (size_t y = 0; y < 4; y++) { + std::stringstream name_sstr; + std::stringstream ui_sstr; + + name_sstr << _name << '[' << x << ']' << '[' << y << ']'; + ui_sstr << _visible_name << '[' << x << ']' << '[' << y << ']'; + + _cache.name[x * 4 + y] = name_sstr.str(); + _cache.visible_name[x * 4 + y] = ui_sstr.str(); + } + } } -gfx::effect_source::string_parameter::string_parameter(std::shared_ptr effect, std::string name) - : parameter(effect, name) +void gfx::effect_source::matrix_parameter::properties(obs_properties_t* props) { - param->get_default_string(value); + auto grp = props; + if (!util::are_property_groups_broken()) { + grp = obs_properties_create(); + auto p = obs_properties_add_group(props, _name.c_str(), _visible_name.c_str(), OBS_GROUP_NORMAL, grp); + obs_property_set_long_description(p, _description.c_str()); + obs_property_set_visible(p, _visible); + } + + for (size_t x = 0; x < 4; x++) { + vec4& min_ref = _minimum.x; + vec4& max_ref = _maximum.x; + vec4& stp_ref = _step.x; + if (x == 0) { + min_ref = _minimum.x; + max_ref = _maximum.x; + stp_ref = _step.x; + } else if (x == 1) { + min_ref = _minimum.y; + max_ref = _maximum.y; + stp_ref = _step.y; + } else if (x == 2) { + min_ref = _minimum.z; + max_ref = _maximum.z; + stp_ref = _step.z; + } else { + min_ref = _minimum.t; + max_ref = _maximum.t; + stp_ref = _step.t; + } + + for (size_t y = 0; y < 4; y++) { + size_t idx = x * 4 + y; + + if (_mode == value_mode::INPUT) { + auto p = obs_properties_add_float(grp, _cache.name[idx].c_str(), _cache.visible_name[idx].c_str(), + min_ref.ptr[y], max_ref.ptr[y], stp_ref.ptr[y]); + obs_property_set_visible(p, _visible); + } else { + auto p = + obs_properties_add_float_slider(grp, _cache.name[idx].c_str(), _cache.visible_name[idx].c_str(), + min_ref.ptr[y], max_ref.ptr[y], stp_ref.ptr[y]); + obs_property_set_visible(p, _visible); + } + } + } } -gfx::effect_source::texture_parameter::texture_parameter(std::shared_ptr effect, std::string name) - : parameter(effect, name) +void gfx::effect_source::matrix_parameter::remove_properties(obs_properties_t* props) +{ + for (size_t x = 0; x < 4; x++) { + for (size_t y = 0; y < 4; y++) { + size_t idx = x * 4 + y; + obs_properties_remove_by_name(props, _cache.name[idx].c_str()); + } + } +} + +void gfx::effect_source::matrix_parameter::update(obs_data_t* data) +{ + for (size_t x = 0; x < 4; x++) { + vec4& v_ref = _value.x; + if (x == 0) { + vec4& v_ref = _value.x; + } else if (x == 1) { + vec4& v_ref = _value.y; + } else if (x == 2) { + vec4& v_ref = _value.z; + } else { + vec4& v_ref = _value.t; + } + + for (size_t y = 0; y < 4; y++) { + size_t idx = x * 4 + y; + v_ref.ptr[y] = obs_data_get_double(data, _cache.name[idx].c_str()); + } + } +} + +void gfx::effect_source::matrix_parameter::tick(float_t time) {} + +void gfx::effect_source::matrix_parameter::prepare() {} + +void gfx::effect_source::matrix_parameter::assign() +{ + _param->set_matrix(_value); +} + +gfx::effect_source::string_parameter::string_parameter(std::shared_ptr effect, + std::shared_ptr param) + : parameter(effect, param) +{ + param->get_default_string(_value); +} + +void gfx::effect_source::string_parameter::properties(obs_properties_t* props) {} + +void gfx::effect_source::string_parameter::remove_properties(obs_properties_t* props) {} + +void gfx::effect_source::string_parameter::update(obs_data_t* data) {} + +void gfx::effect_source::string_parameter::tick(float_t time) {} + +void gfx::effect_source::string_parameter::prepare() {} + +void gfx::effect_source::string_parameter::assign() {} + +gfx::effect_source::texture_parameter::texture_parameter(std::shared_ptr effect, + std::shared_ptr param) + : parameter(effect, param) {} -void gfx::effect_source::shader_instance::load_file(std::string file) +void gfx::effect_source::texture_parameter::properties(obs_properties_t* props) {} + +void gfx::effect_source::texture_parameter::remove_properties(obs_properties_t* props) {} + +void gfx::effect_source::texture_parameter::update(obs_data_t* data) {} + +void gfx::effect_source::texture_parameter::tick(float_t time) {} + +void gfx::effect_source::texture_parameter::prepare() {} + +void gfx::effect_source::texture_parameter::assign() {} + +bool gfx::effect_source::effect_source::modified2(obs_properties_t* props, obs_property_t* property, + obs_data_t* settings) { + auto gctx = gs::context(); + for (auto& kv : _params) { + if (kv.second) + kv.second->remove_properties(props); + } + + try { + const char* str = obs_data_get_string(settings, ST_FILE); + load_file(str ? str : ""); + } catch (std::exception& ex) { + P_LOG_ERROR(" Failed to load effect \"%s\" due to error: %s", _file.c_str(), ex.what()); + } + + for (auto& kv : _params) { + if (kv.second) + kv.second->properties(props); + } + + return true; +} + +void gfx::effect_source::effect_source::load_file(std::string file) +{ + auto gctx = gs::context(); + _params.clear(); _effect.reset(); _file = file; struct stat st; - if (!os_stat(_file.c_str(), &st)) { + if (os_stat(_file.c_str(), &st) == -1) { _last_size = 0; _last_modify_time = 0; _last_create_time = 0; @@ -172,46 +718,83 @@ void gfx::effect_source::shader_instance::load_file(std::string file) param_ident_t identity; identity.first = prm->get_type(); identity.second = prm->get_name(); - std::shared_ptr parameter; - switch (prm->get_type()) { - case gs::effect_parameter::type::Boolean: - parameter = std::make_shared(_effect, prm->get_name()); - break; - case gs::effect_parameter::type::Float: - case gs::effect_parameter::type::Float2: - case gs::effect_parameter::type::Float3: - case gs::effect_parameter::type::Float4: - case gs::effect_parameter::type::Integer: - case gs::effect_parameter::type::Integer2: - case gs::effect_parameter::type::Integer3: - case gs::effect_parameter::type::Integer4: - parameter = std::make_shared(_effect, prm->get_name()); - break; - case gs::effect_parameter::type::Matrix: - parameter = std::make_shared(_effect, prm->get_name()); - break; - case gs::effect_parameter::type::String: - parameter = std::make_shared(_effect, prm->get_name()); - break; - case gs::effect_parameter::type::Texture: - parameter = std::make_shared(_effect, prm->get_name()); - break; + bool skip = false; + for (auto v : static_parameters) { + if (prm->get_name() == v) { + skip = true; + break; + } } + if (skip) + continue; - _params.emplace(identity, parameter); + _params.emplace(identity, parameter::create(_effect, prm)); } } -gfx::effect_source::shader_instance::shader_instance(std::string file) +gfx::effect_source::effect_source::effect_source() : _last_check(0), _last_size(0), _last_modify_time(0), _last_create_time(0) { - load_file(file); + auto gctx = gs::context(); + + _tri = std::make_shared(3ul, uint8_t(1)); + { + auto& vtx = _tri->at(0); + vec3_set(vtx.position, 0, 0, 0); + vec4_set(vtx.uv[0], 0, 0, 0, 0); + } + { + auto& vtx = _tri->at(1); + vec3_set(vtx.position, 2, 0, 0); + vec4_set(vtx.uv[0], 2, 0, 0, 0); + } + { + auto& vtx = _tri->at(2); + vec3_set(vtx.position, 0, 2, 0); + vec4_set(vtx.uv[0], 0, 2, 0, 0); + } } -gfx::effect_source::shader_instance::~shader_instance() {} +gfx::effect_source::effect_source::~effect_source() {} -void gfx::effect_source::shader_instance::tick(float_t time) +void gfx::effect_source::effect_source::properties(obs_properties_t* props) +{ + auto p = obs_properties_add_path(props, ST_FILE, D_TRANSLATE(ST_FILE), OBS_PATH_FILE, "Effects (*.effect);;*.*", + nullptr); + /*obs_property_set_modified_callback2(p, + [](void* priv, obs_properties_t* props, obs_property_t* property, obs_data_t* settings) { + return reinterpret_cast(priv)->modified2(props, property, settings); + }, this);*/ + obs_properties_add_text(props, ST_TECHNIQUE, D_TRANSLATE(ST_TECHNIQUE), OBS_TEXT_DEFAULT); + + for (auto& kv : _params) { + if (kv.second) + kv.second->properties(props); + } +} + +void gfx::effect_source::effect_source::update(obs_data_t* data) +{ + const char* file = obs_data_get_string(data, ST_FILE); + if (file != _file) { + try { + load_file(file); + } catch (std::exception& ex) { + P_LOG_ERROR(" Failed to load effect \"%s\" due to error: %s", _file.c_str(), ex.what()); + } + } + + const char* str = obs_data_get_string(data, ST_TECHNIQUE); + _tech = str ? str : "Draw"; + + for (auto& kv : _params) { + if (kv.second) + kv.second->update(data); + } +} + +void gfx::effect_source::effect_source::tick(float_t time) { _last_check += time; if (_last_check >= 0.5f) { @@ -219,18 +802,65 @@ void gfx::effect_source::shader_instance::tick(float_t time) bool changed = false; struct stat st; - if (os_stat(_file.c_str(), &st)) { + if (os_stat(_file.c_str(), &st) != -1) { changed = (_last_size != st.st_size) || (_last_modify_time != st.st_mtime) || (_last_create_time != st.st_ctime); } if (changed) { - load_file(_file); + try { + load_file(_file); + } catch (std::exception& ex) { + P_LOG_ERROR("Loading shader \"%s\" failed, error: %s", _file.c_str(), ex.what()); + } } } + + for (auto& kv : _params) { + if (kv.second) + kv.second->tick(time); + } + + _time += time; + _time_since_last_tick = time; } -void gfx::effect_source::shader_instance::render(std::string technique) +void gfx::effect_source::effect_source::render() { + if (!_effect) + return; + + for (auto& kv : _params) { + if (kv.second) + kv.second->prepare(); + } + + for (auto& kv : _params) { + if (kv.second) + kv.second->assign(); + } + + // Apply "special" parameters. + _time_active += _time_since_last_tick; + { + auto p_time = _effect->get_parameter("Time"); + if (p_time && (p_time->get_type() == gs::effect_parameter::type::Float4)) { + p_time->set_float4(_time, _time_active, _time_since_last_tick, _random_dist(_random_generator)); + } + auto p_random = _effect->get_parameter("Random"); + if (p_random && (p_random->get_type() == gs::effect_parameter::type::Matrix)) { + matrix4 m; + vec4_set(&m.x, _random_dist(_random_generator), _random_dist(_random_generator), + _random_dist(_random_generator), _random_dist(_random_generator)); + vec4_set(&m.y, _random_dist(_random_generator), _random_dist(_random_generator), + _random_dist(_random_generator), _random_dist(_random_generator)); + vec4_set(&m.z, _random_dist(_random_generator), _random_dist(_random_generator), + _random_dist(_random_generator), _random_dist(_random_generator)); + vec4_set(&m.t, _random_dist(_random_generator), _random_dist(_random_generator), + _random_dist(_random_generator), _random_dist(_random_generator)); + p_random->set_matrix(m); + } + } + gs_blend_state_push(); gs_matrix_push(); @@ -242,7 +872,10 @@ void gfx::effect_source::shader_instance::render(std::string technique) gs_enable_stencil_write(false); gs_ortho(0, 1, 0, 1, -1., 1.); - while (gs_effect_loop(_effect->get_object(), technique.c_str())) { + while (gs_effect_loop(_effect->get_object(), _tech.c_str())) { + gs_load_vertexbuffer(_tri->update()); + gs_load_indexbuffer(nullptr); + gs_draw(gs_draw_mode::GS_TRIS, 0, _tri->size()); } gs_matrix_pop(); diff --git a/source/gfx/gfx-effect-source.hpp b/source/gfx/gfx-effect-source.hpp index 0a0ef6b7..ccfd5c4f 100644 --- a/source/gfx/gfx-effect-source.hpp +++ b/source/gfx/gfx-effect-source.hpp @@ -16,8 +16,10 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #pragma once +#include #include #include +#include #include #include #include @@ -43,61 +45,193 @@ extern "C" { namespace gfx { namespace effect_source { - struct parameter { - std::shared_ptr effect; - std::shared_ptr param; - - std::string name; - std::string description; - std::string formulae; - bool visible; - - parameter(std::shared_ptr effect, std::string name); + enum class value_mode { + INPUT, + SLIDER, }; - struct bool_parameter : parameter { - bool value; - - bool_parameter(std::shared_ptr effect, std::string name); + enum class string_mode { + TEXT, + MULTILINE, + PASSWORD, }; - struct value_parameter : parameter { + enum class texture_mode { + FILE, + SOURCE, + }; + + class parameter { + protected: + std::shared_ptr _effect; + std::shared_ptr _param; + + std::string _name; + std::string _visible_name; + std::string _description; + std::string _formulae; + bool _visible; + + public: + parameter(std::shared_ptr effect, std::shared_ptr param); + virtual ~parameter(); + + virtual void properties(obs_properties_t* props) = 0; + + virtual void remove_properties(obs_properties_t* props) = 0; + + virtual void update(obs_data_t* data) = 0; + + virtual void tick(float_t time) = 0; + + virtual void prepare() = 0; + + virtual void assign() = 0; + + std::shared_ptr get_param(); + + public: + static std::shared_ptr create(std::shared_ptr effect, + std::shared_ptr param); + }; + + class bool_parameter : public parameter { + bool _value; + + public: + bool_parameter(std::shared_ptr effect, std::shared_ptr param); + + virtual void properties(obs_properties_t* props) override; + + virtual void remove_properties(obs_properties_t* props) override; + + virtual void update(obs_data_t* data) override; + + virtual void tick(float_t time) override; + + virtual void prepare() override; + + virtual void assign() override; + }; + + class value_parameter : public parameter { union { - float_t f[16]; + float_t f[4]; int32_t i[4]; - } value; + } _value; union { - float_t f[16]; + float_t f[4]; int32_t i[4]; - } minimum; + } _minimum; union { - float_t f[16]; + float_t f[4]; int32_t i[4]; - } maximum; + } _maximum; + union { + float_t f[4]; + int32_t i[4]; + } _step; + value_mode _mode = value_mode::INPUT; - value_parameter(std::shared_ptr effect, std::string name); + struct { + std::string name[4]; + std::string visible_name[4]; + } _cache; + + public: + value_parameter(std::shared_ptr effect, std::shared_ptr param); + + virtual void properties(obs_properties_t* props) override; + + virtual void remove_properties(obs_properties_t* props) override; + + virtual void update(obs_data_t* data) override; + + virtual void tick(float_t time) override; + + virtual void prepare() override; + + virtual void assign() override; }; - struct matrix_parameter : parameter { - matrix4 value; - matrix4 minimum; - matrix4 maximum; - matrix_parameter(std::shared_ptr effect, std::string name); + class matrix_parameter : public parameter { + matrix4 _value; + matrix4 _minimum; + matrix4 _maximum; + matrix4 _step; + value_mode _mode = value_mode::INPUT; + + struct { + std::string name[16]; + std::string visible_name[16]; + } _cache; + + public: + matrix_parameter(std::shared_ptr effect, std::shared_ptr param); + + virtual void properties(obs_properties_t* props) override; + + virtual void remove_properties(obs_properties_t* props) override; + + virtual void update(obs_data_t* data) override; + + virtual void tick(float_t time) override; + + virtual void prepare() override; + + virtual void assign() override; }; - struct string_parameter : parameter { - std::string value; - string_parameter(std::shared_ptr effect, std::string name); + class string_parameter : public parameter { + std::string _value; + string_mode _mode = string_mode::TEXT; + + public: + string_parameter(std::shared_ptr effect, std::shared_ptr param); + + virtual void properties(obs_properties_t* props) override; + + virtual void remove_properties(obs_properties_t* props) override; + + virtual void update(obs_data_t* data) override; + + virtual void tick(float_t time) override; + + virtual void prepare() override; + + virtual void assign() override; }; - struct texture_parameter : parameter { - std::shared_ptr value; - texture_parameter(std::shared_ptr effect, std::string name); + class texture_parameter : public parameter { + std::string _file_name; + std::shared_ptr _file; + + std::string _source_name; + std::shared_ptr _source; + std::shared_ptr _source_renderer; + + texture_mode _mode = texture_mode::FILE; + + public: + texture_parameter(std::shared_ptr effect, std::shared_ptr param); + + virtual void properties(obs_properties_t* props) override; + + virtual void remove_properties(obs_properties_t* props) override; + + virtual void update(obs_data_t* data) override; + + virtual void tick(float_t time) override; + + virtual void prepare() override; + + virtual void assign() override; }; typedef std::pair param_ident_t; - class shader_instance { + class effect_source { std::string _file; std::shared_ptr _effect; + std::string _tech; std::map> _params; std::shared_ptr _tri; @@ -107,15 +241,28 @@ namespace gfx { time_t _last_modify_time; time_t _last_create_time; + float_t _time; + float_t _time_active; + float_t _time_since_last_tick; + + std::uniform_real_distribution _random_dist{0.f, 1.f}; + std::default_random_engine _random_generator; + + bool modified2(obs_properties_t* props, obs_property_t* property, obs_data_t* settings); + void load_file(std::string file); public: - shader_instance(std::string file); - ~shader_instance(); + effect_source(); + ~effect_source(); + + void properties(obs_properties_t* props); + + void update(obs_data_t* data); void tick(float_t time); - void render(std::string technique); + void render(); }; } // namespace effect_source } // namespace gfx