diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 8e42cd998..0ae6aff1b 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -17,6 +17,7 @@ add_subdirectory(blend) add_subdirectory(block) add_subdirectory(color) +add_subdirectory(distort) add_subdirectory(generator) add_subdirectory(input) add_subdirectory(output) diff --git a/app/node/distort/CMakeLists.txt b/app/node/distort/CMakeLists.txt new file mode 100644 index 000000000..f43e36675 --- /dev/null +++ b/app/node/distort/CMakeLists.txt @@ -0,0 +1,22 @@ +# 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 . + +add_subdirectory(transform) + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + PARENT_SCOPE +) diff --git a/app/node/distort/transform/CMakeLists.txt b/app/node/distort/transform/CMakeLists.txt new file mode 100644 index 000000000..bfa0cfc5a --- /dev/null +++ b/app/node/distort/transform/CMakeLists.txt @@ -0,0 +1,22 @@ +# 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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/distort/transform/transform.h + node/distort/transform/transform.cpp + PARENT_SCOPE +) diff --git a/app/node/distort/transform/transform.cpp b/app/node/distort/transform/transform.cpp new file mode 100644 index 000000000..a0dab0602 --- /dev/null +++ b/app/node/distort/transform/transform.cpp @@ -0,0 +1,87 @@ +/*** + + 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 "transform.h" + +#include +#include + +TransformDistort::TransformDistort() +{ + position_input_ = new NodeInput("pos_in"); + position_input_->add_data_input(NodeParam::kVec2); + AddParameter(position_input_); + + rotation_input_ = new NodeInput("rot_in"); + rotation_input_->add_data_input(NodeParam::kFloat); + AddParameter(rotation_input_); + + scale_input_ = new NodeInput("scale_in"); + scale_input_->add_data_input(NodeParam::kVec2); + AddParameter(scale_input_); + + anchor_input_ = new NodeInput("anchor_in"); + anchor_input_->add_data_input(NodeParam::kVec2); + AddParameter(anchor_input_); + + matrix_output_ = new NodeOutput("matrix_out"); + matrix_output_->set_data_type(NodeParam::kMatrix); + AddParameter(matrix_output_); +} + +QString TransformDistort::Name() +{ + return tr("Transform"); +} + +QString TransformDistort::id() +{ + return "org.olivevideoeditor.Olive.transform"; +} + +QString TransformDistort::Category() +{ + return tr("Distort"); +} + +QString TransformDistort::Description() +{ + return tr("Apply transformations to position, rotation, and scale."); +} + +NodeOutput *TransformDistort::matrix_output() +{ + return matrix_output_; +} + +QVariant TransformDistort::Value(NodeOutput *output, const rational &time) +{ + if (output == matrix_output_) { + QMatrix4x4 mat; + + mat.translate(position_input_->get_value(time).value()); + //mat.rotate(0) + //mat.scale() + + return mat; + } + + return 0; +} diff --git a/app/node/distort/transform/transform.h b/app/node/distort/transform/transform.h new file mode 100644 index 000000000..2f1ae34ff --- /dev/null +++ b/app/node/distort/transform/transform.h @@ -0,0 +1,55 @@ +/*** + + 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 . + +***/ + +#ifndef TRANSFORMDISTORT_H +#define TRANSFORMDISTORT_H + +#include "node/node.h" + +class TransformDistort : public Node +{ + Q_OBJECT +public: + TransformDistort(); + + virtual QString Name() override; + virtual QString id() override; + virtual QString Category() override; + virtual QString Description() override; + + NodeOutput* matrix_output(); + +protected: + virtual QVariant Value(NodeOutput *output, const rational &time) override; + +private: + NodeInput* position_input_; + + NodeInput* rotation_input_; + + NodeInput* scale_input_; + + NodeInput* anchor_input_; + + NodeOutput* matrix_output_; + +}; + +#endif // TRANSFORMDISTORT_H diff --git a/app/node/input.h b/app/node/input.h index 6c56d6eca..f65c00c43 100644 --- a/app/node/input.h +++ b/app/node/input.h @@ -31,6 +31,15 @@ class NodeInput : public NodeParam { Q_OBJECT public: + /** + * @brief NodeInput Constructor + * + * @param id + * + * Unique ID associated with this parameter for this Node. This ID only has to be unique within this Node. Used for + * saving/loading data from this Node so that parameter order can be changed without issues loading data saved by an + * older version. This of course assumes that parameters don't change their ID. + */ NodeInput(const QString &id); /** diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index ffec28176..34a12e192 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -226,7 +226,7 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &time) internal_tex_.Bind(); // Use pipeline to blit using transformation matrix from input - QMatrix4x4 transform; + QMatrix4x4 transform = matrix_input_->get_value(time).value(); if (renderer->mode() == olive::RenderMode::kOffline) { olive::gl::OCIOBlit(pipeline_, ocio_texture_, false, transform); } else { diff --git a/app/node/param.cpp b/app/node/param.cpp index d57d1effa..e8b51704b 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -227,6 +227,9 @@ QString NodeParam::GetDefaultDataTypeName(const DataType& type) case kFootage: return tr("Footage"); case kTrack: return tr("Track"); case kRational: return tr("Rational"); + case kVec2: return tr("Vector2D"); + case kVec3: return tr("Vector3D"); + case kVec4: return tr("Vector4D"); case kAny: return tr("Any"); } diff --git a/app/node/param.h b/app/node/param.h index d5977f7b6..bbc59f291 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -56,19 +56,55 @@ public: */ enum DataType { kNone, + + /// Resolves to `int` kInt, + + /// Resolves to `double` kFloat, + + /// Resolves to TBA kColor, + + /// Resolves to `QString` kString, + + /// Resolves to `bool` kBoolean, + + /// Resolves to TBA kFont, + + /// Resolves to `QString` filename kFile, + + /// Resolves to `RenderTexturePtr` kTexture, + + /// Resolves to `QMatrix4x4` kMatrix, + + /// Resolves to `Block*` kBlock, + + /// Resolves to `Footage*` kFootage, + + /// Resolves to `TrackOutput*` kTrack, + + /// Resolves to `rational` kRational, + + /// Resolves to `QVector2D` + kVec2, + + /// Resolves to `QVector3D` + kVec3, + + /// Resolves to `QVector4D` + kVec4, + kAny }; diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 572aded01..1a3d8cd07 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -69,6 +69,66 @@ void NodeParamViewWidgetBridge::CreateWidgets() connect(slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback())); break; } + case NodeParam::kVec2: + { + QVector2D vec2 = base_input->get_value(0).value(); + + FloatSlider* x_slider = new FloatSlider(); + x_slider->SetValue(static_cast(vec2.x())); + widgets_.append(x_slider); + connect(x_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback())); + + FloatSlider* y_slider = new FloatSlider(); + y_slider->SetValue(static_cast(vec2.y())); + widgets_.append(y_slider); + connect(y_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback())); + break; + } + case NodeParam::kVec3: + { + QVector3D vec3 = base_input->get_value(0).value(); + + FloatSlider* x_slider = new FloatSlider(); + x_slider->SetValue(static_cast(vec3.x())); + widgets_.append(x_slider); + connect(x_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback())); + + FloatSlider* y_slider = new FloatSlider(); + y_slider->SetValue(static_cast(vec3.y())); + widgets_.append(y_slider); + connect(y_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback())); + + FloatSlider* z_slider = new FloatSlider(); + z_slider->SetValue(static_cast(vec3.z())); + widgets_.append(z_slider); + connect(z_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback())); + break; + } + case NodeParam::kVec4: + { + QVector4D vec4 = base_input->get_value(0).value(); + + FloatSlider* x_slider = new FloatSlider(); + x_slider->SetValue(static_cast(vec4.x())); + widgets_.append(x_slider); + connect(x_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback())); + + FloatSlider* y_slider = new FloatSlider(); + y_slider->SetValue(static_cast(vec4.y())); + widgets_.append(y_slider); + connect(y_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback())); + + FloatSlider* z_slider = new FloatSlider(); + z_slider->SetValue(static_cast(vec4.z())); + widgets_.append(z_slider); + connect(z_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback())); + + FloatSlider* w_slider = new FloatSlider(); + w_slider->SetValue(static_cast(vec4.w())); + widgets_.append(w_slider); + connect(w_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback())); + break; + } case NodeParam::kFile: // FIXME: File selector break; @@ -143,6 +203,69 @@ void NodeParamViewWidgetBridge::WidgetCallback() input->set_value(float_slider->GetValue()); break; } + case NodeParam::kVec2: + { + // Widgets are two FloatSliders + FloatSlider* slider = static_cast(sender()); + + QVector2D val = input->get_value(0).value(); + + if (slider == widgets_.at(0)) { + // Slider is X slider + val.setX(static_cast(slider->GetValue())); + } else { + // Slider is Y slider + val.setY(static_cast(slider->GetValue())); + } + + input->set_value(val); + break; + } + case NodeParam::kVec3: + { + // Widgets are three FloatSliders + FloatSlider* slider = static_cast(sender()); + + QVector3D val = input->get_value(0).value(); + + if (slider == widgets_.at(0)) { + // Slider is X slider + val.setX(static_cast(slider->GetValue())); + } else if (slider == widgets_.at(1)) { + // Slider is Y slider + val.setY(static_cast(slider->GetValue())); + } else { + // Slider is Z slider + val.setZ(static_cast(slider->GetValue())); + } + + input->set_value(val); + break; + } + case NodeParam::kVec4: + { + // Widgets are three FloatSliders + FloatSlider* slider = static_cast(sender()); + + QVector4D val = input->get_value(0).value(); + + if (slider == widgets_.at(0)) { + // Slider is X slider + val.setX(static_cast(slider->GetValue())); + } else if (slider == widgets_.at(1)) { + // Slider is Y slider + val.setY(static_cast(slider->GetValue())); + } else if (slider == widgets_.at(2)) { + // Slider is Z slider + val.setZ(static_cast(slider->GetValue())); + } else { + // Slider is W slider + val.setW(static_cast(slider->GetValue())); + } + + input->set_value(val); + break; + } case NodeParam::kFile: // FIXME: File selector break; diff --git a/app/widget/timelineview/tool/import.cpp b/app/widget/timelineview/tool/import.cpp index ecc6f956d..c6750f8d8 100644 --- a/app/widget/timelineview/tool/import.cpp +++ b/app/widget/timelineview/tool/import.cpp @@ -24,6 +24,7 @@ #include "config/config.h" #include "common/qtversionabstraction.h" +#include "node/distort/transform/transform.h" #include "node/input/media/media.h" TimelineView::ImportTool::ImportTool(TimelineView *parent) : @@ -155,15 +156,18 @@ void TimelineView::ImportTool::DragDrop(QDropEvent *event) foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) { ClipBlock* clip = new ClipBlock(); MediaInput* media = new MediaInput(); + TransformDistort* transform = new TransformDistort(); // Set parents to node_memory_manager in case no TimelineOutput receives this signal clip->setParent(&node_memory_manager); media->setParent(&node_memory_manager); + transform->setParent(&node_memory_manager); clip->set_length(ghost->Length()); media->SetFootage(ghost->data(0).value()->footage()); NodeParam::ConnectEdge(media->texture_output(), clip->texture_input()); + NodeParam::ConnectEdge(transform->matrix_output(), media->matrix_input()); if (event->keyboardModifiers() & Qt::ControlModifier) { //emit parent()->RequestInsertBlockAtTime(clip, ghost->GetAdjustedIn());