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:
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -276,18 +276,21 @@ OfxStatus OlivePluginInstance::clearPersistentMessage()
|
||||
}
|
||||
void OlivePluginInstance::getProjectSize(double &xSize, double &ySize) const
|
||||
{
|
||||
xSize =params_.width();
|
||||
ySize =params_.height();
|
||||
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();
|
||||
yOffset =params_.y();
|
||||
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();
|
||||
ySize =params_.height();
|
||||
double par = params_.pixel_aspect_ratio().toDouble();
|
||||
xSize = params_.width() * par;
|
||||
ySize = params_.height();
|
||||
}
|
||||
double OlivePluginInstance::getProjectPixelAspectRatio() const
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user