Browse Source

libobs: Change scene items to using relative coordinates

derrod 1 năm trước cách đây
mục cha
commit
1565ca8eb1
3 tập tin đã thay đổi với 315 bổ sung46 xóa
  1. 300 46
      libobs/obs-scene.c
  2. 2 0
      libobs/obs-scene.h
  3. 13 0
      libobs/obs.h

+ 300 - 46
libobs/obs-scene.c

@@ -24,7 +24,7 @@
 
 const struct obs_source_info group_info;
 
-static void resize_group(obs_sceneitem_t *group);
+static void resize_group(obs_sceneitem_t *group, bool scene_resize);
 static void resize_scene(obs_scene_t *scene);
 static void signal_parent(obs_scene_t *parent, const char *name,
 			  calldata_t *params);
@@ -353,6 +353,109 @@ void add_alignment(struct vec2 *v, uint32_t align, int cx, int cy)
 		v->y += (float)(cy / 2);
 }
 
+static uint32_t scene_getwidth(void *data);
+static uint32_t scene_getheight(void *data);
+
+static inline void get_scene_dimensions(const obs_sceneitem_t *item, float *x,
+					float *y)
+{
+	obs_scene_t *parent = item->parent;
+	if (!parent || parent->is_group) {
+		*x = (float)obs->video.main_mix->ovi.base_width;
+		*y = (float)obs->video.main_mix->ovi.base_height;
+	} else {
+		*x = (float)scene_getwidth(parent);
+		*y = (float)scene_getheight(parent);
+	}
+}
+
+/* Rounds absolute pixel values to next .5. */
+static inline void nudge_abs_values(struct vec2 *dst, const struct vec2 *v)
+{
+	dst->x = floorf(v->x * 2.0f + 0.5f) / 2.0f;
+	dst->y = floorf(v->y * 2.0f + 0.5f) / 2.0f;
+}
+
+static inline void pos_from_absolute(struct vec2 *dst, const struct vec2 *v,
+				     const obs_sceneitem_t *item)
+{
+	float x, y;
+	get_scene_dimensions(item, &x, &y);
+	/* Scaled so that height (y) is [-1, 1]. */
+	dst->x = (2 * v->x - x) / y;
+	dst->y = 2 * v->y / y - 1.0f;
+}
+
+static inline void pos_to_absolute(struct vec2 *dst, const struct vec2 *v,
+				   const obs_sceneitem_t *item)
+{
+	float x, y;
+	get_scene_dimensions(item, &x, &y);
+	dst->x = (v->x * y + x) / 2;
+	dst->y = (v->y * y + y) / 2;
+
+	/* In order for group resizing to behave properly they need all the precision
+	 * they can get, so do not nudge their values. */
+	if (!item->is_group && !(item->parent && item->parent->is_group))
+		nudge_abs_values(dst, dst);
+}
+
+static inline void size_from_absolute(struct vec2 *dst, const struct vec2 *v,
+				      const obs_sceneitem_t *item)
+{
+	float x, y;
+	get_scene_dimensions(item, &x, &y);
+	/* The height of the canvas is from [-1, 1], so 2.0f * aspect is the
+	 * full width (depending on aspect ratio). */
+	dst->x = (2 * v->x) / y;
+	dst->y = 2 * v->y / y;
+}
+
+static inline void size_to_absolute(struct vec2 *dst, const struct vec2 *v,
+				    const obs_sceneitem_t *item)
+{
+	float x, y;
+	get_scene_dimensions(item, &x, &y);
+	dst->x = (v->x * y) / 2;
+	dst->y = (v->y * y) / 2;
+
+	if (!item->is_group && !(item->parent && item->parent->is_group))
+		nudge_abs_values(dst, dst);
+}
+
+/* Return item's scale value scaled from original to current canvas size. */
+static inline void item_canvas_scale(struct vec2 *dst,
+				     const obs_sceneitem_t *item)
+{
+	/* Groups will themselves resize so their items do not need to be
+	 * rescaled manually. Nested scenes will use the updated canvas
+	 * resolution, so they also don't need manual adjustment. */
+	if (item->is_group || item->is_scene) {
+		vec2_copy(dst, &item->scale);
+		return;
+	}
+
+	float x, y;
+	get_scene_dimensions(item, &x, &y);
+	float scale_factor = y / item->scale_ref.y;
+	vec2_mulf(dst, &item->scale, scale_factor);
+}
+
+/* Return scale value scaled to original canvas size. */
+static inline void item_relative_scale(struct vec2 *dst, const struct vec2 *v,
+				       const obs_sceneitem_t *item)
+{
+	if (item->is_group || item->is_scene) {
+		vec2_copy(dst, v);
+		return;
+	}
+
+	float x, y;
+	get_scene_dimensions(item, &x, &y);
+	float scale_factor = item->scale_ref.y / y;
+	vec2_mulf(dst, v, scale_factor);
+}
+
 static inline bool crop_to_bounds(const struct obs_scene_item *item)
 {
 	return item->crop_to_bounds &&
@@ -365,15 +468,17 @@ static void calculate_bounds_data(struct obs_scene_item *item,
 				  struct vec2 *origin, struct vec2 *scale,
 				  uint32_t *cx, uint32_t *cy)
 {
+	struct vec2 bounds;
+	size_to_absolute(&bounds, &item->bounds, item);
 	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 bounds_aspect = bounds.x / bounds.y;
 	uint32_t bounds_type = item->bounds_type;
 	float width_diff, height_diff;
 
 	if (item->bounds_type == OBS_BOUNDS_MAX_ONLY)
-		if (width > item->bounds.x || height > item->bounds.y)
+		if (width > bounds.x || height > bounds.y)
 			bounds_type = OBS_BOUNDS_SCALE_INNER;
 
 	if (bounds_type == OBS_BOUNDS_SCALE_INNER ||
@@ -384,30 +489,29 @@ static void calculate_bounds_data(struct obs_scene_item *item,
 		if (item->bounds_type == OBS_BOUNDS_SCALE_OUTER)
 			use_width = !use_width;
 
-		mul = use_width ? item->bounds.x / width
-				: item->bounds.y / height;
+		mul = use_width ? bounds.x / width : bounds.y / height;
 
 		vec2_mulf(scale, scale, mul);
 
 	} else if (bounds_type == OBS_BOUNDS_SCALE_TO_WIDTH) {
-		vec2_mulf(scale, scale, item->bounds.x / width);
+		vec2_mulf(scale, scale, bounds.x / width);
 
 	} else if (bounds_type == OBS_BOUNDS_SCALE_TO_HEIGHT) {
-		vec2_mulf(scale, scale, item->bounds.y / height);
+		vec2_mulf(scale, scale, bounds.y / height);
 
 	} else if (bounds_type == OBS_BOUNDS_STRETCH) {
-		scale->x = copysignf(item->bounds.x / (float)(*cx), scale->x);
-		scale->y = copysignf(item->bounds.y / (float)(*cy), scale->y);
+		scale->x = copysignf(bounds.x / (float)(*cx), scale->x);
+		scale->y = copysignf(bounds.y / (float)(*cy), scale->y);
 	}
 
 	width = (float)(*cx) * scale->x;
 	height = (float)(*cy) * scale->y;
 
 	/* Disregards flip when calculating size diff */
-	width_diff = item->bounds.x - fabsf(width);
-	height_diff = item->bounds.y - fabsf(height);
-	*cx = (uint32_t)item->bounds.x;
-	*cy = (uint32_t)item->bounds.y;
+	width_diff = bounds.x - fabsf(width);
+	height_diff = bounds.y - fabsf(height);
+	*cx = (uint32_t)bounds.x;
+	*cy = (uint32_t)bounds.y;
 
 	add_alignment(origin, item->bounds_align, (int)-width_diff,
 		      (int)-height_diff);
@@ -490,6 +594,34 @@ static inline uint32_t calc_cy(const struct obs_scene_item *item,
 	return (crop_cy > height) ? 2 : (height - crop_cy);
 }
 
+#ifdef _DEBUG
+static inline void log_matrix(const struct matrix4 *mat, const char *name)
+{
+	blog(LOG_DEBUG,
+	     "Matrix \"%s\":\n"
+	     "┏ %9.4f %9.4f %9.4f %9.4f ┓\n"
+	     "┃ %9.4f %9.4f %9.4f %9.4f ┃\n"
+	     "┃ %9.4f %9.4f %9.4f %9.4f ┃\n"
+	     "┗ %9.4f %9.4f %9.4f %9.4f ┛",
+	     name, mat->x.x, mat->x.y, mat->x.z, mat->x.w, mat->y.x, mat->y.y,
+	     mat->y.z, mat->y.w, mat->z.x, mat->z.y, mat->z.z, mat->z.w,
+	     mat->t.x, mat->t.y, mat->t.z, mat->t.w);
+}
+#endif
+
+static inline void update_nested_scene_crop(struct obs_scene_item *item,
+					    uint32_t width, uint32_t height)
+{
+	/* Use last size and new size to calculate factor to adjust crop by. */
+	float scale_x = (float)width / (float)item->last_width;
+	float scale_y = (float)height / (float)item->last_height;
+
+	item->crop.left = (int)((float)item->crop.left * scale_x);
+	item->crop.right = (int)((float)item->crop.right * scale_x);
+	item->crop.top = (int)((float)item->crop.top * scale_y);
+	item->crop.bottom = (int)((float)item->crop.bottom * scale_y);
+}
+
 static void update_item_transform(struct obs_scene_item *item, bool update_tex)
 {
 	uint32_t width;
@@ -499,6 +631,7 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex)
 	struct vec2 base_origin;
 	struct vec2 origin;
 	struct vec2 scale;
+	struct vec2 position;
 	struct calldata params;
 	uint8_t stack[128];
 
@@ -510,9 +643,13 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex)
 
 	width = obs_source_get_width(item->source);
 	height = obs_source_get_height(item->source);
+
+	/* Adjust crop on nested scenes (if any) */
+	if (update_tex && item->is_scene)
+		update_nested_scene_crop(item, width, height);
+
 	cx = calc_cx(item, width);
 	cy = calc_cy(item, height);
-	scale = item->scale;
 	item->last_width = width;
 	item->last_height = height;
 
@@ -522,6 +659,9 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex)
 	vec2_zero(&base_origin);
 	vec2_zero(&origin);
 
+	item_canvas_scale(&scale, item);
+	pos_to_absolute(&position, &item->pos, item);
+
 	/* ----------------------- */
 
 	if (item->bounds_type != OBS_BOUNDS_NONE) {
@@ -541,17 +681,23 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex)
 	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);
+			    position.x, position.y, 0.0f);
+
+#ifdef _DEBUG
+	blog(LOG_DEBUG, "Transform updated for \"%s\":",
+	     obs_source_get_name(item->source));
+	log_matrix(&item->draw_transform, "draw_transform");
+#endif
 
 	item->output_scale = scale;
 
 	/* ----------------------- */
 
 	if (item->bounds_type != OBS_BOUNDS_NONE) {
-		vec2_copy(&scale, &item->bounds);
+		size_to_absolute(&scale, &item->bounds, item);
 	} else {
-		scale.x = (float)width * item->scale.x;
-		scale.y = (float)height * item->scale.y;
+		scale.x *= (float)width;
+		scale.y *= (float)height;
 	}
 
 	item->box_scale = scale;
@@ -566,7 +712,11 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex)
 	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);
+			    position.x, position.y, 0.0f);
+
+#ifdef _DEBUG
+	log_matrix(&item->draw_transform, "box_transform");
+#endif
 
 	/* ----------------------- */
 
@@ -966,7 +1116,7 @@ static void update_transforms_and_prune_sources(
 	}
 
 	if (rebuild_group && group_sceneitem)
-		resize_group(group_sceneitem);
+		resize_group(group_sceneitem, scene_size_changed);
 }
 
 static inline bool scene_size_changed(obs_scene_t *scene)
@@ -1106,8 +1256,18 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
 	item->align = (uint32_t)obs_data_get_int(item_data, "align");
 	visible = obs_data_get_bool(item_data, "visible");
 	lock = obs_data_get_bool(item_data, "locked");
-	obs_data_get_vec2(item_data, "pos", &item->pos);
-	obs_data_get_vec2(item_data, "scale", &item->scale);
+
+	if (obs_data_has_user_value(item_data, "pos_rel") &&
+	    obs_data_has_user_value(item_data, "scale_rel") &&
+	    obs_data_has_user_value(item_data, "scale_ref")) {
+		obs_data_get_vec2(item_data, "pos_rel", &item->pos);
+		obs_data_get_vec2(item_data, "scale_rel", &item->scale);
+		obs_data_get_vec2(item_data, "scale_ref", &item->scale_ref);
+	} else {
+		obs_data_get_vec2(item_data, "pos", &item->pos);
+		pos_from_absolute(&item->pos, &item->pos, item);
+		obs_data_get_vec2(item_data, "scale", &item->scale);
+	}
 
 	obs_data_release(item->private_settings);
 	item->private_settings =
@@ -1125,6 +1285,13 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
 	item->crop_to_bounds = obs_data_get_bool(item_data, "bounds_crop");
 	obs_data_get_vec2(item_data, "bounds", &item->bounds);
 
+	if (obs_data_has_user_value(item_data, "bounds_rel")) {
+		obs_data_get_vec2(item_data, "bounds_rel", &item->bounds);
+	} else {
+		obs_data_get_vec2(item_data, "bounds", &item->bounds);
+		size_from_absolute(&item->bounds, &item->bounds, item);
+	}
+
 	item->crop.left = (uint32_t)obs_data_get_int(item_data, "crop_left");
 	item->crop.top = (uint32_t)obs_data_get_int(item_data, "crop_top");
 	item->crop.right = (uint32_t)obs_data_get_int(item_data, "crop_right");
@@ -1241,18 +1408,29 @@ static void scene_save_item(obs_data_array_t *array,
 		get_ungrouped_transform(backup_group, &pos, &scale, &rot);
 	}
 
+	/* For backwards compatibility, also store absolute values. */
+	struct vec2 tmp_abs;
+
 	obs_data_set_string(item_data, "name", name);
 	obs_data_set_string(item_data, "source_uuid", src_uuid);
 	obs_data_set_bool(item_data, "visible", item->user_visible);
 	obs_data_set_bool(item_data, "locked", item->locked);
 	obs_data_set_double(item_data, "rot", rot);
-	obs_data_set_vec2(item_data, "pos", &pos);
-	obs_data_set_vec2(item_data, "scale", &scale);
+	pos_to_absolute(&tmp_abs, &pos, item);
+	obs_data_set_vec2(item_data, "pos", &tmp_abs);
+	obs_data_set_vec2(item_data, "pos_rel", &pos);
+	item_canvas_scale(&tmp_abs, item);
+	obs_data_set_vec2(item_data, "scale", &tmp_abs);
+	obs_data_set_vec2(item_data, "scale_rel", &scale);
+	obs_data_set_vec2(item_data, "scale_ref", &item->scale_ref);
 	obs_data_set_int(item_data, "align", (int)item->align);
 	obs_data_set_int(item_data, "bounds_type", (int)item->bounds_type);
 	obs_data_set_int(item_data, "bounds_align", (int)item->bounds_align);
 	obs_data_set_bool(item_data, "bounds_crop", item->crop_to_bounds);
 	obs_data_set_vec2(item_data, "bounds", &item->bounds);
+	size_to_absolute(&tmp_abs, &item->bounds, item);
+	obs_data_set_vec2(item_data, "bounds", &tmp_abs);
+	obs_data_set_vec2(item_data, "bounds_rel", &item->bounds);
 	obs_data_set_int(item_data, "crop_left", (int)item->crop.left);
 	obs_data_set_int(item_data, "crop_top", (int)item->crop.top);
 	obs_data_set_int(item_data, "crop_right", (int)item->crop.right);
@@ -2339,10 +2517,13 @@ static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene,
 	item->user_visible = true;
 	item->locked = false;
 	item->is_group = strcmp(source->info.id, group_info.id) == 0;
+	item->is_scene = strcmp(source->info.id, scene_info.id) == 0;
 	item->private_settings = obs_data_create();
 	item->toggle_visibility = OBS_INVALID_HOTKEY_PAIR_ID;
 	os_atomic_set_long(&item->active_refs, 1);
 	vec2_set(&item->scale, 1.0f, 1.0f);
+	vec2_set(&item->scale_ref, (float)scene_getwidth(scene),
+		 (float)scene_getheight(scene));
 	matrix4_identity(&item->draw_transform);
 	matrix4_identity(&item->box_transform);
 
@@ -2721,6 +2902,15 @@ bool obs_sceneitem_selected(const obs_sceneitem_t *item)
 	} while (false)
 
 void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos)
+{
+	if (item) {
+		pos_from_absolute(&item->pos, pos, item);
+		do_update_transform(item);
+	}
+}
+
+void obs_sceneitem_set_relative_pos(obs_sceneitem_t *item,
+				    const struct vec2 *pos)
 {
 	if (item) {
 		vec2_copy(&item->pos, pos);
@@ -2739,7 +2929,7 @@ void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot)
 void obs_sceneitem_set_scale(obs_sceneitem_t *item, const struct vec2 *scale)
 {
 	if (item) {
-		vec2_copy(&item->scale, scale);
+		item_relative_scale(&item->scale, scale, item);
 		do_update_transform(item);
 	}
 }
@@ -2903,12 +3093,28 @@ void obs_sceneitem_set_bounds_crop(obs_sceneitem_t *item, bool crop)
 void obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds)
 {
 	if (item) {
-		item->bounds = *bounds;
+		size_from_absolute(&item->bounds, bounds, item);
+		do_update_transform(item);
+	}
+}
+
+void obs_sceneitem_set_relative_bounds(obs_sceneitem_t *item,
+				       const struct vec2 *bounds)
+{
+	if (item) {
+		vec2_copy(&item->bounds, bounds);
 		do_update_transform(item);
 	}
 }
 
 void obs_sceneitem_get_pos(const obs_sceneitem_t *item, struct vec2 *pos)
+{
+	if (item)
+		pos_to_absolute(pos, &item->pos, item);
+}
+
+void obs_sceneitem_get_relative_pos(const obs_sceneitem_t *item,
+				    struct vec2 *pos)
 {
 	if (item)
 		vec2_copy(pos, &item->pos);
@@ -2922,7 +3128,7 @@ float obs_sceneitem_get_rot(const obs_sceneitem_t *item)
 void obs_sceneitem_get_scale(const obs_sceneitem_t *item, struct vec2 *scale)
 {
 	if (item)
-		vec2_copy(scale, &item->scale);
+		item_canvas_scale(scale, item);
 }
 
 uint32_t obs_sceneitem_get_alignment(const obs_sceneitem_t *item)
@@ -2948,19 +3154,27 @@ bool obs_sceneitem_get_bounds_crop(const obs_sceneitem_t *item)
 void obs_sceneitem_get_bounds(const obs_sceneitem_t *item, struct vec2 *bounds)
 {
 	if (item)
-		*bounds = item->bounds;
+		size_to_absolute(bounds, &item->bounds, item);
+}
+
+void obs_sceneitem_get_relative_bounds(const obs_sceneitem_t *item,
+				       struct vec2 *bounds)
+{
+	if (item)
+		vec2_copy(bounds, &item->bounds);
 }
 
 void obs_sceneitem_get_info(const obs_sceneitem_t *item,
 			    struct obs_transform_info *info)
 {
 	if (item && info) {
-		info->pos = item->pos;
+		pos_to_absolute(&info->pos, &item->pos, item);
 		info->rot = item->rot;
-		info->scale = item->scale;
+		item_canvas_scale(&info->scale, item);
 		info->alignment = item->align;
 		info->bounds_type = item->bounds_type;
 		info->bounds_alignment = item->bounds_align;
+		size_to_absolute(&info->bounds, &item->bounds, item);
 		info->bounds = item->bounds;
 	}
 }
@@ -2969,29 +3183,45 @@ void obs_sceneitem_get_info2(const obs_sceneitem_t *item,
 			     struct obs_transform_info *info)
 {
 	if (item && info) {
-		info->pos = item->pos;
+		pos_to_absolute(&info->pos, &item->pos, item);
 		info->rot = item->rot;
-		info->scale = item->scale;
+		item_canvas_scale(&info->scale, item);
 		info->alignment = item->align;
 		info->bounds_type = item->bounds_type;
 		info->bounds_alignment = item->bounds_align;
+		size_to_absolute(&info->bounds, &item->bounds, item);
 		info->bounds = item->bounds;
 		info->crop_to_bounds = item->crop_to_bounds;
 	}
 }
 
+void obs_sceneitem_get_info3(const obs_sceneitem_t *item,
+			     struct obs_transform_info *info)
+{
+	if (item && info) {
+		info->pos = item->pos;
+		info->rot = item->rot;
+		item_canvas_scale(&info->scale, item);
+		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_transform_info *info)
 {
 	if (item && info) {
-		item->pos = info->pos;
+		pos_from_absolute(&item->pos, &info->pos, item);
 		item->rot = info->rot;
 		if (isfinite(info->scale.x) && isfinite(info->scale.y)) {
-			item->scale = info->scale;
+			item_relative_scale(&item->scale, &info->scale, item);
 		}
 		item->align = info->alignment;
 		item->bounds_type = info->bounds_type;
 		item->bounds_align = info->bounds_alignment;
+		size_from_absolute(&item->bounds, &info->bounds, item);
 		item->bounds = info->bounds;
 		do_update_transform(item);
 	}
@@ -3001,20 +3231,38 @@ void obs_sceneitem_set_info2(obs_sceneitem_t *item,
 			     const struct obs_transform_info *info)
 {
 	if (item && info) {
-		item->pos = info->pos;
+		pos_from_absolute(&item->pos, &info->pos, item);
 		item->rot = info->rot;
 		if (isfinite(info->scale.x) && isfinite(info->scale.y)) {
-			item->scale = info->scale;
+			item_relative_scale(&item->scale, &info->scale, item);
 		}
 		item->align = info->alignment;
 		item->bounds_type = info->bounds_type;
 		item->bounds_align = info->bounds_alignment;
+		size_from_absolute(&item->bounds, &info->bounds, item);
 		item->bounds = info->bounds;
 		item->crop_to_bounds = info->crop_to_bounds;
 		do_update_transform(item);
 	}
 }
 
+void obs_sceneitem_set_info3(obs_sceneitem_t *item,
+			     const struct obs_transform_info *info)
+{
+	if (item && info) {
+		item->pos = info->pos;
+		item->rot = info->rot;
+		if (isfinite(info->scale.x) && isfinite(info->scale.y)) {
+			item_relative_scale(&item->scale, &info->scale, item);
+		}
+		item->align = info->alignment;
+		item->bounds_type = info->bounds_type;
+		item->bounds_align = info->bounds_alignment;
+		item->bounds = info->bounds;
+		do_update_transform(item);
+	}
+}
+
 void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item,
 				      struct matrix4 *transform)
 {
@@ -3483,10 +3731,15 @@ static bool resize_scene_base(obs_scene_t *scene, struct vec2 *minv,
 	}
 
 	item = scene->first_item;
-	while (item) {
-		vec2_sub(&item->pos, &item->pos, minv);
-		update_item_transform(item, false);
-		item = item->next;
+	if (item) {
+		struct vec2 minv_rel;
+		size_from_absolute(&minv_rel, minv, item);
+
+		while (item) {
+			vec2_sub(&item->pos, &item->pos, &minv_rel);
+			update_item_transform(item, false);
+			item = item->next;
+		}
 	}
 
 	vec2_sub(scale, maxv, minv);
@@ -3504,7 +3757,7 @@ static void resize_scene(obs_scene_t *scene)
 }
 
 /* assumes group scene and parent scene is locked */
-static void resize_group(obs_sceneitem_t *group)
+static void resize_group(obs_sceneitem_t *group, bool scene_resize)
 {
 	obs_scene_t *scene = group->source->context.data;
 	struct vec2 minv;
@@ -3517,7 +3770,7 @@ static void resize_group(obs_sceneitem_t *group)
 	if (!resize_scene_base(scene, &minv, &maxv, &scale))
 		return;
 
-	if (group->bounds_type == OBS_BOUNDS_NONE) {
+	if (group->bounds_type == OBS_BOUNDS_NONE && !scene_resize) {
 		struct vec2 new_pos;
 
 		if ((group->align & OBS_ALIGN_LEFT) != 0)
@@ -3535,6 +3788,7 @@ static void resize_group(obs_sceneitem_t *group)
 			new_pos.y = (maxv.y - minv.y) * 0.5f + minv.y;
 
 		transform_val(&new_pos, &group->draw_transform);
+		pos_from_absolute(&new_pos, &new_pos, group);
 		vec2_copy(&group->pos, &new_pos);
 	}
 
@@ -3602,7 +3856,7 @@ obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene, const char *name,
 		apply_group_transform(items[idx], item);
 	}
 	items[0]->prev = NULL;
-	resize_group(item);
+	resize_group(item, false);
 	full_unlock(sub_scene);
 	full_unlock(scene);
 
@@ -3749,7 +4003,7 @@ void obs_sceneitem_group_add_item(obs_sceneitem_t *group, obs_sceneitem_t *item)
 
 	apply_group_transform(item, group);
 
-	resize_group(group);
+	resize_group(group, false);
 
 	full_unlock(groupscene);
 	full_unlock(scene);
@@ -3778,7 +4032,7 @@ void obs_sceneitem_group_remove_item(obs_sceneitem_t *group,
 	detach_sceneitem(item);
 	attach_sceneitem(scene, item, NULL);
 
-	resize_group(group);
+	resize_group(group, false);
 
 	full_unlock(groupscene);
 	full_unlock(scene);
@@ -3961,7 +4215,7 @@ bool obs_scene_reorder_items2(obs_scene_t *scene,
 				sub_prev = sub_item;
 			}
 
-			resize_group(info->item);
+			resize_group(info->item, false);
 			full_unlock(sub_scene);
 			obs_scene_release(sub_scene);
 		}

+ 2 - 0
libobs/obs-scene.h

@@ -32,6 +32,7 @@ struct obs_scene_item {
 	volatile bool removed;
 
 	bool is_group;
+	bool is_scene;
 	bool update_transform;
 	bool update_group_resize;
 
@@ -52,6 +53,7 @@ struct obs_scene_item {
 
 	struct vec2 pos;
 	struct vec2 scale;
+	struct vec2 scale_ref;
 	float rot;
 	uint32_t align;
 

+ 13 - 0
libobs/obs.h

@@ -1855,6 +1855,8 @@ EXPORT bool obs_sceneitem_set_locked(obs_sceneitem_t *item, bool lock);
 /* Functions for getting/setting specific orientation of a scene item */
 EXPORT void obs_sceneitem_set_pos(obs_sceneitem_t *item,
 				  const struct vec2 *pos);
+EXPORT void obs_sceneitem_set_relative_pos(obs_sceneitem_t *item,
+					   const struct vec2 *pos);
 EXPORT void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot_deg);
 EXPORT void obs_sceneitem_set_scale(obs_sceneitem_t *item,
 				    const struct vec2 *scale);
@@ -1871,11 +1873,15 @@ EXPORT void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item,
 EXPORT void obs_sceneitem_set_bounds_crop(obs_sceneitem_t *item, bool crop);
 EXPORT void obs_sceneitem_set_bounds(obs_sceneitem_t *item,
 				     const struct vec2 *bounds);
+EXPORT void obs_sceneitem_set_relative_bounds(obs_sceneitem_t *item,
+					      const struct vec2 *bounds);
 
 EXPORT int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item);
 
 EXPORT void obs_sceneitem_get_pos(const obs_sceneitem_t *item,
 				  struct vec2 *pos);
+EXPORT void obs_sceneitem_get_relative_pos(const obs_sceneitem_t *item,
+					   struct vec2 *pos);
 EXPORT float obs_sceneitem_get_rot(const obs_sceneitem_t *item);
 EXPORT void obs_sceneitem_get_scale(const obs_sceneitem_t *item,
 				    struct vec2 *scale);
@@ -1887,6 +1893,9 @@ EXPORT uint32_t obs_sceneitem_get_bounds_alignment(const obs_sceneitem_t *item);
 EXPORT bool obs_sceneitem_get_bounds_crop(const obs_sceneitem_t *item);
 EXPORT void obs_sceneitem_get_bounds(const obs_sceneitem_t *item,
 				     struct vec2 *bounds);
+EXPORT void obs_sceneitem_get_relative_bounds(const obs_sceneitem_t *item,
+					      struct vec2 *bounds);
+
 OBS_DEPRECATED EXPORT void
 obs_sceneitem_get_info(const obs_sceneitem_t *item,
 		       struct obs_transform_info *info);
@@ -1897,6 +1906,10 @@ EXPORT void obs_sceneitem_get_info2(const obs_sceneitem_t *item,
 				    struct obs_transform_info *info);
 EXPORT void obs_sceneitem_set_info2(obs_sceneitem_t *item,
 				    const struct obs_transform_info *info);
+EXPORT void obs_sceneitem_get_info3(const obs_sceneitem_t *item,
+				    struct obs_transform_info *info);
+EXPORT void obs_sceneitem_set_info3(obs_sceneitem_t *item,
+				    const struct obs_transform_info *info);
 
 EXPORT void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item,
 					     struct matrix4 *transform);