From 55547fc236cb41579ec16318fc5db03a364c9e82 Mon Sep 17 00:00:00 2001 From: est77 Date: Sun, 19 Apr 2020 10:58:14 +0200 Subject: [PATCH] Split XML project file reading and writing. --- src/appleseed.python/bindproject.cpp | 25 +- .../project/projectmanager.cpp | 1 + src/appleseed/CMakeLists.txt | 4 + .../modeling/project/projectfilereader.cpp | 3304 +--------------- .../modeling/project/projectfilereader.h | 42 +- .../modeling/project/projectfilewriter.cpp | 1019 +---- .../modeling/project/projectfilewriter.h | 17 - .../modeling/project/xmlprojectfilereader.cpp | 3344 +++++++++++++++++ .../modeling/project/xmlprojectfilereader.h | 76 + .../modeling/project/xmlprojectfilewriter.cpp | 1062 ++++++ .../modeling/project/xmlprojectfilewriter.h | 62 + 11 files changed, 4620 insertions(+), 4336 deletions(-) create mode 100644 src/appleseed/renderer/modeling/project/xmlprojectfilereader.cpp create mode 100644 src/appleseed/renderer/modeling/project/xmlprojectfilereader.h create mode 100644 src/appleseed/renderer/modeling/project/xmlprojectfilewriter.cpp create mode 100644 src/appleseed/renderer/modeling/project/xmlprojectfilewriter.h diff --git a/src/appleseed.python/bindproject.cpp b/src/appleseed.python/bindproject.cpp index 16d442a819..6a36bbbb5d 100644 --- a/src/appleseed.python/bindproject.cpp +++ b/src/appleseed.python/bindproject.cpp @@ -248,27 +248,28 @@ namespace } bpy::object project_file_reader_read_default_opts( - ProjectFileReader* reader, const char* project_filename, const char* schema_filename) { - auto_release_ptr project(reader->read(project_filename, schema_filename)); + auto_release_ptr project( + ProjectFileReader::read(project_filename, schema_filename)); return bpy::object(project); } bpy::object project_file_reader_read_with_opts( - ProjectFileReader* reader, const char* project_filename, const char* schema_filename, const ProjectFileReader::Options opts) { - auto_release_ptr project(reader->read(project_filename, schema_filename, opts)); + auto_release_ptr project( + ProjectFileReader::read(project_filename, schema_filename, opts)); return bpy::object(project); } - bpy::object project_file_reader_load_builtin(ProjectFileReader* reader, const char* project_name) + bpy::object project_file_reader_load_builtin(const char* project_name) { - auto_release_ptr project(reader->load_builtin(project_name)); + auto_release_ptr project( + ProjectFileReader::load_builtin(project_name)); return bpy::object(project); } @@ -353,9 +354,9 @@ void bind_project() .value("OmitProjectSchemaValidation", ProjectFileReader::OmitProjectSchemaValidation); bpy::class_("ProjectFileReader") - .def("read", &project_file_reader_read_default_opts) - .def("read", &project_file_reader_read_with_opts) - .def("load_builtin", &project_file_reader_load_builtin); + .def("read", &project_file_reader_read_default_opts).staticmethod("read") + .def("read", &project_file_reader_read_with_opts).staticmethod("read") + .def("load_builtin", &project_file_reader_load_builtin).staticmethod("load_builtin"); bpy::enum_("ProjectFileWriterOptions") .value("Defaults", ProjectFileWriter::Defaults) @@ -366,7 +367,7 @@ void bind_project() bpy::class_("ProjectFileWriter") // These methods are static but for symmetry with ProjectFileReader we're exposing them as non-static. - .def("write", write_project_default_opts) - .def("write", write_project_with_opts) - .def("write", write_project_with_opts_and_comments); + .def("write", write_project_default_opts).staticmethod("write") + .def("write", write_project_with_opts).staticmethod("write") + .def("write", write_project_with_opts_and_comments).staticmethod("write"); } diff --git a/src/appleseed.qtcommon/project/projectmanager.cpp b/src/appleseed.qtcommon/project/projectmanager.cpp index a29e994ff5..69ab6a9c32 100644 --- a/src/appleseed.qtcommon/project/projectmanager.cpp +++ b/src/appleseed.qtcommon/project/projectmanager.cpp @@ -38,6 +38,7 @@ // appleseed.foundation headers. #include "foundation/platform/compiler.h" +#include "foundation/utility/searchpaths.h" // Qt headers. #include diff --git a/src/appleseed/CMakeLists.txt b/src/appleseed/CMakeLists.txt index 57df7ea090..0603196193 100644 --- a/src/appleseed/CMakeLists.txt +++ b/src/appleseed/CMakeLists.txt @@ -2028,6 +2028,10 @@ set (renderer_modeling_project_sources renderer/modeling/project/projecttracker.cpp renderer/modeling/project/projecttracker.h renderer/modeling/project/renderingtimer.h + renderer/modeling/project/xmlprojectfilereader.cpp + renderer/modeling/project/xmlprojectfilereader.h + renderer/modeling/project/xmlprojectfilewriter.cpp + renderer/modeling/project/xmlprojectfilewriter.h ) list (APPEND appleseed_sources ${renderer_modeling_project_sources} diff --git a/src/appleseed/renderer/modeling/project/projectfilereader.cpp b/src/appleseed/renderer/modeling/project/projectfilereader.cpp index 05929c5544..51dcf9823e 100644 --- a/src/appleseed/renderer/modeling/project/projectfilereader.cpp +++ b/src/appleseed/renderer/modeling/project/projectfilereader.cpp @@ -31,3100 +31,32 @@ #include "projectfilereader.h" // appleseed.renderer headers. -#include "renderer/global/globallogger.h" -#include "renderer/global/globaltypes.h" -#include "renderer/modeling/aov/aov.h" -#include "renderer/modeling/aov/aovfactoryregistrar.h" -#include "renderer/modeling/aov/iaovfactory.h" -#include "renderer/modeling/bsdf/bsdf.h" -#include "renderer/modeling/bsdf/bsdffactoryregistrar.h" -#include "renderer/modeling/bsdf/ibsdffactory.h" -#include "renderer/modeling/bssrdf/bssrdf.h" -#include "renderer/modeling/bssrdf/bssrdffactoryregistrar.h" -#include "renderer/modeling/bssrdf/ibssrdffactory.h" -#include "renderer/modeling/camera/camera.h" -#include "renderer/modeling/camera/camerafactoryregistrar.h" -#include "renderer/modeling/camera/icamerafactory.h" -#include "renderer/modeling/color/colorentity.h" -#include "renderer/modeling/display/display.h" -#include "renderer/modeling/edf/edf.h" -#include "renderer/modeling/edf/edffactoryregistrar.h" -#include "renderer/modeling/edf/iedffactory.h" -#include "renderer/modeling/environment/environment.h" -#include "renderer/modeling/environmentedf/environmentedf.h" -#include "renderer/modeling/environmentedf/environmentedffactoryregistrar.h" -#include "renderer/modeling/environmentedf/ienvironmentedffactory.h" -#include "renderer/modeling/environmentshader/environmentshader.h" -#include "renderer/modeling/environmentshader/environmentshaderfactoryregistrar.h" -#include "renderer/modeling/environmentshader/ienvironmentshaderfactory.h" -#include "renderer/modeling/frame/frame.h" -#include "renderer/modeling/light/ilightfactory.h" -#include "renderer/modeling/light/light.h" -#include "renderer/modeling/light/lightfactoryregistrar.h" -#include "renderer/modeling/material/imaterialfactory.h" -#include "renderer/modeling/material/material.h" -#include "renderer/modeling/material/materialfactoryregistrar.h" -#include "renderer/modeling/object/iobjectfactory.h" -#include "renderer/modeling/object/object.h" -#include "renderer/modeling/object/objectfactoryregistrar.h" -#include "renderer/modeling/postprocessingstage/ipostprocessingstagefactory.h" -#include "renderer/modeling/postprocessingstage/postprocessingstage.h" -#include "renderer/modeling/postprocessingstage/postprocessingstagefactoryregistrar.h" #include "renderer/modeling/project-builtin/cornellboxproject.h" #include "renderer/modeling/project-builtin/defaultproject.h" #include "renderer/modeling/project/configuration.h" -#include "renderer/modeling/project/configurationcontainer.h" #include "renderer/modeling/project/eventcounters.h" #include "renderer/modeling/project/project.h" #include "renderer/modeling/project/projectfileupdater.h" #include "renderer/modeling/project/projectformatrevision.h" +#include "renderer/modeling/project/xmlprojectfilereader.h" #include "renderer/modeling/scene/assembly.h" -#include "renderer/modeling/scene/assemblyfactoryregistrar.h" -#include "renderer/modeling/scene/assemblyinstance.h" -#include "renderer/modeling/scene/containers.h" -#include "renderer/modeling/scene/iassemblyfactory.h" -#include "renderer/modeling/scene/objectinstance.h" #include "renderer/modeling/scene/scene.h" -#include "renderer/modeling/scene/textureinstance.h" -#include "renderer/modeling/shadergroup/shadergroup.h" -#include "renderer/modeling/surfaceshader/isurfaceshaderfactory.h" -#include "renderer/modeling/surfaceshader/surfaceshader.h" -#include "renderer/modeling/surfaceshader/surfaceshaderfactoryregistrar.h" -#include "renderer/modeling/texture/itexturefactory.h" -#include "renderer/modeling/texture/texture.h" -#include "renderer/modeling/texture/texturefactoryregistrar.h" -#include "renderer/modeling/volume/ivolumefactory.h" -#include "renderer/modeling/volume/volume.h" -#include "renderer/modeling/volume/volumefactoryregistrar.h" -#include "renderer/utility/paramarray.h" -#include "renderer/utility/pluginstore.h" -#include "renderer/utility/transformsequence.h" // appleseed.foundation headers. -#include "foundation/containers/dictionary.h" -#include "foundation/core/exceptions/exceptionunsupportedfileformat.h" -#include "foundation/log/log.h" -#include "foundation/math/aabb.h" -#include "foundation/math/matrix.h" -#include "foundation/math/scalar.h" -#include "foundation/math/transform.h" -#include "foundation/math/vector.h" -#include "foundation/memory/memory.h" -#include "foundation/platform/compiler.h" -#include "foundation/platform/defaulttimers.h" -#include "foundation/platform/types.h" #include "foundation/string/string.h" -#include "foundation/utility/api/apiarray.h" -#include "foundation/utility/api/apistring.h" -#include "foundation/utility/foreach.h" -#include "foundation/utility/iterators.h" -#include "foundation/utility/otherwise.h" -#include "foundation/utility/searchpaths.h" #include "foundation/utility/stopwatch.h" -#include "foundation/utility/xercesc.h" -#include "foundation/utility/zip.h" -// Xerces-C++ headers. -#include "xercesc/sax2/Attributes.hpp" -#include "xercesc/sax2/SAX2XMLReader.hpp" -#include "xercesc/sax2/XMLReaderFactory.hpp" -#include "xercesc/util/XMLException.hpp" -#include "xercesc/util/XMLUni.hpp" - -// Boost headers. -#include "boost/filesystem.hpp" -#include "boost/filesystem/operations.hpp" - -// Standard headers. -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace foundation; -using namespace xercesc; -namespace bf = boost::filesystem; - -namespace renderer -{ - -// -// ProjectFileReader class implementation. -// - -namespace -{ - // - // Like foundation::ErrorHandler, but additionally keeps track - // of the number of warnings and errors that were emitted. - // - - class ErrorLoggerAndCounter - : public ErrorLogger - { - public: - ErrorLoggerAndCounter( - const std::string& input_filepath, - EventCounters& event_counters) - : ErrorLogger(global_logger(), input_filepath) - , m_event_counters(event_counters) - { - } - - void resetErrors() override - { - m_event_counters.clear(); - } - - void warning(const SAXParseException& e) override - { - ErrorLogger::warning(e); - m_event_counters.signal_warning(); - } - - void error(const SAXParseException& e) override - { - ErrorLogger::error(e); - m_event_counters.signal_error(); - throw e; // terminate parsing - } - - void fatalError(const SAXParseException& e) override - { - ErrorLogger::fatalError(e); - m_event_counters.signal_error(); - throw e; // terminate parsing - } - - private: - EventCounters& m_event_counters; - }; - - - // - // A set of objects that is passed to all element handlers. - // - - class ParseContext - { - public: - ParseContext( - Project& project, - const int options, - EventCounters& event_counters) - : m_project(project) - , m_options(options) - , m_event_counters(event_counters) - { - } - - Project& get_project() - { - return m_project; - } - - int get_options() const - { - return m_options; - } - - EventCounters& get_event_counters() - { - return m_event_counters; - } - - private: - Project& m_project; - const int m_options; - EventCounters& m_event_counters; - }; - - - // - // Utility functions. - // - - template - T get_scalar( - const std::string& text, - ParseContext& context) - { - try - { - return from_string(text); - } - catch (const ExceptionStringConversionError&) - { - RENDERER_LOG_ERROR("expected scalar value, got \"%s\".", text.c_str()); - context.get_event_counters().signal_error(); - return T(0.0); - } - } - - Vector3d get_vector3( - const std::string& text, - ParseContext& context) - { - Vector3d vec; - bool succeeded = false; - - try - { - const size_t count = tokenize(text, Blanks, &vec[0], 3); - if (count == 1) - { - vec[1] = vec[0]; - vec[2] = vec[0]; - succeeded = true; - } - else if (count == 3) - { - succeeded = true; - } - } - catch (const ExceptionStringConversionError&) - { - } - - if (!succeeded) - { - RENDERER_LOG_ERROR("invalid vector format."); - context.get_event_counters().signal_error(); - vec = Vector3d(0.0); - } - - return vec; - } - - template - void get_vector( - const std::string& text, - Vec& values, - ParseContext& context) - { - try - { - tokenize(text, Blanks, values); - } - catch (const ExceptionStringConversionError&) - { - RENDERER_LOG_ERROR("invalid vector format."); - context.get_event_counters().signal_error(); - values.clear(); - } - } - - template - auto_release_ptr create_entity( - const EntityFactoryRegistrar& registrar, - const std::string& type, - const std::string& model, - const std::string& name, - const ParamArray& params, - ParseContext& context) - { - try - { - const typename EntityFactoryRegistrar::FactoryType* factory = - registrar.lookup(model.c_str()); - - if (factory) - { - return factory->create(name.c_str(), params); - } - else - { - RENDERER_LOG_ERROR( - "while defining %s \"%s\": invalid model \"%s\".", - type.c_str(), - name.c_str(), - model.c_str()); - context.get_event_counters().signal_error(); - } - } - catch (const ExceptionDictionaryKeyNotFound& e) - { - RENDERER_LOG_ERROR( - "while defining %s \"%s\": required parameter \"%s\" missing.", - type.c_str(), - name.c_str(), - e.string()); - context.get_event_counters().signal_error(); - } - catch (const ExceptionUnknownEntity& e) - { - RENDERER_LOG_ERROR( - "while defining %s \"%s\": unknown entity \"%s\".", - type.c_str(), - name.c_str(), - e.string()); - context.get_event_counters().signal_error(); - } - - return auto_release_ptr(nullptr); - } - - - // - // Numeric representation of the XML elements. - // - - enum ProjectElementID - { - ElementAlpha, - ElementAOV, - ElementAOVs, - ElementAssembly, - ElementAssemblyInstance, - ElementAssignMaterial, - ElementBSDF, - ElementBSSRDF, - ElementCamera, - ElementColor, - ElementConfiguration, - ElementConfigurations, - ElementDisplay, - ElementEDF, - ElementEnvironment, - ElementEnvironmentEDF, - ElementEnvironmentShader, - ElementFrame, - ElementLight, - ElementLookAt, - ElementMaterial, - ElementMatrix, - ElementObject, - ElementObjectInstance, - ElementOSLCode, - ElementOutput, - ElementParameter, - ElementParameters, - ElementPostProcessingStage, - ElementPostProcessingStages, - ElementProject, - ElementRotation, - ElementScaling, - ElementScene, - ElementSearchPath, - ElementSearchPaths, - ElementShader, - ElementShaderConnection, - ElementShaderGroup, - ElementSurfaceShader, - ElementTexture, - ElementTextureInstance, - ElementTransform, - ElementTranslation, - ElementValues, - ElementVolume - }; - - typedef IElementHandler ElementHandlerType; - typedef ElementHandlerBase ElementHandlerBaseType; - - - // - // element handler. - // - - class ParameterElementHandler - : public ElementHandlerBaseType - { - public: - explicit ParameterElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - m_name = ElementHandlerBaseType::get_value(attrs, "name"); - m_value = ElementHandlerBaseType::get_value(attrs, "value"); - } - - void characters( - const XMLCh* const chars, - const XMLSize_t length) override - { - const std::string inner_value = transcode(chars); - if (!m_value.empty() && !inner_value.empty()) - { - RENDERER_LOG_ERROR( - "while defining element: value specified multiple times."); - m_context.get_event_counters().signal_error(); - } - else - { - m_value = inner_value; - } - } - - const std::string& get_name() const - { - return m_name; - } - - const std::string& get_value() const - { - return m_value; - } - - private: - ParseContext& m_context; - std::string m_name; - std::string m_value; - }; - - - // - // Handle an element containing a (hierarchical) set of parameters. - // - - class ParametrizedElementHandler - : public ElementHandlerBaseType - { - public: - void start_element(const Attributes& attrs) override; - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override; - - protected: - ParamArray m_params; - }; - - - // - // element handler. - // - - class ParametersElementHandler - : public ParametrizedElementHandler - { - public: - explicit ParametersElementHandler(ParseContext& context) - { - } - - void start_element(const Attributes& attrs) override - { - ParametrizedElementHandler::start_element(attrs); - - m_params.clear(); - - m_name = get_value(attrs, "name"); - } - - const std::string& get_name() const - { - return m_name; - } - - const ParamArray& get_parameters() const - { - return m_params; - } - - private: - std::string m_name; - }; - - - // - // ParametrizedElementHandler class implementation. - // - - void ParametrizedElementHandler::start_element(const Attributes& attrs) - { - m_params.clear(); - } - - void ParametrizedElementHandler::end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) - { - switch (element) - { - case ElementParameter: - { - ParameterElementHandler* param_handler = - static_cast(handler); - m_params.insert_path( - param_handler->get_name().c_str(), - param_handler->get_value()); - } - break; - - case ElementParameters: - { - ParametersElementHandler* params_handler = - static_cast(handler); - m_params.dictionaries().insert( - params_handler->get_name().c_str(), - params_handler->get_parameters()); - } - break; - - assert_otherwise; - } - } - - - // - // element handler. - // - - class LookAtElementHandler - : public ElementHandlerBaseType - { - public: - explicit LookAtElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - m_matrix = Matrix4d::identity(); - - const Vector3d origin = get_vector3(get_value(attrs, "origin"), m_context); - const Vector3d target = get_vector3(get_value(attrs, "target"), m_context); - const Vector3d up = get_vector3(get_value(attrs, "up"), m_context); - - if (norm(origin - target) > 0.0 && - norm(up) > 0.0 && - norm(cross(up, origin - target)) > 0.0) - { - m_matrix = Matrix4d::make_lookat(origin, target, normalize(up)); - } - else - { - RENDERER_LOG_ERROR( - "while defining element: the vectors\n" - " origin (%f, %f, %f)\n" - " target (%f, %f, %f)\n" - " up (%f, %f, %f)\n" - "form a singular transformation matrix.", - origin[0], origin[1], origin[2], - target[0], target[1], target[2], - up[0], up[1], up[2]); - m_context.get_event_counters().signal_error(); - } - } - - const Matrix4d& get_matrix() const - { - return m_matrix; - } - - private: - ParseContext& m_context; - Matrix4d m_matrix; - }; - - - // - // element handler. - // - - class MatrixElementHandler - : public ElementHandlerBaseType - { - public: - explicit MatrixElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - m_matrix = Matrix4d::identity(); - clear_keep_memory(m_values); - } - - void end_element() override - { - if (m_values.size() == 16) - { - for (size_t i = 0; i < 16; ++i) - m_matrix[i] = m_values[i]; - } - else - { - RENDERER_LOG_ERROR( - "while defining element: expected 16 scalar coefficients, got " FMT_SIZE_T ".", - m_values.size()); - m_context.get_event_counters().signal_error(); - } - } - - void characters( - const XMLCh* const chars, - const XMLSize_t length) override - { - get_vector(transcode(chars), m_values, m_context); - } - - const Matrix4d& get_matrix() const - { - return m_matrix; - } - - private: - ParseContext& m_context; - Matrix4d m_matrix; - std::vector m_values; - }; - - - // - // element handler. - // - - class RotationElementHandler - : public ElementHandlerBaseType - { - public: - explicit RotationElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - m_matrix = Matrix4d::identity(); - - const Vector3d axis = get_vector3(get_value(attrs, "axis"), m_context); - const double angle = get_scalar(get_value(attrs, "angle"), m_context); - - if (norm(axis) > 0.0) - { - m_matrix = Matrix4d::make_rotation(normalize(axis), deg_to_rad(angle)); - } - else - { - RENDERER_LOG_ERROR("while defining element: the rotation axis cannot be null."); - m_context.get_event_counters().signal_error(); - } - } - - const Matrix4d& get_matrix() const - { - return m_matrix; - } - - private: - ParseContext& m_context; - Matrix4d m_matrix; - }; - - - // - // element handler. - // - - class ScalingElementHandler - : public ElementHandlerBaseType - { - public: - explicit ScalingElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - const Vector3d value = get_vector3(get_value(attrs, "value"), m_context); - m_matrix = Matrix4d::make_scaling(value); - } - - const Matrix4d& get_matrix() const - { - return m_matrix; - } - - private: - ParseContext& m_context; - Matrix4d m_matrix; - }; - - - // - // element handler. - // - - class TranslationElementHandler - : public ElementHandlerBaseType - { - public: - explicit TranslationElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - const Vector3d value = get_vector3(get_value(attrs, "value"), m_context); - m_matrix = Matrix4d::make_translation(value); - } - - const Matrix4d& get_matrix() const - { - return m_matrix; - } - - private: - ParseContext& m_context; - Matrix4d m_matrix; - }; - - - // - // element handler. - // - - class TransformElementHandler - : public ElementHandlerBaseType - { - public: - explicit TransformElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - m_time = get_scalar(get_value(attrs, "time", "0.0"), m_context); - m_matrix = Matrix4d::identity(); - } - - void end_element() override - { - try - { - m_transform = Transformd::from_local_to_parent(m_matrix); - } - catch (const ExceptionSingularMatrix&) - { - RENDERER_LOG_ERROR("while defining element: the transformation matrix is singular."); - m_context.get_event_counters().signal_error(); - m_transform = Transformd::identity(); - } - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementLookAt: - { - LookAtElementHandler* lookat_handler = - static_cast(handler); - m_matrix = lookat_handler->get_matrix() * m_matrix; - } - break; - - case ElementMatrix: - { - MatrixElementHandler* matrix_handler = - static_cast(handler); - m_matrix = matrix_handler->get_matrix() * m_matrix; - } - break; - - case ElementRotation: - { - RotationElementHandler* rotation_handler = - static_cast(handler); - m_matrix = rotation_handler->get_matrix() * m_matrix; - } - break; - - case ElementScaling: - { - ScalingElementHandler* scaling_handler = - static_cast(handler); - m_matrix = scaling_handler->get_matrix() * m_matrix; - } - break; - - case ElementTranslation: - { - TranslationElementHandler* translation_handler = - static_cast(handler); - m_matrix = translation_handler->get_matrix() * m_matrix; - } - break; - - assert_otherwise; - } - } - - float get_time() const - { - return m_time; - } - - const Transformd& get_transform() const - { - return m_transform; - } - - private: - ParseContext& m_context; - float m_time; - Matrix4d m_matrix; - Transformd m_transform; - }; - - - // - // Handle a transformation sequence. - // - - template - class TransformSequenceElementHandler - : public Base - { - public: - void start_element(const Attributes& attrs) override - { - Base::start_element(attrs); - } - - void end_element() override - { - if (m_transforms.size() > 1) - collapse_transforms(); - - if (m_transforms.empty()) - m_transforms[0.0f] = Transformd::identity(); - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementTransform: - { - TransformElementHandler* transform_handler = - static_cast(handler); - m_transforms[transform_handler->get_time()] = transform_handler->get_transform(); - } - break; - - default: - Base::end_child_element(element, handler); - break; - } - } - - void copy_transform_sequence_to(TransformSequence& target) const - { - target.clear(); - - for (const_each i = m_transforms; i; ++i) - target.set_transform(i->first, i->second); - } - - Transformd get_earliest_transform() const - { - TransformSequence sequence; - copy_transform_sequence_to(sequence); - return sequence.get_earliest_transform(); - } - - private: - typedef std::map TransformMap; - - TransformMap m_transforms; - - void collapse_transforms() - { - if (are_transforms_identical()) - { - const Transformd transform = m_transforms.begin()->second; - m_transforms.clear(); - m_transforms[0.0f] = transform; - } - } - - bool are_transforms_identical() const - { - for (TransformMap::const_iterator i = m_transforms.begin(), e = pred(m_transforms.end()); i != e; ++i) - { - if (i->second != succ(i)->second) - return false; - } - - return true; - } - }; - - - // - // element handler. - // - - class ValuesElementHandler - : public ElementHandlerBaseType - { - public: - explicit ValuesElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - m_values.clear(); - } - - void characters( - const XMLCh* const chars, - const XMLSize_t length) override - { - get_vector(transcode(chars), m_values, m_context); - } - - const ColorValueArray& get_values() const - { - return m_values; - } - - private: - ParseContext& m_context; - ColorValueArray m_values; - }; - - - // - // and elements handler. - // - - class ColorElementHandler - : public ParametrizedElementHandler - { - public: - explicit ColorElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - ParametrizedElementHandler::start_element(attrs); - - m_color_entity.reset(); - m_values.clear(); - m_alpha.clear(); - - m_name = get_value(attrs, "name"); - } - - void end_element() override - { - ParametrizedElementHandler::end_element(); - - try - { - m_color_entity = - m_alpha.empty() - ? ColorEntityFactory::create( - m_name.c_str(), - m_params, - m_values) - : ColorEntityFactory::create( - m_name.c_str(), - m_params, - m_values, - m_alpha); - } - catch (const ExceptionDictionaryKeyNotFound& e) - { - RENDERER_LOG_ERROR( - "while defining color \"%s\": required parameter \"%s\" missing.", - m_name.c_str(), - e.string()); - m_context.get_event_counters().signal_error(); - } - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementValues: - m_values = static_cast(handler)->get_values(); - break; - - case ElementAlpha: - m_alpha = static_cast(handler)->get_values(); - break; - - default: - ParametrizedElementHandler::end_child_element(element, handler); - break; - } - } - - auto_release_ptr get_color_entity() - { - return m_color_entity; - } - - private: - ParseContext& m_context; - auto_release_ptr m_color_entity; - std::string m_name; - ColorValueArray m_values; - ColorValueArray m_alpha; - }; - - - // - // Handle an element defining an entity. - // - - template - class EntityElementHandler - : public Base - { - public: - EntityElementHandler(const std::string& entity_type, ParseContext& context) - : m_context(context) - , m_entity_type(entity_type) - { - } - - void start_element(const Attributes& attrs) override - { - Base::start_element(attrs); - - m_entity.reset(); - - m_name = Base::get_value(attrs, "name"); - m_model = Base::get_value(attrs, "model"); - } - - void end_element() override - { - Base::end_element(); - - m_entity = - create_entity( - m_context.get_project().template get_factory_registrar(), - m_entity_type, - m_model, - m_name, - Base::m_params, - m_context); - } - - auto_release_ptr get_entity() - { - return m_entity; - } - - protected: - ParseContext& m_context; - const std::string m_entity_type; - auto_release_ptr m_entity; - std::string m_name; - std::string m_model; - }; - - - // - // element handler. - // - - class TextureElementHandler - : public ParametrizedElementHandler - { - public: - explicit TextureElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - ParametrizedElementHandler::start_element(attrs); - - m_texture.reset(); - - m_name = get_value(attrs, "name"); - m_model = get_value(attrs, "model"); - } - - void end_element() override - { - ParametrizedElementHandler::end_element(); - - try - { - const TextureFactoryRegistrar::FactoryType* factory = - m_texture_factory_registrar.lookup(m_model.c_str()); - - if (factory) - { - m_texture = - factory->create( - m_name.c_str(), - m_params, - m_context.get_project().search_paths()); - } - else - { - RENDERER_LOG_ERROR( - "while defining texture \"%s\": invalid model \"%s\".", - m_name.c_str(), - m_model.c_str()); - m_context.get_event_counters().signal_error(); - } - } - catch (const ExceptionDictionaryKeyNotFound& e) - { - RENDERER_LOG_ERROR( - "while defining texture \"%s\": required parameter \"%s\" missing.", - m_name.c_str(), - e.string()); - m_context.get_event_counters().signal_error(); - } - } - - auto_release_ptr get_texture() - { - return m_texture; - } - - private: - const TextureFactoryRegistrar m_texture_factory_registrar; - ParseContext& m_context; - auto_release_ptr m_texture; - std::string m_name; - std::string m_model; - }; - - - // - // element handler. - // - - class TextureInstanceElementHandler - : public TransformSequenceElementHandler - { - public: - explicit TextureInstanceElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - Base::start_element(attrs); - - m_texture_instance.reset(); - - m_name = get_value(attrs, "name"); - m_texture = get_value(attrs, "texture"); - } - - void end_element() override - { - Base::end_element(); - - try - { - m_texture_instance = - TextureInstanceFactory::create( - m_name.c_str(), - m_params, - m_texture.c_str(), - Transformf( - get_earliest_transform().get_local_to_parent(), - get_earliest_transform().get_parent_to_local())); - } - catch (const ExceptionDictionaryKeyNotFound& e) - { - RENDERER_LOG_ERROR( - "while defining texture instance \"%s\": required parameter \"%s\" missing.", - m_name.c_str(), - e.string()); - m_context.get_event_counters().signal_error(); - } - } - - auto_release_ptr get_texture_instance() - { - return m_texture_instance; - } - - private: - typedef TransformSequenceElementHandler Base; - - ParseContext& m_context; - auto_release_ptr m_texture_instance; - std::string m_name; - std::string m_texture; - }; - - - // - // element handler. - // - - class BSDFElementHandler - : public EntityElementHandler - { - public: - explicit BSDFElementHandler(ParseContext& context) - : EntityElementHandler("bsdf", context) - { - } - }; - - - // - // element handler. - // - - class BSSRDFElementHandler - : public EntityElementHandler - { - public: - explicit BSSRDFElementHandler(ParseContext& context) - : EntityElementHandler("bssrdf", context) - { - } - }; - - - // - // element handler. - // - - class EDFElementHandler - : public EntityElementHandler - { - public: - explicit EDFElementHandler(ParseContext& context) - : EntityElementHandler("edf", context) - { - } - }; - - - // - // element handler. - // - - class VolumeElementHandler - : public EntityElementHandler - { - public: - explicit VolumeElementHandler(ParseContext& context) - : EntityElementHandler("volume", context) - { - } - }; - - - // - // element handler. - // - - class SurfaceShaderElementHandler - : public EntityElementHandler - { - public: - explicit SurfaceShaderElementHandler(ParseContext& context) - : EntityElementHandler< SurfaceShader, ParametrizedElementHandler>("surface shader", context) - { - } - }; - - - // - // element handler. - // - - class EnvironmentElementHandler - : public ParametrizedElementHandler - { - public: - explicit EnvironmentElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - ParametrizedElementHandler::start_element(attrs); - - m_environment.reset(); - - m_name = get_value(attrs, "name"); - m_model = get_value(attrs, "model"); - } - - void end_element() override - { - ParametrizedElementHandler::end_element(); - - if (m_model == EnvironmentFactory::get_model()) - m_environment = EnvironmentFactory::create(m_name.c_str(), m_params); - else - { - RENDERER_LOG_ERROR( - "while defining environment \"%s\": invalid model \"%s\".", - m_name.c_str(), - m_model.c_str()); - m_context.get_event_counters().signal_error(); - } - } - - auto_release_ptr get_environment() - { - return m_environment; - } - - private: - ParseContext& m_context; - auto_release_ptr m_environment; - std::string m_name; - std::string m_model; - }; - - - // - // element handler. - // - - class EnvironmentEDFElementHandler - : public EntityElementHandler> - { - public: - explicit EnvironmentEDFElementHandler(ParseContext& context) - : Base("environment edf", context) - { - } - - void end_element() override - { - Base::end_element(); - - if (m_entity.get()) - copy_transform_sequence_to(m_entity->transform_sequence()); - } - - private: - typedef EntityElementHandler> Base; - }; - - - // - // element handler. - // - - class EnvironmentShaderElementHandler - : public EntityElementHandler - { - public: - explicit EnvironmentShaderElementHandler(ParseContext& context) - : EntityElementHandler("environment shader", context) - { - } - }; - - - // - // element handler. - // - - class LightElementHandler - : public EntityElementHandler> - { - public: - explicit LightElementHandler(ParseContext& context) - : Base("light", context) - { - } - - void end_element() override - { - Base::end_element(); - - if (m_entity.get()) - m_entity->set_transform(get_earliest_transform()); - } - - private: - typedef EntityElementHandler> Base; - }; - - - // - // element handler. - // - - class MaterialElementHandler - : public EntityElementHandler - { - public: - explicit MaterialElementHandler(ParseContext& context) - : EntityElementHandler("material", context) - { - } - }; - - - // - // element handler. - // - - class CameraElementHandler - : public EntityElementHandler> - { - public: - explicit CameraElementHandler(ParseContext& context) - : Base("camera", context) - { - } - - void end_element() override - { - Base::end_element(); - - if (m_entity.get()) - copy_transform_sequence_to(m_entity->transform_sequence()); - } - - private: - typedef EntityElementHandler> Base; - }; - - - // - // element handler. - // - - class ObjectElementHandler - : public ParametrizedElementHandler - { - public: - typedef std::vector ObjectVector; - - explicit ObjectElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - ParametrizedElementHandler::start_element(attrs); - - clear_keep_memory(m_objects); - - m_name = get_value(attrs, "name"); - m_model = get_value(attrs, "model"); - } - - void end_element() override - { - ParametrizedElementHandler::end_element(); - - try - { - const IObjectFactory* factory = - m_context.get_project().get_factory_registrar().lookup(m_model.c_str()); - - if (factory) - { - ObjectArray objects; - if (!factory->create( - m_name.c_str(), - m_params, - m_context.get_project().search_paths(), - m_context.get_options() & ProjectFileReader::OmitReadingMeshFiles, - objects)) - m_context.get_event_counters().signal_error(); - - m_objects = array_vector(objects); - } - else - { - RENDERER_LOG_ERROR( - "while defining object \"%s\": invalid model \"%s\".", - m_name.c_str(), - m_model.c_str()); - m_context.get_event_counters().signal_error(); - } - } - catch (const ExceptionDictionaryKeyNotFound& e) - { - RENDERER_LOG_ERROR( - "while defining object \"%s\": required parameter \"%s\" missing.", - m_name.c_str(), - e.string()); - m_context.get_event_counters().signal_error(); - } - catch (const ExceptionUnknownEntity& e) - { - RENDERER_LOG_ERROR( - "while defining object \"%s\": unknown entity \"%s\".", - m_name.c_str(), - e.string()); - m_context.get_event_counters().signal_error(); - } - catch (const Exception& e) - { - RENDERER_LOG_ERROR( - "while defining object \"%s\": %s", - m_name.c_str(), - e.what()); - m_context.get_event_counters().signal_error(); - } - } - - const ObjectVector& get_objects() const - { - return m_objects; - } - - private: - ParseContext& m_context; - ObjectVector m_objects; - std::string m_name; - std::string m_model; - }; - - - // - // element handler. - // - - class AssignMaterialElementHandler - : public ElementHandlerBaseType - { - public: - explicit AssignMaterialElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - m_slot = get_value(attrs, "slot"); - m_side = get_value(attrs, "side", "front"); - m_material = get_value(attrs, "material"); - - if (m_side != "front" && m_side != "back" && m_side != "both") - { - RENDERER_LOG_ERROR( - "while assigning material: side must be \"front\", \"back\" or \"both\", got \"%s\".", - m_side.c_str()); - m_context.get_event_counters().signal_error(); - m_side = "front"; - } - } - - const std::string& get_material_slot() const - { - return m_slot; - } - - const std::string& get_material_side() const - { - return m_side; - } - - const std::string& get_material_name() const - { - return m_material; - } - - private: - ParseContext& m_context; - std::string m_slot; - std::string m_side; - std::string m_material; - }; - - - // - // element handler. - // - - class ObjectInstanceElementHandler - : public TransformSequenceElementHandler - { - public: - explicit ObjectInstanceElementHandler(ParseContext& context) - { - } - - void start_element(const Attributes& attrs) override - { - Base::start_element(attrs); - - m_object_instance.reset(); - m_front_material_mappings.clear(); - m_back_material_mappings.clear(); - - m_name = get_value(attrs, "name"); - m_object = get_value(attrs, "object"); - } - - void end_element() override - { - Base::end_element(); - - m_object_instance = - ObjectInstanceFactory::create( - m_name.c_str(), - m_params, - m_object.c_str(), - get_earliest_transform(), - m_front_material_mappings, - m_back_material_mappings); - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementAssignMaterial: - { - AssignMaterialElementHandler* assign_mat_handler = - static_cast(handler); - - const std::string& material_slot = assign_mat_handler->get_material_slot(); - const std::string& material_side = assign_mat_handler->get_material_side(); - const std::string& material_name = assign_mat_handler->get_material_name(); - - if (material_side == "front") - m_front_material_mappings.insert(material_slot.c_str(), material_name); - else if (material_side == "back") - m_back_material_mappings.insert(material_slot.c_str(), material_name); - else if (material_side == "both") - { - m_front_material_mappings.insert(material_slot.c_str(), material_name); - m_back_material_mappings.insert(material_slot.c_str(), material_name); - } - } - break; - - default: - Base::end_child_element(element, handler); - break; - } - } - - auto_release_ptr get_object_instance() - { - return m_object_instance; - } - - private: - typedef TransformSequenceElementHandler Base; - - auto_release_ptr m_object_instance; - StringDictionary m_front_material_mappings; - StringDictionary m_back_material_mappings; - std::string m_name; - std::string m_object; - }; - - - // - // element handler. - // - - class AssemblyInstanceElementHandler - : public TransformSequenceElementHandler - { - public: - explicit AssemblyInstanceElementHandler(ParseContext& context) - { - } - - void start_element(const Attributes& attrs) override - { - Base::start_element(attrs); - - m_assembly_instance.reset(); - - m_name = get_value(attrs, "name"); - m_assembly = get_value(attrs, "assembly"); - } - - void end_element() override - { - Base::end_element(); - - m_assembly_instance = - AssemblyInstanceFactory::create( - m_name.c_str(), - m_params, - m_assembly.c_str()); - - copy_transform_sequence_to(m_assembly_instance->transform_sequence()); - } - - auto_release_ptr get_assembly_instance() - { - return m_assembly_instance; - } - - private: - typedef TransformSequenceElementHandler Base; - - auto_release_ptr m_assembly_instance; - std::string m_name; - std::string m_assembly; - }; - - - // - // element handler. - // - - class OSLCodeElementHandler - : public ElementHandlerBaseType - { - public: - explicit OSLCodeElementHandler(ParseContext& context) - { - } - - void characters( - const XMLCh* const chars, - const XMLSize_t length) override - { - m_code += transcode(chars); - } - - std::string get_code() const - { - return trim_both(m_code); - } - - private: - std::string m_code; - }; - - - // - // element handler. - // - - class ShaderElementHandler - : public ParametrizedElementHandler - { - public: - explicit ShaderElementHandler(ParseContext& context) - { - } - - void start_element(const Attributes& attrs) override - { - ParametrizedElementHandler::start_element(attrs); - m_type = get_value(attrs, "type"); - m_name = get_value(attrs, "name"); - m_layer = get_value(attrs, "layer"); - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementOSLCode: - { - OSLCodeElementHandler* code_handler = - static_cast(handler); - m_code = code_handler->get_code(); - } - break; - - default: - ParametrizedElementHandler::end_child_element(element, handler); - break; - } - } - - const std::string& get_type() const - { - return m_type; - } - - const std::string& get_name() const - { - return m_name; - } - - const std::string& get_layer() const - { - return m_layer; - } - - const std::string& get_code() const - { - return m_code; - } - - const ParamArray& get_params() const - { - return m_params; - } - - private: - std::string m_type; - std::string m_name; - std::string m_layer; - std::string m_code; - }; - - - // - // element handler. - // - - class ShaderConnectionElementHandler - : public ElementHandlerBaseType - { - public: - explicit ShaderConnectionElementHandler(ParseContext& context) - { - } - - void start_element(const Attributes& attrs) override - { - m_src_layer = get_value(attrs, "src_layer"); - m_src_param = get_value(attrs, "src_param"); - m_dst_layer = get_value(attrs, "dst_layer"); - m_dst_param = get_value(attrs, "dst_param"); - } - - const std::string& src_layer() - { - return m_src_layer; - } - - const std::string& src_param() - { - return m_src_param; - } - - const std::string& dst_layer() - { - return m_dst_layer; - } - - const std::string& dst_param() - { - return m_dst_param; - } - - private: - std::string m_src_layer; - std::string m_src_param; - std::string m_dst_layer; - std::string m_dst_param; - }; - - - // - // element handler. - // - - class ShaderGroupElementHandler - : public ElementHandlerBaseType - { - public: - explicit ShaderGroupElementHandler(ParseContext& context) - { - } - - void start_element(const Attributes& attrs) override - { - m_name = get_value(attrs, "name"); - m_shader_group = ShaderGroupFactory::create(m_name.c_str()); - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementShader: - { - ShaderElementHandler* shader_handler = - static_cast(handler); - - if (shader_handler->get_code().empty()) - { - m_shader_group->add_shader( - shader_handler->get_type().c_str(), - shader_handler->get_name().c_str(), - shader_handler->get_layer().c_str(), - shader_handler->get_params()); - } - else - { - m_shader_group->add_source_shader( - shader_handler->get_type().c_str(), - shader_handler->get_name().c_str(), - shader_handler->get_layer().c_str(), - shader_handler->get_code().c_str(), - shader_handler->get_params()); - } - } - break; - - case ElementShaderConnection: - { - ShaderConnectionElementHandler* shader_handler = - static_cast(handler); - m_shader_group->add_connection( - shader_handler->src_layer().c_str(), - shader_handler->src_param().c_str(), - shader_handler->dst_layer().c_str(), - shader_handler->dst_param().c_str()); - } - break; - } - } - - auto_release_ptr get_shader_group() - { - return m_shader_group; - } - - private: - std::string m_name; - auto_release_ptr m_shader_group; - }; - - - // - // Base class for assembly and scene element handlers. - // - - class BaseGroupElementHandler - : public ParametrizedElementHandler - { - public: - explicit BaseGroupElementHandler(ParseContext& context) - : m_context(context) - { - } - - protected: - ParseContext& m_context; - - template - void insert(Container& container, auto_release_ptr entity) - { - if (entity.get() == nullptr) - return; - - if (container.get_by_name(entity->get_name()) != nullptr) - { - RENDERER_LOG_ERROR( - "an entity with the path \"%s\" already exists.", - entity->get_path().c_str()); - m_context.get_event_counters().signal_error(); - return; - } - - container.insert(entity); - } - }; - - - // - // element handler. - // - - class AssemblyElementHandler - : public BaseGroupElementHandler - { - public: - explicit AssemblyElementHandler(ParseContext& context) - : BaseGroupElementHandler(context) - { - } - - void start_element(const Attributes& attrs) override - { - ParametrizedElementHandler::start_element(attrs); - - m_assembly.reset(); - - m_assemblies.clear(); - m_assembly_instances.clear(); - m_bsdfs.clear(); - m_bssrdfs.clear(); - m_colors.clear(); - m_edfs.clear(); - m_lights.clear(); - m_materials.clear(); - m_objects.clear(); - m_object_instances.clear(); - m_volumes.clear(); - m_shader_groups.clear(); - m_surface_shaders.clear(); - m_textures.clear(); - m_texture_instances.clear(); - - m_name = get_value(attrs, "name"); - m_model = get_value(attrs, "model", AssemblyFactory().get_model()); - } - - void end_element() override - { - ParametrizedElementHandler::end_element(); - - const IAssemblyFactory* factory = - m_context.get_project().get_factory_registrar().lookup(m_model.c_str()); - - if (factory) - { - m_assembly = factory->create(m_name.c_str(), m_params); - - m_assembly->assemblies().swap(m_assemblies); - m_assembly->assembly_instances().swap(m_assembly_instances); - m_assembly->bsdfs().swap(m_bsdfs); - m_assembly->bssrdfs().swap(m_bssrdfs); - m_assembly->colors().swap(m_colors); - m_assembly->edfs().swap(m_edfs); - m_assembly->lights().swap(m_lights); - m_assembly->materials().swap(m_materials); - m_assembly->objects().swap(m_objects); - m_assembly->object_instances().swap(m_object_instances); - m_assembly->volumes().swap(m_volumes); - m_assembly->shader_groups().swap(m_shader_groups); - m_assembly->surface_shaders().swap(m_surface_shaders); - m_assembly->textures().swap(m_textures); - m_assembly->texture_instances().swap(m_texture_instances); - } - else - { - RENDERER_LOG_ERROR( - "while defining assembly \"%s\": invalid model \"%s\".", - m_name.c_str(), - m_model.c_str()); - m_context.get_event_counters().signal_error(); - } - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementAssembly: - insert( - m_assemblies, - static_cast(handler)->get_assembly()); - break; - - case ElementAssemblyInstance: - insert( - m_assembly_instances, - static_cast(handler)->get_assembly_instance()); - break; - - case ElementBSDF: - insert( - m_bsdfs, - static_cast(handler)->get_entity()); - break; - - case ElementBSSRDF: - insert( - m_bssrdfs, - static_cast(handler)->get_entity()); - break; - - case ElementColor: - insert( - m_colors, - static_cast(handler)->get_color_entity()); - break; - - case ElementEDF: - insert( - m_edfs, - static_cast(handler)->get_entity()); - break; - - case ElementLight: - insert( - m_lights, - static_cast(handler)->get_entity()); - break; - - case ElementMaterial: - insert( - m_materials, - static_cast(handler)->get_entity()); - break; - - case ElementObject: - for (Object* object : static_cast(handler)->get_objects()) - insert(m_objects, auto_release_ptr(object)); - break; - - case ElementObjectInstance: - insert( - m_object_instances, - static_cast(handler)->get_object_instance()); - break; - - case ElementShaderGroup: - insert( - m_shader_groups, - static_cast(handler)->get_shader_group()); - break; - - case ElementSurfaceShader: - insert( - m_surface_shaders, - static_cast(handler)->get_entity()); - break; - - case ElementTexture: - insert( - m_textures, - static_cast(handler)->get_texture()); - break; - - case ElementTextureInstance: - insert( - m_texture_instances, - static_cast(handler)->get_texture_instance()); - break; - - case ElementVolume: - insert( - m_volumes, - static_cast(handler)->get_entity()); - break; - - default: - ParametrizedElementHandler::end_child_element(element, handler); - break; - } - } - - auto_release_ptr get_assembly() - { - return m_assembly; - } - - private: - auto_release_ptr m_assembly; - std::string m_name; - std::string m_model; - AssemblyContainer m_assemblies; - AssemblyInstanceContainer m_assembly_instances; - BSDFContainer m_bsdfs; - BSSRDFContainer m_bssrdfs; - ColorContainer m_colors; - EDFContainer m_edfs; - LightContainer m_lights; - MaterialContainer m_materials; - ObjectContainer m_objects; - ObjectInstanceContainer m_object_instances; - VolumeContainer m_volumes; - ShaderGroupContainer m_shader_groups; - SurfaceShaderContainer m_surface_shaders; - TextureContainer m_textures; - TextureInstanceContainer m_texture_instances; - }; - - - // - // element handler. - // - - class SceneElementHandler - : public BaseGroupElementHandler - { - public: - explicit SceneElementHandler(ParseContext& context) - : BaseGroupElementHandler(context) - { - } - - void start_element(const Attributes& attrs) override - { - ParametrizedElementHandler::start_element(attrs); - - // Discover and load plugins before building the scene. - m_context.get_project().get_plugin_store().load_all_plugins_from_paths(m_context.get_project().search_paths()); - - m_scene = SceneFactory::create(); - } - - void end_element() override - { - ParametrizedElementHandler::end_element(); - - m_scene->get_parameters() = m_params; - - const GAABB3 scene_bbox = m_scene->compute_bbox(); - const Vector3d scene_center(scene_bbox.center()); - - RENDERER_LOG_INFO( - "scene bounding box: (%f, %f, %f)-(%f, %f, %f).\n" - "scene bounding sphere: center (%f, %f, %f), diameter %f.", - scene_bbox.min[0], scene_bbox.min[1], scene_bbox.min[2], - scene_bbox.max[0], scene_bbox.max[1], scene_bbox.max[2], - scene_center[0], scene_center[1], scene_center[2], - scene_bbox.diameter()); - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - assert(m_scene.get()); - - switch (element) - { - case ElementAssembly: - insert( - m_scene->assemblies(), - static_cast(handler)->get_assembly()); - break; - - case ElementAssemblyInstance: - insert( - m_scene->assembly_instances(), - static_cast(handler)->get_assembly_instance()); - break; - - case ElementColor: - insert( - m_scene->colors(), - static_cast(handler)->get_color_entity()); - break; - - case ElementCamera: - { - auto_release_ptr camera = - static_cast(handler)->get_entity(); - if (camera.get()) - m_scene->cameras().insert(camera); - } - break; - - case ElementEnvironment: - { - auto_release_ptr environment = - static_cast(handler)->get_environment(); - if (environment.get()) - { - if (m_scene->get_environment()) - { - RENDERER_LOG_ERROR("cannot define multiple environments."); - m_context.get_event_counters().signal_error(); - } - m_scene->set_environment(environment); - } - } - break; - - case ElementEnvironmentEDF: - insert( - m_scene->environment_edfs(), - static_cast(handler)->get_entity()); - break; - - case ElementEnvironmentShader: - insert( - m_scene->environment_shaders(), - static_cast(handler)->get_entity()); - break; - - case ElementTexture: - insert( - m_scene->textures(), - static_cast(handler)->get_texture()); - break; - - case ElementTextureInstance: - insert( - m_scene->texture_instances(), - static_cast(handler)->get_texture_instance()); - break; - - case ElementShaderGroup: - insert( - m_scene->shader_groups(), - static_cast(handler)->get_shader_group()); - break; - - default: - ParametrizedElementHandler::end_child_element(element, handler); - break; - } - } - - auto_release_ptr get_scene() - { - return m_scene; - } - - private: - auto_release_ptr m_scene; - }; - - - // - // element handler. - // - - class AOVElementHandler - : public ParametrizedElementHandler - { - public: - explicit AOVElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - ParametrizedElementHandler::start_element(attrs); - - m_aov.reset(); - - m_model = ParametrizedElementHandler::get_value(attrs, "model"); - } - - void end_element() override - { - ParametrizedElementHandler::end_element(); - - try - { - const IAOVFactory* factory = - m_context.get_project().get_factory_registrar().lookup(m_model.c_str()); - - if (factory) - m_aov = factory->create(m_params); - else - { - RENDERER_LOG_ERROR( - "while defining aov: invalid model \"%s\".", - m_model.c_str()); - m_context.get_event_counters().signal_error(); - } - } - catch (const ExceptionDictionaryKeyNotFound& e) - { - RENDERER_LOG_ERROR( - "while defining aov \"%s\": required parameter \"%s\" missing.", - m_model.c_str(), - e.string()); - m_context.get_event_counters().signal_error(); - } - catch (const ExceptionUnknownEntity& e) - { - RENDERER_LOG_ERROR( - "while defining aov \"%s\": unknown entity \"%s\".", - m_model.c_str(), - e.string()); - m_context.get_event_counters().signal_error(); - } - } - - auto_release_ptr get_aov() - { - return m_aov; - } - - protected: - ParseContext& m_context; - auto_release_ptr m_aov; - std::string m_model; - }; - - - // - // element handler. - // - - class AOVsElementHandler - : public ElementHandlerBaseType - { - public: - explicit AOVsElementHandler(ParseContext& context) - : m_context(context) - , m_aovs(nullptr) - { - } - - void set_aov_container(AOVContainer* aovs) - { - m_aovs = aovs; - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - assert(m_aovs); - - switch (element) - { - case ElementAOV: - { - auto_release_ptr aov( - static_cast(handler)->get_aov()); - - if (aov.get() != nullptr) - { - if (m_aovs->get_by_name(aov->get_name()) == nullptr) - m_aovs->insert(aov); - else - { - RENDERER_LOG_ERROR( - "an aov with the path \"%s\" already exists.", - aov->get_path().c_str()); - m_context.get_event_counters().signal_error(); - } - } - } - break; - - assert_otherwise; - } - } - - private: - ParseContext& m_context; - AOVContainer* m_aovs; - }; - - - // - // element handler. - // - - class PostProcessingStageElementHandler - : public EntityElementHandler - { - public: - explicit PostProcessingStageElementHandler(ParseContext& context) - : EntityElementHandler("post-processing stage", context) - { - } - }; - - - // - // element handler. - // - - class PostProcessingStagesElementHandler - : public ElementHandlerBaseType - { - public: - explicit PostProcessingStagesElementHandler(ParseContext& context) - : m_context(context) - , m_stages(nullptr) - { - } - - void set_post_processing_stage_container(PostProcessingStageContainer* stages) - { - m_stages = stages; - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - assert(m_stages); - - switch (element) - { - case ElementPostProcessingStage: - { - auto_release_ptr stage = - static_cast(handler)->get_entity(); - - if (stage.get() != nullptr) - { - if (m_stages->get_by_name(stage->get_name()) == nullptr) - m_stages->insert(stage); - else - { - RENDERER_LOG_ERROR( - "a post-processing stage with the path \"%s\" already exists.", - stage->get_path().c_str()); - m_context.get_event_counters().signal_error(); - } - } - } - break; - - assert_otherwise; - } - } - - private: - ParseContext& m_context; - PostProcessingStageContainer* m_stages; - }; - - - // - // element handler. - // - - class FrameElementHandler - : public ParametrizedElementHandler - { - public: - explicit FrameElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - ParametrizedElementHandler::start_element(attrs); - - m_frame.reset(); - - m_name = get_value(attrs, "name"); - - m_aovs.clear(); - m_post_processing_stages.clear(); - } - - void end_element() override - { - ParametrizedElementHandler::end_element(); - - m_frame = - FrameFactory::create( - m_name.c_str(), - m_params, - m_aovs, - m_context.get_project().search_paths()); - - m_frame->post_processing_stages().swap(m_post_processing_stages); - } - - void start_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementAOVs: - { - AOVsElementHandler* aovs_handler = - static_cast(handler); - aovs_handler->set_aov_container(&m_aovs); - } - break; - - case ElementPostProcessingStages: - { - PostProcessingStagesElementHandler* post_processing_stages_handler = - static_cast(handler); - post_processing_stages_handler->set_post_processing_stage_container(&m_post_processing_stages); - } - break; - - default: - ParametrizedElementHandler::start_child_element(element, handler); - break; - } - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementAOVs: - // Nothing to do, AOVs were directly inserted into the project. - break; - - case ElementPostProcessingStages: - // Nothing to do, post-processing stages were directly inserted into the project. - break; - - default: - ParametrizedElementHandler::end_child_element(element, handler); - break; - } - } - - auto_release_ptr get_frame() - { - return m_frame; - } - - private: - ParseContext& m_context; - auto_release_ptr m_frame; - std::string m_name; - AOVContainer m_aovs; - PostProcessingStageContainer m_post_processing_stages; - }; - - - // - // element handler. - // - - class OutputElementHandler - : public ElementHandlerBaseType - { - public: - explicit OutputElementHandler(ParseContext& context) - : m_context(context) - { - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementFrame: - { - FrameElementHandler* frame_handler = - static_cast(handler); - m_context.get_project().set_frame(frame_handler->get_frame()); - } - break; - - assert_otherwise; - } - } - - private: - ParseContext& m_context; - }; - - - // - // element handler. - // - - class ConfigurationElementHandler - : public ParametrizedElementHandler - { - public: - explicit ConfigurationElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - ParametrizedElementHandler::start_element(attrs); - - m_configuration.reset(); - - m_name = get_value(attrs, "name"); - m_base_name = get_value(attrs, "base"); - } - - void end_element() override - { - ParametrizedElementHandler::end_element(); - - m_configuration = - ConfigurationFactory::create( - m_name.c_str(), - m_params); - - // Handle configuration inheritance. - if (!m_base_name.empty()) - { - const Configuration* base = - m_context.get_project().configurations().get_by_name(m_base_name.c_str()); - - if (base) - m_configuration->set_base(base); - else - { - RENDERER_LOG_ERROR( - "while defining configuration \"%s\": the configuration \"%s\" does not exist.", - m_configuration->get_path().c_str(), - m_base_name.c_str()); - m_context.get_event_counters().signal_error(); - } - } - } - - auto_release_ptr get_configuration() - { - return m_configuration; - } - - private: - ParseContext& m_context; - auto_release_ptr m_configuration; - std::string m_name; - std::string m_base_name; - }; - - - // - // element handler. - // - - class ConfigurationsElementHandler - : public ElementHandlerBaseType - { - public: - explicit ConfigurationsElementHandler(ParseContext& context) - : m_context(context) - { - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementConfiguration: - { - // Insert the configuration directly into the project. - ConfigurationElementHandler* config_handler = - static_cast(handler); - m_context.get_project().configurations().insert( - config_handler->get_configuration()); - } - break; - - assert_otherwise; - } - } - - private: - ParseContext& m_context; - }; - - - // - // element handler. - // - - class SearchPathElementHandler - : public ElementHandlerBaseType - { - public: - explicit SearchPathElementHandler(ParseContext& context) - { - } - - void characters( - const XMLCh* const chars, - const XMLSize_t length) override - { - m_path += transcode(chars); - } - - std::string get_path() const - { - return trim_both(m_path); - } - - private: - std::string m_path; - }; - - - // - // element handler. - // - - class SearchPathsElementHandler - : public ElementHandlerBaseType - { - public: - explicit SearchPathsElementHandler(ParseContext& context) - : m_context(context) - { - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementSearchPath: - { - // Skip search paths if asked to do so. - if (m_context.get_options() & ProjectFileReader::OmitSearchPaths) - return; - - SearchPathElementHandler* path_handler = - static_cast(handler); - const std::string& path = path_handler->get_path(); - if (!path.empty()) - m_context.get_project().search_paths().push_back_explicit_path(path); - } - break; - - assert_otherwise; - } - } - - private: - ParseContext& m_context; - }; - - - // - // element handler. - // - - class DisplayElementHandler - : public ParametrizedElementHandler - { - public: - explicit DisplayElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - ParametrizedElementHandler::start_element(attrs); - m_name = get_value(attrs, "name"); - } - - void end_element() override - { - ParametrizedElementHandler::end_element(); - m_context.get_project().set_display(DisplayFactory::create(m_name.c_str(), m_params)); - } - - private: - ParseContext& m_context; - std::string m_name; - }; - - - // - // element handler. - // - - class ProjectElementHandler - : public ElementHandlerBaseType - { - public: - explicit ProjectElementHandler(ParseContext& context) - : m_context(context) - { - } - - void start_element(const Attributes& attrs) override - { - ElementHandlerBaseType::start_element(attrs); - - const size_t format_revision = - from_string( - ElementHandlerBaseType::get_value(attrs, "format_revision", "2")); - - if (format_revision > ProjectFormatRevision) - { - RENDERER_LOG_WARNING( - "this project was created with a newer version of appleseed; it may fail to load or render properly with this version."); - m_context.get_event_counters().signal_warning(); - } - - m_context.get_project().set_format_revision(format_revision); - } - - void end_child_element( - const ProjectElementID element, - ElementHandlerType* handler) override - { - switch (element) - { - case ElementConfigurations: - // Nothing to do, configurations were directly inserted into the project. - break; - - case ElementOutput: - // Nothing to do, frames were directly inserted into the project. - break; - - case ElementScene: - { - SceneElementHandler* scene_handler = - static_cast(handler); - auto_release_ptr scene = scene_handler->get_scene(); - if (scene.get()) - m_context.get_project().set_scene(scene); - } - break; - - case ElementSearchPaths: - // Nothing to do, search paths were directly inserted into the project. - break; - - case ElementDisplay: - // Nothing to do, display was directly inserted into the project. - break; - - assert_otherwise; - } - } - - private: - ParseContext& m_context; - }; - - - // - // Content handler. - // - - class ContentHandler - : public SAX2ContentHandler - { - public: - ContentHandler(Project* project, ParseContext& context) - : m_context(context) - { - register_factory_helper("alpha", ElementAlpha); - register_factory_helper("aov", ElementAOV); - register_factory_helper("aovs", ElementAOVs); - register_factory_helper("assembly", ElementAssembly); - register_factory_helper("assembly_instance", ElementAssemblyInstance); - register_factory_helper("assign_material", ElementAssignMaterial); - register_factory_helper("bsdf", ElementBSDF); - register_factory_helper("bssrdf", ElementBSSRDF); - register_factory_helper("camera", ElementCamera); - register_factory_helper("color", ElementColor); - register_factory_helper("configuration", ElementConfiguration); - register_factory_helper("configurations", ElementConfigurations); - register_factory_helper("connect_shaders", ElementShaderConnection); - register_factory_helper("display", ElementDisplay); - register_factory_helper("edf", ElementEDF); - register_factory_helper("environment", ElementEnvironment); - register_factory_helper("environment_edf", ElementEnvironmentEDF); - register_factory_helper("environment_shader", ElementEnvironmentShader); - register_factory_helper("frame", ElementFrame); - register_factory_helper("light", ElementLight); - register_factory_helper("look_at", ElementLookAt); - register_factory_helper("material", ElementMaterial); - register_factory_helper("matrix", ElementMatrix); - register_factory_helper("object", ElementObject); - register_factory_helper("object_instance", ElementObjectInstance); - register_factory_helper("output", ElementOutput); - register_factory_helper("osl_code", ElementOSLCode); - register_factory_helper("parameter", ElementParameter); - register_factory_helper("parameters", ElementParameters); - register_factory_helper("post_processing_stage", ElementPostProcessingStage); - register_factory_helper("post_processing_stages", ElementPostProcessingStages); - register_factory_helper("rotation", ElementRotation); - register_factory_helper("scaling", ElementScaling); - register_factory_helper("scene", ElementScene); - register_factory_helper("search_path", ElementSearchPath); - register_factory_helper("search_paths", ElementSearchPaths); - register_factory_helper("shader", ElementShader); - register_factory_helper("shader_group", ElementShaderGroup); - register_factory_helper("surface_shader", ElementSurfaceShader); - register_factory_helper("texture", ElementTexture); - register_factory_helper("texture_instance", ElementTextureInstance); - register_factory_helper("transform", ElementTransform); - register_factory_helper("translation", ElementTranslation); - register_factory_helper("values", ElementValues); - register_factory_helper("volume", ElementVolume); - - std::unique_ptr> factory( - new ProjectElementHandlerFactory(m_context)); - - register_factory("project", ElementProject, std::move(factory)); - } - - private: - ParseContext& m_context; - - struct ProjectElementHandlerFactory - : public IElementHandlerFactory - { - ParseContext& m_context; - - explicit ProjectElementHandlerFactory(ParseContext& context) - : m_context(context) - { - } - - std::unique_ptr create() override - { - return std::unique_ptr(new ProjectElementHandler(m_context)); - } - }; - - template - struct GenericElementHandlerFactory - : public IElementHandlerFactory - { - ParseContext& m_context; - - explicit GenericElementHandlerFactory(ParseContext& context) - : m_context(context) - { - } +// Standard headers. +#include - std::unique_ptr create() override - { - return std::unique_ptr(new ElementHandler(m_context)); - } - }; +using namespace foundation; - template - void register_factory_helper(const std::string& name, const ProjectElementID id) - { - std::unique_ptr> factory( - new GenericElementHandlerFactory(m_context)); +namespace renderer +{ - register_factory(name, id, move(factory)); - } - }; -} +// +// ProjectFileReader class implementation. +// namespace { @@ -3140,30 +72,6 @@ namespace return false; } - - // Return the name of the single .appleseed file inside a given archive file. - // If there are zero or more than one .appleseed file inside the archive, an - // empty string is returned (i.e. the archive is not a valid packed project). - std::string get_project_filename_from_archive(const char* project_filepath) - { - const std::vector files = - get_filenames_with_extension_from_zip(project_filepath, ".appleseed"); - - return files.size() == 1 ? files[0] : std::string(); - } - - std::string unpack_project( - const std::string& project_filepath, - const std::string& project_name, - const bf::path& unpacked_project_directory) - { - if (bf::exists(unpacked_project_directory)) - bf::remove_all(unpacked_project_directory); - - unzip(project_filepath, unpacked_project_directory.string()); - - return (unpacked_project_directory / project_name).string().c_str(); - } } auto_release_ptr ProjectFileReader::read( @@ -3178,57 +86,17 @@ auto_release_ptr ProjectFileReader::read( if (is_builtin_project(project_filepath, project_name)) return load_builtin(project_name.c_str()); - // Handle packed projects. - std::string actual_project_filepath; - if (is_zip_file(project_filepath)) - { - const std::string project_filename = get_project_filename_from_archive(project_filepath); - if (project_filename.empty()) - { - RENDERER_LOG_ERROR( - "%s looks like a packed project file, but it should contain a single *.appleseed file in order to be valid.", - project_filepath); - return auto_release_ptr(nullptr); - } - - const std::string unpacked_project_directory = - bf::path(project_filepath).replace_extension(".unpacked").string(); - - RENDERER_LOG_INFO( - "%s appears to be a packed project; unpacking to %s...", - project_filepath, - unpacked_project_directory.c_str()); - - actual_project_filepath = - unpack_project( - project_filepath, - project_filename, - unpacked_project_directory); - - project_filepath = actual_project_filepath.data(); - } - - XercesCContext xerces_context(global_logger()); - if (!xerces_context.is_initialized()) - return auto_release_ptr(nullptr); - - if (!(options & OmitProjectSchemaValidation) && schema_filepath == nullptr) - { - RENDERER_LOG_ERROR( - "project schema validation enabled, but no schema filepath provided."); - return auto_release_ptr(nullptr); - } - Stopwatch stopwatch; stopwatch.start(); EventCounters event_counters; - auto_release_ptr project( - load_project_file( - project_filepath, - schema_filepath, - options, - event_counters)); + + auto_release_ptr project = + XMLProjectFileReader::read( + project_filepath, + schema_filepath, + options, + event_counters); if (project.get()) postprocess_project(project.ref(), event_counters, options); @@ -3252,53 +120,18 @@ auto_release_ptr ProjectFileReader::read_archive( { assert(archive_filepath); - // Handle packed archives. - std::string actual_archive_filepath; - if (is_zip_file(archive_filepath)) - { - const std::string archive_name = get_project_filename_from_archive(archive_filepath); - if (archive_name.empty()) - { - RENDERER_LOG_ERROR( - "%s looks like a packed archive file, but it should contain a single *.appleseed file in order to be valid.", - archive_filepath); - return auto_release_ptr(nullptr); - } - - const std::string unpacked_archive_directory = - bf::path(archive_filepath).replace_extension(".unpacked").string(); - - actual_archive_filepath = - unpack_project( - archive_filepath, - archive_name, - unpacked_archive_directory); - - archive_filepath = actual_archive_filepath.data(); - } - - XercesCContext xerces_context(global_logger()); - if (!xerces_context.is_initialized()) - return auto_release_ptr(nullptr); - - if (!(options & OmitProjectSchemaValidation) && schema_filepath == nullptr) - { - RENDERER_LOG_ERROR( - "archive schema validation enabled, but no schema filepath provided."); - return auto_release_ptr(nullptr); - } + EventCounters event_counters; Stopwatch stopwatch; stopwatch.start(); - EventCounters event_counters; - auto_release_ptr project( - load_project_file( + auto_release_ptr project = + XMLProjectFileReader::read_archive( archive_filepath, schema_filepath, + search_paths, options | OmitSearchPaths, - event_counters, - &search_paths)); + event_counters); if (project.get()) { @@ -3357,92 +190,9 @@ auto_release_ptr ProjectFileReader::load_builtin( return event_counters.has_errors() ? auto_release_ptr(nullptr) : project; } -auto_release_ptr ProjectFileReader::load_project_file( - const char* project_filepath, - const char* schema_filepath, - const int options, - EventCounters& event_counters, - const foundation::SearchPaths* search_paths) const -{ - // Create an empty project. - auto_release_ptr project(ProjectFactory::create(project_filepath)); - project->set_path(project_filepath); - - if (!(options & OmitSearchPaths)) - { - project->search_paths().set_root_path( - bf::absolute(project_filepath).parent_path().string()); - } - else - { - assert(search_paths); - project->search_paths() = *search_paths; - } - - // Create the error handler. - std::unique_ptr error_handler( - new ErrorLoggerAndCounter( - project_filepath, - event_counters)); - - // Create the content handler. - ParseContext context(project.ref(), options, event_counters); - std::unique_ptr content_handler( - new ContentHandler( - project.get(), - context)); - - // Create the parser. - std::unique_ptr parser(XMLReaderFactory::createXMLReader()); - parser->setFeature(XMLUni::fgSAX2CoreNameSpaces, true); // perform namespace processing - - if (!(options & OmitProjectSchemaValidation)) - { - assert(schema_filepath); - parser->setFeature(XMLUni::fgSAX2CoreValidation, true); // report all validation errors - parser->setFeature(XMLUni::fgXercesSchema, true); // enable the parser's schema support - parser->setProperty( - XMLUni::fgXercesSchemaExternalNoNameSpaceSchemaLocation, - const_cast( - static_cast( - transcode(schema_filepath).c_str()))); - } - else - { - parser->setFeature(XMLUni::fgSAX2CoreValidation, false); // ignore all validation errors - parser->setFeature(XMLUni::fgXercesSchema, false); // disable the parser's schema support - } - - parser->setErrorHandler(error_handler.get()); - parser->setContentHandler(content_handler.get()); - - // Load the project file. - RENDERER_LOG_INFO("loading project file %s...", project_filepath); - try - { - parser->parse(project_filepath); - } - catch (const XMLException&) - { - return auto_release_ptr(nullptr); - } - catch (const SAXParseException&) - { - return auto_release_ptr(nullptr); - } - - // Report a failure in case of warnings or errors. - if (error_handler->get_warning_count() > 0 || - error_handler->get_error_count() > 0 || - error_handler->get_fatal_error_count() > 0) - return auto_release_ptr(nullptr); - - return project; -} - auto_release_ptr ProjectFileReader::construct_builtin_project( const char* project_name, - EventCounters& event_counters) const + EventCounters& event_counters) { if (!strcmp(project_name, "cornell_box")) { @@ -3463,7 +213,7 @@ auto_release_ptr ProjectFileReader::construct_builtin_project( void ProjectFileReader::postprocess_project( Project& project, EventCounters& event_counters, - const int options) const + const int options) { if (!event_counters.has_errors()) validate_project(project, event_counters); @@ -3479,7 +229,7 @@ void ProjectFileReader::postprocess_project( void ProjectFileReader::validate_project( const Project& project, - EventCounters& event_counters) const + EventCounters& event_counters) { // Make sure the project contains a scene. if (project.get_scene()) @@ -3519,7 +269,7 @@ void ProjectFileReader::validate_project( void ProjectFileReader::complete_project( Project& project, - EventCounters& event_counters) const + EventCounters& event_counters) { // Add a default environment if the project doesn't define any. if (project.get_scene()->get_environment() == nullptr) @@ -3532,7 +282,7 @@ void ProjectFileReader::complete_project( void ProjectFileReader::upgrade_project( Project& project, - EventCounters& event_counters) const + EventCounters& event_counters) { ProjectFileUpdater updater; updater.update(project, event_counters); @@ -3542,7 +292,7 @@ void ProjectFileReader::print_loading_results( const char* project_name, const bool builtin_project, const EventCounters& event_counters, - const double loading_time) const + const double loading_time) { const size_t warning_count = event_counters.get_warning_count(); const size_t error_count = event_counters.get_error_count(); diff --git a/src/appleseed/renderer/modeling/project/projectfilereader.h b/src/appleseed/renderer/modeling/project/projectfilereader.h index c884c1db61..56134b08c6 100644 --- a/src/appleseed/renderer/modeling/project/projectfilereader.h +++ b/src/appleseed/renderer/modeling/project/projectfilereader.h @@ -31,15 +31,15 @@ // appleseed.foundation headers. #include "foundation/memory/autoreleaseptr.h" -#include "foundation/utility/searchpaths.h" // appleseed.main headers. #include "main/dllsymbol.h" // Forward declarations. -namespace renderer { class Assembly; } -namespace renderer { class EventCounters; } -namespace renderer { class Project; } +namespace foundation { class SearchPaths; } +namespace renderer { class Assembly; } +namespace renderer { class EventCounters; } +namespace renderer { class Project; } namespace renderer { @@ -62,62 +62,62 @@ class APPLESEED_DLLSYMBOL ProjectFileReader // Read a project from disk (or load a built-in project). // Return 0 if reading or parsing the file failed. - foundation::auto_release_ptr read( + static foundation::auto_release_ptr read( const char* project_filepath, const char* schema_filepath, const int options = Defaults); // Load a built-in project. // Return 0 if the requested built-in project does not exist. - foundation::auto_release_ptr load_builtin( + static foundation::auto_release_ptr load_builtin( const char* project_name); // Read an archive from disk. // Return 0 if reading or parsing the file failed. - foundation::auto_release_ptr read_archive( + static foundation::auto_release_ptr read_archive( const char* archive_filepath, const char* schema_filepath, const foundation::SearchPaths& search_paths, const int options = Defaults); private: - foundation::auto_release_ptr load_project_file( + static foundation::auto_release_ptr load_project_file( const char* project_filepath, const char* schema_filepath, const int options, EventCounters& event_counters, - const foundation::SearchPaths* search_paths = nullptr) const; + const foundation::SearchPaths* search_paths = nullptr); - foundation::auto_release_ptr construct_builtin_project( + static foundation::auto_release_ptr construct_builtin_project( const char* project_name, - EventCounters& event_counters) const; + EventCounters& event_counters); // Finish loading a project. - void postprocess_project( + static void postprocess_project( Project& project, EventCounters& event_counters, - const int options = Defaults) const; + const int options = Defaults); // Check the validity of a project. - void validate_project( + static void validate_project( const Project& project, - EventCounters& event_counters) const; + EventCounters& event_counters); // Add missing entities to a valid project. - void complete_project( + static void complete_project( Project& project, - EventCounters& event_counters) const; + EventCounters& event_counters); // Update a project to the latest project format revision. - void upgrade_project( + static void upgrade_project( Project& project, - EventCounters& event_counters) const; + EventCounters& event_counters); - void print_loading_results( + static void print_loading_results( const char* project_name, const bool builtin_project, const EventCounters& event_counters, - const double loading_time) const; + const double loading_time); }; } // namespace renderer diff --git a/src/appleseed/renderer/modeling/project/projectfilewriter.cpp b/src/appleseed/renderer/modeling/project/projectfilewriter.cpp index 246816b898..3949462936 100644 --- a/src/appleseed/renderer/modeling/project/projectfilewriter.cpp +++ b/src/appleseed/renderer/modeling/project/projectfilewriter.cpp @@ -31,74 +31,14 @@ #include "projectfilewriter.h" // appleseed.renderer headers. -#include "renderer/modeling/aov/aov.h" -#include "renderer/modeling/bsdf/bsdf.h" -#include "renderer/modeling/bssrdf/bssrdf.h" -#include "renderer/modeling/camera/camera.h" -#include "renderer/modeling/color/colorentity.h" -#include "renderer/modeling/display/display.h" -#include "renderer/modeling/edf/edf.h" -#include "renderer/modeling/environment/environment.h" -#include "renderer/modeling/environmentedf/environmentedf.h" -#include "renderer/modeling/environmentshader/environmentshader.h" -#include "renderer/modeling/frame/frame.h" -#include "renderer/modeling/light/light.h" -#include "renderer/modeling/material/material.h" -#include "renderer/modeling/object/curveobject.h" -#include "renderer/modeling/object/curveobjectwriter.h" -#include "renderer/modeling/object/meshobject.h" -#include "renderer/modeling/object/meshobjectwriter.h" -#include "renderer/modeling/object/object.h" -#include "renderer/modeling/postprocessingstage/postprocessingstage.h" -#include "renderer/modeling/project/assethandler.h" -#include "renderer/modeling/project/configuration.h" -#include "renderer/modeling/project/configurationcontainer.h" -#include "renderer/modeling/project/project.h" -#include "renderer/modeling/scene/assembly.h" -#include "renderer/modeling/scene/assemblyinstance.h" -#include "renderer/modeling/scene/containers.h" -#include "renderer/modeling/scene/objectinstance.h" -#include "renderer/modeling/scene/proceduralassembly.h" -#include "renderer/modeling/scene/scene.h" -#include "renderer/modeling/scene/textureinstance.h" -#include "renderer/modeling/shadergroup/shader.h" -#include "renderer/modeling/shadergroup/shaderconnection.h" -#include "renderer/modeling/shadergroup/shadergroup.h" -#include "renderer/modeling/shadergroup/shaderparam.h" -#include "renderer/modeling/surfaceshader/surfaceshader.h" -#include "renderer/modeling/texture/texture.h" -#include "renderer/modeling/volume/volume.h" -#include "renderer/utility/transformsequence.h" +#include "renderer/modeling/project/xmlprojectfilewriter.h" // appleseed.foundation headers. -#include "foundation/containers/dictionary.h" -#include "foundation/core/appleseed.h" -#include "foundation/math/transform.h" -#include "foundation/platform/defaulttimers.h" #include "foundation/string/string.h" -#include "foundation/utility/foreach.h" -#include "foundation/utility/indenter.h" -#include "foundation/utility/searchpaths.h" -#include "foundation/utility/stopwatch.h" -#include "foundation/utility/xmlelement.h" -#include "foundation/utility/zip.h" // Boost headers. #include "boost/filesystem.hpp" -// Standard headers. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace boost; using namespace foundation; namespace bf = boost::filesystem; @@ -109,965 +49,26 @@ namespace renderer // ProjectFileWriter class implementation. // -namespace -{ - // Floating-point formatting settings. - const char* MatrixFormat = "%.17f"; - const char* ColorValueFormat = "%.9f"; - - class Writer - { - public: - // Constructor. - Writer( - const Project& project, - const char* filepath, - FILE* file, - const int options) - : m_project_new_root_dir(filesystem::path(filepath).parent_path()) - , m_file(file) - , m_options(options) - , m_indenter(4) - { - } - - // Write the element. - void write_project(const Project& project) - { - XMLElement element("project", m_file, m_indenter); - element.add_attribute("format_revision", project.get_format_revision()); - element.write(XMLElement::HasChildElements); - - write_search_paths(project); - - if (project.get_display()) - write(*project.get_display()); - - if (project.get_scene()) - write_scene(*project.get_scene()); - - write_output(project); - write_configurations(project); - } - - private: - const filesystem::path m_project_new_root_dir; - FILE* m_file; - const int m_options; - Indenter m_indenter; - - // Return a lexicographically-sorted vector of references to entities. - template - std::vector> sorted(const Collection& collection) - { - return sorted_impl(collection.begin(), collection.end()); - } - template - std::vector> sorted(Collection& collection) - { - return sorted_impl(collection.begin(), collection.end()); - } - template - std::vector> sorted_impl(Iterator input_begin, Iterator input_end) - { - std::vector> result; - result.reserve(std::distance(input_begin, input_end)); - - for (Iterator it = input_begin; it != input_end; ++it) - result.emplace_back(*it); - - sort(result.begin(), result.end(), [](const Entity& lhs, const Entity& rhs) - { - return strcmp(lhs.get_name(), rhs.get_name()) < 0; - }); - - return result; - } - - // Write a vector of scalars. - template - void write_vector( - const Vec& v, - const size_t size, - const size_t columns, - const char* fmt) - { - assert(columns > 0); - - for (size_t i = 0; i < size; ++i) - { - const size_t col = i % columns; - - if (col == 0) - fputs(m_indenter.c_str(), m_file); - else fputc(' ', m_file); - - fprintf(m_file, fmt, v[i]); - - if (col == columns - 1 || i == size - 1) - fputc('\n', m_file); - } - } - - // Write a (possibly hierarchical) set of parameters. - void write_params(const Dictionary& params) - { - write_dictionary(params, m_file, m_indenter); - } - - // Write a element. - template - void write_transform(const Transform& transform) - { - if (transform.get_local_to_parent() == Matrix::identity()) - return; - - XMLElement element("transform", m_file, m_indenter); - element.write(XMLElement::HasChildElements); - - { - XMLElement child_element("matrix", m_file, m_indenter); - child_element.write(XMLElement::HasChildElements); - - write_vector( - transform.get_local_to_parent(), - 16, - 4, - MatrixFormat); - } - } - - // Write a transform sequence. - void write_transform_sequence(const TransformSequence& transform_sequence) - { - if (transform_sequence.size() == 1) - { - float time; - Transformd transform; - transform_sequence.get_transform(0, time, transform); - - if (transform.get_local_to_parent() == Matrix4d::identity()) - return; - } - - for (size_t i = 0, e = transform_sequence.size(); i < e; ++i) - { - float time; - Transformd transform; - transform_sequence.get_transform(i, time, transform); - - XMLElement element("transform", m_file, m_indenter); - element.add_attribute("time", time); - element.write(XMLElement::HasChildElements); - - { - XMLElement child_element("matrix", m_file, m_indenter); - child_element.write(XMLElement::HasChildElements); - - write_vector( - transform.get_local_to_parent(), - 16, - 4, - MatrixFormat); - } - } - } - - // Write an array of color values. - void write_value_array(const char* element_name, const ColorValueArray& values) - { - XMLElement element(element_name, m_file, m_indenter); - element.write(XMLElement::HasChildElements); - write_vector( - values, - values.size(), - 8, - ColorValueFormat); - } - - template - void write_entity(const char* element_name, const Entity& entity) - { - write_entity(element_name, entity, entity.get_name()); - } - - template - void write_entity(const char* element_name, const Entity& entity, const char* entity_name) - { - XMLElement element(element_name, m_file, m_indenter); - element.add_attribute("name", entity_name); - element.add_attribute("model", entity.get_model()); - element.write( - !entity.get_parameters().empty() - ? XMLElement::HasChildElements - : XMLElement::HasNoContent); - - write_params(entity.get_parameters()); - } - - template - void write_collection(const Collection& collection) - { - for (const auto& entity : sorted(collection)) - write(entity); - } - - // Write an element. - void write(const AOV& aov) - { - XMLElement element("aov", m_file, m_indenter); - element.add_attribute("model", aov.get_model()); - element.write( - !aov.get_parameters().empty() - ? XMLElement::HasChildElements - : XMLElement::HasNoContent); - - write_params(aov.get_parameters()); - } - - // Write an element. - void write_aovs(const Frame& frame) - { - const AOVContainer& aovs = frame.aovs(); - if (!aovs.empty()) - { - XMLElement element("aovs", m_file, m_indenter); - element.write(XMLElement::HasChildElements); - - write_collection(aovs); - } - } - - // Write an element. - void write(const Assembly& assembly) - { - XMLElement element("assembly", m_file, m_indenter); - element.add_attribute("name", assembly.get_name()); - - // Don't write the assembly model for normal assemblies - // to preserve compatibility with older appleseed versions. - if (strcmp(assembly.get_model(), AssemblyFactory().get_model()) != 0) - element.add_attribute("model", assembly.get_model()); - - // Don't write the content of the assembly if it was - // generated procedurally. - if (dynamic_cast(&assembly) != nullptr) - { - element.write( - !assembly.get_parameters().empty() - ? XMLElement::HasChildElements - : XMLElement::HasNoContent); - - write_params(assembly.get_parameters()); - } - else - { - element.write( - !assembly.get_parameters().empty() || - !assembly.colors().empty() || - !assembly.textures().empty() || - !assembly.texture_instances().empty() || - !assembly.bsdfs().empty() || - !assembly.bssrdfs().empty() || - !assembly.edfs().empty() || - !assembly.shader_groups().empty() || - !assembly.surface_shaders().empty() || - !assembly.materials().empty() || - !assembly.lights().empty() || - !assembly.objects().empty() || - !assembly.object_instances().empty() || - !assembly.volumes().empty() || - !assembly.assemblies().empty() || - !assembly.assembly_instances().empty() - ? XMLElement::HasChildElements - : XMLElement::HasNoContent); - - write_params(assembly.get_parameters()); - write_collection(assembly.colors()); - write_collection(assembly.textures()); - write_collection(assembly.texture_instances()); - write_collection(assembly.bsdfs()); - write_collection(assembly.bssrdfs()); - write_collection(assembly.edfs()); - write_collection(assembly.shader_groups()); - write_collection(assembly.surface_shaders()); - write_collection(assembly.materials()); - write_collection(assembly.lights()); - write_object_collection(assembly.objects()); - write_collection(assembly.object_instances()); - write_collection(assembly.volumes()); - write_collection(assembly.assemblies()); - write_collection(assembly.assembly_instances()); - } - } - - // Write an element. - void write(const AssemblyInstance& assembly_instance) - { - XMLElement element("assembly_instance", m_file, m_indenter); - element.add_attribute("name", assembly_instance.get_name()); - element.add_attribute("assembly", assembly_instance.get_assembly_name()); - element.write(XMLElement::HasChildElements); - - write_params(assembly_instance.get_parameters()); - write_transform_sequence(assembly_instance.transform_sequence()); - } - - // Write an element. - void write_assign_material( - const std::string& slot, - const std::string& side, - const std::string& name) - { - XMLElement element("assign_material", m_file, m_indenter); - element.add_attribute("slot", slot); - element.add_attribute("side", side); - element.add_attribute("material", name); - element.write(XMLElement::HasNoContent); - } - - // Write a series of elements. - void write_assign_materials( - const ObjectInstance::Side side, - const StringDictionary& material_mappings) - { - const std::string side_string = side == ObjectInstance::FrontSide ? "front" : "back"; - - for (const_each i = material_mappings; i; ++i) - write_assign_material(i->key(), side_string, i->value()); - } - - // Write a element. - void write(const BSDF& bsdf) - { - write_entity("bsdf", bsdf); - } - - // Write a element. - void write(const BSSRDF& bssrdf) - { - write_entity("bssrdf", bssrdf); - } - - // Write a element. - void write(const Camera& camera) - { - XMLElement element("camera", m_file, m_indenter); - element.add_attribute("name", camera.get_name()); - element.add_attribute("model", camera.get_model()); - element.write(XMLElement::HasChildElements); - - write_params(camera.get_parameters()); - write_transform_sequence(camera.transform_sequence()); - } - - // Write a element. - void write(const ColorEntity& color_entity) - { - XMLElement element("color", m_file, m_indenter); - element.add_attribute("name", color_entity.get_name()); - element.write(XMLElement::HasChildElements); - - write_params(color_entity.get_parameters()); - write_value_array("values", color_entity.get_values()); - write_value_array("alpha", color_entity.get_alpha()); - } - - // Write a element. - void write_configuration(const Configuration& configuration) - { - XMLElement element("configuration", m_file, m_indenter); - element.add_attribute("name", configuration.get_name()); - if (configuration.get_base()) - element.add_attribute("base", configuration.get_base()->get_name()); - element.write( - !configuration.get_parameters().empty() - ? XMLElement::HasChildElements - : XMLElement::HasNoContent); - - write_params(configuration.get_parameters()); - } - - size_t count_non_base_configurations(const ConfigurationContainer& configurations) - { - size_t count = 0; - - for (const Configuration& configuration : configurations) - { - if (!BaseConfigurationFactory::is_base_configuration(configuration.get_name())) - ++count; - } - - return count; - } - - // Write a element. - void write_configurations(const Project& project) - { - XMLElement element("configurations", m_file, m_indenter); - element.write( - count_non_base_configurations(project.configurations()) > 0 - ? XMLElement::HasChildElements - : XMLElement::HasNoContent); - - // Write configurations. - for (const Configuration& configuration : sorted(project.configurations())) - { - if (!BaseConfigurationFactory::is_base_configuration(configuration.get_name())) - write_configuration(configuration); - } - } - - // Write a element. - void write(const Display& display) - { - XMLElement element("display", m_file, m_indenter); - element.add_attribute("name", display.get_name()); - element.write( - !display.get_parameters().empty() - ? XMLElement::HasChildElements - : XMLElement::HasNoContent); - - write_params(display.get_parameters()); - } - - // Write an element. - void write(const EDF& edf) - { - write_entity("edf", edf); - } - - // Write an element. - void write(const Environment& environment) - { - write_entity("environment", environment); - } - - // Write an element. - void write(const EnvironmentEDF& env_edf) - { - XMLElement element("environment_edf", m_file, m_indenter); - element.add_attribute("name", env_edf.get_name()); - element.add_attribute("model", env_edf.get_model()); - element.write(XMLElement::HasChildElements); - - write_params(env_edf.get_parameters()); - write_transform_sequence(env_edf.transform_sequence()); - } - - // Write an element. - void write(const EnvironmentShader& env_shader) - { - write_entity("environment_shader", env_shader); - } - - // Write a element. - void write_frame(const Frame& frame) - { - XMLElement element("frame", m_file, m_indenter); - element.add_attribute("name", frame.get_name()); - element.write( - !frame.get_parameters().empty() || - !frame.aovs().empty() || - !frame.post_processing_stages().empty() - ? XMLElement::HasChildElements - : XMLElement::HasNoContent); - - write_params(frame.get_parameters()); - write_aovs(frame); - write_post_processing_stages(frame); - } - - // Write a element. - void write(const Light& light) - { - XMLElement element("light", m_file, m_indenter); - element.add_attribute("name", light.get_name()); - element.add_attribute("model", light.get_model()); - element.write(XMLElement::HasChildElements); - - write_params(light.get_parameters()); - write_transform(light.get_transform()); - } - - // Write a element. - void write(const Material& material) - { - write_entity("material", material); - } - - // Write a element. - void write(const Volume& material) - { - write_entity("volume", material); - } - - // Write a collection of elements. - void write_object_collection(ObjectContainer& objects) - { - std::set groups; - - for (Object& object : sorted(objects)) - { - if (strcmp(object.get_model(), MeshObjectFactory().get_model()) == 0) - write_mesh_object(static_cast(object), groups); - else if (strcmp(object.get_model(), CurveObjectFactory().get_model()) == 0) - write_curve_object(static_cast(object)); - else write(object); - } - } - - // Write a mesh object. - void write_mesh_object(MeshObject& object, std::set& groups) - { - ParamArray& params = object.get_parameters(); - - // If the object is a mesh primitive, do not write geometry to disk. - if (params.strings().exist("primitive")) - { - XMLElement element("object", m_file, m_indenter); - element.add_attribute("name", object.get_name()); - element.add_attribute("model", MeshObjectFactory().get_model()); - element.write(XMLElement::HasChildElements); - write_params(params); - return; - } - - if (params.strings().exist("__base_object_name")) - { - // This object belongs to a group of objects. - const std::string group_name = params.get("__base_object_name"); - if (groups.find(group_name) == groups.end()) - { - // This is the first time we encounter this group of objects. - groups.insert(group_name); - - // Write the object group. - params.strings().remove("__base_object_name"); - write_entity("object", object, group_name.c_str()); - params.strings().insert("__base_object_name", group_name); - } - } - else if (params.strings().exist("filename") || params.dictionaries().exist("filename")) - { - // This object has a filename parameter. - write_entity("object", object, object.get_name()); - } - else - { - // This object does not belong to a group and does not have a filename parameter. - write_orphan_mesh_object(object); - } - } - - // Object name mapping established by do_write_orphan_mesh_object(). - typedef std::map ObjectNameMapping; - ObjectNameMapping m_object_name_mapping; - - // Get the new name of an object, given its old name. - std::string translate_object_name(const std::string& old_name) const - { - const ObjectNameMapping::const_iterator i = m_object_name_mapping.find(old_name); - return i == m_object_name_mapping.end() ? old_name : i->second; - } - - // Write an element for a mesh object without a filename. - void write_orphan_mesh_object(const MeshObject& object) - { - // Construct the name of the mesh file. - const std::string object_name = object.get_name(); - const std::string filename = object_name + ".binarymesh"; - - if (!(m_options & ProjectFileWriter::OmitWritingGeometryFiles)) - { - // Write the mesh file to disk. - const std::string filepath = (m_project_new_root_dir / filename).string(); - MeshObjectWriter::write(object, object_name.c_str(), filepath.c_str()); - } - - // Write the element. - XMLElement element("object", m_file, m_indenter); - element.add_attribute("name", object_name); - element.add_attribute("model", MeshObjectFactory().get_model()); - element.write(XMLElement::HasChildElements); - - // Output a "filename" parameter but don't add it to the object. - ParamArray params = object.get_parameters(); - params.insert("filename", filename); - write_params(params); - - // Update the object name mapping. - m_object_name_mapping[object_name] = object_name + "." + object_name; - } - - // Write a curve object. - void write_curve_object(CurveObject& object) - { - ParamArray& params = object.get_parameters(); - - if (!params.strings().exist("filepath")) - { - const std::string object_name = object.get_name(); - const std::string filename = object_name + ".binarycurve"; - - if (!(m_options & ProjectFileWriter::OmitWritingGeometryFiles)) - { - // Write the curve file to disk. - const std::string filepath = (m_project_new_root_dir / filename).string(); - CurveObjectWriter::write(object, filepath.c_str()); - } - - // Add a file path parameter to the object. - params.insert("filepath", filename); - } - - // Write the element. - write_entity("object", object); - } - - // Write an element. - void write(const Object& object) - { - write_entity("object", object); - } - - // Write an element. - void write(const ObjectInstance& object_instance) - { - XMLElement element("object_instance", m_file, m_indenter); - element.add_attribute("name", object_instance.get_name()); - element.add_attribute("object", translate_object_name(object_instance.get_object_name())); - element.write(XMLElement::HasChildElements); - - write_params(object_instance.get_parameters()); - write_transform(object_instance.get_transform()); - write_assign_materials(ObjectInstance::FrontSide, object_instance.get_front_material_mappings()); - write_assign_materials(ObjectInstance::BackSide, object_instance.get_back_material_mappings()); - } - - // Write an element. - void write_output(const Project& project) - { - XMLElement element("output", m_file, m_indenter); - element.write( - project.get_frame() != nullptr - ? XMLElement::HasChildElements - : XMLElement::HasNoContent); - - if (project.get_frame()) - write_frame(*project.get_frame()); - } - - // Write a element. - void write(const PostProcessingStage& stage) - { - write_entity("post_processing_stage", stage); - } - - // Write an element. - void write_post_processing_stages(const Frame& frame) - { - const PostProcessingStageContainer& stages = frame.post_processing_stages(); - if (!stages.empty()) - { - XMLElement element("post_processing_stages", m_file, m_indenter); - element.write(XMLElement::HasChildElements); - - write_collection(stages); - } - } - - // Write a element. - void write_scene(const Scene& scene) - { - XMLElement element("scene", m_file, m_indenter); - element.write( - !scene.cameras().empty() || - !scene.colors().empty() || - !scene.textures().empty() || - !scene.texture_instances().empty() || - !scene.environment_edfs().empty() || - !scene.environment_shaders().empty() || - scene.get_environment() != nullptr || - !scene.shader_groups().empty() || - !scene.assemblies().empty() || - !scene.assembly_instances().empty() - ? XMLElement::HasChildElements - : XMLElement::HasNoContent); - - write_params(scene.get_parameters()); - write_collection(scene.cameras()); - write_collection(scene.colors()); - write_collection(scene.textures()); - write_collection(scene.texture_instances()); - write_collection(scene.environment_edfs()); - write_collection(scene.environment_shaders()); - if (scene.get_environment()) - write(*scene.get_environment()); - write_collection(scene.shader_groups()); - write_collection(scene.assemblies()); - write_collection(scene.assembly_instances()); - } - - // Write a element. - void write_search_path(const char* search_path) - { - XMLElement element("search_path", m_file, m_indenter); - element.write(XMLElement::HasChildElements); - - fprintf(m_file, "%s%s\n", m_indenter.c_str(), search_path); - } - - // Write a element. - void write_search_paths(const Project& project) - { - const SearchPaths& search_paths = project.search_paths(); - - if (search_paths.get_explicit_path_count() > 0) - { - XMLElement element("search_paths", m_file, m_indenter); - element.write(XMLElement::HasChildElements); - - for (size_t i = 0; i < search_paths.get_explicit_path_count(); ++i) - write_search_path(search_paths.get_explicit_path(i)); - } - } - - // Write a shader's element. - void write(const ShaderParam& param) - { - XMLElement element("parameter", m_file, m_indenter); - element.add_attribute("name", param.get_name()); - element.add_attribute("value", param.get_value_as_string()); - element.write(XMLElement::HasNoContent); - } - - // Write an element. - void write_osl_code(const char* code) - { - XMLElement element("osl_code", m_file, m_indenter); - element.write(XMLElement::HasChildElements); - - fprintf(m_file, "%s\n", replace_special_xml_characters(code).c_str()); - } - - // Write a element. - void write(const Shader& shader) - { - XMLElement element("shader", m_file, m_indenter); - element.add_attribute("type", shader.get_type()); - element.add_attribute("name", shader.get_shader()); - element.add_attribute("layer", shader.get_layer()); - element.write(XMLElement::HasChildElements); - - write_collection(shader.shader_params()); - - if (shader.get_source_code()) - write_osl_code(shader.get_source_code()); - } - - // Write a element. - void write(const ShaderConnection& connection) - { - XMLElement element("connect_shaders", m_file, m_indenter); - element.add_attribute("src_layer", connection.get_src_layer()); - element.add_attribute("src_param", connection.get_src_param()); - element.add_attribute("dst_layer", connection.get_dst_layer()); - element.add_attribute("dst_param", connection.get_dst_param()); - element.write(XMLElement::HasNoContent); - } - - // Write a element. - void write(const ShaderGroup& shader_group) - { - XMLElement element("shader_group", m_file, m_indenter); - element.add_attribute("name", shader_group.get_name()); - element.write(XMLElement::HasChildElements); - - // Write shaders in the original order. - for (const Shader& shader : shader_group.shaders()) - write(shader); - - // Write shader connections in the original order. - for (const ShaderConnection& shader_connection : shader_group.shader_connections()) - write(shader_connection); - } - - // Write a element. - void write(const SurfaceShader& surface_shader) - { - write_entity("surface_shader", surface_shader); - } - - // Write a element. - void write(const Texture& texture) - { - write_entity("texture", texture); - } - - // Write a element. - void write(const TextureInstance& texture_instance) - { - XMLElement element("texture_instance", m_file, m_indenter); - element.add_attribute("name", texture_instance.get_name()); - element.add_attribute("texture", texture_instance.get_texture_name()); - element.write(XMLElement::HasChildElements); - - write_params(texture_instance.get_parameters()); - write_transform(texture_instance.get_transform()); - } - }; -} - bool ProjectFileWriter::write( Project& project, const char* filepath, const int options, const char* extra_comments) { - return - lower_case(bf::path(filepath).extension().string()) == ".appleseedz" - ? write_packed_project_file(project, filepath, options, extra_comments) - : write_plain_project_file(project, filepath, options, extra_comments); -} - -bool ProjectFileWriter::write_plain_project_file( - Project& project, - const char* filepath, - const int options, - const char* extra_comments) -{ - Stopwatch stopwatch; - stopwatch.start(); + const auto ext = lower_case(bf::path(filepath).extension().string()); - RENDERER_LOG_INFO("writing project file %s...", filepath); - - if (!(options & OmitHandlingAssetFiles)) - { - // Manage references to external asset files. - const AssetHandler asset_handler( + if (ext == ".appleseedz") + return XMLProjectFileWriter::write_packed_project_file( project, filepath, - (options & CopyAllAssets) != 0 - ? AssetHandler::CopyAllAssets - : AssetHandler::CopyRelativeAssetsOnly); - if (!asset_handler.handle_assets()) - { - RENDERER_LOG_ERROR("failed to write project file %s.", filepath); - return false; - } - } - - // Open the file for writing. - FILE* file = fopen(filepath, "wt"); - if (file == nullptr) - { - RENDERER_LOG_ERROR("failed to write project file %s: i/o error.", filepath); - return false; - } - - // Write the file header. - fprintf(file, "\n"); - - // Write an optional header comment. - if (!(options & ProjectFileWriter::OmitHeaderComment)) - { - fprintf( - file, - "\n", - Appleseed::get_synthetic_version_string()); - - // Write any optional comments. - if (extra_comments) - { - std::vector lines; - split(extra_comments, "\n", lines); + options, + extra_comments); - for (const auto& line : lines) - { - if (!line.empty()) - fprintf(file, "\n", line.c_str()); - } - } - } - - // Write the project. - Writer writer(project, filepath, file, options); - writer.write_project(project); - - // Close the file. - fclose(file); - - stopwatch.measure(); - - RENDERER_LOG_INFO( - "wrote project file %s in %s.", + return XMLProjectFileWriter::write_plain_project_file( + project, filepath, - pretty_time(stopwatch.get_seconds()).c_str()); - - return true; -} - -bool ProjectFileWriter::write_packed_project_file( - Project& project, - const char* filepath, - const int options, - const char* extra_comments) -{ - const bf::path project_path(filepath); - - const bf::path temp_directory = - project_path.parent_path() / - project_path.filename().replace_extension(".unpacked.temp"); - - const bf::path temp_project_filepath = - temp_directory / - project_path.filename().replace_extension(".appleseed"); - - if (!bf::create_directory(temp_directory)) - { - RENDERER_LOG_ERROR("failed to create directory %s", temp_directory.string().c_str()); - return false; - } - - bool success = true; - - try - { - success = - write_plain_project_file( - project, - temp_project_filepath.string().c_str(), - options | ProjectFileWriter::CopyAllAssets, - extra_comments); - - if (success) - { - Stopwatch stopwatch; - stopwatch.start(); - - RENDERER_LOG_INFO("packing project to %s...", filepath); - - zip(filepath, temp_directory.string()); - - stopwatch.measure(); - - RENDERER_LOG_INFO( - "packed project to %s in %s.", - filepath, - pretty_time(stopwatch.get_seconds()).c_str()); - } - } - catch (const std::exception&) // namespace qualification required - { - RENDERER_LOG_ERROR("failed to write project file %s.", filepath); - success = false; - } - - if (bf::exists(temp_directory)) - bf::remove_all(temp_directory); - - return success; + options, + extra_comments); } } // namespace renderer diff --git a/src/appleseed/renderer/modeling/project/projectfilewriter.h b/src/appleseed/renderer/modeling/project/projectfilewriter.h index e3d708449e..a47c397d51 100644 --- a/src/appleseed/renderer/modeling/project/projectfilewriter.h +++ b/src/appleseed/renderer/modeling/project/projectfilewriter.h @@ -61,23 +61,6 @@ class APPLESEED_DLLSYMBOL ProjectFileWriter const char* filepath, const int options = Defaults, const char* extra_comments = nullptr); - - private: - // Write a project to disk as a plain project file. - // Returns true on success, false otherwise. - static bool write_plain_project_file( - Project& project, - const char* filepath, - const int options, - const char* comments); - - // Write a project file to disk as a packed project file. - // Returns true on success, false otherwise. - static bool write_packed_project_file( - Project& project, - const char* filepath, - const int options, - const char* extra_comments); }; } // namespace renderer diff --git a/src/appleseed/renderer/modeling/project/xmlprojectfilereader.cpp b/src/appleseed/renderer/modeling/project/xmlprojectfilereader.cpp new file mode 100644 index 0000000000..a6df83e96e --- /dev/null +++ b/src/appleseed/renderer/modeling/project/xmlprojectfilereader.cpp @@ -0,0 +1,3344 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited +// Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// Interface header. +#include "xmlprojectfilereader.h" + +// appleseed.renderer headers. +#include "renderer/global/globallogger.h" +#include "renderer/global/globaltypes.h" +#include "renderer/modeling/aov/aov.h" +#include "renderer/modeling/aov/aovfactoryregistrar.h" +#include "renderer/modeling/aov/iaovfactory.h" +#include "renderer/modeling/bsdf/bsdf.h" +#include "renderer/modeling/bsdf/bsdffactoryregistrar.h" +#include "renderer/modeling/bsdf/ibsdffactory.h" +#include "renderer/modeling/bssrdf/bssrdf.h" +#include "renderer/modeling/bssrdf/bssrdffactoryregistrar.h" +#include "renderer/modeling/bssrdf/ibssrdffactory.h" +#include "renderer/modeling/camera/camera.h" +#include "renderer/modeling/camera/camerafactoryregistrar.h" +#include "renderer/modeling/camera/icamerafactory.h" +#include "renderer/modeling/color/colorentity.h" +#include "renderer/modeling/display/display.h" +#include "renderer/modeling/edf/edf.h" +#include "renderer/modeling/edf/edffactoryregistrar.h" +#include "renderer/modeling/edf/iedffactory.h" +#include "renderer/modeling/environment/environment.h" +#include "renderer/modeling/environmentedf/environmentedf.h" +#include "renderer/modeling/environmentedf/environmentedffactoryregistrar.h" +#include "renderer/modeling/environmentedf/ienvironmentedffactory.h" +#include "renderer/modeling/environmentshader/environmentshader.h" +#include "renderer/modeling/environmentshader/environmentshaderfactoryregistrar.h" +#include "renderer/modeling/environmentshader/ienvironmentshaderfactory.h" +#include "renderer/modeling/frame/frame.h" +#include "renderer/modeling/light/ilightfactory.h" +#include "renderer/modeling/light/light.h" +#include "renderer/modeling/light/lightfactoryregistrar.h" +#include "renderer/modeling/material/imaterialfactory.h" +#include "renderer/modeling/material/material.h" +#include "renderer/modeling/material/materialfactoryregistrar.h" +#include "renderer/modeling/object/iobjectfactory.h" +#include "renderer/modeling/object/object.h" +#include "renderer/modeling/object/objectfactoryregistrar.h" +#include "renderer/modeling/postprocessingstage/ipostprocessingstagefactory.h" +#include "renderer/modeling/postprocessingstage/postprocessingstage.h" +#include "renderer/modeling/postprocessingstage/postprocessingstagefactoryregistrar.h" +#include "renderer/modeling/project/configuration.h" +#include "renderer/modeling/project/configurationcontainer.h" +#include "renderer/modeling/project/eventcounters.h" +#include "renderer/modeling/project/project.h" +#include "renderer/modeling/project/projectfilereader.h" +#include "renderer/modeling/project/projectformatrevision.h" +#include "renderer/modeling/scene/assembly.h" +#include "renderer/modeling/scene/assemblyfactoryregistrar.h" +#include "renderer/modeling/scene/assemblyinstance.h" +#include "renderer/modeling/scene/containers.h" +#include "renderer/modeling/scene/iassemblyfactory.h" +#include "renderer/modeling/scene/objectinstance.h" +#include "renderer/modeling/scene/scene.h" +#include "renderer/modeling/scene/textureinstance.h" +#include "renderer/modeling/shadergroup/shadergroup.h" +#include "renderer/modeling/surfaceshader/isurfaceshaderfactory.h" +#include "renderer/modeling/surfaceshader/surfaceshader.h" +#include "renderer/modeling/surfaceshader/surfaceshaderfactoryregistrar.h" +#include "renderer/modeling/texture/itexturefactory.h" +#include "renderer/modeling/texture/texture.h" +#include "renderer/modeling/texture/texturefactoryregistrar.h" +#include "renderer/modeling/volume/ivolumefactory.h" +#include "renderer/modeling/volume/volume.h" +#include "renderer/modeling/volume/volumefactoryregistrar.h" +#include "renderer/utility/paramarray.h" +#include "renderer/utility/pluginstore.h" +#include "renderer/utility/transformsequence.h" + +// appleseed.foundation headers. +#include "foundation/containers/dictionary.h" +#include "foundation/core/exceptions/exceptionunsupportedfileformat.h" +#include "foundation/log/log.h" +#include "foundation/math/aabb.h" +#include "foundation/math/matrix.h" +#include "foundation/math/scalar.h" +#include "foundation/math/transform.h" +#include "foundation/math/vector.h" +#include "foundation/memory/memory.h" +#include "foundation/platform/compiler.h" +#include "foundation/platform/defaulttimers.h" +#include "foundation/platform/types.h" +#include "foundation/string/string.h" +#include "foundation/utility/api/apiarray.h" +#include "foundation/utility/api/apistring.h" +#include "foundation/utility/foreach.h" +#include "foundation/utility/iterators.h" +#include "foundation/utility/otherwise.h" +#include "foundation/utility/searchpaths.h" +#include "foundation/utility/stopwatch.h" +#include "foundation/utility/xercesc.h" +#include "foundation/utility/zip.h" + +// Xerces-C++ headers. +#include "xercesc/sax2/Attributes.hpp" +#include "xercesc/sax2/SAX2XMLReader.hpp" +#include "xercesc/sax2/XMLReaderFactory.hpp" +#include "xercesc/util/XMLException.hpp" +#include "xercesc/util/XMLUni.hpp" + +// Boost headers. +#include "boost/filesystem.hpp" +#include "boost/filesystem/operations.hpp" + +// Standard headers. +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace foundation; +using namespace xercesc; +namespace bf = boost::filesystem; + +namespace renderer +{ + +// +// XMLProjectFileReader class implementation. +// + +namespace +{ + // + // Like foundation::ErrorHandler, but additionally keeps track + // of the number of warnings and errors that were emitted. + // + + class ErrorLoggerAndCounter + : public ErrorLogger + { + public: + ErrorLoggerAndCounter( + const std::string& input_filepath, + EventCounters& event_counters) + : ErrorLogger(global_logger(), input_filepath) + , m_event_counters(event_counters) + { + } + + void resetErrors() override + { + m_event_counters.clear(); + } + + void warning(const SAXParseException& e) override + { + ErrorLogger::warning(e); + m_event_counters.signal_warning(); + } + + void error(const SAXParseException& e) override + { + ErrorLogger::error(e); + m_event_counters.signal_error(); + throw e; // terminate parsing + } + + void fatalError(const SAXParseException& e) override + { + ErrorLogger::fatalError(e); + m_event_counters.signal_error(); + throw e; // terminate parsing + } + + private: + EventCounters& m_event_counters; + }; + + + // + // A set of objects that is passed to all element handlers. + // + + class ParseContext + { + public: + ParseContext( + Project& project, + const int options, + EventCounters& event_counters) + : m_project(project) + , m_options(options) + , m_event_counters(event_counters) + { + } + + Project& get_project() + { + return m_project; + } + + int get_options() const + { + return m_options; + } + + EventCounters& get_event_counters() + { + return m_event_counters; + } + + private: + Project& m_project; + const int m_options; + EventCounters& m_event_counters; + }; + + + // + // Utility functions. + // + + template + T get_scalar( + const std::string& text, + ParseContext& context) + { + try + { + return from_string(text); + } + catch (const ExceptionStringConversionError&) + { + RENDERER_LOG_ERROR("expected scalar value, got \"%s\".", text.c_str()); + context.get_event_counters().signal_error(); + return T(0.0); + } + } + + Vector3d get_vector3( + const std::string& text, + ParseContext& context) + { + Vector3d vec; + bool succeeded = false; + + try + { + const size_t count = tokenize(text, Blanks, &vec[0], 3); + if (count == 1) + { + vec[1] = vec[0]; + vec[2] = vec[0]; + succeeded = true; + } + else if (count == 3) + { + succeeded = true; + } + } + catch (const ExceptionStringConversionError&) + { + } + + if (!succeeded) + { + RENDERER_LOG_ERROR("invalid vector format."); + context.get_event_counters().signal_error(); + vec = Vector3d(0.0); + } + + return vec; + } + + template + void get_vector( + const std::string& text, + Vec& values, + ParseContext& context) + { + try + { + tokenize(text, Blanks, values); + } + catch (const ExceptionStringConversionError&) + { + RENDERER_LOG_ERROR("invalid vector format."); + context.get_event_counters().signal_error(); + values.clear(); + } + } + + template + auto_release_ptr create_entity( + const EntityFactoryRegistrar& registrar, + const std::string& type, + const std::string& model, + const std::string& name, + const ParamArray& params, + ParseContext& context) + { + try + { + const typename EntityFactoryRegistrar::FactoryType* factory = + registrar.lookup(model.c_str()); + + if (factory) + { + return factory->create(name.c_str(), params); + } + else + { + RENDERER_LOG_ERROR( + "while defining %s \"%s\": invalid model \"%s\".", + type.c_str(), + name.c_str(), + model.c_str()); + context.get_event_counters().signal_error(); + } + } + catch (const ExceptionDictionaryKeyNotFound& e) + { + RENDERER_LOG_ERROR( + "while defining %s \"%s\": required parameter \"%s\" missing.", + type.c_str(), + name.c_str(), + e.string()); + context.get_event_counters().signal_error(); + } + catch (const ExceptionUnknownEntity& e) + { + RENDERER_LOG_ERROR( + "while defining %s \"%s\": unknown entity \"%s\".", + type.c_str(), + name.c_str(), + e.string()); + context.get_event_counters().signal_error(); + } + + return auto_release_ptr(nullptr); + } + + + // + // Numeric representation of the XML elements. + // + + enum ProjectElementID + { + ElementAlpha, + ElementAOV, + ElementAOVs, + ElementAssembly, + ElementAssemblyInstance, + ElementAssignMaterial, + ElementBSDF, + ElementBSSRDF, + ElementCamera, + ElementColor, + ElementConfiguration, + ElementConfigurations, + ElementDisplay, + ElementEDF, + ElementEnvironment, + ElementEnvironmentEDF, + ElementEnvironmentShader, + ElementFrame, + ElementLight, + ElementLookAt, + ElementMaterial, + ElementMatrix, + ElementObject, + ElementObjectInstance, + ElementOSLCode, + ElementOutput, + ElementParameter, + ElementParameters, + ElementPostProcessingStage, + ElementPostProcessingStages, + ElementProject, + ElementRotation, + ElementScaling, + ElementScene, + ElementSearchPath, + ElementSearchPaths, + ElementShader, + ElementShaderConnection, + ElementShaderGroup, + ElementSurfaceShader, + ElementTexture, + ElementTextureInstance, + ElementTransform, + ElementTranslation, + ElementValues, + ElementVolume + }; + + typedef IElementHandler ElementHandlerType; + typedef ElementHandlerBase ElementHandlerBaseType; + + + // + // element handler. + // + + class ParameterElementHandler + : public ElementHandlerBaseType + { + public: + explicit ParameterElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + m_name = ElementHandlerBaseType::get_value(attrs, "name"); + m_value = ElementHandlerBaseType::get_value(attrs, "value"); + } + + void characters( + const XMLCh* const chars, + const XMLSize_t length) override + { + const std::string inner_value = transcode(chars); + if (!m_value.empty() && !inner_value.empty()) + { + RENDERER_LOG_ERROR( + "while defining element: value specified multiple times."); + m_context.get_event_counters().signal_error(); + } + else + { + m_value = inner_value; + } + } + + const std::string& get_name() const + { + return m_name; + } + + const std::string& get_value() const + { + return m_value; + } + + private: + ParseContext& m_context; + std::string m_name; + std::string m_value; + }; + + + // + // Handle an element containing a (hierarchical) set of parameters. + // + + class ParametrizedElementHandler + : public ElementHandlerBaseType + { + public: + void start_element(const Attributes& attrs) override; + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override; + + protected: + ParamArray m_params; + }; + + + // + // element handler. + // + + class ParametersElementHandler + : public ParametrizedElementHandler + { + public: + explicit ParametersElementHandler(ParseContext& context) + { + } + + void start_element(const Attributes& attrs) override + { + ParametrizedElementHandler::start_element(attrs); + + m_params.clear(); + + m_name = get_value(attrs, "name"); + } + + const std::string& get_name() const + { + return m_name; + } + + const ParamArray& get_parameters() const + { + return m_params; + } + + private: + std::string m_name; + }; + + + // + // ParametrizedElementHandler class implementation. + // + + void ParametrizedElementHandler::start_element(const Attributes& attrs) + { + m_params.clear(); + } + + void ParametrizedElementHandler::end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) + { + switch (element) + { + case ElementParameter: + { + ParameterElementHandler* param_handler = + static_cast(handler); + m_params.insert_path( + param_handler->get_name().c_str(), + param_handler->get_value()); + } + break; + + case ElementParameters: + { + ParametersElementHandler* params_handler = + static_cast(handler); + m_params.dictionaries().insert( + params_handler->get_name().c_str(), + params_handler->get_parameters()); + } + break; + + assert_otherwise; + } + } + + + // + // element handler. + // + + class LookAtElementHandler + : public ElementHandlerBaseType + { + public: + explicit LookAtElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + m_matrix = Matrix4d::identity(); + + const Vector3d origin = get_vector3(get_value(attrs, "origin"), m_context); + const Vector3d target = get_vector3(get_value(attrs, "target"), m_context); + const Vector3d up = get_vector3(get_value(attrs, "up"), m_context); + + if (norm(origin - target) > 0.0 && + norm(up) > 0.0 && + norm(cross(up, origin - target)) > 0.0) + { + m_matrix = Matrix4d::make_lookat(origin, target, normalize(up)); + } + else + { + RENDERER_LOG_ERROR( + "while defining element: the vectors\n" + " origin (%f, %f, %f)\n" + " target (%f, %f, %f)\n" + " up (%f, %f, %f)\n" + "form a singular transformation matrix.", + origin[0], origin[1], origin[2], + target[0], target[1], target[2], + up[0], up[1], up[2]); + m_context.get_event_counters().signal_error(); + } + } + + const Matrix4d& get_matrix() const + { + return m_matrix; + } + + private: + ParseContext& m_context; + Matrix4d m_matrix; + }; + + + // + // element handler. + // + + class MatrixElementHandler + : public ElementHandlerBaseType + { + public: + explicit MatrixElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + m_matrix = Matrix4d::identity(); + clear_keep_memory(m_values); + } + + void end_element() override + { + if (m_values.size() == 16) + { + for (size_t i = 0; i < 16; ++i) + m_matrix[i] = m_values[i]; + } + else + { + RENDERER_LOG_ERROR( + "while defining element: expected 16 scalar coefficients, got " FMT_SIZE_T ".", + m_values.size()); + m_context.get_event_counters().signal_error(); + } + } + + void characters( + const XMLCh* const chars, + const XMLSize_t length) override + { + get_vector(transcode(chars), m_values, m_context); + } + + const Matrix4d& get_matrix() const + { + return m_matrix; + } + + private: + ParseContext& m_context; + Matrix4d m_matrix; + std::vector m_values; + }; + + + // + // element handler. + // + + class RotationElementHandler + : public ElementHandlerBaseType + { + public: + explicit RotationElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + m_matrix = Matrix4d::identity(); + + const Vector3d axis = get_vector3(get_value(attrs, "axis"), m_context); + const double angle = get_scalar(get_value(attrs, "angle"), m_context); + + if (norm(axis) > 0.0) + { + m_matrix = Matrix4d::make_rotation(normalize(axis), deg_to_rad(angle)); + } + else + { + RENDERER_LOG_ERROR("while defining element: the rotation axis cannot be null."); + m_context.get_event_counters().signal_error(); + } + } + + const Matrix4d& get_matrix() const + { + return m_matrix; + } + + private: + ParseContext& m_context; + Matrix4d m_matrix; + }; + + + // + // element handler. + // + + class ScalingElementHandler + : public ElementHandlerBaseType + { + public: + explicit ScalingElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + const Vector3d value = get_vector3(get_value(attrs, "value"), m_context); + m_matrix = Matrix4d::make_scaling(value); + } + + const Matrix4d& get_matrix() const + { + return m_matrix; + } + + private: + ParseContext& m_context; + Matrix4d m_matrix; + }; + + + // + // element handler. + // + + class TranslationElementHandler + : public ElementHandlerBaseType + { + public: + explicit TranslationElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + const Vector3d value = get_vector3(get_value(attrs, "value"), m_context); + m_matrix = Matrix4d::make_translation(value); + } + + const Matrix4d& get_matrix() const + { + return m_matrix; + } + + private: + ParseContext& m_context; + Matrix4d m_matrix; + }; + + + // + // element handler. + // + + class TransformElementHandler + : public ElementHandlerBaseType + { + public: + explicit TransformElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + m_time = get_scalar(get_value(attrs, "time", "0.0"), m_context); + m_matrix = Matrix4d::identity(); + } + + void end_element() override + { + try + { + m_transform = Transformd::from_local_to_parent(m_matrix); + } + catch (const ExceptionSingularMatrix&) + { + RENDERER_LOG_ERROR("while defining element: the transformation matrix is singular."); + m_context.get_event_counters().signal_error(); + m_transform = Transformd::identity(); + } + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementLookAt: + { + LookAtElementHandler* lookat_handler = + static_cast(handler); + m_matrix = lookat_handler->get_matrix() * m_matrix; + } + break; + + case ElementMatrix: + { + MatrixElementHandler* matrix_handler = + static_cast(handler); + m_matrix = matrix_handler->get_matrix() * m_matrix; + } + break; + + case ElementRotation: + { + RotationElementHandler* rotation_handler = + static_cast(handler); + m_matrix = rotation_handler->get_matrix() * m_matrix; + } + break; + + case ElementScaling: + { + ScalingElementHandler* scaling_handler = + static_cast(handler); + m_matrix = scaling_handler->get_matrix() * m_matrix; + } + break; + + case ElementTranslation: + { + TranslationElementHandler* translation_handler = + static_cast(handler); + m_matrix = translation_handler->get_matrix() * m_matrix; + } + break; + + assert_otherwise; + } + } + + float get_time() const + { + return m_time; + } + + const Transformd& get_transform() const + { + return m_transform; + } + + private: + ParseContext& m_context; + float m_time; + Matrix4d m_matrix; + Transformd m_transform; + }; + + + // + // Handle a transformation sequence. + // + + template + class TransformSequenceElementHandler + : public Base + { + public: + void start_element(const Attributes& attrs) override + { + Base::start_element(attrs); + } + + void end_element() override + { + if (m_transforms.size() > 1) + collapse_transforms(); + + if (m_transforms.empty()) + m_transforms[0.0f] = Transformd::identity(); + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementTransform: + { + TransformElementHandler* transform_handler = + static_cast(handler); + m_transforms[transform_handler->get_time()] = transform_handler->get_transform(); + } + break; + + default: + Base::end_child_element(element, handler); + break; + } + } + + void copy_transform_sequence_to(TransformSequence& target) const + { + target.clear(); + + for (const_each i = m_transforms; i; ++i) + target.set_transform(i->first, i->second); + } + + Transformd get_earliest_transform() const + { + TransformSequence sequence; + copy_transform_sequence_to(sequence); + return sequence.get_earliest_transform(); + } + + private: + typedef std::map TransformMap; + + TransformMap m_transforms; + + void collapse_transforms() + { + if (are_transforms_identical()) + { + const Transformd transform = m_transforms.begin()->second; + m_transforms.clear(); + m_transforms[0.0f] = transform; + } + } + + bool are_transforms_identical() const + { + for (TransformMap::const_iterator i = m_transforms.begin(), e = pred(m_transforms.end()); i != e; ++i) + { + if (i->second != succ(i)->second) + return false; + } + + return true; + } + }; + + + // + // element handler. + // + + class ValuesElementHandler + : public ElementHandlerBaseType + { + public: + explicit ValuesElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + m_values.clear(); + } + + void characters( + const XMLCh* const chars, + const XMLSize_t length) override + { + get_vector(transcode(chars), m_values, m_context); + } + + const ColorValueArray& get_values() const + { + return m_values; + } + + private: + ParseContext& m_context; + ColorValueArray m_values; + }; + + + // + // and elements handler. + // + + class ColorElementHandler + : public ParametrizedElementHandler + { + public: + explicit ColorElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + ParametrizedElementHandler::start_element(attrs); + + m_color_entity.reset(); + m_values.clear(); + m_alpha.clear(); + + m_name = get_value(attrs, "name"); + } + + void end_element() override + { + ParametrizedElementHandler::end_element(); + + try + { + m_color_entity = + m_alpha.empty() + ? ColorEntityFactory::create( + m_name.c_str(), + m_params, + m_values) + : ColorEntityFactory::create( + m_name.c_str(), + m_params, + m_values, + m_alpha); + } + catch (const ExceptionDictionaryKeyNotFound& e) + { + RENDERER_LOG_ERROR( + "while defining color \"%s\": required parameter \"%s\" missing.", + m_name.c_str(), + e.string()); + m_context.get_event_counters().signal_error(); + } + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementValues: + m_values = static_cast(handler)->get_values(); + break; + + case ElementAlpha: + m_alpha = static_cast(handler)->get_values(); + break; + + default: + ParametrizedElementHandler::end_child_element(element, handler); + break; + } + } + + auto_release_ptr get_color_entity() + { + return m_color_entity; + } + + private: + ParseContext& m_context; + auto_release_ptr m_color_entity; + std::string m_name; + ColorValueArray m_values; + ColorValueArray m_alpha; + }; + + + // + // Handle an element defining an entity. + // + + template + class EntityElementHandler + : public Base + { + public: + EntityElementHandler(const std::string& entity_type, ParseContext& context) + : m_context(context) + , m_entity_type(entity_type) + { + } + + void start_element(const Attributes& attrs) override + { + Base::start_element(attrs); + + m_entity.reset(); + + m_name = Base::get_value(attrs, "name"); + m_model = Base::get_value(attrs, "model"); + } + + void end_element() override + { + Base::end_element(); + + m_entity = + create_entity( + m_context.get_project().template get_factory_registrar(), + m_entity_type, + m_model, + m_name, + Base::m_params, + m_context); + } + + auto_release_ptr get_entity() + { + return m_entity; + } + + protected: + ParseContext& m_context; + const std::string m_entity_type; + auto_release_ptr m_entity; + std::string m_name; + std::string m_model; + }; + + + // + // element handler. + // + + class TextureElementHandler + : public ParametrizedElementHandler + { + public: + explicit TextureElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + ParametrizedElementHandler::start_element(attrs); + + m_texture.reset(); + + m_name = get_value(attrs, "name"); + m_model = get_value(attrs, "model"); + } + + void end_element() override + { + ParametrizedElementHandler::end_element(); + + try + { + const TextureFactoryRegistrar::FactoryType* factory = + m_texture_factory_registrar.lookup(m_model.c_str()); + + if (factory) + { + m_texture = + factory->create( + m_name.c_str(), + m_params, + m_context.get_project().search_paths()); + } + else + { + RENDERER_LOG_ERROR( + "while defining texture \"%s\": invalid model \"%s\".", + m_name.c_str(), + m_model.c_str()); + m_context.get_event_counters().signal_error(); + } + } + catch (const ExceptionDictionaryKeyNotFound& e) + { + RENDERER_LOG_ERROR( + "while defining texture \"%s\": required parameter \"%s\" missing.", + m_name.c_str(), + e.string()); + m_context.get_event_counters().signal_error(); + } + } + + auto_release_ptr get_texture() + { + return m_texture; + } + + private: + const TextureFactoryRegistrar m_texture_factory_registrar; + ParseContext& m_context; + auto_release_ptr m_texture; + std::string m_name; + std::string m_model; + }; + + + // + // element handler. + // + + class TextureInstanceElementHandler + : public TransformSequenceElementHandler + { + public: + explicit TextureInstanceElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + Base::start_element(attrs); + + m_texture_instance.reset(); + + m_name = get_value(attrs, "name"); + m_texture = get_value(attrs, "texture"); + } + + void end_element() override + { + Base::end_element(); + + try + { + m_texture_instance = + TextureInstanceFactory::create( + m_name.c_str(), + m_params, + m_texture.c_str(), + Transformf( + get_earliest_transform().get_local_to_parent(), + get_earliest_transform().get_parent_to_local())); + } + catch (const ExceptionDictionaryKeyNotFound& e) + { + RENDERER_LOG_ERROR( + "while defining texture instance \"%s\": required parameter \"%s\" missing.", + m_name.c_str(), + e.string()); + m_context.get_event_counters().signal_error(); + } + } + + auto_release_ptr get_texture_instance() + { + return m_texture_instance; + } + + private: + typedef TransformSequenceElementHandler Base; + + ParseContext& m_context; + auto_release_ptr m_texture_instance; + std::string m_name; + std::string m_texture; + }; + + + // + // element handler. + // + + class BSDFElementHandler + : public EntityElementHandler + { + public: + explicit BSDFElementHandler(ParseContext& context) + : EntityElementHandler("bsdf", context) + { + } + }; + + + // + // element handler. + // + + class BSSRDFElementHandler + : public EntityElementHandler + { + public: + explicit BSSRDFElementHandler(ParseContext& context) + : EntityElementHandler("bssrdf", context) + { + } + }; + + + // + // element handler. + // + + class EDFElementHandler + : public EntityElementHandler + { + public: + explicit EDFElementHandler(ParseContext& context) + : EntityElementHandler("edf", context) + { + } + }; + + + // + // element handler. + // + + class VolumeElementHandler + : public EntityElementHandler + { + public: + explicit VolumeElementHandler(ParseContext& context) + : EntityElementHandler("volume", context) + { + } + }; + + + // + // element handler. + // + + class SurfaceShaderElementHandler + : public EntityElementHandler + { + public: + explicit SurfaceShaderElementHandler(ParseContext& context) + : EntityElementHandler< SurfaceShader, ParametrizedElementHandler>("surface shader", context) + { + } + }; + + + // + // element handler. + // + + class EnvironmentElementHandler + : public ParametrizedElementHandler + { + public: + explicit EnvironmentElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + ParametrizedElementHandler::start_element(attrs); + + m_environment.reset(); + + m_name = get_value(attrs, "name"); + m_model = get_value(attrs, "model"); + } + + void end_element() override + { + ParametrizedElementHandler::end_element(); + + if (m_model == EnvironmentFactory::get_model()) + m_environment = EnvironmentFactory::create(m_name.c_str(), m_params); + else + { + RENDERER_LOG_ERROR( + "while defining environment \"%s\": invalid model \"%s\".", + m_name.c_str(), + m_model.c_str()); + m_context.get_event_counters().signal_error(); + } + } + + auto_release_ptr get_environment() + { + return m_environment; + } + + private: + ParseContext& m_context; + auto_release_ptr m_environment; + std::string m_name; + std::string m_model; + }; + + + // + // element handler. + // + + class EnvironmentEDFElementHandler + : public EntityElementHandler> + { + public: + explicit EnvironmentEDFElementHandler(ParseContext& context) + : Base("environment edf", context) + { + } + + void end_element() override + { + Base::end_element(); + + if (m_entity.get()) + copy_transform_sequence_to(m_entity->transform_sequence()); + } + + private: + typedef EntityElementHandler> Base; + }; + + + // + // element handler. + // + + class EnvironmentShaderElementHandler + : public EntityElementHandler + { + public: + explicit EnvironmentShaderElementHandler(ParseContext& context) + : EntityElementHandler("environment shader", context) + { + } + }; + + + // + // element handler. + // + + class LightElementHandler + : public EntityElementHandler> + { + public: + explicit LightElementHandler(ParseContext& context) + : Base("light", context) + { + } + + void end_element() override + { + Base::end_element(); + + if (m_entity.get()) + m_entity->set_transform(get_earliest_transform()); + } + + private: + typedef EntityElementHandler> Base; + }; + + + // + // element handler. + // + + class MaterialElementHandler + : public EntityElementHandler + { + public: + explicit MaterialElementHandler(ParseContext& context) + : EntityElementHandler("material", context) + { + } + }; + + + // + // element handler. + // + + class CameraElementHandler + : public EntityElementHandler> + { + public: + explicit CameraElementHandler(ParseContext& context) + : Base("camera", context) + { + } + + void end_element() override + { + Base::end_element(); + + if (m_entity.get()) + copy_transform_sequence_to(m_entity->transform_sequence()); + } + + private: + typedef EntityElementHandler> Base; + }; + + + // + // element handler. + // + + class ObjectElementHandler + : public ParametrizedElementHandler + { + public: + typedef std::vector ObjectVector; + + explicit ObjectElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + ParametrizedElementHandler::start_element(attrs); + + clear_keep_memory(m_objects); + + m_name = get_value(attrs, "name"); + m_model = get_value(attrs, "model"); + } + + void end_element() override + { + ParametrizedElementHandler::end_element(); + + try + { + const IObjectFactory* factory = + m_context.get_project().get_factory_registrar().lookup(m_model.c_str()); + + if (factory) + { + ObjectArray objects; + if (!factory->create( + m_name.c_str(), + m_params, + m_context.get_project().search_paths(), + m_context.get_options() & ProjectFileReader::OmitReadingMeshFiles, + objects)) + m_context.get_event_counters().signal_error(); + + m_objects = array_vector(objects); + } + else + { + RENDERER_LOG_ERROR( + "while defining object \"%s\": invalid model \"%s\".", + m_name.c_str(), + m_model.c_str()); + m_context.get_event_counters().signal_error(); + } + } + catch (const ExceptionDictionaryKeyNotFound& e) + { + RENDERER_LOG_ERROR( + "while defining object \"%s\": required parameter \"%s\" missing.", + m_name.c_str(), + e.string()); + m_context.get_event_counters().signal_error(); + } + catch (const ExceptionUnknownEntity& e) + { + RENDERER_LOG_ERROR( + "while defining object \"%s\": unknown entity \"%s\".", + m_name.c_str(), + e.string()); + m_context.get_event_counters().signal_error(); + } + catch (const Exception& e) + { + RENDERER_LOG_ERROR( + "while defining object \"%s\": %s", + m_name.c_str(), + e.what()); + m_context.get_event_counters().signal_error(); + } + } + + const ObjectVector& get_objects() const + { + return m_objects; + } + + private: + ParseContext& m_context; + ObjectVector m_objects; + std::string m_name; + std::string m_model; + }; + + + // + // element handler. + // + + class AssignMaterialElementHandler + : public ElementHandlerBaseType + { + public: + explicit AssignMaterialElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + m_slot = get_value(attrs, "slot"); + m_side = get_value(attrs, "side", "front"); + m_material = get_value(attrs, "material"); + + if (m_side != "front" && m_side != "back" && m_side != "both") + { + RENDERER_LOG_ERROR( + "while assigning material: side must be \"front\", \"back\" or \"both\", got \"%s\".", + m_side.c_str()); + m_context.get_event_counters().signal_error(); + m_side = "front"; + } + } + + const std::string& get_material_slot() const + { + return m_slot; + } + + const std::string& get_material_side() const + { + return m_side; + } + + const std::string& get_material_name() const + { + return m_material; + } + + private: + ParseContext& m_context; + std::string m_slot; + std::string m_side; + std::string m_material; + }; + + + // + // element handler. + // + + class ObjectInstanceElementHandler + : public TransformSequenceElementHandler + { + public: + explicit ObjectInstanceElementHandler(ParseContext& context) + { + } + + void start_element(const Attributes& attrs) override + { + Base::start_element(attrs); + + m_object_instance.reset(); + m_front_material_mappings.clear(); + m_back_material_mappings.clear(); + + m_name = get_value(attrs, "name"); + m_object = get_value(attrs, "object"); + } + + void end_element() override + { + Base::end_element(); + + m_object_instance = + ObjectInstanceFactory::create( + m_name.c_str(), + m_params, + m_object.c_str(), + get_earliest_transform(), + m_front_material_mappings, + m_back_material_mappings); + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementAssignMaterial: + { + AssignMaterialElementHandler* assign_mat_handler = + static_cast(handler); + + const std::string& material_slot = assign_mat_handler->get_material_slot(); + const std::string& material_side = assign_mat_handler->get_material_side(); + const std::string& material_name = assign_mat_handler->get_material_name(); + + if (material_side == "front") + m_front_material_mappings.insert(material_slot.c_str(), material_name); + else if (material_side == "back") + m_back_material_mappings.insert(material_slot.c_str(), material_name); + else if (material_side == "both") + { + m_front_material_mappings.insert(material_slot.c_str(), material_name); + m_back_material_mappings.insert(material_slot.c_str(), material_name); + } + } + break; + + default: + Base::end_child_element(element, handler); + break; + } + } + + auto_release_ptr get_object_instance() + { + return m_object_instance; + } + + private: + typedef TransformSequenceElementHandler Base; + + auto_release_ptr m_object_instance; + StringDictionary m_front_material_mappings; + StringDictionary m_back_material_mappings; + std::string m_name; + std::string m_object; + }; + + + // + // element handler. + // + + class AssemblyInstanceElementHandler + : public TransformSequenceElementHandler + { + public: + explicit AssemblyInstanceElementHandler(ParseContext& context) + { + } + + void start_element(const Attributes& attrs) override + { + Base::start_element(attrs); + + m_assembly_instance.reset(); + + m_name = get_value(attrs, "name"); + m_assembly = get_value(attrs, "assembly"); + } + + void end_element() override + { + Base::end_element(); + + m_assembly_instance = + AssemblyInstanceFactory::create( + m_name.c_str(), + m_params, + m_assembly.c_str()); + + copy_transform_sequence_to(m_assembly_instance->transform_sequence()); + } + + auto_release_ptr get_assembly_instance() + { + return m_assembly_instance; + } + + private: + typedef TransformSequenceElementHandler Base; + + auto_release_ptr m_assembly_instance; + std::string m_name; + std::string m_assembly; + }; + + + // + // element handler. + // + + class OSLCodeElementHandler + : public ElementHandlerBaseType + { + public: + explicit OSLCodeElementHandler(ParseContext& context) + { + } + + void characters( + const XMLCh* const chars, + const XMLSize_t length) override + { + m_code += transcode(chars); + } + + std::string get_code() const + { + return trim_both(m_code); + } + + private: + std::string m_code; + }; + + + // + // element handler. + // + + class ShaderElementHandler + : public ParametrizedElementHandler + { + public: + explicit ShaderElementHandler(ParseContext& context) + { + } + + void start_element(const Attributes& attrs) override + { + ParametrizedElementHandler::start_element(attrs); + m_type = get_value(attrs, "type"); + m_name = get_value(attrs, "name"); + m_layer = get_value(attrs, "layer"); + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementOSLCode: + { + OSLCodeElementHandler* code_handler = + static_cast(handler); + m_code = code_handler->get_code(); + } + break; + + default: + ParametrizedElementHandler::end_child_element(element, handler); + break; + } + } + + const std::string& get_type() const + { + return m_type; + } + + const std::string& get_name() const + { + return m_name; + } + + const std::string& get_layer() const + { + return m_layer; + } + + const std::string& get_code() const + { + return m_code; + } + + const ParamArray& get_params() const + { + return m_params; + } + + private: + std::string m_type; + std::string m_name; + std::string m_layer; + std::string m_code; + }; + + + // + // element handler. + // + + class ShaderConnectionElementHandler + : public ElementHandlerBaseType + { + public: + explicit ShaderConnectionElementHandler(ParseContext& context) + { + } + + void start_element(const Attributes& attrs) override + { + m_src_layer = get_value(attrs, "src_layer"); + m_src_param = get_value(attrs, "src_param"); + m_dst_layer = get_value(attrs, "dst_layer"); + m_dst_param = get_value(attrs, "dst_param"); + } + + const std::string& src_layer() + { + return m_src_layer; + } + + const std::string& src_param() + { + return m_src_param; + } + + const std::string& dst_layer() + { + return m_dst_layer; + } + + const std::string& dst_param() + { + return m_dst_param; + } + + private: + std::string m_src_layer; + std::string m_src_param; + std::string m_dst_layer; + std::string m_dst_param; + }; + + + // + // element handler. + // + + class ShaderGroupElementHandler + : public ElementHandlerBaseType + { + public: + explicit ShaderGroupElementHandler(ParseContext& context) + { + } + + void start_element(const Attributes& attrs) override + { + m_name = get_value(attrs, "name"); + m_shader_group = ShaderGroupFactory::create(m_name.c_str()); + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementShader: + { + ShaderElementHandler* shader_handler = + static_cast(handler); + + if (shader_handler->get_code().empty()) + { + m_shader_group->add_shader( + shader_handler->get_type().c_str(), + shader_handler->get_name().c_str(), + shader_handler->get_layer().c_str(), + shader_handler->get_params()); + } + else + { + m_shader_group->add_source_shader( + shader_handler->get_type().c_str(), + shader_handler->get_name().c_str(), + shader_handler->get_layer().c_str(), + shader_handler->get_code().c_str(), + shader_handler->get_params()); + } + } + break; + + case ElementShaderConnection: + { + ShaderConnectionElementHandler* shader_handler = + static_cast(handler); + m_shader_group->add_connection( + shader_handler->src_layer().c_str(), + shader_handler->src_param().c_str(), + shader_handler->dst_layer().c_str(), + shader_handler->dst_param().c_str()); + } + break; + } + } + + auto_release_ptr get_shader_group() + { + return m_shader_group; + } + + private: + std::string m_name; + auto_release_ptr m_shader_group; + }; + + + // + // Base class for assembly and scene element handlers. + // + + class BaseGroupElementHandler + : public ParametrizedElementHandler + { + public: + explicit BaseGroupElementHandler(ParseContext& context) + : m_context(context) + { + } + + protected: + ParseContext& m_context; + + template + void insert(Container& container, auto_release_ptr entity) + { + if (entity.get() == nullptr) + return; + + if (container.get_by_name(entity->get_name()) != nullptr) + { + RENDERER_LOG_ERROR( + "an entity with the path \"%s\" already exists.", + entity->get_path().c_str()); + m_context.get_event_counters().signal_error(); + return; + } + + container.insert(entity); + } + }; + + + // + // element handler. + // + + class AssemblyElementHandler + : public BaseGroupElementHandler + { + public: + explicit AssemblyElementHandler(ParseContext& context) + : BaseGroupElementHandler(context) + { + } + + void start_element(const Attributes& attrs) override + { + ParametrizedElementHandler::start_element(attrs); + + m_assembly.reset(); + + m_assemblies.clear(); + m_assembly_instances.clear(); + m_bsdfs.clear(); + m_bssrdfs.clear(); + m_colors.clear(); + m_edfs.clear(); + m_lights.clear(); + m_materials.clear(); + m_objects.clear(); + m_object_instances.clear(); + m_volumes.clear(); + m_shader_groups.clear(); + m_surface_shaders.clear(); + m_textures.clear(); + m_texture_instances.clear(); + + m_name = get_value(attrs, "name"); + m_model = get_value(attrs, "model", AssemblyFactory().get_model()); + } + + void end_element() override + { + ParametrizedElementHandler::end_element(); + + const IAssemblyFactory* factory = + m_context.get_project().get_factory_registrar().lookup(m_model.c_str()); + + if (factory) + { + m_assembly = factory->create(m_name.c_str(), m_params); + + m_assembly->assemblies().swap(m_assemblies); + m_assembly->assembly_instances().swap(m_assembly_instances); + m_assembly->bsdfs().swap(m_bsdfs); + m_assembly->bssrdfs().swap(m_bssrdfs); + m_assembly->colors().swap(m_colors); + m_assembly->edfs().swap(m_edfs); + m_assembly->lights().swap(m_lights); + m_assembly->materials().swap(m_materials); + m_assembly->objects().swap(m_objects); + m_assembly->object_instances().swap(m_object_instances); + m_assembly->volumes().swap(m_volumes); + m_assembly->shader_groups().swap(m_shader_groups); + m_assembly->surface_shaders().swap(m_surface_shaders); + m_assembly->textures().swap(m_textures); + m_assembly->texture_instances().swap(m_texture_instances); + } + else + { + RENDERER_LOG_ERROR( + "while defining assembly \"%s\": invalid model \"%s\".", + m_name.c_str(), + m_model.c_str()); + m_context.get_event_counters().signal_error(); + } + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementAssembly: + insert( + m_assemblies, + static_cast(handler)->get_assembly()); + break; + + case ElementAssemblyInstance: + insert( + m_assembly_instances, + static_cast(handler)->get_assembly_instance()); + break; + + case ElementBSDF: + insert( + m_bsdfs, + static_cast(handler)->get_entity()); + break; + + case ElementBSSRDF: + insert( + m_bssrdfs, + static_cast(handler)->get_entity()); + break; + + case ElementColor: + insert( + m_colors, + static_cast(handler)->get_color_entity()); + break; + + case ElementEDF: + insert( + m_edfs, + static_cast(handler)->get_entity()); + break; + + case ElementLight: + insert( + m_lights, + static_cast(handler)->get_entity()); + break; + + case ElementMaterial: + insert( + m_materials, + static_cast(handler)->get_entity()); + break; + + case ElementObject: + for (Object* object : static_cast(handler)->get_objects()) + insert(m_objects, auto_release_ptr(object)); + break; + + case ElementObjectInstance: + insert( + m_object_instances, + static_cast(handler)->get_object_instance()); + break; + + case ElementShaderGroup: + insert( + m_shader_groups, + static_cast(handler)->get_shader_group()); + break; + + case ElementSurfaceShader: + insert( + m_surface_shaders, + static_cast(handler)->get_entity()); + break; + + case ElementTexture: + insert( + m_textures, + static_cast(handler)->get_texture()); + break; + + case ElementTextureInstance: + insert( + m_texture_instances, + static_cast(handler)->get_texture_instance()); + break; + + case ElementVolume: + insert( + m_volumes, + static_cast(handler)->get_entity()); + break; + + default: + ParametrizedElementHandler::end_child_element(element, handler); + break; + } + } + + auto_release_ptr get_assembly() + { + return m_assembly; + } + + private: + auto_release_ptr m_assembly; + std::string m_name; + std::string m_model; + AssemblyContainer m_assemblies; + AssemblyInstanceContainer m_assembly_instances; + BSDFContainer m_bsdfs; + BSSRDFContainer m_bssrdfs; + ColorContainer m_colors; + EDFContainer m_edfs; + LightContainer m_lights; + MaterialContainer m_materials; + ObjectContainer m_objects; + ObjectInstanceContainer m_object_instances; + VolumeContainer m_volumes; + ShaderGroupContainer m_shader_groups; + SurfaceShaderContainer m_surface_shaders; + TextureContainer m_textures; + TextureInstanceContainer m_texture_instances; + }; + + + // + // element handler. + // + + class SceneElementHandler + : public BaseGroupElementHandler + { + public: + explicit SceneElementHandler(ParseContext& context) + : BaseGroupElementHandler(context) + { + } + + void start_element(const Attributes& attrs) override + { + ParametrizedElementHandler::start_element(attrs); + + // Discover and load plugins before building the scene. + m_context.get_project().get_plugin_store().load_all_plugins_from_paths(m_context.get_project().search_paths()); + + m_scene = SceneFactory::create(); + } + + void end_element() override + { + ParametrizedElementHandler::end_element(); + + m_scene->get_parameters() = m_params; + + const GAABB3 scene_bbox = m_scene->compute_bbox(); + const Vector3d scene_center(scene_bbox.center()); + + RENDERER_LOG_INFO( + "scene bounding box: (%f, %f, %f)-(%f, %f, %f).\n" + "scene bounding sphere: center (%f, %f, %f), diameter %f.", + scene_bbox.min[0], scene_bbox.min[1], scene_bbox.min[2], + scene_bbox.max[0], scene_bbox.max[1], scene_bbox.max[2], + scene_center[0], scene_center[1], scene_center[2], + scene_bbox.diameter()); + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + assert(m_scene.get()); + + switch (element) + { + case ElementAssembly: + insert( + m_scene->assemblies(), + static_cast(handler)->get_assembly()); + break; + + case ElementAssemblyInstance: + insert( + m_scene->assembly_instances(), + static_cast(handler)->get_assembly_instance()); + break; + + case ElementColor: + insert( + m_scene->colors(), + static_cast(handler)->get_color_entity()); + break; + + case ElementCamera: + { + auto_release_ptr camera = + static_cast(handler)->get_entity(); + if (camera.get()) + m_scene->cameras().insert(camera); + } + break; + + case ElementEnvironment: + { + auto_release_ptr environment = + static_cast(handler)->get_environment(); + if (environment.get()) + { + if (m_scene->get_environment()) + { + RENDERER_LOG_ERROR("cannot define multiple environments."); + m_context.get_event_counters().signal_error(); + } + m_scene->set_environment(environment); + } + } + break; + + case ElementEnvironmentEDF: + insert( + m_scene->environment_edfs(), + static_cast(handler)->get_entity()); + break; + + case ElementEnvironmentShader: + insert( + m_scene->environment_shaders(), + static_cast(handler)->get_entity()); + break; + + case ElementTexture: + insert( + m_scene->textures(), + static_cast(handler)->get_texture()); + break; + + case ElementTextureInstance: + insert( + m_scene->texture_instances(), + static_cast(handler)->get_texture_instance()); + break; + + case ElementShaderGroup: + insert( + m_scene->shader_groups(), + static_cast(handler)->get_shader_group()); + break; + + default: + ParametrizedElementHandler::end_child_element(element, handler); + break; + } + } + + auto_release_ptr get_scene() + { + return m_scene; + } + + private: + auto_release_ptr m_scene; + }; + + + // + // element handler. + // + + class AOVElementHandler + : public ParametrizedElementHandler + { + public: + explicit AOVElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + ParametrizedElementHandler::start_element(attrs); + + m_aov.reset(); + + m_model = ParametrizedElementHandler::get_value(attrs, "model"); + } + + void end_element() override + { + ParametrizedElementHandler::end_element(); + + try + { + const IAOVFactory* factory = + m_context.get_project().get_factory_registrar().lookup(m_model.c_str()); + + if (factory) + m_aov = factory->create(m_params); + else + { + RENDERER_LOG_ERROR( + "while defining aov: invalid model \"%s\".", + m_model.c_str()); + m_context.get_event_counters().signal_error(); + } + } + catch (const ExceptionDictionaryKeyNotFound& e) + { + RENDERER_LOG_ERROR( + "while defining aov \"%s\": required parameter \"%s\" missing.", + m_model.c_str(), + e.string()); + m_context.get_event_counters().signal_error(); + } + catch (const ExceptionUnknownEntity& e) + { + RENDERER_LOG_ERROR( + "while defining aov \"%s\": unknown entity \"%s\".", + m_model.c_str(), + e.string()); + m_context.get_event_counters().signal_error(); + } + } + + auto_release_ptr get_aov() + { + return m_aov; + } + + protected: + ParseContext& m_context; + auto_release_ptr m_aov; + std::string m_model; + }; + + + // + // element handler. + // + + class AOVsElementHandler + : public ElementHandlerBaseType + { + public: + explicit AOVsElementHandler(ParseContext& context) + : m_context(context) + , m_aovs(nullptr) + { + } + + void set_aov_container(AOVContainer* aovs) + { + m_aovs = aovs; + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + assert(m_aovs); + + switch (element) + { + case ElementAOV: + { + auto_release_ptr aov( + static_cast(handler)->get_aov()); + + if (aov.get() != nullptr) + { + if (m_aovs->get_by_name(aov->get_name()) == nullptr) + m_aovs->insert(aov); + else + { + RENDERER_LOG_ERROR( + "an aov with the path \"%s\" already exists.", + aov->get_path().c_str()); + m_context.get_event_counters().signal_error(); + } + } + } + break; + + assert_otherwise; + } + } + + private: + ParseContext& m_context; + AOVContainer* m_aovs; + }; + + + // + // element handler. + // + + class PostProcessingStageElementHandler + : public EntityElementHandler + { + public: + explicit PostProcessingStageElementHandler(ParseContext& context) + : EntityElementHandler("post-processing stage", context) + { + } + }; + + + // + // element handler. + // + + class PostProcessingStagesElementHandler + : public ElementHandlerBaseType + { + public: + explicit PostProcessingStagesElementHandler(ParseContext& context) + : m_context(context) + , m_stages(nullptr) + { + } + + void set_post_processing_stage_container(PostProcessingStageContainer* stages) + { + m_stages = stages; + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + assert(m_stages); + + switch (element) + { + case ElementPostProcessingStage: + { + auto_release_ptr stage = + static_cast(handler)->get_entity(); + + if (stage.get() != nullptr) + { + if (m_stages->get_by_name(stage->get_name()) == nullptr) + m_stages->insert(stage); + else + { + RENDERER_LOG_ERROR( + "a post-processing stage with the path \"%s\" already exists.", + stage->get_path().c_str()); + m_context.get_event_counters().signal_error(); + } + } + } + break; + + assert_otherwise; + } + } + + private: + ParseContext& m_context; + PostProcessingStageContainer* m_stages; + }; + + + // + // element handler. + // + + class FrameElementHandler + : public ParametrizedElementHandler + { + public: + explicit FrameElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + ParametrizedElementHandler::start_element(attrs); + + m_frame.reset(); + + m_name = get_value(attrs, "name"); + + m_aovs.clear(); + m_post_processing_stages.clear(); + } + + void end_element() override + { + ParametrizedElementHandler::end_element(); + + m_frame = + FrameFactory::create( + m_name.c_str(), + m_params, + m_aovs, + m_context.get_project().search_paths()); + + m_frame->post_processing_stages().swap(m_post_processing_stages); + } + + void start_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementAOVs: + { + AOVsElementHandler* aovs_handler = + static_cast(handler); + aovs_handler->set_aov_container(&m_aovs); + } + break; + + case ElementPostProcessingStages: + { + PostProcessingStagesElementHandler* post_processing_stages_handler = + static_cast(handler); + post_processing_stages_handler->set_post_processing_stage_container(&m_post_processing_stages); + } + break; + + default: + ParametrizedElementHandler::start_child_element(element, handler); + break; + } + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementAOVs: + // Nothing to do, AOVs were directly inserted into the project. + break; + + case ElementPostProcessingStages: + // Nothing to do, post-processing stages were directly inserted into the project. + break; + + default: + ParametrizedElementHandler::end_child_element(element, handler); + break; + } + } + + auto_release_ptr get_frame() + { + return m_frame; + } + + private: + ParseContext& m_context; + auto_release_ptr m_frame; + std::string m_name; + AOVContainer m_aovs; + PostProcessingStageContainer m_post_processing_stages; + }; + + + // + // element handler. + // + + class OutputElementHandler + : public ElementHandlerBaseType + { + public: + explicit OutputElementHandler(ParseContext& context) + : m_context(context) + { + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementFrame: + { + FrameElementHandler* frame_handler = + static_cast(handler); + m_context.get_project().set_frame(frame_handler->get_frame()); + } + break; + + assert_otherwise; + } + } + + private: + ParseContext& m_context; + }; + + + // + // element handler. + // + + class ConfigurationElementHandler + : public ParametrizedElementHandler + { + public: + explicit ConfigurationElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + ParametrizedElementHandler::start_element(attrs); + + m_configuration.reset(); + + m_name = get_value(attrs, "name"); + m_base_name = get_value(attrs, "base"); + } + + void end_element() override + { + ParametrizedElementHandler::end_element(); + + m_configuration = + ConfigurationFactory::create( + m_name.c_str(), + m_params); + + // Handle configuration inheritance. + if (!m_base_name.empty()) + { + const Configuration* base = + m_context.get_project().configurations().get_by_name(m_base_name.c_str()); + + if (base) + m_configuration->set_base(base); + else + { + RENDERER_LOG_ERROR( + "while defining configuration \"%s\": the configuration \"%s\" does not exist.", + m_configuration->get_path().c_str(), + m_base_name.c_str()); + m_context.get_event_counters().signal_error(); + } + } + } + + auto_release_ptr get_configuration() + { + return m_configuration; + } + + private: + ParseContext& m_context; + auto_release_ptr m_configuration; + std::string m_name; + std::string m_base_name; + }; + + + // + // element handler. + // + + class ConfigurationsElementHandler + : public ElementHandlerBaseType + { + public: + explicit ConfigurationsElementHandler(ParseContext& context) + : m_context(context) + { + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementConfiguration: + { + // Insert the configuration directly into the project. + ConfigurationElementHandler* config_handler = + static_cast(handler); + m_context.get_project().configurations().insert( + config_handler->get_configuration()); + } + break; + + assert_otherwise; + } + } + + private: + ParseContext& m_context; + }; + + + // + // element handler. + // + + class SearchPathElementHandler + : public ElementHandlerBaseType + { + public: + explicit SearchPathElementHandler(ParseContext& context) + { + } + + void characters( + const XMLCh* const chars, + const XMLSize_t length) override + { + m_path += transcode(chars); + } + + std::string get_path() const + { + return trim_both(m_path); + } + + private: + std::string m_path; + }; + + + // + // element handler. + // + + class SearchPathsElementHandler + : public ElementHandlerBaseType + { + public: + explicit SearchPathsElementHandler(ParseContext& context) + : m_context(context) + { + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementSearchPath: + { + // Skip search paths if asked to do so. + if (m_context.get_options() & ProjectFileReader::OmitSearchPaths) + return; + + SearchPathElementHandler* path_handler = + static_cast(handler); + const std::string& path = path_handler->get_path(); + if (!path.empty()) + m_context.get_project().search_paths().push_back_explicit_path(path); + } + break; + + assert_otherwise; + } + } + + private: + ParseContext& m_context; + }; + + + // + // element handler. + // + + class DisplayElementHandler + : public ParametrizedElementHandler + { + public: + explicit DisplayElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + ParametrizedElementHandler::start_element(attrs); + m_name = get_value(attrs, "name"); + } + + void end_element() override + { + ParametrizedElementHandler::end_element(); + m_context.get_project().set_display(DisplayFactory::create(m_name.c_str(), m_params)); + } + + private: + ParseContext& m_context; + std::string m_name; + }; + + + // + // element handler. + // + + class ProjectElementHandler + : public ElementHandlerBaseType + { + public: + explicit ProjectElementHandler(ParseContext& context) + : m_context(context) + { + } + + void start_element(const Attributes& attrs) override + { + ElementHandlerBaseType::start_element(attrs); + + const size_t format_revision = + from_string( + ElementHandlerBaseType::get_value(attrs, "format_revision", "2")); + + if (format_revision > ProjectFormatRevision) + { + RENDERER_LOG_WARNING( + "this project was created with a newer version of appleseed; it may fail to load or render properly with this version."); + m_context.get_event_counters().signal_warning(); + } + + m_context.get_project().set_format_revision(format_revision); + } + + void end_child_element( + const ProjectElementID element, + ElementHandlerType* handler) override + { + switch (element) + { + case ElementConfigurations: + // Nothing to do, configurations were directly inserted into the project. + break; + + case ElementOutput: + // Nothing to do, frames were directly inserted into the project. + break; + + case ElementScene: + { + SceneElementHandler* scene_handler = + static_cast(handler); + auto_release_ptr scene = scene_handler->get_scene(); + if (scene.get()) + m_context.get_project().set_scene(scene); + } + break; + + case ElementSearchPaths: + // Nothing to do, search paths were directly inserted into the project. + break; + + case ElementDisplay: + // Nothing to do, display was directly inserted into the project. + break; + + assert_otherwise; + } + } + + private: + ParseContext& m_context; + }; + + + // + // Content handler. + // + + class ContentHandler + : public SAX2ContentHandler + { + public: + ContentHandler(Project* project, ParseContext& context) + : m_context(context) + { + register_factory_helper("alpha", ElementAlpha); + register_factory_helper("aov", ElementAOV); + register_factory_helper("aovs", ElementAOVs); + register_factory_helper("assembly", ElementAssembly); + register_factory_helper("assembly_instance", ElementAssemblyInstance); + register_factory_helper("assign_material", ElementAssignMaterial); + register_factory_helper("bsdf", ElementBSDF); + register_factory_helper("bssrdf", ElementBSSRDF); + register_factory_helper("camera", ElementCamera); + register_factory_helper("color", ElementColor); + register_factory_helper("configuration", ElementConfiguration); + register_factory_helper("configurations", ElementConfigurations); + register_factory_helper("connect_shaders", ElementShaderConnection); + register_factory_helper("display", ElementDisplay); + register_factory_helper("edf", ElementEDF); + register_factory_helper("environment", ElementEnvironment); + register_factory_helper("environment_edf", ElementEnvironmentEDF); + register_factory_helper("environment_shader", ElementEnvironmentShader); + register_factory_helper("frame", ElementFrame); + register_factory_helper("light", ElementLight); + register_factory_helper("look_at", ElementLookAt); + register_factory_helper("material", ElementMaterial); + register_factory_helper("matrix", ElementMatrix); + register_factory_helper("object", ElementObject); + register_factory_helper("object_instance", ElementObjectInstance); + register_factory_helper("output", ElementOutput); + register_factory_helper("osl_code", ElementOSLCode); + register_factory_helper("parameter", ElementParameter); + register_factory_helper("parameters", ElementParameters); + register_factory_helper("post_processing_stage", ElementPostProcessingStage); + register_factory_helper("post_processing_stages", ElementPostProcessingStages); + register_factory_helper("rotation", ElementRotation); + register_factory_helper("scaling", ElementScaling); + register_factory_helper("scene", ElementScene); + register_factory_helper("search_path", ElementSearchPath); + register_factory_helper("search_paths", ElementSearchPaths); + register_factory_helper("shader", ElementShader); + register_factory_helper("shader_group", ElementShaderGroup); + register_factory_helper("surface_shader", ElementSurfaceShader); + register_factory_helper("texture", ElementTexture); + register_factory_helper("texture_instance", ElementTextureInstance); + register_factory_helper("transform", ElementTransform); + register_factory_helper("translation", ElementTranslation); + register_factory_helper("values", ElementValues); + register_factory_helper("volume", ElementVolume); + + std::unique_ptr> factory( + new ProjectElementHandlerFactory(m_context)); + + register_factory("project", ElementProject, std::move(factory)); + } + + private: + ParseContext& m_context; + + struct ProjectElementHandlerFactory + : public IElementHandlerFactory + { + ParseContext& m_context; + + explicit ProjectElementHandlerFactory(ParseContext& context) + : m_context(context) + { + } + + std::unique_ptr create() override + { + return std::unique_ptr(new ProjectElementHandler(m_context)); + } + }; + + template + struct GenericElementHandlerFactory + : public IElementHandlerFactory + { + ParseContext& m_context; + + explicit GenericElementHandlerFactory(ParseContext& context) + : m_context(context) + { + } + + std::unique_ptr create() override + { + return std::unique_ptr(new ElementHandler(m_context)); + } + }; + + template + void register_factory_helper(const std::string& name, const ProjectElementID id) + { + std::unique_ptr> factory( + new GenericElementHandlerFactory(m_context)); + + register_factory(name, id, move(factory)); + } + }; +} + +namespace +{ + // Return the name of the single .appleseed file inside a given archive file. + // If there are zero or more than one .appleseed file inside the archive, an + // empty string is returned (i.e. the archive is not a valid packed project). + std::string get_project_filename_from_archive(const char* project_filepath) + { + const std::vector files = + get_filenames_with_extension_from_zip(project_filepath, ".appleseed"); + + return files.size() == 1 ? files[0] : std::string(); + } + + std::string unpack_project( + const std::string& project_filepath, + const std::string& project_name, + const bf::path& unpacked_project_directory) + { + if (bf::exists(unpacked_project_directory)) + bf::remove_all(unpacked_project_directory); + + unzip(project_filepath, unpacked_project_directory.string()); + + return (unpacked_project_directory / project_name).string().c_str(); + } +} + +auto_release_ptr XMLProjectFileReader::read( + const char* project_filepath, + const char* schema_filepath, + const int options, + EventCounters& event_counters) +{ + assert(project_filepath); + + // Handle packed projects. + std::string actual_project_filepath; + if (is_zip_file(project_filepath)) + { + const std::string project_filename = get_project_filename_from_archive(project_filepath); + if (project_filename.empty()) + { + RENDERER_LOG_ERROR( + "%s looks like a packed project file, but it should contain a single *.appleseed file in order to be valid.", + project_filepath); + return auto_release_ptr(nullptr); + } + + const std::string unpacked_project_directory = + bf::path(project_filepath).replace_extension(".unpacked").string(); + + RENDERER_LOG_INFO( + "%s appears to be a packed project; unpacking to %s...", + project_filepath, + unpacked_project_directory.c_str()); + + actual_project_filepath = + unpack_project( + project_filepath, + project_filename, + unpacked_project_directory); + + project_filepath = actual_project_filepath.data(); + } + + XercesCContext xerces_context(global_logger()); + if (!xerces_context.is_initialized()) + return auto_release_ptr(nullptr); + + if (!(options & ProjectFileReader::OmitProjectSchemaValidation) && schema_filepath == nullptr) + { + RENDERER_LOG_ERROR( + "project schema validation enabled, but no schema filepath provided."); + return auto_release_ptr(nullptr); + } + + return load_project_file( + project_filepath, + schema_filepath, + options, + event_counters); +} + +auto_release_ptr XMLProjectFileReader::read_archive( + const char* archive_filepath, + const char* schema_filepath, + const SearchPaths& search_paths, + const int options, + EventCounters& event_counters) +{ + // Handle packed archives. + std::string actual_archive_filepath; + if (is_zip_file(archive_filepath)) + { + const std::string archive_name = get_project_filename_from_archive(archive_filepath); + if (archive_name.empty()) + { + RENDERER_LOG_ERROR( + "%s looks like a packed archive file, but it should contain a single *.appleseed file in order to be valid.", + archive_filepath); + return auto_release_ptr(); + } + + const std::string unpacked_archive_directory = + bf::path(archive_filepath).replace_extension(".unpacked").string(); + + actual_archive_filepath = + unpack_project( + archive_filepath, + archive_name, + unpacked_archive_directory); + + archive_filepath = actual_archive_filepath.data(); + } + + XercesCContext xerces_context(global_logger()); + if (!xerces_context.is_initialized()) + return auto_release_ptr(); + + if (!(options & ProjectFileReader::OmitProjectSchemaValidation) && schema_filepath == nullptr) + { + RENDERER_LOG_ERROR( + "archive schema validation enabled, but no schema filepath provided."); + return auto_release_ptr(); + } + + return load_project_file( + archive_filepath, + schema_filepath, + options | ProjectFileReader::OmitSearchPaths, + event_counters, + &search_paths); +} + +auto_release_ptr XMLProjectFileReader::load_project_file( + const char* project_filepath, + const char* schema_filepath, + const int options, + EventCounters& event_counters, + const foundation::SearchPaths* search_paths) +{ + // Create an empty project. + auto_release_ptr project(ProjectFactory::create(project_filepath)); + project->set_path(project_filepath); + + if (!(options & ProjectFileReader::OmitSearchPaths)) + { + project->search_paths().set_root_path( + bf::absolute(project_filepath).parent_path().string()); + } + else + { + assert(search_paths); + project->search_paths() = *search_paths; + } + + // Create the error handler. + std::unique_ptr error_handler( + new ErrorLoggerAndCounter( + project_filepath, + event_counters)); + + // Create the content handler. + ParseContext context(project.ref(), options, event_counters); + std::unique_ptr content_handler( + new ContentHandler( + project.get(), + context)); + + // Create the parser. + std::unique_ptr parser(XMLReaderFactory::createXMLReader()); + parser->setFeature(XMLUni::fgSAX2CoreNameSpaces, true); // perform namespace processing + + if (!(options & ProjectFileReader::OmitProjectSchemaValidation)) + { + assert(schema_filepath); + parser->setFeature(XMLUni::fgSAX2CoreValidation, true); // report all validation errors + parser->setFeature(XMLUni::fgXercesSchema, true); // enable the parser's schema support + parser->setProperty( + XMLUni::fgXercesSchemaExternalNoNameSpaceSchemaLocation, + const_cast( + static_cast( + transcode(schema_filepath).c_str()))); + } + else + { + parser->setFeature(XMLUni::fgSAX2CoreValidation, false); // ignore all validation errors + parser->setFeature(XMLUni::fgXercesSchema, false); // disable the parser's schema support + } + + parser->setErrorHandler(error_handler.get()); + parser->setContentHandler(content_handler.get()); + + // Load the project file. + RENDERER_LOG_INFO("loading project file %s...", project_filepath); + try + { + parser->parse(project_filepath); + } + catch (const XMLException&) + { + return auto_release_ptr(nullptr); + } + catch (const SAXParseException&) + { + return auto_release_ptr(nullptr); + } + + // Report a failure in case of warnings or errors. + if (error_handler->get_warning_count() > 0 || + error_handler->get_error_count() > 0 || + error_handler->get_fatal_error_count() > 0) + return auto_release_ptr(nullptr); + + return project; +} + +} // namespace renderer diff --git a/src/appleseed/renderer/modeling/project/xmlprojectfilereader.h b/src/appleseed/renderer/modeling/project/xmlprojectfilereader.h new file mode 100644 index 0000000000..93f50f4dea --- /dev/null +++ b/src/appleseed/renderer/modeling/project/xmlprojectfilereader.h @@ -0,0 +1,76 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited +// Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#pragma once + +// appleseed.foundation headers. +#include "foundation/memory/autoreleaseptr.h" + +// Forward declarations. +namespace foundation { class SearchPaths; } +namespace renderer { class EventCounters; } +namespace renderer { class Project; } + +namespace renderer +{ + +// +// XML Project file reader. +// + +class XMLProjectFileReader +{ + public: + // Read a project from disk (or load a built-in project). + // Return 0 if reading or parsing the file failed. + static foundation::auto_release_ptr read( + const char* project_filepath, + const char* schema_filepath, + const int options, + EventCounters& event_counters); + + // Read an archive from disk. + // Return 0 if reading or parsing the file failed. + static foundation::auto_release_ptr read_archive( + const char* archive_filepath, + const char* schema_filepath, + const foundation::SearchPaths& search_paths, + const int options, + EventCounters& event_counters); + + private: + static foundation::auto_release_ptr load_project_file( + const char* project_filepath, + const char* schema_filepath, + const int options, + EventCounters& event_counters, + const foundation::SearchPaths* search_paths = nullptr); +}; + +} // namespace renderer diff --git a/src/appleseed/renderer/modeling/project/xmlprojectfilewriter.cpp b/src/appleseed/renderer/modeling/project/xmlprojectfilewriter.cpp new file mode 100644 index 0000000000..6fa26a2966 --- /dev/null +++ b/src/appleseed/renderer/modeling/project/xmlprojectfilewriter.cpp @@ -0,0 +1,1062 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited +// Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// Interface header. +#include "xmlprojectfilewriter.h" + +// appleseed.renderer headers. +#include "renderer/modeling/aov/aov.h" +#include "renderer/modeling/bsdf/bsdf.h" +#include "renderer/modeling/bssrdf/bssrdf.h" +#include "renderer/modeling/camera/camera.h" +#include "renderer/modeling/color/colorentity.h" +#include "renderer/modeling/display/display.h" +#include "renderer/modeling/edf/edf.h" +#include "renderer/modeling/environment/environment.h" +#include "renderer/modeling/environmentedf/environmentedf.h" +#include "renderer/modeling/environmentshader/environmentshader.h" +#include "renderer/modeling/frame/frame.h" +#include "renderer/modeling/light/light.h" +#include "renderer/modeling/material/material.h" +#include "renderer/modeling/object/curveobject.h" +#include "renderer/modeling/object/curveobjectwriter.h" +#include "renderer/modeling/object/meshobject.h" +#include "renderer/modeling/object/meshobjectwriter.h" +#include "renderer/modeling/object/object.h" +#include "renderer/modeling/postprocessingstage/postprocessingstage.h" +#include "renderer/modeling/project/assethandler.h" +#include "renderer/modeling/project/configuration.h" +#include "renderer/modeling/project/configurationcontainer.h" +#include "renderer/modeling/project/project.h" +#include "renderer/modeling/project/projectfilewriter.h" +#include "renderer/modeling/scene/assembly.h" +#include "renderer/modeling/scene/assemblyinstance.h" +#include "renderer/modeling/scene/containers.h" +#include "renderer/modeling/scene/objectinstance.h" +#include "renderer/modeling/scene/proceduralassembly.h" +#include "renderer/modeling/scene/scene.h" +#include "renderer/modeling/scene/textureinstance.h" +#include "renderer/modeling/shadergroup/shader.h" +#include "renderer/modeling/shadergroup/shaderconnection.h" +#include "renderer/modeling/shadergroup/shadergroup.h" +#include "renderer/modeling/shadergroup/shaderparam.h" +#include "renderer/modeling/surfaceshader/surfaceshader.h" +#include "renderer/modeling/texture/texture.h" +#include "renderer/modeling/volume/volume.h" +#include "renderer/utility/transformsequence.h" + +// appleseed.foundation headers. +#include "foundation/containers/dictionary.h" +#include "foundation/core/appleseed.h" +#include "foundation/math/transform.h" +#include "foundation/platform/defaulttimers.h" +#include "foundation/string/string.h" +#include "foundation/utility/foreach.h" +#include "foundation/utility/indenter.h" +#include "foundation/utility/searchpaths.h" +#include "foundation/utility/stopwatch.h" +#include "foundation/utility/xmlelement.h" +#include "foundation/utility/zip.h" + +// Boost headers. +#include "boost/filesystem.hpp" + +// Standard headers. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost; +using namespace foundation; +namespace bf = boost::filesystem; + +namespace renderer +{ + +// +// XML ProjectWriter class implementation. +// + +namespace +{ + // Floating-point formatting settings. + const char* MatrixFormat = "%.17f"; + const char* ColorValueFormat = "%.9f"; + + class Writer + { + public: + // Constructor. + Writer( + const Project& project, + const char* filepath, + FILE* file, + const int options) + : m_project_new_root_dir(filesystem::path(filepath).parent_path()) + , m_file(file) + , m_options(options) + , m_indenter(4) + { + } + + // Write the element. + void write_project(const Project& project) + { + XMLElement element("project", m_file, m_indenter); + element.add_attribute("format_revision", project.get_format_revision()); + element.write(XMLElement::HasChildElements); + + write_search_paths(project); + + if (project.get_display()) + write(*project.get_display()); + + if (project.get_scene()) + write_scene(*project.get_scene()); + + write_output(project); + write_configurations(project); + } + + private: + const filesystem::path m_project_new_root_dir; + FILE* m_file; + const int m_options; + Indenter m_indenter; + + // Return a lexicographically-sorted vector of references to entities. + template + std::vector> sorted(const Collection& collection) + { + return sorted_impl(collection.begin(), collection.end()); + } + template + std::vector> sorted(Collection& collection) + { + return sorted_impl(collection.begin(), collection.end()); + } + template + std::vector> sorted_impl(Iterator input_begin, Iterator input_end) + { + std::vector> result; + result.reserve(std::distance(input_begin, input_end)); + + for (Iterator it = input_begin; it != input_end; ++it) + result.emplace_back(*it); + + sort(result.begin(), result.end(), [](const Entity& lhs, const Entity& rhs) + { + return strcmp(lhs.get_name(), rhs.get_name()) < 0; + }); + + return result; + } + + // Write a vector of scalars. + template + void write_vector( + const Vec& v, + const size_t size, + const size_t columns, + const char* fmt) + { + assert(columns > 0); + + for (size_t i = 0; i < size; ++i) + { + const size_t col = i % columns; + + if (col == 0) + fputs(m_indenter.c_str(), m_file); + else fputc(' ', m_file); + + fprintf(m_file, fmt, v[i]); + + if (col == columns - 1 || i == size - 1) + fputc('\n', m_file); + } + } + + // Write a (possibly hierarchical) set of parameters. + void write_params(const Dictionary& params) + { + write_dictionary(params, m_file, m_indenter); + } + + // Write a element. + template + void write_transform(const Transform& transform) + { + if (transform.get_local_to_parent() == Matrix::identity()) + return; + + XMLElement element("transform", m_file, m_indenter); + element.write(XMLElement::HasChildElements); + + { + XMLElement child_element("matrix", m_file, m_indenter); + child_element.write(XMLElement::HasChildElements); + + write_vector( + transform.get_local_to_parent(), + 16, + 4, + MatrixFormat); + } + } + + // Write a transform sequence. + void write_transform_sequence(const TransformSequence& transform_sequence) + { + if (transform_sequence.size() == 1) + { + float time; + Transformd transform; + transform_sequence.get_transform(0, time, transform); + + if (transform.get_local_to_parent() == Matrix4d::identity()) + return; + } + + for (size_t i = 0, e = transform_sequence.size(); i < e; ++i) + { + float time; + Transformd transform; + transform_sequence.get_transform(i, time, transform); + + XMLElement element("transform", m_file, m_indenter); + element.add_attribute("time", time); + element.write(XMLElement::HasChildElements); + + { + XMLElement child_element("matrix", m_file, m_indenter); + child_element.write(XMLElement::HasChildElements); + + write_vector( + transform.get_local_to_parent(), + 16, + 4, + MatrixFormat); + } + } + } + + // Write an array of color values. + void write_value_array(const char* element_name, const ColorValueArray& values) + { + XMLElement element(element_name, m_file, m_indenter); + element.write(XMLElement::HasChildElements); + write_vector( + values, + values.size(), + 8, + ColorValueFormat); + } + + template + void write_entity(const char* element_name, const Entity& entity) + { + write_entity(element_name, entity, entity.get_name()); + } + + template + void write_entity(const char* element_name, const Entity& entity, const char* entity_name) + { + XMLElement element(element_name, m_file, m_indenter); + element.add_attribute("name", entity_name); + element.add_attribute("model", entity.get_model()); + element.write( + !entity.get_parameters().empty() + ? XMLElement::HasChildElements + : XMLElement::HasNoContent); + + write_params(entity.get_parameters()); + } + + template + void write_collection(const Collection& collection) + { + for (const auto& entity : sorted(collection)) + write(entity); + } + + // Write an element. + void write(const AOV& aov) + { + XMLElement element("aov", m_file, m_indenter); + element.add_attribute("model", aov.get_model()); + element.write( + !aov.get_parameters().empty() + ? XMLElement::HasChildElements + : XMLElement::HasNoContent); + + write_params(aov.get_parameters()); + } + + // Write an element. + void write_aovs(const Frame& frame) + { + const AOVContainer& aovs = frame.aovs(); + if (!aovs.empty()) + { + XMLElement element("aovs", m_file, m_indenter); + element.write(XMLElement::HasChildElements); + + write_collection(aovs); + } + } + + // Write an element. + void write(const Assembly& assembly) + { + XMLElement element("assembly", m_file, m_indenter); + element.add_attribute("name", assembly.get_name()); + + // Don't write the assembly model for normal assemblies + // to preserve compatibility with older appleseed versions. + if (strcmp(assembly.get_model(), AssemblyFactory().get_model()) != 0) + element.add_attribute("model", assembly.get_model()); + + // Don't write the content of the assembly if it was + // generated procedurally. + if (dynamic_cast(&assembly) != nullptr) + { + element.write( + !assembly.get_parameters().empty() + ? XMLElement::HasChildElements + : XMLElement::HasNoContent); + + write_params(assembly.get_parameters()); + } + else + { + element.write( + !assembly.get_parameters().empty() || + !assembly.colors().empty() || + !assembly.textures().empty() || + !assembly.texture_instances().empty() || + !assembly.bsdfs().empty() || + !assembly.bssrdfs().empty() || + !assembly.edfs().empty() || + !assembly.shader_groups().empty() || + !assembly.surface_shaders().empty() || + !assembly.materials().empty() || + !assembly.lights().empty() || + !assembly.objects().empty() || + !assembly.object_instances().empty() || + !assembly.volumes().empty() || + !assembly.assemblies().empty() || + !assembly.assembly_instances().empty() + ? XMLElement::HasChildElements + : XMLElement::HasNoContent); + + write_params(assembly.get_parameters()); + write_collection(assembly.colors()); + write_collection(assembly.textures()); + write_collection(assembly.texture_instances()); + write_collection(assembly.bsdfs()); + write_collection(assembly.bssrdfs()); + write_collection(assembly.edfs()); + write_collection(assembly.shader_groups()); + write_collection(assembly.surface_shaders()); + write_collection(assembly.materials()); + write_collection(assembly.lights()); + write_object_collection(assembly.objects()); + write_collection(assembly.object_instances()); + write_collection(assembly.volumes()); + write_collection(assembly.assemblies()); + write_collection(assembly.assembly_instances()); + } + } + + // Write an element. + void write(const AssemblyInstance& assembly_instance) + { + XMLElement element("assembly_instance", m_file, m_indenter); + element.add_attribute("name", assembly_instance.get_name()); + element.add_attribute("assembly", assembly_instance.get_assembly_name()); + element.write(XMLElement::HasChildElements); + + write_params(assembly_instance.get_parameters()); + write_transform_sequence(assembly_instance.transform_sequence()); + } + + // Write an element. + void write_assign_material( + const std::string& slot, + const std::string& side, + const std::string& name) + { + XMLElement element("assign_material", m_file, m_indenter); + element.add_attribute("slot", slot); + element.add_attribute("side", side); + element.add_attribute("material", name); + element.write(XMLElement::HasNoContent); + } + + // Write a series of elements. + void write_assign_materials( + const ObjectInstance::Side side, + const StringDictionary& material_mappings) + { + const std::string side_string = side == ObjectInstance::FrontSide ? "front" : "back"; + + for (const_each i = material_mappings; i; ++i) + write_assign_material(i->key(), side_string, i->value()); + } + + // Write a element. + void write(const BSDF& bsdf) + { + write_entity("bsdf", bsdf); + } + + // Write a element. + void write(const BSSRDF& bssrdf) + { + write_entity("bssrdf", bssrdf); + } + + // Write a element. + void write(const Camera& camera) + { + XMLElement element("camera", m_file, m_indenter); + element.add_attribute("name", camera.get_name()); + element.add_attribute("model", camera.get_model()); + element.write(XMLElement::HasChildElements); + + write_params(camera.get_parameters()); + write_transform_sequence(camera.transform_sequence()); + } + + // Write a element. + void write(const ColorEntity& color_entity) + { + XMLElement element("color", m_file, m_indenter); + element.add_attribute("name", color_entity.get_name()); + element.write(XMLElement::HasChildElements); + + write_params(color_entity.get_parameters()); + write_value_array("values", color_entity.get_values()); + write_value_array("alpha", color_entity.get_alpha()); + } + + // Write a element. + void write_configuration(const Configuration& configuration) + { + XMLElement element("configuration", m_file, m_indenter); + element.add_attribute("name", configuration.get_name()); + if (configuration.get_base()) + element.add_attribute("base", configuration.get_base()->get_name()); + element.write( + !configuration.get_parameters().empty() + ? XMLElement::HasChildElements + : XMLElement::HasNoContent); + + write_params(configuration.get_parameters()); + } + + size_t count_non_base_configurations(const ConfigurationContainer& configurations) + { + size_t count = 0; + + for (const Configuration& configuration : configurations) + { + if (!BaseConfigurationFactory::is_base_configuration(configuration.get_name())) + ++count; + } + + return count; + } + + // Write a element. + void write_configurations(const Project& project) + { + XMLElement element("configurations", m_file, m_indenter); + element.write( + count_non_base_configurations(project.configurations()) > 0 + ? XMLElement::HasChildElements + : XMLElement::HasNoContent); + + // Write configurations. + for (const Configuration& configuration : sorted(project.configurations())) + { + if (!BaseConfigurationFactory::is_base_configuration(configuration.get_name())) + write_configuration(configuration); + } + } + + // Write a element. + void write(const Display& display) + { + XMLElement element("display", m_file, m_indenter); + element.add_attribute("name", display.get_name()); + element.write( + !display.get_parameters().empty() + ? XMLElement::HasChildElements + : XMLElement::HasNoContent); + + write_params(display.get_parameters()); + } + + // Write an element. + void write(const EDF& edf) + { + write_entity("edf", edf); + } + + // Write an element. + void write(const Environment& environment) + { + write_entity("environment", environment); + } + + // Write an element. + void write(const EnvironmentEDF& env_edf) + { + XMLElement element("environment_edf", m_file, m_indenter); + element.add_attribute("name", env_edf.get_name()); + element.add_attribute("model", env_edf.get_model()); + element.write(XMLElement::HasChildElements); + + write_params(env_edf.get_parameters()); + write_transform_sequence(env_edf.transform_sequence()); + } + + // Write an element. + void write(const EnvironmentShader& env_shader) + { + write_entity("environment_shader", env_shader); + } + + // Write a element. + void write_frame(const Frame& frame) + { + XMLElement element("frame", m_file, m_indenter); + element.add_attribute("name", frame.get_name()); + element.write( + !frame.get_parameters().empty() || + !frame.aovs().empty() || + !frame.post_processing_stages().empty() + ? XMLElement::HasChildElements + : XMLElement::HasNoContent); + + write_params(frame.get_parameters()); + write_aovs(frame); + write_post_processing_stages(frame); + } + + // Write a element. + void write(const Light& light) + { + XMLElement element("light", m_file, m_indenter); + element.add_attribute("name", light.get_name()); + element.add_attribute("model", light.get_model()); + element.write(XMLElement::HasChildElements); + + write_params(light.get_parameters()); + write_transform(light.get_transform()); + } + + // Write a element. + void write(const Material& material) + { + write_entity("material", material); + } + + // Write a element. + void write(const Volume& material) + { + write_entity("volume", material); + } + + // Write a collection of elements. + void write_object_collection(ObjectContainer& objects) + { + std::set groups; + + for (Object& object : sorted(objects)) + { + if (strcmp(object.get_model(), MeshObjectFactory().get_model()) == 0) + write_mesh_object(static_cast(object), groups); + else if (strcmp(object.get_model(), CurveObjectFactory().get_model()) == 0) + write_curve_object(static_cast(object)); + else write(object); + } + } + + // Write a mesh object. + void write_mesh_object(MeshObject& object, std::set& groups) + { + ParamArray& params = object.get_parameters(); + + // If the object is a mesh primitive, do not write geometry to disk. + if (params.strings().exist("primitive")) + { + XMLElement element("object", m_file, m_indenter); + element.add_attribute("name", object.get_name()); + element.add_attribute("model", MeshObjectFactory().get_model()); + element.write(XMLElement::HasChildElements); + write_params(params); + return; + } + + if (params.strings().exist("__base_object_name")) + { + // This object belongs to a group of objects. + const std::string group_name = params.get("__base_object_name"); + if (groups.find(group_name) == groups.end()) + { + // This is the first time we encounter this group of objects. + groups.insert(group_name); + + // Write the object group. + params.strings().remove("__base_object_name"); + write_entity("object", object, group_name.c_str()); + params.strings().insert("__base_object_name", group_name); + } + } + else if (params.strings().exist("filename") || params.dictionaries().exist("filename")) + { + // This object has a filename parameter. + write_entity("object", object, object.get_name()); + } + else + { + // This object does not belong to a group and does not have a filename parameter. + write_orphan_mesh_object(object); + } + } + + // Object name mapping established by do_write_orphan_mesh_object(). + typedef std::map ObjectNameMapping; + ObjectNameMapping m_object_name_mapping; + + // Get the new name of an object, given its old name. + std::string translate_object_name(const std::string& old_name) const + { + const ObjectNameMapping::const_iterator i = m_object_name_mapping.find(old_name); + return i == m_object_name_mapping.end() ? old_name : i->second; + } + + // Write an element for a mesh object without a filename. + void write_orphan_mesh_object(const MeshObject& object) + { + // Construct the name of the mesh file. + const std::string object_name = object.get_name(); + const std::string filename = object_name + ".binarymesh"; + + if (!(m_options & ProjectFileWriter::OmitWritingGeometryFiles)) + { + // Write the mesh file to disk. + const std::string filepath = (m_project_new_root_dir / filename).string(); + MeshObjectWriter::write(object, object_name.c_str(), filepath.c_str()); + } + + // Write the element. + XMLElement element("object", m_file, m_indenter); + element.add_attribute("name", object_name); + element.add_attribute("model", MeshObjectFactory().get_model()); + element.write(XMLElement::HasChildElements); + + // Output a "filename" parameter but don't add it to the object. + ParamArray params = object.get_parameters(); + params.insert("filename", filename); + write_params(params); + + // Update the object name mapping. + m_object_name_mapping[object_name] = object_name + "." + object_name; + } + + // Write a curve object. + void write_curve_object(CurveObject& object) + { + ParamArray& params = object.get_parameters(); + + if (!params.strings().exist("filepath")) + { + const std::string object_name = object.get_name(); + const std::string filename = object_name + ".binarycurve"; + + if (!(m_options & ProjectFileWriter::OmitWritingGeometryFiles)) + { + // Write the curve file to disk. + const std::string filepath = (m_project_new_root_dir / filename).string(); + CurveObjectWriter::write(object, filepath.c_str()); + } + + // Add a file path parameter to the object. + params.insert("filepath", filename); + } + + // Write the element. + write_entity("object", object); + } + + // Write an element. + void write(const Object& object) + { + write_entity("object", object); + } + + // Write an element. + void write(const ObjectInstance& object_instance) + { + XMLElement element("object_instance", m_file, m_indenter); + element.add_attribute("name", object_instance.get_name()); + element.add_attribute("object", translate_object_name(object_instance.get_object_name())); + element.write(XMLElement::HasChildElements); + + write_params(object_instance.get_parameters()); + write_transform(object_instance.get_transform()); + write_assign_materials(ObjectInstance::FrontSide, object_instance.get_front_material_mappings()); + write_assign_materials(ObjectInstance::BackSide, object_instance.get_back_material_mappings()); + } + + // Write an element. + void write_output(const Project& project) + { + XMLElement element("output", m_file, m_indenter); + element.write( + project.get_frame() != nullptr + ? XMLElement::HasChildElements + : XMLElement::HasNoContent); + + if (project.get_frame()) + write_frame(*project.get_frame()); + } + + // Write a element. + void write(const PostProcessingStage& stage) + { + write_entity("post_processing_stage", stage); + } + + // Write an element. + void write_post_processing_stages(const Frame& frame) + { + const PostProcessingStageContainer& stages = frame.post_processing_stages(); + if (!stages.empty()) + { + XMLElement element("post_processing_stages", m_file, m_indenter); + element.write(XMLElement::HasChildElements); + + write_collection(stages); + } + } + + // Write a element. + void write_scene(const Scene& scene) + { + XMLElement element("scene", m_file, m_indenter); + element.write( + !scene.cameras().empty() || + !scene.colors().empty() || + !scene.textures().empty() || + !scene.texture_instances().empty() || + !scene.environment_edfs().empty() || + !scene.environment_shaders().empty() || + scene.get_environment() != nullptr || + !scene.shader_groups().empty() || + !scene.assemblies().empty() || + !scene.assembly_instances().empty() + ? XMLElement::HasChildElements + : XMLElement::HasNoContent); + + write_params(scene.get_parameters()); + write_collection(scene.cameras()); + write_collection(scene.colors()); + write_collection(scene.textures()); + write_collection(scene.texture_instances()); + write_collection(scene.environment_edfs()); + write_collection(scene.environment_shaders()); + if (scene.get_environment()) + write(*scene.get_environment()); + write_collection(scene.shader_groups()); + write_collection(scene.assemblies()); + write_collection(scene.assembly_instances()); + } + + // Write a element. + void write_search_path(const char* search_path) + { + XMLElement element("search_path", m_file, m_indenter); + element.write(XMLElement::HasChildElements); + + fprintf(m_file, "%s%s\n", m_indenter.c_str(), search_path); + } + + // Write a element. + void write_search_paths(const Project& project) + { + const SearchPaths& search_paths = project.search_paths(); + + if (search_paths.get_explicit_path_count() > 0) + { + XMLElement element("search_paths", m_file, m_indenter); + element.write(XMLElement::HasChildElements); + + for (size_t i = 0; i < search_paths.get_explicit_path_count(); ++i) + write_search_path(search_paths.get_explicit_path(i)); + } + } + + // Write a shader's element. + void write(const ShaderParam& param) + { + XMLElement element("parameter", m_file, m_indenter); + element.add_attribute("name", param.get_name()); + element.add_attribute("value", param.get_value_as_string()); + element.write(XMLElement::HasNoContent); + } + + // Write an element. + void write_osl_code(const char* code) + { + XMLElement element("osl_code", m_file, m_indenter); + element.write(XMLElement::HasChildElements); + + fprintf(m_file, "%s\n", replace_special_xml_characters(code).c_str()); + } + + // Write a element. + void write(const Shader& shader) + { + XMLElement element("shader", m_file, m_indenter); + element.add_attribute("type", shader.get_type()); + element.add_attribute("name", shader.get_shader()); + element.add_attribute("layer", shader.get_layer()); + element.write(XMLElement::HasChildElements); + + write_collection(shader.shader_params()); + + if (shader.get_source_code()) + write_osl_code(shader.get_source_code()); + } + + // Write a element. + void write(const ShaderConnection& connection) + { + XMLElement element("connect_shaders", m_file, m_indenter); + element.add_attribute("src_layer", connection.get_src_layer()); + element.add_attribute("src_param", connection.get_src_param()); + element.add_attribute("dst_layer", connection.get_dst_layer()); + element.add_attribute("dst_param", connection.get_dst_param()); + element.write(XMLElement::HasNoContent); + } + + // Write a element. + void write(const ShaderGroup& shader_group) + { + XMLElement element("shader_group", m_file, m_indenter); + element.add_attribute("name", shader_group.get_name()); + element.write(XMLElement::HasChildElements); + + // Write shaders in the original order. + for (const Shader& shader : shader_group.shaders()) + write(shader); + + // Write shader connections in the original order. + for (const ShaderConnection& shader_connection : shader_group.shader_connections()) + write(shader_connection); + } + + // Write a element. + void write(const SurfaceShader& surface_shader) + { + write_entity("surface_shader", surface_shader); + } + + // Write a element. + void write(const Texture& texture) + { + write_entity("texture", texture); + } + + // Write a element. + void write(const TextureInstance& texture_instance) + { + XMLElement element("texture_instance", m_file, m_indenter); + element.add_attribute("name", texture_instance.get_name()); + element.add_attribute("texture", texture_instance.get_texture_name()); + element.write(XMLElement::HasChildElements); + + write_params(texture_instance.get_parameters()); + write_transform(texture_instance.get_transform()); + } + }; +} + +bool XMLProjectFileWriter::write_plain_project_file( + Project& project, + const char* filepath, + const int options, + const char* extra_comments) +{ + Stopwatch stopwatch; + stopwatch.start(); + + RENDERER_LOG_INFO("writing project file %s...", filepath); + + if (!(options & ProjectFileWriter::OmitHandlingAssetFiles)) + { + // Manage references to external asset files. + const AssetHandler asset_handler( + project, + filepath, + (options & ProjectFileWriter::CopyAllAssets) != 0 + ? AssetHandler::CopyAllAssets + : AssetHandler::CopyRelativeAssetsOnly); + if (!asset_handler.handle_assets()) + { + RENDERER_LOG_ERROR("failed to write project file %s.", filepath); + return false; + } + } + + // Open the file for writing. + FILE* file = fopen(filepath, "wt"); + if (file == nullptr) + { + RENDERER_LOG_ERROR("failed to write project file %s: i/o error.", filepath); + return false; + } + + // Write the file header. + fprintf(file, "\n"); + + // Write an optional header comment. + if (!(options & ProjectFileWriter::OmitHeaderComment)) + { + fprintf( + file, + "\n", + Appleseed::get_synthetic_version_string()); + + // Write any optional comments. + if (extra_comments) + { + std::vector lines; + split(extra_comments, "\n", lines); + + for (const auto& line : lines) + { + if (!line.empty()) + fprintf(file, "\n", line.c_str()); + } + } + } + + // Write the project. + Writer writer(project, filepath, file, options); + writer.write_project(project); + + // Close the file. + fclose(file); + + stopwatch.measure(); + + RENDERER_LOG_INFO( + "wrote project file %s in %s.", + filepath, + pretty_time(stopwatch.get_seconds()).c_str()); + + return true; +} + +bool XMLProjectFileWriter::write_packed_project_file( + Project& project, + const char* filepath, + const int options, + const char* extra_comments) +{ + const bf::path project_path(filepath); + + const bf::path temp_directory = + project_path.parent_path() / + project_path.filename().replace_extension(".unpacked.temp"); + + const bf::path temp_project_filepath = + temp_directory / + project_path.filename().replace_extension(".appleseed"); + + if (!bf::create_directory(temp_directory)) + { + RENDERER_LOG_ERROR("failed to create directory %s", temp_directory.string().c_str()); + return false; + } + + bool success = true; + + try + { + success = + write_plain_project_file( + project, + temp_project_filepath.string().c_str(), + options | ProjectFileWriter::CopyAllAssets, + extra_comments); + + if (success) + { + Stopwatch stopwatch; + stopwatch.start(); + + RENDERER_LOG_INFO("packing project to %s...", filepath); + + zip(filepath, temp_directory.string()); + + stopwatch.measure(); + + RENDERER_LOG_INFO( + "packed project to %s in %s.", + filepath, + pretty_time(stopwatch.get_seconds()).c_str()); + } + } + catch (const std::exception&) // namespace qualification required + { + RENDERER_LOG_ERROR("failed to write project file %s.", filepath); + success = false; + } + + if (bf::exists(temp_directory)) + bf::remove_all(temp_directory); + + return success; +} + +} // namespace renderer diff --git a/src/appleseed/renderer/modeling/project/xmlprojectfilewriter.h b/src/appleseed/renderer/modeling/project/xmlprojectfilewriter.h new file mode 100644 index 0000000000..41cbf3c26d --- /dev/null +++ b/src/appleseed/renderer/modeling/project/xmlprojectfilewriter.h @@ -0,0 +1,62 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited +// Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#pragma once + +// Forward declarations. +namespace renderer { class Project; } + +namespace renderer +{ + +// +// XML Project file writer. +// + +class XMLProjectFileWriter +{ + public: + // Write a project to disk as a plain project file. + // Returns true on success, false otherwise. + static bool write_plain_project_file( + Project& project, + const char* filepath, + const int options, + const char* comments); + + // Write a project file to disk as a packed project file. + // Returns true on success, false otherwise. + static bool write_packed_project_file( + Project& project, + const char* filepath, + const int options, + const char* extra_comments); +}; + +} // namespace renderer