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());