transitions: extended transition logic

General code and functionality improvements. Moved more code out of the
OpenGL backend for portability. Implemented audio transitions.
Implemented basic transition animation curve settings.
This commit is contained in:
itsmattkc
2020-07-16 00:36:21 +10:00
parent de74adeb5e
commit 662d66ad10
10 changed files with 224 additions and 52 deletions
@@ -59,4 +59,31 @@ ShaderCode CrossDissolveTransition::GetShaderCode(const QString &shader_id) cons
return ShaderCode(Node::ReadFileAsString(":/shaders/crossdissolve.frag"), QString());
}
void CrossDissolveTransition::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const
{
for (int i=0; i<out_samples->sample_count(); i++) {
double this_sample_time = out_samples->audio_params().samples_to_time(i).toDouble() + time_in;
double progress = GetTotalProgress(this_sample_time);
for (int j=0; j<out_samples->audio_params().channel_count(); j++) {
out_samples->data()[j][i] = 0;
if (from_samples) {
if (i < from_samples->sample_count()) {
out_samples->data()[j][i] += from_samples->data()[j][i] * TransformCurve(1.0 - progress);
}
}
if (to_samples) {
// Offset input samples from the end
int in_index = i - (out_samples->sample_count() - to_samples->sample_count());
if (in_index >= 0) {
out_samples->data()[j][i] += to_samples->data()[j][in_index] * TransformCurve(progress);
}
}
}
}
}
OLIVE_NAMESPACE_EXIT
@@ -41,6 +41,9 @@ public:
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
protected:
virtual void SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const;
};
OLIVE_NAMESPACE_EXIT
+111 -16
View File
@@ -28,17 +28,22 @@ TransitionBlock::TransitionBlock() :
connected_out_block_(nullptr),
connected_in_block_(nullptr)
{
out_block_input_ = new NodeInput("out_block_in", NodeParam::kBuffer);
out_block_input_ = new NodeInput(QStringLiteral("out_block_in"), NodeParam::kBuffer);
out_block_input_->set_is_keyframable(false);
connect(out_block_input_, &NodeParam::EdgeAdded, this, &TransitionBlock::BlockConnected);
connect(out_block_input_, &NodeParam::EdgeRemoved, this, &TransitionBlock::BlockDisconnected);
AddInput(out_block_input_);
in_block_input_ = new NodeInput("in_block_in", NodeParam::kBuffer);
in_block_input_ = new NodeInput(QStringLiteral("in_block_in"), NodeParam::kBuffer);
in_block_input_->set_is_keyframable(false);
connect(in_block_input_, &NodeParam::EdgeAdded, this, &TransitionBlock::BlockConnected);
connect(in_block_input_, &NodeParam::EdgeRemoved, this, &TransitionBlock::BlockDisconnected);
AddInput(in_block_input_);
curve_input_ = new NodeInput(QStringLiteral("curve_in"), NodeParam::kCombo);
curve_input_->set_is_keyframable(false);
curve_input_->set_connectable(false);
AddInput(curve_input_);
}
Block::Type TransitionBlock::type() const
@@ -62,6 +67,10 @@ void TransitionBlock::Retranslate()
out_block_input_->set_name(tr("From"));
in_block_input_->set_name(tr("To"));
curve_input_->set_name(tr("Curve"));
// These must correspond to the CurveType enum
curve_input_->set_combobox_strings({ tr("Linear"), tr("Exponential"), tr("Logarithmic") });
}
rational TransitionBlock::in_offset() const
@@ -106,12 +115,12 @@ Block *TransitionBlock::connected_in_block() const
return connected_in_block_;
}
double TransitionBlock::GetTotalProgress(const rational &time) const
double TransitionBlock::GetTotalProgress(const double &time) const
{
return GetInternalTransitionTime(time) / length().toDouble();
}
double TransitionBlock::GetOutProgress(const rational &time) const
double TransitionBlock::GetOutProgress(const double &time) const
{
if (out_offset() == 0) {
return 0;
@@ -120,7 +129,7 @@ double TransitionBlock::GetOutProgress(const rational &time) const
return clamp(1.0 - (GetInternalTransitionTime(time) / out_offset().toDouble()), 0.0, 1.0);
}
double TransitionBlock::GetInProgress(const rational &time) const
double TransitionBlock::GetInProgress(const double &time) const
{
if (in_offset() == 0) {
return 0;
@@ -133,18 +142,34 @@ void TransitionBlock::Hash(QCryptographicHash &hash, const rational &time) const
{
Node::Hash(hash, time);
double all_prog = GetTotalProgress(time);
double in_prog = GetInProgress(time);
double out_prog = GetOutProgress(time);
double time_dbl = time.toDouble();
double all_prog = GetTotalProgress(time_dbl);
double in_prog = GetInProgress(time_dbl);
double out_prog = GetOutProgress(time_dbl);
hash.addData(reinterpret_cast<const char*>(&all_prog), sizeof(double));
hash.addData(reinterpret_cast<const char*>(&in_prog), sizeof(double));
hash.addData(reinterpret_cast<const char*>(&out_prog), sizeof(double));
}
double TransitionBlock::GetInternalTransitionTime(const rational &time) const
double TransitionBlock::GetInternalTransitionTime(const double &time) const
{
return time.toDouble() - in().toDouble();
return time - in().toDouble();
}
void TransitionBlock::InsertTransitionTimes(AcceleratedJob *job, const double &time) const
{
// Provides total transition progress from 0.0 (start) - 1.0 (end)
job->InsertValue(QStringLiteral("ove_tprog_all"),
NodeValue(NodeParam::kFloat, GetTotalProgress(time), this));
// Provides progress of out section from 1.0 (start) - 0.0 (end)
job->InsertValue(QStringLiteral("ove_tprog_out"),
NodeValue(NodeParam::kFloat, GetOutProgress(time), this));
// Provides progress of in section from 0.0 (start) - 1.0 (end)
job->InsertValue(QStringLiteral("ove_tprog_in"),
NodeValue(NodeParam::kFloat, GetInProgress(time), this));
}
void TransitionBlock::BlockConnected(NodeEdgePtr edge)
@@ -173,16 +198,62 @@ void TransitionBlock::BlockDisconnected(NodeEdgePtr edge)
NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const
{
ShaderJob job;
NodeParam::DataType data_type;
job.InsertValue(out_block_input(), value);
job.InsertValue(in_block_input(), value);
job.SetAlphaChannelRequired(true);
if (out_block_input()->is_connected()) {
data_type = value[out_block_input()].GetWithMeta(NodeParam::kBuffer).type();
} else if (in_block_input()->is_connected()) {
data_type = value[in_block_input()].GetWithMeta(NodeParam::kBuffer).type();
} else {
data_type = NodeParam::kNone;
}
ShaderJobEvent(value, job);
NodeParam::DataType job_type;
QVariant push_job;
if (data_type == NodeParam::kTexture) {
// This must be a visual transition
ShaderJob job;
job.InsertValue(out_block_input(), value);
job.InsertValue(in_block_input(), value);
job.InsertValue(curve_input_, value);
job.SetAlphaChannelRequired(true);
double time = value[QStringLiteral("global")].Get(NodeParam::kFloat, QStringLiteral("time_in")).toDouble();
InsertTransitionTimes(&job, time);
ShaderJobEvent(value, job);
job_type = NodeParam::kShaderJob;
push_job = QVariant::fromValue(job);
} else if (data_type == NodeParam::kSamples) {
// This must be an audio transition
SampleBufferPtr from_samples = value[out_block_input()].Take(NodeParam::kBuffer).value<SampleBufferPtr>();
SampleBufferPtr to_samples = value[in_block_input()].Take(NodeParam::kBuffer).value<SampleBufferPtr>();
if (from_samples || to_samples) {
double time_in = value[QStringLiteral("global")].Get(NodeParam::kFloat, QStringLiteral("time_in")).toDouble();
double time_out = value[QStringLiteral("global")].Get(NodeParam::kFloat, QStringLiteral("time_out")).toDouble();
const AudioParams& params = (from_samples) ? from_samples->audio_params() : to_samples->audio_params();
int nb_samples = params.time_to_samples(time_out - time_in);
SampleBufferPtr out_samples = SampleBuffer::CreateAllocated(params, nb_samples);
SampleJobEvent(from_samples, to_samples, out_samples, time_in);
job_type = NodeParam::kSamples;
push_job = QVariant::fromValue(out_samples);
}
}
NodeValueTable table = value.Merge();
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
if (!push_job.isNull()) {
table.Push(job_type, push_job, this);
}
return table;
}
@@ -192,4 +263,28 @@ void TransitionBlock::ShaderJobEvent(NodeValueDatabase &value, ShaderJob &job) c
Q_UNUSED(job)
}
void TransitionBlock::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const
{
Q_UNUSED(from_samples)
Q_UNUSED(to_samples)
Q_UNUSED(out_samples)
Q_UNUSED(time_in)
}
double TransitionBlock::TransformCurve(double linear) const
{
switch (static_cast<CurveType>(curve_input_->get_standard_value().toInt())) {
case kLinear:
break;
case kExponential:
linear *= linear;
break;
case kLogarithmic:
linear = qSqrt(linear);
break;
}
return linear;
}
OLIVE_NAMESPACE_EXIT
+18 -4
View File
@@ -43,9 +43,9 @@ public:
Block* connected_out_block() const;
Block* connected_in_block() const;
double GetTotalProgress(const rational& time) const;
double GetOutProgress(const rational& time) const;
double GetInProgress(const rational& time) const;
double GetTotalProgress(const double &time) const;
double GetOutProgress(const double &time) const;
double GetInProgress(const double &time) const;
virtual void Hash(QCryptographicHash& hash, const rational &time) const override;
@@ -54,13 +54,27 @@ public:
protected:
virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const;
virtual void SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const;
double TransformCurve(double linear) const;
private:
double GetInternalTransitionTime(const rational& time) const;
enum CurveType {
kLinear,
kExponential,
kLogarithmic
};
double GetInternalTransitionTime(const double &time) const;
void InsertTransitionTimes(AcceleratedJob* job, const double& time) const;
NodeInput* out_block_input_;
NodeInput* in_block_input_;
NodeInput* curve_input_;
Block* connected_out_block_;
Block* connected_in_block_;
+10 -5
View File
@@ -41,11 +41,7 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa
database.Insert(input, ProcessInput(input, input_time));
}
// Insert global variables
NodeValueTable global;
global.Push(NodeParam::kFloat, range.in().toDouble(), nullptr, QStringLiteral("time_in"));
global.Push(NodeParam::kFloat, range.out().toDouble(), nullptr, QStringLiteral("time_out"));
database.Insert(QStringLiteral("global"), global);
AddGlobalsToDatabase(database, range);
return database;
}
@@ -156,6 +152,15 @@ QVariant NodeTraverser::GetCachedFrame(const Node *node, const rational &time)
return QVariant();
}
void NodeTraverser::AddGlobalsToDatabase(NodeValueDatabase &db, const TimeRange& range)
{
// Insert global variables
NodeValueTable global;
global.Push(NodeParam::kFloat, range.in().toDouble(), nullptr, QStringLiteral("time_in"));
global.Push(NodeParam::kFloat, range.out().toDouble(), nullptr, QStringLiteral("time_out"));
db.Insert(QStringLiteral("global"), global);
}
void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, NodeValueTable &output_params)
{
bool got_cached_frame = false;
+2
View File
@@ -56,6 +56,8 @@ protected:
virtual QVariant GetCachedFrame(const Node *node, const rational &time);
static void AddGlobalsToDatabase(NodeValueDatabase& db, const TimeRange &range);
private:
void PostProcessTable(const Node *node, const TimeRange &range, NodeValueTable &output_params);