diff --git a/libobs/obs-config.h b/libobs/obs-config.h index 4e110bbff..2411cf1d9 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -34,14 +34,14 @@ * * Reset to zero each major version */ -#define LIBOBS_API_MINOR_VER 2 +#define LIBOBS_API_MINOR_VER 3 /* * Increment if backward-compatible bug fix * * Reset to zero each major or minor version */ -#define LIBOBS_API_PATCH_VER 4 +#define LIBOBS_API_PATCH_VER 0 #define LIBOBS_API_VER ((LIBOBS_API_MAJOR_VER << 24) | \ (LIBOBS_API_MINOR_VER << 16) | \ diff --git a/libobs/obs-defs.h b/libobs/obs-defs.h index cf570b925..f2c9717e7 100644 --- a/libobs/obs-defs.h +++ b/libobs/obs-defs.h @@ -20,6 +20,12 @@ /** Maximum number of source channels for output and per display */ #define MAX_CHANNELS 64 +#define OBS_ALIGN_CENTER (0) +#define OBS_ALIGN_LEFT (1<<0) +#define OBS_ALIGN_RIGHT (1<<1) +#define OBS_ALIGN_TOP (1<<2) +#define OBS_ALIGN_BOTTOM (1<<3) + #define MODULE_SUCCESS 0 #define MODULE_ERROR -1 #define MODULE_FILE_NOT_FOUND -2 diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index d663ac768..cd76a106a 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -26,6 +26,9 @@ static const char *obs_scene_signals[] = { "void item_move_down(ptr scene, ptr item)", "void item_move_top(ptr scene, ptr item)", "void item_move_bottom(ptr scene, ptr item)", + "void item_select(ptr scene, ptr item)", + "void item_deselect(ptr scene, ptr item)", + "void item_transform(ptr scene, ptr item)", NULL }; @@ -155,6 +158,138 @@ static inline void attach_sceneitem(struct obs_scene *parent, } } +static void add_alignment(struct vec2 *v, uint32_t align, int cx, int cy) +{ + if (align & OBS_ALIGN_RIGHT) + v->x += (float)cx; + else if ((align & OBS_ALIGN_LEFT) == 0) + v->x += (float)(cx / 2); + + if (align & OBS_ALIGN_BOTTOM) + v->y += (float)cy; + else if ((align & OBS_ALIGN_TOP) == 0) + v->y += (float)(cy / 2); +} + +static void calculate_bounds_data(struct obs_scene_item *item, + struct vec2 *origin, struct vec2 *scale, + uint32_t *cx, uint32_t *cy) +{ + float width = (float)(*cx) * fabsf(scale->x); + float height = (float)(*cy) * fabsf(scale->y); + float item_aspect = width / height; + float bounds_aspect = item->bounds.x / item->bounds.y; + float width_diff, height_diff; + + if (item->bounds_type == OBS_BOUNDS_SCALE_INNER || + item->bounds_type == OBS_BOUNDS_SCALE_OUTER) { + bool use_width = (bounds_aspect < item_aspect); + float mul; + + if (item->bounds_type == OBS_BOUNDS_SCALE_OUTER) + use_width = !use_width; + + mul = use_width ? + item->bounds.x / width : + item->bounds.y / height; + + vec2_mulf(scale, scale, mul); + + } else if (item->bounds_type == OBS_BOUNDS_SCALE_TO_WIDTH) { + vec2_mulf(scale, scale, item->bounds.x / width); + + } else if (item->bounds_type == OBS_BOUNDS_SCALE_TO_HEIGHT) { + vec2_mulf(scale, scale, item->bounds.y / height); + + } else if (item->bounds_type == OBS_BOUNDS_STRETCH) { + scale->x = item->bounds.x / (float)(*cx); + scale->y = item->bounds.y / (float)(*cy); + } + + width = (float)(*cx) * scale->x; + height = (float)(*cy) * scale->y; + width_diff = item->bounds.x - width; + height_diff = item->bounds.y - height; + *cx = (uint32_t)item->bounds.x; + *cy = (uint32_t)item->bounds.y; + + add_alignment(origin, item->bounds_align, + (int)-width_diff, (int)-height_diff); +} + +static void update_item_transform(struct obs_scene_item *item) +{ + uint32_t width = obs_source_getwidth(item->source); + uint32_t height = obs_source_getheight(item->source); + uint32_t cx = width; + uint32_t cy = height; + struct vec2 base_origin = {0.0f, 0.0f}; + struct vec2 origin = {0.0f, 0.0f}; + struct vec2 scale = item->scale; + struct calldata params = {0}; + + /* ----------------------- */ + + if (item->bounds_type != OBS_BOUNDS_NONE) { + calculate_bounds_data(item, &origin, &scale, &cx, &cy); + } else { + cx = (uint32_t)((float)cx * scale.x); + cy = (uint32_t)((float)cy * scale.y); + } + + add_alignment(&origin, item->align, (int)cx, (int)cy); + + matrix4_identity(&item->draw_transform); + matrix4_scale3f(&item->draw_transform, &item->draw_transform, + scale.x, scale.y, 1.0f); + matrix4_translate3f(&item->draw_transform, &item->draw_transform, + -origin.x, -origin.y, 0.0f); + matrix4_rotate_aa4f(&item->draw_transform, &item->draw_transform, + 0.0f, 0.0f, 1.0f, RAD(item->rot)); + matrix4_translate3f(&item->draw_transform, &item->draw_transform, + item->pos.x, item->pos.y, 0.0f); + + /* ----------------------- */ + + if (item->bounds_type != OBS_BOUNDS_NONE) { + vec2_copy(&scale, &item->bounds); + } else { + scale.x = (float)width * item->scale.x; + scale.y = (float)height * item->scale.y; + } + + add_alignment(&base_origin, item->align, (int)scale.x, (int)scale.y); + + matrix4_identity(&item->box_transform); + matrix4_scale3f(&item->box_transform, &item->box_transform, + scale.x, scale.y, 1.0f); + matrix4_translate3f(&item->box_transform, &item->box_transform, + -base_origin.x, -base_origin.y, 0.0f); + matrix4_rotate_aa4f(&item->box_transform, &item->box_transform, + 0.0f, 0.0f, 1.0f, RAD(item->rot)); + matrix4_translate3f(&item->box_transform, &item->box_transform, + item->pos.x, item->pos.y, 0.0f); + + /* ----------------------- */ + + item->last_width = width; + item->last_height = height; + + calldata_setptr(¶ms, "scene", item->parent); + calldata_setptr(¶ms, "item", item); + signal_handler_signal(item->parent->source->context.signals, + "item_transform", ¶ms); + calldata_free(¶ms); +} + +static inline bool source_size_changed(struct obs_scene_item *item) +{ + uint32_t width = obs_source_getwidth(item->source); + uint32_t height = obs_source_getheight(item->source); + + return item->last_width != width || item->last_height != height; +} + static void scene_video_render(void *data, effect_t effect) { struct obs_scene *scene = data; @@ -173,14 +308,12 @@ static void scene_video_render(void *data, effect_t effect) continue; } + if (source_size_changed(item)) + update_item_transform(item); + gs_matrix_push(); - gs_matrix_translate3f(item->origin.x, item->origin.y, 0.0f); - gs_matrix_scale3f(item->scale.x, item->scale.y, 1.0f); - gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD(-item->rot)); - gs_matrix_translate3f(-item->pos.x, -item->pos.y, 0.0f); - + gs_matrix_mul(&item->draw_transform); obs_source_video_render(item->source); - gs_matrix_pop(); item = item->next; @@ -205,12 +338,24 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t item_data) item = obs_scene_add(scene, source); + obs_data_set_default_int(item_data, "align", + OBS_ALIGN_TOP | OBS_ALIGN_LEFT); + item->rot = (float)obs_data_getdouble(item_data, "rot"); + item->align = (uint32_t)obs_data_getint(item_data, "align"); item->visible = obs_data_getbool(item_data, "visible"); - obs_data_get_vec2(item_data, "origin", &item->origin); obs_data_get_vec2(item_data, "pos", &item->pos); obs_data_get_vec2(item_data, "scale", &item->scale); + + item->bounds_type = + (enum obs_bounds_type)obs_data_getint(item_data, "bounds_type"); + item->bounds_align = + (uint32_t)obs_data_getint(item_data, "bounds_align"); + obs_data_get_vec2(item_data, "bounds", &item->bounds); + obs_source_release(source); + + update_item_transform(item); } static void scene_load(void *scene, obs_data_t settings) @@ -238,12 +383,15 @@ static void scene_save_item(obs_data_array_t array, struct obs_scene_item *item) obs_data_t item_data = obs_data_create(); const char *name = obs_source_getname(item->source); - obs_data_setstring(item_data, "name", name); - obs_data_setbool (item_data, "visible", item->visible); - obs_data_setdouble(item_data, "rot", item->rot); - obs_data_set_vec2 (item_data, "origin", &item->origin); - obs_data_set_vec2 (item_data, "pos", &item->pos); - obs_data_set_vec2 (item_data, "scale", &item->scale); + obs_data_setstring(item_data, "name", name); + obs_data_setbool (item_data, "visible", item->visible); + obs_data_setdouble(item_data, "rot", item->rot); + obs_data_set_vec2 (item_data, "pos", &item->pos); + obs_data_set_vec2 (item_data, "scale", &item->scale); + obs_data_setint (item_data, "align", (int)item->align); + obs_data_setint (item_data, "bounds_type", (int)item->bounds_type); + obs_data_setint (item_data, "bounds_align", (int)item->bounds_align); + obs_data_set_vec2 (item_data, "bounds", &item->bounds); obs_data_array_push_back(array, item_data); obs_data_release(item_data); @@ -400,7 +548,10 @@ obs_sceneitem_t obs_scene_add(obs_scene_t scene, obs_source_t source) item->visible = true; item->parent = scene; item->ref = 1; + item->align = OBS_ALIGN_TOP | OBS_ALIGN_LEFT; vec2_set(&item->scale, 1.0f, 1.0f); + matrix4_identity(&item->draw_transform); + matrix4_identity(&item->box_transform); obs_source_addref(source); obs_source_add_child(scene->source, source); @@ -495,28 +646,59 @@ obs_source_t obs_sceneitem_getsource(obs_sceneitem_t item) return item ? item->source : NULL; } +void obs_sceneitem_select(obs_sceneitem_t item, bool select) +{ + struct calldata params = {0}; + const char *command = select ? "item_select" : "item_deselect"; + + if (!item || item->selected == select) + return; + + item->selected = select; + + calldata_setptr(¶ms, "scene", item->parent); + calldata_setptr(¶ms, "item", item); + signal_handler_signal(item->parent->source->context.signals, + command, ¶ms); + + calldata_free(¶ms); +} + +bool obs_sceneitem_selected(obs_sceneitem_t item) +{ + return item ? item->selected : false; +} + void obs_sceneitem_setpos(obs_sceneitem_t item, const struct vec2 *pos) { - if (item) + if (item) { vec2_copy(&item->pos, pos); + update_item_transform(item); + } } void obs_sceneitem_setrot(obs_sceneitem_t item, float rot) { - if (item) + if (item) { item->rot = rot; -} - -void obs_sceneitem_setorigin(obs_sceneitem_t item, const struct vec2 *origin) -{ - if (item) - vec2_copy(&item->origin, origin); + update_item_transform(item); + } } void obs_sceneitem_setscale(obs_sceneitem_t item, const struct vec2 *scale) { - if (item) + if (item) { vec2_copy(&item->scale, scale); + update_item_transform(item); + } +} + +void obs_sceneitem_setalignment(obs_sceneitem_t item, uint32_t alignment) +{ + if (item) { + item->align = alignment; + update_item_transform(item); + } } static inline void signal_move_dir(struct obs_scene_item *item, @@ -583,6 +765,32 @@ void obs_sceneitem_setorder(obs_sceneitem_t item, enum order_movement movement) obs_scene_release(scene); } +void obs_sceneitem_set_bounds_type(obs_sceneitem_t item, + enum obs_bounds_type type) +{ + if (item) { + item->bounds_type = type; + update_item_transform(item); + } +} + +void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t item, + uint32_t alignment) +{ + if (item) { + item->bounds_align = alignment; + update_item_transform(item); + } +} + +void obs_sceneitem_set_bounds(obs_sceneitem_t item, const struct vec2 *bounds) +{ + if (item) { + item->bounds = *bounds; + update_item_transform(item); + } +} + void obs_sceneitem_getpos(obs_sceneitem_t item, struct vec2 *pos) { if (item) @@ -594,14 +802,72 @@ float obs_sceneitem_getrot(obs_sceneitem_t item) return item ? item->rot : 0.0f; } -void obs_sceneitem_getorigin(obs_sceneitem_t item, struct vec2 *origin) -{ - if (item) - vec2_copy(origin, &item->origin); -} - void obs_sceneitem_getscale(obs_sceneitem_t item, struct vec2 *scale) { if (item) vec2_copy(scale, &item->scale); } + +uint32_t obs_sceneitem_getalignment(obs_sceneitem_t item) +{ + return item ? item->align : 0; +} + +enum obs_bounds_type obs_sceneitem_get_bounds_type(obs_sceneitem_t item) +{ + return item ? item->bounds_type : OBS_BOUNDS_NONE; +} + +uint32_t obs_sceneitem_get_bounds_alignment(obs_sceneitem_t item) +{ + return item ? item->bounds_align : 0; +} + +void obs_sceneitem_get_bounds(obs_sceneitem_t item, struct vec2 *bounds) +{ + if (item) + *bounds = item->bounds; +} + +void obs_sceneitem_get_info(obs_sceneitem_t item, + struct obs_sceneitem_info *info) +{ + if (item && info) { + info->pos = item->pos; + info->rot = item->rot; + info->scale = item->scale; + info->alignment = item->align; + info->bounds_type = item->bounds_type; + info->bounds_alignment = item->bounds_align; + info->bounds = item->bounds; + } +} + +void obs_sceneitem_set_info(obs_sceneitem_t item, + const struct obs_sceneitem_info *info) +{ + if (item && info) { + item->pos = info->pos; + item->rot = info->rot; + item->scale = info->scale; + item->align = info->alignment; + item->bounds_type = info->bounds_type; + item->bounds_align = info->bounds_alignment; + item->bounds = info->bounds; + update_item_transform(item); + } +} + +void obs_sceneitem_get_draw_transform(obs_sceneitem_t item, + struct matrix4 *transform) +{ + if (item) + matrix4_copy(transform, &item->draw_transform); +} + +void obs_sceneitem_get_box_transform(obs_sceneitem_t item, + struct matrix4 *transform) +{ + if (item) + matrix4_copy(transform, &item->box_transform); +} diff --git a/libobs/obs-scene.h b/libobs/obs-scene.h index 5451a7f91..f827919da 100644 --- a/libobs/obs-scene.h +++ b/libobs/obs-scene.h @@ -19,6 +19,7 @@ #include "obs.h" #include "obs-internal.h" +#include "graphics/matrix4.h" /* how obs scene! */ @@ -29,11 +30,24 @@ struct obs_scene_item { struct obs_scene *parent; struct obs_source *source; bool visible; + bool selected; - struct vec2 origin; struct vec2 pos; struct vec2 scale; float rot; + uint32_t align; + + /* last width/height of the source, this is used to check whether + * ths transform needs updating */ + uint32_t last_width; + uint32_t last_height; + + struct matrix4 box_transform; + struct matrix4 draw_transform; + + enum obs_bounds_type bounds_type; + uint32_t bounds_align; + struct vec2 bounds; /* would do **prev_next, but not really great for reordering */ struct obs_scene_item *prev; diff --git a/libobs/obs.h b/libobs/obs.h index 8b25d8354..c6efffa79 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -21,6 +21,7 @@ #include "util/bmem.h" #include "graphics/graphics.h" #include "graphics/vec2.h" +#include "graphics/vec3.h" #include "media-io/audio-io.h" #include "media-io/video-io.h" #include "callback/signal.h" @@ -32,6 +33,8 @@ #include "obs-ui.h" #include "obs-properties.h" +struct matrix4; + /* opaque types */ struct obs_display; struct obs_view; @@ -85,13 +88,38 @@ enum allow_direct_render { ALLOW_DIRECT_RENDERING, }; +/** + * Used with scene items to indicate the type of bounds to use for scene items. + * Mostly determines how the image will be scaled within those bounds, or + * whether to use bounds at all. + */ +enum obs_bounds_type { + OBS_BOUNDS_NONE, /**< no bounds */ + OBS_BOUNDS_ALIGN_ONLY, /**< no scaling to bounds, align only */ + OBS_BOUNDS_SCALE_INNER, /**< scales to inner rectangle */ + OBS_BOUNDS_SCALE_OUTER, /**< scales to outer rectangle */ + OBS_BOUNDS_SCALE_TO_WIDTH, /**< scales to the width */ + OBS_BOUNDS_SCALE_TO_HEIGHT, /**< scales to the height */ + OBS_BOUNDS_STRETCH /**< stretch (ignores base scale) */ +}; + +struct obs_sceneitem_info { + struct vec2 pos; + float rot; + struct vec2 scale; + uint32_t alignment; + + enum obs_bounds_type bounds_type; + uint32_t bounds_alignment; + struct vec2 bounds; +}; + /** * Video initialization structure */ struct obs_video_info { /** - * Graphics module to use (usually "libobs-opengl" or - * "libobs-d3d11") + * Graphics module to use (usually "libobs-opengl" or "libobs-d3d11") */ const char *graphics_module; @@ -669,20 +697,44 @@ EXPORT obs_scene_t obs_sceneitem_getscene(obs_sceneitem_t item); /** Gets the source of a scene item. */ EXPORT obs_source_t obs_sceneitem_getsource(obs_sceneitem_t item); -/* Functions for gettings/setting specific oriantation of a scene item */ +EXPORT void obs_sceneitem_select(obs_sceneitem_t item, bool select); +EXPORT bool obs_sceneitem_selected(obs_sceneitem_t item); + +/* Functions for gettings/setting specific orientation of a scene item */ EXPORT void obs_sceneitem_setpos(obs_sceneitem_t item, const struct vec2 *pos); -EXPORT void obs_sceneitem_setrot(obs_sceneitem_t item, float rot); -EXPORT void obs_sceneitem_setorigin(obs_sceneitem_t item, - const struct vec2 *origin); +EXPORT void obs_sceneitem_setrot(obs_sceneitem_t item, float rot_deg); EXPORT void obs_sceneitem_setscale(obs_sceneitem_t item, const struct vec2 *scale); +EXPORT void obs_sceneitem_setalignment(obs_sceneitem_t item, + uint32_t alignment); EXPORT void obs_sceneitem_setorder(obs_sceneitem_t item, enum order_movement movement); +EXPORT void obs_sceneitem_set_bounds_type(obs_sceneitem_t item, + enum obs_bounds_type type); +EXPORT void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t item, + uint32_t alignment); +EXPORT void obs_sceneitem_set_bounds(obs_sceneitem_t item, + const struct vec2 *bounds); + EXPORT void obs_sceneitem_getpos(obs_sceneitem_t item, struct vec2 *pos); EXPORT float obs_sceneitem_getrot(obs_sceneitem_t item); -EXPORT void obs_sceneitem_getorigin(obs_sceneitem_t item, struct vec2 *center); EXPORT void obs_sceneitem_getscale(obs_sceneitem_t item, struct vec2 *scale); +EXPORT uint32_t obs_sceneitem_getalignment(obs_sceneitem_t item); + +EXPORT enum obs_bounds_type obs_sceneitem_get_bounds_type(obs_sceneitem_t item); +EXPORT uint32_t obs_sceneitem_get_bounds_alignment(obs_sceneitem_t item); +EXPORT void obs_sceneitem_get_bounds(obs_sceneitem_t item, struct vec2 *bounds); + +EXPORT void obs_sceneitem_get_info(obs_sceneitem_t item, + struct obs_sceneitem_info *info); +EXPORT void obs_sceneitem_set_info(obs_sceneitem_t item, + const struct obs_sceneitem_info *info); + +EXPORT void obs_sceneitem_get_draw_transform(obs_sceneitem_t item, + struct matrix4 *transform); +EXPORT void obs_sceneitem_get_box_transform(obs_sceneitem_t item, + struct matrix4 *transform); /* ------------------------------------------------------------------------- */