diff --git a/app/node/input/multicam/multicamnode.cpp b/app/node/input/multicam/multicamnode.cpp index f125131b3..16edb4ac2 100644 --- a/app/node/input/multicam/multicamnode.cpp +++ b/app/node/input/multicam/multicamnode.cpp @@ -16,6 +16,9 @@ MultiCamNode::MultiCamNode() SetInputProperty(kCurrentInput, QStringLiteral("min"), 0); AddInput(kSourcesInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray)); + SetInputProperty(kSourcesInput, QStringLiteral("arraystart"), 1); + + monitor_ = false; } QString MultiCamNode::Name() const @@ -40,7 +43,7 @@ QString MultiCamNode::Description() const Node::ActiveElements MultiCamNode::GetActiveElementsAtTime(const QString &input, const TimeRange &r) const { - if (input == kSourcesInput) { + if (input == kSourcesInput && !monitor_) { Node::ActiveElements a; a.add(GetStandardValue(kCurrentInput).toInt()); return a; @@ -49,11 +52,98 @@ Node::ActiveElements MultiCamNode::GetActiveElementsAtTime(const QString &input, } } +QString dblToGlsl(double d) +{ + return QString::number(d, 'f'); +} + +ShaderCode MultiCamNode::GetShaderCode(const ShaderRequest &id) const +{ + QStringList pieces = id.id.split(','); + int rows = pieces.at(0).toInt(); + int cols = pieces.at(1).toInt(); + int multiplier = std::max(cols, rows); + + QStringList shader; + + shader.append(QStringLiteral("in vec2 ove_texcoord;")); + shader.append(QStringLiteral("out vec4 frag_color;")); + + for (int x=0;x 0) { + shader.append(QStringLiteral(" else")); + } + if (x == cols-1) { + shader.append(QStringLiteral(" {")); + } else { + shader.append(QStringLiteral(" if (ove_texcoord.x < %1) {").arg(dblToGlsl(double(x+1)/double(multiplier)))); + } + + for (int y=0;y 0) { + shader.append(QStringLiteral(" else")); + } + if (y == rows-1) { + shader.append(QStringLiteral(" {")); + } else { + shader.append(QStringLiteral(" if (ove_texcoord.y < %1) {").arg(dblToGlsl(double(y+1)/double(multiplier)))); + } + QString input = QStringLiteral("tex_%1_%2").arg(QString::number(y), QString::number(x)); + shader.append(QStringLiteral(" vec2 coord = vec2((ove_texcoord.x+%1)*%2, (ove_texcoord.y+%3)*%4);").arg( + dblToGlsl( - double(x)/double(multiplier)), + dblToGlsl(multiplier), + dblToGlsl( - double(y)/double(multiplier)), + dblToGlsl(multiplier) + )); + shader.append(QStringLiteral(" if (%1_enabled && coord.x >= 0.0 && coord.x < 1.0 && coord.y >= 0.0 && coord.y < 1.0) {").arg(input)); + shader.append(QStringLiteral(" frag_color = texture(%1, coord);").arg(input)); + shader.append(QStringLiteral(" } else {")); + shader.append(QStringLiteral(" discard;")); + shader.append(QStringLiteral(" }")); + shader.append(QStringLiteral(" }")); + } + + shader.append(QStringLiteral(" }")); + } + + shader.append(QStringLiteral("}")); + + return ShaderCode(shader.join('\n')); +} + void MultiCamNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const { - NodeValueArray arr = value[kSourcesInput].toArray(); - if (!arr.empty()) { - table->Push(arr.begin()->second); + if (!monitor_) { + NodeValueArray arr = value[kSourcesInput].toArray(); + if (!arr.empty()) { + table->Push(arr.begin()->second); + } + } else { + NodeValueArray arr = value[kSourcesInput].toArray(); + + int rows, cols; + GetRowsAndColumns(arr.size(), &rows, &cols); + + ShaderJob job; + + job.SetShaderID(QStringLiteral("%1,%2").arg(QString::number(rows), QString::number(cols))); + + for (size_t i=0; iPush(NodeValue::kTexture, Texture::Job(globals.vparams(), job), this); } } @@ -65,4 +155,20 @@ void MultiCamNode::Retranslate() SetInputName(kSourcesInput, tr("Sources")); } +void MultiCamNode::GetRowsAndColumns(int sources, int *rows_in, int *cols_in) +{ + int &rows = *rows_in; + int &cols = *cols_in; + + rows = 1; + cols = 1; + while (rows*cols < sources) { + if (rows < cols) { + rows++; + } else { + cols++; + } + } +} + } diff --git a/app/node/input/multicam/multicamnode.h b/app/node/input/multicam/multicamnode.h index d8895a7f5..5adf5eb12 100644 --- a/app/node/input/multicam/multicamnode.h +++ b/app/node/input/multicam/multicamnode.h @@ -20,6 +20,8 @@ public: virtual ActiveElements GetActiveElementsAtTime(const QString &input, const TimeRange &r) const override; + virtual ShaderCode GetShaderCode(const ShaderRequest &id) const override; + virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; virtual void Retranslate() override; @@ -27,6 +29,27 @@ public: static const QString kCurrentInput; static const QString kSourcesInput; + void SetMonitorMode(bool e) { monitor_ = e; } + + int GetSourceCount() const + { + return InputArraySize(kSourcesInput); + } + + static void GetRowsAndColumns(int sources, int *rows, int *cols); + void GetRowsAndColumns(int *rows, int *cols) const + { + return GetRowsAndColumns(GetSourceCount(), rows, cols); + } + + static int RowsColsToIndex(int row, int col, int total_rows, int total_cols) + { + return col + row * total_cols; + } + +private: + bool monitor_; + }; } diff --git a/app/panel/multicam/multicampanel.h b/app/panel/multicam/multicampanel.h index 1523d5ddc..b8634bbfc 100644 --- a/app/panel/multicam/multicampanel.h +++ b/app/panel/multicam/multicampanel.h @@ -12,6 +12,11 @@ class MulticamPanel : public ViewerPanelBase public: MulticamPanel(QWidget* parent = nullptr); + void SetMulticamNode(MultiCamNode *n) + { + widget_->SetMulticamNode(n); + } + protected: virtual void Retranslate() override; diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 9a0de0898..584405fd1 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -24,6 +24,7 @@ #include #include "codec/conformmanager.h" +#include "node/input/multicam/multicamnode.h" #include "node/inputdragger.h" #include "node/project/project.h" #include "render/diskmanager.h" @@ -41,7 +42,9 @@ PreviewAutoCacher::PreviewAutoCacher(QObject *parent) : use_custom_range_(false), pause_renders_(false), single_frame_render_(nullptr), - display_color_processor_(nullptr) + display_color_processor_(nullptr), + multicam_mode_(false), + ignore_cache_requests_(false) { // Set defaults SetPlayhead(0); @@ -285,6 +288,13 @@ void PreviewAutoCacher::AddNode(Node *node) // Copy node Node* copy = node->copy(); + // Fairly hacky way of getting multicam nodes to produce a monitor rather than a single source + if (multicam_mode_) { + if (MultiCamNode *m = dynamic_cast(copy)) { + m->SetMonitorMode(true); + } + } + // Add to project copy->setParent(&copied_project_); @@ -368,7 +378,9 @@ void PreviewAutoCacher::InsertIntoCopyMap(Node *node, Node *copy) Node::CopyInputs(node, copy, false); // Connect to node's cache - ConnectToNodeCache(node); + if (ignore_cache_requests_) { + ConnectToNodeCache(node); + } } void PreviewAutoCacher::ConnectToNodeCache(Node *node) diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index 6f0fc6ac7..9f68bb9e5 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -90,6 +90,9 @@ public: void SetRendersPaused(bool e); + void SetMulticamMode(bool e) { multicam_mode_ = e; } + void SetIgnoreCacheRequests(bool e) { ignore_cache_requests_ = e; } + public slots: void SetDisplayColorProcessor(ColorProcessorPtr processor) { @@ -218,6 +221,9 @@ private: ColorProcessorPtr display_color_processor_; + bool multicam_mode_; + bool ignore_cache_requests_; + private slots: /** * @brief Handler for when the NodeGraph reports a video change over a certain time range diff --git a/app/shaders/multicam.frag b/app/shaders/multicam.frag deleted file mode 100644 index f3230598f..000000000 --- a/app/shaders/multicam.frag +++ /dev/null @@ -1,7 +0,0 @@ -uniform int rows; -uniform int cols; - -void main(void) -{ - -} diff --git a/app/widget/multicam/multicamwidget.cpp b/app/widget/multicam/multicamwidget.cpp index 8c2af4dd0..ab423ed1d 100644 --- a/app/widget/multicam/multicamwidget.cpp +++ b/app/widget/multicam/multicamwidget.cpp @@ -1,13 +1,53 @@ #include "multicamwidget.h" +#include "widget/nodeparamview/nodeparamviewundo.h" namespace olive { #define super ViewerWidget MulticamWidget::MulticamWidget(QWidget *parent) : - super{parent} + super{parent}, + node_(nullptr) { + auto_cacher()->SetMulticamMode(true); + connect(display_widget(), &ViewerDisplayWidget::DragStarted, this, &MulticamWidget::DisplayClicked); +} + +RenderTicketPtr MulticamWidget::GetSingleFrame(const rational &t, bool dry) +{ + if (node_) { + return auto_cacher()->GetSingleFrame(node_, t, dry); + } else { + return super::GetSingleFrame(t, dry); + } +} + +void MulticamWidget::DisplayClicked(const QPoint &p) +{ + if (!node_) { + return; + } + + QPointF click = display_widget()->ScreenToScenePoint(p); + int width = display_widget()->GetVideoParams().width(); + int height = display_widget()->GetVideoParams().height(); + + if (click.x() < 0 || click.y() < 0 || click.x() >= width || click.y() >= height) { + return; + } + + int rows, cols; + node_->GetRowsAndColumns(&rows, &cols); + + int multi = std::max(cols, rows); + + int c = click.x() / (width/multi); + int r = click.y() / (height/multi); + + MultiUndoCommand *command = new MultiUndoCommand(); + command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(node_, node_->kCurrentInput)), node_->RowsColsToIndex(r, c, rows, cols))); + Core::instance()->undo_stack()->push(command); } } diff --git a/app/widget/multicam/multicamwidget.h b/app/widget/multicam/multicamwidget.h index 19cb08510..f53e77e9c 100644 --- a/app/widget/multicam/multicamwidget.h +++ b/app/widget/multicam/multicamwidget.h @@ -13,8 +13,19 @@ class MulticamWidget : public ViewerWidget public: explicit MulticamWidget(QWidget *parent = nullptr); + void SetMulticamNode(MultiCamNode *n) + { + node_ = n; + } + +protected: + virtual RenderTicketPtr GetSingleFrame(const rational &t, bool dry = false) override; + private: - //MultiCamNode *node_; + MultiCamNode *node_; + +private slots: + void DisplayClicked(const QPoint &p); }; diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 134874425..e70fb7387 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -580,7 +580,7 @@ void ViewerWidget::RequestNextDryRun() } else { RenderTicketWatcher *watcher = new RenderTicketWatcher(this); connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::DryRunFinished); - watcher->SetTicket(auto_cacher_->GetSingleFrame(next_time, true)); + watcher->SetTicket(GetSingleFrame(next_time, true)); dry_run_next_frame_ += playback_speed_; dry_run_watchers_.append(watcher); } @@ -1033,7 +1033,7 @@ RenderTicketPtr ViewerWidget::GetFrame(const rational &t) if (!QFileInfo::exists(cache_fn)) { // Frame hasn't been cached, start render job - return auto_cacher_->GetSingleFrame(t); + return GetSingleFrame(t); } else { // Frame has been cached, grab the frame RenderTicketPtr ticket = std::make_shared(); diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 734132dda..fe3e44ed4 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -184,6 +184,13 @@ protected: ignore_scrub_++; } + virtual RenderTicketPtr GetSingleFrame(const rational &t, bool dry = false) + { + return auto_cacher_->GetSingleFrame(t, dry); + } + + PreviewAutoCacher *auto_cacher() const { return auto_cacher_; } + private: int64_t GetTimestamp() const { diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 27ea8bad4..c3a811fd3 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -431,17 +431,13 @@ void ViewerDisplayWidget::OnPaint() // Draw gizmos if we have any if (gizmos_) { - NodeTraverser gt; - gt.SetCacheVideoParams(gizmo_params_); - - TimeRange range = GenerateGizmoTime(); - gizmo_db_ = gt.GenerateRow(gizmos_, range); - QPainter p(paint_device()); - gizmo_last_draw_transform_ = GenerateGizmoTransform(gt, range); + + GenerateGizmoTransforms(); + p.setWorldTransform(gizmo_last_draw_transform_); - gizmos_->UpdateGizmoPositions(gizmo_db_, NodeTraverser::GenerateGlobals(gizmo_params_, range)); + gizmos_->UpdateGizmoPositions(gizmo_db_, NodeTraverser::GenerateGlobals(gizmo_params_, gizmo_draw_time_)); foreach (NodeGizmo *gizmo, gizmos_->GetGizmos()) { if (gizmo->IsVisible()) { gizmo->Draw(&p); @@ -812,8 +808,7 @@ bool ViewerDisplayWidget::OnMousePress(QMouseEvent *event) add_band_ = true; } else if (gizmos_ - && (gizmo_last_draw_transform_inverted_ = gizmo_last_draw_transform_.inverted(), - current_gizmo_ = TryGizmoPress(gizmo_db_, gizmo_last_draw_transform_inverted_.map(event->pos())))) { + && (current_gizmo_ = TryGizmoPress(gizmo_db_, gizmo_last_draw_transform_inverted_.map(event->pos())))) { // Handle gizmo click gizmo_start_drag_ = event->pos(); @@ -823,7 +818,7 @@ bool ViewerDisplayWidget::OnMousePress(QMouseEvent *event) } else { // Handle standard drag - emit DragStarted(); + emit DragStarted(event->pos()); } @@ -871,7 +866,7 @@ bool ViewerDisplayWidget::OnMouseMove(QMouseEvent *event) // Signal movement if (DraggableGizmo *draggable = dynamic_cast(current_gizmo_)) { if (!gizmo_drag_started_) { - QPointF start = gizmo_start_drag_ * gizmo_last_draw_transform_inverted_; + QPointF start = ScreenToScenePoint(gizmo_start_drag_); rational gizmo_time = GetGizmoTime(); NodeTraverser t; @@ -882,17 +877,17 @@ bool ViewerDisplayWidget::OnMouseMove(QMouseEvent *event) gizmo_drag_started_ = true; } - QPointF v = event->pos() * gizmo_last_draw_transform_inverted_; + QPointF v = ScreenToScenePoint(event->pos()); switch (draggable->GetDragValueBehavior()) { case DraggableGizmo::kAbsolute: // Above value is correct break; case DraggableGizmo::kDeltaFromPrevious: - v -= gizmo_last_drag_ * gizmo_last_draw_transform_inverted_; + v -= ScreenToScenePoint(gizmo_last_drag_); gizmo_last_drag_ = event->pos(); break; case DraggableGizmo::kDeltaFromStart: - v -= gizmo_start_drag_ * gizmo_last_draw_transform_inverted_; + v -= ScreenToScenePoint(gizmo_start_drag_); break; } @@ -1182,6 +1177,21 @@ void ViewerDisplayWidget::CloseTextEditor() text_edit_ = nullptr; } +void ViewerDisplayWidget::GenerateGizmoTransforms() +{ + NodeTraverser gt; + gt.SetCacheVideoParams(gizmo_params_); + + gizmo_draw_time_ = GenerateGizmoTime(); + + if (gizmos_) { + gizmo_db_ = gt.GenerateRow(gizmos_, gizmo_draw_time_); + } + + gizmo_last_draw_transform_ = GenerateGizmoTransform(gt, gizmo_draw_time_); + gizmo_last_draw_transform_inverted_ = gizmo_last_draw_transform_.inverted(); +} + void ViewerDisplayWidget::SetShowFPS(bool e) { show_fps_ = e; @@ -1221,6 +1231,15 @@ void ViewerDisplayWidget::Pause() queue_starved_ = false; } +QPointF ViewerDisplayWidget::ScreenToScenePoint(const QPoint &p) +{ + if (gizmo_last_draw_transform_.isIdentity()) { + GenerateGizmoTransforms(); + } + + return p * gizmo_last_draw_transform_inverted_; +} + void ViewerDisplayWidget::UpdateFromQueue() { int64_t t = timer_.GetTimestampNow(); diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index ab39a2b10..04e9f6e15 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -74,6 +74,7 @@ public: void SetSafeMargins(const ViewerSafeMarginInfo& safe_margin); void SetGizmos(Node* node); + const VideoParams &GetVideoParams() const { return gizmo_params_; } void SetVideoParams(const VideoParams ¶ms); void SetTime(const rational& time); void SetSubtitleTracks(Sequence *list); @@ -131,6 +132,8 @@ public: return &timer_; } + QPointF ScreenToScenePoint(const QPoint &p); + virtual bool eventFilter(QObject *o, QEvent *e) override; public slots: @@ -182,7 +185,7 @@ signals: /** * @brief Signal emitted when the user starts dragging from the viewer */ - void DragStarted(); + void DragStarted(const QPoint &p); /** * @brief Signal emitted when a hand drag starts @@ -290,6 +293,8 @@ private: void CloseTextEditor(); + void GenerateGizmoTransforms(); + /** * @brief Internal reference to the OpenGL texture to draw. Set in SetTexture() and used in paintGL(). */ @@ -340,6 +345,7 @@ private: VideoParams gizmo_params_; QPoint gizmo_start_drag_; QPoint gizmo_last_drag_; + TimeRange gizmo_draw_time_; NodeGizmo *current_gizmo_; bool gizmo_drag_started_; QTransform gizmo_last_draw_transform_; diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index c8595a64a..a4aefc1b6 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -509,13 +509,11 @@ void MainWindow::TimelinePanelSelectionChanged(const QVector &blocks) } if (multicam) { - qDebug() << "Found multicam node!"; - //multicam_panel_->SetNode(multicam); + multicam_panel_->SetMulticamNode(multicam); multicam_panel_->ConnectViewerNode(panel->GetConnectedViewer()); } else { - qDebug() << "Found NO multicam node"; multicam_panel_->ConnectViewerNode(nullptr); - //multicam_panel_->SetNode(nullptr); + multicam_panel_->SetMulticamNode(nullptr); } } }