filter/transform: Modernize code base for future expansion

This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2021-10-22 03:31:35 +02:00
parent a40021b17b
commit e0c6e55259
2 changed files with 126 additions and 103 deletions

View File

@ -87,46 +87,50 @@ using namespace streamfx::filter::transform;
static constexpr std::string_view HELP_URL = "https://github.com/Xaymar/obs-StreamFX/wiki/Filter-3D-Transform";
static const float_t farZ = 2097152.0f; // 2 pow 21
static const float_t nearZ = 1.0f / farZ;
enum class CameraMode : int64_t { Orthographic, Perspective };
static const float farZ = 2097152.0f; // 2 pow 21
static const float nearZ = 1.0f / farZ;
enum RotationOrder : int64_t {
XYZ,
XZY,
YXZ,
YZX,
ZXY,
ZYX,
XYZ = 0,
XZY = 1,
YXZ = 2,
YZX = 3,
ZXY = 4,
ZYX = 5,
};
transform_instance::transform_instance(obs_data_t* data, obs_source_t* context)
: obs::source_instance(data, context), _cache_rendered(), _mipmap_enabled(), _source_rendered(), _source_size(),
_update_mesh(), _rotation_order(), _camera_orthographic(), _camera_fov()
: obs::source_instance(data, context), _camera_mode(), _camera_fov(), _standard_effect(), _sampler(), _params(),
_cache_rendered(), _mipmap_enabled(), _source_rendered(), _source_size(), _update_mesh(true)
{
_cache_rt = std::make_shared<streamfx::obs::gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
_source_rt = std::make_shared<streamfx::obs::gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
_vertex_buffer = std::make_shared<streamfx::obs::gs::vertex_buffer>(uint32_t(4u), uint8_t(1u));
{
auto file = streamfx::data_file_path("effects/standard.effect");
try {
_standard_effect = streamfx::obs::gs::effect::create(file.generic_u8string());
} catch (const std::exception& ex) {
DLOG_ERROR("Error loading '%s' from disk: %s", file.generic_u8string().c_str(), ex.what());
}
}
{
_sampler.set_address_mode_u(GS_ADDRESS_CLAMP);
_sampler.set_address_mode_v(GS_ADDRESS_CLAMP);
_sampler.set_address_mode_w(GS_ADDRESS_CLAMP);
_sampler.set_filter(GS_FILTER_LINEAR);
}
_position = std::make_unique<streamfx::util::vec3a>();
_rotation = std::make_unique<streamfx::util::vec3a>();
_scale = std::make_unique<streamfx::util::vec3a>();
_shear = std::make_unique<streamfx::util::vec3a>();
vec3_set(_position.get(), 0, 0, 0);
vec3_set(_rotation.get(), 0, 0, 0);
vec3_set(_scale.get(), 1, 1, 1);
vec3_set(&_params.position, 0, 0, 0);
vec3_set(&_params.rotation, 0, 0, 0);
vec3_set(&_params.scale, 1, 1, 1);
vec3_set(&_params.shear, 0, 0, 0);
update(data);
}
transform_instance::~transform_instance()
{
_shear.reset();
_scale.reset();
_rotation.reset();
_position.reset();
_vertex_buffer.reset();
_cache_rt.reset();
_cache_texture.reset();
@ -179,7 +183,7 @@ void transform_instance::migrate(obs_data_t* settings, uint64_t version)
COPY_UNSET(double, ST_KEY_MIPMAPPING, "Filter.Transform.Mipmapping");
if (!obs_data_has_user_value(settings, ST_KEY_CAMERA_MODE)) {
SET_IF_UNSET(int, ST_KEY_CAMERA_MODE, static_cast<int>(CameraMode::Orthographic));
SET_IF_UNSET(int, ST_KEY_CAMERA_MODE, static_cast<int>(transform_mode::ORTHOGRAPHIC));
}
}
@ -191,23 +195,24 @@ void transform_instance::migrate(obs_data_t* settings, uint64_t version)
void transform_instance::update(obs_data_t* settings)
{
// Camera
_camera_orthographic = obs_data_get_int(settings, ST_KEY_CAMERA_MODE) == 0;
_camera_fov = static_cast<float_t>(obs_data_get_double(settings, ST_KEY_CAMERA_FIELDOFVIEW));
_camera_mode = static_cast<transform_mode>(obs_data_get_int(settings, ST_KEY_CAMERA_MODE));
_camera_fov = static_cast<float>(obs_data_get_double(settings, ST_KEY_CAMERA_FIELDOFVIEW));
// Source
_position->x = static_cast<float_t>(obs_data_get_double(settings, ST_KEY_POSITION_X) / 100.0);
_position->y = static_cast<float_t>(obs_data_get_double(settings, ST_KEY_POSITION_Y) / 100.0);
_position->z = static_cast<float_t>(obs_data_get_double(settings, ST_KEY_POSITION_Z) / 100.0);
_scale->x = static_cast<float_t>(obs_data_get_double(settings, ST_KEY_SCALE_X) / 100.0);
_scale->y = static_cast<float_t>(obs_data_get_double(settings, ST_KEY_SCALE_Y) / 100.0);
_scale->z = 1.0f;
_rotation_order = static_cast<uint32_t>(obs_data_get_int(settings, ST_KEY_ROTATION_ORDER));
_rotation->x = static_cast<float_t>(obs_data_get_double(settings, ST_KEY_ROTATION_X) / 180.0 * S_PI);
_rotation->y = static_cast<float_t>(obs_data_get_double(settings, ST_KEY_ROTATION_Y) / 180.0 * S_PI);
_rotation->z = static_cast<float_t>(obs_data_get_double(settings, ST_KEY_ROTATION_Z) / 180.0 * S_PI);
_shear->x = static_cast<float_t>(obs_data_get_double(settings, ST_KEY_SHEAR_X) / 100.0);
_shear->y = static_cast<float_t>(obs_data_get_double(settings, ST_KEY_SHEAR_Y) / 100.0);
_shear->z = 0.0f;
{ // Parametrized Mesh
_params.position.x = static_cast<float>(obs_data_get_double(settings, ST_KEY_POSITION_X) / 100.0);
_params.position.y = static_cast<float>(obs_data_get_double(settings, ST_KEY_POSITION_Y) / 100.0);
_params.position.z = static_cast<float>(obs_data_get_double(settings, ST_KEY_POSITION_Z) / 100.0);
_params.scale.x = static_cast<float>(obs_data_get_double(settings, ST_KEY_SCALE_X) / 100.0);
_params.scale.y = static_cast<float>(obs_data_get_double(settings, ST_KEY_SCALE_Y) / 100.0);
_params.scale.z = 1.0f;
_params.rotation_order = static_cast<uint32_t>(obs_data_get_int(settings, ST_KEY_ROTATION_ORDER));
_params.rotation.x = static_cast<float>(obs_data_get_double(settings, ST_KEY_ROTATION_X) / 180.0 * S_PI);
_params.rotation.y = static_cast<float>(obs_data_get_double(settings, ST_KEY_ROTATION_Y) / 180.0 * S_PI);
_params.rotation.z = static_cast<float>(obs_data_get_double(settings, ST_KEY_ROTATION_Z) / 180.0 * S_PI);
_params.shear.x = static_cast<float>(obs_data_get_double(settings, ST_KEY_SHEAR_X) / 100.0);
_params.shear.y = static_cast<float>(obs_data_get_double(settings, ST_KEY_SHEAR_Y) / 100.0);
_params.shear.z = 0.0f;
}
// Mip-mapping
_mipmap_enabled = obs_data_get_bool(settings, ST_KEY_MIPMAPPING);
@ -215,7 +220,7 @@ void transform_instance::update(obs_data_t* settings)
_update_mesh = true;
}
void transform_instance::video_tick(float_t)
void transform_instance::video_tick(float)
{
uint32_t width = 0;
uint32_t height = 0;
@ -248,79 +253,80 @@ void transform_instance::video_tick(float_t)
}
// Calculate Aspect Ratio
float_t aspectRatioX = float_t(width) / float_t(height);
if (_camera_orthographic)
aspectRatioX = 1.0;
float aspect_ratio_x = float(width) / float(height);
if (_camera_mode == transform_mode::ORTHOGRAPHIC)
aspect_ratio_x = 1.0;
// Mesh
matrix4 ident;
matrix4_identity(&ident);
switch (_rotation_order) {
switch (_params.rotation_order) {
case RotationOrder::XYZ: // XYZ
matrix4_rotate_aa4f(&ident, &ident, 1, 0, 0, _rotation->x);
matrix4_rotate_aa4f(&ident, &ident, 0, 1, 0, _rotation->y);
matrix4_rotate_aa4f(&ident, &ident, 0, 0, 1, _rotation->z);
matrix4_rotate_aa4f(&ident, &ident, 1, 0, 0, _params.rotation.x);
matrix4_rotate_aa4f(&ident, &ident, 0, 1, 0, _params.rotation.y);
matrix4_rotate_aa4f(&ident, &ident, 0, 0, 1, _params.rotation.z);
break;
case RotationOrder::XZY: // XZY
matrix4_rotate_aa4f(&ident, &ident, 1, 0, 0, _rotation->x);
matrix4_rotate_aa4f(&ident, &ident, 0, 0, 1, _rotation->z);
matrix4_rotate_aa4f(&ident, &ident, 0, 1, 0, _rotation->y);
matrix4_rotate_aa4f(&ident, &ident, 1, 0, 0, _params.rotation.x);
matrix4_rotate_aa4f(&ident, &ident, 0, 0, 1, _params.rotation.z);
matrix4_rotate_aa4f(&ident, &ident, 0, 1, 0, _params.rotation.y);
break;
case RotationOrder::YXZ: // YXZ
matrix4_rotate_aa4f(&ident, &ident, 0, 1, 0, _rotation->y);
matrix4_rotate_aa4f(&ident, &ident, 1, 0, 0, _rotation->x);
matrix4_rotate_aa4f(&ident, &ident, 0, 0, 1, _rotation->z);
matrix4_rotate_aa4f(&ident, &ident, 0, 1, 0, _params.rotation.y);
matrix4_rotate_aa4f(&ident, &ident, 1, 0, 0, _params.rotation.x);
matrix4_rotate_aa4f(&ident, &ident, 0, 0, 1, _params.rotation.z);
break;
case RotationOrder::YZX: // YZX
matrix4_rotate_aa4f(&ident, &ident, 0, 1, 0, _rotation->y);
matrix4_rotate_aa4f(&ident, &ident, 0, 0, 1, _rotation->z);
matrix4_rotate_aa4f(&ident, &ident, 1, 0, 0, _rotation->x);
matrix4_rotate_aa4f(&ident, &ident, 0, 1, 0, _params.rotation.y);
matrix4_rotate_aa4f(&ident, &ident, 0, 0, 1, _params.rotation.z);
matrix4_rotate_aa4f(&ident, &ident, 1, 0, 0, _params.rotation.x);
break;
case RotationOrder::ZXY: // ZXY
matrix4_rotate_aa4f(&ident, &ident, 0, 0, 1, _rotation->z);
matrix4_rotate_aa4f(&ident, &ident, 1, 0, 0, _rotation->x);
matrix4_rotate_aa4f(&ident, &ident, 0, 1, 0, _rotation->y);
matrix4_rotate_aa4f(&ident, &ident, 0, 0, 1, _params.rotation.z);
matrix4_rotate_aa4f(&ident, &ident, 1, 0, 0, _params.rotation.x);
matrix4_rotate_aa4f(&ident, &ident, 0, 1, 0, _params.rotation.y);
break;
case RotationOrder::ZYX: // ZYX
matrix4_rotate_aa4f(&ident, &ident, 0, 0, 1, _rotation->z);
matrix4_rotate_aa4f(&ident, &ident, 0, 1, 0, _rotation->y);
matrix4_rotate_aa4f(&ident, &ident, 1, 0, 0, _rotation->x);
matrix4_rotate_aa4f(&ident, &ident, 0, 0, 1, _params.rotation.z);
matrix4_rotate_aa4f(&ident, &ident, 0, 1, 0, _params.rotation.y);
matrix4_rotate_aa4f(&ident, &ident, 1, 0, 0, _params.rotation.x);
break;
}
matrix4_translate3f(&ident, &ident, _position->x, _position->y, _position->z);
matrix4_translate3f(&ident, &ident, _params.position.x, _params.position.y, _params.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;
float_t p_y = 1.0f * _scale->y;
float p_x = aspect_ratio_x * _params.scale.x;
float p_y = 1.0f * _params.scale.y;
/// Generate mesh
{
auto vtx = _vertex_buffer->at(0);
*vtx.color = 0xFFFFFFFF;
vec4_set(vtx.uv[0], 0, 0, 0, 0);
vec3_set(vtx.position, -p_x + _shear->x, -p_y - _shear->y, 0);
vec3_set(vtx.position, -p_x + _params.shear.x, -p_y - _params.shear.y, 0);
vec3_transform(vtx.position, vtx.position, &ident);
}
{
auto vtx = _vertex_buffer->at(1);
*vtx.color = 0xFFFFFFFF;
vec4_set(vtx.uv[0], 1, 0, 0, 0);
vec3_set(vtx.position, p_x + _shear->x, -p_y + _shear->y, 0);
vec3_set(vtx.position, p_x + _params.shear.x, -p_y + _params.shear.y, 0);
vec3_transform(vtx.position, vtx.position, &ident);
}
{
auto vtx = _vertex_buffer->at(2);
*vtx.color = 0xFFFFFFFF;
vec4_set(vtx.uv[0], 0, 1, 0, 0);
vec3_set(vtx.position, -p_x - _shear->x, p_y - _shear->y, 0);
vec3_set(vtx.position, -p_x - _params.shear.x, p_y - _params.shear.y, 0);
vec3_transform(vtx.position, vtx.position, &ident);
}
{
auto vtx = _vertex_buffer->at(3);
*vtx.color = 0xFFFFFFFF;
vec4_set(vtx.uv[0], 1, 1, 0, 0);
vec3_set(vtx.position, p_x - _shear->x, p_y + _shear->y, 0);
vec3_set(vtx.position, p_x - _params.shear.x, p_y + _params.shear.y, 0);
vec3_transform(vtx.position, vtx.position, &ident);
}
@ -343,7 +349,7 @@ void transform_instance::video_render(gs_effect_t* effect)
if (!effect)
effect = default_effect;
if (!base_width || !base_height || !parent || !target) { // Skip if something is wrong.
if (!base_width || !base_height || !parent || !target || !_standard_effect) { // Skip if something is wrong.
obs_source_skip_video_filter(_self);
return;
}
@ -382,7 +388,7 @@ void transform_instance::video_render(gs_effect_t* effect)
auto op = _cache_rt->render(cache_width, cache_height);
gs_ortho(0, static_cast<float_t>(base_width), 0, static_cast<float_t>(base_height), -1, 1);
gs_ortho(0, static_cast<float>(base_width), 0, static_cast<float>(base_height), -1, 1);
vec4 clear_color = {0, 0, 0, 0};
gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &clear_color, 0, 0);
@ -448,6 +454,9 @@ void transform_instance::video_render(gs_effect_t* effect)
auto op = _source_rt->render(base_width, base_height);
vec4 clear_color = {0, 0, 0, 0};
gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &clear_color, 0, 0);
gs_blend_state_push();
gs_reset_blend_state();
gs_enable_blending(false);
@ -459,25 +468,28 @@ void transform_instance::video_render(gs_effect_t* effect)
gs_enable_color(true, true, true, true);
gs_set_cull_mode(GS_NEITHER);
if (_camera_orthographic) {
switch (_camera_mode) {
case transform_mode::ORTHOGRAPHIC:
gs_ortho(-1., 1., -1., 1., -farZ, farZ);
} else {
gs_perspective(_camera_fov, float_t(base_width) / float_t(base_height), nearZ, farZ);
break;
case transform_mode::PERSPECTIVE:
gs_perspective(_camera_fov, float(base_width) / float(base_height), nearZ, farZ);
gs_matrix_scale3f(1.0, 1.0, 1.0);
gs_matrix_translate3f(0., 0., -1.0);
break;
}
vec4 clear_color = {0, 0, 0, 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 ? _mipmap_texture->get_object() : _cache_texture->get_object())
: _cache_texture->get_object());
while (gs_effect_loop(default_effect, "Draw")) {
gs_draw(GS_TRISTRIP, 0, 4);
if (auto v = _standard_effect.get_parameter("InputA");
v.get_type() == ::streamfx::obs::gs::effect_parameter::type::Texture) {
v.set_texture(_mipmap_enabled
? (_mipmap_texture ? _mipmap_texture->get_object() : _cache_texture->get_object())
: _cache_texture->get_object());
v.set_sampler(_sampler.get_object());
}
while (gs_effect_loop(_standard_effect.get_object(), "Draw")) {
gs_draw(GS_TRISTRIP, 0, _vertex_buffer->size());
}
gs_load_vertexbuffer(nullptr);
@ -521,7 +533,7 @@ const char* transform_factory::get_name()
void transform_factory::get_defaults2(obs_data_t* settings)
{
obs_data_set_default_int(settings, ST_KEY_CAMERA_MODE, static_cast<int64_t>(CameraMode::Orthographic));
obs_data_set_default_int(settings, ST_KEY_CAMERA_MODE, static_cast<int64_t>(transform_mode::ORTHOGRAPHIC));
obs_data_set_default_double(settings, ST_KEY_CAMERA_FIELDOFVIEW, 90.0);
obs_data_set_default_double(settings, ST_KEY_POSITION_X, 0);
obs_data_set_default_double(settings, ST_KEY_POSITION_Y, 0);
@ -539,10 +551,10 @@ void transform_factory::get_defaults2(obs_data_t* settings)
static bool modified_camera_mode(obs_properties_t* pr, obs_property_t*, obs_data_t* d) noexcept
try {
auto mode = static_cast<CameraMode>(obs_data_get_int(d, ST_KEY_CAMERA_MODE));
auto mode = static_cast<transform_mode>(obs_data_get_int(d, ST_KEY_CAMERA_MODE));
bool is_camera = true;
bool is_perspective = (mode == CameraMode::Perspective) && is_camera;
bool is_orthographic = (mode == CameraMode::Orthographic) && is_camera;
bool is_perspective = (mode == transform_mode::PERSPECTIVE) && is_camera;
bool is_orthographic = (mode == transform_mode::ORTHOGRAPHIC) && is_camera;
obs_property_set_visible(obs_properties_get(pr, ST_KEY_CAMERA_FIELDOFVIEW), is_perspective);
obs_property_set_visible(obs_properties_get(pr, ST_I18N_POSITION), is_camera);
@ -580,9 +592,9 @@ obs_properties_t* transform_factory::get_properties2(transform_instance* data)
auto p = obs_properties_add_list(grp, ST_KEY_CAMERA_MODE, D_TRANSLATE(ST_I18N_CAMERA_MODE),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_CAMERA_MODE_ORTHOGRAPHIC),
static_cast<int64_t>(CameraMode::Orthographic));
static_cast<int64_t>(transform_mode::ORTHOGRAPHIC));
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_CAMERA_MODE_PERSPECTIVE),
static_cast<int64_t>(CameraMode::Perspective));
static_cast<int64_t>(transform_mode::PERSPECTIVE));
obs_property_set_modified_callback(p, modified_camera_mode);
}
{ // Field Of View
@ -606,8 +618,8 @@ obs_properties_t* transform_factory::get_properties2(transform_instance* data)
};
for (auto opt : opts) {
auto p = obs_properties_add_float(grp, opt.first.c_str(), D_TRANSLATE(opt.second.c_str()),
std::numeric_limits<float_t>::lowest(),
std::numeric_limits<float_t>::max(), 0.01);
std::numeric_limits<float>::lowest(),
std::numeric_limits<float>::max(), 0.01);
}
obs_properties_add_group(pr, ST_I18N_POSITION, D_TRANSLATE(ST_I18N_POSITION), OBS_GROUP_NORMAL, grp);

View File

@ -27,7 +27,27 @@
#include "obs/obs-source-factory.hpp"
namespace streamfx::filter::transform {
enum class transform_mode {
ORTHOGRAPHIC = 0,
PERSPECTIVE = 1,
};
class transform_instance : public obs::source_instance {
// Settings
transform_mode _camera_mode;
float _camera_fov;
struct {
vec3 position;
vec3 rotation;
uint32_t rotation_order;
vec3 scale;
vec3 shear;
} _params;
// Data
streamfx::obs::gs::effect _standard_effect;
streamfx::obs::gs::sampler _sampler;
// Cache
bool _cache_rendered;
std::shared_ptr<streamfx::obs::gs::rendertarget> _cache_rt;
@ -48,15 +68,6 @@ namespace streamfx::filter::transform {
// Mesh
bool _update_mesh;
std::shared_ptr<streamfx::obs::gs::vertex_buffer> _vertex_buffer;
uint32_t _rotation_order;
std::unique_ptr<streamfx::util::vec3a> _position;
std::unique_ptr<streamfx::util::vec3a> _rotation;
std::unique_ptr<streamfx::util::vec3a> _scale;
std::unique_ptr<streamfx::util::vec3a> _shear;
// Camera
bool _camera_orthographic;
float_t _camera_fov;
public:
transform_instance(obs_data_t*, obs_source_t*);
@ -66,7 +77,7 @@ namespace streamfx::filter::transform {
virtual void migrate(obs_data_t* data, uint64_t version) override;
virtual void update(obs_data_t*) override;
virtual void video_tick(float_t) override;
virtual void video_tick(float) override;
virtual void video_render(gs_effect_t*) override;
};