diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 0b5c5a5c..83f4186a 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -251,17 +251,22 @@ Filter.Transform.Camera.FieldOfView="Field Of View" Filter.Transform.Camera.FieldOfView.Description="Vertical Field of View of the camera." Filter.Transform.Position="Position" Filter.Transform.Position.Description="Position of the rendered quad." -Filter.Transform.Position.X="Position (X)" -Filter.Transform.Position.Y="Position (Y)" -Filter.Transform.Position.Z="Position (Z)" +Filter.Transform.Position.X="X" +Filter.Transform.Position.Y="Y" +Filter.Transform.Position.Z="Z" Filter.Transform.Scale="Scale" Filter.Transform.Scale.Description="Scale of the rendered quad." -Filter.Transform.Scale.X="Scale (X)" -Filter.Transform.Scale.Y="Scale (Y)" +Filter.Transform.Scale.X="X" +Filter.Transform.Scale.Y="Y" Filter.Transform.Shear="Shear" Filter.Transform.Shear.Description="Shearing of the rendered quad." -Filter.Transform.Shear.X="Shear (X)" -Filter.Transform.Shear.Y="Shear (Y)" +Filter.Transform.Shear.X="X" +Filter.Transform.Shear.Y="Y" +Filter.Transform.Rotation="Rotation" +Filter.Transform.Rotation.Description="Euler rotation of the rendered quad, applied using the rotation order." +Filter.Transform.Rotation.X="Pitch (X)" +Filter.Transform.Rotation.Y="Yaw (Y)" +Filter.Transform.Rotation.Z="Roll (Z)" Filter.Transform.Rotation.Order="Rotation Order" Filter.Transform.Rotation.Order.Description="The order in which to apply the euler angles to the rendered quad." Filter.Transform.Rotation.Order.XYZ="Pitch, Yaw, Roll" @@ -270,11 +275,6 @@ Filter.Transform.Rotation.Order.YXZ="Yaw, Pitch, Roll" Filter.Transform.Rotation.Order.YZX="Yaw, Roll, Pitch" Filter.Transform.Rotation.Order.ZXY="Roll, Pitch, Yaw" Filter.Transform.Rotation.Order.ZYX="Roll, Yaw, Pitch" -Filter.Transform.Rotation="Rotation" -Filter.Transform.Rotation.Description="Euler rotation of the rendered quad, applied using the rotation order." -Filter.Transform.Rotation.X="Pitch (X)" -Filter.Transform.Rotation.Y="Yaw (Y)" -Filter.Transform.Rotation.Z="Roll (Z)" Filter.Transform.Mipmapping="Enable Mipmapping" Filter.Transform.Mipmapping.Description="Generate mipmaps for the source, so that angled and far away parts are smoother." diff --git a/source/filters/filter-transform.cpp b/source/filters/filter-transform.cpp index d8e34214..26055354 100644 --- a/source/filters/filter-transform.cpp +++ b/source/filters/filter-transform.cpp @@ -18,7 +18,9 @@ */ #include "filter-transform.hpp" +#include #include +#include "obs/gs/gs-helper.hpp" #include "strings.hpp" #include "util-math.hpp" @@ -77,13 +79,13 @@ enum RotationOrder : int64_t { }; filter::transform::transform_instance::transform_instance(obs_data_t* data, obs_source_t* context) - : obs::source_instance(data, context), _source_rendered(false), _mipmap_enabled(false), _mipmap_strength(50.0), - _mipmap_generator(gs::mipmapper::generator::Linear), _update_mesh(false), _rotation_order(RotationOrder::ZXY), - _camera_orthographic(true), _camera_fov(90.0) + : obs::source_instance(data, context), _cache_rendered(), _mipmap_enabled(), _mipmap_strength(), + _mipmap_generator(), _source_rendered(), _source_size(), _update_mesh(), _rotation_order(), + _camera_orthographic(), _camera_fov() { - _source_rendertarget = std::make_shared(GS_RGBA, GS_ZS_NONE); - _shape_rendertarget = std::make_shared(GS_RGBA, GS_ZS_NONE); - _vertex_buffer = std::make_shared(uint32_t(4u), uint8_t(1u)); + _cache_rt = std::make_shared(GS_RGBA, GS_ZS_NONE); + _source_rt = std::make_shared(GS_RGBA, GS_ZS_NONE); + _vertex_buffer = std::make_shared(uint32_t(4u), uint8_t(1u)); _position = std::make_unique(); _rotation = std::make_unique(); @@ -104,37 +106,41 @@ filter::transform::transform_instance::~transform_instance() _rotation.reset(); _position.reset(); _vertex_buffer.reset(); - _shape_texture.reset(); - _shape_rendertarget.reset(); - _source_texture.reset(); - _source_rendertarget.reset(); + _cache_rt.reset(); + _cache_texture.reset(); + _mipmap_texture.reset(); } -void filter::transform::transform_instance::update(obs_data_t* data) +void filter::transform::transform_instance::load(obs_data_t* settings) +{ + update(settings); +} + +void filter::transform::transform_instance::update(obs_data_t* settings) { // Camera - _camera_orthographic = obs_data_get_int(data, ST_CAMERA) == 0; - _camera_fov = (float)obs_data_get_double(data, ST_CAMERA_FIELDOFVIEW); + _camera_orthographic = obs_data_get_int(settings, ST_CAMERA) == 0; + _camera_fov = (float)obs_data_get_double(settings, ST_CAMERA_FIELDOFVIEW); // Source - _position->x = static_cast(obs_data_get_double(data, ST_POSITION_X) / 100.0); - _position->y = static_cast(obs_data_get_double(data, ST_POSITION_Y) / 100.0); - _position->z = static_cast(obs_data_get_double(data, ST_POSITION_Z) / 100.0); - _scale->x = static_cast(obs_data_get_double(data, ST_SCALE_X) / 100.0); - _scale->y = static_cast(obs_data_get_double(data, ST_SCALE_Y) / 100.0); + _position->x = static_cast(obs_data_get_double(settings, ST_POSITION_X) / 100.0); + _position->y = static_cast(obs_data_get_double(settings, ST_POSITION_Y) / 100.0); + _position->z = static_cast(obs_data_get_double(settings, ST_POSITION_Z) / 100.0); + _scale->x = static_cast(obs_data_get_double(settings, ST_SCALE_X) / 100.0); + _scale->y = static_cast(obs_data_get_double(settings, ST_SCALE_Y) / 100.0); _scale->z = 1.0f; - _rotation_order = static_cast(obs_data_get_int(data, ST_ROTATION_ORDER)); - _rotation->x = static_cast(obs_data_get_double(data, ST_ROTATION_X) / 180.0 * S_PI); - _rotation->y = static_cast(obs_data_get_double(data, ST_ROTATION_Y) / 180.0 * S_PI); - _rotation->z = static_cast(obs_data_get_double(data, ST_ROTATION_Z) / 180.0 * S_PI); - _shear->x = static_cast(obs_data_get_double(data, ST_SHEAR_X) / 100.0); - _shear->y = static_cast(obs_data_get_double(data, ST_SHEAR_Y) / 100.0); + _rotation_order = static_cast(obs_data_get_int(settings, ST_ROTATION_ORDER)); + _rotation->x = static_cast(obs_data_get_double(settings, ST_ROTATION_X) / 180.0 * S_PI); + _rotation->y = static_cast(obs_data_get_double(settings, ST_ROTATION_Y) / 180.0 * S_PI); + _rotation->z = static_cast(obs_data_get_double(settings, ST_ROTATION_Z) / 180.0 * S_PI); + _shear->x = static_cast(obs_data_get_double(settings, ST_SHEAR_X) / 100.0); + _shear->y = static_cast(obs_data_get_double(settings, ST_SHEAR_Y) / 100.0); _shear->z = 0.0f; // Mipmapping - _mipmap_enabled = obs_data_get_bool(data, ST_MIPMAPPING); - _mipmap_strength = obs_data_get_double(data, S_MIPGENERATOR_INTENSITY); - _mipmap_generator = static_cast(obs_data_get_int(data, S_MIPGENERATOR)); + _mipmap_enabled = obs_data_get_bool(settings, ST_MIPMAPPING); + _mipmap_strength = obs_data_get_double(settings, S_MIPGENERATOR_INTENSITY); + _mipmap_generator = static_cast(obs_data_get_int(settings, S_MIPGENERATOR)); _update_mesh = true; } @@ -212,6 +218,7 @@ void filter::transform::transform_instance::video_tick(float) break; } matrix4_translate3f(&ident, &ident, _position->x, _position->y, _position->z); + //matrix4_scale3f(&ident, &ident, _source_size.first / 2.f, _source_size.second / 2.f, 1.f); /// Calculate vertex position once only. float_t p_x = aspectRatioX * _scale->x; @@ -251,163 +258,143 @@ void filter::transform::transform_instance::video_tick(float) _update_mesh = false; } - this->_source_rendered = false; + _cache_rendered = false; + _mipmap_rendered = false; + _source_rendered = false; } -void filter::transform::transform_instance::video_render(gs_effect_t* paramEffect) +void filter::transform::transform_instance::video_render(gs_effect_t*) { - // Grab parent and target. - obs_source_t* parent = obs_filter_get_parent(_self); - obs_source_t* target = obs_filter_get_target(_self); - if (!parent || !target) { + obs_source_t* parent = obs_filter_get_parent(_self); + obs_source_t* target = obs_filter_get_target(_self); + uint32_t base_width = obs_source_get_base_width(target); + uint32_t base_height = obs_source_get_base_height(target); + gs_effect_t* default_effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); + gs_effect_t* effect = default_effect; + + if (!base_width || !base_height || !parent || !target) { // Skip if something is wrong. obs_source_skip_video_filter(_self); return; } - // Grab width an height of the target source (child filter or source). - uint32_t width = obs_source_get_base_width(target); - uint32_t height = obs_source_get_base_height(target); - if ((width == 0) || (height == 0)) { + gs::debug_marker marker{gs::debug_color_source, "3D Transform: %s", obs_source_get_name(_self)}; + + uint32_t cache_width = base_width; + uint32_t cache_height = base_height; + + if (_mipmap_enabled) { + double_t aspect = double_t(base_width) / double_t(base_height); + double_t aspect2 = 1.0 / aspect; + cache_width = std::clamp(uint32_t(pow(2, util::math::get_power_of_two_exponent_ceil(cache_width))), 1u, 16384u); + cache_height = + std::clamp(uint32_t(pow(2, util::math::get_power_of_two_exponent_ceil(cache_height))), 1u, 16384u); + + if (aspect > 1.0) { + cache_height = std::clamp( + uint32_t(pow(2, util::math::get_power_of_two_exponent_ceil(uint64_t(cache_width * aspect2)))), 1u, + 16384u); + } else if (aspect < 1.0) { + cache_width = std::clamp( + uint32_t(pow(2, util::math::get_power_of_two_exponent_ceil(uint64_t(cache_height * aspect)))), 1u, + 16384u); + } + } + + if (!_cache_rendered) { + gs::debug_marker _marker_cache{gs::debug_color_cache, "Cache"}; + auto op = _cache_rt->render(cache_width, cache_height); + + gs_reset_blend_state(); + gs_blend_function(GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA); + gs_enable_depth_test(false); + gs_enable_stencil_test(false); + gs_enable_stencil_write(false); + gs_enable_color(true, true, true, true); + gs_set_cull_mode(GS_NEITHER); + + gs_ortho(0, static_cast(base_width), 0, static_cast(base_height), -1, 1); + + vec4 clear_color = {0}; + gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &clear_color, 0, 0); + + /// Render original source + if (obs_source_process_filter_begin(_self, GS_RGBA, OBS_ALLOW_DIRECT_RENDERING)) { + obs_source_process_filter_end(_self, effect, base_width, base_height); + } else { + obs_source_skip_video_filter(_self); + return; + } + + _cache_rendered = true; + } + _cache_rt->get_texture(_cache_texture); + if (!_cache_texture) { obs_source_skip_video_filter(_self); return; } - gs_effect_t* default_effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); + if (_mipmap_enabled) { + gs::debug_marker _marker_mipmap{gs::debug_color_convert, "Mipmap"}; - // Only render if we didn't already render. - if (!this->_source_rendered) { - std::shared_ptr source_tex; - uint32_t real_width = width; - uint32_t real_height = height; + if (!_mipmap_texture || (_mipmap_texture->get_width() != cache_width) + || (_mipmap_texture->get_height() != cache_height)) { + size_t mip_levels = std::max(util::math::get_power_of_two_exponent_ceil(cache_width), + util::math::get_power_of_two_exponent_ceil(cache_height)); - // If MipMapping is enabled, resize Render Target to be a Power of Two. - if (_mipmap_enabled) { - real_width = uint32_t(pow(2, util::math::get_power_of_two_exponent_ceil(width))); - real_height = uint32_t(pow(2, util::math::get_power_of_two_exponent_ceil(height))); - if ((real_width >= 8192) || (real_height >= 8192)) { - // Most GPUs cap out here, so let's not go higher. - double_t aspect = double_t(width) / double_t(height); - if (aspect > 1.0) { // height < width - real_width = 8192; - real_height = uint32_t(real_width / aspect); - } else if (aspect < 1.0) { // width > height - real_height = 8192; - real_width = uint32_t(real_height * aspect); - } - } + _mipmap_texture = std::make_shared(cache_width, cache_height, GS_RGBA, mip_levels, nullptr, + gs::texture::flags::None); } + _mipmapper.rebuild(_cache_texture, _mipmap_texture, _mipmap_generator, float_t(_mipmap_strength)); - // Draw previous filters to texture. - try { - GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_ITEM_TEXTURE, "Filter Cache"); - auto op = _source_rendertarget->render(real_width, real_height); - - gs_set_cull_mode(GS_NEITHER); - gs_reset_blend_state(); - gs_blend_function_separate(gs_blend_type::GS_BLEND_ONE, gs_blend_type::GS_BLEND_ZERO, - gs_blend_type::GS_BLEND_ONE, gs_blend_type::GS_BLEND_ZERO); - gs_enable_depth_test(false); - gs_enable_stencil_test(false); - gs_enable_stencil_write(false); - gs_enable_color(true, true, true, true); - gs_ortho(0, static_cast(width), 0, static_cast(height), -1, 1); - - vec4 black; - vec4_zero(&black); - gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &black, 0, 0); - - /// Render original source - if (obs_source_process_filter_begin(_self, GS_RGBA, OBS_NO_DIRECT_RENDERING)) { - obs_source_process_filter_end(_self, paramEffect ? paramEffect : default_effect, width, height); - } else { - obs_source_skip_video_filter(_self); - } - GS_DEBUG_MARKER_END(); - } catch (...) { - GS_DEBUG_MARKER_END(); - obs_source_skip_video_filter(_self); - return; - } - _source_rendertarget->get_texture(source_tex); - - if (_mipmap_enabled) { - GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_ITEM_TEXTURE, "Mipmapping"); - if ((!_source_texture) || (_source_texture->get_width() != real_width) - || (_source_texture->get_height() != real_height)) { - size_t mip_levels = 0; - if (util::math::is_power_of_two(real_width) && util::math::is_power_of_two(real_height)) { - size_t w_level = util::math::get_power_of_two_exponent_ceil(real_width); - size_t h_level = util::math::get_power_of_two_exponent_ceil(real_height); - if (h_level > w_level) { - mip_levels = h_level; - } else { - mip_levels = w_level; - } - } - - _source_texture = - std::make_shared(real_width, real_height, GS_RGBA, uint32_t(1u + mip_levels), nullptr, - gs::texture::flags::BuildMipMaps); - } - - _mipmapper.rebuild(source_tex, _source_texture, _mipmap_generator, float_t(_mipmap_strength)); - GS_DEBUG_MARKER_END(); - } - - // Draw shape to texture - try { - GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_ITEM_TEXTURE, "Transforming"); - auto op = _shape_rendertarget->render(width, height); - - if (_camera_orthographic) { - gs_ortho(-1.0, 1.0, -1.0, 1.0, -farZ, farZ); - } else { - gs_perspective(_camera_fov, float_t(width) / float_t(height), nearZ, farZ); - // Fix camera pointing at -Z instead of +Z. - gs_matrix_scale3f(1.0, 1.0, -1.0); - // Move backwards so we can actually see stuff. - gs_matrix_translate3f(0, 0, 1.0); - } - - // Rendering - vec4 black; - vec4_zero(&black); - gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &black, farZ, 0); - gs_set_cull_mode(GS_NEITHER); - gs_enable_blending(false); - gs_enable_depth_test(false); - gs_depth_function(gs_depth_test::GS_ALWAYS); - gs_enable_stencil_test(false); - gs_enable_stencil_write(false); - gs_enable_color(true, true, true, true); - gs_load_vertexbuffer(_vertex_buffer->update(false)); - gs_load_indexbuffer(nullptr); - while (gs_effect_loop(default_effect, "Draw")) { - gs_effect_set_texture(gs_effect_get_param_by_name(default_effect, "image"), - _mipmap_enabled ? _source_texture->get_object() : source_tex->get_object()); - gs_draw(GS_TRISTRIP, 0, 4); - } - gs_load_vertexbuffer(nullptr); - GS_DEBUG_MARKER_END(); - } catch (...) { - GS_DEBUG_MARKER_END(); - obs_source_skip_video_filter(_self); - return; - } - _shape_rendertarget->get_texture(_shape_texture); - - this->_source_rendered = true; + _mipmap_rendered = true; + } else { + _mipmap_texture = _cache_texture; + } + if (!_mipmap_texture) { + obs_source_skip_video_filter(_self); + return; } - // Draw final shape - GS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_SOURCE, "3D Transform: %s", obs_source_get_name(_self)); - gs_reset_blend_state(); - gs_enable_depth_test(false); - while (gs_effect_loop(default_effect, "Draw")) { - gs_effect_set_texture(gs_effect_get_param_by_name(default_effect, "image"), _shape_texture->get_object()); - gs_draw_sprite(_shape_texture->get_object(), 0, 0, 0); + { + gs::debug_marker _marker_draw{gs::debug_color_cache_render, "Geometry"}; + auto op = _source_rt->render(base_width, base_height); + + gs_matrix_push(); + if (_camera_orthographic) { + gs_ortho(-1., 1., -1., 1., -farZ, farZ); + } else { + gs_perspective(_camera_fov, float_t(base_width) / float_t(base_height), nearZ, farZ); + gs_matrix_scale3f(1.0, 1.0, 1.0); + gs_matrix_translate3f(0., 0., -1.0); + } + + vec4 clear_color = {0}; + gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &clear_color, 0, 0); + + gs_load_vertexbuffer(_vertex_buffer->update(false)); + gs_load_indexbuffer(nullptr); + gs_effect_set_texture(gs_effect_get_param_by_name(default_effect, "image"), + _mipmap_enabled ? _mipmap_texture->get_object() : _cache_texture->get_object()); + while (gs_effect_loop(default_effect, "Draw")) { + gs_draw(GS_TRISTRIP, 0, 4); + } + gs_load_vertexbuffer(nullptr); + + gs_matrix_pop(); + } + _source_rt->get_texture(_source_texture); + if (!_source_texture) { + obs_source_skip_video_filter(_self); + return; + } + + { + gs::debug_marker _marker_draw{gs::debug_color_cache_render, "Geometry"}; + gs_effect_set_texture(gs_effect_get_param_by_name(default_effect, "image"), _source_texture->get_object()); + while (gs_effect_loop(default_effect, "Draw")) { + gs_draw_sprite(nullptr, 0, base_width, base_height); + } } - GS_DEBUG_MARKER_END(); } std::shared_ptr filter::transform::transform_factory::factory_instance = nullptr; @@ -416,7 +403,7 @@ filter::transform::transform_factory::transform_factory() { _info.id = "obs-stream-effects-filter-transform"; _info.type = OBS_SOURCE_TYPE_FILTER; - _info.output_flags = OBS_SOURCE_VIDEO; + _info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW; _info.get_width = _info.get_height = nullptr; @@ -430,25 +417,29 @@ const char* filter::transform::transform_factory::get_name() return D_TRANSLATE(ST); } -void filter::transform::transform_factory::get_defaults2(obs_data_t* data) +void filter::transform::transform_factory::get_defaults2(obs_data_t* settings) { - obs_data_set_default_int(data, ST_CAMERA, (int64_t)CameraMode::Orthographic); - obs_data_set_default_double(data, ST_CAMERA_FIELDOFVIEW, 90.0); - obs_data_set_default_double(data, ST_POSITION_X, 0); - obs_data_set_default_double(data, ST_POSITION_Y, 0); - obs_data_set_default_double(data, ST_POSITION_Z, 0); - obs_data_set_default_double(data, ST_ROTATION_X, 0); - obs_data_set_default_double(data, ST_ROTATION_Y, 0); - obs_data_set_default_double(data, ST_ROTATION_Z, 0); - obs_data_set_default_double(data, ST_SCALE_X, 100); - obs_data_set_default_double(data, ST_SCALE_Y, 100); - obs_data_set_default_double(data, ST_SHEAR_X, 0); - obs_data_set_default_double(data, ST_SHEAR_Y, 0); - obs_data_set_default_bool(data, S_ADVANCED, false); - obs_data_set_default_int(data, ST_ROTATION_ORDER, RotationOrder::ZXY); + obs_data_set_default_int(settings, ST_CAMERA, (int64_t)CameraMode::Orthographic); + obs_data_set_default_double(settings, ST_CAMERA_FIELDOFVIEW, 90.0); + obs_data_set_default_double(settings, ST_POSITION_X, 0); + obs_data_set_default_double(settings, ST_POSITION_Y, 0); + obs_data_set_default_double(settings, ST_POSITION_Z, 0); + obs_data_set_default_double(settings, ST_ROTATION_X, 0); + obs_data_set_default_double(settings, ST_ROTATION_Y, 0); + obs_data_set_default_double(settings, ST_ROTATION_Z, 0); + obs_data_set_default_int(settings, ST_ROTATION_ORDER, RotationOrder::ZXY); + obs_data_set_default_double(settings, ST_SCALE_X, 100); + obs_data_set_default_double(settings, ST_SCALE_Y, 100); + obs_data_set_default_double(settings, ST_SHEAR_X, 0); + obs_data_set_default_double(settings, ST_SHEAR_Y, 0); + obs_data_set_default_bool(settings, S_ADVANCED, false); + obs_data_set_default_bool(settings, ST_MIPMAPPING, false); + obs_data_set_default_int(settings, S_MIPGENERATOR, static_cast(gs::mipmapper::generator::Linear)); + obs_data_set_default_double(settings, S_MIPGENERATOR_INTENSITY, 100.0); } -static bool modified_properties(obs_properties_t* pr, obs_property_t*, obs_data_t* d) noexcept try { +static bool modified_properties(obs_properties_t* pr, obs_property_t*, obs_data_t* d) noexcept +try { switch ((CameraMode)obs_data_get_int(d, ST_CAMERA)) { case CameraMode::Orthographic: obs_property_set_visible(obs_properties_get(pr, ST_CAMERA_FIELDOFVIEW), false); @@ -464,10 +455,6 @@ static bool modified_properties(obs_properties_t* pr, obs_property_t*, obs_data_ obs_property_set_visible(obs_properties_get(pr, ST_ROTATION_ORDER), advancedVisible); obs_property_set_visible(obs_properties_get(pr, ST_MIPMAPPING), advancedVisible); - bool mipmappingVisible = obs_data_get_bool(d, ST_MIPMAPPING) && advancedVisible; - obs_property_set_visible(obs_properties_get(pr, S_MIPGENERATOR), mipmappingVisible); - obs_property_set_visible(obs_properties_get(pr, S_MIPGENERATOR_INTENSITY), mipmappingVisible); - return true; } catch (const std::exception& ex) { P_LOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); @@ -478,99 +465,128 @@ static bool modified_properties(obs_properties_t* pr, obs_property_t*, obs_data_ obs_properties_t* filter::transform::transform_factory::get_properties2(filter::transform::transform_instance* data) { obs_properties_t* pr = obs_properties_create(); - obs_property_t* p = NULL; // Camera - /// Projection Mode - p = obs_properties_add_list(pr, ST_CAMERA, D_TRANSLATE(ST_CAMERA), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_CAMERA))); - obs_property_list_add_int(p, D_TRANSLATE(ST_CAMERA_ORTHOGRAPHIC), (int64_t)CameraMode::Orthographic); - obs_property_list_add_int(p, D_TRANSLATE(ST_CAMERA_PERSPECTIVE), (int64_t)CameraMode::Perspective); - obs_property_set_modified_callback(p, modified_properties); - /// Field Of View - p = obs_properties_add_float_slider(pr, ST_CAMERA_FIELDOFVIEW, D_TRANSLATE(ST_CAMERA_FIELDOFVIEW), 1.0, 179.0, - 0.01); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_CAMERA_FIELDOFVIEW))); + { + auto grp = obs_properties_create(); + + { // Projection Mode + auto p = obs_properties_add_list(grp, ST_CAMERA, D_TRANSLATE(ST_CAMERA), OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_CAMERA))); + obs_property_list_add_int(p, D_TRANSLATE(ST_CAMERA_ORTHOGRAPHIC), (int64_t)CameraMode::Orthographic); + obs_property_list_add_int(p, D_TRANSLATE(ST_CAMERA_PERSPECTIVE), (int64_t)CameraMode::Perspective); + obs_property_set_modified_callback(p, modified_properties); + } + { // Field Of View + auto p = obs_properties_add_float_slider(grp, ST_CAMERA_FIELDOFVIEW, D_TRANSLATE(ST_CAMERA_FIELDOFVIEW), + 1.0, 179.0, 0.01); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_CAMERA_FIELDOFVIEW))); + } + + obs_properties_add_group(pr, ST_CAMERA, D_TRANSLATE(ST_CAMERA), OBS_GROUP_NORMAL, grp); + } // Mesh - /// Position - { - std::pair entries[] = { - std::make_pair(ST_POSITION_X, D_DESC(ST_POSITION)), - std::make_pair(ST_POSITION_Y, D_DESC(ST_POSITION)), - std::make_pair(ST_POSITION_Z, D_DESC(ST_POSITION)), - }; - for (auto kv : entries) { - p = obs_properties_add_float(pr, kv.first, D_TRANSLATE(kv.first), -10000, 10000, 0.01); - obs_property_set_long_description(p, D_TRANSLATE(kv.second)); + { // Position + auto grp = obs_properties_create(); + + const char* opts[] = {ST_POSITION_X, ST_POSITION_Y, ST_POSITION_Z}; + for (auto opt : opts) { + auto p = obs_properties_add_float(grp, opt, D_TRANSLATE(opt), std::numeric_limits::lowest(), + std::numeric_limits::max(), 0.01); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_POSITION))); } + + obs_properties_add_group(pr, ST_POSITION, D_TRANSLATE(ST_POSITION), OBS_GROUP_NORMAL, grp); } - /// Rotation - { - std::pair entries[] = { - std::make_pair(ST_ROTATION_X, D_DESC(ST_ROTATION)), - std::make_pair(ST_ROTATION_Y, D_DESC(ST_ROTATION)), - std::make_pair(ST_ROTATION_Z, D_DESC(ST_ROTATION)), - }; - for (auto kv : entries) { - p = obs_properties_add_float_slider(pr, kv.first, D_TRANSLATE(kv.first), -180, 180, 0.01); - obs_property_set_long_description(p, D_TRANSLATE(kv.second)); + { // Rotation + auto grp = obs_properties_create(); + + { + const char* opts[] = {ST_ROTATION_X, ST_ROTATION_Y, ST_ROTATION_Z}; + for (auto opt : opts) { + auto p = obs_properties_add_float_slider(grp, opt, D_TRANSLATE(opt), -180.0, 180.0, 0.01); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_ROTATION))); + obs_property_float_set_suffix(p, "° Deg"); + } } + + { // Order + auto p = obs_properties_add_list(grp, ST_ROTATION_ORDER, D_TRANSLATE(ST_ROTATION_ORDER), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_ROTATION_ORDER))); + obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_XYZ), RotationOrder::XYZ); + obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_XZY), RotationOrder::XZY); + obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_YXZ), RotationOrder::YXZ); + obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_YZX), RotationOrder::YZX); + obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_ZXY), RotationOrder::ZXY); + obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_ZYX), RotationOrder::ZYX); + } + + obs_properties_add_group(pr, ST_ROTATION, D_TRANSLATE(ST_ROTATION), OBS_GROUP_NORMAL, grp); } - /// Scale - { - std::pair entries[] = { - std::make_pair(ST_SCALE_X, D_DESC(ST_SCALE)), - std::make_pair(ST_SCALE_Y, D_DESC(ST_SCALE)), - }; - for (auto kv : entries) { - p = obs_properties_add_float_slider(pr, kv.first, D_TRANSLATE(kv.first), -1000, 1000, 0.01); - obs_property_set_long_description(p, D_TRANSLATE(kv.second)); + { // Scale + auto grp = obs_properties_create(); + + const char* opts[] = {ST_SCALE_X, ST_SCALE_Y}; + for (auto opt : opts) { + auto p = obs_properties_add_float_slider(grp, opt, D_TRANSLATE(opt), -1000, 1000, 0.01); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALE))); + obs_property_float_set_suffix(p, "%"); } + + obs_properties_add_group(pr, ST_SCALE, D_TRANSLATE(ST_SCALE), OBS_GROUP_NORMAL, grp); } - /// Shear - { - std::pair entries[] = { - std::make_pair(ST_SHEAR_X, D_DESC(ST_SHEAR)), - std::make_pair(ST_SHEAR_Y, D_DESC(ST_SHEAR)), - }; - for (auto kv : entries) { - p = obs_properties_add_float_slider(pr, kv.first, D_TRANSLATE(kv.first), -100.0, 100.0, 0.01); - obs_property_set_long_description(p, D_TRANSLATE(kv.second)); + { // Shear + auto grp = obs_properties_create(); + + const char* opts[] = {ST_SHEAR_X, ST_SHEAR_Y}; + for (auto opt : opts) { + auto p = obs_properties_add_float_slider(grp, opt, D_TRANSLATE(opt), -200.0, 200.0, 0.01); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHEAR))); + obs_property_float_set_suffix(p, "%"); } + + obs_properties_add_group(pr, ST_SHEAR, D_TRANSLATE(ST_SHEAR), OBS_GROUP_NORMAL, grp); } - p = obs_properties_add_bool(pr, S_ADVANCED, D_TRANSLATE(S_ADVANCED)); - obs_property_set_modified_callback(p, modified_properties); + { + auto p = obs_properties_add_bool(pr, S_ADVANCED, D_TRANSLATE(S_ADVANCED)); + obs_property_set_modified_callback(p, modified_properties); + } - p = obs_properties_add_list(pr, ST_ROTATION_ORDER, D_TRANSLATE(ST_ROTATION_ORDER), OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_ROTATION_ORDER))); - obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_XYZ), RotationOrder::XYZ); - obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_XZY), RotationOrder::XZY); - obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_YXZ), RotationOrder::YXZ); - obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_YZX), RotationOrder::YZX); - obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_ZXY), RotationOrder::ZXY); - obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_ZYX), RotationOrder::ZYX); + { + auto grp = obs_properties_create(); - p = obs_properties_add_bool(pr, ST_MIPMAPPING, D_TRANSLATE(ST_MIPMAPPING)); - obs_property_set_modified_callback(p, modified_properties); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_MIPMAPPING))); + { + auto p = obs_properties_add_list(grp, S_MIPGENERATOR, D_TRANSLATE(S_MIPGENERATOR), OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(S_MIPGENERATOR))); + obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_POINT), (long long)gs::mipmapper::generator::Point); + obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_LINEAR), + (long long)gs::mipmapper::generator::Linear); + obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_SHARPEN), + (long long)gs::mipmapper::generator::Sharpen); + obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_SMOOTHEN), + (long long)gs::mipmapper::generator::Smoothen); + obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_BICUBIC), + (long long)gs::mipmapper::generator::Bicubic); + obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_LANCZOS), + (long long)gs::mipmapper::generator::Lanczos); + } + { + auto p = obs_properties_add_float_slider(grp, S_MIPGENERATOR_INTENSITY, + D_TRANSLATE(S_MIPGENERATOR_INTENSITY), 0.0, 1000.0, 0.01); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(S_MIPGENERATOR_INTENSITY))); + obs_property_float_set_suffix(p, "%"); + } - p = obs_properties_add_list(pr, S_MIPGENERATOR, D_TRANSLATE(S_MIPGENERATOR), OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(S_MIPGENERATOR))); - - obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_POINT), (long long)gs::mipmapper::generator::Point); - obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_LINEAR), (long long)gs::mipmapper::generator::Linear); - obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_SHARPEN), (long long)gs::mipmapper::generator::Sharpen); - obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_SMOOTHEN), (long long)gs::mipmapper::generator::Smoothen); - obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_BICUBIC), (long long)gs::mipmapper::generator::Bicubic); - obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_LANCZOS), (long long)gs::mipmapper::generator::Lanczos); - - p = obs_properties_add_float_slider(pr, S_MIPGENERATOR_INTENSITY, D_TRANSLATE(S_MIPGENERATOR_INTENSITY), 0.0, - 1000.0, 0.01); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(S_MIPGENERATOR_INTENSITY))); + { + auto p = obs_properties_add_group(pr, ST_MIPMAPPING, D_TRANSLATE(ST_MIPMAPPING), OBS_GROUP_CHECKABLE, grp); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_MIPMAPPING))); + } + } return pr; } diff --git a/source/filters/filter-transform.hpp b/source/filters/filter-transform.hpp index 1042579e..38e01959 100644 --- a/source/filters/filter-transform.hpp +++ b/source/filters/filter-transform.hpp @@ -30,21 +30,24 @@ namespace filter { namespace transform { class transform_instance : public obs::source_instance { + // Cache + bool _cache_rendered; + std::shared_ptr _cache_rt; + std::shared_ptr _cache_texture; + + // Mip-mapping + bool _mipmap_enabled; + bool _mipmap_rendered; + double_t _mipmap_strength; + gs::mipmapper::generator _mipmap_generator; + gs::mipmapper _mipmapper; + std::shared_ptr _mipmap_texture; + // Input - std::shared_ptr _source_rendertarget; - std::shared_ptr _source_texture; bool _source_rendered; std::pair _source_size; - - // Mipmapping - bool _mipmap_enabled; - double_t _mipmap_strength; - gs::mipmapper::generator _mipmap_generator; - gs::mipmapper _mipmapper; - - // Rendering - std::shared_ptr _shape_rendertarget; - std::shared_ptr _shape_texture; + std::shared_ptr _source_rt; + std::shared_ptr _source_texture; // Mesh bool _update_mesh; @@ -63,7 +66,9 @@ namespace filter { transform_instance(obs_data_t*, obs_source_t*); virtual ~transform_instance() override; + virtual void load(obs_data_t* settings) override; virtual void update(obs_data_t*) override; + virtual void video_tick(float) override; virtual void video_render(gs_effect_t*) override; };