/*** Olive - Non-Linear Video Editor Copyright (C) 2019 Olive Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ***/ #include "transformeffect.h" #include #include #include #include #include #include #include #include #include "ui/collapsiblewidget.h" #include "timeline/clip.h" #include "timeline/sequence.h" #include "project/footage.h" #include "global/math.h" #include "ui/labelslider.h" #include "ui/comboboxex.h" #include "panels/project.h" #include "global/debug.h" #include "panels/panels.h" #include "panels/viewer.h" #include "ui/viewerwidget.h" TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) { SetFlags(Effect::CoordsFlag); position = new Vec2Input(this, "pos", tr("Position")); scale = new Vec2Input(this, "scale", tr("Scale")); scale->SetMinimum(0); scale->SetDefault(100); uniform_scale_field = new BoolInput(this, "uniformscale", tr("Uniform Scale")); connect(uniform_scale_field, SIGNAL(Toggled(bool)), scale, SLOT(SetSingleValueMode(bool))); uniform_scale_field->SetValueAt(0, true); rotation = new DoubleInput(this, "rotation", tr("Rotation")); anchor_point = new Vec2Input(this, "anchor", tr("Anchor Point")); anchor_point->SetDefault(0); // opacity opacity = new DoubleInput(this, "opacity", tr("Opacity")); opacity->SetMinimum(0); opacity->SetMaximum(100); opacity->SetDefault(100); // TEMP - Create matrix output EffectRow* matrix_output = new EffectRow(this, "matrix", "Matrix", false, false); matrix_output->SetOutputDataType(olive::nodes::kMatrix); // set up gizmos top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); top_left_gizmo->set_cursor(Qt::SizeFDiagCursor); top_left_gizmo->x_field1 = static_cast(scale->Field(0)); top_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); top_center_gizmo->set_cursor(Qt::SizeVerCursor); top_center_gizmo->y_field1 = static_cast(scale->Field(0)); top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); top_right_gizmo->set_cursor(Qt::SizeBDiagCursor); top_right_gizmo->x_field1 = static_cast(scale->Field(0)); bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); bottom_left_gizmo->set_cursor(Qt::SizeBDiagCursor); bottom_left_gizmo->x_field1 = static_cast(scale->Field(0)); bottom_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); bottom_center_gizmo->set_cursor(Qt::SizeVerCursor); bottom_center_gizmo->y_field1 = static_cast(scale->Field(0)); bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); bottom_right_gizmo->set_cursor(Qt::SizeFDiagCursor); bottom_right_gizmo->x_field1 = static_cast(scale->Field(0)); left_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); left_center_gizmo->set_cursor(Qt::SizeHorCursor); left_center_gizmo->x_field1 = static_cast(scale->Field(0)); right_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); right_center_gizmo->set_cursor(Qt::SizeHorCursor); right_center_gizmo->x_field1 = static_cast(scale->Field(0)); anchor_gizmo = add_gizmo(GIZMO_TYPE_TARGET); anchor_gizmo->set_cursor(Qt::SizeAllCursor); anchor_gizmo->x_field1 = static_cast(anchor_point->Field(0)); anchor_gizmo->y_field1 = static_cast(anchor_point->Field(1)); anchor_gizmo->x_field2 = static_cast(position->Field(0)); anchor_gizmo->y_field2 = static_cast(position->Field(1)); rotate_gizmo = add_gizmo(GIZMO_TYPE_DOT); rotate_gizmo->color = Qt::green; rotate_gizmo->set_cursor(Qt::SizeAllCursor); rotate_gizmo->x_field1 = static_cast(rotation->Field(0)); rect_gizmo = add_gizmo(GIZMO_TYPE_POLY); rect_gizmo->x_field1 = static_cast(position->Field(0)); rect_gizmo->y_field1 = static_cast(position->Field(1)); connect(uniform_scale_field, SIGNAL(Toggled(bool)), this, SLOT(toggle_uniform_scale(bool))); refresh(); } void TransformEffect::refresh() { if (parent_clip != nullptr && parent_clip->track()->sequence() != nullptr) { position->SetDefault({float(parent_clip->track()->sequence()->width)*0.5f, float(parent_clip->track()->sequence()->height)*0.5f}); double x_percent_multipler = 200.0 / parent_clip->track()->sequence()->width; double y_percent_multipler = 200.0 / parent_clip->track()->sequence()->height; top_left_gizmo->x_field_multi1 = -x_percent_multipler; top_left_gizmo->y_field_multi1 = -y_percent_multipler; top_center_gizmo->y_field_multi1 = -y_percent_multipler; top_right_gizmo->x_field_multi1 = x_percent_multipler; top_right_gizmo->y_field_multi1 = -y_percent_multipler; bottom_left_gizmo->x_field_multi1 = -x_percent_multipler; bottom_left_gizmo->y_field_multi1 = y_percent_multipler; bottom_center_gizmo->y_field_multi1 = y_percent_multipler; bottom_right_gizmo->x_field_multi1 = x_percent_multipler; bottom_right_gizmo->y_field_multi1 = y_percent_multipler; left_center_gizmo->x_field_multi1 = -x_percent_multipler; right_center_gizmo->x_field_multi1 = x_percent_multipler; rotate_gizmo->x_field_multi1 = x_percent_multipler; } } void TransformEffect::toggle_uniform_scale(bool enabled) { scale->SetSingleValueMode(enabled); DoubleField* scale_x = static_cast(scale->Field(0)); DoubleField* scale_y = static_cast(scale->Field(1)); top_center_gizmo->y_field1 = enabled ? scale_x : scale_y; bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y; top_left_gizmo->y_field1 = enabled ? nullptr : scale_y; top_right_gizmo->y_field1 = enabled ? nullptr : scale_y; bottom_left_gizmo->y_field1 = enabled ? nullptr : scale_y; bottom_right_gizmo->y_field1 = enabled ? nullptr : scale_y; } void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, int) { // position coords.matrix.translate(position->GetVector2DAt(timecode) - QVector2D(parent_clip->track()->sequence()->width*0.5f, parent_clip->track()->sequence()->height*0.5f)); // anchor point QVector2D anchor_val = anchor_point->GetVector2DAt(timecode); coords.vertex_top_left -= anchor_val; coords.vertex_top_right -= anchor_val; coords.vertex_bottom_left -= anchor_val; coords.vertex_bottom_right -= anchor_val; // rotation coords.matrix.rotate(QQuaternion::fromEulerAngles(0, 0, rotation->GetDoubleAt(timecode))); // scale coords.matrix.scale(scale->GetVector2DAt(timecode)*0.01); // opacity coords.opacity *= float(opacity->GetDoubleAt(timecode)*0.01); } QVector3D LerpVector3D(const QVector3D& a, const QVector3D& b, float t) { return QVector3D( float_lerp(a.x(), b.x(), t), float_lerp(a.y(), b.y(), t), float_lerp(a.z(), b.z(), t) ); } void TransformEffect::gizmo_draw(double, GLTextureCoords& coords) { top_left_gizmo->world_pos[0] = coords.vertex_top_left; top_right_gizmo->world_pos[0] = coords.vertex_top_right; bottom_right_gizmo->world_pos[0] = coords.vertex_bottom_right; bottom_left_gizmo->world_pos[0] = coords.vertex_bottom_left; top_center_gizmo->world_pos[0] = LerpVector3D(coords.vertex_top_left, coords.vertex_top_right, 0.5); right_center_gizmo->world_pos[0] = LerpVector3D(coords.vertex_top_right, coords.vertex_bottom_right, 0.5); bottom_center_gizmo->world_pos[0] = LerpVector3D(coords.vertex_bottom_right, coords.vertex_bottom_left, 0.5); left_center_gizmo->world_pos[0] = LerpVector3D(coords.vertex_bottom_left, coords.vertex_top_left, 0.5); rotate_gizmo->world_pos[0] = QVector3D( float_lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), 0.5f), float_lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1f), 0.0f ); rect_gizmo->world_pos[0] = coords.vertex_top_left; rect_gizmo->world_pos[1] = coords.vertex_top_right; rect_gizmo->world_pos[2] = coords.vertex_bottom_right; rect_gizmo->world_pos[3] = coords.vertex_bottom_left; }