Merge pull request #114 from jp9000/scene-editing

Scene editing
This commit is contained in:
Jim
2014-06-15 20:44:37 -07:00
18 changed files with 2565 additions and 76 deletions

View File

@@ -15,7 +15,7 @@ MoveUp="Move Up"
MoveDown="Move Down"
Settings="Settings"
Exit="Exit"
Volume="Volume"
Mixer="Mixer"
Browse="Browse"
Mono="Mono"
Stereo="Stereo"
@@ -66,6 +66,34 @@ Basic.Main.DefaultSceneName.Text="Scene %1"
Basic.SourceSelect.CreateNew="Create new"
Basic.SourceSelect.AddExisting="Add Existing"
# transform window
Basic.TransformWindow="Scene Item Transform"
Basic.TransformWindow.Position="Position"
Basic.TransformWindow.Rotation="Rotation"
Basic.TransformWindow.Scale="Scale"
Basic.TransformWindow.Alignment="Positional Alignment"
Basic.TransformWindow.BoundsType="Bounding Box Type"
Basic.TransformWindow.BoundsAlignment="Alignment in Bounding Box"
Basic.TransformWindow.Bounds="Bounding Box Size"
Basic.TransformWindow.Alignment.TopLeft="Top Left"
Basic.TransformWindow.Alignment.TopCenter="Top Center"
Basic.TransformWindow.Alignment.TopRight="Top Right"
Basic.TransformWindow.Alignment.CenterLeft="Center Left"
Basic.TransformWindow.Alignment.Center="Center"
Basic.TransformWindow.Alignment.CenterRight="Center Right"
Basic.TransformWindow.Alignment.BottomLeft="Bottom Left"
Basic.TransformWindow.Alignment.BottomCenter="Bottom Center"
Basic.TransformWindow.Alignment.BottomRight="Bottom Right"
Basic.TransformWindow.BoundsType.None="No bounds"
Basic.TransformWindow.BoundsType.MaxOnly="Maximum size only"
Basic.TransformWindow.BoundsType.ScaleInner="Scale to inner bounds"
Basic.TransformWindow.BoundsType.ScaleOuter="Scale to outer bounds"
Basic.TransformWindow.BoundsType.ScaleToWidth="Scale to width of bounds"
Basic.TransformWindow.BoundsType.ScaleToHeight="Scale to height of bounds"
Basic.TransformWindow.BoundsType.Stretch="Stretch to bounds"
# no scene warning
Basic.Main.AddSourceHelp.Title="Cannot Add Source"
Basic.Main.AddSourceHelp.Text="You need to have at least 1 scene to add a source."
@@ -86,6 +114,24 @@ Basic.MainMenu.File.Import="&Import"
Basic.MainMenu.File.Settings="&Settings"
Basic.MainMenu.File.Exit="E&xit"
# basic mode edit menu
Basic.MainMenu.Edit="&Edit"
Basic.MainMenu.Edit.Undo="&Undo"
Basic.MainMenu.Edit.Redo="&Redo"
Basic.MainMenu.Edit.UndoAction="&Undo $1"
Basic.MainMenu.Edit.RedoAction="&Redo $1"
Basic.MainMenu.Edit.Transform="&Transform"
Basic.MainMenu.Edit.Transform.EditTransform="&Edit Transform..."
Basic.MainMenu.Edit.Transform.ResetTransform="&Reset Transform"
Basic.MainMenu.Edit.Transform.Rotate90CW="Rotate 90 degrees CW"
Basic.MainMenu.Edit.Transform.Rotate90CCW="Rotate 90 degrees CCW"
Basic.MainMenu.Edit.Transform.Rotate180="Rotate 180 degrees"
Basic.MainMenu.Edit.Transform.FlipHorizontal="Flip &Horizontal"
Basic.MainMenu.Edit.Transform.FlipVertical="Flip &Vertical"
Basic.MainMenu.Edit.Transform.FitToScreen="&Fit to screen"
Basic.MainMenu.Edit.Transform.StretchToScreen="&Stretch to screen"
Basic.MainMenu.Edit.Transform.CenterToScreen="&Center to screen"
# basic mode help menu
Basic.MainMenu.Help="&Help"
Basic.MainMenu.Help.Logs="&Log Files"

View File

@@ -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) | \

View File

@@ -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

View File

@@ -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,143 @@ 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;
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)
bounds_type = OBS_BOUNDS_SCALE_INNER;
if (bounds_type == OBS_BOUNDS_SCALE_INNER ||
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 (bounds_type == OBS_BOUNDS_SCALE_TO_WIDTH) {
vec2_mulf(scale, scale, item->bounds.x / width);
} else if (bounds_type == OBS_BOUNDS_SCALE_TO_HEIGHT) {
vec2_mulf(scale, scale, item->bounds.y / height);
} else if (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(&params, "scene", item->parent);
calldata_setptr(&params, "item", item);
signal_handler_signal(item->parent->source->context.signals,
"item_transform", &params);
calldata_free(&params);
}
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 +313,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 +343,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 +388,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 +553,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 +651,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(&params, "scene", item->parent);
calldata_setptr(&params, "item", item);
signal_handler_signal(item->parent->source->context.signals,
command, &params);
calldata_free(&params);
}
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 +770,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 +807,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);
}

View File

@@ -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;

View File

@@ -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_STRETCH, /**< stretch (ignores base scale) */
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_MAX_ONLY, /**< no scaling, maximum size only */
};
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);
/* ------------------------------------------------------------------------- */

View File

@@ -57,6 +57,8 @@ set(obs_SOURCES
window-basic-settings.cpp
window-basic-properties.cpp
window-basic-source-select.cpp
window-basic-transform.cpp
window-basic-preview.cpp
window-namedialog.cpp
window-log-reply.cpp
properties-view.cpp
@@ -71,6 +73,8 @@ set(obs_HEADERS
window-basic-settings.hpp
window-basic-properties.hpp
window-basic-source-select.hpp
window-basic-transform.hpp
window-basic-preview.hpp
window-namedialog.hpp
window-log-reply.hpp
properties-view.hpp
@@ -83,6 +87,7 @@ set(obs_UI
forms/NameDialog.ui
forms/OBSLogReply.ui
forms/OBSBasic.ui
forms/OBSBasicTransform.ui
forms/OBSBasicSettings.ui
forms/OBSBasicSourceSelect.ui
forms/OBSBasicProperties.ui)

View File

@@ -29,7 +29,7 @@
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="OBSQTDisplay" name="preview" native="true">
<widget class="OBSBasicPreview" name="preview" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
@@ -305,7 +305,7 @@
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Volume</string>
<string>Mixer</string>
</property>
</widget>
</item>
@@ -463,7 +463,35 @@
</widget>
<addaction name="menuLogFiles"/>
</widget>
<widget class="QMenu" name="menuBasic_MainMenu_Edit">
<property name="title">
<string>Basic.MainMenu.Edit</string>
</property>
<widget class="QMenu" name="menuBasic_MainMenu_Edit_Transform">
<property name="title">
<string>Basic.MainMenu.Edit.Transform</string>
</property>
<addaction name="actionEditTransform"/>
<addaction name="actionResetTransform"/>
<addaction name="separator"/>
<addaction name="actionRotate90CW"/>
<addaction name="actionRotate90CCW"/>
<addaction name="actionRotate180"/>
<addaction name="separator"/>
<addaction name="actionFlipHorizontal"/>
<addaction name="actionFlipVertical"/>
<addaction name="separator"/>
<addaction name="actionFitToScreen"/>
<addaction name="actionStretchToScreen"/>
<addaction name="actionCenterToScreen"/>
</widget>
<addaction name="actionUndo"/>
<addaction name="actionRedo"/>
<addaction name="separator"/>
<addaction name="menuBasic_MainMenu_Edit_Transform"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menuBasic_MainMenu_Edit"/>
<addaction name="menuBasic_MainMenu_Help"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
@@ -610,12 +638,90 @@
<string>Basic.MainMenu.Help.Logs.UploadCurrentLog</string>
</property>
</action>
<action name="actionUndo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Basic.MainMenu.Edit.Undo</string>
</property>
</action>
<action name="actionRedo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Basic.MainMenu.Edit.Redo</string>
</property>
</action>
<action name="actionEditTransform">
<property name="text">
<string>Basic.MainMenu.Edit.Transform.EditTransform</string>
</property>
</action>
<action name="actionRotate90CW">
<property name="text">
<string>Basic.MainMenu.Edit.Transform.Rotate90CW</string>
</property>
</action>
<action name="actionRotate90CCW">
<property name="text">
<string>Basic.MainMenu.Edit.Transform.Rotate90CCW</string>
</property>
</action>
<action name="actionRotate180">
<property name="text">
<string>Basic.MainMenu.Edit.Transform.Rotate180</string>
</property>
</action>
<action name="actionFitToScreen">
<property name="text">
<string>Basic.MainMenu.Edit.Transform.FitToScreen</string>
</property>
<property name="shortcut">
<string>Ctrl+F</string>
</property>
</action>
<action name="actionStretchToScreen">
<property name="text">
<string>Basic.MainMenu.Edit.Transform.StretchToScreen</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</action>
<action name="actionResetTransform">
<property name="text">
<string>Basic.MainMenu.Edit.Transform.ResetTransform</string>
</property>
<property name="shortcut">
<string>Ctrl+R</string>
</property>
</action>
<action name="actionCenterToScreen">
<property name="text">
<string>Basic.MainMenu.Edit.Transform.CenterToScreen</string>
</property>
<property name="shortcut">
<string>Ctrl+C</string>
</property>
</action>
<action name="actionFlipHorizontal">
<property name="text">
<string>Basic.MainMenu.Edit.Transform.FlipHorizontal</string>
</property>
</action>
<action name="actionFlipVertical">
<property name="text">
<string>Basic.MainMenu.Edit.Transform.FlipVertical</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>OBSQTDisplay</class>
<class>OBSBasicPreview</class>
<extends>QWidget</extends>
<header>qt-display.hpp</header>
<header>window-basic-preview.hpp</header>
<container>1</container>
</customwidget>
</customwidgets>

View File

@@ -0,0 +1,462 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OBSBasicTransform</class>
<widget class="QDialog" name="OBSBasicTransform">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>564</width>
<height>241</height>
</rect>
</property>
<property name="windowTitle">
<string>Basic.TransformWindow</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Basic.TransformWindow.Position</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QWidget" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QDoubleSpinBox" name="positionX">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<double>-9001.000000000000000</double>
</property>
<property name="maximum">
<double>9001.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="positionY">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<double>-9001.000000000000000</double>
</property>
<property name="maximum">
<double>9001.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Basic.TransformWindow.Rotation</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="rotation">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<double>-360.000000000000000</double>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Basic.TransformWindow.Scale</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QWidget" name="widget_2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QDoubleSpinBox" name="scaleX">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<double>-9001.000000000000000</double>
</property>
<property name="maximum">
<double>9001.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="scaleY">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<double>-9001.000000000000000</double>
</property>
<property name="maximum">
<double>9001.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Basic.TransformWindow.Alignment</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="align">
<property name="currentText">
<string>Basic.TransformWindow.Alignment.TopLeft</string>
</property>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.TopLeft</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.TopCenter</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.TopRight</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.CenterLeft</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.Center</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.CenterRight</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.BottomLeft</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.BottomCenter</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.BottomRight</string>
</property>
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Basic.TransformWindow.BoundsType</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="boundsType">
<item>
<property name="text">
<string>Basic.TransformWindow.BoundsType.None</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.BoundsType.Stretch</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.BoundsType.ScaleInner</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.BoundsType.ScaleOuter</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.BoundsType.ScaleToWidth</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.BoundsType.ScaleToHeight</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.BoundsType.MaxOnly</string>
</property>
</item>
</widget>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Basic.TransformWindow.BoundsAlignment</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Basic.TransformWindow.Bounds</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QWidget" name="widget_3" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QDoubleSpinBox" name="boundsWidth">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>9001.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="boundsHeight">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>9001.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="boundsAlign">
<property name="enabled">
<bool>false</bool>
</property>
<property name="currentText">
<string>Basic.TransformWindow.Alignment.TopLeft</string>
</property>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.TopLeft</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.TopCenter</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.TopRight</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.CenterLeft</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.Center</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.CenterRight</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.BottomLeft</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.BottomCenter</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.TransformWindow.Alignment.BottomRight</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -26,6 +26,7 @@
#include <util/dstr.h>
#include <util/util.hpp>
#include <util/platform.h>
#include <graphics/math-defs.h>
#include "obs-app.hpp"
#include "platform.hpp"
@@ -47,6 +48,8 @@
#include <QScreen>
#include <QWindow>
#define PREVIEW_EDGE_SIZE 10
using namespace std;
Q_DECLARE_METATYPE(OBSScene);
@@ -55,15 +58,6 @@ Q_DECLARE_METATYPE(order_movement);
OBSBasic::OBSBasic(QWidget *parent)
: OBSMainWindow (parent),
properties (nullptr),
fileOutput (nullptr),
streamOutput (nullptr),
service (nullptr),
aac (nullptr),
x264 (nullptr),
sceneChanging (false),
resizeTimer (0),
activeRefs (0),
ui (new Ui::OBSBasic)
{
ui->setupUi(this);
@@ -444,6 +438,28 @@ void OBSBasic::InitOBSCallbacks()
OBSBasic::SourceDeactivated, this);
}
void OBSBasic::InitPrimitives()
{
gs_entercontext(obs_graphics());
gs_renderstart(true);
gs_vertex2f(0.0f, 0.0f);
gs_vertex2f(0.0f, 1.0f);
gs_vertex2f(1.0f, 1.0f);
gs_vertex2f(1.0f, 0.0f);
gs_vertex2f(0.0f, 0.0f);
box = gs_rendersave();
gs_renderstart(true);
for (int i = 0; i <= 360; i += (360/20)) {
float pos = RAD(float(i));
gs_vertex2f(cosf(pos), sinf(pos));
}
circle = gs_rendersave();
gs_leavecontext();
}
void OBSBasic::OBSInit()
{
BPtr<char> savePath(os_get_config_path("obs-studio/basic/scenes.json"));
@@ -491,6 +507,8 @@ void OBSBasic::OBSInit()
if (!InitService())
throw "Failed to initialize service";
InitPrimitives();
Load(savePath);
ResetAudioDevices();
}
@@ -501,14 +519,26 @@ OBSBasic::~OBSBasic()
SaveService();
Save(savePath);
/* XXX: any obs data must be released before calling obs_shutdown.
* currently, we can't automate this with C++ RAII because of the
* delicate nature of obs_shutdown needing to be freed before the UI
* can be freed, and we have no control over the destruction order of
* the Qt UI stuff, so we have to manually clear any references to
* libobs. */
if (properties)
delete properties;
if (transformWindow)
delete transformWindow;
/* free the lists before shutting down to remove the scene/item
* references */
ClearVolumeControls();
ui->sources->clear();
ui->scenes->clear();
gs_entercontext(obs_graphics());
vertexbuffer_destroy(box);
vertexbuffer_destroy(circle);
gs_leavecontext();
obs_shutdown();
}
@@ -788,24 +818,73 @@ void OBSBasic::ChannelChanged(void *data, calldata_t params)
Q_ARG(OBSSource, OBSSource(source)));
}
void OBSBasic::DrawBackdrop(float cx, float cy)
{
if (!box)
return;
effect_t solid = obs_get_solid_effect();
eparam_t color = effect_getparambyname(solid, "color");
technique_t tech = effect_gettechnique(solid, "Solid");
vec4 colorVal;
vec4_set(&colorVal, 0.0f, 0.0f, 0.0f, 1.0f);
effect_setvec4(solid, color, &colorVal);
technique_begin(tech);
technique_beginpass(tech, 0);
gs_matrix_push();
gs_matrix_identity();
gs_matrix_scale3f(float(cx), float(cy), 1.0f);
gs_load_vertexbuffer(box);
gs_draw(GS_TRISTRIP, 0, 0);
gs_matrix_pop();
technique_endpass(tech);
technique_end(tech);
gs_load_vertexbuffer(nullptr);
}
void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy)
{
OBSBasic *window = static_cast<OBSBasic*>(data);
obs_video_info ovi;
int newCX, newCY;
obs_get_video_info(&ovi);
newCX = int(window->previewScale * float(ovi.base_width));
newCY = int(window->previewScale * float(ovi.base_height));
window->previewCX = int(window->previewScale * float(ovi.base_width));
window->previewCY = int(window->previewScale * float(ovi.base_height));
gs_viewport_push();
gs_projection_push();
/* --------------------------------------- */
gs_ortho(0.0f, float(ovi.base_width), 0.0f, float(ovi.base_height),
-100.0f, 100.0f);
gs_setviewport(window->previewX, window->previewY, newCX, newCY);
gs_setviewport(window->previewX, window->previewY,
window->previewCX, window->previewCY);
window->DrawBackdrop(float(ovi.base_width), float(ovi.base_height));
obs_render_main_view();
gs_load_vertexbuffer(nullptr);
/* --------------------------------------- */
float right = float(window->ui->preview->width()) - window->previewX;
float bottom = float(window->ui->preview->height()) - window->previewY;
gs_ortho(-window->previewX, right,
-window->previewY, bottom,
-100.0f, 100.0f);
gs_resetviewport();
window->ui->preview->DrawSceneEditing();
/* --------------------------------------- */
gs_projection_pop();
gs_viewport_pop();
@@ -981,9 +1060,13 @@ void OBSBasic::ResizePreview(uint32_t cx, uint32_t cy)
/* resize preview panel to fix to the top section of the window */
targetSize = GetPixelSize(ui->preview);
GetScaleAndCenterPos(int(cx), int(cy),
targetSize.width(), targetSize.height(),
targetSize.width() - PREVIEW_EDGE_SIZE * 2,
targetSize.height() - PREVIEW_EDGE_SIZE * 2,
previewX, previewY, previewScale);
previewX += float(PREVIEW_EDGE_SIZE);
previewY += float(PREVIEW_EDGE_SIZE);
if (isVisible()) {
if (resizeTimer)
killTimer(resizeTimer);
@@ -1155,8 +1238,23 @@ void OBSBasic::on_actionSceneDown_triggered()
void OBSBasic::on_sources_currentItemChanged(QListWidgetItem *current,
QListWidgetItem *prev)
{
/* TODO */
UNUSED_PARAMETER(current);
auto select_one = [] (obs_scene_t scene, obs_sceneitem_t item,
void *param)
{
obs_sceneitem_t selectedItem =
*reinterpret_cast<OBSSceneItem*>(param);
obs_sceneitem_select(item, (selectedItem == item));
UNUSED_PARAMETER(scene);
return true;
};
if (!current)
return;
OBSSceneItem item = current->data(Qt::UserRole).value<OBSSceneItem>();
obs_scene_enum_items(GetCurrentScene(), select_one, &item);
UNUSED_PARAMETER(prev);
}
@@ -1569,3 +1667,192 @@ config_t OBSBasic::Config() const
{
return basicConfig;
}
void OBSBasic::on_actionEditTransform_triggered()
{
delete transformWindow;
transformWindow = new OBSBasicTransform(this);
transformWindow->show();
}
void OBSBasic::on_actionResetTransform_triggered()
{
auto func = [] (obs_scene_t scene, obs_sceneitem_t item, void *param)
{
if (!obs_sceneitem_selected(item))
return true;
obs_sceneitem_info info;
vec2_set(&info.pos, 0.0f, 0.0f);
vec2_set(&info.scale, 1.0f, 1.0f);
info.rot = 0.0f;
info.alignment = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
info.bounds_type = OBS_BOUNDS_NONE;
info.bounds_alignment = OBS_ALIGN_CENTER;
vec2_set(&info.bounds, 0.0f, 0.0f);
obs_sceneitem_set_info(item, &info);
UNUSED_PARAMETER(scene);
UNUSED_PARAMETER(param);
return true;
};
obs_scene_enum_items(GetCurrentScene(), func, nullptr);
}
static vec3 GetItemTL(obs_sceneitem_t item)
{
matrix4 boxTransform;
obs_sceneitem_get_box_transform(item, &boxTransform);
vec3 tl;
vec3_set(&tl, M_INFINITE, M_INFINITE, 0.0f);
auto GetMinPos = [&] (vec3 &val, float x, float y)
{
vec3 pos;
vec3_set(&pos, x, y, 0.0f);
vec3_transform(&pos, &pos, &boxTransform);
vec3_min(&val, &val, &pos);
};
GetMinPos(tl, 0.0f, 0.0f);
GetMinPos(tl, 1.0f, 0.0f);
GetMinPos(tl, 0.0f, 1.0f);
GetMinPos(tl, 1.0f, 1.0f);
return tl;
}
static void SetItemTL(obs_sceneitem_t item, const vec3 &tl)
{
vec3 newTL;
vec2 pos;
obs_sceneitem_getpos(item, &pos);
newTL = GetItemTL(item);
pos.x += tl.x - newTL.x;
pos.y += tl.y - newTL.y;
obs_sceneitem_setpos(item, &pos);
}
static bool RotateSelectedSources(obs_scene_t scene, obs_sceneitem_t item,
void *param)
{
if (!obs_sceneitem_selected(item))
return true;
float rot = *reinterpret_cast<float*>(param);
vec3 tl = GetItemTL(item);
rot += obs_sceneitem_getrot(item);
if (rot >= 360.0f) rot -= 360.0f;
else if (rot <= -360.0f) rot += 360.0f;
obs_sceneitem_setrot(item, rot);
SetItemTL(item, tl);
UNUSED_PARAMETER(scene);
UNUSED_PARAMETER(param);
return true;
};
void OBSBasic::on_actionRotate90CW_triggered()
{
float f90CW = 90.0f;
obs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f90CW);
}
void OBSBasic::on_actionRotate90CCW_triggered()
{
float f90CCW = -90.0f;
obs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f90CCW);
}
void OBSBasic::on_actionRotate180_triggered()
{
float f180 = 180.0f;
obs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f180);
}
static bool MultiplySelectedItemScale(obs_scene_t scene, obs_sceneitem_t item,
void *param)
{
vec2 &mul = *reinterpret_cast<vec2*>(param);
if (!obs_sceneitem_selected(item))
return true;
vec3 tl = GetItemTL(item);
vec2 scale;
obs_sceneitem_getscale(item, &scale);
vec2_mul(&scale, &scale, &mul);
obs_sceneitem_setscale(item, &scale);
SetItemTL(item, tl);
return true;
}
void OBSBasic::on_actionFlipHorizontal_triggered()
{
vec2 scale = {-1.0f, 1.0f};
obs_scene_enum_items(GetCurrentScene(), MultiplySelectedItemScale,
&scale);
}
void OBSBasic::on_actionFlipVertical_triggered()
{
vec2 scale = {1.0f, -1.0f};
obs_scene_enum_items(GetCurrentScene(), MultiplySelectedItemScale,
&scale);
}
static bool CenterAlignSelectedItems(obs_scene_t scene, obs_sceneitem_t item,
void *param)
{
obs_bounds_type boundsType = *reinterpret_cast<obs_bounds_type*>(param);
if (!obs_sceneitem_selected(item))
return true;
obs_video_info ovi;
obs_get_video_info(&ovi);
obs_sceneitem_info itemInfo;
vec2_set(&itemInfo.pos, 0.0f, 0.0f);
vec2_set(&itemInfo.scale, 1.0f, 1.0f);
itemInfo.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP;
itemInfo.rot = 0.0f;
vec2_set(&itemInfo.bounds,
float(ovi.base_width), float(ovi.base_height));
itemInfo.bounds_type = boundsType;
itemInfo.bounds_alignment = OBS_ALIGN_CENTER;
obs_sceneitem_set_info(item, &itemInfo);
UNUSED_PARAMETER(scene);
return true;
}
void OBSBasic::on_actionFitToScreen_triggered()
{
obs_bounds_type boundsType = OBS_BOUNDS_SCALE_INNER;
obs_scene_enum_items(GetCurrentScene(), CenterAlignSelectedItems,
&boundsType);
}
void OBSBasic::on_actionStretchToScreen_triggered()
{
obs_bounds_type boundsType = OBS_BOUNDS_STRETCH;
obs_scene_enum_items(GetCurrentScene(), CenterAlignSelectedItems,
&boundsType);
}
void OBSBasic::on_actionCenterToScreen_triggered()
{
obs_bounds_type boundsType = OBS_BOUNDS_MAX_ONLY;
obs_scene_enum_items(GetCurrentScene(), CenterAlignSelectedItems,
&boundsType);
}

View File

@@ -25,6 +25,7 @@
#include <memory>
#include "window-main.hpp"
#include "window-basic-properties.hpp"
#include "window-basic-transform.hpp"
#include <util/util.hpp>
@@ -45,34 +46,43 @@ class QNetworkReply;
class OBSBasic : public OBSMainWindow {
Q_OBJECT
friend class OBSBasicPreview;
private:
std::unordered_map<obs_source_t, int> sourceSceneRefs;
std::vector<VolControl*> volumes;
QPointer<OBSBasicProperties> properties;
QPointer<OBSBasicTransform> transformWindow;
QNetworkAccessManager networkManager;
QBuffer logUploadPostData;
QNetworkReply *logUploadReply;
QNetworkReply *logUploadReply = nullptr;
QByteArray logUploadReturnData;
obs_output_t fileOutput;
obs_output_t streamOutput;
obs_service_t service;
obs_encoder_t aac;
obs_encoder_t x264;
obs_output_t fileOutput = nullptr;
obs_output_t streamOutput = nullptr;
obs_service_t service = nullptr;
obs_encoder_t aac = nullptr;
obs_encoder_t x264 = nullptr;
bool sceneChanging;
vertbuffer_t box = nullptr;
vertbuffer_t circle = nullptr;
int previewX, previewY;
float previewScale;
int resizeTimer;
bool sceneChanging = false;
int previewX = 0, previewY = 0;
int previewCX = 0, previewCY = 0;
float previewScale = 0.0f;
int resizeTimer = 0;
ConfigFile basicConfig;
int activeRefs;
int activeRefs = 0;
void DrawBackdrop(float cx, float cy);
void SetupEncoders();
@@ -97,7 +107,8 @@ private:
void InitOBSCallbacks();
OBSScene GetCurrentScene();
void InitPrimitives();
OBSSceneItem GetCurrentSceneItem();
void GetFPSCommon(uint32_t &num, uint32_t &den) const;
@@ -153,6 +164,8 @@ private:
void AddSourcePopupMenu(const QPoint &pos);
public:
OBSScene GetCurrentScene();
obs_service_t GetService();
void SetService(obs_service_t service);
@@ -167,6 +180,14 @@ public:
void SaveProject();
void LoadProject();
inline void GetDisplayRect(int &x, int &y, int &cx, int &cy)
{
x = previewX;
y = previewY;
cx = previewCX;
cy = previewCY;
}
protected:
virtual void closeEvent(QCloseEvent *event) override;
virtual void changeEvent(QEvent *event) override;
@@ -178,6 +199,20 @@ private slots:
void on_action_Open_triggered();
void on_action_Save_triggered();
void on_action_Settings_triggered();
void on_actionUploadCurrentLog_triggered();
void on_actionUploadLastLog_triggered();
void on_actionEditTransform_triggered();
void on_actionResetTransform_triggered();
void on_actionRotate90CW_triggered();
void on_actionRotate90CCW_triggered();
void on_actionRotate180_triggered();
void on_actionFlipHorizontal_triggered();
void on_actionFlipVertical_triggered();
void on_actionFitToScreen_triggered();
void on_actionStretchToScreen_triggered();
void on_actionCenterToScreen_triggered();
void on_scenes_currentItemChanged(QListWidgetItem *current,
QListWidgetItem *prev);
void on_scenes_customContextMenuRequested(const QPoint &pos);
@@ -194,8 +229,7 @@ private slots:
void on_actionSourceProperties_triggered();
void on_actionSourceUp_triggered();
void on_actionSourceDown_triggered();
void on_actionUploadCurrentLog_triggered();
void on_actionUploadLastLog_triggered();
void on_streamButton_clicked();
void on_recordButton_clicked();
void on_settingsButton_clicked();

View File

@@ -0,0 +1,718 @@
#include <QGuiApplication>
#include <QMouseEvent>
#include <cmath>
#include <graphics/vec4.h>
#include <graphics/matrix4.h>
#include "window-basic-preview.hpp"
#include "window-basic-main.hpp"
#include "obs-app.hpp"
#define HANDLE_RADIUS 4.0f
#define HANDLE_SEL_RADIUS (HANDLE_RADIUS * 1.5f)
#define CLAMP_DISTANCE 10.0f
/* TODO: make C++ math classes and clean up code here later */
OBSBasicPreview::OBSBasicPreview(QWidget *parent, Qt::WindowFlags flags)
: OBSQTDisplay(parent, flags)
{
setMouseTracking(true);
}
vec2 OBSBasicPreview::GetMouseEventPos(QMouseEvent *event)
{
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
vec2 pos = {
(float(event->x()) - main->previewX) / main->previewScale,
(float(event->y()) - main->previewY) / main->previewScale
};
return pos;
}
struct SceneFindData {
const vec2 &pos;
OBSSceneItem item;
bool selectBelow;
inline SceneFindData(const vec2 &pos_, bool selectBelow_)
: pos (pos_),
selectBelow (selectBelow_)
{}
};
static bool FindItemAtPos(obs_scene_t scene, obs_sceneitem_t item, void *param)
{
SceneFindData *data = reinterpret_cast<SceneFindData*>(param);
matrix4 transform;
vec3 transformedPos;
vec3 pos3 = {data->pos.x, data->pos.y, 0.0f};
obs_sceneitem_get_box_transform(item, &transform);
matrix4_inv(&transform, &transform);
vec3_transform(&transformedPos, &pos3, &transform);
if (transformedPos.x >= 0.0f && transformedPos.x <= 1.0f &&
transformedPos.y >= 0.0f && transformedPos.y <= 1.0f) {
if (data->selectBelow && obs_sceneitem_selected(item)) {
if (data->item)
return false;
else
data->selectBelow = false;
}
data->item = item;
}
UNUSED_PARAMETER(scene);
return true;
}
static vec3 GetTransformedPos(float x, float y, const matrix4 &mat)
{
vec3 result;
vec3_set(&result, x, y, 0.0f);
vec3_transform(&result, &result, &mat);
return result;
}
static vec3 GetTransformedPosScaled(float x, float y, const matrix4 &mat,
float scale)
{
vec3 result;
vec3_set(&result, x, y, 0.0f);
vec3_transform(&result, &result, &mat);
vec3_mulf(&result, &result, scale);
return result;
}
static inline vec2 GetOBSScreenSize()
{
obs_video_info ovi;
vec2 size = {0.0f, 0.0f};
if (obs_get_video_info(&ovi)) {
size.x = float(ovi.base_width);
size.y = float(ovi.base_height);
}
return size;
}
vec3 OBSBasicPreview::GetScreenSnapOffset(const vec3 &tl, const vec3 &br)
{
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
vec2 screenSize = GetOBSScreenSize();
vec3 clampOffset;
vec3_zero(&clampOffset);
const float clampDist = CLAMP_DISTANCE / main->previewScale;
if (fabsf(tl.x) < clampDist)
clampOffset.x = -tl.x;
if (fabsf(clampOffset.x) < EPSILON &&
fabsf(screenSize.x - br.x) < clampDist)
clampOffset.x = screenSize.x - br.x;
if (fabsf(tl.y) < clampDist)
clampOffset.y = -tl.y;
if (fabsf(clampOffset.y) < EPSILON &&
fabsf(screenSize.y - br.y) < clampDist)
clampOffset.y = screenSize.y - br.y;
return clampOffset;
}
OBSSceneItem OBSBasicPreview::GetItemAtPos(const vec2 &pos, bool selectBelow)
{
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
OBSScene scene = main->GetCurrentScene();
if (!scene)
return OBSSceneItem();
SceneFindData data(pos, selectBelow);
obs_scene_enum_items(scene, FindItemAtPos, &data);
return data.item;
}
static bool CheckItemSelected(obs_scene_t scene, obs_sceneitem_t item,
void *param)
{
SceneFindData *data = reinterpret_cast<SceneFindData*>(param);
matrix4 transform;
vec3 transformedPos;
vec3 pos3 = {data->pos.x, data->pos.y, 0.0f};
obs_sceneitem_get_box_transform(item, &transform);
matrix4_inv(&transform, &transform);
vec3_transform(&transformedPos, &pos3, &transform);
if (transformedPos.x >= 0.0f && transformedPos.x <= 1.0f &&
transformedPos.y >= 0.0f && transformedPos.y <= 1.0f) {
if (obs_sceneitem_selected(item)) {
data->item = item;
return false;
}
}
UNUSED_PARAMETER(scene);
return true;
}
bool OBSBasicPreview::SelectedAtPos(const vec2 &pos)
{
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
OBSScene scene = main->GetCurrentScene();
if (!scene)
return false;
SceneFindData data(pos, false);
obs_scene_enum_items(scene, CheckItemSelected, &data);
return !!data.item;
}
struct HandleFindData {
const vec2 &pos;
const float scale;
OBSSceneItem item;
ItemHandle handle = ItemHandle::None;
inline HandleFindData(const vec2 &pos_, float scale_)
: pos (pos_),
scale (scale_)
{}
};
static bool FindHandleAtPos(obs_scene_t scene, obs_sceneitem_t item,
void *param)
{
if (!obs_sceneitem_selected(item))
return true;
HandleFindData *data = reinterpret_cast<HandleFindData*>(param);
matrix4 transform;
vec3 pos3 = {data->pos.x, data->pos.y, 0.0f};
float closestHandle = HANDLE_SEL_RADIUS;
obs_sceneitem_get_box_transform(item, &transform);
auto TestHandle = [&] (float x, float y, ItemHandle handle)
{
vec3 handlePos = GetTransformedPosScaled(x, y, transform,
data->scale);
float dist = vec3_dist(&handlePos, &pos3);
if (dist < HANDLE_SEL_RADIUS) {
if (dist < closestHandle) {
closestHandle = dist;
data->handle = handle;
data->item = item;
}
}
};
TestHandle(0.0f, 0.0f, ItemHandle::TopLeft);
TestHandle(0.5f, 0.0f, ItemHandle::TopCenter);
TestHandle(1.0f, 0.0f, ItemHandle::TopRight);
TestHandle(0.0f, 0.5f, ItemHandle::CenterLeft);
TestHandle(1.0f, 0.5f, ItemHandle::CenterRight);
TestHandle(0.0f, 1.0f, ItemHandle::BottomLeft);
TestHandle(0.5f, 1.0f, ItemHandle::BottomCenter);
TestHandle(1.0f, 1.0f, ItemHandle::BottomRight);
UNUSED_PARAMETER(scene);
return true;
}
static vec2 GetItemSize(obs_sceneitem_t item)
{
obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(item);
vec2 size;
if (boundsType != OBS_BOUNDS_NONE) {
obs_sceneitem_get_bounds(item, &size);
} else {
obs_source_t source = obs_sceneitem_getsource(item);
vec2 scale;
obs_sceneitem_getscale(item, &scale);
size.x = float(obs_source_getwidth(source)) * scale.x;
size.y = float(obs_source_getheight(source)) * scale.y;
}
return size;
}
void OBSBasicPreview::GetStretchHandleData(const vec2 &pos)
{
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
OBSScene scene = main->GetCurrentScene();
if (!scene)
return;
HandleFindData data(pos, main->previewScale);
obs_scene_enum_items(scene, FindHandleAtPos, &data);
stretchItem = std::move(data.item);
stretchHandle = data.handle;
if (stretchHandle != ItemHandle::None) {
matrix4 boxTransform;
vec3 itemUL;
float itemRot;
stretchItemSize = GetItemSize(stretchItem);
obs_sceneitem_get_box_transform(stretchItem, &boxTransform);
itemRot = obs_sceneitem_getrot(stretchItem);
vec3_from_vec4(&itemUL, &boxTransform.t);
/* build the item space conversion matrices */
matrix4_identity(&itemToScreen);
matrix4_rotate_aa4f(&itemToScreen, &itemToScreen,
0.0f, 0.0f, 1.0f, RAD(itemRot));
matrix4_translate3f(&itemToScreen, &itemToScreen,
itemUL.x, itemUL.y, 0.0f);
matrix4_identity(&screenToItem);
matrix4_translate3f(&screenToItem, &screenToItem,
-itemUL.x, -itemUL.y, 0.0f);
matrix4_rotate_aa4f(&screenToItem, &screenToItem,
0.0f, 0.0f, 1.0f, RAD(-itemRot));
}
}
void OBSBasicPreview::mousePressEvent(QMouseEvent *event)
{
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
float x = float(event->x()) - main->previewX;
float y = float(event->y()) - main->previewY;
if (event->button() != Qt::LeftButton ||
x < 0.0f || y < 0.0f || x > main->previewCX || y > main->previewCY)
return;
mouseDown = true;
vec2_set(&startPos, x, y);
GetStretchHandleData(startPos);
vec2_divf(&startPos, &startPos, main->previewScale);
startPos.x = std::round(startPos.x);
startPos.y = std::round(startPos.y);
mouseOverItems = SelectedAtPos(startPos);
vec2_zero(&lastMoveOffset);
}
static bool select_one(obs_scene_t scene, obs_sceneitem_t item, void *param)
{
obs_sceneitem_t selectedItem = reinterpret_cast<obs_sceneitem_t>(param);
obs_sceneitem_select(item, (selectedItem == item));
UNUSED_PARAMETER(scene);
return true;
}
void OBSBasicPreview::DoSelect(const vec2 &pos)
{
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
OBSScene scene = main->GetCurrentScene();
OBSSceneItem item = GetItemAtPos(pos, true);
obs_scene_enum_items(scene, select_one, (obs_sceneitem_t)item);
}
void OBSBasicPreview::DoCtrlSelect(const vec2 &pos)
{
OBSSceneItem item = GetItemAtPos(pos, false);
if (!item)
return;
bool selected = obs_sceneitem_selected(item);
obs_sceneitem_select(item, !selected);
}
void OBSBasicPreview::ProcessClick(const vec2 &pos)
{
Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
if (modifiers & Qt::ControlModifier)
DoCtrlSelect(pos);
else
DoSelect(pos);
}
void OBSBasicPreview::mouseReleaseEvent(QMouseEvent *event)
{
if (mouseDown) {
vec2 pos = GetMouseEventPos(event);
if (!mouseMoved)
ProcessClick(pos);
stretchItem = nullptr;
mouseDown = false;
mouseMoved = false;
}
}
struct SelectedItemBounds {
bool first = true;
vec3 tl, br;
};
static bool AddItemBounds(obs_scene_t scene, obs_sceneitem_t item,
void *param)
{
SelectedItemBounds *data = reinterpret_cast<SelectedItemBounds*>(param);
if (!obs_sceneitem_selected(item))
return true;
matrix4 boxTransform;
obs_sceneitem_get_box_transform(item, &boxTransform);
vec3 t[4] = {
GetTransformedPos(0.0f, 0.0f, boxTransform),
GetTransformedPos(1.0f, 0.0f, boxTransform),
GetTransformedPos(0.0f, 1.0f, boxTransform),
GetTransformedPos(1.0f, 1.0f, boxTransform)
};
for (const vec3 &v : t) {
if (data->first) {
vec3_copy(&data->tl, &v);
vec3_copy(&data->br, &v);
data->first = false;
} else {
vec3_min(&data->tl, &data->tl, &v);
vec3_max(&data->br, &data->br, &v);
}
}
UNUSED_PARAMETER(scene);
return true;
}
void OBSBasicPreview::SnapItemMovement(vec2 &offset)
{
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
OBSScene scene = main->GetCurrentScene();
SelectedItemBounds data;
obs_scene_enum_items(scene, AddItemBounds, &data);
data.tl.x += offset.x;
data.tl.y += offset.y;
data.br.x += offset.x;
data.br.y += offset.y;
vec3 snapOffset = GetScreenSnapOffset(data.tl, data.br);
offset.x += snapOffset.x;
offset.y += snapOffset.y;
}
static bool move_items(obs_scene_t scene, obs_sceneitem_t item, void *param)
{
vec2 *offset = reinterpret_cast<vec2*>(param);
if (obs_sceneitem_selected(item)) {
vec2 pos;
obs_sceneitem_getpos(item, &pos);
vec2_add(&pos, &pos, offset);
obs_sceneitem_setpos(item, &pos);
}
UNUSED_PARAMETER(scene);
return true;
}
void OBSBasicPreview::MoveItems(const vec2 &pos)
{
Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
OBSScene scene = main->GetCurrentScene();
vec2 offset, moveOffset;
vec2_sub(&offset, &pos, &startPos);
vec2_sub(&moveOffset, &offset, &lastMoveOffset);
if (!(modifiers & Qt::ControlModifier))
SnapItemMovement(moveOffset);
vec2_add(&lastMoveOffset, &lastMoveOffset, &moveOffset);
obs_scene_enum_items(scene, move_items, &moveOffset);
}
vec3 OBSBasicPreview::CalculateStretchPos(const vec3 &tl, const vec3 &br)
{
uint32_t alignment = obs_sceneitem_getalignment(stretchItem);
vec3 pos;
vec3_zero(&pos);
if (alignment & OBS_ALIGN_LEFT)
pos.x = tl.x;
else if (alignment & OBS_ALIGN_RIGHT)
pos.x = br.x;
else
pos.x = (br.x - tl.x) * 0.5f + tl.x;
if (alignment & OBS_ALIGN_TOP)
pos.y = tl.y;
else if (alignment & OBS_ALIGN_BOTTOM)
pos.y = br.y;
else
pos.y = (br.y - tl.y) * 0.5f + tl.y;
return pos;
}
void OBSBasicPreview::ClampAspect(vec3 &tl, vec3 &br, vec2 &size,
const vec2 &baseSize)
{
float baseAspect = baseSize.x / baseSize.y;
float aspect = size.x / size.y;
uint32_t stretchFlags = (uint32_t)stretchHandle;
if (stretchHandle == ItemHandle::TopLeft ||
stretchHandle == ItemHandle::TopRight ||
stretchHandle == ItemHandle::BottomLeft ||
stretchHandle == ItemHandle::BottomRight) {
if (aspect < baseAspect)
size.x = size.y * baseAspect;
else
size.y = size.x / baseAspect;
} else if (stretchHandle == ItemHandle::TopCenter ||
stretchHandle == ItemHandle::BottomCenter) {
size.x = size.y * baseAspect;
} else if (stretchHandle == ItemHandle::CenterLeft ||
stretchHandle == ItemHandle::CenterRight) {
size.y = size.x / baseAspect;
}
size.x = std::round(size.x);
size.y = std::round(size.y);
if (stretchFlags & ITEM_LEFT)
tl.x = br.x - size.x;
else if (stretchFlags & ITEM_RIGHT)
br.x = tl.x + size.x;
if (stretchFlags & ITEM_TOP)
tl.y = br.y - size.y;
else if (stretchFlags & ITEM_BOTTOM)
br.y = tl.y + size.y;
}
void OBSBasicPreview::SnapStretchingToScreen(vec3 &tl, vec3 &br)
{
uint32_t stretchFlags = (uint32_t)stretchHandle;
vec3 newTL = GetTransformedPos(tl.x, tl.y, itemToScreen);
vec3 newTR = GetTransformedPos(br.x, tl.y, itemToScreen);
vec3 newBL = GetTransformedPos(tl.x, br.y, itemToScreen);
vec3 newBR = GetTransformedPos(br.x, br.y, itemToScreen);
vec3 boundingTL;
vec3 boundingBR;
vec3_copy(&boundingTL, &newTL);
vec3_min(&boundingTL, &boundingTL, &newTR);
vec3_min(&boundingTL, &boundingTL, &newBL);
vec3_min(&boundingTL, &boundingTL, &newBR);
vec3_copy(&boundingBR, &newTL);
vec3_max(&boundingBR, &boundingBR, &newTR);
vec3_max(&boundingBR, &boundingBR, &newBL);
vec3_max(&boundingBR, &boundingBR, &newBR);
vec3 offset = GetScreenSnapOffset(boundingTL, boundingBR);
vec3_add(&offset, &offset, &newTL);
vec3_transform(&offset, &offset, &screenToItem);
vec3_sub(&offset, &offset, &tl);
if (stretchFlags & ITEM_LEFT)
tl.x += offset.x;
else if (stretchFlags & ITEM_RIGHT)
br.x += offset.x;
if (stretchFlags & ITEM_TOP)
tl.y += offset.y;
else if (stretchFlags & ITEM_BOTTOM)
br.y += offset.y;
}
void OBSBasicPreview::StretchItem(const vec2 &pos)
{
Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(stretchItem);
uint32_t stretchFlags = (uint32_t)stretchHandle;
bool shiftDown = (modifiers & Qt::ShiftModifier);
vec3 tl, br, pos3;
vec3_zero(&tl);
vec3_set(&br, stretchItemSize.x, stretchItemSize.y, 0.0f);
vec3_set(&pos3, pos.x, pos.y, 0.0f);
vec3_transform(&pos3, &pos3, &screenToItem);
if (stretchFlags & ITEM_LEFT)
tl.x = pos3.x;
else if (stretchFlags & ITEM_RIGHT)
br.x = pos3.x;
if (stretchFlags & ITEM_TOP)
tl.y = pos3.y;
else if (stretchFlags & ITEM_BOTTOM)
br.y = pos3.y;
if (!(modifiers & Qt::ControlModifier))
SnapStretchingToScreen(tl, br);
obs_source_t source = obs_sceneitem_getsource(stretchItem);
vec2 baseSize = {
float(obs_source_getwidth(source)),
float(obs_source_getheight(source))
};
vec2 size = {br.x - tl.x, br.y - tl.y};
if (boundsType != OBS_BOUNDS_NONE) {
if (boundsType == OBS_BOUNDS_STRETCH && !shiftDown)
ClampAspect(tl, br, size, baseSize);
if (tl.x > br.x) std::swap(tl.x, br.x);
if (tl.y > br.y) std::swap(tl.y, br.y);
vec2_abs(&size, &size);
obs_sceneitem_set_bounds(stretchItem, &size);
} else {
if (!shiftDown)
ClampAspect(tl, br, size, baseSize);
vec2_div(&size, &size, &baseSize);
obs_sceneitem_setscale(stretchItem, &size);
}
pos3 = CalculateStretchPos(tl, br);
vec3_transform(&pos3, &pos3, &itemToScreen);
vec2 newPos = {pos3.x, pos3.y};
newPos.x = std::round(newPos.x);
newPos.y = std::round(newPos.y);
obs_sceneitem_setpos(stretchItem, &newPos);
}
void OBSBasicPreview::mouseMoveEvent(QMouseEvent *event)
{
if (mouseDown) {
vec2 pos = GetMouseEventPos(event);
if (!mouseMoved && !mouseOverItems &&
stretchHandle == ItemHandle::None) {
ProcessClick(startPos);
mouseOverItems = SelectedAtPos(startPos);
}
pos.x = std::round(pos.x);
pos.y = std::round(pos.y);
if (stretchHandle != ItemHandle::None)
StretchItem(pos);
else if (mouseOverItems)
MoveItems(pos);
mouseMoved = true;
}
}
static void DrawCircleAtPos(float x, float y, matrix4 &matrix,
float previewScale)
{
struct vec3 pos;
vec3_set(&pos, x, y, 0.0f);
vec3_transform(&pos, &pos, &matrix);
vec3_mulf(&pos, &pos, previewScale);
gs_matrix_push();
gs_matrix_translate(&pos);
gs_draw(GS_LINESTRIP, 0, 0);
gs_matrix_pop();
}
bool OBSBasicPreview::DrawSelectedItem(obs_scene_t scene, obs_sceneitem_t item,
void *param)
{
if (!obs_sceneitem_selected(item))
return true;
OBSBasicPreview *preview = reinterpret_cast<OBSBasicPreview*>(param);
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
gs_load_vertexbuffer(main->circle);
matrix4 boxTransform;
obs_sceneitem_get_box_transform(item, &boxTransform);
gs_matrix_push();
gs_matrix_scale3f(HANDLE_RADIUS, HANDLE_RADIUS, 1.0f);
DrawCircleAtPos(0.0f, 0.0f, boxTransform, main->previewScale);
DrawCircleAtPos(0.0f, 1.0f, boxTransform, main->previewScale);
DrawCircleAtPos(1.0f, 0.0f, boxTransform, main->previewScale);
DrawCircleAtPos(1.0f, 1.0f, boxTransform, main->previewScale);
DrawCircleAtPos(0.5f, 0.0f, boxTransform, main->previewScale);
DrawCircleAtPos(0.0f, 0.5f, boxTransform, main->previewScale);
DrawCircleAtPos(0.5f, 1.0f, boxTransform, main->previewScale);
DrawCircleAtPos(1.0f, 0.5f, boxTransform, main->previewScale);
gs_matrix_pop();
gs_load_vertexbuffer(main->box);
gs_matrix_push();
gs_matrix_set(&boxTransform);
gs_matrix_scale3f(main->previewScale, main->previewScale, 1.0f);
gs_draw(GS_LINESTRIP, 0, 0);
gs_matrix_pop();
UNUSED_PARAMETER(scene);
return true;
}
void OBSBasicPreview::DrawSceneEditing()
{
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
effect_t solid = obs_get_solid_effect();
technique_t tech = effect_gettechnique(solid, "Solid");
vec4 color;
vec4_set(&color, 1.0f, 0.0f, 0.0f, 1.0f);
effect_setvec4(solid, effect_getparambyname(solid, "color"), &color);
technique_begin(tech);
technique_beginpass(tech, 0);
OBSScene scene = main->GetCurrentScene();
if (scene)
obs_scene_enum_items(scene, DrawSelectedItem, this);
gs_load_vertexbuffer(nullptr);
technique_endpass(tech);
technique_end(tech);
}

View File

@@ -0,0 +1,84 @@
#pragma once
#include <obs.hpp>
#include <graphics/vec2.h>
#include <graphics/matrix4.h>
#include "qt-display.hpp"
#include "obs-app.hpp"
class OBSBasic;
class QMouseEvent;
#define ITEM_LEFT (1<<0)
#define ITEM_RIGHT (1<<1)
#define ITEM_TOP (1<<2)
#define ITEM_BOTTOM (1<<3)
enum class ItemHandle : uint32_t {
None = 0,
TopLeft = ITEM_TOP | ITEM_LEFT,
TopCenter = ITEM_TOP,
TopRight = ITEM_TOP | ITEM_RIGHT,
CenterLeft = ITEM_LEFT,
CenterRight = ITEM_RIGHT,
BottomLeft = ITEM_BOTTOM | ITEM_LEFT,
BottomCenter = ITEM_BOTTOM,
BottomRight = ITEM_BOTTOM | ITEM_RIGHT
};
class OBSBasicPreview : public OBSQTDisplay {
Q_OBJECT
private:
OBSSceneItem stretchItem;
ItemHandle stretchHandle = ItemHandle::None;
vec2 stretchItemSize;
matrix4 screenToItem;
matrix4 itemToScreen;
vec2 startPos;
vec2 lastMoveOffset;
bool mouseDown = false;
bool mouseMoved = false;
bool mouseOverItems = false;
static vec2 GetMouseEventPos(QMouseEvent *event);
static bool DrawSelectedItem(obs_scene_t scene, obs_sceneitem_t item,
void *param);
static OBSSceneItem GetItemAtPos(const vec2 &pos, bool selectBelow);
static bool SelectedAtPos(const vec2 &pos);
static void DoSelect(const vec2 &pos);
static void DoCtrlSelect(const vec2 &pos);
static vec3 GetScreenSnapOffset(const vec3 &tl, const vec3 &br);
void GetStretchHandleData(const vec2 &pos);
void SnapStretchingToScreen(vec3 &tl, vec3 &br);
void ClampAspect(vec3 &tl, vec3 &br, vec2 &size, const vec2 &baseSize);
vec3 CalculateStretchPos(const vec3 &tl, const vec3 &br);
void StretchItem(const vec2 &pos);
static void SnapItemMovement(vec2 &offset);
void MoveItems(const vec2 &pos);
void ProcessClick(const vec2 &pos);
public:
OBSBasicPreview(QWidget *parent, Qt::WindowFlags flags = 0);
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
void DrawSceneEditing();
/* use libobs allocator for alignment because the matrices itemToScreen
* and screenToItem may contain SSE data, which will cause SSE
* instructions to crash if the data is not aligned to at least a 16
* byte boundry. */
static inline void* operator new(size_t size) {return bmalloc(size);}
static inline void operator delete(void* ptr) {bfree(ptr);}
};

View File

@@ -92,7 +92,6 @@ void OBSBasicSettings::HookWidget(QWidget *widget, const char *signal,
QObject::connect(widget, signal, this, slot);
}
#define COMBO_CHANGED SIGNAL(currentIndexChanged(int))
#define COMBO_CHANGED SIGNAL(currentIndexChanged(int))
#define EDIT_CHANGED SIGNAL(textChanged(const QString &))
#define CBEDIT_CHANGED SIGNAL(editTextChanged(const QString &))

View File

@@ -0,0 +1,253 @@
#include "window-basic-transform.hpp"
#include "window-basic-main.hpp"
Q_DECLARE_METATYPE(OBSSceneItem);
static OBSSceneItem FindASelectedItem(OBSScene scene)
{
auto func = [] (obs_scene_t scene, obs_sceneitem_t item, void *param)
{
OBSSceneItem &dst = *reinterpret_cast<OBSSceneItem*>(param);
if (obs_sceneitem_selected(item)) {
dst = item;
return false;
}
return true;
};
OBSSceneItem item;
obs_scene_enum_items(scene, func, &item);
return item;
}
void OBSBasicTransform::HookWidget(QWidget *widget, const char *signal,
const char *slot)
{
QObject::connect(widget, signal, this, slot);
}
#define COMBO_CHANGED SIGNAL(currentIndexChanged(int))
#define DSCROLL_CHANGED SIGNAL(valueChanged(double))
OBSBasicTransform::OBSBasicTransform(OBSBasic *parent)
: QDialog (parent),
ui (new Ui::OBSBasicTransform),
main (parent)
{
setAttribute(Qt::WA_DeleteOnClose);
ui->setupUi(this);
HookWidget(ui->positionX, DSCROLL_CHANGED, SLOT(OnControlChanged()));
HookWidget(ui->positionY, DSCROLL_CHANGED, SLOT(OnControlChanged()));
HookWidget(ui->rotation, DSCROLL_CHANGED, SLOT(OnControlChanged()));
HookWidget(ui->scaleX, DSCROLL_CHANGED, SLOT(OnControlChanged()));
HookWidget(ui->scaleY, DSCROLL_CHANGED, SLOT(OnControlChanged()));
HookWidget(ui->align, COMBO_CHANGED, SLOT(OnControlChanged()));
HookWidget(ui->boundsType, COMBO_CHANGED, SLOT(OnBoundsType(int)));
HookWidget(ui->boundsAlign, COMBO_CHANGED, SLOT(OnControlChanged()));
HookWidget(ui->boundsWidth, DSCROLL_CHANGED, SLOT(OnControlChanged()));
HookWidget(ui->boundsHeight, DSCROLL_CHANGED, SLOT(OnControlChanged()));
OBSScene curScene = main->GetCurrentScene();
SetScene(curScene);
SetItem(FindASelectedItem(curScene));
channelChangedSignal.Connect(obs_signalhandler(), "channel_change",
OBSChannelChanged, this);
}
void OBSBasicTransform::SetScene(OBSScene scene)
{
transformSignal.Disconnect();
selectSignal.Disconnect();
deselectSignal.Disconnect();
removeSignal.Disconnect();
if (scene) {
OBSSource source = obs_scene_getsource(scene);
signal_handler_t signal = obs_source_signalhandler(source);
transformSignal.Connect(signal, "item_transform",
OBSSceneItemTransform, this);
removeSignal.Connect(signal, "item_remove",
OBSSceneItemRemoved, this);
selectSignal.Connect(signal, "item_select",
OBSSceneItemSelect, this);
deselectSignal.Connect(signal, "item_deselect",
OBSSceneItemDeselect, this);
}
}
void OBSBasicTransform::SetItem(OBSSceneItem newItem)
{
QMetaObject::invokeMethod(this, "SetItemQt",
Q_ARG(OBSSceneItem, OBSSceneItem(newItem)));
}
void OBSBasicTransform::SetItemQt(OBSSceneItem newItem)
{
item = newItem;
if (item)
RefreshControls();
setEnabled(!!item);
}
void OBSBasicTransform::OBSChannelChanged(void *param, calldata_t data)
{
OBSBasicTransform *window = reinterpret_cast<OBSBasicTransform*>(param);
uint32_t channel = (uint32_t)calldata_int(data, "channel");
OBSSource source = (obs_source_t)calldata_ptr(data, "source");
if (channel == 0) {
OBSScene scene = obs_scene_fromsource(source);
window->SetScene(scene);
if (!scene)
window->SetItem(nullptr);
else
window->SetItem(FindASelectedItem(scene));
}
}
void OBSBasicTransform::OBSSceneItemTransform(void *param, calldata_t data)
{
OBSBasicTransform *window = reinterpret_cast<OBSBasicTransform*>(param);
OBSSceneItem item = (obs_sceneitem_t)calldata_ptr(data, "item");
if (item == window->item && !window->ignoreTransformSignal)
QMetaObject::invokeMethod(window, "RefreshControls");
}
void OBSBasicTransform::OBSSceneItemRemoved(void *param, calldata_t data)
{
OBSBasicTransform *window = reinterpret_cast<OBSBasicTransform*>(param);
OBSScene scene = (obs_scene_t)calldata_ptr(data, "scene");
OBSSceneItem item = (obs_sceneitem_t)calldata_ptr(data, "item");
if (item == window->item)
window->SetItem(FindASelectedItem(scene));
}
void OBSBasicTransform::OBSSceneItemSelect(void *param, calldata_t data)
{
OBSBasicTransform *window = reinterpret_cast<OBSBasicTransform*>(param);
OBSSceneItem item = (obs_sceneitem_t)calldata_ptr(data, "item");
if (item != window->item)
window->SetItem(item);
}
void OBSBasicTransform::OBSSceneItemDeselect(void *param, calldata_t data)
{
OBSBasicTransform *window = reinterpret_cast<OBSBasicTransform*>(param);
OBSScene scene = (obs_scene_t)calldata_ptr(data, "scene");
OBSSceneItem item = (obs_sceneitem_t)calldata_ptr(data, "item");
if (item == window->item)
window->SetItem(FindASelectedItem(scene));
}
static const uint32_t listToAlign[] = {
OBS_ALIGN_TOP | OBS_ALIGN_LEFT,
OBS_ALIGN_TOP,
OBS_ALIGN_TOP | OBS_ALIGN_RIGHT,
OBS_ALIGN_LEFT,
OBS_ALIGN_CENTER,
OBS_ALIGN_RIGHT,
OBS_ALIGN_BOTTOM | OBS_ALIGN_LEFT,
OBS_ALIGN_BOTTOM,
OBS_ALIGN_BOTTOM | OBS_ALIGN_RIGHT
};
static int AlignToList(uint32_t align)
{
int index = 0;
for (uint32_t curAlign : listToAlign) {
if (curAlign == align)
return index;
index++;
}
return 0;
}
void OBSBasicTransform::RefreshControls()
{
if (!item)
return;
obs_sceneitem_info osi;
obs_sceneitem_get_info(item, &osi);
int alignIndex = AlignToList(osi.alignment);
int boundsAlignIndex = AlignToList(osi.bounds_alignment);
ignoreItemChange = true;
ui->positionX->setValue(osi.pos.x);
ui->positionY->setValue(osi.pos.y);
ui->rotation->setValue(osi.rot);
ui->scaleX->setValue(osi.scale.x);
ui->scaleY->setValue(osi.scale.y);
ui->align->setCurrentIndex(alignIndex);
ui->boundsType->setCurrentIndex(int(osi.bounds_type));
ui->boundsAlign->setCurrentIndex(boundsAlignIndex);
ui->boundsWidth->setValue(osi.bounds.x);
ui->boundsHeight->setValue(osi.bounds.y);
ignoreItemChange = false;
}
void OBSBasicTransform::OnBoundsType(int index)
{
if (index == -1)
return;
obs_bounds_type type = (obs_bounds_type)index;
bool enable = (type != OBS_BOUNDS_NONE);
ui->boundsAlign->setEnabled(enable);
ui->boundsWidth->setEnabled(enable);
ui->boundsHeight->setEnabled(enable);
if (!ignoreItemChange) {
obs_bounds_type lastType = obs_sceneitem_get_bounds_type(item);
if (lastType == OBS_BOUNDS_NONE) {
OBSSource source = obs_sceneitem_getsource(item);
int width = (int)obs_source_getwidth(source);
int height = (int)obs_source_getheight(source);
ui->boundsWidth->setValue(width);
ui->boundsHeight->setValue(height);
}
}
OnControlChanged();
}
void OBSBasicTransform::OnControlChanged()
{
if (ignoreItemChange)
return;
obs_sceneitem_info osi;
osi.pos.x = float(ui->positionX->value());
osi.pos.y = float(ui->positionY->value());
osi.rot = float(ui->rotation->value());
osi.scale.x = float(ui->scaleX->value());
osi.scale.y = float(ui->scaleY->value());
osi.alignment = listToAlign[ui->align->currentIndex()];
osi.bounds_type = (obs_bounds_type)ui->boundsType->currentIndex();
osi.bounds_alignment = listToAlign[ui->boundsAlign->currentIndex()];
osi.bounds.x = float(ui->boundsWidth->value());
osi.bounds.y = float(ui->boundsHeight->value());
ignoreTransformSignal = true;
obs_sceneitem_set_info(item, &osi);
ignoreTransformSignal = false;
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include <obs.hpp>
#include <memory>
#include "ui_OBSBasicTransform.h"
class OBSBasic;
class OBSBasicTransform : public QDialog {
Q_OBJECT
private:
std::unique_ptr<Ui::OBSBasicTransform> ui;
OBSBasic *main;
OBSSceneItem item;
OBSSignal channelChangedSignal;
OBSSignal transformSignal;
OBSSignal removeSignal;
OBSSignal selectSignal;
OBSSignal deselectSignal;
bool ignoreTransformSignal = false;
bool ignoreItemChange = false;
void HookWidget(QWidget *widget, const char *signal, const char *slot);
void SetScene(OBSScene scene);
void SetItem(OBSSceneItem newItem);
static void OBSChannelChanged(void *param, calldata_t data);
static void OBSSceneItemTransform(void *param, calldata_t data);
static void OBSSceneItemRemoved(void *param, calldata_t data);
static void OBSSceneItemSelect(void *param, calldata_t data);
static void OBSSceneItemDeselect(void *param, calldata_t data);
private slots:
void RefreshControls();
void SetItemQt(OBSSceneItem newItem);
void OnBoundsType(int index);
void OnControlChanged();
public:
OBSBasicTransform(OBSBasic *parent);
};

View File

@@ -22,6 +22,43 @@
<ClInclude Include="..\..\..\libobs\obs-ui.h" />
<ClInclude Include="..\..\..\obs\display-helpers.hpp" />
<ClInclude Include="..\..\..\obs\platform.hpp" />
<CustomBuild Include="..\..\..\obs\window-basic-preview.hpp">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing window-basic-preview.hpp...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_NETWORK_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs" "-I$(QTDIR)\include\QtNetwork"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing window-basic-preview.hpp...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_NETWORK_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs" "-I$(QTDIR)\include\QtNetwork"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing window-basic-preview.hpp...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_NETWORK_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs" "-I$(QTDIR)\include\QtNetwork"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing window-basic-preview.hpp...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_NETWORK_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs" "-I$(QTDIR)\include\QtNetwork"</Command>
</CustomBuild>
<CustomBuild Include="..\..\..\obs\window-basic-transform.hpp">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing window-basic-transform.hpp...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_NETWORK_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs" "-I$(QTDIR)\include\QtNetwork"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing window-basic-transform.hpp...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_NETWORK_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs" "-I$(QTDIR)\include\QtNetwork"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing window-basic-transform.hpp...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_NETWORK_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs" "-I$(QTDIR)\include\QtNetwork"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing window-basic-transform.hpp...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_NETWORK_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs" "-I$(QTDIR)\include\QtNetwork"</Command>
</CustomBuild>
<ClInclude Include="GeneratedFiles\ui_OBSBasicTransform.h" />
<ClInclude Include="GeneratedFiles\ui_OBSLogReply.h" />
<CustomBuild Include="..\..\..\obs\window-basic-source-select.hpp">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
@@ -235,9 +272,11 @@
<ClCompile Include="..\..\..\obs\qt-wrappers.cpp" />
<ClCompile Include="..\..\..\obs\volume-control.cpp" />
<ClCompile Include="..\..\..\obs\window-basic-main.cpp" />
<ClCompile Include="..\..\..\obs\window-basic-preview.cpp" />
<ClCompile Include="..\..\..\obs\window-basic-properties.cpp" />
<ClCompile Include="..\..\..\obs\window-basic-settings.cpp" />
<ClCompile Include="..\..\..\obs\window-basic-source-select.cpp" />
<ClCompile Include="..\..\..\obs\window-basic-transform.cpp" />
<ClCompile Include="..\..\..\obs\window-log-reply.cpp" />
<ClCompile Include="..\..\..\obs\window-namedialog.cpp" />
<ClCompile Include="GeneratedFiles\Debug\moc_obs-app.cpp">
@@ -260,6 +299,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_window-basic-preview.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_window-basic-properties.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
@@ -272,6 +315,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_window-basic-transform.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_window-log-reply.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
@@ -314,6 +361,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_window-basic-preview.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_window-basic-properties.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -326,6 +377,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_window-basic-transform.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_window-log-reply.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -553,6 +608,26 @@
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="..\..\..\obs\forms\OBSBasicTransform.ui">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Uic%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Uic%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Uic%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Uic%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
</CustomBuild>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{B12702AD-ABFB-343A-A199-8E24837244A3}</ProjectGuid>
<Keyword>Qt4VSv1.0</Keyword>

View File

@@ -89,6 +89,15 @@
<CustomBuild Include="..\..\..\obs\forms\OBSLogReply.ui">
<Filter>Form Files</Filter>
</CustomBuild>
<CustomBuild Include="..\..\..\obs\window-basic-preview.hpp">
<Filter>Header Files</Filter>
</CustomBuild>
<CustomBuild Include="..\..\..\obs\forms\OBSBasicTransform.ui">
<Filter>Form Files</Filter>
</CustomBuild>
<CustomBuild Include="..\..\..\obs\window-basic-transform.hpp">
<Filter>Header Files</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\obs\platform.hpp">
@@ -121,6 +130,9 @@
<ClInclude Include="GeneratedFiles\ui_OBSLogReply.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="GeneratedFiles\ui_OBSBasicTransform.h">
<Filter>Generated Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\obs\obs-app.cpp">
@@ -225,6 +237,24 @@
<ClCompile Include="..\..\..\obs\window-log-reply.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_window-basic-preview.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_window-basic-preview.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="..\..\..\obs\window-basic-preview.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_window-basic-transform.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_window-basic-transform.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="..\..\..\obs\window-basic-transform.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Image Include="..\..\..\obs\forms\images\add.ico">