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); + } +}