diff --git a/app/node/distort/CMakeLists.txt b/app/node/distort/CMakeLists.txt
index 03ede0b9a..2feb4ee34 100644
--- a/app/node/distort/CMakeLists.txt
+++ b/app/node/distort/CMakeLists.txt
@@ -18,7 +18,11 @@ add_subdirectory(cornerpin)
add_subdirectory(crop)
add_subdirectory(flip)
add_subdirectory(mask)
+add_subdirectory(ripple)
+add_subdirectory(swirl)
+add_subdirectory(tile)
add_subdirectory(transform)
+add_subdirectory(wave)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
diff --git a/app/node/distort/flip/flipdistortnode.cpp b/app/node/distort/flip/flipdistortnode.cpp
index af14263a1..e2355ade7 100644
--- a/app/node/distort/flip/flipdistortnode.cpp
+++ b/app/node/distort/flip/flipdistortnode.cpp
@@ -87,11 +87,10 @@ void FlipDistortNode::Value(const NodeValueRow &value, const NodeGlobals &global
if (job.Get(kHorizontalInput).toBool() || job.Get(kVerticalInput).toBool()) {
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
} else {
- // If we're not flipping or flopping just push the texture
- table->Push(job.Get(kTextureInput));
+ // If we're not flipping or flopping just push the texture
+ table->Push(job.Get(kTextureInput));
}
}
-
}
}
diff --git a/app/node/distort/ripple/CMakeLists.txt b/app/node/distort/ripple/CMakeLists.txt
new file mode 100644
index 000000000..1860986c6
--- /dev/null
+++ b/app/node/distort/ripple/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Olive - Non-Linear Video Editor
+# Copyright (C) 2022 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/ripple/rippledistortnode.cpp
+ node/distort/ripple/rippledistortnode.h
+ PARENT_SCOPE
+)
diff --git a/app/node/distort/ripple/rippledistortnode.cpp b/app/node/distort/ripple/rippledistortnode.cpp
new file mode 100644
index 000000000..bddb679d4
--- /dev/null
+++ b/app/node/distort/ripple/rippledistortnode.cpp
@@ -0,0 +1,130 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2022 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 "rippledistortnode.h"
+
+namespace olive {
+
+const QString RippleDistortNode::kTextureInput = QStringLiteral("tex_in");
+const QString RippleDistortNode::kEvolutionInput = QStringLiteral("evolution_in");
+const QString RippleDistortNode::kIntensityInput = QStringLiteral("intensity_in");
+const QString RippleDistortNode::kFrequencyInput = QStringLiteral("frequency_in");
+const QString RippleDistortNode::kPositionInput = QStringLiteral("position_in");
+const QString RippleDistortNode::kStretchInput = QStringLiteral("stretch_in");
+
+#define super Node
+
+RippleDistortNode::RippleDistortNode()
+{
+ AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
+
+ AddInput(kEvolutionInput, NodeValue::kFloat, 0);
+ AddInput(kIntensityInput, NodeValue::kFloat, 100);
+
+ AddInput(kFrequencyInput, NodeValue::kFloat, 1);
+ SetInputProperty(kFrequencyInput, QStringLiteral("base"), 0.01);
+
+ AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0));
+ AddInput(kStretchInput, NodeValue::kBoolean, false);
+
+ SetFlags(kVideoEffect);
+ SetEffectInput(kTextureInput);
+
+ gizmo_ = AddDraggableGizmo({
+ NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0),
+ NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1),
+ });
+ gizmo_->SetShape(PointGizmo::kAnchorPoint);
+}
+
+QString RippleDistortNode::Name() const
+{
+ return tr("Ripple");
+}
+
+QString RippleDistortNode::id() const
+{
+ return QStringLiteral("org.oliveeditor.Olive.ripple");
+}
+
+QVector RippleDistortNode::Category() const
+{
+ return {kCategoryDistort};
+}
+
+QString RippleDistortNode::Description() const
+{
+ return tr("Distorts an image with a ripple effect.");
+}
+
+void RippleDistortNode::Retranslate()
+{
+ super::Retranslate();
+
+ SetInputName(kTextureInput, tr("Input"));
+ SetInputName(kFrequencyInput, tr("Frequency"));
+ SetInputName(kIntensityInput, tr("Intensity"));
+ SetInputName(kEvolutionInput, tr("Evolution"));
+ SetInputName(kPositionInput, tr("Position"));
+ SetInputName(kStretchInput, tr("Stretch"));
+}
+
+ShaderCode RippleDistortNode::GetShaderCode(const ShaderRequest &request) const
+{
+ Q_UNUSED(request)
+ return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/ripple.frag"));
+}
+
+void RippleDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
+{
+ ShaderJob job;
+
+ job.Insert(value);
+ job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
+
+ // If there's no texture, no need to run an operation
+ if (job.Get(kTextureInput).toTexture()) {
+ // Only run shader if at least one of flip or flop are selected
+ if (!qIsNull(job.Get(kIntensityInput).toDouble())) {
+ table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
+ } else {
+ // If we're not flipping or flopping just push the texture
+ table->Push(job.Get(kTextureInput));
+ }
+ }
+}
+
+void RippleDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
+{
+ QPointF half_res(globals.resolution_by_par().x()/2, globals.resolution_by_par().y()/2);
+
+ gizmo_->SetPoint(half_res + row[kPositionInput].toVec2().toPointF());
+}
+
+void RippleDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
+{
+ NodeInputDragger &x_drag = gizmo_->GetDraggers()[0];
+ NodeInputDragger &y_drag = gizmo_->GetDraggers()[1];
+
+ x_drag.Drag(x_drag.GetStartValue().toDouble() + x);
+ y_drag.Drag(y_drag.GetStartValue().toDouble() + y);
+}
+
+}
diff --git a/app/node/distort/ripple/rippledistortnode.h b/app/node/distort/ripple/rippledistortnode.h
new file mode 100644
index 000000000..9f05e06a8
--- /dev/null
+++ b/app/node/distort/ripple/rippledistortnode.h
@@ -0,0 +1,66 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2022 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 RIPPLEDISTORTNODE_H
+#define RIPPLEDISTORTNODE_H
+
+#include "node/gizmo/point.h"
+#include "node/node.h"
+
+namespace olive {
+
+class RippleDistortNode : public Node
+{
+ Q_OBJECT
+public:
+ RippleDistortNode();
+
+ NODE_DEFAULT_FUNCTIONS(RippleDistortNode)
+
+ virtual QString Name() const override;
+ virtual QString id() const override;
+ virtual QVector Category() const override;
+ virtual QString Description() const override;
+
+ virtual void Retranslate() override;
+
+ virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
+ virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
+
+ virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
+
+ static const QString kTextureInput;
+ static const QString kEvolutionInput;
+ static const QString kIntensityInput;
+ static const QString kFrequencyInput;
+ static const QString kPositionInput;
+ static const QString kStretchInput;
+
+protected slots:
+ virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
+
+private:
+ PointGizmo *gizmo_;
+
+};
+
+}
+
+#endif // RIPPLEDISTORTNODE_H
diff --git a/app/node/distort/swirl/CMakeLists.txt b/app/node/distort/swirl/CMakeLists.txt
new file mode 100644
index 000000000..e41d599c4
--- /dev/null
+++ b/app/node/distort/swirl/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Olive - Non-Linear Video Editor
+# Copyright (C) 2022 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/swirl/swirldistortnode.cpp
+ node/distort/swirl/swirldistortnode.h
+ PARENT_SCOPE
+)
diff --git a/app/node/distort/swirl/swirldistortnode.cpp b/app/node/distort/swirl/swirldistortnode.cpp
new file mode 100644
index 000000000..658feb134
--- /dev/null
+++ b/app/node/distort/swirl/swirldistortnode.cpp
@@ -0,0 +1,125 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2022 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 "swirldistortnode.h"
+
+namespace olive {
+
+const QString SwirlDistortNode::kTextureInput = QStringLiteral("tex_in");
+const QString SwirlDistortNode::kRadiusInput = QStringLiteral("radius_in");
+const QString SwirlDistortNode::kAngleInput = QStringLiteral("angle_in");
+const QString SwirlDistortNode::kPositionInput = QStringLiteral("pos_in");
+
+#define super Node
+
+SwirlDistortNode::SwirlDistortNode()
+{
+ AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
+
+ AddInput(kRadiusInput, NodeValue::kFloat, 200);
+ SetInputProperty(kRadiusInput, QStringLiteral("min"), 0);
+
+ AddInput(kAngleInput, NodeValue::kFloat, 10);
+ SetInputProperty(kAngleInput, QStringLiteral("base"), 0.1);
+
+ AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0));
+
+ SetFlags(kVideoEffect);
+ SetEffectInput(kTextureInput);
+
+ gizmo_ = AddDraggableGizmo({
+ NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0),
+ NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1),
+ });
+ gizmo_->SetShape(PointGizmo::kAnchorPoint);
+}
+
+QString SwirlDistortNode::Name() const
+{
+ return tr("Swirl");
+}
+
+QString SwirlDistortNode::id() const
+{
+ return QStringLiteral("org.oliveeditor.Olive.swirl");
+}
+
+QVector SwirlDistortNode::Category() const
+{
+ return {kCategoryDistort};
+}
+
+QString SwirlDistortNode::Description() const
+{
+ return tr("Distorts an image along a sine wave.");
+}
+
+void SwirlDistortNode::Retranslate()
+{
+ super::Retranslate();
+
+ SetInputName(kTextureInput, tr("Input"));
+ SetInputName(kRadiusInput, tr("Radius"));
+ SetInputName(kAngleInput, tr("Angle"));
+ SetInputName(kPositionInput, tr("Position"));
+}
+
+ShaderCode SwirlDistortNode::GetShaderCode(const ShaderRequest &request) const
+{
+ Q_UNUSED(request)
+ return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/swirl.frag"));
+}
+
+void SwirlDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
+{
+ ShaderJob job;
+
+ job.Insert(value);
+ job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
+
+ // If there's no texture, no need to run an operation
+ if (job.Get(kTextureInput).toTexture()) {
+ // Only run shader if at least one of flip or flop are selected
+ if (!qIsNull(job.Get(kAngleInput).toDouble()) && !qIsNull(job.Get(kRadiusInput).toDouble())) {
+ table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
+ } else {
+ // If we're not flipping or flopping just push the texture
+ table->Push(job.Get(kTextureInput));
+ }
+ }
+}
+
+void SwirlDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
+{
+ QPointF half_res(globals.resolution_by_par().x()/2, globals.resolution_by_par().y()/2);
+
+ gizmo_->SetPoint(half_res + row[kPositionInput].toVec2().toPointF());
+}
+
+void SwirlDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
+{
+ NodeInputDragger &x_drag = gizmo_->GetDraggers()[0];
+ NodeInputDragger &y_drag = gizmo_->GetDraggers()[1];
+
+ x_drag.Drag(x_drag.GetStartValue().toDouble() + x);
+ y_drag.Drag(y_drag.GetStartValue().toDouble() + y);
+}
+
+}
diff --git a/app/node/distort/swirl/swirldistortnode.h b/app/node/distort/swirl/swirldistortnode.h
new file mode 100644
index 000000000..744704ddc
--- /dev/null
+++ b/app/node/distort/swirl/swirldistortnode.h
@@ -0,0 +1,64 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2022 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 SWIRLDISTORTNODE_H
+#define SWIRLDISTORTNODE_H
+
+#include "node/gizmo/point.h"
+#include "node/node.h"
+
+namespace olive {
+
+class SwirlDistortNode : public Node
+{
+ Q_OBJECT
+public:
+ SwirlDistortNode();
+
+ NODE_DEFAULT_FUNCTIONS(SwirlDistortNode)
+
+ virtual QString Name() const override;
+ virtual QString id() const override;
+ virtual QVector Category() const override;
+ virtual QString Description() const override;
+
+ virtual void Retranslate() override;
+
+ virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
+ virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
+
+ virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
+
+ static const QString kTextureInput;
+ static const QString kRadiusInput;
+ static const QString kAngleInput;
+ static const QString kPositionInput;
+
+protected slots:
+ virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
+
+private:
+ PointGizmo *gizmo_;
+
+};
+
+}
+
+#endif // SWIRLDISTORTNODE_H
diff --git a/app/node/distort/tile/CMakeLists.txt b/app/node/distort/tile/CMakeLists.txt
new file mode 100644
index 000000000..c2f83cbf6
--- /dev/null
+++ b/app/node/distort/tile/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Olive - Non-Linear Video Editor
+# Copyright (C) 2022 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/tile/tiledistortnode.cpp
+ node/distort/tile/tiledistortnode.h
+ PARENT_SCOPE
+)
diff --git a/app/node/distort/tile/tiledistortnode.cpp b/app/node/distort/tile/tiledistortnode.cpp
new file mode 100644
index 000000000..acdb32aee
--- /dev/null
+++ b/app/node/distort/tile/tiledistortnode.cpp
@@ -0,0 +1,165 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2022 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 "tiledistortnode.h"
+
+#include "widget/slider/floatslider.h"
+
+namespace olive {
+
+const QString TileDistortNode::kTextureInput = QStringLiteral("tex_in");
+const QString TileDistortNode::kScaleInput = QStringLiteral("scale_in");
+const QString TileDistortNode::kPositionInput = QStringLiteral("position_in");
+const QString TileDistortNode::kAnchorInput = QStringLiteral("anchor_in");
+const QString TileDistortNode::kMirrorXInput = QStringLiteral("mirrorx_in");
+const QString TileDistortNode::kMirrorYInput = QStringLiteral("mirrory_in");
+
+#define super Node
+
+TileDistortNode::TileDistortNode()
+{
+ AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
+
+ AddInput(kScaleInput, NodeValue::kFloat, 0.5);
+ SetInputProperty(kScaleInput, QStringLiteral("min"), 0);
+ SetInputProperty(kScaleInput, QStringLiteral("view"), FloatSlider::kPercentage);
+
+ AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0));
+
+ AddInput(kAnchorInput, NodeValue::kCombo, kMiddleCenter);
+
+ AddInput(kMirrorXInput, NodeValue::kBoolean, false);
+ AddInput(kMirrorYInput, NodeValue::kBoolean, false);
+
+ SetFlags(kVideoEffect);
+ SetEffectInput(kTextureInput);
+
+ gizmo_ = AddDraggableGizmo({
+ NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0),
+ NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1),
+ });
+ gizmo_->SetShape(PointGizmo::kAnchorPoint);
+}
+
+QString TileDistortNode::Name() const
+{
+ return tr("Tile");
+}
+
+QString TileDistortNode::id() const
+{
+ return QStringLiteral("org.oliveeditor.Olive.tile");
+}
+
+QVector TileDistortNode::Category() const
+{
+ return {kCategoryDistort};
+}
+
+QString TileDistortNode::Description() const
+{
+ return tr("Infinitely tile an image horizontally and vertically.");
+}
+
+void TileDistortNode::Retranslate()
+{
+ super::Retranslate();
+
+ SetInputName(kTextureInput, tr("Input"));
+ SetInputName(kScaleInput, tr("Scale"));
+ SetInputName(kPositionInput, tr("Position"));
+ SetInputName(kMirrorXInput, tr("Mirror Horizontally"));
+ SetInputName(kMirrorYInput, tr("Mirror Vertically"));
+
+ SetInputName(kAnchorInput, tr("Anchor"));
+ SetComboBoxStrings(kAnchorInput, {
+ tr("Top-Left"),
+ tr("Top-Center"),
+ tr("Top-Right"),
+ tr("Middle-Left"),
+ tr("Middle-Center"),
+ tr("Middle-Right"),
+ tr("Bottom-Left"),
+ tr("Bottom-Center"),
+ tr("Bottom-Right"),
+ });
+}
+
+ShaderCode TileDistortNode::GetShaderCode(const ShaderRequest &request) const
+{
+ Q_UNUSED(request)
+ return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/tile.frag"));
+}
+
+void TileDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
+{
+ ShaderJob job;
+
+ job.Insert(value);
+ job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
+
+ // If there's no texture, no need to run an operation
+ if (job.Get(kTextureInput).toTexture()) {
+ // Only run shader if at least one of flip or flop are selected
+ if (!qFuzzyCompare(job.Get(kScaleInput).toDouble(), 1.0)) {
+ table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
+ } else {
+ // If we're not flipping or flopping just push the texture
+ table->Push(job.Get(kTextureInput));
+ }
+ }
+}
+
+void TileDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
+{
+ QPointF res = globals.resolution_by_par().toPointF();
+ QPointF pos = row[kPositionInput].toVec2().toPointF();
+ qreal x = pos.x();
+ qreal y = pos.y();
+
+ Anchor a = static_cast(row[kAnchorInput].toInt());
+ if (a == kTopLeft || a == kTopCenter || a == kTopRight) {
+ // Do nothing
+ } else if (a == kMiddleLeft || a == kMiddleCenter || a == kMiddleRight) {
+ y += res.y()/2;
+ } else if (a == kBottomLeft || a == kBottomCenter || a == kBottomRight) {
+ y += res.y();
+ }
+ if (a == kTopLeft || a == kMiddleLeft || a == kBottomLeft) {
+ // Do nothing
+ } else if (a == kTopCenter || a == kMiddleCenter || a == kBottomCenter) {
+ x += res.x()/2;
+ } else if (a == kTopRight || a == kMiddleRight || a == kBottomRight) {
+ x += res.x();
+ }
+
+ gizmo_->SetPoint(QPointF(x, y));
+}
+
+void TileDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
+{
+ NodeInputDragger &x_drag = gizmo_->GetDraggers()[0];
+ NodeInputDragger &y_drag = gizmo_->GetDraggers()[1];
+
+ x_drag.Drag(x_drag.GetStartValue().toDouble() + x);
+ y_drag.Drag(y_drag.GetStartValue().toDouble() + y);
+}
+
+}
diff --git a/app/node/distort/tile/tiledistortnode.h b/app/node/distort/tile/tiledistortnode.h
new file mode 100644
index 000000000..44d9b9c1e
--- /dev/null
+++ b/app/node/distort/tile/tiledistortnode.h
@@ -0,0 +1,78 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2022 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 TILEDISTORTNODE_H
+#define TILEDISTORTNODE_H
+
+#include "node/gizmo/point.h"
+#include "node/node.h"
+
+namespace olive {
+
+class TileDistortNode : public Node
+{
+ Q_OBJECT
+public:
+ TileDistortNode();
+
+ NODE_DEFAULT_FUNCTIONS(TileDistortNode)
+
+ virtual QString Name() const override;
+ virtual QString id() const override;
+ virtual QVector Category() const override;
+ virtual QString Description() const override;
+
+ virtual void Retranslate() override;
+
+ virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
+ virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
+
+ virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
+
+ static const QString kTextureInput;
+ static const QString kScaleInput;
+ static const QString kPositionInput;
+ static const QString kAnchorInput;
+ static const QString kMirrorXInput;
+ static const QString kMirrorYInput;
+
+protected slots:
+ virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
+
+private:
+ enum Anchor {
+ kTopLeft,
+ kTopCenter,
+ kTopRight,
+ kMiddleLeft,
+ kMiddleCenter,
+ kMiddleRight,
+ kBottomLeft,
+ kBottomCenter,
+ kBottomRight
+ };
+
+ PointGizmo *gizmo_;
+
+};
+
+}
+
+#endif // TILEDISTORTNODE_H
diff --git a/app/node/distort/wave/CMakeLists.txt b/app/node/distort/wave/CMakeLists.txt
new file mode 100644
index 000000000..5cf818d4c
--- /dev/null
+++ b/app/node/distort/wave/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Olive - Non-Linear Video Editor
+# Copyright (C) 2022 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/wave/wavedistortnode.cpp
+ node/distort/wave/wavedistortnode.h
+ PARENT_SCOPE
+)
diff --git a/app/node/distort/wave/wavedistortnode.cpp b/app/node/distort/wave/wavedistortnode.cpp
new file mode 100644
index 000000000..6c49c1e0a
--- /dev/null
+++ b/app/node/distort/wave/wavedistortnode.cpp
@@ -0,0 +1,104 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2022 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 "wavedistortnode.h"
+
+namespace olive {
+
+const QString WaveDistortNode::kTextureInput = QStringLiteral("tex_in");
+const QString WaveDistortNode::kFrequencyInput = QStringLiteral("frequency_in");
+const QString WaveDistortNode::kIntensityInput = QStringLiteral("intensity_in");
+const QString WaveDistortNode::kEvolutionInput = QStringLiteral("evolution_in");
+const QString WaveDistortNode::kVerticalInput = QStringLiteral("vertical_in");
+
+#define super Node
+
+WaveDistortNode::WaveDistortNode()
+{
+ AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
+
+ AddInput(kFrequencyInput, NodeValue::kFloat, 10);
+ AddInput(kIntensityInput, NodeValue::kFloat, 10);
+ AddInput(kEvolutionInput, NodeValue::kFloat, 0);
+
+ AddInput(kVerticalInput, NodeValue::kCombo, false);
+
+ SetFlags(kVideoEffect);
+ SetEffectInput(kTextureInput);
+}
+
+QString WaveDistortNode::Name() const
+{
+ return tr("Wave");
+}
+
+QString WaveDistortNode::id() const
+{
+ return QStringLiteral("org.oliveeditor.Olive.wave");
+}
+
+QVector WaveDistortNode::Category() const
+{
+ return {kCategoryDistort};
+}
+
+QString WaveDistortNode::Description() const
+{
+ return tr("Distorts an image along a sine wave.");
+}
+
+void WaveDistortNode::Retranslate()
+{
+ super::Retranslate();
+
+ SetInputName(kTextureInput, tr("Input"));
+ SetInputName(kFrequencyInput, tr("Frequency"));
+ SetInputName(kIntensityInput, tr("Intensity"));
+ SetInputName(kEvolutionInput, tr("Evolution"));
+ SetInputName(kVerticalInput, tr("Direction"));
+ SetComboBoxStrings(kVerticalInput, {tr("Horizontal"), tr("Vertical")});
+}
+
+ShaderCode WaveDistortNode::GetShaderCode(const ShaderRequest &request) const
+{
+ Q_UNUSED(request)
+ return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/wave.frag"));
+}
+
+void WaveDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
+{
+ ShaderJob job;
+
+ job.Insert(value);
+
+ // If there's no texture, no need to run an operation
+ if (job.Get(kTextureInput).toTexture()) {
+ // Only run shader if at least one of flip or flop are selected
+ if (!qIsNull(job.Get(kIntensityInput).toDouble())) {
+ table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
+ } else {
+ // If we're not flipping or flopping just push the texture
+ table->Push(job.Get(kTextureInput));
+ }
+ }
+
+}
+
+}
diff --git a/app/node/distort/wave/wavedistortnode.h b/app/node/distort/wave/wavedistortnode.h
new file mode 100644
index 000000000..aa253dc0e
--- /dev/null
+++ b/app/node/distort/wave/wavedistortnode.h
@@ -0,0 +1,56 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2022 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 WAVEDISTORTNODE_H
+#define WAVEDISTORTNODE_H
+
+#include "node/node.h"
+
+namespace olive {
+
+class WaveDistortNode : public Node
+{
+ Q_OBJECT
+public:
+ WaveDistortNode();
+
+ NODE_DEFAULT_FUNCTIONS(WaveDistortNode)
+
+ virtual QString Name() const override;
+ virtual QString id() const override;
+ virtual QVector Category() const override;
+ virtual QString Description() const override;
+
+ virtual void Retranslate() override;
+
+ virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
+ virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
+
+ static const QString kTextureInput;
+ static const QString kFrequencyInput;
+ static const QString kIntensityInput;
+ static const QString kEvolutionInput;
+ static const QString kVerticalInput;
+
+};
+
+}
+
+#endif // WAVEDISTORTNODE_H
diff --git a/app/node/factory.cpp b/app/node/factory.cpp
index 41d6f55a5..b67090ba8 100644
--- a/app/node/factory.cpp
+++ b/app/node/factory.cpp
@@ -35,7 +35,11 @@
#include "distort/crop/cropdistortnode.h"
#include "distort/flip/flipdistortnode.h"
#include "distort/mask/mask.h"
+#include "distort/ripple/rippledistortnode.h"
+#include "distort/swirl/swirldistortnode.h"
+#include "distort/tile/tiledistortnode.h"
#include "distort/transform/transformdistortnode.h"
+#include "distort/wave/wavedistortnode.h"
#include "effect/opacity/opacityeffect.h"
#include "filter/blur/blur.h"
#include "filter/dropshadow/dropshadowfilter.h"
@@ -294,6 +298,14 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
return new DropShadowFilter();
case kTimeFormat:
return new TimeFormatNode();
+ case kWaveDistort:
+ return new WaveDistortNode();
+ case kTileDistort:
+ return new TileDistortNode();
+ case kSwirlDistort:
+ return new SwirlDistortNode();
+ case kRippleDistort:
+ return new RippleDistortNode();
case kInternalNodeCount:
break;
diff --git a/app/node/factory.h b/app/node/factory.h
index b6f6037f0..192765ab4 100644
--- a/app/node/factory.h
+++ b/app/node/factory.h
@@ -76,6 +76,10 @@ public:
kMaskDistort,
kDropShadowFilter,
kTimeFormat,
+ kWaveDistort,
+ kRippleDistort,
+ kTileDistort,
+ kSwirlDistort,
// Count value
kInternalNodeCount
diff --git a/app/shaders/ripple.frag b/app/shaders/ripple.frag
new file mode 100644
index 000000000..2fa4dc31b
--- /dev/null
+++ b/app/shaders/ripple.frag
@@ -0,0 +1,38 @@
+uniform float evolution_in;
+uniform float intensity_in;
+uniform float frequency_in;
+uniform vec2 position_in;
+uniform bool stretch_in;
+
+uniform vec2 resolution_in;
+uniform sampler2D tex_in;
+
+in vec2 ove_texcoord;
+out vec4 frag_color;
+
+void main(void) {
+ vec2 center = position_in/resolution_in;
+
+ vec2 adj_texcoord = ove_texcoord;
+
+ adj_texcoord -= 0.5;
+ if (!stretch_in) {
+ // Adjust by aspect ratio
+ float ar = (resolution_in.x/resolution_in.y);
+ if (resolution_in.x > resolution_in.y) {
+ adj_texcoord.y /= ar;
+ center.y /= ar;
+ } else {
+ adj_texcoord.x *= ar;
+ center.x *= ar;
+ }
+ }
+ adj_texcoord += 0.5;
+ center += 0.5;
+
+ adj_texcoord -= center;
+
+ float len = length(adj_texcoord);
+ vec2 uv = ove_texcoord + (adj_texcoord/len)*cos((frequency_in)*(len*12.0-evolution_in))*(intensity_in*0.0005);
+ frag_color = texture(tex_in, uv);
+}
diff --git a/app/shaders/swirl.frag b/app/shaders/swirl.frag
new file mode 100644
index 000000000..910f10a6e
--- /dev/null
+++ b/app/shaders/swirl.frag
@@ -0,0 +1,29 @@
+// Swirl effect parameters
+uniform float radius_in;
+uniform float angle_in;
+uniform vec2 pos_in;
+uniform vec2 resolution_in;
+
+uniform sampler2D tex_in;
+
+in vec2 ove_texcoord;
+out vec4 frag_color;
+
+void main(void) {
+ vec2 center = resolution_in*0.5 + pos_in;
+
+ vec2 uv = ove_texcoord;
+
+ vec2 tc = uv * resolution_in;
+ tc -= center;
+ float dist = length(tc);
+ if (dist < radius_in) {
+ float percent = (radius_in - dist) / radius_in;
+ float theta = percent * percent * -angle_in;
+ float s = sin(theta);
+ float c = cos(theta);
+ tc = vec2(dot(tc, vec2(c, -s)), dot(tc, vec2(s, c)));
+ }
+ tc += center;
+ frag_color = texture(tex_in, tc / resolution_in);
+}
diff --git a/app/shaders/tile.frag b/app/shaders/tile.frag
new file mode 100644
index 000000000..d0fcb0f08
--- /dev/null
+++ b/app/shaders/tile.frag
@@ -0,0 +1,61 @@
+uniform float scale_in;
+uniform vec2 position_in;
+uniform vec2 resolution_in;
+uniform bool mirrorx_in;
+uniform bool mirrory_in;
+uniform int anchor_in;
+
+uniform sampler2D tex_in;
+
+in vec2 ove_texcoord;
+out vec4 frag_color;
+
+#define TOP_LEFT 0
+#define TOP_CENTER 1
+#define TOP_RIGHT 2
+#define MIDDLE_LEFT 3
+#define MIDDLE_CENTER 4
+#define MIDDLE_RIGHT 5
+#define BOTTOM_LEFT 6
+#define BOTTOM_CENTER 7
+#define BOTTOM_RIGHT 8
+
+void main(void) {
+ vec2 coord = ove_texcoord;
+
+ vec2 offset;
+
+ if (anchor_in == TOP_LEFT || anchor_in == TOP_CENTER || anchor_in == TOP_RIGHT) {
+ offset.y = 0.0;
+ } else if (anchor_in == MIDDLE_LEFT || anchor_in == MIDDLE_CENTER || anchor_in == MIDDLE_RIGHT) {
+ offset.y = 0.5;
+ } else if (anchor_in == BOTTOM_LEFT || anchor_in == BOTTOM_CENTER || anchor_in == BOTTOM_RIGHT) {
+ offset.y = 1.0;
+ }
+
+ if (anchor_in == TOP_LEFT || anchor_in == MIDDLE_LEFT || anchor_in == BOTTOM_LEFT) {
+ offset.x = 0.0;
+ } else if (anchor_in == TOP_CENTER || anchor_in == MIDDLE_CENTER || anchor_in == BOTTOM_CENTER) {
+ offset.x = 0.5;
+ } else if (anchor_in == TOP_RIGHT || anchor_in == MIDDLE_RIGHT || anchor_in == BOTTOM_RIGHT) {
+ offset.x = 1.0;
+ }
+
+ coord -= position_in/resolution_in;
+
+ coord -= offset;
+ coord /= scale_in;
+ coord += offset;
+
+ vec2 modcoord = mod(coord, 1.0);
+
+ if (mirrorx_in && mod(coord.x, 2.0) > 1.0) {
+ modcoord.x = 1.0 - modcoord.x;
+ }
+
+ if (mirrory_in && mod(coord.y, 2.0) > 1.0) {
+ modcoord.y = 1.0 - modcoord.y;
+ }
+
+ frag_color = vec4(texture(tex_in, modcoord));
+}
diff --git a/app/shaders/wave.frag b/app/shaders/wave.frag
new file mode 100644
index 000000000..386a3e534
--- /dev/null
+++ b/app/shaders/wave.frag
@@ -0,0 +1,25 @@
+uniform float frequency_in;
+uniform float intensity_in;
+uniform float evolution_in;
+uniform bool vertical_in;
+
+uniform sampler2D tex_in;
+
+in vec2 ove_texcoord;
+out vec4 frag_color;
+
+void main(void) {
+ vec2 pos = ove_texcoord;
+
+ if (vertical_in) {
+ pos.x -= sin((ove_texcoord.y-(evolution_in*0.01))*frequency_in)*intensity_in*0.01;
+ } else {
+ pos.y -= sin((ove_texcoord.x-(evolution_in*0.01))*frequency_in)*intensity_in*0.01;
+ }
+
+ if (pos.x < 0.0 || pos.x >= 1.0 || pos.y < 0.0 || pos.y >= 1.0) {
+ discard;
+ } else {
+ frag_color = texture(tex_in, pos);
+ }
+}