implemented node derivative that loads all metadata/code from an external file
Implemented the ability to load a node externally. This XML data could also eventually be copy/pasted between instances of Olive alongside XML data for other nodes and their connections to allow copying entire node trees.
This commit is contained in:
@@ -0,0 +1,260 @@
|
|||||||
|
#include "external.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
ExternalNode::ExternalNode(const QString &xml_meta_filename) :
|
||||||
|
xml_filename_(xml_meta_filename),
|
||||||
|
iterations_(1),
|
||||||
|
iteration_input_(nullptr)
|
||||||
|
{
|
||||||
|
QFile metadata_file(xml_filename_);
|
||||||
|
|
||||||
|
if (metadata_file.open(QFile::ReadOnly)) {
|
||||||
|
QXmlStreamReader reader(&metadata_file);
|
||||||
|
|
||||||
|
while (!reader.atEnd()) {
|
||||||
|
reader.readNext();
|
||||||
|
|
||||||
|
if (reader.isStartElement() && reader.name() == "effect") {
|
||||||
|
XMLReadEffect(&reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata_file.close();
|
||||||
|
} else {
|
||||||
|
qWarning() << "Failed to load node metadata file" << xml_filename_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Node *ExternalNode::copy() const
|
||||||
|
{
|
||||||
|
return new ExternalNode(xml_filename_);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ExternalNode::Name() const
|
||||||
|
{
|
||||||
|
// FIXME: Update to get currently selected language from core
|
||||||
|
return names_.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ExternalNode::id() const
|
||||||
|
{
|
||||||
|
return id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ExternalNode::Category() const
|
||||||
|
{
|
||||||
|
// FIXME: Update to get currently selected language from core
|
||||||
|
return categories_.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ExternalNode::Description() const
|
||||||
|
{
|
||||||
|
// FIXME: Update to get currently selected language from core
|
||||||
|
return descriptions_.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExternalNode::Retranslate()
|
||||||
|
{
|
||||||
|
QMap<QString, QMap<QString, QString> >::const_iterator iterator;
|
||||||
|
|
||||||
|
// Iterate through parameter language tables that we have
|
||||||
|
for (iterator=param_names_.begin();iterator!=param_names_.end();iterator++) {
|
||||||
|
NodeInput* this_input = static_cast<NodeInput*>(GetParameterWithID(iterator.key()));
|
||||||
|
|
||||||
|
// FIXME: Update to get currently selected language from core
|
||||||
|
this_input->set_name(iterator.value().first());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExternalNode::IsAccelerated() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ExternalNode::AcceleratedCodeVertex() const
|
||||||
|
{
|
||||||
|
return vert_code_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ExternalNode::AcceleratedCodeFragment() const
|
||||||
|
{
|
||||||
|
return frag_code_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ExternalNode::AcceleratedCodeIterations() const
|
||||||
|
{
|
||||||
|
return iterations_;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeInput *ExternalNode::AcceleratedCodeIterativeInput() const
|
||||||
|
{
|
||||||
|
return iteration_input_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExternalNode::XMLReadLanguageString(QXmlStreamReader* reader, QMap<QString, QString>* map)
|
||||||
|
{
|
||||||
|
QString lang;
|
||||||
|
|
||||||
|
// Traverse through name attributes for its language
|
||||||
|
QXmlStreamAttributes attrs = reader->attributes();
|
||||||
|
foreach (const QXmlStreamAttribute& attr, attrs) {
|
||||||
|
if (attr.name() == "lang") {
|
||||||
|
lang = attr.value().toString();
|
||||||
|
|
||||||
|
// We don't recognize any other "name" attributes at this time
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert name with language into map
|
||||||
|
map->insert(lang, reader->readElementText().trimmed());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExternalNode::XMLReadEffect(QXmlStreamReader* reader)
|
||||||
|
{
|
||||||
|
// Traverse through effect attributes for an ID
|
||||||
|
QXmlStreamAttributes attrs = reader->attributes();
|
||||||
|
foreach (const QXmlStreamAttribute& attr, attrs) {
|
||||||
|
if (attr.name() == "id") {
|
||||||
|
id_ = attr.value().toString();
|
||||||
|
|
||||||
|
// We don't recognize any other "effect" attributes at this time
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id_.isEmpty()) {
|
||||||
|
qWarning() << "Effect metadata" << xml_filename_ << "has no ID";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue reading for other metadata
|
||||||
|
while (!reader->atEnd() && !(reader->isEndElement() && reader->name() == "effect")) {
|
||||||
|
reader->readNext();
|
||||||
|
|
||||||
|
if (reader->isStartElement()) {
|
||||||
|
if (reader->name() == "name") {
|
||||||
|
// Pick up name
|
||||||
|
XMLReadLanguageString(reader, &names_);
|
||||||
|
} else if (reader->name() == "category") {
|
||||||
|
// Pick up category
|
||||||
|
XMLReadLanguageString(reader, &categories_);
|
||||||
|
} else if (reader->name() == "description") {
|
||||||
|
// Pick up description
|
||||||
|
XMLReadLanguageString(reader, &descriptions_);
|
||||||
|
} else if (reader->name() == "iterations") {
|
||||||
|
// Pick up iterations
|
||||||
|
XMLReadIterations(reader);
|
||||||
|
} else if (reader->name() == "fragment") {
|
||||||
|
// Pick up fragment shader code
|
||||||
|
XMLReadShader(reader, frag_code_);
|
||||||
|
} else if (reader->name() == "vertex") {
|
||||||
|
// Pick up vertex shader code
|
||||||
|
XMLReadShader(reader, vert_code_);
|
||||||
|
} else if (reader->name() == "param") {
|
||||||
|
// Pick up a parameter
|
||||||
|
XMLReadParam(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExternalNode::XMLReadIterations(QXmlStreamReader* reader)
|
||||||
|
{
|
||||||
|
int iteration_pickup = reader->readElementText().toInt();
|
||||||
|
|
||||||
|
if (iterations_ > 0) {
|
||||||
|
iterations_ = iteration_pickup;
|
||||||
|
} else {
|
||||||
|
// If the iteration value is invalid, don't set it, print an error insteead
|
||||||
|
qWarning() << "Invalid iteration number in" << xml_filename_ << "- setting to default (1)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExternalNode::XMLReadParam(QXmlStreamReader *reader)
|
||||||
|
{
|
||||||
|
QString param_id;
|
||||||
|
NodeParam::DataType param_type = NodeParam::kAny;
|
||||||
|
bool is_iterative = false;
|
||||||
|
|
||||||
|
// Traverse through parameter attributes for an ID
|
||||||
|
QXmlStreamAttributes attrs = reader->attributes();
|
||||||
|
foreach (const QXmlStreamAttribute& attr, attrs) {
|
||||||
|
if (attr.name() == "id") {
|
||||||
|
param_id = attr.value().toString();
|
||||||
|
} else if (attr.name() == "type") {
|
||||||
|
param_type = NodeParam::StringToDataType(attr.value().toString());
|
||||||
|
} else if (attr.name() == "iterative_input") {
|
||||||
|
is_iterative = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param_id.isEmpty()) {
|
||||||
|
qWarning() << "Effect metadata" << xml_filename_ << "contains a parameter with no ID - parameter was not added";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeInput* input = new NodeInput(param_id);
|
||||||
|
input->set_data_type(param_type);
|
||||||
|
|
||||||
|
if (is_iterative) {
|
||||||
|
iteration_input_ = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse through param contents for more data
|
||||||
|
while (!reader->atEnd() && !(reader->isEndElement() && reader->name() == "param")) {
|
||||||
|
reader->readNext();
|
||||||
|
|
||||||
|
if (reader->isStartElement()) {
|
||||||
|
// NOTE: readElementText() returns a string, but for number types (which min and max apply to), QVariant will
|
||||||
|
// convert them automatically
|
||||||
|
if (reader->name() == "name") {
|
||||||
|
|
||||||
|
// See if we've already made a table for this param before
|
||||||
|
if (!param_names_.contains(param_id)) {
|
||||||
|
param_names_.insert(param_id, QMap<QString, QString>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert language into table
|
||||||
|
XMLReadLanguageString(reader, ¶m_names_[param_id]);
|
||||||
|
|
||||||
|
} else if (reader->name() == "default") {
|
||||||
|
input->set_value_at_time(0, reader->readElementText());
|
||||||
|
} else if (reader->name() == "min") {
|
||||||
|
input->set_minimum(reader->readElementText());
|
||||||
|
} else if (reader->name() == "max") {
|
||||||
|
input->set_maximum(reader->readElementText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddInput(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExternalNode::XMLReadShader(QXmlStreamReader *reader, QString &destination)
|
||||||
|
{
|
||||||
|
QString code_url;
|
||||||
|
|
||||||
|
// Traverse through parameter attributes for an ID
|
||||||
|
QXmlStreamAttributes attrs = reader->attributes();
|
||||||
|
foreach (const QXmlStreamAttribute& attr, attrs) {
|
||||||
|
if (attr.name() == "url") {
|
||||||
|
code_url = attr.value().toString();
|
||||||
|
|
||||||
|
// We don't recognize any other "shader" attributes at this time
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add code in file from URL
|
||||||
|
if (!code_url.isEmpty()) {
|
||||||
|
destination.append(ReadFileAsString(code_url));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add any code that's inline in the XML
|
||||||
|
QString element_text = reader->readElementText().trimmed();
|
||||||
|
if (!element_text.isEmpty()) {
|
||||||
|
destination.append(element_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#ifndef EXTERNALNODE_H
|
||||||
|
#define EXTERNALNODE_H
|
||||||
|
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
|
#include "node.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A node generated from an external XML metadata file
|
||||||
|
*/
|
||||||
|
class ExternalNode : public Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ExternalNode(const QString& xml_meta_filename);
|
||||||
|
|
||||||
|
virtual Node* copy() const override;
|
||||||
|
|
||||||
|
virtual QString Name() const override;
|
||||||
|
virtual QString id() const override;
|
||||||
|
virtual QString Category() const override;
|
||||||
|
virtual QString Description() const override;
|
||||||
|
|
||||||
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
|
virtual bool IsAccelerated() const override;
|
||||||
|
virtual QString AcceleratedCodeVertex() const override;
|
||||||
|
virtual QString AcceleratedCodeFragment() const override;
|
||||||
|
virtual int AcceleratedCodeIterations() const override;
|
||||||
|
virtual NodeInput* AcceleratedCodeIterativeInput() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void XMLReadLanguageString(QXmlStreamReader* reader, QMap<QString, QString>* map);
|
||||||
|
void XMLReadEffect(QXmlStreamReader *reader);
|
||||||
|
void XMLReadIterations(QXmlStreamReader* reader);
|
||||||
|
void XMLReadParam(QXmlStreamReader* reader);
|
||||||
|
void XMLReadShader(QXmlStreamReader* reader, QString& destination);
|
||||||
|
|
||||||
|
QString xml_filename_;
|
||||||
|
|
||||||
|
QMap<QString, QString> names_;
|
||||||
|
QMap<QString, QString> descriptions_;
|
||||||
|
QMap<QString, QString> categories_;
|
||||||
|
QMap<QString, QMap<QString, QString> > param_names_;
|
||||||
|
|
||||||
|
QString id_;
|
||||||
|
|
||||||
|
QString frag_code_;
|
||||||
|
QString vert_code_;
|
||||||
|
|
||||||
|
int iterations_;
|
||||||
|
NodeInput* iteration_input_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EXTERNALNODE_H
|
||||||
Reference in New Issue
Block a user