2023-02-28 01:15:26 +00:00
// AUTOGENERATED COPYRIGHT HEADER START
2023-09-30 03:16:02 +00:00
// Copyright (C) 2017-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
2023-02-28 01:15:26 +00:00
// Copyright (C) 2022 lainon <GermanAizek@yandex.ru>
// AUTOGENERATED COPYRIGHT HEADER END
2017-06-29 01:41:32 +00:00
2019-01-14 10:23:21 +00:00
# include "filter-transform.hpp"
2020-04-05 04:13:14 +00:00
# include "strings.hpp"
2019-12-03 17:28:42 +00:00
# include "obs/gs/gs-helper.hpp"
2021-09-07 02:22:46 +00:00
# include "util/util-logging.hpp"
2022-08-29 10:29:44 +00:00
# include "warning-disable.hpp"
# include <algorithm>
# include <stdexcept>
# include "warning-enable.hpp"
// OBS
# include "warning-disable.hpp"
# include <graphics/graphics.h>
# include <graphics/matrix4.h>
# include <util/platform.h>
# include "warning-enable.hpp"
2021-09-07 02:22:46 +00:00
# ifdef _DEBUG
# define ST_PREFIX "<%s> "
# define D_LOG_ERROR(x, ...) P_LOG_ERROR(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__)
# define D_LOG_WARNING(x, ...) P_LOG_WARN(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__)
# define D_LOG_INFO(x, ...) P_LOG_INFO(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__)
# define D_LOG_DEBUG(x, ...) P_LOG_DEBUG(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__)
# else
# define ST_PREFIX "<filter::transform> "
# define D_LOG_ERROR(...) P_LOG_ERROR(ST_PREFIX __VA_ARGS__)
# define D_LOG_WARNING(...) P_LOG_WARN(ST_PREFIX __VA_ARGS__)
# define D_LOG_INFO(...) P_LOG_INFO(ST_PREFIX __VA_ARGS__)
# define D_LOG_DEBUG(...) P_LOG_DEBUG(ST_PREFIX __VA_ARGS__)
# endif
2017-06-29 01:41:32 +00:00
2021-06-08 03:07:02 +00:00
# define ST_I18N "Filter.Transform"
2021-10-21 21:04:37 +00:00
# define ST_I18N_CAMERA ST_I18N ".Camera"
# define ST_I18N_CAMERA_MODE ST_I18N_CAMERA ".Mode"
# define ST_KEY_CAMERA_MODE "Camera.Mode"
2021-10-22 01:02:12 +00:00
# define ST_I18N_CAMERA_MODE_CORNER_PIN ST_I18N_CAMERA_MODE ".CornerPin"
2021-10-21 21:04:37 +00:00
# define ST_I18N_CAMERA_MODE_ORTHOGRAPHIC ST_I18N_CAMERA_MODE ".Orthographic"
# define ST_I18N_CAMERA_MODE_PERSPECTIVE ST_I18N_CAMERA_MODE ".Perspective"
# define ST_I18N_CAMERA_FIELDOFVIEW ST_I18N_CAMERA ".FieldOfView"
# define ST_KEY_CAMERA_FIELDOFVIEW "Camera.FieldOfView"
# define ST_I18N_POSITION ST_I18N ".Position"
# define ST_KEY_POSITION_X "Position.X"
# define ST_KEY_POSITION_Y "Position.Y"
# define ST_KEY_POSITION_Z "Position.Z"
# define ST_I18N_ROTATION ST_I18N ".Rotation"
# define ST_KEY_ROTATION_X "Rotation.X"
# define ST_KEY_ROTATION_Y "Rotation.Y"
# define ST_KEY_ROTATION_Z "Rotation.Z"
# define ST_I18N_SCALE ST_I18N ".Scale"
# define ST_KEY_SCALE_X "Scale.X"
# define ST_KEY_SCALE_Y "Scale.Y"
# define ST_I18N_SHEAR ST_I18N ".Shear"
# define ST_KEY_SHEAR_X "Shear.X"
# define ST_KEY_SHEAR_Y "Shear.Y"
# define ST_I18N_ROTATION_ORDER ST_I18N ".Rotation.Order"
# define ST_KEY_ROTATION_ORDER "Rotation.Order"
# define ST_I18N_ROTATION_ORDER_XYZ ST_I18N_ROTATION_ORDER ".XYZ"
# define ST_I18N_ROTATION_ORDER_XZY ST_I18N_ROTATION_ORDER ".XZY"
# define ST_I18N_ROTATION_ORDER_YXZ ST_I18N_ROTATION_ORDER ".YXZ"
# define ST_I18N_ROTATION_ORDER_YZX ST_I18N_ROTATION_ORDER ".YZX"
# define ST_I18N_ROTATION_ORDER_ZXY ST_I18N_ROTATION_ORDER ".ZXY"
# define ST_I18N_ROTATION_ORDER_ZYX ST_I18N_ROTATION_ORDER ".ZYX"
2021-10-22 01:02:12 +00:00
# define ST_I18N_CORNERS ST_I18N ".Corners"
# define ST_I18N_CORNERS_TOPLEFT ST_I18N_CORNERS ".TopLeft"
# define ST_KEY_CORNERS_TOPLEFT "Corners.TopLeft."
# define ST_I18N_CORNERS_TOPRIGHT ST_I18N_CORNERS ".TopRight"
# define ST_KEY_CORNERS_TOPRIGHT "Corners.TopRight."
# define ST_I18N_CORNERS_BOTTOMLEFT ST_I18N_CORNERS ".BottomLeft"
# define ST_KEY_CORNERS_BOTTOMLEFT "Corners.BottomLeft."
# define ST_I18N_CORNERS_BOTTOMRIGHT ST_I18N_CORNERS ".BottomRight"
# define ST_KEY_CORNERS_BOTTOMRIGHT "Corners.BottomRight."
2021-10-21 21:04:37 +00:00
# define ST_I18N_MIPMAPPING ST_I18N ".Mipmapping"
# define ST_KEY_MIPMAPPING "Mipmapping"
2018-09-28 12:49:59 +00:00
2020-04-05 16:52:06 +00:00
using namespace streamfx : : filter : : transform ;
2020-01-13 21:40:15 +00:00
2021-04-16 23:43:30 +00:00
static constexpr std : : string_view HELP_URL = " https://github.com/Xaymar/obs-StreamFX/wiki/Filter-3D-Transform " ;
2021-10-22 01:31:35 +00:00
static const float farZ = 2097152.0f ; // 2 pow 21
static const float nearZ = 1.0f / farZ ;
2017-09-17 21:56:40 +00:00
2017-07-01 01:49:08 +00:00
enum RotationOrder : int64_t {
2021-10-22 01:31:35 +00:00
XYZ = 0 ,
XZY = 1 ,
YXZ = 2 ,
YZX = 3 ,
ZXY = 4 ,
ZYX = 5 ,
2017-07-01 01:49:08 +00:00
} ;
2023-05-13 12:35:46 +00:00
transform_instance : : transform_instance ( obs_data_t * data , obs_source_t * context ) : obs : : source_instance ( data , context ) , _gfx_util ( : : streamfx : : gfx : : util : : get ( ) ) , _camera_mode ( ) , _camera_fov ( ) , _params ( ) , _corners ( ) , _standard_effect ( ) , _transform_effect ( ) , _sampler ( ) , _cache_rendered ( ) , _mipmap_enabled ( ) , _source_rendered ( ) , _source_size ( ) , _update_mesh ( true )
2018-09-28 12:49:59 +00:00
{
2021-10-22 01:31:35 +00:00
{
2021-11-07 13:37:05 +00:00
auto gctx = obs : : gs : : context ( ) ;
_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 ) ;
} catch ( const std : : exception & ex ) {
DLOG_ERROR ( " Error loading '%s': %s " , file . generic_u8string ( ) . c_str ( ) , ex . what ( ) ) ;
}
2021-10-22 01:31:35 +00:00
}
2021-11-07 13:37:05 +00:00
{
auto file = streamfx : : data_file_path ( " effects/transform.effect " ) ;
try {
_transform_effect = streamfx : : obs : : gs : : effect : : create ( file ) ;
} catch ( const std : : exception & ex ) {
DLOG_ERROR ( " Error loading '%s': %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 ) ;
_sampler . set_max_anisotropy ( 8 ) ;
2021-10-22 01:02:12 +00:00
}
2019-01-24 19:18:04 +00:00
2021-11-07 13:37:05 +00:00
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 ) ;
2017-07-01 01:49:08 +00:00
2021-11-07 13:37:05 +00:00
vec2_set ( & _corners . tl , 0 , 0 ) ;
vec2_set ( & _corners . tr , 1 , 0 ) ;
vec2_set ( & _corners . bl , 0 , 1 ) ;
vec2_set ( & _corners . br , 1 , 1 ) ;
}
2021-10-22 01:02:12 +00:00
2017-06-29 01:41:32 +00:00
update ( data ) ;
}
2020-04-05 16:52:06 +00:00
transform_instance : : ~ transform_instance ( )
2019-01-24 03:34:59 +00:00
{
2019-10-18 19:58:16 +00:00
_vertex_buffer . reset ( ) ;
2019-12-03 17:28:42 +00:00
_cache_rt . reset ( ) ;
_cache_texture . reset ( ) ;
_mipmap_texture . reset ( ) ;
2019-01-24 03:34:59 +00:00
}
2020-04-05 16:52:06 +00:00
void transform_instance : : load ( obs_data_t * settings )
2020-03-28 15:18:17 +00:00
{
2020-04-05 04:02:03 +00:00
update ( settings ) ;
}
2020-03-28 15:18:17 +00:00
2021-10-21 21:04:37 +00:00
void transform_instance : : migrate ( obs_data_t * settings , uint64_t version )
2020-04-05 04:02:03 +00:00
{
2021-10-21 21:04:37 +00:00
// Only test for A.B.C in A.B.C.D
version = version & STREAMFX_MASK_UPDATE ;
2021-11-08 11:11:32 +00:00
# define COPY_UNSET(TYPE, NAME, OLDNAME) \
if ( obs_data_has_user_value ( settings , OLDNAME ) ) { \
obs_data_set_ # # TYPE ( settings , NAME , obs_data_get_ # # TYPE ( settings , OLDNAME ) ) ; \
obs_data_unset_user_value ( settings , OLDNAME ) ; \
2021-10-21 21:04:37 +00:00
}
2021-11-08 11:11:32 +00:00
# define COPY_IGNORE(TYPE, NAME, OLDNAME) \
if ( obs_data_has_user_value ( settings , OLDNAME ) ) { \
obs_data_set_ # # TYPE ( settings , NAME , obs_data_get_ # # TYPE ( settings , OLDNAME ) ) ; \
2021-10-21 21:04:37 +00:00
}
# define SET_IF_UNSET(TYPE, NAME, value) \
if ( ! obs_data_has_user_value ( settings , NAME ) ) { \
obs_data_set_ # # TYPE ( settings , NAME , value ) ; \
}
if ( version < STREAMFX_MAKE_VERSION ( 0 , 8 , 0 , 0 ) ) {
COPY_IGNORE ( double , ST_KEY_ROTATION_X , ST_KEY_ROTATION_X ) ;
COPY_IGNORE ( double , ST_KEY_ROTATION_Y , ST_KEY_ROTATION_Y ) ;
}
if ( version < STREAMFX_MAKE_VERSION ( 0 , 11 , 0 , 0 ) ) {
COPY_UNSET ( int , ST_KEY_CAMERA_MODE , " Filter.Transform.Camera " ) ;
COPY_UNSET ( double , ST_KEY_CAMERA_FIELDOFVIEW , " Filter.Transform.Camera.FieldOfView " ) ;
COPY_UNSET ( double , ST_KEY_POSITION_X , " Filter.Transform.Position.X " ) ;
COPY_UNSET ( double , ST_KEY_POSITION_Y , " Filter.Transform.Position.Y " ) ;
COPY_UNSET ( double , ST_KEY_POSITION_Z , " Filter.Transform.Position.Z " ) ;
COPY_UNSET ( double , ST_KEY_ROTATION_X , " Filter.Transform.Rotation.X " ) ;
COPY_UNSET ( double , ST_KEY_ROTATION_Y , " Filter.Transform.Rotation.Y " ) ;
COPY_UNSET ( double , ST_KEY_ROTATION_Z , " Filter.Transform.Rotation.Z " ) ;
COPY_UNSET ( double , ST_KEY_SCALE_X , " Filter.Transform.Scale.X " ) ;
COPY_UNSET ( double , ST_KEY_SCALE_Y , " Filter.Transform.Scale.Y " ) ;
COPY_UNSET ( double , ST_KEY_SHEAR_X , " Filter.Transform.Shear.X " ) ;
COPY_UNSET ( double , ST_KEY_SHEAR_Y , " Filter.Transform.Shear.Y " ) ;
COPY_UNSET ( double , ST_KEY_ROTATION_ORDER , " Filter.Transform.Rotation.Order " ) ;
COPY_UNSET ( double , ST_KEY_MIPMAPPING , " Filter.Transform.Mipmapping " ) ;
if ( ! obs_data_has_user_value ( settings , ST_KEY_CAMERA_MODE ) ) {
2021-10-22 01:31:35 +00:00
SET_IF_UNSET ( int , ST_KEY_CAMERA_MODE , static_cast < int > ( transform_mode : : ORTHOGRAPHIC ) ) ;
2021-10-21 21:04:37 +00:00
}
2020-03-28 15:18:17 +00:00
}
2021-10-21 21:04:37 +00:00
# undef SET_IF_UNSET
# undef COPY_IGNORE
# undef COPY_UNSET
2020-03-28 15:24:26 +00:00
}
2020-04-05 16:52:06 +00:00
void transform_instance : : update ( obs_data_t * settings )
2018-09-28 12:49:59 +00:00
{
2017-07-01 01:49:08 +00:00
// Camera
2021-10-22 01:31:35 +00:00
_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 ) ) ;
{ // 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 ;
}
2021-10-22 01:02:12 +00:00
{ // Corners
std : : pair < std : : string , float & > opts [ ] = {
2023-05-13 12:35:46 +00:00
{ ST_KEY_CORNERS_TOPLEFT " X " , _corners . tl . x } , { ST_KEY_CORNERS_TOPLEFT " Y " , _corners . tl . y } , { ST_KEY_CORNERS_TOPRIGHT " X " , _corners . tr . x } , { ST_KEY_CORNERS_TOPRIGHT " Y " , _corners . tr . y } , { ST_KEY_CORNERS_BOTTOMLEFT " X " , _corners . bl . x } , { ST_KEY_CORNERS_BOTTOMLEFT " Y " , _corners . bl . y } , { ST_KEY_CORNERS_BOTTOMRIGHT " X " , _corners . br . x } , { ST_KEY_CORNERS_BOTTOMRIGHT " Y " , _corners . br . y } ,
2021-10-22 01:02:12 +00:00
} ;
for ( auto opt : opts ) {
opt . second = static_cast < float > ( obs_data_get_double ( settings , opt . first . c_str ( ) ) / 100.0 ) ;
}
}
2018-09-29 13:57:25 +00:00
2021-10-21 21:04:37 +00:00
// Mip-mapping
2021-06-08 03:07:02 +00:00
_mipmap_enabled = obs_data_get_bool ( settings , ST_KEY_MIPMAPPING ) ;
2021-10-22 01:07:15 +00:00
_sampler . set_filter ( _mipmap_enabled ? GS_FILTER_ANISOTROPIC : GS_FILTER_LINEAR ) ;
2018-09-28 23:07:38 +00:00
2019-08-04 14:20:26 +00:00
_update_mesh = true ;
2017-06-29 01:41:32 +00:00
}
2021-10-22 01:31:35 +00:00
void transform_instance : : video_tick ( float )
2018-09-28 12:49:59 +00:00
{
2020-08-10 01:29:05 +00:00
uint32_t width = 0 ;
uint32_t height = 0 ;
2019-02-15 12:52:44 +00:00
// Grab parent and target.
2019-08-04 14:20:26 +00:00
obs_source_t * target = obs_filter_get_target ( _self ) ;
2019-02-15 12:52:44 +00:00
if ( target ) {
// Grab width an height of the target source (child filter or source).
width = obs_source_get_base_width ( target ) ;
height = obs_source_get_base_height ( target ) ;
}
// If size mismatch, force an update.
2019-08-04 14:20:26 +00:00
if ( width ! = _source_size . first ) {
_update_mesh = true ;
} else if ( height ! = _source_size . second ) {
_update_mesh = true ;
2019-02-15 12:52:44 +00:00
}
2017-07-01 01:49:08 +00:00
// Update Mesh
2019-08-04 14:20:26 +00:00
if ( _update_mesh ) {
_source_size . first = width ;
_source_size . second = height ;
2019-02-15 12:52:44 +00:00
2019-01-24 19:18:04 +00:00
if ( width = = 0 ) {
width = 1 ;
}
if ( height = = 0 ) {
height = 1 ;
}
2021-10-22 01:02:12 +00:00
if ( _camera_mode ! = transform_mode : : CORNER_PIN ) {
// Calculate Aspect Ratio
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 ( _params . rotation_order ) {
case RotationOrder : : XYZ : // XYZ
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 , _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 , _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 , _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 , _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 , _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 , _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 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 + _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 + _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 - _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 - _params . shear . x , p_y + _params . shear . y , 0 ) ;
vec3_transform ( vtx . position , vtx . position , & ident ) ;
}
} else if ( _camera_mode = = transform_mode : : CORNER_PIN ) {
// Corner Pin is rendered in Fragment.
2017-07-01 01:49:08 +00:00
}
2018-01-18 04:01:54 +00:00
2019-08-04 14:20:26 +00:00
_vertex_buffer - > update ( true ) ;
_update_mesh = false ;
2018-09-28 23:07:38 +00:00
}
2019-12-03 17:28:42 +00:00
_cache_rendered = false ;
_mipmap_rendered = false ;
_source_rendered = false ;
2019-01-24 19:18:04 +00:00
}
2020-04-05 16:52:06 +00:00
void transform_instance : : video_render ( gs_effect_t * effect )
2019-01-24 19:18:04 +00:00
{
2019-12-03 17:28:42 +00:00
obs_source_t * parent = obs_filter_get_parent ( _self ) ;
obs_source_t * target = obs_filter_get_target ( _self ) ;
2020-08-10 01:29:05 +00:00
uint32_t base_width = obs_source_get_base_width ( target ) ;
uint32_t base_height = obs_source_get_base_height ( target ) ;
2019-12-03 17:28:42 +00:00
gs_effect_t * default_effect = obs_get_base_effect ( OBS_EFFECT_DEFAULT ) ;
2020-03-07 02:38:30 +00:00
if ( ! effect )
effect = default_effect ;
2019-12-03 17:28:42 +00:00
2023-05-13 12:35:46 +00:00
if ( ! base_width | | ! base_height | | ! parent | | ! target | | ! _standard_effect | | ! _transform_effect ) { // Skip if something is wrong.
2019-08-04 14:20:26 +00:00
obs_source_skip_video_filter ( _self ) ;
2018-09-28 23:07:38 +00:00
return ;
}
2019-01-24 19:18:04 +00:00
2023-04-05 16:24:26 +00:00
# if defined(ENABLE_PROFILING) && !defined(D_PLATFORM_MAC) && _DEBUG
2023-05-13 12:35:46 +00:00
streamfx : : obs : : gs : : debug_marker gdmp { streamfx : : obs : : gs : : debug_color_source , " 3D Transform '%s' on '%s' " , obs_source_get_name ( _self ) , obs_source_get_name ( obs_filter_get_parent ( _self ) ) } ;
2020-04-25 23:04:04 +00:00
# endif
2019-12-03 17:28:42 +00:00
2020-08-10 01:29:05 +00:00
uint32_t cache_width = base_width ;
uint32_t cache_height = base_height ;
2019-12-03 17:28:42 +00:00
if ( _mipmap_enabled ) {
double_t aspect = double_t ( base_width ) / double_t ( base_height ) ;
double_t aspect2 = 1.0 / aspect ;
2023-05-13 12:35:46 +00:00
cache_width = std : : clamp ( uint32_t ( pow ( 2 , streamfx : : util : : math : : get_power_of_two_exponent_ceil ( cache_width ) ) ) , 1u , 16384u ) ;
cache_height = std : : clamp ( uint32_t ( pow ( 2 , streamfx : : util : : math : : get_power_of_two_exponent_ceil ( cache_height ) ) ) , 1u , 16384u ) ;
2019-12-03 17:28:42 +00:00
if ( aspect > 1.0 ) {
2023-05-13 12:35:46 +00:00
cache_height = std : : clamp ( uint32_t ( pow ( 2 , streamfx : : util : : math : : get_power_of_two_exponent_ceil ( uint64_t ( cache_width * aspect2 ) ) ) ) , 1u , 16384u ) ;
2019-12-03 17:28:42 +00:00
} else if ( aspect < 1.0 ) {
2023-05-13 12:35:46 +00:00
cache_width = std : : clamp ( uint32_t ( pow ( 2 , streamfx : : util : : math : : get_power_of_two_exponent_ceil ( uint64_t ( cache_height * aspect ) ) ) ) , 1u , 16384u ) ;
2019-12-03 17:28:42 +00:00
}
2019-01-24 19:18:04 +00:00
}
2019-12-03 17:28:42 +00:00
if ( ! _cache_rendered ) {
2023-04-05 16:24:26 +00:00
# if defined(ENABLE_PROFILING) && !defined(D_PLATFORM_MAC) && _DEBUG
2021-06-08 02:38:24 +00:00
streamfx : : obs : : gs : : debug_marker gdm { streamfx : : obs : : gs : : debug_color_cache , " Cache " } ;
2020-04-25 23:04:04 +00:00
# endif
auto op = _cache_rt - > render ( cache_width , cache_height ) ;
2018-09-29 13:57:25 +00:00
2021-10-22 01:31:35 +00:00
gs_ortho ( 0 , static_cast < float > ( base_width ) , 0 , static_cast < float > ( base_height ) , - 1 , 1 ) ;
2019-12-03 17:28:42 +00:00
2020-04-08 21:38:42 +00:00
vec4 clear_color = { 0 , 0 , 0 , 0 } ;
2019-12-03 17:28:42 +00:00
gs_clear ( GS_CLEAR_COLOR | GS_CLEAR_DEPTH , & clear_color , 0 , 0 ) ;
/// Render original source
2020-03-07 02:38:30 +00:00
if ( obs_source_process_filter_begin ( _self , GS_RGBA , OBS_NO_DIRECT_RENDERING ) ) {
gs_blend_state_push ( ) ;
gs_reset_blend_state ( ) ;
gs_enable_blending ( false ) ;
gs_blend_function_separate ( GS_BLEND_ONE , GS_BLEND_ZERO , GS_BLEND_SRCALPHA , 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_set_cull_mode ( GS_NEITHER ) ;
obs_source_process_filter_end ( _self , default_effect , base_width , base_height ) ;
gs_blend_state_pop ( ) ;
2019-12-03 17:28:42 +00:00
} else {
2019-08-04 14:20:26 +00:00
obs_source_skip_video_filter ( _self ) ;
2019-01-24 19:18:04 +00:00
return ;
2017-08-19 22:31:19 +00:00
}
2017-07-01 01:49:08 +00:00
2019-12-03 17:28:42 +00:00
_cache_rendered = true ;
}
_cache_rt - > get_texture ( _cache_texture ) ;
if ( ! _cache_texture ) {
obs_source_skip_video_filter ( _self ) ;
return ;
}
if ( _mipmap_enabled ) {
2023-04-05 16:24:26 +00:00
# if defined(ENABLE_PROFILING) && !defined(D_PLATFORM_MAC) && _DEBUG
2021-06-08 02:38:24 +00:00
streamfx : : obs : : gs : : debug_marker gdm { streamfx : : obs : : gs : : debug_color_convert , " Mipmap " } ;
2020-04-25 23:04:04 +00:00
# endif
2019-12-03 17:28:42 +00:00
2023-05-13 12:35:46 +00:00
if ( ! _mipmap_texture | | ( _mipmap_texture - > get_width ( ) ! = cache_width ) | | ( _mipmap_texture - > get_height ( ) ! = cache_height ) ) {
2023-04-05 16:24:26 +00:00
# if defined(ENABLE_PROFILING) && !defined(D_PLATFORM_MAC) && _DEBUG
2021-06-08 02:38:24 +00:00
streamfx : : obs : : gs : : debug_marker gdr { streamfx : : obs : : gs : : debug_color_allocate , " Allocate Mipmapped Texture " } ;
2020-04-25 23:04:04 +00:00
# endif
2021-11-09 10:34:38 +00:00
std : : size_t mip_levels = _mipmapper . calculate_max_mip_level ( cache_width , cache_height ) ;
2023-05-13 12:35:46 +00:00
_mipmap_texture = std : : make_shared < streamfx : : obs : : gs : : texture > ( cache_width , cache_height , GS_RGBA , static_cast < uint32_t > ( mip_levels ) , nullptr , streamfx : : obs : : gs : : texture : : flags : : None ) ;
2017-06-29 01:41:32 +00:00
}
2020-04-25 06:15:20 +00:00
_mipmapper . rebuild ( _cache_texture , _mipmap_texture ) ;
2017-06-29 01:41:32 +00:00
2019-12-03 17:28:42 +00:00
_mipmap_rendered = true ;
2020-04-25 06:15:20 +00:00
if ( ! _mipmap_texture ) {
obs_source_skip_video_filter ( _self ) ;
return ;
}
2019-12-03 17:28:42 +00:00
}
2019-01-24 19:18:04 +00:00
2019-12-03 17:28:42 +00:00
{
2023-04-05 16:24:26 +00:00
# if defined(ENABLE_PROFILING) && !defined(D_PLATFORM_MAC) && _DEBUG
2021-06-08 02:38:24 +00:00
streamfx : : obs : : gs : : debug_marker gdm { streamfx : : obs : : gs : : debug_color_convert , " Transform " } ;
2020-04-25 23:04:04 +00:00
# endif
auto op = _source_rt - > render ( base_width , base_height ) ;
2019-12-03 17:28:42 +00:00
2021-10-22 01:31:35 +00:00
vec4 clear_color = { 0 , 0 , 0 , 0 } ;
gs_clear ( GS_CLEAR_COLOR | GS_CLEAR_DEPTH , & clear_color , 0 , 0 ) ;
2020-03-07 02:38:30 +00:00
gs_blend_state_push ( ) ;
gs_reset_blend_state ( ) ;
gs_enable_blending ( false ) ;
gs_blend_function_separate ( GS_BLEND_ONE , GS_BLEND_ZERO , GS_BLEND_ONE , 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_set_cull_mode ( GS_NEITHER ) ;
2021-10-22 01:31:35 +00:00
switch ( _camera_mode ) {
case transform_mode : : ORTHOGRAPHIC :
2019-12-03 17:28:42 +00:00
gs_ortho ( - 1. , 1. , - 1. , 1. , - farZ , farZ ) ;
2021-10-22 01:31:35 +00:00
break ;
case transform_mode : : PERSPECTIVE :
gs_perspective ( _camera_fov , float ( base_width ) / float ( base_height ) , nearZ , farZ ) ;
2019-12-03 17:28:42 +00:00
gs_matrix_scale3f ( 1.0 , 1.0 , 1.0 ) ;
gs_matrix_translate3f ( 0. , 0. , - 1.0 ) ;
2021-10-22 01:31:35 +00:00
break ;
2021-10-22 01:02:12 +00:00
case transform_mode : : CORNER_PIN :
gs_ortho ( 0. , 1. , 0. , 1. , - farZ , farZ ) ;
break ;
2017-06-29 01:41:32 +00:00
}
2019-01-24 19:18:04 +00:00
2021-10-22 01:02:12 +00:00
if ( _camera_mode ! = transform_mode : : CORNER_PIN ) {
gs_load_vertexbuffer ( _vertex_buffer - > update ( false ) ) ;
gs_load_indexbuffer ( nullptr ) ;
2023-05-13 12:35:46 +00:00
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 ( ) ) ;
2021-10-22 01:02:12 +00:00
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 ) ;
} else {
gs_load_vertexbuffer ( nullptr ) ;
gs_load_indexbuffer ( nullptr ) ;
2023-05-13 12:35:46 +00:00
if ( auto v = _transform_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 ( ) ) ;
2021-10-22 01:02:12 +00:00
v . set_sampler ( _sampler . get_object ( ) ) ;
}
2023-05-13 12:35:46 +00:00
if ( auto v = _transform_effect . get_parameter ( " CornerTL " ) ; v . get_type ( ) = = : : streamfx : : obs : : gs : : effect_parameter : : type : : Float2 ) {
2021-10-22 01:02:12 +00:00
v . set_float2 ( _corners . tl ) ;
}
2023-05-13 12:35:46 +00:00
if ( auto v = _transform_effect . get_parameter ( " CornerTR " ) ; v . get_type ( ) = = : : streamfx : : obs : : gs : : effect_parameter : : type : : Float2 ) {
2021-10-22 01:02:12 +00:00
v . set_float2 ( _corners . tr ) ;
}
2023-05-13 12:35:46 +00:00
if ( auto v = _transform_effect . get_parameter ( " CornerBL " ) ; v . get_type ( ) = = : : streamfx : : obs : : gs : : effect_parameter : : type : : Float2 ) {
2021-10-22 01:02:12 +00:00
v . set_float2 ( _corners . bl ) ;
}
2023-05-13 12:35:46 +00:00
if ( auto v = _transform_effect . get_parameter ( " CornerBR " ) ; v . get_type ( ) = = : : streamfx : : obs : : gs : : effect_parameter : : type : : Float2 ) {
2021-10-22 01:02:12 +00:00
v . set_float2 ( _corners . br ) ;
}
while ( gs_effect_loop ( _transform_effect . get_object ( ) , " CornerPin " ) ) {
2022-11-28 08:11:55 +00:00
_gfx_util - > draw_fullscreen_triangle ( ) ;
2021-10-22 01:02:12 +00:00
}
2019-12-03 17:28:42 +00:00
}
2020-03-07 02:38:30 +00:00
gs_blend_state_pop ( ) ;
2019-12-03 17:28:42 +00:00
}
_source_rt - > get_texture ( _source_texture ) ;
if ( ! _source_texture ) {
obs_source_skip_video_filter ( _self ) ;
return ;
2017-06-29 01:41:32 +00:00
}
2019-12-03 17:28:42 +00:00
{
2023-04-05 16:24:26 +00:00
# if defined(ENABLE_PROFILING) && !defined(D_PLATFORM_MAC) && _DEBUG
2021-06-08 02:38:24 +00:00
streamfx : : obs : : gs : : debug_marker gdm { streamfx : : obs : : gs : : debug_color_render , " Render " } ;
2020-04-25 23:04:04 +00:00
# endif
2020-03-07 02:38:30 +00:00
gs_effect_set_texture ( gs_effect_get_param_by_name ( effect , " image " ) , _source_texture - > get_object ( ) ) ;
while ( gs_effect_loop ( effect , " Draw " ) ) {
2019-12-03 17:28:42 +00:00
gs_draw_sprite ( nullptr , 0 , base_width , base_height ) ;
}
2017-06-29 01:41:32 +00:00
}
}
2019-10-18 19:58:16 +00:00
2020-04-05 16:52:06 +00:00
transform_factory : : transform_factory ( )
2019-10-18 19:58:16 +00:00
{
2021-06-08 02:16:33 +00:00
_info . id = S_PREFIX " filter-transform " ;
2019-10-18 19:58:16 +00:00
_info . type = OBS_SOURCE_TYPE_FILTER ;
2019-12-14 20:32:48 +00:00
_info . output_flags = OBS_SOURCE_VIDEO ;
2019-10-18 19:58:16 +00:00
2022-05-11 03:33:38 +00:00
support_size ( false ) ;
2019-12-14 20:32:48 +00:00
finish_setup ( ) ;
2020-06-14 02:53:15 +00:00
register_proxy ( " obs-stream-effects-filter-transform " ) ;
2019-10-18 19:58:16 +00:00
}
2020-04-05 16:52:06 +00:00
transform_factory : : ~ transform_factory ( ) { }
2019-10-18 19:58:16 +00:00
2020-04-05 16:52:06 +00:00
const char * transform_factory : : get_name ( )
2019-10-18 19:58:16 +00:00
{
2021-06-08 03:07:02 +00:00
return D_TRANSLATE ( ST_I18N ) ;
2019-10-18 19:58:16 +00:00
}
2020-04-05 16:52:06 +00:00
void transform_factory : : get_defaults2 ( obs_data_t * settings )
2019-10-18 19:58:16 +00:00
{
2021-10-22 01:02:12 +00:00
obs_data_set_default_int ( settings , ST_KEY_CAMERA_MODE , static_cast < int64_t > ( transform_mode : : CORNER_PIN ) ) ;
2021-06-08 03:07:02 +00:00
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 ) ;
obs_data_set_default_double ( settings , ST_KEY_POSITION_Z , 0 ) ;
obs_data_set_default_double ( settings , ST_KEY_ROTATION_X , 0 ) ;
obs_data_set_default_double ( settings , ST_KEY_ROTATION_Y , 0 ) ;
obs_data_set_default_double ( settings , ST_KEY_ROTATION_Z , 0 ) ;
obs_data_set_default_int ( settings , ST_KEY_ROTATION_ORDER , static_cast < int64_t > ( RotationOrder : : ZXY ) ) ;
obs_data_set_default_double ( settings , ST_KEY_SCALE_X , 100 ) ;
obs_data_set_default_double ( settings , ST_KEY_SCALE_Y , 100 ) ;
obs_data_set_default_double ( settings , ST_KEY_SHEAR_X , 0 ) ;
obs_data_set_default_double ( settings , ST_KEY_SHEAR_Y , 0 ) ;
2021-10-22 01:02:12 +00:00
obs_data_set_default_double ( settings , ST_KEY_CORNERS_TOPLEFT " X " , - 100. ) ;
obs_data_set_default_double ( settings , ST_KEY_CORNERS_TOPLEFT " Y " , - 100. ) ;
obs_data_set_default_double ( settings , ST_KEY_CORNERS_TOPRIGHT " X " , 100. ) ;
obs_data_set_default_double ( settings , ST_KEY_CORNERS_TOPRIGHT " Y " , - 100. ) ;
obs_data_set_default_double ( settings , ST_KEY_CORNERS_BOTTOMLEFT " X " , - 100. ) ;
obs_data_set_default_double ( settings , ST_KEY_CORNERS_BOTTOMLEFT " Y " , 100. ) ;
obs_data_set_default_double ( settings , ST_KEY_CORNERS_BOTTOMRIGHT " X " , 100. ) ;
obs_data_set_default_double ( settings , ST_KEY_CORNERS_BOTTOMRIGHT " Y " , 100. ) ;
2021-06-08 03:07:02 +00:00
obs_data_set_default_bool ( settings , ST_KEY_MIPMAPPING , false ) ;
2019-10-18 19:58:16 +00:00
}
2021-10-21 21:04:37 +00:00
static bool modified_camera_mode ( obs_properties_t * pr , obs_property_t * , obs_data_t * d ) noexcept
2022-08-27 11:17:47 +00:00
{
try {
auto mode = static_cast < transform_mode > ( obs_data_get_int ( d , ST_KEY_CAMERA_MODE ) ) ;
bool is_camera = mode ! = transform_mode : : CORNER_PIN ;
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 ) ;
obs_property_set_visible ( obs_properties_get ( pr , ST_KEY_POSITION_Z ) , is_perspective ) ;
obs_property_set_visible ( obs_properties_get ( pr , ST_I18N_ROTATION ) , is_camera ) ;
obs_property_set_visible ( obs_properties_get ( pr , ST_I18N_SCALE ) , is_camera ) ;
obs_property_set_visible ( obs_properties_get ( pr , ST_I18N_SHEAR ) , is_camera ) ;
obs_property_set_visible ( obs_properties_get ( pr , ST_KEY_ROTATION_ORDER ) , is_camera ) ;
obs_property_set_visible ( obs_properties_get ( pr , ST_I18N_CORNERS ) , ! is_camera ) ;
return true ;
} catch ( const std : : exception & ex ) {
DLOG_ERROR ( " Unexpected exception in function '%s': %s. " , __FUNCTION_NAME__ , ex . what ( ) ) ;
return true ;
} catch ( . . . ) {
DLOG_ERROR ( " Unexpected exception in function '%s'. " , __FUNCTION_NAME__ ) ;
return true ;
}
2019-10-18 19:58:16 +00:00
}
2020-04-05 16:52:06 +00:00
obs_properties_t * transform_factory : : get_properties2 ( transform_instance * data )
2019-10-18 19:58:16 +00:00
{
obs_properties_t * pr = obs_properties_create ( ) ;
2021-04-16 23:43:30 +00:00
# ifdef ENABLE_FRONTEND
{
2023-05-13 12:35:46 +00:00
obs_properties_add_button2 ( pr , S_MANUAL_OPEN , D_TRANSLATE ( S_MANUAL_OPEN ) , streamfx : : filter : : transform : : transform_factory : : on_manual_open , nullptr ) ;
2021-04-16 23:43:30 +00:00
}
# endif
2019-10-18 19:58:16 +00:00
// Camera
2019-12-03 17:28:42 +00:00
{
auto grp = obs_properties_create ( ) ;
{ // Projection Mode
2023-05-13 12:35:46 +00:00
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_CORNER_PIN ) , static_cast < int64_t > ( transform_mode : : CORNER_PIN ) ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_CAMERA_MODE_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 > ( transform_mode : : PERSPECTIVE ) ) ;
2021-10-21 21:04:37 +00:00
obs_property_set_modified_callback ( p , modified_camera_mode ) ;
2019-12-03 17:28:42 +00:00
}
{ // Field Of View
2023-05-13 12:35:46 +00:00
auto p = obs_properties_add_float_slider ( grp , ST_KEY_CAMERA_FIELDOFVIEW , D_TRANSLATE ( ST_I18N_CAMERA_FIELDOFVIEW ) , 1.0 , 179.0 , 0.01 ) ;
2019-12-03 17:28:42 +00:00
}
2021-10-21 21:04:37 +00:00
obs_properties_add_group ( pr , ST_I18N_CAMERA , D_TRANSLATE ( ST_I18N_CAMERA ) , OBS_GROUP_NORMAL , grp ) ;
2019-12-03 17:28:42 +00:00
}
2019-10-18 19:58:16 +00:00
2021-10-21 21:04:37 +00:00
{
; // Parmametrized Mesh
2019-12-03 17:28:42 +00:00
2021-10-21 21:04:37 +00:00
{ // Position
auto grp = obs_properties_create ( ) ;
2019-12-03 17:28:42 +00:00
2021-10-21 21:04:37 +00:00
std : : pair < std : : string , std : : string > opts [ ] = {
{ ST_KEY_POSITION_X , " X " } ,
{ ST_KEY_POSITION_Y , " Y " } ,
{ ST_KEY_POSITION_Z , " Z " } ,
} ;
2022-07-21 11:09:10 +00:00
for ( const auto & opt : opts ) {
2023-05-13 12:35:46 +00:00
auto p = obs_properties_add_float ( grp , opt . first . c_str ( ) , D_TRANSLATE ( opt . second . c_str ( ) ) , std : : numeric_limits < float > : : lowest ( ) , std : : numeric_limits < float > : : max ( ) , 0.01 ) ;
2021-10-21 21:04:37 +00:00
}
2019-12-03 17:28:42 +00:00
2021-10-21 21:04:37 +00:00
obs_properties_add_group ( pr , ST_I18N_POSITION , D_TRANSLATE ( ST_I18N_POSITION ) , OBS_GROUP_NORMAL , grp ) ;
}
{ // Rotation
auto grp = obs_properties_create ( ) ;
std : : pair < std : : string , std : : string > opts [ ] = {
{ ST_KEY_ROTATION_X , D_TRANSLATE ( ST_I18N_ROTATION " .X " ) } ,
{ ST_KEY_ROTATION_Y , D_TRANSLATE ( ST_I18N_ROTATION " .Y " ) } ,
{ ST_KEY_ROTATION_Z , D_TRANSLATE ( ST_I18N_ROTATION " .Z " ) } ,
} ;
2022-07-21 11:09:10 +00:00
for ( const auto & opt : opts ) {
2023-05-13 12:35:46 +00:00
auto p = obs_properties_add_float_slider ( grp , opt . first . c_str ( ) , D_TRANSLATE ( opt . second . c_str ( ) ) , - 180.0 , 180.0 , 0.01 ) ;
2019-12-03 17:28:42 +00:00
obs_property_float_set_suffix ( p , " ° Deg " ) ;
}
2021-10-21 21:04:37 +00:00
obs_properties_add_group ( pr , ST_I18N_ROTATION , D_TRANSLATE ( ST_I18N_ROTATION ) , OBS_GROUP_NORMAL , grp ) ;
2019-12-03 17:28:42 +00:00
}
2021-10-21 21:04:37 +00:00
{ // Scale
auto grp = obs_properties_create ( ) ;
2019-12-03 17:28:42 +00:00
2021-10-21 21:04:37 +00:00
std : : pair < std : : string , std : : string > opts [ ] = {
{ ST_KEY_SCALE_X , " X " } ,
{ ST_KEY_SCALE_Y , " Y " } ,
} ;
2022-07-21 11:09:10 +00:00
for ( const auto & opt : opts ) {
2021-10-21 21:04:37 +00:00
auto p = obs_properties_add_float_slider ( grp , opt . first . c_str ( ) , opt . second . c_str ( ) , - 1000 , 1000 , 0.01 ) ;
obs_property_float_set_suffix ( p , " % " ) ;
}
2019-12-03 17:28:42 +00:00
2021-10-21 21:04:37 +00:00
obs_properties_add_group ( pr , ST_I18N_SCALE , D_TRANSLATE ( ST_I18N_SCALE ) , OBS_GROUP_NORMAL , grp ) ;
2019-10-18 19:58:16 +00:00
}
2021-10-21 21:04:37 +00:00
{ // Shear
auto grp = obs_properties_create ( ) ;
2019-12-03 17:28:42 +00:00
2021-10-21 21:04:37 +00:00
std : : pair < std : : string , std : : string > opts [ ] = {
{ ST_KEY_SHEAR_X , " X " } ,
{ ST_KEY_SHEAR_Y , " Y " } ,
} ;
2022-07-21 11:09:10 +00:00
for ( const auto & opt : opts ) {
2023-05-13 12:35:46 +00:00
auto p = obs_properties_add_float_slider ( grp , opt . first . c_str ( ) , opt . second . c_str ( ) , - 200.0 , 200.0 , 0.01 ) ;
2021-10-21 21:04:37 +00:00
obs_property_float_set_suffix ( p , " % " ) ;
}
2019-12-03 17:28:42 +00:00
2021-10-21 21:04:37 +00:00
obs_properties_add_group ( pr , ST_I18N_SHEAR , D_TRANSLATE ( ST_I18N_SHEAR ) , OBS_GROUP_NORMAL , grp ) ;
2019-10-18 19:58:16 +00:00
}
}
2021-10-22 01:02:12 +00:00
{ // Corners
auto grp = obs_properties_create ( ) ;
{ // Top Left
auto grp2 = obs_properties_create ( ) ;
std : : pair < std : : string , std : : string > opts [ ] = {
{ ST_KEY_CORNERS_TOPLEFT " X " , " X " } ,
{ ST_KEY_CORNERS_TOPLEFT " Y " , " Y " } ,
} ;
for ( auto & opt : opts ) {
2023-05-13 12:35:46 +00:00
auto p = obs_properties_add_float_slider ( grp2 , opt . first . c_str ( ) , opt . second . c_str ( ) , - 200.0 , 200.0 , 0.01 ) ;
2021-10-22 01:02:12 +00:00
obs_property_float_set_suffix ( p , " % " ) ;
}
2023-05-13 12:35:46 +00:00
obs_properties_add_group ( grp , ST_I18N_CORNERS_TOPLEFT , D_TRANSLATE ( ST_I18N_CORNERS_TOPLEFT ) , OBS_GROUP_NORMAL , grp2 ) ;
2021-10-22 01:02:12 +00:00
}
{ // Top Right
auto grp2 = obs_properties_create ( ) ;
std : : pair < std : : string , std : : string > opts [ ] = {
{ ST_KEY_CORNERS_TOPRIGHT " X " , " X " } ,
{ ST_KEY_CORNERS_TOPRIGHT " Y " , " Y " } ,
} ;
for ( auto & opt : opts ) {
2023-05-13 12:35:46 +00:00
auto p = obs_properties_add_float_slider ( grp2 , opt . first . c_str ( ) , opt . second . c_str ( ) , - 200.0 , 200.0 , 0.01 ) ;
2021-10-22 01:02:12 +00:00
obs_property_float_set_suffix ( p , " % " ) ;
}
2023-05-13 12:35:46 +00:00
obs_properties_add_group ( grp , ST_I18N_CORNERS_TOPRIGHT , D_TRANSLATE ( ST_I18N_CORNERS_TOPRIGHT ) , OBS_GROUP_NORMAL , grp2 ) ;
2021-10-22 01:02:12 +00:00
}
{ // Bottom Left
auto grp2 = obs_properties_create ( ) ;
std : : pair < std : : string , std : : string > opts [ ] = {
{ ST_KEY_CORNERS_BOTTOMLEFT " X " , " X " } ,
{ ST_KEY_CORNERS_BOTTOMLEFT " Y " , " Y " } ,
} ;
for ( auto & opt : opts ) {
2023-05-13 12:35:46 +00:00
auto p = obs_properties_add_float_slider ( grp2 , opt . first . c_str ( ) , opt . second . c_str ( ) , - 200.0 , 200.0 , 0.01 ) ;
2021-10-22 01:02:12 +00:00
obs_property_float_set_suffix ( p , " % " ) ;
}
2023-05-13 12:35:46 +00:00
obs_properties_add_group ( grp , ST_I18N_CORNERS_BOTTOMLEFT , D_TRANSLATE ( ST_I18N_CORNERS_BOTTOMLEFT ) , OBS_GROUP_NORMAL , grp2 ) ;
2021-10-22 01:02:12 +00:00
}
{ // Bottom Right
auto grp2 = obs_properties_create ( ) ;
std : : pair < std : : string , std : : string > opts [ ] = {
{ ST_KEY_CORNERS_BOTTOMRIGHT " X " , " X " } ,
{ ST_KEY_CORNERS_BOTTOMRIGHT " Y " , " Y " } ,
} ;
for ( auto & opt : opts ) {
2023-05-13 12:35:46 +00:00
auto p = obs_properties_add_float_slider ( grp2 , opt . first . c_str ( ) , opt . second . c_str ( ) , - 200.0 , 200.0 , 0.01 ) ;
2021-10-22 01:02:12 +00:00
obs_property_float_set_suffix ( p , " % " ) ;
}
2023-05-13 12:35:46 +00:00
obs_properties_add_group ( grp , ST_I18N_CORNERS_BOTTOMRIGHT , D_TRANSLATE ( ST_I18N_CORNERS_BOTTOMRIGHT ) , OBS_GROUP_NORMAL , grp2 ) ;
2021-10-22 01:02:12 +00:00
}
obs_properties_add_group ( pr , ST_I18N_CORNERS , D_TRANSLATE ( ST_I18N_CORNERS ) , OBS_GROUP_NORMAL , grp ) ;
}
2019-12-03 17:28:42 +00:00
{
auto grp = obs_properties_create ( ) ;
2020-04-25 06:15:20 +00:00
obs_properties_add_group ( pr , S_ADVANCED , D_TRANSLATE ( S_ADVANCED ) , OBS_GROUP_NORMAL , grp ) ;
2019-12-03 17:28:42 +00:00
2021-10-21 21:04:37 +00:00
{ // Mip-mapping
2021-06-08 03:07:02 +00:00
auto p = obs_properties_add_bool ( grp , ST_KEY_MIPMAPPING , D_TRANSLATE ( ST_I18N_MIPMAPPING ) ) ;
2019-12-03 17:28:42 +00:00
}
2020-04-25 06:15:20 +00:00
{ // Order
2023-05-13 12:35:46 +00:00
auto p = obs_properties_add_list ( grp , ST_KEY_ROTATION_ORDER , D_TRANSLATE ( ST_I18N_ROTATION_ORDER ) , OBS_COMBO_TYPE_LIST , OBS_COMBO_FORMAT_INT ) ;
2021-06-08 03:07:02 +00:00
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_ROTATION_ORDER_XYZ ) , RotationOrder : : XYZ ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_ROTATION_ORDER_XZY ) , RotationOrder : : XZY ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_ROTATION_ORDER_YXZ ) , RotationOrder : : YXZ ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_ROTATION_ORDER_YZX ) , RotationOrder : : YZX ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_ROTATION_ORDER_ZXY ) , RotationOrder : : ZXY ) ;
obs_property_list_add_int ( p , D_TRANSLATE ( ST_I18N_ROTATION_ORDER_ZYX ) , RotationOrder : : ZYX ) ;
2019-12-03 17:28:42 +00:00
}
}
2019-10-18 19:58:16 +00:00
return pr ;
}
2020-04-05 16:52:06 +00:00
2021-04-16 23:43:30 +00:00
# ifdef ENABLE_FRONTEND
bool transform_factory : : on_manual_open ( obs_properties_t * props , obs_property_t * property , void * data )
2022-08-27 11:17:47 +00:00
{
try {
streamfx : : open_url ( HELP_URL ) ;
return false ;
} catch ( const std : : exception & ex ) {
D_LOG_ERROR ( " Failed to open manual due to error: %s " , ex . what ( ) ) ;
return false ;
} catch ( . . . ) {
D_LOG_ERROR ( " Failed to open manual due to unknown error. " , " " ) ;
return false ;
}
2021-04-16 23:43:30 +00:00
}
# endif
2023-05-14 06:47:03 +00:00
std : : shared_ptr < transform_factory > transform_factory : : instance ( )
2022-08-27 11:17:47 +00:00
{
2023-05-14 06:47:03 +00:00
static std : : weak_ptr < transform_factory > winst ;
static std : : mutex mtx ;
std : : unique_lock < decltype ( mtx ) > lock ( mtx ) ;
auto instance = winst . lock ( ) ;
if ( ! instance ) {
instance = std : : shared_ptr < transform_factory > ( new transform_factory ( ) ) ;
winst = instance ;
2022-08-27 11:17:47 +00:00
}
2023-05-14 06:47:03 +00:00
return instance ;
2020-04-05 16:52:06 +00:00
}
2023-05-14 06:47:03 +00:00
static std : : shared_ptr < transform_factory > loader_instance ;
2020-04-05 16:52:06 +00:00
2023-05-14 06:47:03 +00:00
static auto loader = streamfx : : loader (
[ ] ( ) { // Initalizer
loader_instance = transform_factory : : instance ( ) ;
} ,
[ ] ( ) { // Finalizer
loader_instance . reset ( ) ;
} ,
streamfx : : loader_priority : : NORMAL ) ;