Scenes: Implement more item positioning features
There are a ridiculous number of features related to scaling and positioning due to requests by a number of people who complained that they hated the way that OBS1 would always resize their sources when the source's base size changed. There were also people who wanted more control for how the resizing was handled, or the ability to completely prevent resizing entirely if desired. So I made it so that you can optionally use a 'bounds' system, which allows you to specify different styles of controlling resizing. If disabled, the source will always automatically resize and only the base scale is applied. If enabled, you have a variety of different ways to limit/control how it can resize within the bounds, or make it so it can't resize at all. You can also control alignment within that bounding box, so you can make it so that a source always aligns to a side or corner of the box. I also added an alignment value which changes how the source is oriented relative to the position of the scene item. For example, setting bottom-right alignment will make it so that the position of the item is the bottom right corner of the source. When the source resizies, it will resize leftward and upward in that case, which solves the problem of how a source resizes relative to a desired position.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user