filter-transform: Further refactoring

This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2019-12-03 18:28:42 +01:00
parent 48e17ad562
commit efb6b0b9be
3 changed files with 309 additions and 288 deletions

View file

@ -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.Camera.FieldOfView.Description="Vertical Field of View of the camera."
Filter.Transform.Position="Position" Filter.Transform.Position="Position"
Filter.Transform.Position.Description="Position of the rendered quad." Filter.Transform.Position.Description="Position of the rendered quad."
Filter.Transform.Position.X="Position (X)" Filter.Transform.Position.X="X"
Filter.Transform.Position.Y="Position (Y)" Filter.Transform.Position.Y="Y"
Filter.Transform.Position.Z="Position (Z)" Filter.Transform.Position.Z="Z"
Filter.Transform.Scale="Scale" Filter.Transform.Scale="Scale"
Filter.Transform.Scale.Description="Scale of the rendered quad." Filter.Transform.Scale.Description="Scale of the rendered quad."
Filter.Transform.Scale.X="Scale (X)" Filter.Transform.Scale.X="X"
Filter.Transform.Scale.Y="Scale (Y)" Filter.Transform.Scale.Y="Y"
Filter.Transform.Shear="Shear" Filter.Transform.Shear="Shear"
Filter.Transform.Shear.Description="Shearing of the rendered quad." Filter.Transform.Shear.Description="Shearing of the rendered quad."
Filter.Transform.Shear.X="Shear (X)" Filter.Transform.Shear.X="X"
Filter.Transform.Shear.Y="Shear (Y)" 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="Rotation Order"
Filter.Transform.Rotation.Order.Description="The order in which to apply the euler angles to the rendered quad." 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" 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.YZX="Yaw, Roll, Pitch"
Filter.Transform.Rotation.Order.ZXY="Roll, Pitch, Yaw" Filter.Transform.Rotation.Order.ZXY="Roll, Pitch, Yaw"
Filter.Transform.Rotation.Order.ZYX="Roll, Yaw, Pitch" 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="Enable Mipmapping"
Filter.Transform.Mipmapping.Description="Generate mipmaps for the source, so that angled and far away parts are smoother." Filter.Transform.Mipmapping.Description="Generate mipmaps for the source, so that angled and far away parts are smoother."

View file

@ -18,7 +18,9 @@
*/ */
#include "filter-transform.hpp" #include "filter-transform.hpp"
#include <algorithm>
#include <stdexcept> #include <stdexcept>
#include "obs/gs/gs-helper.hpp"
#include "strings.hpp" #include "strings.hpp"
#include "util-math.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) 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), : obs::source_instance(data, context), _cache_rendered(), _mipmap_enabled(), _mipmap_strength(),
_mipmap_generator(gs::mipmapper::generator::Linear), _update_mesh(false), _rotation_order(RotationOrder::ZXY), _mipmap_generator(), _source_rendered(), _source_size(), _update_mesh(), _rotation_order(),
_camera_orthographic(true), _camera_fov(90.0) _camera_orthographic(), _camera_fov()
{ {
_source_rendertarget = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE); _cache_rt = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
_shape_rendertarget = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE); _source_rt = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
_vertex_buffer = std::make_shared<gs::vertex_buffer>(uint32_t(4u), uint8_t(1u)); _vertex_buffer = std::make_shared<gs::vertex_buffer>(uint32_t(4u), uint8_t(1u));
_position = std::make_unique<util::vec3a>(); _position = std::make_unique<util::vec3a>();
_rotation = std::make_unique<util::vec3a>(); _rotation = std::make_unique<util::vec3a>();
@ -104,37 +106,41 @@ filter::transform::transform_instance::~transform_instance()
_rotation.reset(); _rotation.reset();
_position.reset(); _position.reset();
_vertex_buffer.reset(); _vertex_buffer.reset();
_shape_texture.reset(); _cache_rt.reset();
_shape_rendertarget.reset(); _cache_texture.reset();
_source_texture.reset(); _mipmap_texture.reset();
_source_rendertarget.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
_camera_orthographic = obs_data_get_int(data, ST_CAMERA) == 0; _camera_orthographic = obs_data_get_int(settings, ST_CAMERA) == 0;
_camera_fov = (float)obs_data_get_double(data, ST_CAMERA_FIELDOFVIEW); _camera_fov = (float)obs_data_get_double(settings, ST_CAMERA_FIELDOFVIEW);
// Source // Source
_position->x = static_cast<float_t>(obs_data_get_double(data, ST_POSITION_X) / 100.0); _position->x = static_cast<float_t>(obs_data_get_double(settings, ST_POSITION_X) / 100.0);
_position->y = static_cast<float_t>(obs_data_get_double(data, ST_POSITION_Y) / 100.0); _position->y = static_cast<float_t>(obs_data_get_double(settings, ST_POSITION_Y) / 100.0);
_position->z = static_cast<float_t>(obs_data_get_double(data, ST_POSITION_Z) / 100.0); _position->z = static_cast<float_t>(obs_data_get_double(settings, ST_POSITION_Z) / 100.0);
_scale->x = static_cast<float_t>(obs_data_get_double(data, ST_SCALE_X) / 100.0); _scale->x = static_cast<float_t>(obs_data_get_double(settings, ST_SCALE_X) / 100.0);
_scale->y = static_cast<float_t>(obs_data_get_double(data, ST_SCALE_Y) / 100.0); _scale->y = static_cast<float_t>(obs_data_get_double(settings, ST_SCALE_Y) / 100.0);
_scale->z = 1.0f; _scale->z = 1.0f;
_rotation_order = static_cast<uint32_t>(obs_data_get_int(data, ST_ROTATION_ORDER)); _rotation_order = static_cast<uint32_t>(obs_data_get_int(settings, ST_ROTATION_ORDER));
_rotation->x = static_cast<float_t>(obs_data_get_double(data, ST_ROTATION_X) / 180.0 * S_PI); _rotation->x = static_cast<float_t>(obs_data_get_double(settings, ST_ROTATION_X) / 180.0 * S_PI);
_rotation->y = static_cast<float_t>(obs_data_get_double(data, ST_ROTATION_Y) / 180.0 * S_PI); _rotation->y = static_cast<float_t>(obs_data_get_double(settings, ST_ROTATION_Y) / 180.0 * S_PI);
_rotation->z = static_cast<float_t>(obs_data_get_double(data, ST_ROTATION_Z) / 180.0 * S_PI); _rotation->z = static_cast<float_t>(obs_data_get_double(settings, ST_ROTATION_Z) / 180.0 * S_PI);
_shear->x = static_cast<float_t>(obs_data_get_double(data, ST_SHEAR_X) / 100.0); _shear->x = static_cast<float_t>(obs_data_get_double(settings, ST_SHEAR_X) / 100.0);
_shear->y = static_cast<float_t>(obs_data_get_double(data, ST_SHEAR_Y) / 100.0); _shear->y = static_cast<float_t>(obs_data_get_double(settings, ST_SHEAR_Y) / 100.0);
_shear->z = 0.0f; _shear->z = 0.0f;
// Mipmapping // Mipmapping
_mipmap_enabled = obs_data_get_bool(data, ST_MIPMAPPING); _mipmap_enabled = obs_data_get_bool(settings, ST_MIPMAPPING);
_mipmap_strength = obs_data_get_double(data, S_MIPGENERATOR_INTENSITY); _mipmap_strength = obs_data_get_double(settings, S_MIPGENERATOR_INTENSITY);
_mipmap_generator = static_cast<gs::mipmapper::generator>(obs_data_get_int(data, S_MIPGENERATOR)); _mipmap_generator = static_cast<gs::mipmapper::generator>(obs_data_get_int(settings, S_MIPGENERATOR));
_update_mesh = true; _update_mesh = true;
} }
@ -212,6 +218,7 @@ void filter::transform::transform_instance::video_tick(float)
break; break;
} }
matrix4_translate3f(&ident, &ident, _position->x, _position->y, _position->z); 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. /// Calculate vertex position once only.
float_t p_x = aspectRatioX * _scale->x; float_t p_x = aspectRatioX * _scale->x;
@ -251,163 +258,143 @@ void filter::transform::transform_instance::video_tick(float)
_update_mesh = false; _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* parent = obs_filter_get_parent(_self); obs_source_t* target = obs_filter_get_target(_self);
obs_source_t* target = obs_filter_get_target(_self); uint32_t base_width = obs_source_get_base_width(target);
if (!parent || !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); obs_source_skip_video_filter(_self);
return; return;
} }
// Grab width an height of the target source (child filter or source). gs::debug_marker marker{gs::debug_color_source, "3D Transform: %s", obs_source_get_name(_self)};
uint32_t width = obs_source_get_base_width(target);
uint32_t height = obs_source_get_base_height(target); uint32_t cache_width = base_width;
if ((width == 0) || (height == 0)) { 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<float_t>(base_width), 0, static_cast<float_t>(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); obs_source_skip_video_filter(_self);
return; 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 (!_mipmap_texture || (_mipmap_texture->get_width() != cache_width)
if (!this->_source_rendered) { || (_mipmap_texture->get_height() != cache_height)) {
std::shared_ptr<gs::texture> source_tex; size_t mip_levels = std::max(util::math::get_power_of_two_exponent_ceil(cache_width),
uint32_t real_width = width; util::math::get_power_of_two_exponent_ceil(cache_height));
uint32_t real_height = height;
// If MipMapping is enabled, resize Render Target to be a Power of Two. _mipmap_texture = std::make_shared<gs::texture>(cache_width, cache_height, GS_RGBA, mip_levels, nullptr,
if (_mipmap_enabled) { gs::texture::flags::None);
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);
}
}
} }
_mipmapper.rebuild(_cache_texture, _mipmap_texture, _mipmap_generator, float_t(_mipmap_strength));
// Draw previous filters to texture. _mipmap_rendered = true;
try { } else {
GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_ITEM_TEXTURE, "Filter Cache"); _mipmap_texture = _cache_texture;
auto op = _source_rendertarget->render(real_width, real_height); }
if (!_mipmap_texture) {
gs_set_cull_mode(GS_NEITHER); obs_source_skip_video_filter(_self);
gs_reset_blend_state(); return;
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<float_t>(width), 0, static_cast<float_t>(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<gs::texture>(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;
} }
// Draw final shape {
GS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_SOURCE, "3D Transform: %s", obs_source_get_name(_self)); gs::debug_marker _marker_draw{gs::debug_color_cache_render, "Geometry"};
gs_reset_blend_state(); auto op = _source_rt->render(base_width, base_height);
gs_enable_depth_test(false);
while (gs_effect_loop(default_effect, "Draw")) { gs_matrix_push();
gs_effect_set_texture(gs_effect_get_param_by_name(default_effect, "image"), _shape_texture->get_object()); if (_camera_orthographic) {
gs_draw_sprite(_shape_texture->get_object(), 0, 0, 0); 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> filter::transform::transform_factory::factory_instance = nullptr; std::shared_ptr<filter::transform::transform_factory> 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.id = "obs-stream-effects-filter-transform";
_info.type = OBS_SOURCE_TYPE_FILTER; _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; _info.get_width = _info.get_height = nullptr;
@ -430,25 +417,29 @@ const char* filter::transform::transform_factory::get_name()
return D_TRANSLATE(ST); 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_int(settings, ST_CAMERA, (int64_t)CameraMode::Orthographic);
obs_data_set_default_double(data, ST_CAMERA_FIELDOFVIEW, 90.0); obs_data_set_default_double(settings, ST_CAMERA_FIELDOFVIEW, 90.0);
obs_data_set_default_double(data, ST_POSITION_X, 0); obs_data_set_default_double(settings, ST_POSITION_X, 0);
obs_data_set_default_double(data, ST_POSITION_Y, 0); obs_data_set_default_double(settings, ST_POSITION_Y, 0);
obs_data_set_default_double(data, ST_POSITION_Z, 0); obs_data_set_default_double(settings, ST_POSITION_Z, 0);
obs_data_set_default_double(data, ST_ROTATION_X, 0); obs_data_set_default_double(settings, ST_ROTATION_X, 0);
obs_data_set_default_double(data, ST_ROTATION_Y, 0); obs_data_set_default_double(settings, ST_ROTATION_Y, 0);
obs_data_set_default_double(data, ST_ROTATION_Z, 0); obs_data_set_default_double(settings, ST_ROTATION_Z, 0);
obs_data_set_default_double(data, ST_SCALE_X, 100); obs_data_set_default_int(settings, ST_ROTATION_ORDER, RotationOrder::ZXY);
obs_data_set_default_double(data, ST_SCALE_Y, 100); obs_data_set_default_double(settings, ST_SCALE_X, 100);
obs_data_set_default_double(data, ST_SHEAR_X, 0); obs_data_set_default_double(settings, ST_SCALE_Y, 100);
obs_data_set_default_double(data, ST_SHEAR_Y, 0); obs_data_set_default_double(settings, ST_SHEAR_X, 0);
obs_data_set_default_bool(data, S_ADVANCED, false); obs_data_set_default_double(settings, ST_SHEAR_Y, 0);
obs_data_set_default_int(data, ST_ROTATION_ORDER, RotationOrder::ZXY); 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<int64_t>(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)) { switch ((CameraMode)obs_data_get_int(d, ST_CAMERA)) {
case CameraMode::Orthographic: case CameraMode::Orthographic:
obs_property_set_visible(obs_properties_get(pr, ST_CAMERA_FIELDOFVIEW), false); 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_ROTATION_ORDER), advancedVisible);
obs_property_set_visible(obs_properties_get(pr, ST_MIPMAPPING), 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; return true;
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
P_LOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 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* filter::transform::transform_factory::get_properties2(filter::transform::transform_instance* data)
{ {
obs_properties_t* pr = obs_properties_create(); obs_properties_t* pr = obs_properties_create();
obs_property_t* p = NULL;
// Camera // Camera
/// Projection Mode {
p = obs_properties_add_list(pr, ST_CAMERA, D_TRANSLATE(ST_CAMERA), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); auto grp = obs_properties_create();
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); { // Projection Mode
obs_property_list_add_int(p, D_TRANSLATE(ST_CAMERA_PERSPECTIVE), (int64_t)CameraMode::Perspective); auto p = obs_properties_add_list(grp, ST_CAMERA, D_TRANSLATE(ST_CAMERA), OBS_COMBO_TYPE_LIST,
obs_property_set_modified_callback(p, modified_properties); OBS_COMBO_FORMAT_INT);
/// Field Of View obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_CAMERA)));
p = obs_properties_add_float_slider(pr, ST_CAMERA_FIELDOFVIEW, D_TRANSLATE(ST_CAMERA_FIELDOFVIEW), 1.0, 179.0, obs_property_list_add_int(p, D_TRANSLATE(ST_CAMERA_ORTHOGRAPHIC), (int64_t)CameraMode::Orthographic);
0.01); obs_property_list_add_int(p, D_TRANSLATE(ST_CAMERA_PERSPECTIVE), (int64_t)CameraMode::Perspective);
obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_CAMERA_FIELDOFVIEW))); 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 // Mesh
/// Position { // Position
{ auto grp = obs_properties_create();
std::pair<const char*, const char*> entries[] = {
std::make_pair(ST_POSITION_X, D_DESC(ST_POSITION)), const char* opts[] = {ST_POSITION_X, ST_POSITION_Y, ST_POSITION_Z};
std::make_pair(ST_POSITION_Y, D_DESC(ST_POSITION)), for (auto opt : opts) {
std::make_pair(ST_POSITION_Z, D_DESC(ST_POSITION)), auto p = obs_properties_add_float(grp, opt, D_TRANSLATE(opt), std::numeric_limits<float_t>::lowest(),
}; std::numeric_limits<float_t>::max(), 0.01);
for (auto kv : entries) { obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_POSITION)));
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));
} }
obs_properties_add_group(pr, ST_POSITION, D_TRANSLATE(ST_POSITION), OBS_GROUP_NORMAL, grp);
} }
/// Rotation { // Rotation
{ auto grp = obs_properties_create();
std::pair<const char*, const char*> entries[] = {
std::make_pair(ST_ROTATION_X, D_DESC(ST_ROTATION)), {
std::make_pair(ST_ROTATION_Y, D_DESC(ST_ROTATION)), const char* opts[] = {ST_ROTATION_X, ST_ROTATION_Y, ST_ROTATION_Z};
std::make_pair(ST_ROTATION_Z, D_DESC(ST_ROTATION)), for (auto opt : opts) {
}; auto p = obs_properties_add_float_slider(grp, opt, D_TRANSLATE(opt), -180.0, 180.0, 0.01);
for (auto kv : entries) { obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_ROTATION)));
p = obs_properties_add_float_slider(pr, kv.first, D_TRANSLATE(kv.first), -180, 180, 0.01); obs_property_float_set_suffix(p, "° Deg");
obs_property_set_long_description(p, D_TRANSLATE(kv.second)); }
} }
{ // 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 { // Scale
{ auto grp = obs_properties_create();
std::pair<const char*, const char*> entries[] = {
std::make_pair(ST_SCALE_X, D_DESC(ST_SCALE)), const char* opts[] = {ST_SCALE_X, ST_SCALE_Y};
std::make_pair(ST_SCALE_Y, D_DESC(ST_SCALE)), for (auto opt : opts) {
}; auto p = obs_properties_add_float_slider(grp, opt, D_TRANSLATE(opt), -1000, 1000, 0.01);
for (auto kv : entries) { obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SCALE)));
p = obs_properties_add_float_slider(pr, kv.first, D_TRANSLATE(kv.first), -1000, 1000, 0.01); obs_property_float_set_suffix(p, "%");
obs_property_set_long_description(p, D_TRANSLATE(kv.second));
} }
obs_properties_add_group(pr, ST_SCALE, D_TRANSLATE(ST_SCALE), OBS_GROUP_NORMAL, grp);
} }
/// Shear { // Shear
{ auto grp = obs_properties_create();
std::pair<const char*, const char*> entries[] = {
std::make_pair(ST_SHEAR_X, D_DESC(ST_SHEAR)), const char* opts[] = {ST_SHEAR_X, ST_SHEAR_Y};
std::make_pair(ST_SHEAR_Y, D_DESC(ST_SHEAR)), for (auto opt : opts) {
}; auto p = obs_properties_add_float_slider(grp, opt, D_TRANSLATE(opt), -200.0, 200.0, 0.01);
for (auto kv : entries) { obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHEAR)));
p = obs_properties_add_float_slider(pr, kv.first, D_TRANSLATE(kv.first), -100.0, 100.0, 0.01); obs_property_float_set_suffix(p, "%");
obs_property_set_long_description(p, D_TRANSLATE(kv.second));
} }
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); auto grp = obs_properties_create();
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);
p = obs_properties_add_bool(pr, ST_MIPMAPPING, D_TRANSLATE(ST_MIPMAPPING)); {
obs_property_set_modified_callback(p, modified_properties); auto p = obs_properties_add_list(grp, S_MIPGENERATOR, D_TRANSLATE(S_MIPGENERATOR), OBS_COMBO_TYPE_LIST,
obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_MIPMAPPING))); 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); 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(S_MIPGENERATOR))); obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_MIPMAPPING)));
}
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)));
return pr; return pr;
} }

View file

@ -30,21 +30,24 @@
namespace filter { namespace filter {
namespace transform { namespace transform {
class transform_instance : public obs::source_instance { class transform_instance : public obs::source_instance {
// Cache
bool _cache_rendered;
std::shared_ptr<gs::rendertarget> _cache_rt;
std::shared_ptr<gs::texture> _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<gs::texture> _mipmap_texture;
// Input // Input
std::shared_ptr<gs::rendertarget> _source_rendertarget;
std::shared_ptr<gs::texture> _source_texture;
bool _source_rendered; bool _source_rendered;
std::pair<uint32_t, uint32_t> _source_size; std::pair<uint32_t, uint32_t> _source_size;
std::shared_ptr<gs::rendertarget> _source_rt;
// Mipmapping std::shared_ptr<gs::texture> _source_texture;
bool _mipmap_enabled;
double_t _mipmap_strength;
gs::mipmapper::generator _mipmap_generator;
gs::mipmapper _mipmapper;
// Rendering
std::shared_ptr<gs::rendertarget> _shape_rendertarget;
std::shared_ptr<gs::texture> _shape_texture;
// Mesh // Mesh
bool _update_mesh; bool _update_mesh;
@ -63,7 +66,9 @@ namespace filter {
transform_instance(obs_data_t*, obs_source_t*); transform_instance(obs_data_t*, obs_source_t*);
virtual ~transform_instance() override; virtual ~transform_instance() override;
virtual void load(obs_data_t* settings) override;
virtual void update(obs_data_t*) override; virtual void update(obs_data_t*) override;
virtual void video_tick(float) override; virtual void video_tick(float) override;
virtual void video_render(gs_effect_t*) override; virtual void video_render(gs_effect_t*) override;
}; };