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.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."

View file

@ -18,7 +18,9 @@
*/
#include "filter-transform.hpp"
#include <algorithm>
#include <stdexcept>
#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::rendertarget>(GS_RGBA, GS_ZS_NONE);
_shape_rendertarget = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
_vertex_buffer = std::make_shared<gs::vertex_buffer>(uint32_t(4u), uint8_t(1u));
_cache_rt = 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));
_position = std::make_unique<util::vec3a>();
_rotation = std::make_unique<util::vec3a>();
@ -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<float_t>(obs_data_get_double(data, ST_POSITION_X) / 100.0);
_position->y = static_cast<float_t>(obs_data_get_double(data, ST_POSITION_Y) / 100.0);
_position->z = static_cast<float_t>(obs_data_get_double(data, ST_POSITION_Z) / 100.0);
_scale->x = static_cast<float_t>(obs_data_get_double(data, ST_SCALE_X) / 100.0);
_scale->y = static_cast<float_t>(obs_data_get_double(data, ST_SCALE_Y) / 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(settings, ST_POSITION_Y) / 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(settings, ST_SCALE_X) / 100.0);
_scale->y = static_cast<float_t>(obs_data_get_double(settings, ST_SCALE_Y) / 100.0);
_scale->z = 1.0f;
_rotation_order = static_cast<uint32_t>(obs_data_get_int(data, ST_ROTATION_ORDER));
_rotation->x = static_cast<float_t>(obs_data_get_double(data, 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->z = static_cast<float_t>(obs_data_get_double(data, ST_ROTATION_Z) / 180.0 * S_PI);
_shear->x = static_cast<float_t>(obs_data_get_double(data, ST_SHEAR_X) / 100.0);
_shear->y = static_cast<float_t>(obs_data_get_double(data, ST_SHEAR_Y) / 100.0);
_rotation_order = static_cast<uint32_t>(obs_data_get_int(settings, ST_ROTATION_ORDER));
_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(settings, ST_ROTATION_Y) / 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(settings, ST_SHEAR_X) / 100.0);
_shear->y = static_cast<float_t>(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<gs::mipmapper::generator>(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<gs::mipmapper::generator>(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<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);
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<gs::texture> 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<gs::texture>(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<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;
_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> 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<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)) {
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<const char*, const char*> 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<float_t>::lowest(),
std::numeric_limits<float_t>::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<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)),
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<const char*, const char*> 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<const char*, const char*> 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;
}

View file

@ -30,21 +30,24 @@
namespace filter {
namespace transform {
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
std::shared_ptr<gs::rendertarget> _source_rendertarget;
std::shared_ptr<gs::texture> _source_texture;
bool _source_rendered;
std::pair<uint32_t, uint32_t> _source_size;
// Mipmapping
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;
std::shared_ptr<gs::rendertarget> _source_rt;
std::shared_ptr<gs::texture> _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;
};