feat: hide non-texture OFX params from node graph + host coordinate standardization

Hide non-texture OFX params from node graph
  --------------------------------------------
  OFX plugins like ColorCorrect expose dozens of scalar parameters as
  node inputs, making nodes extremely tall and pushing Source/Mask far
  down. Previously attempted via kInputFlagHidden, but that also hid
  them from the parameter panel.

  Fix: move the filter to NodeViewItem::IsInputValid() instead.
  For OFX plugin nodes (getPluginInstance() != nullptr), only
  kTexture inputs are rendered as ports. Scalar parameters remain
  fully visible in the parameter panel.

  Files: app/widget/nodeview/nodeviewitem.cpp
         app/node/plugins/Plugin.cpp

  Standardize OFX host coordinate system
  --------------------------------------
  Olive's OFX host had partial and inconsistent coordinate handling.

  1. Fix Project coordinate methods
     - getProjectSize() / getProjectExtent() / getProjectOffset()
       now multiply X by pixel_aspect_ratio(), returning canonical
       coordinates per the OFX spec.

  2. Fix Clip default RoD
     - OliveClipInstance::getRegionOfDefinition() default now returns
       {0, 0, width*PAR, height} instead of raw pixel coords.

  3. Add parameter coordinate system conversion
     - DoubleInstance / Double2DInstance / Double3DInstance now check
       _descriptor.getDefaultCoordinateSystem().
     - For kOfxParamCoordinatesNormalised:
         get: internal pixel value -> normalised (divide by extent)
         set: normalised plugin value -> pixel (multiply by extent)
     - DefaultValueForParam() also converts normalised defaults to
       canonical before storing in Node, keeping Olive internal/UI
       values consistently in pixel space.

  Files: app/pluginSupport/OlivePluginInstance.cpp
         app/pluginSupport/OliveClip.cpp
         app/pluginSupport/paraminstance.h
         app/node/plugins/Plugin.cpp
This commit is contained in:
2026-05-14 21:54:18 +08:00
parent e012f16083
commit 4aa4d59770
5 changed files with 171 additions and 18 deletions
+35 -1
View File
@@ -21,6 +21,7 @@
#include "render/rendermanager.h"
#include "render/job/pluginjob.h"
#include "pluginSupport/OlivePluginInstance.h"
#include "common/Current.h"
#include <algorithm>
#include <cstring>
@@ -34,6 +35,24 @@
namespace {
QHash<QString, QHash<QString, QVariant>> g_plugin_param_defaults;
static bool IsNormalisedCoordSystem(const OFX::Host::Param::Base *param)
{
return param->getDefaultCoordinateSystem() ==
kOfxParamCoordinatesNormalised;
}
static void GetProjectExtent(double &xSize, double &ySize)
{
auto &vp = Current::getInstance().currentVideoParams();
xSize = vp.width() * vp.pixel_aspect_ratio().toDouble();
ySize = vp.height();
}
static double ToCanonical(double normalised, double extent)
{
return extent > 0 ? normalised * extent : normalised;
}
QVariant DefaultValueForParam(const OFX::Host::Param::Base *param)
{
if (!param) {
@@ -50,7 +69,13 @@ QVariant DefaultValueForParam(const OFX::Host::Param::Base *param)
return props.getIntProperty(kOfxParamPropDefault) != 0;
}
if (ofxType == kOfxParamTypeDouble) {
return props.getDoubleProperty(kOfxParamPropDefault);
double val = props.getDoubleProperty(kOfxParamPropDefault);
if (IsNormalisedCoordSystem(param)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
val = ToCanonical(val, xSize);
}
return val;
}
if (ofxType == kOfxParamTypeString ||
ofxType == kOfxParamTypeStrChoice ||
@@ -81,6 +106,15 @@ QVariant DefaultValueForParam(const OFX::Host::Param::Base *param)
if (is_double) {
double values[3] = {0.0, 0.0, 0.0};
props.getDoublePropertyN(kOfxParamPropDefault, values, count);
if (IsNormalisedCoordSystem(param)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
values[0] = ToCanonical(values[0], xSize);
values[1] = ToCanonical(values[1], ySize);
if (count == 3) {
values[2] = ToCanonical(values[2], xSize);
}
}
if (count == 2) {
return QVector2D(values[0], values[1]);
}
+2 -1
View File
@@ -538,7 +538,8 @@ olive::plugin::OliveClipInstance::getRegionOfDefinition(OfxTime time) const
}
OfxRectD regionOfDefinition;
regionOfDefinition.x1 = regionOfDefinition.y1 = 0;
regionOfDefinition.x2 = params_.width();
double par = params_.pixel_aspect_ratio().toDouble();
regionOfDefinition.x2 = params_.width() * par;
regionOfDefinition.y2 = params_.height();
return regionOfDefinition;
}
+6 -3
View File
@@ -276,17 +276,20 @@ OfxStatus OlivePluginInstance::clearPersistentMessage()
}
void OlivePluginInstance::getProjectSize(double &xSize, double &ySize) const
{
xSize =params_.width();
double par = params_.pixel_aspect_ratio().toDouble();
xSize = params_.width() * par;
ySize = params_.height();
}
void OlivePluginInstance::getProjectOffset(double &xOffset, double &yOffset) const
{
xOffset =params_.x();
double par = params_.pixel_aspect_ratio().toDouble();
xOffset = params_.x() * par;
yOffset = params_.y();
}
void OlivePluginInstance::getProjectExtent(double &xSize, double &ySize) const
{
xSize =params_.width();
double par = params_.pixel_aspect_ratio().toDouble();
xSize = params_.width() * par;
ySize = params_.height();
}
double OlivePluginInstance::getProjectPixelAspectRatio() const
+113 -9
View File
@@ -32,12 +32,38 @@
#include "node/plugins/Plugin.h"
#include "core.h"
#include "undo/undocommand.h"
#include "common/Current.h"
#include <iostream>
#include <qlogging.h>
namespace olive
{
namespace plugin
{
inline bool IsNormalisedCoordinateSystem(
const OFX::Host::Param::Descriptor &descriptor)
{
return descriptor.getDefaultCoordinateSystem() ==
kOfxParamCoordinatesNormalised;
}
inline void GetProjectExtent(double &xSize, double &ySize)
{
auto &vp = Current::getInstance().currentVideoParams();
xSize = vp.width() * vp.pixel_aspect_ratio().toDouble();
ySize = vp.height();
}
inline double ToNormalised(double canonical, double extent)
{
return extent > 0 ? canonical / extent : canonical;
}
inline double ToCanonical(double normalised, double extent)
{
return extent > 0 ? normalised * extent : normalised;
}
inline QString ParamChangeLabel(const OFX::Host::Param::Descriptor &descriptor)
{
return QStringLiteral("Change %1")
@@ -185,6 +211,11 @@ public:
QVariant variant = node->GetStandardValue(_descriptor.getName().c_str());
if (variant.canConvert<double>()) {
data = variant.toDouble();
if (IsNormalisedCoordinateSystem(_descriptor)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
data = ToNormalised(data, xSize);
}
return kOfxStatOK;
}
data = 0.0;
@@ -201,6 +232,11 @@ public:
rational::fromDouble(time));
if (variant.canConvert<double>()) {
data = variant.toDouble();
if (IsNormalisedCoordinateSystem(_descriptor)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
data = ToNormalised(data, xSize);
}
return kOfxStatOK;
}
data = 0.0;
@@ -213,8 +249,14 @@ public:
has_value_ = true;
return kOfxStatOK;
}
double val = data;
if (IsNormalisedCoordinateSystem(_descriptor)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
val = ToCanonical(val, xSize);
}
SplitValue split = NodeValue::split_normal_value_into_track_values(
NodeValue::kFloat, data);
NodeValue::kFloat, val);
auto command = new NodeParamSetSplitStandardValueCommand(
NodeInput(node.get(), _descriptor.getName().c_str()), split);
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
@@ -227,10 +269,16 @@ public:
has_value_ = true;
return kOfxStatOK;
}
double val = data;
if (IsNormalisedCoordinateSystem(_descriptor)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
val = ToCanonical(val, xSize);
}
auto command = new MultiUndoCommand();
Node::SetValueAtTime(
NodeInput(node.get(), _descriptor.getName().c_str()),
rational::fromDouble(time), data, 0, command, true);
rational::fromDouble(time), val, 0, command, true);
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
@@ -661,6 +709,12 @@ public:
.value<QVector2D>();
x = static_cast<double>(vec.x());
y = static_cast<double>(vec.y());
if (IsNormalisedCoordinateSystem(_descriptor)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
x = ToNormalised(x, xSize);
y = ToNormalised(y, ySize);
}
return kOfxStatOK;
}
OfxStatus get(OfxTime time,double& x,double& y)
@@ -680,6 +734,12 @@ public:
.value<QVector2D>();
x = static_cast<double>(vec.x());
y = static_cast<double>(vec.y());
if (IsNormalisedCoordinateSystem(_descriptor)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
x = ToNormalised(x, xSize);
y = ToNormalised(y, ySize);
}
return kOfxStatOK;
}
OfxStatus set(double x,double y)
@@ -690,8 +750,15 @@ public:
has_value_ = true;
return kOfxStatOK;
}
double xv = x, yv = y;
if (IsNormalisedCoordinateSystem(_descriptor)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
xv = ToCanonical(xv, xSize);
yv = ToCanonical(yv, ySize);
}
SplitValue split = NodeValue::split_normal_value_into_track_values(
NodeValue::kVec2, QVector2D(x, y));
NodeValue::kVec2, QVector2D(xv, yv));
auto command = new NodeParamSetSplitStandardValueCommand(
NodeInput(node.get(), _descriptor.getName().c_str()), split);
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
@@ -705,12 +772,19 @@ public:
has_value_ = true;
return kOfxStatOK;
}
double xv = x, yv = y;
if (IsNormalisedCoordinateSystem(_descriptor)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
xv = ToCanonical(xv, xSize);
yv = ToCanonical(yv, ySize);
}
auto command = new MultiUndoCommand();
const QString name = _descriptor.getName().c_str();
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
x, 0, command, true);
xv, 0, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
y, 1, command, true);
yv, 1, command, true);
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
@@ -844,6 +918,13 @@ public:
x = static_cast<double>(vec.x());
y = static_cast<double>(vec.y());
z = static_cast<double>(vec.z());
if (IsNormalisedCoordinateSystem(_descriptor)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
x = ToNormalised(x, xSize);
y = ToNormalised(y, ySize);
z = ToNormalised(z, xSize);
}
return kOfxStatOK;
}
OfxStatus get(OfxTime time,double& x,double& y,double& z)
@@ -865,6 +946,13 @@ public:
x = static_cast<double>(vec.x());
y = static_cast<double>(vec.y());
z = static_cast<double>(vec.z());
if (IsNormalisedCoordinateSystem(_descriptor)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
x = ToNormalised(x, xSize);
y = ToNormalised(y, ySize);
z = ToNormalised(z, xSize);
}
return kOfxStatOK;
}
OfxStatus set(double x,double y,double z)
@@ -876,8 +964,16 @@ public:
has_value_ = true;
return kOfxStatOK;
}
double xv = x, yv = y, zv = z;
if (IsNormalisedCoordinateSystem(_descriptor)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
xv = ToCanonical(xv, xSize);
yv = ToCanonical(yv, ySize);
zv = ToCanonical(zv, xSize);
}
SplitValue split = NodeValue::split_normal_value_into_track_values(
NodeValue::kVec3, QVector3D(x, y, z));
NodeValue::kVec3, QVector3D(xv, yv, zv));
auto command = new NodeParamSetSplitStandardValueCommand(
NodeInput(node.get(), _descriptor.getName().c_str()), split);
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
@@ -892,14 +988,22 @@ public:
has_value_ = true;
return kOfxStatOK;
}
double xv = x, yv = y, zv = z;
if (IsNormalisedCoordinateSystem(_descriptor)) {
double xSize, ySize;
GetProjectExtent(xSize, ySize);
xv = ToCanonical(xv, xSize);
yv = ToCanonical(yv, ySize);
zv = ToCanonical(zv, xSize);
}
auto command = new MultiUndoCommand();
const QString name = _descriptor.getName().c_str();
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), x, 0, command, true);
rational::fromDouble(time), xv, 0, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), y, 1, command, true);
rational::fromDouble(time), yv, 1, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), z, 2, command, true);
rational::fromDouble(time), zv, 2, command, true);
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
+12 -1
View File
@@ -31,6 +31,7 @@
#include "config/config.h"
#include "core.h"
#include "node/nodeundo.h"
#include "node/value.h"
#include "pluginSupport/OlivePluginInstance.h"
#include "nodeview.h"
#include "nodeviewscene.h"
@@ -753,7 +754,17 @@ void NodeViewItem::UpdateOutputConnectorPosition()
bool NodeViewItem::IsInputValid(const QString &input)
{
return node_->IsInputConnectable(input) && !node_->IsInputHidden(input);
if (!node_->IsInputConnectable(input) || node_->IsInputHidden(input)) {
return false;
}
// For OFX plugin nodes, only show texture inputs in the node graph
// to avoid excessively tall nodes with dozens of scalar parameters.
// Scalar parameters are still visible in the parameter panel.
if (node_->getPluginInstance() != nullptr &&
node_->GetInputDataType(input) != NodeValue::kTexture) {
return false;
}
return true;
}
void NodeViewItem::SetRectSize(int height_units)