mathnode: implemented add/subtract/multiply/divide/power functions
Includes implementing combobox UI for the NodeParamViewWidgetBridge.
This commit is contained in:
+231
-58
@@ -23,12 +23,13 @@
|
||||
#include <QMatrix4x4>
|
||||
#include <QVector2D>
|
||||
|
||||
#include "render/color.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
MathNode::MathNode()
|
||||
{
|
||||
// FIXME: Make this a combobox
|
||||
method_in_ = new NodeInput(QStringLiteral("method_in"), NodeParam::kText);
|
||||
method_in_ = new NodeInput(QStringLiteral("method_in"), NodeParam::kCombo);
|
||||
method_in_->SetConnectable(false);
|
||||
method_in_->set_is_keyframable(false);
|
||||
AddInput(method_in_);
|
||||
@@ -72,15 +73,16 @@ void MathNode::Retranslate()
|
||||
method_in_->set_name(tr("Method"));
|
||||
param_a_in_->set_name(tr("Value"));
|
||||
param_b_in_->set_name(tr("Value"));
|
||||
|
||||
QStringList operations = {tr("Add"), tr("Subtract"), tr("Multiply"), tr("Divide")};
|
||||
method_in_->set_combobox_strings(operations);
|
||||
}
|
||||
|
||||
Node::Capabilities MathNode::GetCapabilities(const NodeValueDatabase &input) const
|
||||
{
|
||||
QVector<int> pair_likelihood_a = GetPairLikelihood(input[param_a_in_]);
|
||||
QVector<int> pair_likelihood_b = GetPairLikelihood(input[param_b_in_]);
|
||||
Pairing most_likely_pairing = GetMostLikelyPairing(pair_likelihood_a, pair_likelihood_b);
|
||||
PairingCalculator calc(input[param_a_in_], input[param_b_in_]);
|
||||
|
||||
switch (most_likely_pairing) {
|
||||
switch (calc.GetMostLikelyPairing()) {
|
||||
case kPairTextureColor:
|
||||
case kPairTextureNumber:
|
||||
case kPairTextureTexture:
|
||||
@@ -97,24 +99,43 @@ QString MathNode::ShaderID(const NodeValueDatabase &input) const
|
||||
{
|
||||
QString method = QString::number(GetOperation());
|
||||
|
||||
QVector<int> pair_likelihood_a = GetPairLikelihood(input[param_a_in_]);
|
||||
QVector<int> pair_likelihood_b = GetPairLikelihood(input[param_b_in_]);
|
||||
Pairing most_likely_pairing = GetMostLikelyPairing(pair_likelihood_a, pair_likelihood_b);
|
||||
PairingCalculator calc(input[param_a_in_], input[param_b_in_]);
|
||||
|
||||
QString type_a = QString::number(input[param_a_in_].At(pair_likelihood_a.at(most_likely_pairing)).type());
|
||||
QString type_b = QString::number(input[param_b_in_].At(pair_likelihood_b.at(most_likely_pairing)).type());
|
||||
QString type_a = QString::number(calc.GetMostLikelyValueA().type());
|
||||
QString type_b = QString::number(calc.GetMostLikelyValueB().type());
|
||||
|
||||
return id().append(method).append(type_a).append(type_b);
|
||||
}
|
||||
|
||||
QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const
|
||||
{
|
||||
QVector<int> pair_likelihood_a = GetPairLikelihood(input[param_a_in_]);
|
||||
QVector<int> pair_likelihood_b = GetPairLikelihood(input[param_b_in_]);
|
||||
Pairing most_likely_pairing = GetMostLikelyPairing(pair_likelihood_a, pair_likelihood_b);
|
||||
PairingCalculator calc(input[param_a_in_], input[param_b_in_]);
|
||||
|
||||
NodeParam::DataType type_a = input[param_a_in_].At(pair_likelihood_a.at(most_likely_pairing)).type();
|
||||
NodeParam::DataType type_b = input[param_b_in_].At(pair_likelihood_b.at(most_likely_pairing)).type();
|
||||
NodeParam::DataType type_a = calc.GetMostLikelyValueA().type();
|
||||
NodeParam::DataType type_b = calc.GetMostLikelyValueB().type();
|
||||
|
||||
QString operation;
|
||||
|
||||
switch (GetOperation()) {
|
||||
case kOpAdd:
|
||||
operation = QStringLiteral("%1 + %2");
|
||||
break;
|
||||
case kOpSubtract:
|
||||
operation = QStringLiteral("%1 - %2");
|
||||
break;
|
||||
case kOpMultiply:
|
||||
operation = QStringLiteral("%1 * %2");
|
||||
break;
|
||||
case kOpDivide:
|
||||
operation = QStringLiteral("%1 / %2");
|
||||
break;
|
||||
case kOpPower:
|
||||
operation = QStringLiteral("pow(%1, %2)");
|
||||
break;
|
||||
}
|
||||
|
||||
operation = operation.arg(GetShaderVariableCall(param_a_in_->id(), type_a),
|
||||
GetShaderVariableCall(param_b_in_->id(), type_b));
|
||||
|
||||
return QStringLiteral("#version 110\n"
|
||||
"\n"
|
||||
@@ -124,26 +145,23 @@ QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const
|
||||
"uniform %2 %4;\n"
|
||||
"\n"
|
||||
"void main(void) {\n"
|
||||
" gl_FragColor = %5 + %6;\n"
|
||||
" gl_FragColor = %5;\n"
|
||||
"}\n").arg(GetShaderUniformType(type_a),
|
||||
GetShaderUniformType(type_b),
|
||||
param_a_in_->id(),
|
||||
param_b_in_->id(),
|
||||
GetShaderVariableCall(param_a_in_->id(), type_a),
|
||||
GetShaderVariableCall(param_b_in_->id(), type_b));
|
||||
operation);
|
||||
}
|
||||
|
||||
NodeValue MathNode::InputValueFromTable(NodeInput *input, const NodeValueDatabase &db) const
|
||||
{
|
||||
if (input == param_a_in_ || input == param_b_in_) {
|
||||
QVector<int> pair_likelihood_a = GetPairLikelihood(db[param_a_in_]);
|
||||
QVector<int> pair_likelihood_b = GetPairLikelihood(db[param_b_in_]);
|
||||
Pairing most_likely_pairing = GetMostLikelyPairing(pair_likelihood_a, pair_likelihood_b);
|
||||
PairingCalculator calc(db[param_a_in_], db[param_b_in_]);
|
||||
|
||||
if (input == param_a_in_) {
|
||||
return db[input].At(pair_likelihood_a.at(most_likely_pairing));
|
||||
return calc.GetMostLikelyValueA();
|
||||
} else {
|
||||
return db[input].At(pair_likelihood_b.at(most_likely_pairing));
|
||||
return calc.GetMostLikelyValueB();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,28 +174,26 @@ NodeValueTable MathNode::Value(const NodeValueDatabase &value) const
|
||||
|
||||
// Auto-detect what values to operate with
|
||||
// FIXME: Add manual override for this
|
||||
QVector<int> pair_likelihood_a = GetPairLikelihood(value[param_a_in_]);
|
||||
QVector<int> pair_likelihood_b = GetPairLikelihood(value[param_b_in_]);
|
||||
Pairing most_likely_pairing = GetMostLikelyPairing(pair_likelihood_a, pair_likelihood_b);
|
||||
PairingCalculator calc(value[param_a_in_], value[param_b_in_]);
|
||||
|
||||
NodeValue val_a, val_b;
|
||||
if (most_likely_pairing >= 0 && most_likely_pairing < kPairCount) {
|
||||
val_a = value[param_a_in_].At(pair_likelihood_a.at(most_likely_pairing));
|
||||
val_b = value[param_a_in_].At(pair_likelihood_a.at(most_likely_pairing));
|
||||
if (!calc.FoundMostLikelyPairing()) {
|
||||
return output;
|
||||
}
|
||||
|
||||
switch (most_likely_pairing) {
|
||||
NodeValue val_a = calc.GetMostLikelyValueA();
|
||||
NodeValue val_b = calc.GetMostLikelyValueB();
|
||||
|
||||
switch (calc.GetMostLikelyPairing()) {
|
||||
|
||||
case kPairNumberNumber:
|
||||
{
|
||||
if (val_a.type() == NodeParam::kRational && val_b.type() == NodeParam::kRational) {
|
||||
if (val_a.type() == NodeParam::kRational && val_b.type() == NodeParam::kRational && GetOperation() != kOpPower) {
|
||||
// Preserve rationals
|
||||
output.Push(NodeParam::kRational, QVariant::fromValue(val_a.data().value<rational>() + val_b.data().value<rational>()));
|
||||
output.Push(NodeParam::kRational,
|
||||
QVariant::fromValue(PerformAddSubMultDiv<rational, rational>(val_a.data().value<rational>(), val_b.data().value<rational>())));
|
||||
} else {
|
||||
float flt_a = RetrieveNumber(val_a);
|
||||
float flt_b = RetrieveNumber(val_b);
|
||||
|
||||
output.Push(NodeParam::kFloat, flt_a + flt_b);
|
||||
output.Push(NodeParam::kFloat,
|
||||
PerformAll<float, float>(RetrieveNumber(val_a), RetrieveNumber(val_b)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -186,7 +202,9 @@ NodeValueTable MathNode::Value(const NodeValueDatabase &value) const
|
||||
{
|
||||
// We convert all vectors to QVector4D just for simplicity and exploit the fact that kVec4 is higher than kVec2 in
|
||||
// the enum to find the largest data type
|
||||
PushVector(&output, qMax(val_a.type(), val_b.type()), RetrieveVector(val_a) + RetrieveVector(val_b));
|
||||
PushVector(&output,
|
||||
qMax(val_a.type(), val_b.type()),
|
||||
PerformAddSubMultDiv<QVector4D, QVector4D>(RetrieveVector(val_a), RetrieveVector(val_b)));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -195,7 +213,10 @@ NodeValueTable MathNode::Value(const NodeValueDatabase &value) const
|
||||
QMatrix4x4 matrix = (val_a.type() == NodeParam::kMatrix) ? val_a.data().value<QMatrix4x4>() : val_b.data().value<QMatrix4x4>();
|
||||
QVector4D vec = (val_a.type() == NodeParam::kMatrix) ? RetrieveVector(val_b) : RetrieveVector(val_a);
|
||||
|
||||
PushVector(&output, qMax(val_a.type(), val_b.type()), vec * matrix);
|
||||
// Only valid operation is multiply
|
||||
PushVector(&output,
|
||||
qMax(val_a.type(), val_b.type()),
|
||||
PerformMult<QVector4D, QMatrix4x4>(vec, matrix));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -204,29 +225,44 @@ NodeValueTable MathNode::Value(const NodeValueDatabase &value) const
|
||||
QVector4D vec = (val_a.type() & NodeParam::kVector) ? RetrieveVector(val_a) : RetrieveVector(val_b);
|
||||
float number = RetrieveNumber((val_a.type() & NodeParam::kMatrix) ? val_b : val_a);
|
||||
|
||||
PushVector(&output, val_a.type(), vec * number);
|
||||
// Only multiply and divide are valid operations
|
||||
PushVector(&output, val_a.type(), PerformMultDiv<QVector4D, float>(vec, number));
|
||||
break;
|
||||
}
|
||||
|
||||
case kPairMatrixMatrix:
|
||||
{
|
||||
QMatrix4x4 mat_a = value[param_a_in_].At(pair_likelihood_a.at(most_likely_pairing)).data().value<QMatrix4x4>();
|
||||
QMatrix4x4 mat_b = value[param_b_in_].At(pair_likelihood_b.at(most_likely_pairing)).data().value<QMatrix4x4>();
|
||||
output.Push(NodeParam::kMatrix, mat_a + mat_b);
|
||||
QMatrix4x4 mat_a = val_a.data().value<QMatrix4x4>();
|
||||
QMatrix4x4 mat_b = val_b.data().value<QMatrix4x4>();
|
||||
output.Push(NodeParam::kMatrix, PerformAddSubMult<QMatrix4x4, QMatrix4x4>(mat_a, mat_b));
|
||||
break;
|
||||
}
|
||||
|
||||
case kPairColorColor:
|
||||
{
|
||||
Color col_a = val_a.data().value<Color>();
|
||||
Color col_b = val_b.data().value<Color>();
|
||||
|
||||
// Only add and subtract are valid operations
|
||||
output.Push(NodeParam::kColor, QVariant::fromValue(PerformAddSub<Color, Color>(col_a, col_b)));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case kPairNumberColor:
|
||||
{
|
||||
// FIXME: Still undecided on the true representation of color
|
||||
Color col = (val_a.type() == NodeParam::kColor) ? val_a.data().value<Color>() : val_b.data().value<Color>();
|
||||
float num = (val_a.type() == NodeParam::kColor) ? val_b.data().toFloat() : val_a.data().toFloat();
|
||||
|
||||
// Only multiply and divide are valid operations
|
||||
output.Push(NodeParam::kColor, QVariant::fromValue(PerformMult<Color, float>(col, num)));
|
||||
break;
|
||||
}
|
||||
|
||||
case kPairSampleSample:
|
||||
{
|
||||
SampleBufferPtr samples_a = value[param_a_in_].Get(NodeParam::kSamples).value<SampleBufferPtr>();
|
||||
SampleBufferPtr samples_b = value[param_b_in_].Get(NodeParam::kSamples).value<SampleBufferPtr>();
|
||||
SampleBufferPtr samples_a = val_a.data().value<SampleBufferPtr>();
|
||||
SampleBufferPtr samples_b = val_b.data().value<SampleBufferPtr>();
|
||||
|
||||
int max_samples = qMax(samples_a->sample_count_per_channel(), samples_b->sample_count_per_channel());
|
||||
int min_samples = qMin(samples_a->sample_count_per_channel(), samples_b->sample_count_per_channel());
|
||||
@@ -236,7 +272,7 @@ NodeValueTable MathNode::Value(const NodeValueDatabase &value) const
|
||||
// Mix samples that are in both buffers
|
||||
for (int i=0;i<mixed_samples->audio_params().channel_count();i++) {
|
||||
for (int j=0;j<min_samples;j++) {
|
||||
mixed_samples->data()[i][j] = samples_a->data()[i][j] + samples_b->data()[i][j];
|
||||
mixed_samples->data()[i][j] = PerformAll<float, float>(samples_a->data()[i][j], samples_b->data()[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +291,6 @@ NodeValueTable MathNode::Value(const NodeValueDatabase &value) const
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case kPairNone:
|
||||
case kPairCount:
|
||||
case kPairTextureColor:
|
||||
@@ -272,12 +307,10 @@ NodeValueTable MathNode::Value(const NodeValueDatabase &value) const
|
||||
|
||||
NodeInput *MathNode::ProcessesSamplesFrom(const NodeValueDatabase &value) const
|
||||
{
|
||||
QVector<int> pair_likelihood_a = GetPairLikelihood(value[param_a_in_]);
|
||||
QVector<int> pair_likelihood_b = GetPairLikelihood(value[param_b_in_]);
|
||||
Pairing most_likely_pairing = GetMostLikelyPairing(pair_likelihood_a, pair_likelihood_b);
|
||||
PairingCalculator calc(value[param_a_in_], value[param_b_in_]);
|
||||
|
||||
if (most_likely_pairing == kPairSampleNumber) {
|
||||
if (value[param_a_in_].At(pair_likelihood_a.at(most_likely_pairing)).type() == NodeParam::kSamples) {
|
||||
if (calc.GetMostLikelyPairing() == kPairSampleNumber) {
|
||||
if (calc.GetMostLikelyValueA().type() == NodeParam::kSamples) {
|
||||
return param_a_in_;
|
||||
} else {
|
||||
return param_b_in_;
|
||||
@@ -295,7 +328,7 @@ void MathNode::ProcessSamples(const NodeValueDatabase &values, const AudioRender
|
||||
float number_flt = RetrieveNumber(number_val);
|
||||
|
||||
for (int i=0;i<params.channel_count();i++) {
|
||||
output->data()[i][index] = input->data()[i][index] + number_flt;
|
||||
output->data()[i][index] = PerformAll<float, float>(input->data()[i][index], number_flt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,7 +344,7 @@ NodeInput *MathNode::param_b_in() const
|
||||
|
||||
MathNode::Operation MathNode::GetOperation() const
|
||||
{
|
||||
return kOpAdd;
|
||||
return static_cast<Operation>(method_in_->get_standard_value().toInt());
|
||||
}
|
||||
|
||||
QString MathNode::GetShaderUniformType(const NodeParam::DataType &type)
|
||||
@@ -337,7 +370,7 @@ QString MathNode::GetShaderVariableCall(const QString &input_id, const NodeParam
|
||||
return input_id;
|
||||
}
|
||||
|
||||
QVector<int> MathNode::GetPairLikelihood(const NodeValueTable &table)
|
||||
QVector<int> MathNode::PairingCalculator::GetPairLikelihood(const NodeValueTable &table)
|
||||
{
|
||||
// FIXME: When we introduce a manual override, placing it here would be the least problematic
|
||||
|
||||
@@ -378,7 +411,7 @@ QVector<int> MathNode::GetPairLikelihood(const NodeValueTable &table)
|
||||
return likelihood;
|
||||
}
|
||||
|
||||
MathNode::Pairing MathNode::GetMostLikelyPairing(const QVector<int> &a, const QVector<int> &b)
|
||||
MathNode::Pairing MathNode::PairingCalculator::GetMostLikelyPairingInternal(const QVector<int> &a, const QVector<int> &b)
|
||||
{
|
||||
QVector<int> likelihoods(kPairCount);
|
||||
|
||||
@@ -446,4 +479,144 @@ float MathNode::RetrieveNumber(const NodeValue &val)
|
||||
}
|
||||
}
|
||||
|
||||
MathNode::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, const NodeValueTable &table_b)
|
||||
{
|
||||
table_a_ = table_a;
|
||||
table_b_ = table_b;
|
||||
pair_likelihood_a_ = GetPairLikelihood(table_a_);
|
||||
pair_likelihood_b_ = GetPairLikelihood(table_b_);
|
||||
most_likely_pairing_ = GetMostLikelyPairingInternal(pair_likelihood_a_, pair_likelihood_b_);
|
||||
}
|
||||
|
||||
bool MathNode::PairingCalculator::FoundMostLikelyPairing() const
|
||||
{
|
||||
return (most_likely_pairing_ >= 0 && most_likely_pairing_ < kPairCount);
|
||||
}
|
||||
|
||||
MathNode::Pairing MathNode::PairingCalculator::GetMostLikelyPairing() const
|
||||
{
|
||||
return most_likely_pairing_;
|
||||
}
|
||||
|
||||
NodeValue MathNode::PairingCalculator::GetMostLikelyValueA() const
|
||||
{
|
||||
return GetMostLikelyValue(table_a_, pair_likelihood_a_);
|
||||
}
|
||||
|
||||
NodeValue MathNode::PairingCalculator::GetMostLikelyValueB() const
|
||||
{
|
||||
return GetMostLikelyValue(table_b_, pair_likelihood_b_);
|
||||
}
|
||||
|
||||
NodeValue MathNode::PairingCalculator::GetMostLikelyValue(const NodeValueTable &table, const QVector<int> &likelihood) const
|
||||
{
|
||||
return table.At(likelihood.at(most_likely_pairing_));
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
T MathNode::PerformAll(T a, U b) const
|
||||
{
|
||||
switch (GetOperation()) {
|
||||
case kOpAdd:
|
||||
return a + b;
|
||||
case kOpSubtract:
|
||||
return a - b;
|
||||
case kOpMultiply:
|
||||
return a * b;
|
||||
case kOpDivide:
|
||||
return a / b;
|
||||
case kOpPower:
|
||||
return qPow(a, b);
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
T MathNode::PerformMultDiv(T a, U b) const
|
||||
{
|
||||
switch (GetOperation()) {
|
||||
case kOpMultiply:
|
||||
return a * b;
|
||||
case kOpDivide:
|
||||
return a / b;
|
||||
case kOpAdd:
|
||||
case kOpSubtract:
|
||||
case kOpPower:
|
||||
break;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
T MathNode::PerformAddSub(T a, U b) const
|
||||
{
|
||||
switch (GetOperation()) {
|
||||
case kOpAdd:
|
||||
return a + b;
|
||||
case kOpSubtract:
|
||||
return a - b;
|
||||
case kOpMultiply:
|
||||
case kOpDivide:
|
||||
case kOpPower:
|
||||
break;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
T MathNode::PerformMult(T a, U b) const
|
||||
{
|
||||
switch (GetOperation()) {
|
||||
case kOpMultiply:
|
||||
return a * b;
|
||||
case kOpAdd:
|
||||
case kOpSubtract:
|
||||
case kOpDivide:
|
||||
case kOpPower:
|
||||
break;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
T MathNode::PerformAddSubMult(T a, U b) const
|
||||
{
|
||||
switch (GetOperation()) {
|
||||
case kOpAdd:
|
||||
return a + b;
|
||||
case kOpSubtract:
|
||||
return a - b;
|
||||
case kOpMultiply:
|
||||
return a * b;
|
||||
case kOpDivide:
|
||||
case kOpPower:
|
||||
break;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
T MathNode::PerformAddSubMultDiv(T a, U b) const
|
||||
{
|
||||
switch (GetOperation()) {
|
||||
case kOpAdd:
|
||||
return a + b;
|
||||
case kOpSubtract:
|
||||
return a - b;
|
||||
case kOpMultiply:
|
||||
return a * b;
|
||||
case kOpDivide:
|
||||
return a / b;
|
||||
case kOpPower:
|
||||
break;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
Reference in New Issue
Block a user