From 94fba47513239a2ea20722d7d68b19e7e69e6b26 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 16 Jun 2020 17:14:50 +0200 Subject: Cleanup: use explicit enum type for duplicate option of `BKE_object_duplicate` Using enum type itself in implementations, and uint in headers (as using enums types in headers is a pain when enum are not defined and used in a single same header file...). --- source/blender/io/collada/BCMath.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'source/blender/io') diff --git a/source/blender/io/collada/BCMath.cpp b/source/blender/io/collada/BCMath.cpp index ec9977c1469..e8765fa2bcd 100644 --- a/source/blender/io/collada/BCMath.cpp +++ b/source/blender/io/collada/BCMath.cpp @@ -17,6 +17,8 @@ * All rights reserved. */ +#include "BLI_utildefines.h" + #include "BCMath.h" #include "BlenderContext.h" -- cgit v1.2.3 From ba312bc8a7fdbffff0632176abd55df211773ff6 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 17 Jun 2020 14:00:09 +1000 Subject: Cleanup: doxy comments --- source/blender/io/collada/AnimationImporter.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp index edac84e2aaa..1de86425521 100644 --- a/source/blender/io/collada/AnimationImporter.cpp +++ b/source/blender/io/collada/AnimationImporter.cpp @@ -344,9 +344,11 @@ bool AnimationImporter::write_animation_list(const COLLADAFW::AnimationList *ani return true; } -/* \todo refactor read_node_transform to not automatically apply anything, +/** + * \todo refactor read_node_transform to not automatically apply anything, * but rather return the transform matrix, so caller can do with it what is - * necessary. Same for \ref get_node_mat */ + * necessary. Same for \ref get_node_mat + */ void AnimationImporter::read_node_transform(COLLADAFW::Node *node, Object *ob) { float mat[4][4]; -- cgit v1.2.3 From 084c5d6c7e2cf89bb9a7a9a9d00e9ee4475e222f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 8 May 2020 16:17:08 +0200 Subject: IO: Move Abstract Hierarchy Iterator into `io/common` The goal of the `AbstractHierarchyIterator` class (and supporting classes) was to use it in different exporters. It shouldn't be part of the USD module + namespace any more, now that it will also be used in the upcoming Alembic exporter rewrite. The source files are moved into `io/common`, which is compiled & linked into a new library `bf_io_common`. The unittests are still inside the `tests/gtests/usd` directory. They should be moved to a separate test module too, but that will be delayed until after T73268 has been resolved. Reviewed By: mont29 Differential Revision: https://developer.blender.org/D7669 --- source/blender/io/CMakeLists.txt | 2 + source/blender/io/alembic/CMakeLists.txt | 2 + source/blender/io/common/CMakeLists.txt | 45 ++ .../io/common/IO_abstract_hierarchy_iterator.h | 265 +++++++++ .../common/intern/abstract_hierarchy_iterator.cc | 654 +++++++++++++++++++++ source/blender/io/usd/CMakeLists.txt | 4 +- .../io/usd/intern/abstract_hierarchy_iterator.cc | 652 -------------------- .../io/usd/intern/abstract_hierarchy_iterator.h | 263 --------- .../blender/io/usd/intern/usd_hierarchy_iterator.h | 6 +- source/blender/io/usd/intern/usd_writer_abstract.h | 5 +- 10 files changed, 979 insertions(+), 919 deletions(-) create mode 100644 source/blender/io/common/CMakeLists.txt create mode 100644 source/blender/io/common/IO_abstract_hierarchy_iterator.h create mode 100644 source/blender/io/common/intern/abstract_hierarchy_iterator.cc delete mode 100644 source/blender/io/usd/intern/abstract_hierarchy_iterator.cc delete mode 100644 source/blender/io/usd/intern/abstract_hierarchy_iterator.h (limited to 'source/blender/io') diff --git a/source/blender/io/CMakeLists.txt b/source/blender/io/CMakeLists.txt index bc2f8d628e2..360cacc4360 100644 --- a/source/blender/io/CMakeLists.txt +++ b/source/blender/io/CMakeLists.txt @@ -18,6 +18,8 @@ # All rights reserved. # ***** END GPL LICENSE BLOCK ***** +add_subdirectory(common) + if(WITH_ALEMBIC) add_subdirectory(alembic) endif() diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt index 16f2d944876..d864aa51e64 100644 --- a/source/blender/io/alembic/CMakeLists.txt +++ b/source/blender/io/alembic/CMakeLists.txt @@ -20,6 +20,7 @@ set(INC . + ../common ../../blenkernel ../../blenlib ../../blenloader @@ -92,6 +93,7 @@ set(SRC set(LIB bf_blenkernel bf_blenlib + bf_io_common ${ALEMBIC_LIBRARIES} ${OPENEXR_LIBRARIES} diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt new file mode 100644 index 00000000000..4ed6f12762e --- /dev/null +++ b/source/blender/io/common/CMakeLists.txt @@ -0,0 +1,45 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2020, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + ../../blenkernel + ../../blenlib + ../../depsgraph + ../../makesdna +) + +set(INC_SYS +) + +set(SRC + intern/abstract_hierarchy_iterator.cc + + IO_abstract_hierarchy_iterator.h +) + +set(LIB + bf_blenkernel + bf_blenlib +) + +blender_add_lib(bf_io_common "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +target_link_libraries(bf_io_common INTERFACE) diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h new file mode 100644 index 00000000000..480b72ea0cf --- /dev/null +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -0,0 +1,265 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ + +/* + * This file contains the AbstractHierarchyIterator. It is intended for exporters for file + * formats that concern an entire hierarchy of objects (rather than, for example, an OBJ file that + * contains only a single mesh). Examples are Universal Scene Description (USD) and Alembic. + * AbstractHierarchyIterator is intended to be subclassed to support concrete file formats. + * + * The AbstractHierarchyIterator makes a distinction between the actual object hierarchy and the + * export hierarchy. The former is the parent/child structure in Blender, which can have multiple + * parent-like objects. For example, a duplicated object can have both a duplicator and a parent, + * both determining the final transform. The export hierarchy is the hierarchy as written to the + * file, and every object has only one export-parent. + * + * Currently the AbstractHierarchyIterator does not make any decisions about *what* to export. + * Selections like "selected only" or "no hair systems" are left to concrete subclasses. + */ + +#ifndef __ABSTRACT_HIERARCHY_ITERATOR_H__ +#define __ABSTRACT_HIERARCHY_ITERATOR_H__ + +#include +#include +#include + +struct Base; +struct Depsgraph; +struct DupliObject; +struct ID; +struct Object; +struct ParticleSystem; +struct ViewLayer; + +namespace blender { +namespace io { + +class AbstractHierarchyWriter; + +/* HierarchyContext structs are created by the AbstractHierarchyIterator. Each HierarchyContext + * struct contains everything necessary to export a single object to a file. */ +struct HierarchyContext { + /*********** Determined during hierarchy iteration: ***************/ + Object *object; /* Evaluated object. */ + Object *export_parent; + Object *duplicator; + float matrix_world[4][4]; + std::string export_name; + + /* When weak_export=true, the object will be exported only as transform, and only if is an + * ancestor of an object with weak_export=false. + * + * In other words: when weak_export=true but this object has no children, or all descendants also + * have weak_export=true, this object (and by recursive reasoning all its descendants) will be + * excluded from the export. + * + * The export hierarchy is kept as close to the hierarchy in Blender as possible. As such, an + * object that serves as a parent for another object, but which should NOT be exported itself, is + * exported only as transform (i.e. as empty). This happens with objects that are part of a + * holdout collection (which prevents them from being exported) but also parent of an exported + * object. */ + bool weak_export; + + /* When true, this object should check its parents for animation data when determining whether + * it's animated. This is necessary when a parent object in Blender is not part of the export. */ + bool animation_check_include_parent; + + /*********** Determined during writer creation: ***************/ + float parent_matrix_inv_world[4][4]; // Inverse of the parent's world matrix. + std::string export_path; // Hierarchical path, such as "/grandparent/parent/objectname". + ParticleSystem *particle_system; // Only set for particle/hair writers. + + /* Hierarchical path of the object this object is duplicating; only set when this object should + * be stored as a reference to its original. It can happen that the original is not part of the + * exported objects, in which case this string is empty even though 'duplicator' is set. */ + std::string original_export_path; + + bool operator<(const HierarchyContext &other) const; + + /* Return a HierarchyContext representing the root of the export hierarchy. */ + static const HierarchyContext *root(); + + /* For handling instanced collections, instances created by particles, etc. */ + bool is_instance() const; + void mark_as_instance_of(const std::string &reference_export_path); + void mark_as_not_instanced(); +}; + +/* Abstract writer for objects. Create concrete subclasses to write to USD, Alembic, etc. + * + * Instantiated by the AbstractHierarchyIterator on the first frame an object exists. Generally + * that's the first frame to be exported, but can be later, for example when objects are + * instantiated by particles. The AbstractHierarchyWriter::write() function is called on every + * frame the object exists in the dependency graph and should be exported. + */ +class AbstractHierarchyWriter { + public: + virtual ~AbstractHierarchyWriter(); + virtual void write(HierarchyContext &context) = 0; + // TODO(Sybren): add function like absent() that's called when a writer was previously created, + // but wasn't used while exporting the current frame (for example, a particle-instanced mesh of + // which the particle is no longer alive). + protected: + virtual bool check_is_animated(const HierarchyContext &context) const; +}; + +/* AbstractHierarchyIterator iterates over objects in a dependency graph, and constructs export + * writers. These writers are then called to perform the actual writing to a USD or Alembic file. + * + * Dealing with file- and scene-level data (for example, creating a USD scene, setting the frame + * rate, etc.) is not part of the AbstractHierarchyIterator class structure, and should be done + * in separate code. + */ +class AbstractHierarchyIterator { + public: + /* Mapping from export path to writer. */ + typedef std::map WriterMap; + /* Pair of a (potentially duplicated) object and its duplicator (or nullptr). + * This is typically used to store a pair of HierarchyContext::object and + * HierarchyContext::duplicator. */ + typedef std::pair DupliAndDuplicator; + /* All the children of some object, as per the export hierarchy. */ + typedef std::set ExportChildren; + /* Mapping from an object and its duplicator to the object's export-children. */ + typedef std::map ExportGraph; + /* Mapping from ID to its export path. This is used for instancing; given an + * instanced datablock, the export path of the original can be looked up. */ + typedef std::map ExportPathMap; + + protected: + ExportGraph export_graph_; + ExportPathMap duplisource_export_path_; + Depsgraph *depsgraph_; + WriterMap writers_; + + public: + explicit AbstractHierarchyIterator(Depsgraph *depsgraph); + virtual ~AbstractHierarchyIterator(); + + /* Iterate over the depsgraph, create writers, and tell the writers to write. + * Main entry point for the AbstractHierarchyIterator, must be called for every to-be-exported + * frame. */ + void iterate_and_write(); + + /* Release all writers. Call after all frames have been exported. */ + void release_writers(); + + /* Convert the given name to something that is valid for the exported file format. + * This base implementation is a no-op; override in a concrete subclass. */ + virtual std::string make_valid_name(const std::string &name) const; + + /* Return the name of this ID datablock that is valid for the exported file format. Overriding is + * only necessary if make_valid_name(id->name+2) is not suitable for the exported file format. + * NULL-safe: when `id == nullptr` this returns an empty string. */ + virtual std::string get_id_name(const ID *id) const; + + /* Given a HierarchyContext of some Object *, return an export path that is valid for its + * object->data. Overriding is necessary when the exported format does NOT expect the object's + * data to be a child of the object. */ + virtual std::string get_object_data_path(const HierarchyContext *context) const; + + private: + void debug_print_export_graph(const ExportGraph &graph) const; + + void export_graph_construct(); + void connect_loose_objects(); + void export_graph_prune(); + void export_graph_clear(); + + void visit_object(Object *object, Object *export_parent, bool weak_export); + void visit_dupli_object(DupliObject *dupli_object, + Object *duplicator, + const std::set &dupli_set); + + ExportChildren &graph_children(const HierarchyContext *parent_context); + void context_update_for_graph_index(HierarchyContext *context, + const ExportGraph::key_type &graph_index) const; + + void determine_export_paths(const HierarchyContext *parent_context); + void determine_duplication_references(const HierarchyContext *parent_context, + std::string indent); + + /* These three functions create writers and call their write() method. */ + void make_writers(const HierarchyContext *parent_context); + void make_writer_object_data(const HierarchyContext *context); + void make_writers_particle_systems(const HierarchyContext *context); + + /* Convenience wrappers around get_id_name(). */ + std::string get_object_name(const Object *object) const; + std::string get_object_data_name(const Object *object) const; + + AbstractHierarchyWriter *get_writer(const std::string &export_path) const; + + typedef AbstractHierarchyWriter *(AbstractHierarchyIterator::*create_writer_func)( + const HierarchyContext *); + /* Ensure that a writer exists; if it doesn't, call create_func(context). The create_func + * function should be one of the create_XXXX_writer(context) functions declared below. */ + AbstractHierarchyWriter *ensure_writer(HierarchyContext *context, + create_writer_func create_func); + + protected: + /* Construct a valid path for the export file format. This class concatenates by using '/' as a + * path separator, which is valid for both Alembic and USD. */ + virtual std::string path_concatenate(const std::string &parent_path, + const std::string &child_path) const; + + /* Return whether this object should be marked as 'weak export' or not. + * + * When this returns false, writers for the transform and data are created, + * and dupli-objects dupli-object generated from this object will be passed to + * should_visit_dupli_object(). + * + * When this returns true, only a transform writer is created and marked as + * 'weak export'. In this case, the transform writer will be removed before + * exporting starts, unless a descendant of this object is to be exported. + * Dupli-object generated from this object will also be skipped. + * + * See HierarchyContext::weak_export. + */ + virtual bool mark_as_weak_export(const Object *object) const; + + virtual bool should_visit_dupli_object(const DupliObject *dupli_object) const; + + virtual ExportGraph::key_type determine_graph_index_object(const HierarchyContext *context); + virtual ExportGraph::key_type determine_graph_index_dupli(const HierarchyContext *context, + const std::set &dupli_set); + + /* These functions should create an AbstractHierarchyWriter subclass instance, or return + * nullptr if the object or its data should not be exported. Returning a nullptr for + * data/hair/particle will NOT prevent the transform to be written. + * + * The returned writer is owned by the AbstractHierarchyWriter, and should be freed in + * delete_object_writer(). + * + * The created AbstractHierarchyWriter instances should NOT keep a copy of the context pointer. + * The context can be stack-allocated and go out of scope. */ + virtual AbstractHierarchyWriter *create_transform_writer(const HierarchyContext *context) = 0; + virtual AbstractHierarchyWriter *create_data_writer(const HierarchyContext *context) = 0; + virtual AbstractHierarchyWriter *create_hair_writer(const HierarchyContext *context) = 0; + virtual AbstractHierarchyWriter *create_particle_writer(const HierarchyContext *context) = 0; + + /* Called by release_writers() to free what the create_XXX_writer() functions allocated. */ + virtual void delete_object_writer(AbstractHierarchyWriter *writer) = 0; +}; + +} // namespace io +} // namespace blender + +#endif /* __ABSTRACT_HIERARCHY_ITERATOR_H__ */ diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc new file mode 100644 index 00000000000..5a318485203 --- /dev/null +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc @@ -0,0 +1,654 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#include "IO_abstract_hierarchy_iterator.h" + +#include +#include +#include +#include +#include + +#include "BKE_anim_data.h" +#include "BKE_duplilist.h" +#include "BKE_key.h" +#include "BKE_particle.h" + +#include "BLI_assert.h" +#include "BLI_listbase.h" +#include "BLI_math_matrix.h" + +#include "DNA_ID.h" +#include "DNA_layer_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" + +#include "DEG_depsgraph_query.h" + +namespace blender { +namespace io { + +const HierarchyContext *HierarchyContext::root() +{ + return nullptr; +} + +bool HierarchyContext::operator<(const HierarchyContext &other) const +{ + if (object != other.object) { + return object < other.object; + } + if (duplicator != nullptr && duplicator == other.duplicator) { + // Only resort to string comparisons when both objects are created by the same duplicator. + return export_name < other.export_name; + } + + return export_parent < other.export_parent; +} + +bool HierarchyContext::is_instance() const +{ + return !original_export_path.empty(); +} +void HierarchyContext::mark_as_instance_of(const std::string &reference_export_path) +{ + original_export_path = reference_export_path; +} +void HierarchyContext::mark_as_not_instanced() +{ + original_export_path.clear(); +} + +AbstractHierarchyWriter::~AbstractHierarchyWriter() +{ +} + +bool AbstractHierarchyWriter::check_is_animated(const HierarchyContext &context) const +{ + const Object *object = context.object; + + if (BKE_animdata_id_is_animated(static_cast(object->data))) { + return true; + } + if (BKE_key_from_object(object) != nullptr) { + return true; + } + + /* Test modifiers. */ + /* TODO(Sybren): replace this with a check on the depsgraph to properly check for dependency on + * time. */ + ModifierData *md = static_cast(object->modifiers.first); + while (md) { + if (md->type != eModifierType_Subsurf) { + return true; + } + md = md->next; + } + + return false; +} + +AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph) + : depsgraph_(depsgraph), writers_() +{ +} + +AbstractHierarchyIterator::~AbstractHierarchyIterator() +{ +} + +void AbstractHierarchyIterator::iterate_and_write() +{ + export_graph_construct(); + connect_loose_objects(); + export_graph_prune(); + determine_export_paths(HierarchyContext::root()); + determine_duplication_references(HierarchyContext::root(), ""); + make_writers(HierarchyContext::root()); + export_graph_clear(); +} + +void AbstractHierarchyIterator::release_writers() +{ + for (WriterMap::value_type it : writers_) { + delete_object_writer(it.second); + } + writers_.clear(); +} + +std::string AbstractHierarchyIterator::make_valid_name(const std::string &name) const +{ + return name; +} + +std::string AbstractHierarchyIterator::get_id_name(const ID *id) const +{ + if (id == nullptr) { + return ""; + } + + return make_valid_name(std::string(id->name + 2)); +} + +std::string AbstractHierarchyIterator::get_object_data_path(const HierarchyContext *context) const +{ + BLI_assert(!context->export_path.empty()); + BLI_assert(context->object->data); + + return path_concatenate(context->export_path, get_object_data_name(context->object)); +} + +void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &graph) const +{ + size_t total_graph_size = 0; + for (const ExportGraph::value_type &map_iter : graph) { + const DupliAndDuplicator &parent_info = map_iter.first; + Object *const export_parent = parent_info.first; + Object *const duplicator = parent_info.second; + + if (duplicator != nullptr) { + printf(" DU %s (as dupped by %s):\n", + export_parent == nullptr ? "-null-" : (export_parent->id.name + 2), + duplicator->id.name + 2); + } + else { + printf(" OB %s:\n", export_parent == nullptr ? "-null-" : (export_parent->id.name + 2)); + } + + total_graph_size += map_iter.second.size(); + for (HierarchyContext *child_ctx : map_iter.second) { + if (child_ctx->duplicator == nullptr) { + printf(" - %s%s%s\n", + child_ctx->object->id.name + 2, + child_ctx->weak_export ? " (weak)" : "", + child_ctx->original_export_path.size() ? + (std::string("ref ") + child_ctx->original_export_path).c_str() : + ""); + } + else { + printf(" - %s (dup by %s%s) %s\n", + child_ctx->object->id.name + 2, + child_ctx->duplicator->id.name + 2, + child_ctx->weak_export ? ", weak" : "", + child_ctx->original_export_path.size() ? + (std::string("ref ") + child_ctx->original_export_path).c_str() : + ""); + } + } + } + printf(" (Total graph size: %zu objects\n", total_graph_size); +} + +void AbstractHierarchyIterator::export_graph_construct() +{ + Scene *scene = DEG_get_evaluated_scene(depsgraph_); + + DEG_OBJECT_ITER_BEGIN (depsgraph_, + object, + DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | + DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) { + // Non-instanced objects always have their object-parent as export-parent. + const bool weak_export = mark_as_weak_export(object); + visit_object(object, object->parent, weak_export); + + if (weak_export) { + // If a duplicator shouldn't be exported, its duplilist also shouldn't be. + continue; + } + + // Export the duplicated objects instanced by this object. + ListBase *lb = object_duplilist(depsgraph_, scene, object); + if (lb) { + // Construct the set of duplicated objects, so that later we can determine whether a parent + // is also duplicated itself. + std::set dupli_set; + LISTBASE_FOREACH (DupliObject *, dupli_object, lb) { + if (!should_visit_dupli_object(dupli_object)) { + continue; + } + dupli_set.insert(dupli_object->ob); + } + + LISTBASE_FOREACH (DupliObject *, dupli_object, lb) { + if (!should_visit_dupli_object(dupli_object)) { + continue; + } + + visit_dupli_object(dupli_object, object, dupli_set); + } + } + + free_object_duplilist(lb); + } + DEG_OBJECT_ITER_END; +} + +void AbstractHierarchyIterator::connect_loose_objects() +{ + // Find those objects whose parent is not part of the export graph; these + // objects would be skipped when traversing the graph as a hierarchy. + // These objects will have to be re-attached to some parent object in order to + // fit into the hierarchy. + ExportGraph loose_objects_graph = export_graph_; + for (const ExportGraph::value_type &map_iter : export_graph_) { + for (const HierarchyContext *child : map_iter.second) { + // An object that is marked as a child of another object is not considered 'loose'. + loose_objects_graph.erase(std::make_pair(child->object, child->duplicator)); + } + } + // The root of the hierarchy is always found, so it's never considered 'loose'. + loose_objects_graph.erase(std::make_pair(nullptr, nullptr)); + + // Iterate over the loose objects and connect them to their export parent. + for (const ExportGraph::value_type &map_iter : loose_objects_graph) { + const DupliAndDuplicator &export_info = map_iter.first; + Object *object = export_info.first; + + while (true) { + // Loose objects will all be real objects, as duplicated objects always have + // their duplicator or other exported duplicated object as ancestor. + + ExportGraph::iterator found_parent_iter = export_graph_.find( + std::make_pair(object->parent, nullptr)); + visit_object(object, object->parent, true); + if (found_parent_iter != export_graph_.end()) { + break; + } + // 'object->parent' will never be nullptr here, as the export graph contains the + // tuple as root and thus will cause a break. + BLI_assert(object->parent != nullptr); + + object = object->parent; + } + } +} + +static bool remove_weak_subtrees(const HierarchyContext *context, + AbstractHierarchyIterator::ExportGraph &clean_graph, + const AbstractHierarchyIterator::ExportGraph &input_graph) +{ + bool all_is_weak = context != nullptr && context->weak_export; + Object *object = context != nullptr ? context->object : nullptr; + Object *duplicator = context != nullptr ? context->duplicator : nullptr; + + const AbstractHierarchyIterator::DupliAndDuplicator map_key = std::make_pair(object, duplicator); + AbstractHierarchyIterator::ExportGraph::const_iterator child_iterator; + + child_iterator = input_graph.find(map_key); + if (child_iterator != input_graph.end()) { + for (HierarchyContext *child_context : child_iterator->second) { + bool child_tree_is_weak = remove_weak_subtrees(child_context, clean_graph, input_graph); + all_is_weak &= child_tree_is_weak; + + if (child_tree_is_weak) { + // This subtree is all weak, so we can remove it from the current object's children. + clean_graph[map_key].erase(child_context); + delete child_context; + } + } + } + + if (all_is_weak) { + // This node and all its children are weak, so it can be removed from the export graph. + clean_graph.erase(map_key); + } + + return all_is_weak; +} + +void AbstractHierarchyIterator::export_graph_prune() +{ + // Take a copy of the map so that we can modify while recursing. + ExportGraph unpruned_export_graph = export_graph_; + remove_weak_subtrees(HierarchyContext::root(), export_graph_, unpruned_export_graph); +} + +void AbstractHierarchyIterator::export_graph_clear() +{ + for (ExportGraph::iterator::value_type &it : export_graph_) { + for (HierarchyContext *context : it.second) { + delete context; + } + } + export_graph_.clear(); +} + +void AbstractHierarchyIterator::visit_object(Object *object, + Object *export_parent, + bool weak_export) +{ + HierarchyContext *context = new HierarchyContext(); + context->object = object; + context->export_name = get_object_name(object); + context->export_parent = export_parent; + context->duplicator = nullptr; + context->weak_export = weak_export; + context->animation_check_include_parent = false; + context->export_path = ""; + context->original_export_path = ""; + copy_m4_m4(context->matrix_world, object->obmat); + + ExportGraph::key_type graph_index = determine_graph_index_object(context); + context_update_for_graph_index(context, graph_index); + + // Store this HierarchyContext as child of the export parent. + export_graph_[graph_index].insert(context); + + // Create an empty entry for this object to indicate it is part of the export. This will be used + // by connect_loose_objects(). Having such an "indicator" will make it possible to do an O(log n) + // check on whether an object is part of the export, rather than having to check all objects in + // the map. Note that it's not possible to simply search for (object->parent, nullptr), as the + // object's parent in Blender may not be the same as its export-parent. + ExportGraph::key_type object_key = std::make_pair(object, nullptr); + if (export_graph_.find(object_key) == export_graph_.end()) { + export_graph_[object_key] = ExportChildren(); + } +} + +AbstractHierarchyIterator::ExportGraph::key_type AbstractHierarchyIterator:: + determine_graph_index_object(const HierarchyContext *context) +{ + return std::make_pair(context->export_parent, nullptr); +} + +void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object, + Object *duplicator, + const std::set &dupli_set) +{ + HierarchyContext *context = new HierarchyContext(); + context->object = dupli_object->ob; + context->duplicator = duplicator; + context->weak_export = false; + context->export_path = ""; + context->original_export_path = ""; + context->export_path = ""; + context->animation_check_include_parent = false; + + copy_m4_m4(context->matrix_world, dupli_object->mat); + + // Construct export name for the dupli-instance. + std::stringstream suffix_stream; + suffix_stream << std::hex; + for (int i = 0; i < MAX_DUPLI_RECUR && dupli_object->persistent_id[i] != INT_MAX; i++) { + suffix_stream << "-" << dupli_object->persistent_id[i]; + } + context->export_name = make_valid_name(get_object_name(context->object) + suffix_stream.str()); + + ExportGraph::key_type graph_index = determine_graph_index_dupli(context, dupli_set); + context_update_for_graph_index(context, graph_index); + export_graph_[graph_index].insert(context); +} + +AbstractHierarchyIterator::ExportGraph::key_type AbstractHierarchyIterator:: + determine_graph_index_dupli(const HierarchyContext *context, + const std::set &dupli_set) +{ + /* If the dupli-object's parent is also instanced by this object, use that as the + * export parent. Otherwise use the dupli-parent as export parent. */ + + Object *parent = context->object->parent; + if (parent != nullptr && dupli_set.find(parent) != dupli_set.end()) { + // The parent object is part of the duplicated collection. + return std::make_pair(parent, context->duplicator); + } + return std::make_pair(context->duplicator, nullptr); +} + +void AbstractHierarchyIterator::context_update_for_graph_index( + HierarchyContext *context, const ExportGraph::key_type &graph_index) const +{ + // Update the HierarchyContext so that it is consistent with the graph index. + context->export_parent = graph_index.first; + if (context->export_parent != context->object->parent) { + /* The parent object in Blender is NOT used as the export parent. This means + * that the world transform of this object can be influenced by objects that + * are not part of its export graph. */ + context->animation_check_include_parent = true; + } +} + +AbstractHierarchyIterator::ExportChildren &AbstractHierarchyIterator::graph_children( + const HierarchyContext *context) +{ + if (context == nullptr) { + return export_graph_[std::make_pair(nullptr, nullptr)]; + } + + return export_graph_[std::make_pair(context->object, context->duplicator)]; +} + +void AbstractHierarchyIterator::determine_export_paths(const HierarchyContext *parent_context) +{ + const std::string &parent_export_path = parent_context ? parent_context->export_path : ""; + + for (HierarchyContext *context : graph_children(parent_context)) { + context->export_path = path_concatenate(parent_export_path, context->export_name); + + if (context->duplicator == nullptr) { + /* This is an original (i.e. non-instanced) object, so we should keep track of where it was + * exported to, just in case it gets instanced somewhere. */ + ID *source_ob = &context->object->id; + duplisource_export_path_[source_ob] = context->export_path; + + if (context->object->data != nullptr) { + ID *source_data = static_cast(context->object->data); + duplisource_export_path_[source_data] = get_object_data_path(context); + } + } + + determine_export_paths(context); + } +} + +void AbstractHierarchyIterator::determine_duplication_references( + const HierarchyContext *parent_context, std::string indent) +{ + ExportChildren children = graph_children(parent_context); + + for (HierarchyContext *context : children) { + if (context->duplicator != nullptr) { + ID *source_id = &context->object->id; + const ExportPathMap::const_iterator &it = duplisource_export_path_.find(source_id); + + if (it == duplisource_export_path_.end()) { + // The original was not found, so mark this instance as "the original". + context->mark_as_not_instanced(); + duplisource_export_path_[source_id] = context->export_path; + } + else { + context->mark_as_instance_of(it->second); + } + + if (context->object->data) { + ID *source_data_id = (ID *)context->object->data; + const ExportPathMap::const_iterator &it = duplisource_export_path_.find(source_data_id); + + if (it == duplisource_export_path_.end()) { + // The original was not found, so mark this instance as "original". + std::string data_path = get_object_data_path(context); + context->mark_as_not_instanced(); + duplisource_export_path_[source_id] = context->export_path; + duplisource_export_path_[source_data_id] = data_path; + } + } + } + + determine_duplication_references(context, indent + " "); + } +} + +void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_context) +{ + AbstractHierarchyWriter *transform_writer = nullptr; + float parent_matrix_inv_world[4][4]; + + if (parent_context) { + invert_m4_m4(parent_matrix_inv_world, parent_context->matrix_world); + } + else { + unit_m4(parent_matrix_inv_world); + } + + for (HierarchyContext *context : graph_children(parent_context)) { + // Update the context so that it is correct for this parent-child relation. + copy_m4_m4(context->parent_matrix_inv_world, parent_matrix_inv_world); + + // Get or create the transform writer. + transform_writer = ensure_writer(context, &AbstractHierarchyIterator::create_transform_writer); + if (transform_writer == nullptr) { + // Unable to export, so there is nothing to attach any children to; just abort this entire + // branch of the export hierarchy. + return; + } + + BLI_assert(DEG_is_evaluated_object(context->object)); + /* XXX This can lead to too many XForms being written. For example, a camera writer can refuse + * to write an orthographic camera. By the time that this is known, the XForm has already been + * written. */ + transform_writer->write(*context); + + if (!context->weak_export) { + make_writers_particle_systems(context); + make_writer_object_data(context); + } + + // Recurse into this object's children. + make_writers(context); + } + + // TODO(Sybren): iterate over all unused writers and call unused_during_iteration() or something. +} + +void AbstractHierarchyIterator::make_writer_object_data(const HierarchyContext *context) +{ + if (context->object->data == nullptr) { + return; + } + + HierarchyContext data_context = *context; + data_context.export_path = get_object_data_path(context); + + /* data_context.original_export_path is just a copy from the context. It points to the object, + * but needs to point to the object data. */ + if (data_context.is_instance()) { + ID *object_data = static_cast(context->object->data); + data_context.original_export_path = duplisource_export_path_[object_data]; + + /* If the object is marked as an instance, so should the object data. */ + BLI_assert(data_context.is_instance()); + } + + AbstractHierarchyWriter *data_writer; + data_writer = ensure_writer(&data_context, &AbstractHierarchyIterator::create_data_writer); + if (data_writer == nullptr) { + return; + } + + data_writer->write(data_context); +} + +void AbstractHierarchyIterator::make_writers_particle_systems( + const HierarchyContext *transform_context) +{ + Object *object = transform_context->object; + ParticleSystem *psys = static_cast(object->particlesystem.first); + for (; psys; psys = psys->next) { + if (!psys_check_enabled(object, psys, true)) { + continue; + } + + HierarchyContext hair_context = *transform_context; + hair_context.export_path = path_concatenate(transform_context->export_path, + make_valid_name(psys->name)); + hair_context.particle_system = psys; + + AbstractHierarchyWriter *writer = nullptr; + switch (psys->part->type) { + case PART_HAIR: + writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_hair_writer); + break; + case PART_EMITTER: + writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_particle_writer); + break; + } + + if (writer != nullptr) { + writer->write(hair_context); + } + } +} + +std::string AbstractHierarchyIterator::get_object_name(const Object *object) const +{ + return get_id_name(&object->id); +} + +std::string AbstractHierarchyIterator::get_object_data_name(const Object *object) const +{ + ID *object_data = static_cast(object->data); + return get_id_name(object_data); +} + +AbstractHierarchyWriter *AbstractHierarchyIterator::get_writer( + const std::string &export_path) const +{ + WriterMap::const_iterator it = writers_.find(export_path); + + if (it == writers_.end()) { + return nullptr; + } + return it->second; +} + +AbstractHierarchyWriter *AbstractHierarchyIterator::ensure_writer( + HierarchyContext *context, AbstractHierarchyIterator::create_writer_func create_func) +{ + AbstractHierarchyWriter *writer = get_writer(context->export_path); + if (writer != nullptr) { + return writer; + } + + writer = (this->*create_func)(context); + if (writer == nullptr) { + return nullptr; + } + + writers_[context->export_path] = writer; + + return writer; +} + +std::string AbstractHierarchyIterator::path_concatenate(const std::string &parent_path, + const std::string &child_path) const +{ + return parent_path + "/" + child_path; +} + +bool AbstractHierarchyIterator::mark_as_weak_export(const Object * /*object*/) const +{ + return false; +} +bool AbstractHierarchyIterator::should_visit_dupli_object(const DupliObject *dupli_object) const +{ + // Removing dupli_object->no_draw hides things like custom bone shapes. + return !dupli_object->no_draw; +} + +} // namespace io +} // namespace blender diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt index 732a638a255..fc4f45cdef8 100644 --- a/source/blender/io/usd/CMakeLists.txt +++ b/source/blender/io/usd/CMakeLists.txt @@ -32,6 +32,7 @@ add_definitions(-DPXR_STATIC) set(INC . + ../common ../../blenkernel ../../blenlib ../../blenloader @@ -52,7 +53,6 @@ set(INC_SYS ) set(SRC - intern/abstract_hierarchy_iterator.cc intern/usd_capi.cc intern/usd_hierarchy_iterator.cc intern/usd_writer_abstract.cc @@ -64,7 +64,6 @@ set(SRC intern/usd_writer_transform.cc usd.h - intern/abstract_hierarchy_iterator.h intern/usd_exporter_context.h intern/usd_hierarchy_iterator.h intern/usd_writer_abstract.h @@ -79,6 +78,7 @@ set(SRC set(LIB bf_blenkernel bf_blenlib + bf_io_common ) list(APPEND LIB diff --git a/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc b/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc deleted file mode 100644 index ab83ea2c3c4..00000000000 --- a/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc +++ /dev/null @@ -1,652 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#include "abstract_hierarchy_iterator.h" - -#include -#include -#include -#include -#include - -#include "BKE_anim_data.h" -#include "BKE_duplilist.h" -#include "BKE_key.h" -#include "BKE_particle.h" - -#include "BLI_assert.h" -#include "BLI_listbase.h" -#include "BLI_math_matrix.h" - -#include "DNA_ID.h" -#include "DNA_layer_types.h" -#include "DNA_modifier_types.h" -#include "DNA_object_types.h" -#include "DNA_particle_types.h" - -#include "DEG_depsgraph_query.h" - -namespace USD { - -const HierarchyContext *HierarchyContext::root() -{ - return nullptr; -} - -bool HierarchyContext::operator<(const HierarchyContext &other) const -{ - if (object != other.object) { - return object < other.object; - } - if (duplicator != nullptr && duplicator == other.duplicator) { - // Only resort to string comparisons when both objects are created by the same duplicator. - return export_name < other.export_name; - } - - return export_parent < other.export_parent; -} - -bool HierarchyContext::is_instance() const -{ - return !original_export_path.empty(); -} -void HierarchyContext::mark_as_instance_of(const std::string &reference_export_path) -{ - original_export_path = reference_export_path; -} -void HierarchyContext::mark_as_not_instanced() -{ - original_export_path.clear(); -} - -AbstractHierarchyWriter::~AbstractHierarchyWriter() -{ -} - -bool AbstractHierarchyWriter::check_is_animated(const HierarchyContext &context) const -{ - const Object *object = context.object; - - if (BKE_animdata_id_is_animated(static_cast(object->data))) { - return true; - } - if (BKE_key_from_object(object) != nullptr) { - return true; - } - - /* Test modifiers. */ - /* TODO(Sybren): replace this with a check on the depsgraph to properly check for dependency on - * time. */ - ModifierData *md = static_cast(object->modifiers.first); - while (md) { - if (md->type != eModifierType_Subsurf) { - return true; - } - md = md->next; - } - - return false; -} - -AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph) - : depsgraph_(depsgraph), writers_() -{ -} - -AbstractHierarchyIterator::~AbstractHierarchyIterator() -{ -} - -void AbstractHierarchyIterator::iterate_and_write() -{ - export_graph_construct(); - connect_loose_objects(); - export_graph_prune(); - determine_export_paths(HierarchyContext::root()); - determine_duplication_references(HierarchyContext::root(), ""); - make_writers(HierarchyContext::root()); - export_graph_clear(); -} - -void AbstractHierarchyIterator::release_writers() -{ - for (WriterMap::value_type it : writers_) { - delete_object_writer(it.second); - } - writers_.clear(); -} - -std::string AbstractHierarchyIterator::make_valid_name(const std::string &name) const -{ - return name; -} - -std::string AbstractHierarchyIterator::get_id_name(const ID *id) const -{ - if (id == nullptr) { - return ""; - } - - return make_valid_name(std::string(id->name + 2)); -} - -std::string AbstractHierarchyIterator::get_object_data_path(const HierarchyContext *context) const -{ - BLI_assert(!context->export_path.empty()); - BLI_assert(context->object->data); - - return path_concatenate(context->export_path, get_object_data_name(context->object)); -} - -void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &graph) const -{ - size_t total_graph_size = 0; - for (const ExportGraph::value_type &map_iter : graph) { - const DupliAndDuplicator &parent_info = map_iter.first; - Object *const export_parent = parent_info.first; - Object *const duplicator = parent_info.second; - - if (duplicator != nullptr) { - printf(" DU %s (as dupped by %s):\n", - export_parent == nullptr ? "-null-" : (export_parent->id.name + 2), - duplicator->id.name + 2); - } - else { - printf(" OB %s:\n", export_parent == nullptr ? "-null-" : (export_parent->id.name + 2)); - } - - total_graph_size += map_iter.second.size(); - for (HierarchyContext *child_ctx : map_iter.second) { - if (child_ctx->duplicator == nullptr) { - printf(" - %s%s%s\n", - child_ctx->object->id.name + 2, - child_ctx->weak_export ? " (weak)" : "", - child_ctx->original_export_path.size() ? - (std::string("ref ") + child_ctx->original_export_path).c_str() : - ""); - } - else { - printf(" - %s (dup by %s%s) %s\n", - child_ctx->object->id.name + 2, - child_ctx->duplicator->id.name + 2, - child_ctx->weak_export ? ", weak" : "", - child_ctx->original_export_path.size() ? - (std::string("ref ") + child_ctx->original_export_path).c_str() : - ""); - } - } - } - printf(" (Total graph size: %zu objects\n", total_graph_size); -} - -void AbstractHierarchyIterator::export_graph_construct() -{ - Scene *scene = DEG_get_evaluated_scene(depsgraph_); - - DEG_OBJECT_ITER_BEGIN (depsgraph_, - object, - DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | - DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) { - // Non-instanced objects always have their object-parent as export-parent. - const bool weak_export = mark_as_weak_export(object); - visit_object(object, object->parent, weak_export); - - if (weak_export) { - // If a duplicator shouldn't be exported, its duplilist also shouldn't be. - continue; - } - - // Export the duplicated objects instanced by this object. - ListBase *lb = object_duplilist(depsgraph_, scene, object); - if (lb) { - // Construct the set of duplicated objects, so that later we can determine whether a parent - // is also duplicated itself. - std::set dupli_set; - LISTBASE_FOREACH (DupliObject *, dupli_object, lb) { - if (!should_visit_dupli_object(dupli_object)) { - continue; - } - dupli_set.insert(dupli_object->ob); - } - - LISTBASE_FOREACH (DupliObject *, dupli_object, lb) { - if (!should_visit_dupli_object(dupli_object)) { - continue; - } - - visit_dupli_object(dupli_object, object, dupli_set); - } - } - - free_object_duplilist(lb); - } - DEG_OBJECT_ITER_END; -} - -void AbstractHierarchyIterator::connect_loose_objects() -{ - // Find those objects whose parent is not part of the export graph; these - // objects would be skipped when traversing the graph as a hierarchy. - // These objects will have to be re-attached to some parent object in order to - // fit into the hierarchy. - ExportGraph loose_objects_graph = export_graph_; - for (const ExportGraph::value_type &map_iter : export_graph_) { - for (const HierarchyContext *child : map_iter.second) { - // An object that is marked as a child of another object is not considered 'loose'. - loose_objects_graph.erase(std::make_pair(child->object, child->duplicator)); - } - } - // The root of the hierarchy is always found, so it's never considered 'loose'. - loose_objects_graph.erase(std::make_pair(nullptr, nullptr)); - - // Iterate over the loose objects and connect them to their export parent. - for (const ExportGraph::value_type &map_iter : loose_objects_graph) { - const DupliAndDuplicator &export_info = map_iter.first; - Object *object = export_info.first; - - while (true) { - // Loose objects will all be real objects, as duplicated objects always have - // their duplicator or other exported duplicated object as ancestor. - - ExportGraph::iterator found_parent_iter = export_graph_.find( - std::make_pair(object->parent, nullptr)); - visit_object(object, object->parent, true); - if (found_parent_iter != export_graph_.end()) { - break; - } - // 'object->parent' will never be nullptr here, as the export graph contains the - // tuple as root and thus will cause a break. - BLI_assert(object->parent != nullptr); - - object = object->parent; - } - } -} - -static bool remove_weak_subtrees(const HierarchyContext *context, - AbstractHierarchyIterator::ExportGraph &clean_graph, - const AbstractHierarchyIterator::ExportGraph &input_graph) -{ - bool all_is_weak = context != nullptr && context->weak_export; - Object *object = context != nullptr ? context->object : nullptr; - Object *duplicator = context != nullptr ? context->duplicator : nullptr; - - const AbstractHierarchyIterator::DupliAndDuplicator map_key = std::make_pair(object, duplicator); - AbstractHierarchyIterator::ExportGraph::const_iterator child_iterator; - - child_iterator = input_graph.find(map_key); - if (child_iterator != input_graph.end()) { - for (HierarchyContext *child_context : child_iterator->second) { - bool child_tree_is_weak = remove_weak_subtrees(child_context, clean_graph, input_graph); - all_is_weak &= child_tree_is_weak; - - if (child_tree_is_weak) { - // This subtree is all weak, so we can remove it from the current object's children. - clean_graph[map_key].erase(child_context); - delete child_context; - } - } - } - - if (all_is_weak) { - // This node and all its children are weak, so it can be removed from the export graph. - clean_graph.erase(map_key); - } - - return all_is_weak; -} - -void AbstractHierarchyIterator::export_graph_prune() -{ - // Take a copy of the map so that we can modify while recursing. - ExportGraph unpruned_export_graph = export_graph_; - remove_weak_subtrees(HierarchyContext::root(), export_graph_, unpruned_export_graph); -} - -void AbstractHierarchyIterator::export_graph_clear() -{ - for (ExportGraph::iterator::value_type &it : export_graph_) { - for (HierarchyContext *context : it.second) { - delete context; - } - } - export_graph_.clear(); -} - -void AbstractHierarchyIterator::visit_object(Object *object, - Object *export_parent, - bool weak_export) -{ - HierarchyContext *context = new HierarchyContext(); - context->object = object; - context->export_name = get_object_name(object); - context->export_parent = export_parent; - context->duplicator = nullptr; - context->weak_export = weak_export; - context->animation_check_include_parent = false; - context->export_path = ""; - context->original_export_path = ""; - copy_m4_m4(context->matrix_world, object->obmat); - - ExportGraph::key_type graph_index = determine_graph_index_object(context); - context_update_for_graph_index(context, graph_index); - - // Store this HierarchyContext as child of the export parent. - export_graph_[graph_index].insert(context); - - // Create an empty entry for this object to indicate it is part of the export. This will be used - // by connect_loose_objects(). Having such an "indicator" will make it possible to do an O(log n) - // check on whether an object is part of the export, rather than having to check all objects in - // the map. Note that it's not possible to simply search for (object->parent, nullptr), as the - // object's parent in Blender may not be the same as its export-parent. - ExportGraph::key_type object_key = std::make_pair(object, nullptr); - if (export_graph_.find(object_key) == export_graph_.end()) { - export_graph_[object_key] = ExportChildren(); - } -} - -AbstractHierarchyIterator::ExportGraph::key_type AbstractHierarchyIterator:: - determine_graph_index_object(const HierarchyContext *context) -{ - return std::make_pair(context->export_parent, nullptr); -} - -void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object, - Object *duplicator, - const std::set &dupli_set) -{ - HierarchyContext *context = new HierarchyContext(); - context->object = dupli_object->ob; - context->duplicator = duplicator; - context->weak_export = false; - context->export_path = ""; - context->original_export_path = ""; - context->export_path = ""; - context->animation_check_include_parent = false; - - copy_m4_m4(context->matrix_world, dupli_object->mat); - - // Construct export name for the dupli-instance. - std::stringstream suffix_stream; - suffix_stream << std::hex; - for (int i = 0; i < MAX_DUPLI_RECUR && dupli_object->persistent_id[i] != INT_MAX; i++) { - suffix_stream << "-" << dupli_object->persistent_id[i]; - } - context->export_name = make_valid_name(get_object_name(context->object) + suffix_stream.str()); - - ExportGraph::key_type graph_index = determine_graph_index_dupli(context, dupli_set); - context_update_for_graph_index(context, graph_index); - export_graph_[graph_index].insert(context); -} - -AbstractHierarchyIterator::ExportGraph::key_type AbstractHierarchyIterator:: - determine_graph_index_dupli(const HierarchyContext *context, - const std::set &dupli_set) -{ - /* If the dupli-object's parent is also instanced by this object, use that as the - * export parent. Otherwise use the dupli-parent as export parent. */ - - Object *parent = context->object->parent; - if (parent != nullptr && dupli_set.find(parent) != dupli_set.end()) { - // The parent object is part of the duplicated collection. - return std::make_pair(parent, context->duplicator); - } - return std::make_pair(context->duplicator, nullptr); -} - -void AbstractHierarchyIterator::context_update_for_graph_index( - HierarchyContext *context, const ExportGraph::key_type &graph_index) const -{ - // Update the HierarchyContext so that it is consistent with the graph index. - context->export_parent = graph_index.first; - if (context->export_parent != context->object->parent) { - /* The parent object in Blender is NOT used as the export parent. This means - * that the world transform of this object can be influenced by objects that - * are not part of its export graph. */ - context->animation_check_include_parent = true; - } -} - -AbstractHierarchyIterator::ExportChildren &AbstractHierarchyIterator::graph_children( - const HierarchyContext *context) -{ - if (context == nullptr) { - return export_graph_[std::make_pair(nullptr, nullptr)]; - } - - return export_graph_[std::make_pair(context->object, context->duplicator)]; -} - -void AbstractHierarchyIterator::determine_export_paths(const HierarchyContext *parent_context) -{ - const std::string &parent_export_path = parent_context ? parent_context->export_path : ""; - - for (HierarchyContext *context : graph_children(parent_context)) { - context->export_path = path_concatenate(parent_export_path, context->export_name); - - if (context->duplicator == nullptr) { - /* This is an original (i.e. non-instanced) object, so we should keep track of where it was - * exported to, just in case it gets instanced somewhere. */ - ID *source_ob = &context->object->id; - duplisource_export_path_[source_ob] = context->export_path; - - if (context->object->data != nullptr) { - ID *source_data = static_cast(context->object->data); - duplisource_export_path_[source_data] = get_object_data_path(context); - } - } - - determine_export_paths(context); - } -} - -void AbstractHierarchyIterator::determine_duplication_references( - const HierarchyContext *parent_context, std::string indent) -{ - ExportChildren children = graph_children(parent_context); - - for (HierarchyContext *context : children) { - if (context->duplicator != nullptr) { - ID *source_id = &context->object->id; - const ExportPathMap::const_iterator &it = duplisource_export_path_.find(source_id); - - if (it == duplisource_export_path_.end()) { - // The original was not found, so mark this instance as "the original". - context->mark_as_not_instanced(); - duplisource_export_path_[source_id] = context->export_path; - } - else { - context->mark_as_instance_of(it->second); - } - - if (context->object->data) { - ID *source_data_id = (ID *)context->object->data; - const ExportPathMap::const_iterator &it = duplisource_export_path_.find(source_data_id); - - if (it == duplisource_export_path_.end()) { - // The original was not found, so mark this instance as "original". - std::string data_path = get_object_data_path(context); - context->mark_as_not_instanced(); - duplisource_export_path_[source_id] = context->export_path; - duplisource_export_path_[source_data_id] = data_path; - } - } - } - - determine_duplication_references(context, indent + " "); - } -} - -void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_context) -{ - AbstractHierarchyWriter *transform_writer = nullptr; - float parent_matrix_inv_world[4][4]; - - if (parent_context) { - invert_m4_m4(parent_matrix_inv_world, parent_context->matrix_world); - } - else { - unit_m4(parent_matrix_inv_world); - } - - for (HierarchyContext *context : graph_children(parent_context)) { - // Update the context so that it is correct for this parent-child relation. - copy_m4_m4(context->parent_matrix_inv_world, parent_matrix_inv_world); - - // Get or create the transform writer. - transform_writer = ensure_writer(context, &AbstractHierarchyIterator::create_transform_writer); - if (transform_writer == nullptr) { - // Unable to export, so there is nothing to attach any children to; just abort this entire - // branch of the export hierarchy. - return; - } - - BLI_assert(DEG_is_evaluated_object(context->object)); - /* XXX This can lead to too many XForms being written. For example, a camera writer can refuse - * to write an orthographic camera. By the time that this is known, the XForm has already been - * written. */ - transform_writer->write(*context); - - if (!context->weak_export) { - make_writers_particle_systems(context); - make_writer_object_data(context); - } - - // Recurse into this object's children. - make_writers(context); - } - - // TODO(Sybren): iterate over all unused writers and call unused_during_iteration() or something. -} - -void AbstractHierarchyIterator::make_writer_object_data(const HierarchyContext *context) -{ - if (context->object->data == nullptr) { - return; - } - - HierarchyContext data_context = *context; - data_context.export_path = get_object_data_path(context); - - /* data_context.original_export_path is just a copy from the context. It points to the object, - * but needs to point to the object data. */ - if (data_context.is_instance()) { - ID *object_data = static_cast(context->object->data); - data_context.original_export_path = duplisource_export_path_[object_data]; - - /* If the object is marked as an instance, so should the object data. */ - BLI_assert(data_context.is_instance()); - } - - AbstractHierarchyWriter *data_writer; - data_writer = ensure_writer(&data_context, &AbstractHierarchyIterator::create_data_writer); - if (data_writer == nullptr) { - return; - } - - data_writer->write(data_context); -} - -void AbstractHierarchyIterator::make_writers_particle_systems( - const HierarchyContext *transform_context) -{ - Object *object = transform_context->object; - ParticleSystem *psys = static_cast(object->particlesystem.first); - for (; psys; psys = psys->next) { - if (!psys_check_enabled(object, psys, true)) { - continue; - } - - HierarchyContext hair_context = *transform_context; - hair_context.export_path = path_concatenate(transform_context->export_path, - make_valid_name(psys->name)); - hair_context.particle_system = psys; - - AbstractHierarchyWriter *writer = nullptr; - switch (psys->part->type) { - case PART_HAIR: - writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_hair_writer); - break; - case PART_EMITTER: - writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_particle_writer); - break; - } - - if (writer != nullptr) { - writer->write(hair_context); - } - } -} - -std::string AbstractHierarchyIterator::get_object_name(const Object *object) const -{ - return get_id_name(&object->id); -} - -std::string AbstractHierarchyIterator::get_object_data_name(const Object *object) const -{ - ID *object_data = static_cast(object->data); - return get_id_name(object_data); -} - -AbstractHierarchyWriter *AbstractHierarchyIterator::get_writer( - const std::string &export_path) const -{ - WriterMap::const_iterator it = writers_.find(export_path); - - if (it == writers_.end()) { - return nullptr; - } - return it->second; -} - -AbstractHierarchyWriter *AbstractHierarchyIterator::ensure_writer( - HierarchyContext *context, AbstractHierarchyIterator::create_writer_func create_func) -{ - AbstractHierarchyWriter *writer = get_writer(context->export_path); - if (writer != nullptr) { - return writer; - } - - writer = (this->*create_func)(context); - if (writer == nullptr) { - return nullptr; - } - - writers_[context->export_path] = writer; - - return writer; -} - -std::string AbstractHierarchyIterator::path_concatenate(const std::string &parent_path, - const std::string &child_path) const -{ - return parent_path + "/" + child_path; -} - -bool AbstractHierarchyIterator::mark_as_weak_export(const Object * /*object*/) const -{ - return false; -} -bool AbstractHierarchyIterator::should_visit_dupli_object(const DupliObject *dupli_object) const -{ - // Removing dupli_object->no_draw hides things like custom bone shapes. - return !dupli_object->no_draw; -} - -} // namespace USD diff --git a/source/blender/io/usd/intern/abstract_hierarchy_iterator.h b/source/blender/io/usd/intern/abstract_hierarchy_iterator.h deleted file mode 100644 index e31d5c91252..00000000000 --- a/source/blender/io/usd/intern/abstract_hierarchy_iterator.h +++ /dev/null @@ -1,263 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ - -/* - * This file contains the AbstractHierarchyIterator. It is intended for exporters for file - * formats that concern an entire hierarchy of objects (rather than, for example, an OBJ file that - * contains only a single mesh). Examples are Universal Scene Description (USD) and Alembic. - * AbstractHierarchyIterator is intended to be subclassed to support concrete file formats. - * - * The AbstractHierarchyIterator makes a distinction between the actual object hierarchy and the - * export hierarchy. The former is the parent/child structure in Blender, which can have multiple - * parent-like objects. For example, a duplicated object can have both a duplicator and a parent, - * both determining the final transform. The export hierarchy is the hierarchy as written to the - * file, and every object has only one export-parent. - * - * Currently the AbstractHierarchyIterator does not make any decisions about *what* to export. - * Selections like "selected only" or "no hair systems" are left to concrete subclasses. - */ - -#ifndef __ABSTRACT_HIERARCHY_ITERATOR_H__ -#define __ABSTRACT_HIERARCHY_ITERATOR_H__ - -#include -#include -#include - -struct Base; -struct Depsgraph; -struct DupliObject; -struct ID; -struct Object; -struct ParticleSystem; -struct ViewLayer; - -namespace USD { - -class AbstractHierarchyWriter; - -/* HierarchyContext structs are created by the AbstractHierarchyIterator. Each HierarchyContext - * struct contains everything necessary to export a single object to a file. */ -struct HierarchyContext { - /*********** Determined during hierarchy iteration: ***************/ - Object *object; /* Evaluated object. */ - Object *export_parent; - Object *duplicator; - float matrix_world[4][4]; - std::string export_name; - - /* When weak_export=true, the object will be exported only as transform, and only if is an - * ancestor of an object with weak_export=false. - * - * In other words: when weak_export=true but this object has no children, or all descendants also - * have weak_export=true, this object (and by recursive reasoning all its descendants) will be - * excluded from the export. - * - * The export hierarchy is kept as close to the hierarchy in Blender as possible. As such, an - * object that serves as a parent for another object, but which should NOT be exported itself, is - * exported only as transform (i.e. as empty). This happens with objects that are part of a - * holdout collection (which prevents them from being exported) but also parent of an exported - * object. */ - bool weak_export; - - /* When true, this object should check its parents for animation data when determining whether - * it's animated. This is necessary when a parent object in Blender is not part of the export. */ - bool animation_check_include_parent; - - /*********** Determined during writer creation: ***************/ - float parent_matrix_inv_world[4][4]; // Inverse of the parent's world matrix. - std::string export_path; // Hierarchical path, such as "/grandparent/parent/objectname". - ParticleSystem *particle_system; // Only set for particle/hair writers. - - /* Hierarchical path of the object this object is duplicating; only set when this object should - * be stored as a reference to its original. It can happen that the original is not part of the - * exported objects, in which case this string is empty even though 'duplicator' is set. */ - std::string original_export_path; - - bool operator<(const HierarchyContext &other) const; - - /* Return a HierarchyContext representing the root of the export hierarchy. */ - static const HierarchyContext *root(); - - /* For handling instanced collections, instances created by particles, etc. */ - bool is_instance() const; - void mark_as_instance_of(const std::string &reference_export_path); - void mark_as_not_instanced(); -}; - -/* Abstract writer for objects. Create concrete subclasses to write to USD, Alembic, etc. - * - * Instantiated by the AbstractHierarchyIterator on the first frame an object exists. Generally - * that's the first frame to be exported, but can be later, for example when objects are - * instantiated by particles. The AbstractHierarchyWriter::write() function is called on every - * frame the object exists in the dependency graph and should be exported. - */ -class AbstractHierarchyWriter { - public: - virtual ~AbstractHierarchyWriter(); - virtual void write(HierarchyContext &context) = 0; - // TODO(Sybren): add function like absent() that's called when a writer was previously created, - // but wasn't used while exporting the current frame (for example, a particle-instanced mesh of - // which the particle is no longer alive). - protected: - virtual bool check_is_animated(const HierarchyContext &context) const; -}; - -/* AbstractHierarchyIterator iterates over objects in a dependency graph, and constructs export - * writers. These writers are then called to perform the actual writing to a USD or Alembic file. - * - * Dealing with file- and scene-level data (for example, creating a USD scene, setting the frame - * rate, etc.) is not part of the AbstractHierarchyIterator class structure, and should be done - * in separate code. - */ -class AbstractHierarchyIterator { - public: - /* Mapping from export path to writer. */ - typedef std::map WriterMap; - /* Pair of a (potentially duplicated) object and its duplicator (or nullptr). - * This is typically used to store a pair of HierarchyContext::object and - * HierarchyContext::duplicator. */ - typedef std::pair DupliAndDuplicator; - /* All the children of some object, as per the export hierarchy. */ - typedef std::set ExportChildren; - /* Mapping from an object and its duplicator to the object's export-children. */ - typedef std::map ExportGraph; - /* Mapping from ID to its export path. This is used for instancing; given an - * instanced datablock, the export path of the original can be looked up. */ - typedef std::map ExportPathMap; - - protected: - ExportGraph export_graph_; - ExportPathMap duplisource_export_path_; - Depsgraph *depsgraph_; - WriterMap writers_; - - public: - explicit AbstractHierarchyIterator(Depsgraph *depsgraph); - virtual ~AbstractHierarchyIterator(); - - /* Iterate over the depsgraph, create writers, and tell the writers to write. - * Main entry point for the AbstractHierarchyIterator, must be called for every to-be-exported - * frame. */ - void iterate_and_write(); - - /* Release all writers. Call after all frames have been exported. */ - void release_writers(); - - /* Convert the given name to something that is valid for the exported file format. - * This base implementation is a no-op; override in a concrete subclass. */ - virtual std::string make_valid_name(const std::string &name) const; - - /* Return the name of this ID datablock that is valid for the exported file format. Overriding is - * only necessary if make_valid_name(id->name+2) is not suitable for the exported file format. - * NULL-safe: when `id == nullptr` this returns an empty string. */ - virtual std::string get_id_name(const ID *id) const; - - /* Given a HierarchyContext of some Object *, return an export path that is valid for its - * object->data. Overriding is necessary when the exported format does NOT expect the object's - * data to be a child of the object. */ - virtual std::string get_object_data_path(const HierarchyContext *context) const; - - private: - void debug_print_export_graph(const ExportGraph &graph) const; - - void export_graph_construct(); - void connect_loose_objects(); - void export_graph_prune(); - void export_graph_clear(); - - void visit_object(Object *object, Object *export_parent, bool weak_export); - void visit_dupli_object(DupliObject *dupli_object, - Object *duplicator, - const std::set &dupli_set); - - ExportChildren &graph_children(const HierarchyContext *parent_context); - void context_update_for_graph_index(HierarchyContext *context, - const ExportGraph::key_type &graph_index) const; - - void determine_export_paths(const HierarchyContext *parent_context); - void determine_duplication_references(const HierarchyContext *parent_context, - std::string indent); - - /* These three functions create writers and call their write() method. */ - void make_writers(const HierarchyContext *parent_context); - void make_writer_object_data(const HierarchyContext *context); - void make_writers_particle_systems(const HierarchyContext *context); - - /* Convenience wrappers around get_id_name(). */ - std::string get_object_name(const Object *object) const; - std::string get_object_data_name(const Object *object) const; - - AbstractHierarchyWriter *get_writer(const std::string &export_path) const; - - typedef AbstractHierarchyWriter *(AbstractHierarchyIterator::*create_writer_func)( - const HierarchyContext *); - /* Ensure that a writer exists; if it doesn't, call create_func(context). The create_func - * function should be one of the create_XXXX_writer(context) functions declared below. */ - AbstractHierarchyWriter *ensure_writer(HierarchyContext *context, - create_writer_func create_func); - - protected: - /* Construct a valid path for the export file format. This class concatenates by using '/' as a - * path separator, which is valid for both Alembic and USD. */ - virtual std::string path_concatenate(const std::string &parent_path, - const std::string &child_path) const; - - /* Return whether this object should be marked as 'weak export' or not. - * - * When this returns false, writers for the transform and data are created, - * and dupli-objects dupli-object generated from this object will be passed to - * should_visit_dupli_object(). - * - * When this returns true, only a transform writer is created and marked as - * 'weak export'. In this case, the transform writer will be removed before - * exporting starts, unless a descendant of this object is to be exported. - * Dupli-object generated from this object will also be skipped. - * - * See HierarchyContext::weak_export. - */ - virtual bool mark_as_weak_export(const Object *object) const; - - virtual bool should_visit_dupli_object(const DupliObject *dupli_object) const; - - virtual ExportGraph::key_type determine_graph_index_object(const HierarchyContext *context); - virtual ExportGraph::key_type determine_graph_index_dupli(const HierarchyContext *context, - const std::set &dupli_set); - - /* These functions should create an AbstractHierarchyWriter subclass instance, or return - * nullptr if the object or its data should not be exported. Returning a nullptr for - * data/hair/particle will NOT prevent the transform to be written. - * - * The returned writer is owned by the AbstractHierarchyWriter, and should be freed in - * delete_object_writer(). - * - * The created AbstractHierarchyWriter instances should NOT keep a copy of the context pointer. - * The context can be stack-allocated and go out of scope. */ - virtual AbstractHierarchyWriter *create_transform_writer(const HierarchyContext *context) = 0; - virtual AbstractHierarchyWriter *create_data_writer(const HierarchyContext *context) = 0; - virtual AbstractHierarchyWriter *create_hair_writer(const HierarchyContext *context) = 0; - virtual AbstractHierarchyWriter *create_particle_writer(const HierarchyContext *context) = 0; - - /* Called by release_writers() to free what the create_XXX_writer() functions allocated. */ - virtual void delete_object_writer(AbstractHierarchyWriter *writer) = 0; -}; - -} // namespace USD - -#endif /* __ABSTRACT_HIERARCHY_ITERATOR_H__ */ diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.h b/source/blender/io/usd/intern/usd_hierarchy_iterator.h index a608012a390..c61a0d2bf07 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.h +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.h @@ -19,7 +19,7 @@ #ifndef __USD_HIERARCHY_ITERATOR_H__ #define __USD_HIERARCHY_ITERATOR_H__ -#include "abstract_hierarchy_iterator.h" +#include "IO_abstract_hierarchy_iterator.h" #include "usd.h" #include "usd_exporter_context.h" @@ -34,6 +34,10 @@ struct Object; namespace USD { +using blender::io::AbstractHierarchyIterator; +using blender::io::AbstractHierarchyWriter; +using blender::io::HierarchyContext; + class USDHierarchyIterator : public AbstractHierarchyIterator { private: const pxr::UsdStageRefPtr stage_; diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h index 01b53f4c916..03c4c263e32 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.h +++ b/source/blender/io/usd/intern/usd_writer_abstract.h @@ -19,7 +19,7 @@ #ifndef __USD_WRITER_ABSTRACT_H__ #define __USD_WRITER_ABSTRACT_H__ -#include "abstract_hierarchy_iterator.h" +#include "IO_abstract_hierarchy_iterator.h" #include "usd_exporter_context.h" #include @@ -38,6 +38,9 @@ struct Object; namespace USD { +using blender::io::AbstractHierarchyWriter; +using blender::io::HierarchyContext; + class USDAbstractWriter : public AbstractHierarchyWriter { protected: const USDExporterContext usd_export_context_; -- cgit v1.2.3 From 69c3d9804f03f637618e71b296aea279b27577f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 8 May 2020 14:14:49 +0200 Subject: IO: Allow exporting a subset of the writers This is in order to prepare for compatibility with the Alembic exporter. That exporter is capable of writing object transforms and object data at different (sub)frames. The rename from `created_writers` to `used_writers` is necessary, as not all created writers will be actually used in each iteration. The Universal Scene Description (USD) exporter does not make use of this. Reviewed By: mont29 Differential Revision: https://developer.blender.org/D7670 --- .../io/common/IO_abstract_hierarchy_iterator.h | 52 ++++++++++++- .../common/intern/abstract_hierarchy_iterator.cc | 89 +++++++++++++++++----- 2 files changed, 118 insertions(+), 23 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h index 480b72ea0cf..9930b79f802 100644 --- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -120,6 +120,39 @@ class AbstractHierarchyWriter { virtual bool check_is_animated(const HierarchyContext &context) const; }; +/* Determines which subset of the writers actually gets to write. */ +struct ExportSubset { + bool transforms : 1; + bool shapes : 1; +}; + +/* EnsuredWriter represents an AbstractHierarchyWriter* combined with information whether it was + * newly created or not. It's returned by AbstractHierarchyIterator::ensure_writer(). */ +class EnsuredWriter { + private: + AbstractHierarchyWriter *writer_; + + /* Is set to truth when ensure_writer() did not find existing writer and created a new one. + * Is set to false when writer has been re-used or when allocation of the new one has failed + * (`writer` will be `nullptr` in that case and bool(ensured_writer) will be false). */ + bool newly_created_; + + EnsuredWriter(AbstractHierarchyWriter *writer, bool newly_created); + + public: + EnsuredWriter(); + + static EnsuredWriter empty(); + static EnsuredWriter existing(AbstractHierarchyWriter *writer); + static EnsuredWriter newly_created(AbstractHierarchyWriter *writer); + + bool is_newly_created() const; + + /* These operators make an EnsuredWriter* act as an AbstractHierarchyWriter* */ + operator bool() const; + AbstractHierarchyWriter *operator->(); +}; + /* AbstractHierarchyIterator iterates over objects in a dependency graph, and constructs export * writers. These writers are then called to perform the actual writing to a USD or Alembic file. * @@ -148,6 +181,7 @@ class AbstractHierarchyIterator { ExportPathMap duplisource_export_path_; Depsgraph *depsgraph_; WriterMap writers_; + ExportSubset export_subset_; public: explicit AbstractHierarchyIterator(Depsgraph *depsgraph); @@ -161,6 +195,15 @@ class AbstractHierarchyIterator { /* Release all writers. Call after all frames have been exported. */ void release_writers(); + /* Determine which subset of writers is used for exporting. + * Set this before calling iterate_and_write(). + * + * Note that writers are created for each iterated object, regardless of this option. When a + * writer is created it will also write the current iteration, to ensure the hierarchy is + * complete. The `export_subset` option is only in effect when the writer already existed from a + * previous iteration. */ + void set_export_subset(ExportSubset export_subset_); + /* Convert the given name to something that is valid for the exported file format. * This base implementation is a no-op; override in a concrete subclass. */ virtual std::string make_valid_name(const std::string &name) const; @@ -209,10 +252,11 @@ class AbstractHierarchyIterator { typedef AbstractHierarchyWriter *(AbstractHierarchyIterator::*create_writer_func)( const HierarchyContext *); - /* Ensure that a writer exists; if it doesn't, call create_func(context). The create_func - * function should be one of the create_XXXX_writer(context) functions declared below. */ - AbstractHierarchyWriter *ensure_writer(HierarchyContext *context, - create_writer_func create_func); + /* Ensure that a writer exists; if it doesn't, call create_func(context). + * + * The create_func function should be one of the create_XXXX_writer(context) functions declared + * below. */ + EnsuredWriter ensure_writer(HierarchyContext *context, create_writer_func create_func); protected: /* Construct a valid path for the export file format. This class concatenates by using '/' as a diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc index 5a318485203..053b22970dc 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc @@ -75,6 +75,43 @@ void HierarchyContext::mark_as_not_instanced() original_export_path.clear(); } +EnsuredWriter::EnsuredWriter() : writer_(nullptr), newly_created_(false) +{ +} + +EnsuredWriter::EnsuredWriter(AbstractHierarchyWriter *writer, bool newly_created) + : writer_(writer), newly_created_(newly_created) +{ +} + +EnsuredWriter EnsuredWriter::empty() +{ + return EnsuredWriter(nullptr, false); +} +EnsuredWriter EnsuredWriter::existing(AbstractHierarchyWriter *writer) +{ + return EnsuredWriter(writer, false); +} +EnsuredWriter EnsuredWriter::newly_created(AbstractHierarchyWriter *writer) +{ + return EnsuredWriter(writer, true); +} + +bool EnsuredWriter::is_newly_created() const +{ + return newly_created_; +} + +EnsuredWriter::operator bool() const +{ + return writer_ != nullptr; +} + +AbstractHierarchyWriter *EnsuredWriter::operator->() +{ + return writer_; +} + AbstractHierarchyWriter::~AbstractHierarchyWriter() { } @@ -105,7 +142,7 @@ bool AbstractHierarchyWriter::check_is_animated(const HierarchyContext &context) } AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph) - : depsgraph_(depsgraph), writers_() + : depsgraph_(depsgraph), writers_(), export_subset_({true, true}) { } @@ -132,6 +169,11 @@ void AbstractHierarchyIterator::release_writers() writers_.clear(); } +void AbstractHierarchyIterator::set_export_subset(ExportSubset export_subset) +{ + export_subset_ = export_subset; +} + std::string AbstractHierarchyIterator::make_valid_name(const std::string &name) const { return name; @@ -495,7 +537,6 @@ void AbstractHierarchyIterator::determine_duplication_references( void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_context) { - AbstractHierarchyWriter *transform_writer = nullptr; float parent_matrix_inv_world[4][4]; if (parent_context) { @@ -510,18 +551,22 @@ void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_cont copy_m4_m4(context->parent_matrix_inv_world, parent_matrix_inv_world); // Get or create the transform writer. - transform_writer = ensure_writer(context, &AbstractHierarchyIterator::create_transform_writer); - if (transform_writer == nullptr) { + EnsuredWriter transform_writer = ensure_writer( + context, &AbstractHierarchyIterator::create_transform_writer); + + if (!transform_writer) { // Unable to export, so there is nothing to attach any children to; just abort this entire // branch of the export hierarchy. return; } BLI_assert(DEG_is_evaluated_object(context->object)); - /* XXX This can lead to too many XForms being written. For example, a camera writer can refuse - * to write an orthographic camera. By the time that this is known, the XForm has already been - * written. */ - transform_writer->write(*context); + if (transform_writer.is_newly_created() || export_subset_.transforms) { + /* XXX This can lead to too many XForms being written. For example, a camera writer can + * refuse to write an orthographic camera. By the time that this is known, the XForm has + * already been written. */ + transform_writer->write(*context); + } if (!context->weak_export) { make_writers_particle_systems(context); @@ -554,13 +599,16 @@ void AbstractHierarchyIterator::make_writer_object_data(const HierarchyContext * BLI_assert(data_context.is_instance()); } - AbstractHierarchyWriter *data_writer; - data_writer = ensure_writer(&data_context, &AbstractHierarchyIterator::create_data_writer); - if (data_writer == nullptr) { + /* Always write upon creation, otherwise depend on which subset is active. */ + EnsuredWriter data_writer = ensure_writer(&data_context, + &AbstractHierarchyIterator::create_data_writer); + if (!data_writer) { return; } - data_writer->write(data_context); + if (data_writer.is_newly_created() || export_subset_.shapes) { + data_writer->write(data_context); + } } void AbstractHierarchyIterator::make_writers_particle_systems( @@ -578,7 +626,7 @@ void AbstractHierarchyIterator::make_writers_particle_systems( make_valid_name(psys->name)); hair_context.particle_system = psys; - AbstractHierarchyWriter *writer = nullptr; + EnsuredWriter writer; switch (psys->part->type) { case PART_HAIR: writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_hair_writer); @@ -587,8 +635,12 @@ void AbstractHierarchyIterator::make_writers_particle_systems( writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_particle_writer); break; } + if (!writer) { + continue; + } - if (writer != nullptr) { + /* Always write upon creation, otherwise depend on which subset is active. */ + if (writer.is_newly_created() || export_subset_.shapes) { writer->write(hair_context); } } @@ -616,22 +668,21 @@ AbstractHierarchyWriter *AbstractHierarchyIterator::get_writer( return it->second; } -AbstractHierarchyWriter *AbstractHierarchyIterator::ensure_writer( +EnsuredWriter AbstractHierarchyIterator::ensure_writer( HierarchyContext *context, AbstractHierarchyIterator::create_writer_func create_func) { AbstractHierarchyWriter *writer = get_writer(context->export_path); if (writer != nullptr) { - return writer; + return EnsuredWriter::existing(writer); } writer = (this->*create_func)(context); if (writer == nullptr) { - return nullptr; + return EnsuredWriter::empty(); } writers_[context->export_path] = writer; - - return writer; + return EnsuredWriter::newly_created(writer); } std::string AbstractHierarchyIterator::path_concatenate(const std::string &parent_path, -- cgit v1.2.3 From 0ae7883d7d34bb2553407a028e9510e407a36d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 8 May 2020 15:51:23 +0200 Subject: IO: ensure export path and export name are always consistent Before this, there was one code path that set `context.export_path`, and a different code path for `context.export_name`, allowing the two to diverge. Keeping track of the export path of the export parent (which can be, but is not always, the Blender parent object) also allows a concrete subclass of `AbstractHierarchyIterator` to find the `AbstractWriter` for the export parent. In case of exporting to Alembic this is important, as it's not possible to simply give the Alembic library the full export path of an object like we do in the Universal Scene Description (USD) exporter; Alembic needs the C++ object of the parent. --- .../io/common/IO_abstract_hierarchy_iterator.h | 12 ++++++++++ .../common/intern/abstract_hierarchy_iterator.cc | 26 +++++++++++++++++----- 2 files changed, 32 insertions(+), 6 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h index 9930b79f802..8cac3f4c72d 100644 --- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -91,6 +91,14 @@ struct HierarchyContext { * exported objects, in which case this string is empty even though 'duplicator' is set. */ std::string original_export_path; + /* Export path of the higher-up exported data. For transforms, this is the export path of the + * parent object. For object data, this is the export path of that object's transform. + * + * From the exported file's point of view, this is the path to the parent in that file. The term + * "parent" is not used here to avoid confusion with Blender's meaning of the word (which always + * refers to a different object). */ + std::string higher_up_export_path; + bool operator<(const HierarchyContext &other) const; /* Return a HierarchyContext representing the root of the export hierarchy. */ @@ -244,6 +252,10 @@ class AbstractHierarchyIterator { void make_writer_object_data(const HierarchyContext *context); void make_writers_particle_systems(const HierarchyContext *context); + /* Return the appropriate HierarchyContext for the data of the object represented by + * object_context. */ + HierarchyContext context_for_object_data(const HierarchyContext *object_context) const; + /* Convenience wrappers around get_id_name(). */ std::string get_object_name(const Object *object) const; std::string get_object_data_name(const Object *object) const; diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc index 053b22970dc..9a456bfaf69 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc @@ -384,6 +384,8 @@ void AbstractHierarchyIterator::visit_object(Object *object, context->animation_check_include_parent = false; context->export_path = ""; context->original_export_path = ""; + context->higher_up_export_path = ""; + copy_m4_m4(context->matrix_world, object->obmat); ExportGraph::key_type graph_index = determine_graph_index_object(context); @@ -549,6 +551,9 @@ void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_cont for (HierarchyContext *context : graph_children(parent_context)) { // Update the context so that it is correct for this parent-child relation. copy_m4_m4(context->parent_matrix_inv_world, parent_matrix_inv_world); + if (parent_context != nullptr) { + context->higher_up_export_path = parent_context->export_path; + } // Get or create the transform writer. EnsuredWriter transform_writer = ensure_writer( @@ -580,17 +585,24 @@ void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_cont // TODO(Sybren): iterate over all unused writers and call unused_during_iteration() or something. } +HierarchyContext AbstractHierarchyIterator::context_for_object_data( + const HierarchyContext *object_context) const +{ + HierarchyContext data_context = *object_context; + data_context.higher_up_export_path = object_context->export_path; + data_context.export_name = get_object_data_name(data_context.object); + data_context.export_path = path_concatenate(data_context.higher_up_export_path, + data_context.export_name); + return data_context; +} + void AbstractHierarchyIterator::make_writer_object_data(const HierarchyContext *context) { if (context->object->data == nullptr) { return; } - HierarchyContext data_context = *context; - data_context.export_path = get_object_data_path(context); - - /* data_context.original_export_path is just a copy from the context. It points to the object, - * but needs to point to the object data. */ + HierarchyContext data_context = context_for_object_data(context); if (data_context.is_instance()) { ID *object_data = static_cast(context->object->data); data_context.original_export_path = duplisource_export_path_[object_data]; @@ -622,8 +634,10 @@ void AbstractHierarchyIterator::make_writers_particle_systems( } HierarchyContext hair_context = *transform_context; + hair_context.export_name = make_valid_name(psys->name); hair_context.export_path = path_concatenate(transform_context->export_path, - make_valid_name(psys->name)); + hair_context.export_name); + hair_context.higher_up_export_path = transform_context->export_path; hair_context.particle_system = psys; EnsuredWriter writer; -- cgit v1.2.3 From 0d744cf673e893bd1e44fa7fd91e916935a3ff45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 18 Jun 2020 18:10:19 +0200 Subject: Alembic: export object data with object data name Previously the Alembic exporter exported a mesh object to `{object.name}/{object.name}Shape`. Now it exports to `{object.name}/{mesh.name}` instead. The same change also applies to other object data types. Note that the code now is a bit hackish, as `m_name` is set even in cases where it isn't used. This hackishness was already there, though, but it's now just more visible. This will all be cleaned up when the Alembic exporter is ported to use the `AbstractHierarchyImporter` structure of the Universal Scene Description (USD) exporter. Reviewed By: mont29 Differential Revision: https://developer.blender.org/D7672 --- source/blender/io/alembic/intern/abc_writer_object.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/intern/abc_writer_object.cc b/source/blender/io/alembic/intern/abc_writer_object.cc index f4a3587f54d..3d280d9f0a4 100644 --- a/source/blender/io/alembic/intern/abc_writer_object.cc +++ b/source/blender/io/alembic/intern/abc_writer_object.cc @@ -30,7 +30,17 @@ AbcObjectWriter::AbcObjectWriter(Object *ob, AbcObjectWriter *parent) : m_object(ob), m_settings(settings), m_time_sampling(time_sampling), m_first_frame(true) { - m_name = get_id_name(m_object) + "Shape"; + /* This class is used as superclass for objects themselves (i.e. transforms) and for object + * data (meshes, curves, cameras, etc.). However, when writing transforms, the m_name field is + * ignored. This is a temporary tweak to get the exporter to write object data with the data + * name instead of the object name in a safe way. */ + if (m_object->data == nullptr) { + m_name = get_id_name(m_object); + } + else { + ID *ob_data = static_cast(m_object->data); + m_name = get_id_name(ob_data); + } if (parent) { parent->addChild(this); -- cgit v1.2.3 From f106369ce8577aa9115fead1eff3acd34273a86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 8 May 2020 13:42:39 +0200 Subject: Alembic: prevent spaces in names of exported particle systems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Other types already had spaces, periods, and colons replaced by underscores. The upcoming Alembic exporter (based on the `AbstractHierarcyIterator` class) will be more consistent and apply the same naming rules everywhere. This is in preparation for that change. The `get_…_name()` functions in `abc_util.{cc,h}` will be removed then. --- source/blender/io/alembic/intern/abc_util.cc | 14 +++++++++----- source/blender/io/alembic/intern/abc_util.h | 1 + source/blender/io/alembic/intern/abc_writer_hair.cc | 3 ++- source/blender/io/alembic/intern/abc_writer_points.cc | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/intern/abc_util.cc b/source/blender/io/alembic/intern/abc_util.cc index 1f3bd2a1aaa..92f4b9532a1 100644 --- a/source/blender/io/alembic/intern/abc_util.cc +++ b/source/blender/io/alembic/intern/abc_util.cc @@ -49,12 +49,16 @@ std::string get_id_name(const Object *const ob) std::string get_id_name(const ID *const id) { - std::string name(id->name + 2); - std::replace(name.begin(), name.end(), ' ', '_'); - std::replace(name.begin(), name.end(), '.', '_'); - std::replace(name.begin(), name.end(), ':', '_'); + return get_valid_abc_name(id->name + 2); +} - return name; +std::string get_valid_abc_name(const char *name) +{ + std::string name_string(name); + std::replace(name_string.begin(), name_string.end(), ' ', '_'); + std::replace(name_string.begin(), name_string.end(), '.', '_'); + std::replace(name_string.begin(), name_string.end(), ':', '_'); + return name_string; } /** diff --git a/source/blender/io/alembic/intern/abc_util.h b/source/blender/io/alembic/intern/abc_util.h index 57b4d9800a5..8a5763bb38b 100644 --- a/source/blender/io/alembic/intern/abc_util.h +++ b/source/blender/io/alembic/intern/abc_util.h @@ -49,6 +49,7 @@ struct Object; std::string get_id_name(const ID *const id); std::string get_id_name(const Object *const ob); +std::string get_valid_abc_name(const char *name); std::string get_object_dag_path_name(const Object *const ob, Object *dupli_parent); /* Convert from float to Alembic matrix representations. Does NOT convert from Z-up to Y-up. */ diff --git a/source/blender/io/alembic/intern/abc_writer_hair.cc b/source/blender/io/alembic/intern/abc_writer_hair.cc index ed62889b03d..43c05627130 100644 --- a/source/blender/io/alembic/intern/abc_writer_hair.cc +++ b/source/blender/io/alembic/intern/abc_writer_hair.cc @@ -53,7 +53,8 @@ AbcHairWriter::AbcHairWriter(Object *ob, { m_psys = psys; - OCurves curves(parent->alembicXform(), psys->name, m_time_sampling); + std::string psys_name = get_valid_abc_name(psys->name); + OCurves curves(parent->alembicXform(), psys_name, m_time_sampling); m_schema = curves.getSchema(); } diff --git a/source/blender/io/alembic/intern/abc_writer_points.cc b/source/blender/io/alembic/intern/abc_writer_points.cc index ac663b62693..6be4bcfdbe7 100644 --- a/source/blender/io/alembic/intern/abc_writer_points.cc +++ b/source/blender/io/alembic/intern/abc_writer_points.cc @@ -51,7 +51,8 @@ AbcPointsWriter::AbcPointsWriter(Object *ob, { m_psys = psys; - OPoints points(parent->alembicXform(), psys->name, m_time_sampling); + std::string psys_name = get_valid_abc_name(psys->name); + OPoints points(parent->alembicXform(), psys_name, m_time_sampling); m_schema = points.getSchema(); } -- cgit v1.2.3 From 2dff08c8ce9b9ce7511328cbe107d26a9539c38a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 19 Jun 2020 10:24:57 +0200 Subject: Alembic: always export transforms as inheriting Blender now always exports transforms as as "inheriting", as Blender has no concept of parenting without inheriting the transform. Previously only objects with an actual parent were marked as "inheriting", and parentless objects as "non-inheriting". However, certain packages (for example USD's Alembic plugin) are incompatible with non-inheriting transforms and will completely ignore such transforms, placing all such objects at the world origin. When importing non-inheriting transforms from Alembic, Blender will break the parent-child relation and thus force the child to (correctly) interpret the transform as world matrix. --- source/blender/io/alembic/intern/abc_writer_transform.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/intern/abc_writer_transform.cc b/source/blender/io/alembic/intern/abc_writer_transform.cc index 1ec7db0a1c6..ef3e7e29db2 100644 --- a/source/blender/io/alembic/intern/abc_writer_transform.cc +++ b/source/blender/io/alembic/intern/abc_writer_transform.cc @@ -97,7 +97,12 @@ void AbcTransformWriter::do_write() m_matrix = convert_matrix_datatype(yup_mat); m_sample.setMatrix(m_matrix); - m_sample.setInheritsXforms(m_inherits_xform); + + /* Always export as "inherits transform", as this is the only way in which Blender works. The + * above code has already taken care of writing the correct matrix so that this option is not + * necessary. However, certain packages (for example the USD Alembic exporter) are incompatible + * with non-inheriting transforms and will completely ignore the transform if that is used. */ + m_sample.setInheritsXforms(true); m_schema.set(m_sample); } -- cgit v1.2.3 From 89b7f785e64275819bd7372f6b47f1cc9225078f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 19 Jun 2020 13:45:34 +0200 Subject: Cleanup: Alembic, moved exporter code into separate directory This moves most of the exporter-related code from `source/blender/io/alembic/intern` to `source/blender/io/alembic/exporter` This is to prepare the Alembic code for the switchover to using `blender::io::AbstractHierarchyIterator`. When that happens, a few more files will be added, and having things in a separate 'exporter' directory makes things less cluttered. Note that exporting consists of multiple steps (determine export hierarchy, create Alembic archive, and then write data into it), which is why the directory is called "exporter", but many of the files are called "writer". No functional changes. --- source/blender/io/alembic/CMakeLists.txt | 47 +- .../blender/io/alembic/exporter/abc_export_capi.cc | 239 ++++++++ source/blender/io/alembic/exporter/abc_exporter.cc | 673 +++++++++++++++++++++ source/blender/io/alembic/exporter/abc_exporter.h | 127 ++++ .../io/alembic/exporter/abc_writer_archive.cc | 98 +++ .../io/alembic/exporter/abc_writer_archive.h | 50 ++ .../io/alembic/exporter/abc_writer_camera.cc | 79 +++ .../io/alembic/exporter/abc_writer_camera.h | 45 ++ .../io/alembic/exporter/abc_writer_curves.cc | 188 ++++++ .../io/alembic/exporter/abc_writer_curves.h | 55 ++ .../blender/io/alembic/exporter/abc_writer_hair.cc | 291 +++++++++ .../blender/io/alembic/exporter/abc_writer_hair.h | 62 ++ .../io/alembic/exporter/abc_writer_mball.cc | 95 +++ .../blender/io/alembic/exporter/abc_writer_mball.h | 56 ++ .../blender/io/alembic/exporter/abc_writer_mesh.cc | 591 ++++++++++++++++++ .../blender/io/alembic/exporter/abc_writer_mesh.h | 91 +++ .../io/alembic/exporter/abc_writer_nurbs.cc | 170 ++++++ .../blender/io/alembic/exporter/abc_writer_nurbs.h | 42 ++ .../io/alembic/exporter/abc_writer_object.cc | 87 +++ .../io/alembic/exporter/abc_writer_object.h | 69 +++ .../io/alembic/exporter/abc_writer_points.cc | 122 ++++ .../io/alembic/exporter/abc_writer_points.h | 49 ++ .../io/alembic/exporter/abc_writer_transform.cc | 124 ++++ .../io/alembic/exporter/abc_writer_transform.h | 60 ++ source/blender/io/alembic/intern/abc_exporter.cc | 673 --------------------- source/blender/io/alembic/intern/abc_exporter.h | 127 ---- .../io/alembic/intern/abc_writer_archive.cc | 98 --- .../blender/io/alembic/intern/abc_writer_archive.h | 50 -- .../blender/io/alembic/intern/abc_writer_camera.cc | 79 --- .../blender/io/alembic/intern/abc_writer_camera.h | 45 -- .../blender/io/alembic/intern/abc_writer_curves.cc | 188 ------ .../blender/io/alembic/intern/abc_writer_curves.h | 55 -- .../blender/io/alembic/intern/abc_writer_hair.cc | 291 --------- source/blender/io/alembic/intern/abc_writer_hair.h | 62 -- .../blender/io/alembic/intern/abc_writer_mball.cc | 95 --- .../blender/io/alembic/intern/abc_writer_mball.h | 56 -- .../blender/io/alembic/intern/abc_writer_mesh.cc | 591 ------------------ source/blender/io/alembic/intern/abc_writer_mesh.h | 91 --- .../blender/io/alembic/intern/abc_writer_nurbs.cc | 170 ------ .../blender/io/alembic/intern/abc_writer_nurbs.h | 42 -- .../blender/io/alembic/intern/abc_writer_object.cc | 87 --- .../blender/io/alembic/intern/abc_writer_object.h | 69 --- .../blender/io/alembic/intern/abc_writer_points.cc | 122 ---- .../blender/io/alembic/intern/abc_writer_points.h | 49 -- .../io/alembic/intern/abc_writer_transform.cc | 124 ---- .../io/alembic/intern/abc_writer_transform.h | 60 -- source/blender/io/alembic/intern/alembic_capi.cc | 196 ------ 47 files changed, 3488 insertions(+), 3442 deletions(-) create mode 100644 source/blender/io/alembic/exporter/abc_export_capi.cc create mode 100644 source/blender/io/alembic/exporter/abc_exporter.cc create mode 100644 source/blender/io/alembic/exporter/abc_exporter.h create mode 100644 source/blender/io/alembic/exporter/abc_writer_archive.cc create mode 100644 source/blender/io/alembic/exporter/abc_writer_archive.h create mode 100644 source/blender/io/alembic/exporter/abc_writer_camera.cc create mode 100644 source/blender/io/alembic/exporter/abc_writer_camera.h create mode 100644 source/blender/io/alembic/exporter/abc_writer_curves.cc create mode 100644 source/blender/io/alembic/exporter/abc_writer_curves.h create mode 100644 source/blender/io/alembic/exporter/abc_writer_hair.cc create mode 100644 source/blender/io/alembic/exporter/abc_writer_hair.h create mode 100644 source/blender/io/alembic/exporter/abc_writer_mball.cc create mode 100644 source/blender/io/alembic/exporter/abc_writer_mball.h create mode 100644 source/blender/io/alembic/exporter/abc_writer_mesh.cc create mode 100644 source/blender/io/alembic/exporter/abc_writer_mesh.h create mode 100644 source/blender/io/alembic/exporter/abc_writer_nurbs.cc create mode 100644 source/blender/io/alembic/exporter/abc_writer_nurbs.h create mode 100644 source/blender/io/alembic/exporter/abc_writer_object.cc create mode 100644 source/blender/io/alembic/exporter/abc_writer_object.h create mode 100644 source/blender/io/alembic/exporter/abc_writer_points.cc create mode 100644 source/blender/io/alembic/exporter/abc_writer_points.h create mode 100644 source/blender/io/alembic/exporter/abc_writer_transform.cc create mode 100644 source/blender/io/alembic/exporter/abc_writer_transform.h delete mode 100644 source/blender/io/alembic/intern/abc_exporter.cc delete mode 100644 source/blender/io/alembic/intern/abc_exporter.h delete mode 100644 source/blender/io/alembic/intern/abc_writer_archive.cc delete mode 100644 source/blender/io/alembic/intern/abc_writer_archive.h delete mode 100644 source/blender/io/alembic/intern/abc_writer_camera.cc delete mode 100644 source/blender/io/alembic/intern/abc_writer_camera.h delete mode 100644 source/blender/io/alembic/intern/abc_writer_curves.cc delete mode 100644 source/blender/io/alembic/intern/abc_writer_curves.h delete mode 100644 source/blender/io/alembic/intern/abc_writer_hair.cc delete mode 100644 source/blender/io/alembic/intern/abc_writer_hair.h delete mode 100644 source/blender/io/alembic/intern/abc_writer_mball.cc delete mode 100644 source/blender/io/alembic/intern/abc_writer_mball.h delete mode 100644 source/blender/io/alembic/intern/abc_writer_mesh.cc delete mode 100644 source/blender/io/alembic/intern/abc_writer_mesh.h delete mode 100644 source/blender/io/alembic/intern/abc_writer_nurbs.cc delete mode 100644 source/blender/io/alembic/intern/abc_writer_nurbs.h delete mode 100644 source/blender/io/alembic/intern/abc_writer_object.cc delete mode 100644 source/blender/io/alembic/intern/abc_writer_object.h delete mode 100644 source/blender/io/alembic/intern/abc_writer_points.cc delete mode 100644 source/blender/io/alembic/intern/abc_writer_points.h delete mode 100644 source/blender/io/alembic/intern/abc_writer_transform.cc delete mode 100644 source/blender/io/alembic/intern/abc_writer_transform.h (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt index d864aa51e64..c0c91099ad2 100644 --- a/source/blender/io/alembic/CMakeLists.txt +++ b/source/blender/io/alembic/CMakeLists.txt @@ -43,7 +43,6 @@ set(INC_SYS set(SRC intern/abc_axis_conversion.cc intern/abc_customdata.cc - intern/abc_exporter.cc intern/abc_reader_archive.cc intern/abc_reader_camera.cc intern/abc_reader_curves.cc @@ -53,22 +52,24 @@ set(SRC intern/abc_reader_points.cc intern/abc_reader_transform.cc intern/abc_util.cc - intern/abc_writer_archive.cc - intern/abc_writer_camera.cc - intern/abc_writer_curves.cc - intern/abc_writer_hair.cc - intern/abc_writer_mball.cc - intern/abc_writer_mesh.cc - intern/abc_writer_nurbs.cc - intern/abc_writer_object.cc - intern/abc_writer_points.cc - intern/abc_writer_transform.cc intern/alembic_capi.cc + exporter/abc_export_capi.cc + exporter/abc_exporter.cc + exporter/abc_writer_archive.cc + exporter/abc_writer_camera.cc + exporter/abc_writer_curves.cc + exporter/abc_writer_hair.cc + exporter/abc_writer_mball.cc + exporter/abc_writer_mesh.cc + exporter/abc_writer_nurbs.cc + exporter/abc_writer_object.cc + exporter/abc_writer_points.cc + exporter/abc_writer_transform.cc + ABC_alembic.h intern/abc_axis_conversion.h intern/abc_customdata.h - intern/abc_exporter.h intern/abc_reader_archive.h intern/abc_reader_camera.h intern/abc_reader_curves.h @@ -78,16 +79,18 @@ set(SRC intern/abc_reader_points.h intern/abc_reader_transform.h intern/abc_util.h - intern/abc_writer_archive.h - intern/abc_writer_camera.h - intern/abc_writer_curves.h - intern/abc_writer_hair.h - intern/abc_writer_mball.h - intern/abc_writer_mesh.h - intern/abc_writer_nurbs.h - intern/abc_writer_object.h - intern/abc_writer_points.h - intern/abc_writer_transform.h + + exporter/abc_exporter.h + exporter/abc_writer_archive.h + exporter/abc_writer_camera.h + exporter/abc_writer_curves.h + exporter/abc_writer_hair.h + exporter/abc_writer_mball.h + exporter/abc_writer_mesh.h + exporter/abc_writer_nurbs.h + exporter/abc_writer_object.h + exporter/abc_writer_points.h + exporter/abc_writer_transform.h ) set(LIB diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc new file mode 100644 index 00000000000..3f8e95a6128 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_export_capi.cc @@ -0,0 +1,239 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +#include "ABC_alembic.h" +#include "abc_writer_camera.h" +#include "abc_writer_curves.h" +#include "abc_writer_hair.h" +#include "abc_writer_mesh.h" +#include "abc_writer_nurbs.h" +#include "abc_writer_points.h" +#include "abc_writer_transform.h" + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +#include "DNA_modifier_types.h" +#include "DNA_scene_types.h" + +#include "BKE_blender_version.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_scene.h" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "WM_api.h" +#include "WM_types.h" +} + +struct ExportJobData { + ViewLayer *view_layer; + Main *bmain; + wmWindowManager *wm; + + char filename[1024]; + ExportSettings settings; + + short *stop; + short *do_update; + float *progress; + + bool was_canceled; + bool export_ok; +}; + +static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) +{ + ExportJobData *data = static_cast(customdata); + + data->stop = stop; + data->do_update = do_update; + data->progress = progress; + + /* XXX annoying hack: needed to prevent data corruption when changing + * scene frame in separate threads + */ + G.is_rendering = true; + WM_set_locked_interface(data->wm, true); + G.is_break = false; + + DEG_graph_build_from_view_layer( + data->settings.depsgraph, data->bmain, data->settings.scene, data->view_layer); + BKE_scene_graph_update_tagged(data->settings.depsgraph, data->bmain); + + try { + AbcExporter exporter(data->bmain, data->filename, data->settings); + + Scene *scene = data->settings.scene; /* for the CFRA macro */ + const int orig_frame = CFRA; + + data->was_canceled = false; + exporter(do_update, progress, &data->was_canceled); + + if (CFRA != orig_frame) { + CFRA = orig_frame; + + BKE_scene_graph_update_for_newframe(data->settings.depsgraph, data->bmain); + } + + data->export_ok = !data->was_canceled; + } + catch (const std::exception &e) { + ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n'; + } + catch (...) { + ABC_LOG(data->settings.logger) << "Abc Export: unknown error...\n"; + } +} + +static void export_endjob(void *customdata) +{ + ExportJobData *data = static_cast(customdata); + + DEG_graph_free(data->settings.depsgraph); + + if (data->was_canceled && BLI_exists(data->filename)) { + BLI_delete(data->filename, false, false); + } + + std::string log = data->settings.logger.str(); + if (!log.empty()) { + std::cerr << log; + WM_report(RPT_ERROR, "Errors occurred during the export, look in the console to know more..."); + } + + G.is_rendering = false; + WM_set_locked_interface(data->wm, false); +} + +bool ABC_export(struct Scene *scene, + struct bContext *C, + const char *filepath, + const struct AlembicExportParams *params, + bool as_background_job) +{ + ExportJobData *job = static_cast( + MEM_mallocN(sizeof(ExportJobData), "ExportJobData")); + + job->view_layer = CTX_data_view_layer(C); + job->bmain = CTX_data_main(C); + job->wm = CTX_wm_manager(C); + job->export_ok = false; + BLI_strncpy(job->filename, filepath, 1024); + + /* Alright, alright, alright.... + * + * ExportJobData contains an ExportSettings containing a SimpleLogger. + * + * Since ExportJobData is a C-style struct dynamically allocated with + * MEM_mallocN (see above), its constructor is never called, therefore the + * ExportSettings constructor is not called which implies that the + * SimpleLogger one is not called either. SimpleLogger in turn does not call + * the constructor of its data members which ultimately means that its + * std::ostringstream member has a NULL pointer. To be able to properly use + * the stream's operator<<, the pointer needs to be set, therefore we have + * to properly construct everything. And this is done using the placement + * new operator as here below. It seems hackish, but I'm too lazy to + * do bigger refactor and maybe there is a better way which does not involve + * hardcore refactoring. */ + new (&job->settings) ExportSettings(); + job->settings.scene = scene; + job->settings.depsgraph = DEG_graph_new(job->bmain, scene, job->view_layer, DAG_EVAL_RENDER); + + /* TODO(Sybren): for now we only export the active scene layer. + * Later in the 2.8 development process this may be replaced by using + * a specific collection for Alembic I/O, which can then be toggled + * between "real" objects and cached Alembic files. */ + job->settings.view_layer = job->view_layer; + + job->settings.frame_start = params->frame_start; + job->settings.frame_end = params->frame_end; + job->settings.frame_samples_xform = params->frame_samples_xform; + job->settings.frame_samples_shape = params->frame_samples_shape; + job->settings.shutter_open = params->shutter_open; + job->settings.shutter_close = params->shutter_close; + + /* TODO(Sybren): For now this is ignored, until we can get selection + * detection working through Base pointers (instead of ob->flags). */ + job->settings.selected_only = params->selected_only; + + job->settings.export_face_sets = params->face_sets; + job->settings.export_normals = params->normals; + job->settings.export_uvs = params->uvs; + job->settings.export_vcols = params->vcolors; + job->settings.export_hair = params->export_hair; + job->settings.export_particles = params->export_particles; + job->settings.apply_subdiv = params->apply_subdiv; + job->settings.curves_as_mesh = params->curves_as_mesh; + job->settings.flatten_hierarchy = params->flatten_hierarchy; + + /* TODO(Sybren): visible_layer & renderable only is ignored for now, + * to be replaced with collections later in the 2.8 dev process + * (also see note above). */ + job->settings.visible_objects_only = params->visible_objects_only; + job->settings.renderable_only = params->renderable_only; + + job->settings.use_subdiv_schema = params->use_subdiv_schema; + job->settings.pack_uv = params->packuv; + job->settings.global_scale = params->global_scale; + job->settings.triangulate = params->triangulate; + job->settings.quad_method = params->quad_method; + job->settings.ngon_method = params->ngon_method; + + if (job->settings.frame_start > job->settings.frame_end) { + std::swap(job->settings.frame_start, job->settings.frame_end); + } + + bool export_ok = false; + if (as_background_job) { + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + job->settings.scene, + "Alembic Export", + WM_JOB_PROGRESS, + WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, MEM_freeN); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); + WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + /* Fake a job context, so that we don't need NULL pointer checks while exporting. */ + short stop = 0, do_update = 0; + float progress = 0.f; + + export_startjob(job, &stop, &do_update, &progress); + export_endjob(job); + export_ok = job->export_ok; + + MEM_freeN(job); + } + + return export_ok; +} diff --git a/source/blender/io/alembic/exporter/abc_exporter.cc b/source/blender/io/alembic/exporter/abc_exporter.cc new file mode 100644 index 00000000000..0de6c393e5a --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_exporter.cc @@ -0,0 +1,673 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_exporter.h" + +#include + +#include "abc_writer_archive.h" +#include "abc_writer_camera.h" +#include "abc_writer_curves.h" +#include "abc_writer_hair.h" +#include "abc_writer_mball.h" +#include "abc_writer_mesh.h" +#include "abc_writer_nurbs.h" +#include "abc_writer_points.h" +#include "abc_writer_transform.h" +#include "intern/abc_util.h" + +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_fluid_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meta_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" /* for FILE_MAX */ + +#include "BLI_string.h" + +#ifdef WIN32 +/* needed for MSCV because of snprintf from BLI_string */ +# include "BLI_winstuff.h" +#endif + +#include "BKE_duplilist.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_layer.h" +#include "BKE_main.h" +#include "BKE_mball.h" +#include "BKE_modifier.h" +#include "BKE_particle.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph_query.h" + +using Alembic::Abc::OBox3dProperty; +using Alembic::Abc::TimeSamplingPtr; + +/* ************************************************************************** */ + +ExportSettings::ExportSettings() + : scene(NULL), + view_layer(NULL), + depsgraph(NULL), + logger(), + selected_only(false), + visible_objects_only(false), + renderable_only(false), + frame_start(1), + frame_end(1), + frame_samples_xform(1), + frame_samples_shape(1), + shutter_open(0.0), + shutter_close(1.0), + global_scale(1.0f), + flatten_hierarchy(false), + export_normals(false), + export_uvs(false), + export_vcols(false), + export_face_sets(false), + export_vweigths(false), + export_hair(true), + export_particles(true), + apply_subdiv(false), + use_subdiv_schema(false), + export_child_hairs(true), + pack_uv(false), + triangulate(false), + quad_method(0), + ngon_method(0) +{ +} + +static bool object_is_smoke_sim(Object *ob) +{ + ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluid); + + if (md) { + FluidModifierData *smd = reinterpret_cast(md); + return (smd->type == MOD_FLUID_TYPE_DOMAIN && smd->domain && + smd->domain->type == FLUID_DOMAIN_TYPE_GAS); + } + + return false; +} + +static bool object_type_is_exportable(Scene *scene, Object *ob) +{ + switch (ob->type) { + case OB_MESH: + if (object_is_smoke_sim(ob)) { + return false; + } + + return true; + case OB_EMPTY: + case OB_CURVE: + case OB_SURF: + case OB_CAMERA: + return true; + case OB_MBALL: + return AbcMBallWriter::isBasisBall(scene, ob); + default: + return false; + } +} + +/** + * Returns whether this object should be exported into the Alembic file. + * + * \param settings: export settings, used for options like 'selected only'. + * \param ob: the object's base in question. + * \param is_duplicated: Normally false; true when the object is instanced + * into the scene by a dupli-object (e.g. part of a dupligroup). + * This ignores selection and layer visibility, + * and assumes that the dupli-object itself (e.g. the group-instantiating empty) is exported. + */ +static bool export_object(const ExportSettings *const settings, + const Base *const base, + bool is_duplicated) +{ + if (!is_duplicated) { + View3D *v3d = NULL; + + /* These two tests only make sense when the object isn't being instanced + * into the scene. When it is, its exportability is determined by + * its dupli-object and the DupliObject::no_draw property. */ + if (settings->selected_only && !BASE_SELECTED(v3d, base)) { + return false; + } + // FIXME Sybren: handle these cleanly (maybe just remove code), + // now using active scene layer instead. + if (settings->visible_objects_only && !BASE_VISIBLE(v3d, base)) { + return false; + } + } + + Object *ob_eval = DEG_get_evaluated_object(settings->depsgraph, base->object); + if ((ob_eval->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0) { + /* XXX fix after 2.80: the object was not part of the depsgraph, and thus we cannot get the + * evaluated copy to export. This will be handled more elegantly in the new + * AbstractHierarchyIterator that Sybren is working on. This condition is temporary, and avoids + * a BLI_assert() failure getting the evaluated mesh of this object. */ + return false; + } + + // if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) { + // return false; + // } + + return true; +} + +/* ************************************************************************** */ + +AbcExporter::AbcExporter(Main *bmain, const char *filename, ExportSettings &settings) + : m_bmain(bmain), + m_settings(settings), + m_filename(filename), + m_trans_sampling_index(0), + m_shape_sampling_index(0), + m_writer(NULL) +{ +} + +AbcExporter::~AbcExporter() +{ + /* Free xforms map */ + m_xforms_type::iterator it_x, e_x; + for (it_x = m_xforms.begin(), e_x = m_xforms.end(); it_x != e_x; ++it_x) { + delete it_x->second; + } + + /* Free shapes vector */ + for (int i = 0, e = m_shapes.size(); i != e; i++) { + delete m_shapes[i]; + } + + delete m_writer; +} + +void AbcExporter::getShutterSamples(unsigned int nr_of_samples, + bool time_relative, + std::vector &samples) +{ + Scene *scene = m_settings.scene; /* for use in the FPS macro */ + samples.clear(); + + unsigned int frame_offset = time_relative ? m_settings.frame_start : 0; + double time_factor = time_relative ? FPS : 1.0; + double shutter_open = m_settings.shutter_open; + double shutter_close = m_settings.shutter_close; + double time_inc = (shutter_close - shutter_open) / nr_of_samples; + + /* sample between shutter open & close */ + for (int sample = 0; sample < nr_of_samples; sample++) { + double sample_time = shutter_open + time_inc * sample; + double time = (frame_offset + sample_time) / time_factor; + + samples.push_back(time); + } +} + +Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step) +{ + std::vector samples; + + if (m_settings.frame_start == m_settings.frame_end) { + return TimeSamplingPtr(new Alembic::Abc::TimeSampling()); + } + + getShutterSamples(step, true, samples); + + /* TODO(Sybren): shouldn't we use the FPS macro here? */ + Alembic::Abc::TimeSamplingType ts(static_cast(samples.size()), + 1.0 / m_settings.scene->r.frs_sec); + + return TimeSamplingPtr(new Alembic::Abc::TimeSampling(ts, samples)); +} + +void AbcExporter::getFrameSet(unsigned int nr_of_samples, std::set &frames) +{ + frames.clear(); + + std::vector shutter_samples; + + getShutterSamples(nr_of_samples, false, shutter_samples); + + for (double frame = m_settings.frame_start; frame <= m_settings.frame_end; frame += 1.0) { + for (size_t j = 0; j < nr_of_samples; j++) { + frames.insert(frame + shutter_samples[j]); + } + } +} + +void AbcExporter::operator()(short *do_update, float *progress, bool *was_canceled) +{ + std::string abc_scene_name; + + if (m_bmain->name[0] != '\0') { + char scene_file_name[FILE_MAX]; + BLI_strncpy(scene_file_name, m_bmain->name, FILE_MAX); + abc_scene_name = scene_file_name; + } + else { + abc_scene_name = "untitled"; + } + + m_writer = new ArchiveWriter(m_filename, abc_scene_name, m_settings.scene); + + /* Create time samplings for transforms and shapes. */ + + TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_samples_xform); + + m_trans_sampling_index = m_writer->archive().addTimeSampling(*trans_time); + + TimeSamplingPtr shape_time; + + if ((m_settings.frame_samples_shape == m_settings.frame_samples_xform) || + (m_settings.frame_start == m_settings.frame_end)) { + shape_time = trans_time; + m_shape_sampling_index = m_trans_sampling_index; + } + else { + shape_time = createTimeSampling(m_settings.frame_samples_shape); + m_shape_sampling_index = m_writer->archive().addTimeSampling(*shape_time); + } + + OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds( + m_writer->archive(), m_trans_sampling_index); + + createTransformWritersHierarchy(); + createShapeWriters(); + + /* Make a list of frames to export. */ + + std::set xform_frames; + getFrameSet(m_settings.frame_samples_xform, xform_frames); + + std::set shape_frames; + getFrameSet(m_settings.frame_samples_shape, shape_frames); + + /* Merge all frames needed. */ + std::set frames(xform_frames); + frames.insert(shape_frames.begin(), shape_frames.end()); + + /* Export all frames. */ + + std::set::const_iterator begin = frames.begin(); + std::set::const_iterator end = frames.end(); + + const float size = static_cast(frames.size()); + size_t i = 0; + + for (; begin != end; ++begin) { + *progress = (++i / size); + *do_update = 1; + + if (G.is_break) { + *was_canceled = true; + break; + } + + const double frame = *begin; + + /* 'frame' is offset by start frame, so need to cancel the offset. */ + setCurrentFrame(m_bmain, frame); + + if (shape_frames.count(frame) != 0) { + for (int i = 0, e = m_shapes.size(); i != e; i++) { + m_shapes[i]->write(); + } + } + + if (xform_frames.count(frame) == 0) { + continue; + } + + m_xforms_type::iterator xit, xe; + for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { + xit->second->write(); + } + + /* Save the archive 's bounding box. */ + Imath::Box3d bounds; + + for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { + Imath::Box3d box = xit->second->bounds(); + bounds.extendBy(box); + } + + archive_bounds_prop.set(bounds); + } +} + +void AbcExporter::createTransformWritersHierarchy() +{ + for (Base *base = static_cast(m_settings.view_layer->object_bases.first); base; + base = base->next) { + Object *ob = base->object; + + if (export_object(&m_settings, base, false)) { + switch (ob->type) { + case OB_LAMP: + case OB_LATTICE: + case OB_SPEAKER: + /* We do not export transforms for objects of these classes. */ + break; + default: + exploreTransform(base, ob, ob->parent, NULL); + } + } + } +} + +void AbcExporter::exploreTransform(Base *base, + Object *object, + Object *parent, + Object *dupliObParent) +{ + /* If an object isn't exported itself, its duplilist shouldn't be + * exported either. */ + if (!export_object(&m_settings, base, dupliObParent != NULL)) { + return; + } + + Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object); + if (object_type_is_exportable(m_settings.scene, ob)) { + createTransformWriter(ob, parent, dupliObParent); + } + + ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob); + + if (lb) { + DupliObject *link = static_cast(lb->first); + Object *dupli_ob = NULL; + Object *dupli_parent = NULL; + + for (; link; link = link->next) { + /* This skips things like custom bone shapes. */ + if (m_settings.renderable_only && link->no_draw) { + continue; + } + + if (link->type == OB_DUPLICOLLECTION) { + dupli_ob = link->ob; + dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob; + + exploreTransform(base, dupli_ob, dupli_parent, ob); + } + } + + free_object_duplilist(lb); + } +} + +AbcTransformWriter *AbcExporter::createTransformWriter(Object *ob, + Object *parent, + Object *dupliObParent) +{ + /* An object should not be its own parent, or we'll get infinite loops. */ + BLI_assert(ob != parent); + BLI_assert(ob != dupliObParent); + + std::string name; + if (m_settings.flatten_hierarchy) { + name = get_id_name(ob); + } + else { + name = get_object_dag_path_name(ob, dupliObParent); + } + + /* check if we have already created a transform writer for this object */ + AbcTransformWriter *my_writer = getXForm(name); + if (my_writer != NULL) { + return my_writer; + } + + AbcTransformWriter *parent_writer = NULL; + Alembic::Abc::OObject alembic_parent; + + if (m_settings.flatten_hierarchy || parent == NULL) { + /* Parentless objects still have the "top object" as parent + * in Alembic. */ + alembic_parent = m_writer->archive().getTop(); + } + else { + /* Since there are so many different ways to find parents (as evident + * in the number of conditions below), we can't really look up the + * parent by name. We'll just call createTransformWriter(), which will + * return the parent's AbcTransformWriter pointer. */ + if (parent->parent) { + if (parent == dupliObParent) { + parent_writer = createTransformWriter(parent, parent->parent, NULL); + } + else { + parent_writer = createTransformWriter(parent, parent->parent, dupliObParent); + } + } + else if (parent == dupliObParent) { + if (dupliObParent->parent == NULL) { + parent_writer = createTransformWriter(parent, NULL, NULL); + } + else { + parent_writer = createTransformWriter( + parent, dupliObParent->parent, dupliObParent->parent); + } + } + else { + parent_writer = createTransformWriter(parent, dupliObParent, dupliObParent); + } + + BLI_assert(parent_writer); + alembic_parent = parent_writer->alembicXform(); + } + + my_writer = new AbcTransformWriter( + ob, alembic_parent, parent_writer, m_trans_sampling_index, m_settings); + + /* When flattening, the matrix of the dupliobject has to be added. */ + if (m_settings.flatten_hierarchy && dupliObParent) { + my_writer->m_proxy_from = dupliObParent; + } + + m_xforms[name] = my_writer; + return my_writer; +} + +void AbcExporter::createShapeWriters() +{ + for (Base *base = static_cast(m_settings.view_layer->object_bases.first); base; + base = base->next) { + exploreObject(base, base->object, NULL); + } +} + +void AbcExporter::exploreObject(Base *base, Object *object, Object *dupliObParent) +{ + /* If an object isn't exported itself, its duplilist shouldn't be + * exported either. */ + if (!export_object(&m_settings, base, dupliObParent != NULL)) { + return; + } + + Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object); + createShapeWriter(ob, dupliObParent); + + ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob); + + if (lb) { + DupliObject *link = static_cast(lb->first); + + for (; link; link = link->next) { + /* This skips things like custom bone shapes. */ + if (m_settings.renderable_only && link->no_draw) { + continue; + } + if (link->type == OB_DUPLICOLLECTION) { + exploreObject(base, link->ob, ob); + } + } + + free_object_duplilist(lb); + } +} + +void AbcExporter::createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform) +{ + if (!m_settings.export_hair && !m_settings.export_particles) { + return; + } + + ParticleSystem *psys = static_cast(ob->particlesystem.first); + + for (; psys; psys = psys->next) { + if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) { + continue; + } + + if (m_settings.export_hair && psys->part->type == PART_HAIR) { + m_settings.export_child_hairs = true; + m_shapes.push_back(new AbcHairWriter(ob, xform, m_shape_sampling_index, m_settings, psys)); + } + else if (m_settings.export_particles && + (psys->part->type == PART_EMITTER || psys->part->type == PART_FLUID_FLIP || + psys->part->type == PART_FLUID_SPRAY || psys->part->type == PART_FLUID_BUBBLE || + psys->part->type == PART_FLUID_FOAM || psys->part->type == PART_FLUID_TRACER || + psys->part->type == PART_FLUID_SPRAYFOAM || + psys->part->type == PART_FLUID_SPRAYBUBBLE || + psys->part->type == PART_FLUID_FOAMBUBBLE || + psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE)) { + m_shapes.push_back(new AbcPointsWriter(ob, xform, m_shape_sampling_index, m_settings, psys)); + } + } +} + +void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent) +{ + if (!object_type_is_exportable(m_settings.scene, ob)) { + return; + } + + std::string name; + + if (m_settings.flatten_hierarchy) { + name = get_id_name(ob); + } + else { + name = get_object_dag_path_name(ob, dupliObParent); + } + + AbcTransformWriter *xform = getXForm(name); + + if (!xform) { + ABC_LOG(m_settings.logger) << __func__ << ": xform " << name << " is NULL\n"; + return; + } + + createParticleSystemsWriters(ob, xform); + + switch (ob->type) { + case OB_MESH: { + Mesh *me = static_cast(ob->data); + + if (!me) { + return; + } + + m_shapes.push_back(new AbcMeshWriter(ob, xform, m_shape_sampling_index, m_settings)); + break; + } + case OB_SURF: { + Curve *cu = static_cast(ob->data); + + if (!cu) { + return; + } + + AbcObjectWriter *writer; + if (m_settings.curves_as_mesh) { + writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings); + } + else { + writer = new AbcNurbsWriter(ob, xform, m_shape_sampling_index, m_settings); + } + m_shapes.push_back(writer); + break; + } + case OB_CURVE: { + Curve *cu = static_cast(ob->data); + + if (!cu) { + return; + } + + AbcObjectWriter *writer; + if (m_settings.curves_as_mesh) { + writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings); + } + else { + writer = new AbcCurveWriter(ob, xform, m_shape_sampling_index, m_settings); + } + m_shapes.push_back(writer); + break; + } + case OB_CAMERA: { + Camera *cam = static_cast(ob->data); + + if (cam->type == CAM_PERSP) { + m_shapes.push_back(new AbcCameraWriter(ob, xform, m_shape_sampling_index, m_settings)); + } + + break; + } + case OB_MBALL: { + MetaBall *mball = static_cast(ob->data); + if (!mball) { + return; + } + + m_shapes.push_back( + new AbcMBallWriter(m_bmain, ob, xform, m_shape_sampling_index, m_settings)); + break; + } + } +} + +AbcTransformWriter *AbcExporter::getXForm(const std::string &name) +{ + std::map::iterator it = m_xforms.find(name); + + if (it == m_xforms.end()) { + return NULL; + } + + return it->second; +} + +void AbcExporter::setCurrentFrame(Main *bmain, double t) +{ + m_settings.scene->r.cfra = static_cast(t); + m_settings.scene->r.subframe = static_cast(t) - m_settings.scene->r.cfra; + BKE_scene_graph_update_for_newframe(m_settings.depsgraph, bmain); +} diff --git a/source/blender/io/alembic/exporter/abc_exporter.h b/source/blender/io/alembic/exporter/abc_exporter.h new file mode 100644 index 00000000000..c1c54cb2208 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_exporter.h @@ -0,0 +1,127 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_EXPORTER_H__ +#define __ABC_EXPORTER_H__ + +#include +#include +#include +#include + +#include "intern/abc_util.h" + +class AbcObjectWriter; +class AbcTransformWriter; +class ArchiveWriter; + +struct Base; +struct Depsgraph; +struct Main; +struct Object; +struct Scene; +struct ViewLayer; + +struct ExportSettings { + ExportSettings(); + + Scene *scene; + /** Scene layer to export; all its objects will be exported, unless selected_only=true. */ + ViewLayer *view_layer; + Depsgraph *depsgraph; + SimpleLogger logger; + + bool selected_only; + bool visible_objects_only; + bool renderable_only; + + double frame_start, frame_end; + double frame_samples_xform; + double frame_samples_shape; + double shutter_open; + double shutter_close; + float global_scale; + + bool flatten_hierarchy; + + bool export_normals; + bool export_uvs; + bool export_vcols; + bool export_face_sets; + bool export_vweigths; + bool export_hair; + bool export_particles; + + bool apply_subdiv; + bool curves_as_mesh; + bool use_subdiv_schema; + bool export_child_hairs; + bool pack_uv; + bool triangulate; + + int quad_method; + int ngon_method; +}; + +class AbcExporter { + Main *m_bmain; + ExportSettings &m_settings; + + const char *m_filename; + + unsigned int m_trans_sampling_index, m_shape_sampling_index; + + ArchiveWriter *m_writer; + + /* mapping from name to transform writer */ + typedef std::map m_xforms_type; + m_xforms_type m_xforms; + + std::vector m_shapes; + + public: + AbcExporter(Main *bmain, const char *filename, ExportSettings &settings); + ~AbcExporter(); + + void operator()(short *do_update, float *progress, bool *was_canceled); + + protected: + void getShutterSamples(unsigned int nr_of_samples, + bool time_relative, + std::vector &samples); + void getFrameSet(unsigned int nr_of_samples, std::set &frames); + + private: + Alembic::Abc::TimeSamplingPtr createTimeSampling(double step); + + void createTransformWritersHierarchy(); + AbcTransformWriter *createTransformWriter(Object *ob, Object *parent, Object *dupliObParent); + void exploreTransform(Base *base, Object *object, Object *parent, Object *dupliObParent); + void exploreObject(Base *base, Object *object, Object *dupliObParent); + void createShapeWriters(); + void createShapeWriter(Object *ob, Object *dupliObParent); + void createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform); + + AbcTransformWriter *getXForm(const std::string &name); + + void setCurrentFrame(Main *bmain, double t); +}; + +#endif /* __ABC_EXPORTER_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_archive.cc b/source/blender/io/alembic/exporter/abc_writer_archive.cc new file mode 100644 index 00000000000..40926532f85 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_archive.cc @@ -0,0 +1,98 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_archive.h" + +#include "BKE_blender_version.h" + +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "DNA_scene_types.h" + +#ifdef WIN32 +# include "utfconv.h" +#endif + +#include + +using Alembic::Abc::ErrorHandler; +using Alembic::Abc::kWrapExisting; +using Alembic::Abc::OArchive; + +/* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to + * have a version supporting streams. */ +static OArchive create_archive(std::ostream *ostream, + const std::string &scene_name, + double scene_fps) +{ + Alembic::Abc::MetaData abc_metadata; + + abc_metadata.set(Alembic::Abc::kApplicationNameKey, "Blender"); + abc_metadata.set(Alembic::Abc::kUserDescriptionKey, scene_name); + abc_metadata.set("blender_version", std::string("v") + BKE_blender_version_string()); + abc_metadata.set("FramesPerTimeUnit", std::to_string(scene_fps)); + + time_t raw_time; + time(&raw_time); + char buffer[128]; + +#if defined _WIN32 || defined _WIN64 + ctime_s(buffer, 128, &raw_time); +#else + ctime_r(&raw_time, buffer); +#endif + + const std::size_t buffer_len = strlen(buffer); + if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') { + buffer[buffer_len - 1] = '\0'; + } + + abc_metadata.set(Alembic::Abc::kDateWrittenKey, buffer); + + ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy; + Alembic::AbcCoreOgawa::WriteArchive archive_writer; + return OArchive(archive_writer(ostream, abc_metadata), kWrapExisting, policy); +} + +ArchiveWriter::ArchiveWriter(const char *filename, + const std::string &abc_scene_name, + const Scene *scene) +{ + /* Use stream to support unicode character paths on Windows. */ +#ifdef WIN32 + UTF16_ENCODE(filename); + std::wstring wstr(filename_16); + m_outfile.open(wstr.c_str(), std::ios::out | std::ios::binary); + UTF16_UN_ENCODE(filename); +#else + m_outfile.open(filename, std::ios::out | std::ios::binary); +#endif + + m_archive = create_archive(&m_outfile, abc_scene_name, FPS); +} + +OArchive &ArchiveWriter::archive() +{ + return m_archive; +} diff --git a/source/blender/io/alembic/exporter/abc_writer_archive.h b/source/blender/io/alembic/exporter/abc_writer_archive.h new file mode 100644 index 00000000000..737717c1710 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_archive.h @@ -0,0 +1,50 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_ARCHIVE_H__ +#define __ABC_WRITER_ARCHIVE_H__ + +#include +#include + +#include + +struct Main; +struct Scene; + +/* Wrappers around input and output archives. The goal is to be able to use + * streams so that unicode paths work on Windows (T49112), and to make sure that + * the stream objects remain valid as long as the archives are open. + */ + +class ArchiveWriter { + std::ofstream m_outfile; + Alembic::Abc::OArchive m_archive; + + public: + ArchiveWriter(const char *filename, const std::string &abc_scene_name, const Scene *scene); + + Alembic::Abc::OArchive &archive(); +}; + +#endif /* __ABC_WRITER_ARCHIVE_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.cc b/source/blender/io/alembic/exporter/abc_writer_camera.cc new file mode 100644 index 00000000000..07ae81e584f --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_camera.cc @@ -0,0 +1,79 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_camera.h" +#include "abc_writer_transform.h" + +#include "DNA_camera_types.h" +#include "DNA_object_types.h" + +using Alembic::AbcGeom::OCamera; +using Alembic::AbcGeom::OFloatProperty; + +AbcCameraWriter::AbcCameraWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(ob, time_sampling, settings, parent) +{ + OCamera camera(parent->alembicXform(), m_name, m_time_sampling); + m_camera_schema = camera.getSchema(); + + m_custom_data_container = m_camera_schema.getUserProperties(); + m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling); + m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling); +} + +void AbcCameraWriter::do_write() +{ + Camera *cam = static_cast(m_object->data); + + m_stereo_distance.set(cam->stereo.convergence_distance); + m_eye_separation.set(cam->stereo.interocular_distance); + + const double apperture_x = cam->sensor_x / 10.0; + const double apperture_y = cam->sensor_y / 10.0; + const double film_aspect = apperture_x / apperture_y; + + m_camera_sample.setFocalLength(cam->lens); + m_camera_sample.setHorizontalAperture(apperture_x); + m_camera_sample.setVerticalAperture(apperture_y); + m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx); + m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect); + m_camera_sample.setNearClippingPlane(cam->clip_start); + m_camera_sample.setFarClippingPlane(cam->clip_end); + + if (cam->dof.focus_object) { + Imath::V3f v(m_object->loc[0] - cam->dof.focus_object->loc[0], + m_object->loc[1] - cam->dof.focus_object->loc[1], + m_object->loc[2] - cam->dof.focus_object->loc[2]); + m_camera_sample.setFocusDistance(v.length()); + } + else { + m_camera_sample.setFocusDistance(cam->dof.focus_distance); + } + + /* Blender camera does not have an fstop param, so try to find a custom prop + * instead. */ + m_camera_sample.setFStop(cam->dof.aperture_fstop); + + m_camera_sample.setLensSqueezeRatio(1.0); + m_camera_schema.set(m_camera_sample); +} diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.h b/source/blender/io/alembic/exporter/abc_writer_camera.h new file mode 100644 index 00000000000..3b515911a48 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_camera.h @@ -0,0 +1,45 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_CAMERA_H__ +#define __ABC_WRITER_CAMERA_H__ + +#include "abc_writer_object.h" + +/* ************************************************************************** */ + +class AbcCameraWriter : public AbcObjectWriter { + Alembic::AbcGeom::OCameraSchema m_camera_schema; + Alembic::AbcGeom::CameraSample m_camera_sample; + Alembic::AbcGeom::OCompoundProperty m_custom_data_container; + Alembic::AbcGeom::OFloatProperty m_stereo_distance; + Alembic::AbcGeom::OFloatProperty m_eye_separation; + + public: + AbcCameraWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + private: + virtual void do_write(); +}; + +#endif /* __ABC_WRITER_CAMERA_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.cc b/source/blender/io/alembic/exporter/abc_writer_curves.cc new file mode 100644 index 00000000000..3f9a53cffc6 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_curves.cc @@ -0,0 +1,188 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_curves.h" +#include "abc_writer_transform.h" +#include "intern/abc_axis_conversion.h" +#include "intern/abc_reader_curves.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "BKE_curve.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +using Alembic::AbcGeom::OCompoundProperty; +using Alembic::AbcGeom::OCurves; +using Alembic::AbcGeom::OCurvesSchema; +using Alembic::AbcGeom::OInt16Property; +using Alembic::AbcGeom::ON3fGeomParam; +using Alembic::AbcGeom::OV2fGeomParam; + +AbcCurveWriter::AbcCurveWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(ob, time_sampling, settings, parent) +{ + OCurves curves(parent->alembicXform(), m_name, m_time_sampling); + m_schema = curves.getSchema(); + + Curve *cu = static_cast(m_object->data); + OCompoundProperty user_props = m_schema.getUserProperties(); + OInt16Property user_prop_resolu(user_props, ABC_CURVE_RESOLUTION_U_PROPNAME); + user_prop_resolu.set(cu->resolu); +} + +void AbcCurveWriter::do_write() +{ + Curve *curve = static_cast(m_object->data); + + std::vector verts; + std::vector vert_counts; + std::vector widths; + std::vector weights; + std::vector knots; + std::vector orders; + Imath::V3f temp_vert; + + Alembic::AbcGeom::BasisType curve_basis; + Alembic::AbcGeom::CurveType curve_type; + Alembic::AbcGeom::CurvePeriodicity periodicity; + + Nurb *nurbs = static_cast(curve->nurb.first); + for (; nurbs; nurbs = nurbs->next) { + if (nurbs->bp) { + curve_basis = Alembic::AbcGeom::kNoBasis; + curve_type = Alembic::AbcGeom::kVariableOrder; + + const int totpoint = nurbs->pntsu * nurbs->pntsv; + + const BPoint *point = nurbs->bp; + + for (int i = 0; i < totpoint; i++, point++) { + copy_yup_from_zup(temp_vert.getValue(), point->vec); + verts.push_back(temp_vert); + weights.push_back(point->vec[3]); + widths.push_back(point->radius); + } + } + else if (nurbs->bezt) { + curve_basis = Alembic::AbcGeom::kBezierBasis; + curve_type = Alembic::AbcGeom::kCubic; + + const int totpoint = nurbs->pntsu; + + const BezTriple *bezier = nurbs->bezt; + + /* TODO(kevin): store info about handles, Alembic doesn't have this. */ + for (int i = 0; i < totpoint; i++, bezier++) { + copy_yup_from_zup(temp_vert.getValue(), bezier->vec[1]); + verts.push_back(temp_vert); + widths.push_back(bezier->radius); + } + } + + if ((nurbs->flagu & CU_NURB_ENDPOINT) != 0) { + periodicity = Alembic::AbcGeom::kNonPeriodic; + } + else if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) { + periodicity = Alembic::AbcGeom::kPeriodic; + + /* Duplicate the start points to indicate that the curve is actually + * cyclic since other software need those. + */ + + for (int i = 0; i < nurbs->orderu; i++) { + verts.push_back(verts[i]); + } + } + + if (nurbs->knotsu != NULL) { + const size_t num_knots = KNOTSU(nurbs); + + /* Add an extra knot at the beginning and end of the array since most apps + * require/expect them. */ + knots.resize(num_knots + 2); + + for (int i = 0; i < num_knots; i++) { + knots[i + 1] = nurbs->knotsu[i]; + } + + if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) { + knots[0] = nurbs->knotsu[0]; + knots[num_knots - 1] = nurbs->knotsu[num_knots - 1]; + } + else { + knots[0] = (2.0f * nurbs->knotsu[0] - nurbs->knotsu[1]); + knots[num_knots - 1] = (2.0f * nurbs->knotsu[num_knots - 1] - + nurbs->knotsu[num_knots - 2]); + } + } + + orders.push_back(nurbs->orderu); + vert_counts.push_back(verts.size()); + } + + Alembic::AbcGeom::OFloatGeomParam::Sample width_sample; + width_sample.setVals(widths); + + m_sample = OCurvesSchema::Sample(verts, + vert_counts, + curve_type, + periodicity, + width_sample, + OV2fGeomParam::Sample(), /* UVs */ + ON3fGeomParam::Sample(), /* normals */ + curve_basis, + weights, + orders, + knots); + + m_sample.setSelfBounds(bounds()); + m_schema.set(m_sample); +} + +AbcCurveMeshWriter::AbcCurveMeshWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcGenericMeshWriter(ob, parent, time_sampling, settings) +{ +} + +Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/, + Object *ob_eval, + bool &r_needsfree) +{ + Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); + if (mesh_eval != NULL) { + /* Mesh_eval only exists when generative modifiers are in use. */ + r_needsfree = false; + return mesh_eval; + } + + r_needsfree = true; + return BKE_mesh_new_nomain_from_curve(ob_eval); +} diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.h b/source/blender/io/alembic/exporter/abc_writer_curves.h new file mode 100644 index 00000000000..83f0289dd2d --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_curves.h @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_CURVES_H__ +#define __ABC_WRITER_CURVES_H__ + +#include "abc_writer_mesh.h" +#include "abc_writer_object.h" + +class AbcCurveWriter : public AbcObjectWriter { + Alembic::AbcGeom::OCurvesSchema m_schema; + Alembic::AbcGeom::OCurvesSchema::Sample m_sample; + + public: + AbcCurveWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + protected: + void do_write(); +}; + +class AbcCurveMeshWriter : public AbcGenericMeshWriter { + public: + AbcCurveMeshWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + protected: + Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree); +}; + +#endif /* __ABC_WRITER_CURVES_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.cc b/source/blender/io/alembic/exporter/abc_writer_hair.cc new file mode 100644 index 00000000000..ceb2eb7d316 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_hair.cc @@ -0,0 +1,291 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_hair.h" +#include "abc_writer_transform.h" +#include "intern/abc_axis_conversion.h" + +#include + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" + +#include "BLI_math_geom.h" + +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_particle.h" + +using Alembic::Abc::P3fArraySamplePtr; + +using Alembic::AbcGeom::OCurves; +using Alembic::AbcGeom::OCurvesSchema; +using Alembic::AbcGeom::ON3fGeomParam; +using Alembic::AbcGeom::OV2fGeomParam; + +/* ************************************************************************** */ + +AbcHairWriter::AbcHairWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys) + : AbcObjectWriter(ob, time_sampling, settings, parent), m_uv_warning_shown(false) +{ + m_psys = psys; + + std::string psys_name = get_valid_abc_name(psys->name); + OCurves curves(parent->alembicXform(), psys_name, m_time_sampling); + m_schema = curves.getSchema(); +} + +void AbcHairWriter::do_write() +{ + if (!m_psys) { + return; + } + Mesh *mesh = mesh_get_eval_final( + m_settings.depsgraph, m_settings.scene, m_object, &CD_MASK_MESH); + BKE_mesh_tessface_ensure(mesh); + + std::vector verts; + std::vector hvertices; + std::vector uv_values; + std::vector norm_values; + + if (m_psys->pathcache) { + ParticleSettings *part = m_psys->part; + bool export_children = m_settings.export_child_hairs && m_psys->childcache && + part->childtype != 0; + + if (!export_children || part->draw & PART_DRAW_PARENT) { + write_hair_sample(mesh, part, verts, norm_values, uv_values, hvertices); + } + + if (export_children) { + write_hair_child_sample(mesh, part, verts, norm_values, uv_values, hvertices); + } + } + + Alembic::Abc::P3fArraySample iPos(verts); + m_sample = OCurvesSchema::Sample(iPos, hvertices); + m_sample.setBasis(Alembic::AbcGeom::kNoBasis); + m_sample.setType(Alembic::AbcGeom::kLinear); + m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic); + + if (!uv_values.empty()) { + OV2fGeomParam::Sample uv_smp; + uv_smp.setVals(uv_values); + m_sample.setUVs(uv_smp); + } + + if (!norm_values.empty()) { + ON3fGeomParam::Sample norm_smp; + norm_smp.setVals(norm_values); + m_sample.setNormals(norm_smp); + } + + m_sample.setSelfBounds(bounds()); + m_schema.set(m_sample); +} + +void AbcHairWriter::write_hair_sample(Mesh *mesh, + ParticleSettings *part, + std::vector &verts, + std::vector &norm_values, + std::vector &uv_values, + std::vector &hvertices) +{ + /* Get untransformed vertices, there's a xform under the hair. */ + float inv_mat[4][4]; + invert_m4_m4_safe(inv_mat, m_object->obmat); + + MTFace *mtface = mesh->mtface; + MFace *mface = mesh->mface; + MVert *mverts = mesh->mvert; + + if ((!mtface || !mface) && !m_uv_warning_shown) { + std::fprintf(stderr, + "Warning, no UV set found for underlying geometry of %s.\n", + m_object->id.name + 2); + m_uv_warning_shown = true; + } + + ParticleData *pa = m_psys->particles; + int k; + + ParticleCacheKey **cache = m_psys->pathcache; + ParticleCacheKey *path; + float normal[3]; + Imath::V3f tmp_nor; + + for (int p = 0; p < m_psys->totpart; p++, pa++) { + /* underlying info for faces-only emission */ + path = cache[p]; + + /* Write UV and normal vectors */ + if (part->from == PART_FROM_FACE && mtface) { + const int num = pa->num_dmcache >= 0 ? pa->num_dmcache : pa->num; + + if (num < mesh->totface) { + /* TODO(Sybren): check whether the NULL check here and if(mface) are actually required */ + MFace *face = mface == NULL ? NULL : &mface[num]; + MTFace *tface = mtface + num; + + if (mface) { + float r_uv[2], mapfw[4], vec[3]; + + psys_interpolate_uvs(tface, face->v4, pa->fuv, r_uv); + uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1])); + + psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, normal, NULL, NULL, NULL); + + copy_yup_from_zup(tmp_nor.getValue(), normal); + norm_values.push_back(tmp_nor); + } + } + else { + std::fprintf(stderr, "Particle to faces overflow (%d/%d)\n", num, mesh->totface); + } + } + else if (part->from == PART_FROM_VERT && mtface) { + /* vertex id */ + const int num = (pa->num_dmcache >= 0) ? pa->num_dmcache : pa->num; + + /* iterate over all faces to find a corresponding underlying UV */ + for (int n = 0; n < mesh->totface; n++) { + MFace *face = &mface[n]; + MTFace *tface = mtface + n; + unsigned int vtx[4]; + vtx[0] = face->v1; + vtx[1] = face->v2; + vtx[2] = face->v3; + vtx[3] = face->v4; + bool found = false; + + for (int o = 0; o < 4; o++) { + if (o > 2 && vtx[o] == 0) { + break; + } + + if (vtx[o] == num) { + uv_values.push_back(Imath::V2f(tface->uv[o][0], tface->uv[o][1])); + + MVert *mv = mverts + vtx[o]; + + normal_short_to_float_v3(normal, mv->no); + copy_yup_from_zup(tmp_nor.getValue(), normal); + norm_values.push_back(tmp_nor); + found = true; + break; + } + } + + if (found) { + break; + } + } + } + + int steps = path->segments + 1; + hvertices.push_back(steps); + + for (k = 0; k < steps; k++, path++) { + float vert[3]; + copy_v3_v3(vert, path->co); + mul_m4_v3(inv_mat, vert); + + /* Convert Z-up to Y-up. */ + verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1])); + } + } +} + +void AbcHairWriter::write_hair_child_sample(Mesh *mesh, + ParticleSettings *part, + std::vector &verts, + std::vector &norm_values, + std::vector &uv_values, + std::vector &hvertices) +{ + /* Get untransformed vertices, there's a xform under the hair. */ + float inv_mat[4][4]; + invert_m4_m4_safe(inv_mat, m_object->obmat); + + MTFace *mtface = mesh->mtface; + MVert *mverts = mesh->mvert; + + ParticleCacheKey **cache = m_psys->childcache; + ParticleCacheKey *path; + + ChildParticle *pc = m_psys->child; + + for (int p = 0; p < m_psys->totchild; p++, pc++) { + path = cache[p]; + + if (part->from == PART_FROM_FACE && part->childtype != PART_CHILD_PARTICLES && mtface) { + const int num = pc->num; + if (num < 0) { + ABC_LOG(m_settings.logger) + << "Warning, child particle of hair system " << m_psys->name + << " has unknown face index of geometry of " << (m_object->id.name + 2) + << ", skipping child hair." << std::endl; + continue; + } + + MFace *face = &mesh->mface[num]; + MTFace *tface = mtface + num; + + float r_uv[2], tmpnor[3], mapfw[4], vec[3]; + + psys_interpolate_uvs(tface, face->v4, pc->fuv, r_uv); + uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1])); + + psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, tmpnor, NULL, NULL, NULL); + + /* Convert Z-up to Y-up. */ + norm_values.push_back(Imath::V3f(tmpnor[0], tmpnor[2], -tmpnor[1])); + } + else { + if (uv_values.size()) { + uv_values.push_back(uv_values[pc->parent]); + } + if (norm_values.size()) { + norm_values.push_back(norm_values[pc->parent]); + } + } + + int steps = path->segments + 1; + hvertices.push_back(steps); + + for (int k = 0; k < steps; k++) { + float vert[3]; + copy_v3_v3(vert, path->co); + mul_m4_v3(inv_mat, vert); + + /* Convert Z-up to Y-up. */ + verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1])); + + path++; + } + } +} diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.h b/source/blender/io/alembic/exporter/abc_writer_hair.h new file mode 100644 index 00000000000..67d1b7b3d23 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_hair.h @@ -0,0 +1,62 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_HAIR_H__ +#define __ABC_WRITER_HAIR_H__ + +#include "abc_writer_object.h" + +struct ParticleSettings; +struct ParticleSystem; + +class AbcHairWriter : public AbcObjectWriter { + ParticleSystem *m_psys; + + Alembic::AbcGeom::OCurvesSchema m_schema; + Alembic::AbcGeom::OCurvesSchema::Sample m_sample; + + bool m_uv_warning_shown; + + public: + AbcHairWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys); + + private: + virtual void do_write(); + + void write_hair_sample(struct Mesh *mesh, + ParticleSettings *part, + std::vector &verts, + std::vector &norm_values, + std::vector &uv_values, + std::vector &hvertices); + + void write_hair_child_sample(struct Mesh *mesh, + ParticleSettings *part, + std::vector &verts, + std::vector &norm_values, + std::vector &uv_values, + std::vector &hvertices); +}; + +#endif /* __ABC_WRITER_HAIR_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_mball.cc b/source/blender/io/alembic/exporter/abc_writer_mball.cc new file mode 100644 index 00000000000..3593acf18b0 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_mball.cc @@ -0,0 +1,95 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_mball.h" +#include "abc_writer_mesh.h" + +#include "DNA_mesh_types.h" +#include "DNA_meta_types.h" +#include "DNA_object_types.h" + +#include "BKE_displist.h" +#include "BKE_lib_id.h" +#include "BKE_mball.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +#include "BLI_utildefines.h" + +AbcMBallWriter::AbcMBallWriter(Main *bmain, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcGenericMeshWriter(ob, parent, time_sampling, settings), m_bmain(bmain) +{ + m_is_animated = isAnimated(); +} + +AbcMBallWriter::~AbcMBallWriter() +{ +} + +bool AbcMBallWriter::isAnimated() const +{ + return true; +} + +Mesh *AbcMBallWriter::getEvaluatedMesh(Scene * /*scene_eval*/, Object *ob_eval, bool &r_needsfree) +{ + Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); + if (mesh_eval != NULL) { + /* Mesh_eval only exists when generative modifiers are in use. */ + r_needsfree = false; + return mesh_eval; + } + r_needsfree = true; + + /* The approach below is copied from BKE_mesh_new_from_object() */ + Mesh *tmpmesh = BKE_mesh_add(m_bmain, ((ID *)m_object->data)->name + 2); + BLI_assert(tmpmesh != NULL); + + /* BKE_mesh_add gives us a user count we don't need */ + id_us_min(&tmpmesh->id); + + ListBase disp = {NULL, NULL}; + /* TODO(sergey): This is gonna to work for until Depsgraph + * only contains for_render flag. As soon as CoW is + * implemented, this is to be rethought. + */ + BKE_displist_make_mball_forRender(m_settings.depsgraph, m_settings.scene, m_object, &disp); + BKE_mesh_from_metaball(&disp, tmpmesh); + BKE_displist_free(&disp); + + BKE_mesh_texspace_copy_from_object(tmpmesh, m_object); + + return tmpmesh; +} + +void AbcMBallWriter::freeEvaluatedMesh(struct Mesh *mesh) +{ + BKE_id_free(m_bmain, mesh); +} + +bool AbcMBallWriter::isBasisBall(Scene *scene, Object *ob) +{ + Object *basis_ob = BKE_mball_basis_find(scene, ob); + return ob == basis_ob; +} diff --git a/source/blender/io/alembic/exporter/abc_writer_mball.h b/source/blender/io/alembic/exporter/abc_writer_mball.h new file mode 100644 index 00000000000..e3ac1e69cae --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_mball.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_MBALL_H__ +#define __ABC_WRITER_MBALL_H__ + +#include "abc_writer_mesh.h" +#include "abc_writer_object.h" + +struct Main; +struct Object; + +/* AbcMBallWriter converts the metaballs to meshes at every frame, + * and defers to AbcGenericMeshWriter to perform the writing + * to the Alembic file. Only the basis balls are exported, as this + * results in the entire shape as one mesh. */ +class AbcMBallWriter : public AbcGenericMeshWriter { + Main *m_bmain; + + public: + explicit AbcMBallWriter(Main *bmain, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + ~AbcMBallWriter(); + + static bool isBasisBall(Scene *scene, Object *ob); + + protected: + Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override; + void freeEvaluatedMesh(struct Mesh *mesh) override; + + private: + bool isAnimated() const override; +}; + +#endif /* __ABC_WRITER_MBALL_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc new file mode 100644 index 00000000000..b17e123b426 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -0,0 +1,591 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_mesh.h" +#include "abc_writer_transform.h" +#include "intern/abc_axis_conversion.h" + +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_fluidsim_types.h" + +#include "BKE_anim_data.h" +#include "BKE_key.h" +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_modifier.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include "DEG_depsgraph_query.h" + +using Alembic::Abc::FloatArraySample; +using Alembic::Abc::Int32ArraySample; +using Alembic::Abc::V2fArraySample; +using Alembic::Abc::V3fArraySample; + +using Alembic::AbcGeom::kFacevaryingScope; +using Alembic::AbcGeom::OBoolProperty; +using Alembic::AbcGeom::OCompoundProperty; +using Alembic::AbcGeom::OFaceSet; +using Alembic::AbcGeom::OFaceSetSchema; +using Alembic::AbcGeom::ON3fGeomParam; +using Alembic::AbcGeom::OPolyMesh; +using Alembic::AbcGeom::OPolyMeshSchema; +using Alembic::AbcGeom::OSubD; +using Alembic::AbcGeom::OSubDSchema; +using Alembic::AbcGeom::OV2fGeomParam; +using Alembic::AbcGeom::UInt32ArraySample; + +/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */ + +static void get_vertices(struct Mesh *mesh, std::vector &points) +{ + points.clear(); + points.resize(mesh->totvert); + + MVert *verts = mesh->mvert; + + for (int i = 0, e = mesh->totvert; i < e; i++) { + copy_yup_from_zup(points[i].getValue(), verts[i].co); + } +} + +static void get_topology(struct Mesh *mesh, + std::vector &poly_verts, + std::vector &loop_counts, + bool &r_has_flat_shaded_poly) +{ + const int num_poly = mesh->totpoly; + const int num_loops = mesh->totloop; + MLoop *mloop = mesh->mloop; + MPoly *mpoly = mesh->mpoly; + r_has_flat_shaded_poly = false; + + poly_verts.clear(); + loop_counts.clear(); + poly_verts.reserve(num_loops); + loop_counts.reserve(num_poly); + + /* NOTE: data needs to be written in the reverse order. */ + for (int i = 0; i < num_poly; i++) { + MPoly &poly = mpoly[i]; + loop_counts.push_back(poly.totloop); + + r_has_flat_shaded_poly |= (poly.flag & ME_SMOOTH) == 0; + + MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1); + + for (int j = 0; j < poly.totloop; j++, loop--) { + poly_verts.push_back(loop->v); + } + } +} + +static void get_creases(struct Mesh *mesh, + std::vector &indices, + std::vector &lengths, + std::vector &sharpnesses) +{ + const float factor = 1.0f / 255.0f; + + indices.clear(); + lengths.clear(); + sharpnesses.clear(); + + MEdge *edge = mesh->medge; + + for (int i = 0, e = mesh->totedge; i < e; i++) { + const float sharpness = static_cast(edge[i].crease) * factor; + + if (sharpness != 0.0f) { + indices.push_back(edge[i].v1); + indices.push_back(edge[i].v2); + sharpnesses.push_back(sharpness); + } + } + + lengths.resize(sharpnesses.size(), 2); +} + +static void get_loop_normals(struct Mesh *mesh, + std::vector &normals, + bool has_flat_shaded_poly) +{ + normals.clear(); + + /* If all polygons are smooth shaded, and there are no custom normals, we don't need to export + * normals at all. This is also done by other software, see T71246. */ + if (!has_flat_shaded_poly && !CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL) && + (mesh->flag & ME_AUTOSMOOTH) == 0) { + return; + } + + BKE_mesh_calc_normals_split(mesh); + const float(*lnors)[3] = static_cast(CustomData_get_layer(&mesh->ldata, CD_NORMAL)); + BLI_assert(lnors != NULL || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL"); + + normals.resize(mesh->totloop); + + /* NOTE: data needs to be written in the reverse order. */ + int abc_index = 0; + MPoly *mp = mesh->mpoly; + for (int i = 0, e = mesh->totpoly; i < e; i++, mp++) { + for (int j = mp->totloop - 1; j >= 0; j--, abc_index++) { + int blender_index = mp->loopstart + j; + copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]); + } + } +} + +/* *************** Modifiers *************** */ + +/* check if the mesh is a subsurf, ignoring disabled modifiers and + * displace if it's after subsurf. */ +static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob) +{ + ModifierData *md = static_cast(ob->modifiers.last); + + for (; md; md = md->prev) { + if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) { + continue; + } + + if (md->type == eModifierType_Subsurf) { + SubsurfModifierData *smd = reinterpret_cast(md); + + if (smd->subdivType == ME_CC_SUBSURF) { + return md; + } + } + + /* mesh is not a subsurf. break */ + if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) { + return NULL; + } + } + + return NULL; +} + +static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob) +{ + ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluidsim); + + if (md && (BKE_modifier_is_enabled(scene, md, eModifierMode_Render))) { + FluidsimModifierData *fsmd = reinterpret_cast(md); + + if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) { + return md; + } + } + + return NULL; +} + +/* ************************************************************************** */ + +AbcGenericMeshWriter::AbcGenericMeshWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(ob, time_sampling, settings, parent) +{ + m_is_animated = isAnimated(); + m_subsurf_mod = NULL; + m_is_subd = false; + + /* If the object is static, use the default static time sampling. */ + if (!m_is_animated) { + time_sampling = 0; + } + + if (!m_settings.apply_subdiv) { + m_subsurf_mod = get_subsurf_modifier(m_settings.scene, m_object); + m_is_subd = (m_subsurf_mod != NULL); + } + + m_is_liquid = (get_liquid_sim_modifier(m_settings.scene, m_object) != NULL); + + while (parent->alembicXform().getChildHeader(m_name)) { + m_name.append("_"); + } + + if (m_settings.use_subdiv_schema && m_is_subd) { + OSubD subd(parent->alembicXform(), m_name, m_time_sampling); + m_subdiv_schema = subd.getSchema(); + } + else { + OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling); + m_mesh_schema = mesh.getSchema(); + + OCompoundProperty typeContainer = m_mesh_schema.getUserProperties(); + OBoolProperty type(typeContainer, "meshtype"); + type.set(m_is_subd); + } +} + +AbcGenericMeshWriter::~AbcGenericMeshWriter() +{ + if (m_subsurf_mod) { + m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; + } +} + +bool AbcGenericMeshWriter::isAnimated() const +{ + if (BKE_animdata_id_is_animated(static_cast(m_object->data))) { + return true; + } + if (BKE_key_from_object(m_object) != NULL) { + return true; + } + + /* Test modifiers. */ + ModifierData *md = static_cast(m_object->modifiers.first); + while (md) { + + if (md->type != eModifierType_Subsurf) { + return true; + } + + md = md->next; + } + + return false; +} + +void AbcGenericMeshWriter::setIsAnimated(bool is_animated) +{ + m_is_animated = is_animated; +} + +void AbcGenericMeshWriter::do_write() +{ + /* We have already stored a sample for this object. */ + if (!m_first_frame && !m_is_animated) { + return; + } + + bool needsfree; + struct Mesh *mesh = getFinalMesh(needsfree); + + try { + if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) { + writeSubD(mesh); + } + else { + writeMesh(mesh); + } + + if (needsfree) { + freeEvaluatedMesh(mesh); + } + } + catch (...) { + if (needsfree) { + freeEvaluatedMesh(mesh); + } + throw; + } +} + +void AbcGenericMeshWriter::freeEvaluatedMesh(struct Mesh *mesh) +{ + BKE_id_free(NULL, mesh); +} + +void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh) +{ + std::vector points, normals; + std::vector poly_verts, loop_counts; + std::vector velocities; + bool has_flat_shaded_poly = false; + + get_vertices(mesh, points); + get_topology(mesh, poly_verts, loop_counts, has_flat_shaded_poly); + + if (m_first_frame && m_settings.export_face_sets) { + writeFaceSets(mesh, m_mesh_schema); + } + + m_mesh_sample = OPolyMeshSchema::Sample( + V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts)); + + UVSample sample; + if (m_settings.export_uvs) { + const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata); + + if (!sample.indices.empty() && !sample.uvs.empty()) { + OV2fGeomParam::Sample uv_sample; + uv_sample.setVals(V2fArraySample(sample.uvs)); + uv_sample.setIndices(UInt32ArraySample(sample.indices)); + uv_sample.setScope(kFacevaryingScope); + + m_mesh_schema.setUVSourceName(name); + m_mesh_sample.setUVs(uv_sample); + } + + write_custom_data( + m_mesh_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV); + } + + if (m_settings.export_normals) { + get_loop_normals(mesh, normals, has_flat_shaded_poly); + + ON3fGeomParam::Sample normals_sample; + if (!normals.empty()) { + normals_sample.setScope(kFacevaryingScope); + normals_sample.setVals(V3fArraySample(normals)); + } + + m_mesh_sample.setNormals(normals_sample); + } + + if (m_is_liquid) { + getVelocities(mesh, velocities); + m_mesh_sample.setVelocities(V3fArraySample(velocities)); + } + + m_mesh_sample.setSelfBounds(bounds()); + + m_mesh_schema.set(m_mesh_sample); + + writeArbGeoParams(mesh); +} + +void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh) +{ + std::vector crease_sharpness; + std::vector points; + std::vector poly_verts, loop_counts; + std::vector crease_indices, crease_lengths; + bool has_flat_poly = false; + + get_vertices(mesh, points); + get_topology(mesh, poly_verts, loop_counts, has_flat_poly); + get_creases(mesh, crease_indices, crease_lengths, crease_sharpness); + + if (m_first_frame && m_settings.export_face_sets) { + writeFaceSets(mesh, m_subdiv_schema); + } + + m_subdiv_sample = OSubDSchema::Sample( + V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts)); + + UVSample sample; + if (m_first_frame && m_settings.export_uvs) { + const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata); + + if (!sample.indices.empty() && !sample.uvs.empty()) { + OV2fGeomParam::Sample uv_sample; + uv_sample.setVals(V2fArraySample(sample.uvs)); + uv_sample.setIndices(UInt32ArraySample(sample.indices)); + uv_sample.setScope(kFacevaryingScope); + + m_subdiv_schema.setUVSourceName(name); + m_subdiv_sample.setUVs(uv_sample); + } + + write_custom_data( + m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV); + } + + if (!crease_indices.empty()) { + m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices)); + m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths)); + m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness)); + } + + m_subdiv_sample.setSelfBounds(bounds()); + m_subdiv_schema.set(m_subdiv_sample); + + writeArbGeoParams(mesh); +} + +template void AbcGenericMeshWriter::writeFaceSets(struct Mesh *me, Schema &schema) +{ + std::map> geo_groups; + getGeoGroups(me, geo_groups); + + std::map>::iterator it; + for (it = geo_groups.begin(); it != geo_groups.end(); ++it) { + OFaceSet face_set = schema.createFaceSet(it->first); + OFaceSetSchema::Sample samp; + samp.setFaces(Int32ArraySample(it->second)); + face_set.getSchema().set(samp); + } +} + +Mesh *AbcGenericMeshWriter::getFinalMesh(bool &r_needsfree) +{ + /* We don't want subdivided mesh data */ + if (m_subsurf_mod) { + m_subsurf_mod->mode |= eModifierMode_DisableTemporary; + } + + r_needsfree = false; + + Scene *scene = DEG_get_evaluated_scene(m_settings.depsgraph); + Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object); + struct Mesh *mesh = getEvaluatedMesh(scene, ob_eval, r_needsfree); + + if (m_subsurf_mod) { + m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; + } + + if (m_settings.triangulate) { + const bool tag_only = false; + const int quad_method = m_settings.quad_method; + const int ngon_method = m_settings.ngon_method; + + struct BMeshCreateParams bmcp = {false}; + struct BMeshFromMeshParams bmfmp = {true, false, false, 0}; + BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmcp, &bmfmp); + + BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, NULL, NULL, NULL); + + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); + BM_mesh_free(bm); + + if (r_needsfree) { + BKE_id_free(NULL, mesh); + } + + mesh = result; + r_needsfree = true; + } + + m_custom_data_config.pack_uvs = m_settings.pack_uv; + m_custom_data_config.mpoly = mesh->mpoly; + m_custom_data_config.mloop = mesh->mloop; + m_custom_data_config.totpoly = mesh->totpoly; + m_custom_data_config.totloop = mesh->totloop; + m_custom_data_config.totvert = mesh->totvert; + + return mesh; +} + +void AbcGenericMeshWriter::writeArbGeoParams(struct Mesh *me) +{ + if (m_is_liquid) { + /* We don't need anything more for liquid meshes. */ + return; + } + + if (m_first_frame && m_settings.export_vcols) { + if (m_subdiv_schema.valid()) { + write_custom_data( + m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL); + } + else { + write_custom_data( + m_mesh_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL); + } + } +} + +void AbcGenericMeshWriter::getVelocities(struct Mesh *mesh, std::vector &vels) +{ + const int totverts = mesh->totvert; + + vels.clear(); + vels.resize(totverts); + + ModifierData *md = get_liquid_sim_modifier(m_settings.scene, m_object); + FluidsimModifierData *fmd = reinterpret_cast(md); + FluidsimSettings *fss = fmd->fss; + + if (fss->meshVelocities) { + float *mesh_vels = reinterpret_cast(fss->meshVelocities); + + for (int i = 0; i < totverts; i++) { + copy_yup_from_zup(vels[i].getValue(), mesh_vels); + mesh_vels += 3; + } + } + else { + std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f)); + } +} + +void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh, + std::map> &geo_groups) +{ + const int num_poly = mesh->totpoly; + MPoly *polygons = mesh->mpoly; + + for (int i = 0; i < num_poly; i++) { + MPoly ¤t_poly = polygons[i]; + short mnr = current_poly.mat_nr; + + Material *mat = BKE_object_material_get(m_object, mnr + 1); + + if (!mat) { + continue; + } + + std::string name = get_id_name(&mat->id); + + if (geo_groups.find(name) == geo_groups.end()) { + std::vector faceArray; + geo_groups[name] = faceArray; + } + + geo_groups[name].push_back(i); + } + + if (geo_groups.size() == 0) { + Material *mat = BKE_object_material_get(m_object, 1); + + std::string name = (mat) ? get_id_name(&mat->id) : "default"; + + std::vector faceArray; + + for (int i = 0, e = mesh->totface; i < e; i++) { + faceArray.push_back(i); + } + + geo_groups[name] = faceArray; + } +} + +AbcMeshWriter::AbcMeshWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcGenericMeshWriter(ob, parent, time_sampling, settings) +{ +} + +AbcMeshWriter::~AbcMeshWriter() +{ +} + +Mesh *AbcMeshWriter::getEvaluatedMesh(Scene *scene_eval, + Object *ob_eval, + bool &UNUSED(r_needsfree)) +{ + return mesh_get_eval_final(m_settings.depsgraph, scene_eval, ob_eval, &CD_MASK_MESH); +} diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h new file mode 100644 index 00000000000..264696333a3 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h @@ -0,0 +1,91 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_MESH_H__ +#define __ABC_WRITER_MESH_H__ + +#include "abc_writer_object.h" +#include "intern/abc_customdata.h" + +struct Mesh; +struct ModifierData; + +/* Writer for Alembic meshes. Does not assume the object is a mesh object. */ +class AbcGenericMeshWriter : public AbcObjectWriter { + protected: + Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema; + Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample; + + Alembic::AbcGeom::OSubDSchema m_subdiv_schema; + Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample; + + Alembic::Abc::OArrayProperty m_mat_indices; + + bool m_is_animated; + ModifierData *m_subsurf_mod; + + CDStreamConfig m_custom_data_config; + + bool m_is_liquid; + bool m_is_subd; + + public: + AbcGenericMeshWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + ~AbcGenericMeshWriter(); + void setIsAnimated(bool is_animated); + + protected: + virtual void do_write(); + virtual bool isAnimated() const; + virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) = 0; + virtual void freeEvaluatedMesh(struct Mesh *mesh); + + Mesh *getFinalMesh(bool &r_needsfree); + + void writeMesh(struct Mesh *mesh); + void writeSubD(struct Mesh *mesh); + + void writeArbGeoParams(struct Mesh *mesh); + void getGeoGroups(struct Mesh *mesh, std::map> &geoGroups); + + /* fluid surfaces support */ + void getVelocities(struct Mesh *mesh, std::vector &vels); + + template void writeFaceSets(struct Mesh *mesh, Schema &schema); +}; + +class AbcMeshWriter : public AbcGenericMeshWriter { + public: + AbcMeshWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + ~AbcMeshWriter(); + + protected: + virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override; +}; + +#endif /* __ABC_WRITER_MESH_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc new file mode 100644 index 00000000000..c856f894f0e --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc @@ -0,0 +1,170 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_nurbs.h" +#include "abc_writer_transform.h" +#include "intern/abc_axis_conversion.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "BLI_listbase.h" + +#include "BKE_curve.h" + +using Alembic::AbcGeom::FloatArraySample; +using Alembic::AbcGeom::OBoolProperty; +using Alembic::AbcGeom::OCompoundProperty; +using Alembic::AbcGeom::ONuPatch; +using Alembic::AbcGeom::ONuPatchSchema; + +AbcNurbsWriter::AbcNurbsWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(ob, time_sampling, settings, parent) +{ + m_is_animated = isAnimated(); + + /* if the object is static, use the default static time sampling */ + if (!m_is_animated) { + m_time_sampling = 0; + } + + Curve *curve = static_cast(m_object->data); + size_t numNurbs = BLI_listbase_count(&curve->nurb); + + for (size_t i = 0; i < numNurbs; i++) { + std::stringstream str; + str << m_name << '_' << i; + + while (parent->alembicXform().getChildHeader(str.str())) { + str << "_"; + } + + ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling); + m_nurbs_schema.push_back(nurbs.getSchema()); + } +} + +bool AbcNurbsWriter::isAnimated() const +{ + /* check if object has shape keys */ + Curve *cu = static_cast(m_object->data); + return (cu->key != NULL); +} + +static void get_knots(std::vector &knots, const int num_knots, float *nu_knots) +{ + if (num_knots <= 1) { + return; + } + + /* Add an extra knot at the beginning and end of the array since most apps + * require/expect them. */ + knots.reserve(num_knots + 2); + + knots.push_back(0.0f); + + for (int i = 0; i < num_knots; i++) { + knots.push_back(nu_knots[i]); + } + + knots[0] = 2.0f * knots[1] - knots[2]; + knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]); +} + +void AbcNurbsWriter::do_write() +{ + /* we have already stored a sample for this object. */ + if (!m_first_frame && !m_is_animated) { + return; + } + + if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) { + return; + } + + Curve *curve = static_cast(m_object->data); + ListBase *nulb; + + if (m_object->runtime.curve_cache->deformed_nurbs.first != NULL) { + nulb = &m_object->runtime.curve_cache->deformed_nurbs; + } + else { + nulb = BKE_curve_nurbs_get(curve); + } + + size_t count = 0; + for (Nurb *nu = static_cast(nulb->first); nu; nu = nu->next, count++) { + std::vector knotsU; + get_knots(knotsU, KNOTSU(nu), nu->knotsu); + + std::vector knotsV; + get_knots(knotsV, KNOTSV(nu), nu->knotsv); + + const int size = nu->pntsu * nu->pntsv; + std::vector positions(size); + std::vector weights(size); + + const BPoint *bp = nu->bp; + + for (int i = 0; i < size; i++, bp++) { + copy_yup_from_zup(positions[i].getValue(), bp->vec); + weights[i] = bp->vec[3]; + } + + ONuPatchSchema::Sample sample; + sample.setUOrder(nu->orderu + 1); + sample.setVOrder(nu->orderv + 1); + sample.setPositions(positions); + sample.setPositionWeights(weights); + sample.setUKnot(FloatArraySample(knotsU)); + sample.setVKnot(FloatArraySample(knotsV)); + sample.setNu(nu->pntsu); + sample.setNv(nu->pntsv); + + /* TODO(kevin): to accommodate other software we should duplicate control + * points to indicate that a NURBS is cyclic. */ + OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties(); + + if ((nu->flagu & CU_NURB_ENDPOINT) != 0) { + OBoolProperty prop(user_props, "endpoint_u"); + prop.set(true); + } + + if ((nu->flagv & CU_NURB_ENDPOINT) != 0) { + OBoolProperty prop(user_props, "endpoint_v"); + prop.set(true); + } + + if ((nu->flagu & CU_NURB_CYCLIC) != 0) { + OBoolProperty prop(user_props, "cyclic_u"); + prop.set(true); + } + + if ((nu->flagv & CU_NURB_CYCLIC) != 0) { + OBoolProperty prop(user_props, "cyclic_v"); + prop.set(true); + } + + m_nurbs_schema[count].set(sample); + } +} diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.h b/source/blender/io/alembic/exporter/abc_writer_nurbs.h new file mode 100644 index 00000000000..c6a3c399b66 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.h @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_NURBS_H__ +#define __ABC_WRITER_NURBS_H__ + +#include "abc_writer_object.h" + +class AbcNurbsWriter : public AbcObjectWriter { + std::vector m_nurbs_schema; + bool m_is_animated; + + public: + AbcNurbsWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + private: + virtual void do_write(); + + bool isAnimated() const; +}; + +#endif /* __ABC_WRITER_NURBS_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_object.cc b/source/blender/io/alembic/exporter/abc_writer_object.cc new file mode 100644 index 00000000000..3d280d9f0a4 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_object.cc @@ -0,0 +1,87 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_object.h" + +#include "DNA_object_types.h" + +#include "BKE_object.h" + +AbcObjectWriter::AbcObjectWriter(Object *ob, + uint32_t time_sampling, + ExportSettings &settings, + AbcObjectWriter *parent) + : m_object(ob), m_settings(settings), m_time_sampling(time_sampling), m_first_frame(true) +{ + /* This class is used as superclass for objects themselves (i.e. transforms) and for object + * data (meshes, curves, cameras, etc.). However, when writing transforms, the m_name field is + * ignored. This is a temporary tweak to get the exporter to write object data with the data + * name instead of the object name in a safe way. */ + if (m_object->data == nullptr) { + m_name = get_id_name(m_object); + } + else { + ID *ob_data = static_cast(m_object->data); + m_name = get_id_name(ob_data); + } + + if (parent) { + parent->addChild(this); + } +} + +AbcObjectWriter::~AbcObjectWriter() +{ +} + +void AbcObjectWriter::addChild(AbcObjectWriter *child) +{ + m_children.push_back(child); +} + +Imath::Box3d AbcObjectWriter::bounds() +{ + BoundBox *bb = BKE_object_boundbox_get(this->m_object); + + if (!bb) { + if (this->m_object->type != OB_CAMERA) { + ABC_LOG(m_settings.logger) << "Bounding box is null!\n"; + } + + return Imath::Box3d(); + } + + /* Convert Z-up to Y-up. This also changes which vector goes into which min/max property. */ + this->m_bounds.min.x = bb->vec[0][0]; + this->m_bounds.min.y = bb->vec[0][2]; + this->m_bounds.min.z = -bb->vec[6][1]; + + this->m_bounds.max.x = bb->vec[6][0]; + this->m_bounds.max.y = bb->vec[6][2]; + this->m_bounds.max.z = -bb->vec[0][1]; + + return this->m_bounds; +} + +void AbcObjectWriter::write() +{ + do_write(); + m_first_frame = false; +} diff --git a/source/blender/io/alembic/exporter/abc_writer_object.h b/source/blender/io/alembic/exporter/abc_writer_object.h new file mode 100644 index 00000000000..830c4aee903 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_object.h @@ -0,0 +1,69 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_OBJECT_H__ +#define __ABC_WRITER_OBJECT_H__ + +#include +#include + +#include "abc_exporter.h" + +#include "DNA_ID.h" + +class AbcTransformWriter; + +struct Main; +struct Object; + +class AbcObjectWriter { + protected: + Object *m_object; + ExportSettings &m_settings; + + uint32_t m_time_sampling; + + Imath::Box3d m_bounds; + std::vector m_children; + + std::vector> m_props; + + bool m_first_frame; + std::string m_name; + + public: + AbcObjectWriter(Object *ob, + uint32_t time_sampling, + ExportSettings &settings, + AbcObjectWriter *parent = NULL); + + virtual ~AbcObjectWriter(); + + void addChild(AbcObjectWriter *child); + + virtual Imath::Box3d bounds(); + + void write(); + + private: + virtual void do_write() = 0; +}; + +#endif /* __ABC_WRITER_OBJECT_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_points.cc b/source/blender/io/alembic/exporter/abc_writer_points.cc new file mode 100644 index 00000000000..27f51586f47 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_points.cc @@ -0,0 +1,122 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_points.h" +#include "abc_writer_mesh.h" +#include "abc_writer_transform.h" +#include "intern/abc_util.h" + +#include "DNA_object_types.h" +#include "DNA_particle_types.h" + +#include "BKE_lattice.h" +#include "BKE_particle.h" + +#include "BLI_math.h" + +#include "DEG_depsgraph_query.h" + +using Alembic::AbcGeom::kVertexScope; +using Alembic::AbcGeom::OPoints; +using Alembic::AbcGeom::OPointsSchema; + +/* ************************************************************************** */ + +AbcPointsWriter::AbcPointsWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys) + : AbcObjectWriter(ob, time_sampling, settings, parent) +{ + m_psys = psys; + + std::string psys_name = get_valid_abc_name(psys->name); + OPoints points(parent->alembicXform(), psys_name, m_time_sampling); + m_schema = points.getSchema(); +} + +void AbcPointsWriter::do_write() +{ + if (!m_psys) { + return; + } + + std::vector points; + std::vector velocities; + std::vector widths; + std::vector ids; + + ParticleKey state; + + ParticleSimulationData sim; + sim.depsgraph = m_settings.depsgraph; + sim.scene = m_settings.scene; + sim.ob = m_object; + sim.psys = m_psys; + + m_psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); + + uint64_t index = 0; + for (int p = 0; p < m_psys->totpart; p++) { + float pos[3], vel[3]; + + if (m_psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) { + continue; + } + + state.time = DEG_get_ctime(m_settings.depsgraph); + + if (psys_get_particle_state(&sim, p, &state, 0) == 0) { + continue; + } + + /* location */ + mul_v3_m4v3(pos, m_object->imat, state.co); + + /* velocity */ + sub_v3_v3v3(vel, state.co, m_psys->particles[p].prev_state.co); + + /* Convert Z-up to Y-up. */ + points.push_back(Imath::V3f(pos[0], pos[2], -pos[1])); + velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1])); + widths.push_back(m_psys->particles[p].size); + ids.push_back(index++); + } + + if (m_psys->lattice_deform_data) { + BKE_lattice_deform_data_destroy(m_psys->lattice_deform_data); + m_psys->lattice_deform_data = NULL; + } + + Alembic::Abc::P3fArraySample psample(points); + Alembic::Abc::UInt64ArraySample idsample(ids); + Alembic::Abc::V3fArraySample vsample(velocities); + Alembic::Abc::FloatArraySample wsample_array(widths); + Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope); + + m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample); + m_sample.setSelfBounds(bounds()); + + m_schema.set(m_sample); +} diff --git a/source/blender/io/alembic/exporter/abc_writer_points.h b/source/blender/io/alembic/exporter/abc_writer_points.h new file mode 100644 index 00000000000..e5c1b81f266 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_points.h @@ -0,0 +1,49 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_POINTS_H__ +#define __ABC_WRITER_POINTS_H__ + +#include "abc_writer_object.h" +#include "intern/abc_customdata.h" + +struct ParticleSystem; + +/* ************************************************************************** */ + +class AbcPointsWriter : public AbcObjectWriter { + Alembic::AbcGeom::OPointsSchema m_schema; + Alembic::AbcGeom::OPointsSchema::Sample m_sample; + ParticleSystem *m_psys; + + public: + AbcPointsWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys); + + void do_write(); +}; + +#endif /* __ABC_WRITER_POINTS_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.cc b/source/blender/io/alembic/exporter/abc_writer_transform.cc new file mode 100644 index 00000000000..46130cb8675 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_transform.cc @@ -0,0 +1,124 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_transform.h" +#include "intern/abc_axis_conversion.h" + +#include + +#include "DNA_object_types.h" + +#include "BLI_math.h" + +#include "DEG_depsgraph_query.h" + +using Alembic::AbcGeom::OObject; +using Alembic::AbcGeom::OXform; + +AbcTransformWriter::AbcTransformWriter(Object *ob, + const OObject &abc_parent, + AbcTransformWriter *parent, + unsigned int time_sampling, + ExportSettings &settings) + : AbcObjectWriter(ob, time_sampling, settings, parent), m_proxy_from(NULL) +{ + m_is_animated = hasAnimation(m_object); + + if (!m_is_animated) { + time_sampling = 0; + } + + m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling); + m_schema = m_xform.getSchema(); + + /* Blender objects can't have a parent without inheriting the transform. */ + m_inherits_xform = parent != NULL; +} + +void AbcTransformWriter::do_write() +{ + Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object); + + if (m_first_frame) { + m_visibility = Alembic::AbcGeom::CreateVisibilityProperty( + m_xform, m_xform.getSchema().getTimeSampling()); + } + + m_visibility.set(!(ob_eval->restrictflag & OB_RESTRICT_VIEWPORT)); + + if (!m_first_frame && !m_is_animated) { + return; + } + + float yup_mat[4][4]; + create_transform_matrix( + ob_eval, yup_mat, m_inherits_xform ? ABC_MATRIX_LOCAL : ABC_MATRIX_WORLD, m_proxy_from); + + /* If the parent is a camera, undo its to-Maya rotation (see below). */ + bool is_root_object = !m_inherits_xform || ob_eval->parent == nullptr; + if (!is_root_object && ob_eval->parent->type == OB_CAMERA) { + float rot_mat[4][4]; + axis_angle_to_mat4_single(rot_mat, 'X', M_PI_2); + mul_m4_m4m4(yup_mat, rot_mat, yup_mat); + } + + /* If the object is a camera, apply an extra rotation to Maya camera orientation. */ + if (ob_eval->type == OB_CAMERA) { + float rot_mat[4][4]; + axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2); + mul_m4_m4m4(yup_mat, yup_mat, rot_mat); + } + + if (is_root_object) { + /* Only apply scaling to root objects, parenting will propagate it. */ + float scale_mat[4][4]; + scale_m4_fl(scale_mat, m_settings.global_scale); + scale_mat[3][3] = m_settings.global_scale; /* also scale translation */ + mul_m4_m4m4(yup_mat, yup_mat, scale_mat); + yup_mat[3][3] /= m_settings.global_scale; /* normalise the homogeneous component */ + } + + m_matrix = convert_matrix_datatype(yup_mat); + m_sample.setMatrix(m_matrix); + + /* Always export as "inherits transform", as this is the only way in which Blender works. The + * above code has already taken care of writing the correct matrix so that this option is not + * necessary. However, certain packages (for example the USD Alembic exporter) are incompatible + * with non-inheriting transforms and will completely ignore the transform if that is used. */ + m_sample.setInheritsXforms(true); + m_schema.set(m_sample); +} + +Imath::Box3d AbcTransformWriter::bounds() +{ + Imath::Box3d bounds; + + for (int i = 0; i < m_children.size(); i++) { + Imath::Box3d box(m_children[i]->bounds()); + bounds.extendBy(box); + } + + return Imath::transform(bounds, m_matrix); +} + +bool AbcTransformWriter::hasAnimation(Object * /*ob*/) const +{ + return true; +} diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.h b/source/blender/io/alembic/exporter/abc_writer_transform.h new file mode 100644 index 00000000000..4397b220761 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_transform.h @@ -0,0 +1,60 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_TRANSFORM_H__ +#define __ABC_WRITER_TRANSFORM_H__ + +#include "abc_writer_object.h" + +#include + +class AbcTransformWriter : public AbcObjectWriter { + Alembic::AbcGeom::OXform m_xform; + Alembic::AbcGeom::OXformSchema m_schema; + Alembic::AbcGeom::XformSample m_sample; + Alembic::AbcGeom::OVisibilityProperty m_visibility; + Alembic::Abc::M44d m_matrix; + + bool m_is_animated; + bool m_inherits_xform; + + public: + Object *m_proxy_from; + + public: + AbcTransformWriter(Object *ob, + const Alembic::AbcGeom::OObject &abc_parent, + AbcTransformWriter *parent, + unsigned int time_sampling, + ExportSettings &settings); + + Alembic::AbcGeom::OXform &alembicXform() + { + return m_xform; + } + virtual Imath::Box3d bounds(); + + private: + virtual void do_write(); + + bool hasAnimation(Object *ob) const; +}; + +#endif /* __ABC_WRITER_TRANSFORM_H__ */ diff --git a/source/blender/io/alembic/intern/abc_exporter.cc b/source/blender/io/alembic/intern/abc_exporter.cc deleted file mode 100644 index 8dad8dff199..00000000000 --- a/source/blender/io/alembic/intern/abc_exporter.cc +++ /dev/null @@ -1,673 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_exporter.h" - -#include - -#include "abc_util.h" -#include "abc_writer_archive.h" -#include "abc_writer_camera.h" -#include "abc_writer_curves.h" -#include "abc_writer_hair.h" -#include "abc_writer_mball.h" -#include "abc_writer_mesh.h" -#include "abc_writer_nurbs.h" -#include "abc_writer_points.h" -#include "abc_writer_transform.h" - -#include "DNA_camera_types.h" -#include "DNA_curve_types.h" -#include "DNA_fluid_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meta_types.h" -#include "DNA_modifier_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_space_types.h" /* for FILE_MAX */ - -#include "BLI_string.h" - -#ifdef WIN32 -/* needed for MSCV because of snprintf from BLI_string */ -# include "BLI_winstuff.h" -#endif - -#include "BKE_duplilist.h" -#include "BKE_global.h" -#include "BKE_idprop.h" -#include "BKE_layer.h" -#include "BKE_main.h" -#include "BKE_mball.h" -#include "BKE_modifier.h" -#include "BKE_particle.h" -#include "BKE_scene.h" - -#include "DEG_depsgraph_query.h" - -using Alembic::Abc::OBox3dProperty; -using Alembic::Abc::TimeSamplingPtr; - -/* ************************************************************************** */ - -ExportSettings::ExportSettings() - : scene(NULL), - view_layer(NULL), - depsgraph(NULL), - logger(), - selected_only(false), - visible_objects_only(false), - renderable_only(false), - frame_start(1), - frame_end(1), - frame_samples_xform(1), - frame_samples_shape(1), - shutter_open(0.0), - shutter_close(1.0), - global_scale(1.0f), - flatten_hierarchy(false), - export_normals(false), - export_uvs(false), - export_vcols(false), - export_face_sets(false), - export_vweigths(false), - export_hair(true), - export_particles(true), - apply_subdiv(false), - use_subdiv_schema(false), - export_child_hairs(true), - pack_uv(false), - triangulate(false), - quad_method(0), - ngon_method(0) -{ -} - -static bool object_is_smoke_sim(Object *ob) -{ - ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluid); - - if (md) { - FluidModifierData *smd = reinterpret_cast(md); - return (smd->type == MOD_FLUID_TYPE_DOMAIN && smd->domain && - smd->domain->type == FLUID_DOMAIN_TYPE_GAS); - } - - return false; -} - -static bool object_type_is_exportable(Scene *scene, Object *ob) -{ - switch (ob->type) { - case OB_MESH: - if (object_is_smoke_sim(ob)) { - return false; - } - - return true; - case OB_EMPTY: - case OB_CURVE: - case OB_SURF: - case OB_CAMERA: - return true; - case OB_MBALL: - return AbcMBallWriter::isBasisBall(scene, ob); - default: - return false; - } -} - -/** - * Returns whether this object should be exported into the Alembic file. - * - * \param settings: export settings, used for options like 'selected only'. - * \param ob: the object's base in question. - * \param is_duplicated: Normally false; true when the object is instanced - * into the scene by a dupli-object (e.g. part of a dupligroup). - * This ignores selection and layer visibility, - * and assumes that the dupli-object itself (e.g. the group-instantiating empty) is exported. - */ -static bool export_object(const ExportSettings *const settings, - const Base *const base, - bool is_duplicated) -{ - if (!is_duplicated) { - View3D *v3d = NULL; - - /* These two tests only make sense when the object isn't being instanced - * into the scene. When it is, its exportability is determined by - * its dupli-object and the DupliObject::no_draw property. */ - if (settings->selected_only && !BASE_SELECTED(v3d, base)) { - return false; - } - // FIXME Sybren: handle these cleanly (maybe just remove code), - // now using active scene layer instead. - if (settings->visible_objects_only && !BASE_VISIBLE(v3d, base)) { - return false; - } - } - - Object *ob_eval = DEG_get_evaluated_object(settings->depsgraph, base->object); - if ((ob_eval->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0) { - /* XXX fix after 2.80: the object was not part of the depsgraph, and thus we cannot get the - * evaluated copy to export. This will be handled more elegantly in the new - * AbstractHierarchyIterator that Sybren is working on. This condition is temporary, and avoids - * a BLI_assert() failure getting the evaluated mesh of this object. */ - return false; - } - - // if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) { - // return false; - // } - - return true; -} - -/* ************************************************************************** */ - -AbcExporter::AbcExporter(Main *bmain, const char *filename, ExportSettings &settings) - : m_bmain(bmain), - m_settings(settings), - m_filename(filename), - m_trans_sampling_index(0), - m_shape_sampling_index(0), - m_writer(NULL) -{ -} - -AbcExporter::~AbcExporter() -{ - /* Free xforms map */ - m_xforms_type::iterator it_x, e_x; - for (it_x = m_xforms.begin(), e_x = m_xforms.end(); it_x != e_x; ++it_x) { - delete it_x->second; - } - - /* Free shapes vector */ - for (int i = 0, e = m_shapes.size(); i != e; i++) { - delete m_shapes[i]; - } - - delete m_writer; -} - -void AbcExporter::getShutterSamples(unsigned int nr_of_samples, - bool time_relative, - std::vector &samples) -{ - Scene *scene = m_settings.scene; /* for use in the FPS macro */ - samples.clear(); - - unsigned int frame_offset = time_relative ? m_settings.frame_start : 0; - double time_factor = time_relative ? FPS : 1.0; - double shutter_open = m_settings.shutter_open; - double shutter_close = m_settings.shutter_close; - double time_inc = (shutter_close - shutter_open) / nr_of_samples; - - /* sample between shutter open & close */ - for (int sample = 0; sample < nr_of_samples; sample++) { - double sample_time = shutter_open + time_inc * sample; - double time = (frame_offset + sample_time) / time_factor; - - samples.push_back(time); - } -} - -Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step) -{ - std::vector samples; - - if (m_settings.frame_start == m_settings.frame_end) { - return TimeSamplingPtr(new Alembic::Abc::TimeSampling()); - } - - getShutterSamples(step, true, samples); - - /* TODO(Sybren): shouldn't we use the FPS macro here? */ - Alembic::Abc::TimeSamplingType ts(static_cast(samples.size()), - 1.0 / m_settings.scene->r.frs_sec); - - return TimeSamplingPtr(new Alembic::Abc::TimeSampling(ts, samples)); -} - -void AbcExporter::getFrameSet(unsigned int nr_of_samples, std::set &frames) -{ - frames.clear(); - - std::vector shutter_samples; - - getShutterSamples(nr_of_samples, false, shutter_samples); - - for (double frame = m_settings.frame_start; frame <= m_settings.frame_end; frame += 1.0) { - for (size_t j = 0; j < nr_of_samples; j++) { - frames.insert(frame + shutter_samples[j]); - } - } -} - -void AbcExporter::operator()(short *do_update, float *progress, bool *was_canceled) -{ - std::string abc_scene_name; - - if (m_bmain->name[0] != '\0') { - char scene_file_name[FILE_MAX]; - BLI_strncpy(scene_file_name, m_bmain->name, FILE_MAX); - abc_scene_name = scene_file_name; - } - else { - abc_scene_name = "untitled"; - } - - m_writer = new ArchiveWriter(m_filename, abc_scene_name, m_settings.scene); - - /* Create time samplings for transforms and shapes. */ - - TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_samples_xform); - - m_trans_sampling_index = m_writer->archive().addTimeSampling(*trans_time); - - TimeSamplingPtr shape_time; - - if ((m_settings.frame_samples_shape == m_settings.frame_samples_xform) || - (m_settings.frame_start == m_settings.frame_end)) { - shape_time = trans_time; - m_shape_sampling_index = m_trans_sampling_index; - } - else { - shape_time = createTimeSampling(m_settings.frame_samples_shape); - m_shape_sampling_index = m_writer->archive().addTimeSampling(*shape_time); - } - - OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds( - m_writer->archive(), m_trans_sampling_index); - - createTransformWritersHierarchy(); - createShapeWriters(); - - /* Make a list of frames to export. */ - - std::set xform_frames; - getFrameSet(m_settings.frame_samples_xform, xform_frames); - - std::set shape_frames; - getFrameSet(m_settings.frame_samples_shape, shape_frames); - - /* Merge all frames needed. */ - std::set frames(xform_frames); - frames.insert(shape_frames.begin(), shape_frames.end()); - - /* Export all frames. */ - - std::set::const_iterator begin = frames.begin(); - std::set::const_iterator end = frames.end(); - - const float size = static_cast(frames.size()); - size_t i = 0; - - for (; begin != end; ++begin) { - *progress = (++i / size); - *do_update = 1; - - if (G.is_break) { - *was_canceled = true; - break; - } - - const double frame = *begin; - - /* 'frame' is offset by start frame, so need to cancel the offset. */ - setCurrentFrame(m_bmain, frame); - - if (shape_frames.count(frame) != 0) { - for (int i = 0, e = m_shapes.size(); i != e; i++) { - m_shapes[i]->write(); - } - } - - if (xform_frames.count(frame) == 0) { - continue; - } - - m_xforms_type::iterator xit, xe; - for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { - xit->second->write(); - } - - /* Save the archive 's bounding box. */ - Imath::Box3d bounds; - - for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { - Imath::Box3d box = xit->second->bounds(); - bounds.extendBy(box); - } - - archive_bounds_prop.set(bounds); - } -} - -void AbcExporter::createTransformWritersHierarchy() -{ - for (Base *base = static_cast(m_settings.view_layer->object_bases.first); base; - base = base->next) { - Object *ob = base->object; - - if (export_object(&m_settings, base, false)) { - switch (ob->type) { - case OB_LAMP: - case OB_LATTICE: - case OB_SPEAKER: - /* We do not export transforms for objects of these classes. */ - break; - default: - exploreTransform(base, ob, ob->parent, NULL); - } - } - } -} - -void AbcExporter::exploreTransform(Base *base, - Object *object, - Object *parent, - Object *dupliObParent) -{ - /* If an object isn't exported itself, its duplilist shouldn't be - * exported either. */ - if (!export_object(&m_settings, base, dupliObParent != NULL)) { - return; - } - - Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object); - if (object_type_is_exportable(m_settings.scene, ob)) { - createTransformWriter(ob, parent, dupliObParent); - } - - ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob); - - if (lb) { - DupliObject *link = static_cast(lb->first); - Object *dupli_ob = NULL; - Object *dupli_parent = NULL; - - for (; link; link = link->next) { - /* This skips things like custom bone shapes. */ - if (m_settings.renderable_only && link->no_draw) { - continue; - } - - if (link->type == OB_DUPLICOLLECTION) { - dupli_ob = link->ob; - dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob; - - exploreTransform(base, dupli_ob, dupli_parent, ob); - } - } - - free_object_duplilist(lb); - } -} - -AbcTransformWriter *AbcExporter::createTransformWriter(Object *ob, - Object *parent, - Object *dupliObParent) -{ - /* An object should not be its own parent, or we'll get infinite loops. */ - BLI_assert(ob != parent); - BLI_assert(ob != dupliObParent); - - std::string name; - if (m_settings.flatten_hierarchy) { - name = get_id_name(ob); - } - else { - name = get_object_dag_path_name(ob, dupliObParent); - } - - /* check if we have already created a transform writer for this object */ - AbcTransformWriter *my_writer = getXForm(name); - if (my_writer != NULL) { - return my_writer; - } - - AbcTransformWriter *parent_writer = NULL; - Alembic::Abc::OObject alembic_parent; - - if (m_settings.flatten_hierarchy || parent == NULL) { - /* Parentless objects still have the "top object" as parent - * in Alembic. */ - alembic_parent = m_writer->archive().getTop(); - } - else { - /* Since there are so many different ways to find parents (as evident - * in the number of conditions below), we can't really look up the - * parent by name. We'll just call createTransformWriter(), which will - * return the parent's AbcTransformWriter pointer. */ - if (parent->parent) { - if (parent == dupliObParent) { - parent_writer = createTransformWriter(parent, parent->parent, NULL); - } - else { - parent_writer = createTransformWriter(parent, parent->parent, dupliObParent); - } - } - else if (parent == dupliObParent) { - if (dupliObParent->parent == NULL) { - parent_writer = createTransformWriter(parent, NULL, NULL); - } - else { - parent_writer = createTransformWriter( - parent, dupliObParent->parent, dupliObParent->parent); - } - } - else { - parent_writer = createTransformWriter(parent, dupliObParent, dupliObParent); - } - - BLI_assert(parent_writer); - alembic_parent = parent_writer->alembicXform(); - } - - my_writer = new AbcTransformWriter( - ob, alembic_parent, parent_writer, m_trans_sampling_index, m_settings); - - /* When flattening, the matrix of the dupliobject has to be added. */ - if (m_settings.flatten_hierarchy && dupliObParent) { - my_writer->m_proxy_from = dupliObParent; - } - - m_xforms[name] = my_writer; - return my_writer; -} - -void AbcExporter::createShapeWriters() -{ - for (Base *base = static_cast(m_settings.view_layer->object_bases.first); base; - base = base->next) { - exploreObject(base, base->object, NULL); - } -} - -void AbcExporter::exploreObject(Base *base, Object *object, Object *dupliObParent) -{ - /* If an object isn't exported itself, its duplilist shouldn't be - * exported either. */ - if (!export_object(&m_settings, base, dupliObParent != NULL)) { - return; - } - - Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object); - createShapeWriter(ob, dupliObParent); - - ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob); - - if (lb) { - DupliObject *link = static_cast(lb->first); - - for (; link; link = link->next) { - /* This skips things like custom bone shapes. */ - if (m_settings.renderable_only && link->no_draw) { - continue; - } - if (link->type == OB_DUPLICOLLECTION) { - exploreObject(base, link->ob, ob); - } - } - - free_object_duplilist(lb); - } -} - -void AbcExporter::createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform) -{ - if (!m_settings.export_hair && !m_settings.export_particles) { - return; - } - - ParticleSystem *psys = static_cast(ob->particlesystem.first); - - for (; psys; psys = psys->next) { - if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) { - continue; - } - - if (m_settings.export_hair && psys->part->type == PART_HAIR) { - m_settings.export_child_hairs = true; - m_shapes.push_back(new AbcHairWriter(ob, xform, m_shape_sampling_index, m_settings, psys)); - } - else if (m_settings.export_particles && - (psys->part->type == PART_EMITTER || psys->part->type == PART_FLUID_FLIP || - psys->part->type == PART_FLUID_SPRAY || psys->part->type == PART_FLUID_BUBBLE || - psys->part->type == PART_FLUID_FOAM || psys->part->type == PART_FLUID_TRACER || - psys->part->type == PART_FLUID_SPRAYFOAM || - psys->part->type == PART_FLUID_SPRAYBUBBLE || - psys->part->type == PART_FLUID_FOAMBUBBLE || - psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE)) { - m_shapes.push_back(new AbcPointsWriter(ob, xform, m_shape_sampling_index, m_settings, psys)); - } - } -} - -void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent) -{ - if (!object_type_is_exportable(m_settings.scene, ob)) { - return; - } - - std::string name; - - if (m_settings.flatten_hierarchy) { - name = get_id_name(ob); - } - else { - name = get_object_dag_path_name(ob, dupliObParent); - } - - AbcTransformWriter *xform = getXForm(name); - - if (!xform) { - ABC_LOG(m_settings.logger) << __func__ << ": xform " << name << " is NULL\n"; - return; - } - - createParticleSystemsWriters(ob, xform); - - switch (ob->type) { - case OB_MESH: { - Mesh *me = static_cast(ob->data); - - if (!me) { - return; - } - - m_shapes.push_back(new AbcMeshWriter(ob, xform, m_shape_sampling_index, m_settings)); - break; - } - case OB_SURF: { - Curve *cu = static_cast(ob->data); - - if (!cu) { - return; - } - - AbcObjectWriter *writer; - if (m_settings.curves_as_mesh) { - writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings); - } - else { - writer = new AbcNurbsWriter(ob, xform, m_shape_sampling_index, m_settings); - } - m_shapes.push_back(writer); - break; - } - case OB_CURVE: { - Curve *cu = static_cast(ob->data); - - if (!cu) { - return; - } - - AbcObjectWriter *writer; - if (m_settings.curves_as_mesh) { - writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings); - } - else { - writer = new AbcCurveWriter(ob, xform, m_shape_sampling_index, m_settings); - } - m_shapes.push_back(writer); - break; - } - case OB_CAMERA: { - Camera *cam = static_cast(ob->data); - - if (cam->type == CAM_PERSP) { - m_shapes.push_back(new AbcCameraWriter(ob, xform, m_shape_sampling_index, m_settings)); - } - - break; - } - case OB_MBALL: { - MetaBall *mball = static_cast(ob->data); - if (!mball) { - return; - } - - m_shapes.push_back( - new AbcMBallWriter(m_bmain, ob, xform, m_shape_sampling_index, m_settings)); - break; - } - } -} - -AbcTransformWriter *AbcExporter::getXForm(const std::string &name) -{ - std::map::iterator it = m_xforms.find(name); - - if (it == m_xforms.end()) { - return NULL; - } - - return it->second; -} - -void AbcExporter::setCurrentFrame(Main *bmain, double t) -{ - m_settings.scene->r.cfra = static_cast(t); - m_settings.scene->r.subframe = static_cast(t) - m_settings.scene->r.cfra; - BKE_scene_graph_update_for_newframe(m_settings.depsgraph, bmain); -} diff --git a/source/blender/io/alembic/intern/abc_exporter.h b/source/blender/io/alembic/intern/abc_exporter.h deleted file mode 100644 index 049ccb291bd..00000000000 --- a/source/blender/io/alembic/intern/abc_exporter.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_EXPORTER_H__ -#define __ABC_EXPORTER_H__ - -#include -#include -#include -#include - -#include "abc_util.h" - -class AbcObjectWriter; -class AbcTransformWriter; -class ArchiveWriter; - -struct Base; -struct Depsgraph; -struct Main; -struct Object; -struct Scene; -struct ViewLayer; - -struct ExportSettings { - ExportSettings(); - - Scene *scene; - /** Scene layer to export; all its objects will be exported, unless selected_only=true. */ - ViewLayer *view_layer; - Depsgraph *depsgraph; - SimpleLogger logger; - - bool selected_only; - bool visible_objects_only; - bool renderable_only; - - double frame_start, frame_end; - double frame_samples_xform; - double frame_samples_shape; - double shutter_open; - double shutter_close; - float global_scale; - - bool flatten_hierarchy; - - bool export_normals; - bool export_uvs; - bool export_vcols; - bool export_face_sets; - bool export_vweigths; - bool export_hair; - bool export_particles; - - bool apply_subdiv; - bool curves_as_mesh; - bool use_subdiv_schema; - bool export_child_hairs; - bool pack_uv; - bool triangulate; - - int quad_method; - int ngon_method; -}; - -class AbcExporter { - Main *m_bmain; - ExportSettings &m_settings; - - const char *m_filename; - - unsigned int m_trans_sampling_index, m_shape_sampling_index; - - ArchiveWriter *m_writer; - - /* mapping from name to transform writer */ - typedef std::map m_xforms_type; - m_xforms_type m_xforms; - - std::vector m_shapes; - - public: - AbcExporter(Main *bmain, const char *filename, ExportSettings &settings); - ~AbcExporter(); - - void operator()(short *do_update, float *progress, bool *was_canceled); - - protected: - void getShutterSamples(unsigned int nr_of_samples, - bool time_relative, - std::vector &samples); - void getFrameSet(unsigned int nr_of_samples, std::set &frames); - - private: - Alembic::Abc::TimeSamplingPtr createTimeSampling(double step); - - void createTransformWritersHierarchy(); - AbcTransformWriter *createTransformWriter(Object *ob, Object *parent, Object *dupliObParent); - void exploreTransform(Base *base, Object *object, Object *parent, Object *dupliObParent); - void exploreObject(Base *base, Object *object, Object *dupliObParent); - void createShapeWriters(); - void createShapeWriter(Object *ob, Object *dupliObParent); - void createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform); - - AbcTransformWriter *getXForm(const std::string &name); - - void setCurrentFrame(Main *bmain, double t); -}; - -#endif /* __ABC_EXPORTER_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_archive.cc b/source/blender/io/alembic/intern/abc_writer_archive.cc deleted file mode 100644 index 40926532f85..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_archive.cc +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_archive.h" - -#include "BKE_blender_version.h" - -#include "BLI_path_util.h" -#include "BLI_string.h" - -#include "DNA_scene_types.h" - -#ifdef WIN32 -# include "utfconv.h" -#endif - -#include - -using Alembic::Abc::ErrorHandler; -using Alembic::Abc::kWrapExisting; -using Alembic::Abc::OArchive; - -/* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to - * have a version supporting streams. */ -static OArchive create_archive(std::ostream *ostream, - const std::string &scene_name, - double scene_fps) -{ - Alembic::Abc::MetaData abc_metadata; - - abc_metadata.set(Alembic::Abc::kApplicationNameKey, "Blender"); - abc_metadata.set(Alembic::Abc::kUserDescriptionKey, scene_name); - abc_metadata.set("blender_version", std::string("v") + BKE_blender_version_string()); - abc_metadata.set("FramesPerTimeUnit", std::to_string(scene_fps)); - - time_t raw_time; - time(&raw_time); - char buffer[128]; - -#if defined _WIN32 || defined _WIN64 - ctime_s(buffer, 128, &raw_time); -#else - ctime_r(&raw_time, buffer); -#endif - - const std::size_t buffer_len = strlen(buffer); - if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') { - buffer[buffer_len - 1] = '\0'; - } - - abc_metadata.set(Alembic::Abc::kDateWrittenKey, buffer); - - ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy; - Alembic::AbcCoreOgawa::WriteArchive archive_writer; - return OArchive(archive_writer(ostream, abc_metadata), kWrapExisting, policy); -} - -ArchiveWriter::ArchiveWriter(const char *filename, - const std::string &abc_scene_name, - const Scene *scene) -{ - /* Use stream to support unicode character paths on Windows. */ -#ifdef WIN32 - UTF16_ENCODE(filename); - std::wstring wstr(filename_16); - m_outfile.open(wstr.c_str(), std::ios::out | std::ios::binary); - UTF16_UN_ENCODE(filename); -#else - m_outfile.open(filename, std::ios::out | std::ios::binary); -#endif - - m_archive = create_archive(&m_outfile, abc_scene_name, FPS); -} - -OArchive &ArchiveWriter::archive() -{ - return m_archive; -} diff --git a/source/blender/io/alembic/intern/abc_writer_archive.h b/source/blender/io/alembic/intern/abc_writer_archive.h deleted file mode 100644 index 737717c1710..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_archive.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_ARCHIVE_H__ -#define __ABC_WRITER_ARCHIVE_H__ - -#include -#include - -#include - -struct Main; -struct Scene; - -/* Wrappers around input and output archives. The goal is to be able to use - * streams so that unicode paths work on Windows (T49112), and to make sure that - * the stream objects remain valid as long as the archives are open. - */ - -class ArchiveWriter { - std::ofstream m_outfile; - Alembic::Abc::OArchive m_archive; - - public: - ArchiveWriter(const char *filename, const std::string &abc_scene_name, const Scene *scene); - - Alembic::Abc::OArchive &archive(); -}; - -#endif /* __ABC_WRITER_ARCHIVE_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_camera.cc b/source/blender/io/alembic/intern/abc_writer_camera.cc deleted file mode 100644 index 07ae81e584f..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_camera.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_camera.h" -#include "abc_writer_transform.h" - -#include "DNA_camera_types.h" -#include "DNA_object_types.h" - -using Alembic::AbcGeom::OCamera; -using Alembic::AbcGeom::OFloatProperty; - -AbcCameraWriter::AbcCameraWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent) -{ - OCamera camera(parent->alembicXform(), m_name, m_time_sampling); - m_camera_schema = camera.getSchema(); - - m_custom_data_container = m_camera_schema.getUserProperties(); - m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling); - m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling); -} - -void AbcCameraWriter::do_write() -{ - Camera *cam = static_cast(m_object->data); - - m_stereo_distance.set(cam->stereo.convergence_distance); - m_eye_separation.set(cam->stereo.interocular_distance); - - const double apperture_x = cam->sensor_x / 10.0; - const double apperture_y = cam->sensor_y / 10.0; - const double film_aspect = apperture_x / apperture_y; - - m_camera_sample.setFocalLength(cam->lens); - m_camera_sample.setHorizontalAperture(apperture_x); - m_camera_sample.setVerticalAperture(apperture_y); - m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx); - m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect); - m_camera_sample.setNearClippingPlane(cam->clip_start); - m_camera_sample.setFarClippingPlane(cam->clip_end); - - if (cam->dof.focus_object) { - Imath::V3f v(m_object->loc[0] - cam->dof.focus_object->loc[0], - m_object->loc[1] - cam->dof.focus_object->loc[1], - m_object->loc[2] - cam->dof.focus_object->loc[2]); - m_camera_sample.setFocusDistance(v.length()); - } - else { - m_camera_sample.setFocusDistance(cam->dof.focus_distance); - } - - /* Blender camera does not have an fstop param, so try to find a custom prop - * instead. */ - m_camera_sample.setFStop(cam->dof.aperture_fstop); - - m_camera_sample.setLensSqueezeRatio(1.0); - m_camera_schema.set(m_camera_sample); -} diff --git a/source/blender/io/alembic/intern/abc_writer_camera.h b/source/blender/io/alembic/intern/abc_writer_camera.h deleted file mode 100644 index 3b515911a48..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_camera.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_CAMERA_H__ -#define __ABC_WRITER_CAMERA_H__ - -#include "abc_writer_object.h" - -/* ************************************************************************** */ - -class AbcCameraWriter : public AbcObjectWriter { - Alembic::AbcGeom::OCameraSchema m_camera_schema; - Alembic::AbcGeom::CameraSample m_camera_sample; - Alembic::AbcGeom::OCompoundProperty m_custom_data_container; - Alembic::AbcGeom::OFloatProperty m_stereo_distance; - Alembic::AbcGeom::OFloatProperty m_eye_separation; - - public: - AbcCameraWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - private: - virtual void do_write(); -}; - -#endif /* __ABC_WRITER_CAMERA_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_curves.cc b/source/blender/io/alembic/intern/abc_writer_curves.cc deleted file mode 100644 index db93ac1920e..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_curves.cc +++ /dev/null @@ -1,188 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_curves.h" -#include "abc_axis_conversion.h" -#include "abc_reader_curves.h" -#include "abc_writer_transform.h" - -#include "DNA_curve_types.h" -#include "DNA_object_types.h" - -#include "BKE_curve.h" -#include "BKE_mesh.h" -#include "BKE_object.h" - -using Alembic::AbcGeom::OCompoundProperty; -using Alembic::AbcGeom::OCurves; -using Alembic::AbcGeom::OCurvesSchema; -using Alembic::AbcGeom::OInt16Property; -using Alembic::AbcGeom::ON3fGeomParam; -using Alembic::AbcGeom::OV2fGeomParam; - -AbcCurveWriter::AbcCurveWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent) -{ - OCurves curves(parent->alembicXform(), m_name, m_time_sampling); - m_schema = curves.getSchema(); - - Curve *cu = static_cast(m_object->data); - OCompoundProperty user_props = m_schema.getUserProperties(); - OInt16Property user_prop_resolu(user_props, ABC_CURVE_RESOLUTION_U_PROPNAME); - user_prop_resolu.set(cu->resolu); -} - -void AbcCurveWriter::do_write() -{ - Curve *curve = static_cast(m_object->data); - - std::vector verts; - std::vector vert_counts; - std::vector widths; - std::vector weights; - std::vector knots; - std::vector orders; - Imath::V3f temp_vert; - - Alembic::AbcGeom::BasisType curve_basis; - Alembic::AbcGeom::CurveType curve_type; - Alembic::AbcGeom::CurvePeriodicity periodicity; - - Nurb *nurbs = static_cast(curve->nurb.first); - for (; nurbs; nurbs = nurbs->next) { - if (nurbs->bp) { - curve_basis = Alembic::AbcGeom::kNoBasis; - curve_type = Alembic::AbcGeom::kVariableOrder; - - const int totpoint = nurbs->pntsu * nurbs->pntsv; - - const BPoint *point = nurbs->bp; - - for (int i = 0; i < totpoint; i++, point++) { - copy_yup_from_zup(temp_vert.getValue(), point->vec); - verts.push_back(temp_vert); - weights.push_back(point->vec[3]); - widths.push_back(point->radius); - } - } - else if (nurbs->bezt) { - curve_basis = Alembic::AbcGeom::kBezierBasis; - curve_type = Alembic::AbcGeom::kCubic; - - const int totpoint = nurbs->pntsu; - - const BezTriple *bezier = nurbs->bezt; - - /* TODO(kevin): store info about handles, Alembic doesn't have this. */ - for (int i = 0; i < totpoint; i++, bezier++) { - copy_yup_from_zup(temp_vert.getValue(), bezier->vec[1]); - verts.push_back(temp_vert); - widths.push_back(bezier->radius); - } - } - - if ((nurbs->flagu & CU_NURB_ENDPOINT) != 0) { - periodicity = Alembic::AbcGeom::kNonPeriodic; - } - else if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) { - periodicity = Alembic::AbcGeom::kPeriodic; - - /* Duplicate the start points to indicate that the curve is actually - * cyclic since other software need those. - */ - - for (int i = 0; i < nurbs->orderu; i++) { - verts.push_back(verts[i]); - } - } - - if (nurbs->knotsu != NULL) { - const size_t num_knots = KNOTSU(nurbs); - - /* Add an extra knot at the beginning and end of the array since most apps - * require/expect them. */ - knots.resize(num_knots + 2); - - for (int i = 0; i < num_knots; i++) { - knots[i + 1] = nurbs->knotsu[i]; - } - - if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) { - knots[0] = nurbs->knotsu[0]; - knots[num_knots - 1] = nurbs->knotsu[num_knots - 1]; - } - else { - knots[0] = (2.0f * nurbs->knotsu[0] - nurbs->knotsu[1]); - knots[num_knots - 1] = (2.0f * nurbs->knotsu[num_knots - 1] - - nurbs->knotsu[num_knots - 2]); - } - } - - orders.push_back(nurbs->orderu); - vert_counts.push_back(verts.size()); - } - - Alembic::AbcGeom::OFloatGeomParam::Sample width_sample; - width_sample.setVals(widths); - - m_sample = OCurvesSchema::Sample(verts, - vert_counts, - curve_type, - periodicity, - width_sample, - OV2fGeomParam::Sample(), /* UVs */ - ON3fGeomParam::Sample(), /* normals */ - curve_basis, - weights, - orders, - knots); - - m_sample.setSelfBounds(bounds()); - m_schema.set(m_sample); -} - -AbcCurveMeshWriter::AbcCurveMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcGenericMeshWriter(ob, parent, time_sampling, settings) -{ -} - -Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/, - Object *ob_eval, - bool &r_needsfree) -{ - Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); - if (mesh_eval != NULL) { - /* Mesh_eval only exists when generative modifiers are in use. */ - r_needsfree = false; - return mesh_eval; - } - - r_needsfree = true; - return BKE_mesh_new_nomain_from_curve(ob_eval); -} diff --git a/source/blender/io/alembic/intern/abc_writer_curves.h b/source/blender/io/alembic/intern/abc_writer_curves.h deleted file mode 100644 index 83f0289dd2d..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_curves.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_CURVES_H__ -#define __ABC_WRITER_CURVES_H__ - -#include "abc_writer_mesh.h" -#include "abc_writer_object.h" - -class AbcCurveWriter : public AbcObjectWriter { - Alembic::AbcGeom::OCurvesSchema m_schema; - Alembic::AbcGeom::OCurvesSchema::Sample m_sample; - - public: - AbcCurveWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - protected: - void do_write(); -}; - -class AbcCurveMeshWriter : public AbcGenericMeshWriter { - public: - AbcCurveMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - protected: - Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree); -}; - -#endif /* __ABC_WRITER_CURVES_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_hair.cc b/source/blender/io/alembic/intern/abc_writer_hair.cc deleted file mode 100644 index 43c05627130..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_hair.cc +++ /dev/null @@ -1,291 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_hair.h" -#include "abc_axis_conversion.h" -#include "abc_writer_transform.h" - -#include - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_modifier_types.h" -#include "DNA_object_types.h" - -#include "BLI_math_geom.h" - -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_particle.h" - -using Alembic::Abc::P3fArraySamplePtr; - -using Alembic::AbcGeom::OCurves; -using Alembic::AbcGeom::OCurvesSchema; -using Alembic::AbcGeom::ON3fGeomParam; -using Alembic::AbcGeom::OV2fGeomParam; - -/* ************************************************************************** */ - -AbcHairWriter::AbcHairWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings, - ParticleSystem *psys) - : AbcObjectWriter(ob, time_sampling, settings, parent), m_uv_warning_shown(false) -{ - m_psys = psys; - - std::string psys_name = get_valid_abc_name(psys->name); - OCurves curves(parent->alembicXform(), psys_name, m_time_sampling); - m_schema = curves.getSchema(); -} - -void AbcHairWriter::do_write() -{ - if (!m_psys) { - return; - } - Mesh *mesh = mesh_get_eval_final( - m_settings.depsgraph, m_settings.scene, m_object, &CD_MASK_MESH); - BKE_mesh_tessface_ensure(mesh); - - std::vector verts; - std::vector hvertices; - std::vector uv_values; - std::vector norm_values; - - if (m_psys->pathcache) { - ParticleSettings *part = m_psys->part; - bool export_children = m_settings.export_child_hairs && m_psys->childcache && - part->childtype != 0; - - if (!export_children || part->draw & PART_DRAW_PARENT) { - write_hair_sample(mesh, part, verts, norm_values, uv_values, hvertices); - } - - if (export_children) { - write_hair_child_sample(mesh, part, verts, norm_values, uv_values, hvertices); - } - } - - Alembic::Abc::P3fArraySample iPos(verts); - m_sample = OCurvesSchema::Sample(iPos, hvertices); - m_sample.setBasis(Alembic::AbcGeom::kNoBasis); - m_sample.setType(Alembic::AbcGeom::kLinear); - m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic); - - if (!uv_values.empty()) { - OV2fGeomParam::Sample uv_smp; - uv_smp.setVals(uv_values); - m_sample.setUVs(uv_smp); - } - - if (!norm_values.empty()) { - ON3fGeomParam::Sample norm_smp; - norm_smp.setVals(norm_values); - m_sample.setNormals(norm_smp); - } - - m_sample.setSelfBounds(bounds()); - m_schema.set(m_sample); -} - -void AbcHairWriter::write_hair_sample(Mesh *mesh, - ParticleSettings *part, - std::vector &verts, - std::vector &norm_values, - std::vector &uv_values, - std::vector &hvertices) -{ - /* Get untransformed vertices, there's a xform under the hair. */ - float inv_mat[4][4]; - invert_m4_m4_safe(inv_mat, m_object->obmat); - - MTFace *mtface = mesh->mtface; - MFace *mface = mesh->mface; - MVert *mverts = mesh->mvert; - - if ((!mtface || !mface) && !m_uv_warning_shown) { - std::fprintf(stderr, - "Warning, no UV set found for underlying geometry of %s.\n", - m_object->id.name + 2); - m_uv_warning_shown = true; - } - - ParticleData *pa = m_psys->particles; - int k; - - ParticleCacheKey **cache = m_psys->pathcache; - ParticleCacheKey *path; - float normal[3]; - Imath::V3f tmp_nor; - - for (int p = 0; p < m_psys->totpart; p++, pa++) { - /* underlying info for faces-only emission */ - path = cache[p]; - - /* Write UV and normal vectors */ - if (part->from == PART_FROM_FACE && mtface) { - const int num = pa->num_dmcache >= 0 ? pa->num_dmcache : pa->num; - - if (num < mesh->totface) { - /* TODO(Sybren): check whether the NULL check here and if(mface) are actually required */ - MFace *face = mface == NULL ? NULL : &mface[num]; - MTFace *tface = mtface + num; - - if (mface) { - float r_uv[2], mapfw[4], vec[3]; - - psys_interpolate_uvs(tface, face->v4, pa->fuv, r_uv); - uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1])); - - psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, normal, NULL, NULL, NULL); - - copy_yup_from_zup(tmp_nor.getValue(), normal); - norm_values.push_back(tmp_nor); - } - } - else { - std::fprintf(stderr, "Particle to faces overflow (%d/%d)\n", num, mesh->totface); - } - } - else if (part->from == PART_FROM_VERT && mtface) { - /* vertex id */ - const int num = (pa->num_dmcache >= 0) ? pa->num_dmcache : pa->num; - - /* iterate over all faces to find a corresponding underlying UV */ - for (int n = 0; n < mesh->totface; n++) { - MFace *face = &mface[n]; - MTFace *tface = mtface + n; - unsigned int vtx[4]; - vtx[0] = face->v1; - vtx[1] = face->v2; - vtx[2] = face->v3; - vtx[3] = face->v4; - bool found = false; - - for (int o = 0; o < 4; o++) { - if (o > 2 && vtx[o] == 0) { - break; - } - - if (vtx[o] == num) { - uv_values.push_back(Imath::V2f(tface->uv[o][0], tface->uv[o][1])); - - MVert *mv = mverts + vtx[o]; - - normal_short_to_float_v3(normal, mv->no); - copy_yup_from_zup(tmp_nor.getValue(), normal); - norm_values.push_back(tmp_nor); - found = true; - break; - } - } - - if (found) { - break; - } - } - } - - int steps = path->segments + 1; - hvertices.push_back(steps); - - for (k = 0; k < steps; k++, path++) { - float vert[3]; - copy_v3_v3(vert, path->co); - mul_m4_v3(inv_mat, vert); - - /* Convert Z-up to Y-up. */ - verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1])); - } - } -} - -void AbcHairWriter::write_hair_child_sample(Mesh *mesh, - ParticleSettings *part, - std::vector &verts, - std::vector &norm_values, - std::vector &uv_values, - std::vector &hvertices) -{ - /* Get untransformed vertices, there's a xform under the hair. */ - float inv_mat[4][4]; - invert_m4_m4_safe(inv_mat, m_object->obmat); - - MTFace *mtface = mesh->mtface; - MVert *mverts = mesh->mvert; - - ParticleCacheKey **cache = m_psys->childcache; - ParticleCacheKey *path; - - ChildParticle *pc = m_psys->child; - - for (int p = 0; p < m_psys->totchild; p++, pc++) { - path = cache[p]; - - if (part->from == PART_FROM_FACE && part->childtype != PART_CHILD_PARTICLES && mtface) { - const int num = pc->num; - if (num < 0) { - ABC_LOG(m_settings.logger) - << "Warning, child particle of hair system " << m_psys->name - << " has unknown face index of geometry of " << (m_object->id.name + 2) - << ", skipping child hair." << std::endl; - continue; - } - - MFace *face = &mesh->mface[num]; - MTFace *tface = mtface + num; - - float r_uv[2], tmpnor[3], mapfw[4], vec[3]; - - psys_interpolate_uvs(tface, face->v4, pc->fuv, r_uv); - uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1])); - - psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, tmpnor, NULL, NULL, NULL); - - /* Convert Z-up to Y-up. */ - norm_values.push_back(Imath::V3f(tmpnor[0], tmpnor[2], -tmpnor[1])); - } - else { - if (uv_values.size()) { - uv_values.push_back(uv_values[pc->parent]); - } - if (norm_values.size()) { - norm_values.push_back(norm_values[pc->parent]); - } - } - - int steps = path->segments + 1; - hvertices.push_back(steps); - - for (int k = 0; k < steps; k++) { - float vert[3]; - copy_v3_v3(vert, path->co); - mul_m4_v3(inv_mat, vert); - - /* Convert Z-up to Y-up. */ - verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1])); - - path++; - } - } -} diff --git a/source/blender/io/alembic/intern/abc_writer_hair.h b/source/blender/io/alembic/intern/abc_writer_hair.h deleted file mode 100644 index 67d1b7b3d23..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_hair.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_HAIR_H__ -#define __ABC_WRITER_HAIR_H__ - -#include "abc_writer_object.h" - -struct ParticleSettings; -struct ParticleSystem; - -class AbcHairWriter : public AbcObjectWriter { - ParticleSystem *m_psys; - - Alembic::AbcGeom::OCurvesSchema m_schema; - Alembic::AbcGeom::OCurvesSchema::Sample m_sample; - - bool m_uv_warning_shown; - - public: - AbcHairWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings, - ParticleSystem *psys); - - private: - virtual void do_write(); - - void write_hair_sample(struct Mesh *mesh, - ParticleSettings *part, - std::vector &verts, - std::vector &norm_values, - std::vector &uv_values, - std::vector &hvertices); - - void write_hair_child_sample(struct Mesh *mesh, - ParticleSettings *part, - std::vector &verts, - std::vector &norm_values, - std::vector &uv_values, - std::vector &hvertices); -}; - -#endif /* __ABC_WRITER_HAIR_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_mball.cc b/source/blender/io/alembic/intern/abc_writer_mball.cc deleted file mode 100644 index 3593acf18b0..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_mball.cc +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_mball.h" -#include "abc_writer_mesh.h" - -#include "DNA_mesh_types.h" -#include "DNA_meta_types.h" -#include "DNA_object_types.h" - -#include "BKE_displist.h" -#include "BKE_lib_id.h" -#include "BKE_mball.h" -#include "BKE_mesh.h" -#include "BKE_object.h" - -#include "BLI_utildefines.h" - -AbcMBallWriter::AbcMBallWriter(Main *bmain, - Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcGenericMeshWriter(ob, parent, time_sampling, settings), m_bmain(bmain) -{ - m_is_animated = isAnimated(); -} - -AbcMBallWriter::~AbcMBallWriter() -{ -} - -bool AbcMBallWriter::isAnimated() const -{ - return true; -} - -Mesh *AbcMBallWriter::getEvaluatedMesh(Scene * /*scene_eval*/, Object *ob_eval, bool &r_needsfree) -{ - Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); - if (mesh_eval != NULL) { - /* Mesh_eval only exists when generative modifiers are in use. */ - r_needsfree = false; - return mesh_eval; - } - r_needsfree = true; - - /* The approach below is copied from BKE_mesh_new_from_object() */ - Mesh *tmpmesh = BKE_mesh_add(m_bmain, ((ID *)m_object->data)->name + 2); - BLI_assert(tmpmesh != NULL); - - /* BKE_mesh_add gives us a user count we don't need */ - id_us_min(&tmpmesh->id); - - ListBase disp = {NULL, NULL}; - /* TODO(sergey): This is gonna to work for until Depsgraph - * only contains for_render flag. As soon as CoW is - * implemented, this is to be rethought. - */ - BKE_displist_make_mball_forRender(m_settings.depsgraph, m_settings.scene, m_object, &disp); - BKE_mesh_from_metaball(&disp, tmpmesh); - BKE_displist_free(&disp); - - BKE_mesh_texspace_copy_from_object(tmpmesh, m_object); - - return tmpmesh; -} - -void AbcMBallWriter::freeEvaluatedMesh(struct Mesh *mesh) -{ - BKE_id_free(m_bmain, mesh); -} - -bool AbcMBallWriter::isBasisBall(Scene *scene, Object *ob) -{ - Object *basis_ob = BKE_mball_basis_find(scene, ob); - return ob == basis_ob; -} diff --git a/source/blender/io/alembic/intern/abc_writer_mball.h b/source/blender/io/alembic/intern/abc_writer_mball.h deleted file mode 100644 index e3ac1e69cae..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_mball.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_MBALL_H__ -#define __ABC_WRITER_MBALL_H__ - -#include "abc_writer_mesh.h" -#include "abc_writer_object.h" - -struct Main; -struct Object; - -/* AbcMBallWriter converts the metaballs to meshes at every frame, - * and defers to AbcGenericMeshWriter to perform the writing - * to the Alembic file. Only the basis balls are exported, as this - * results in the entire shape as one mesh. */ -class AbcMBallWriter : public AbcGenericMeshWriter { - Main *m_bmain; - - public: - explicit AbcMBallWriter(Main *bmain, - Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - ~AbcMBallWriter(); - - static bool isBasisBall(Scene *scene, Object *ob); - - protected: - Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override; - void freeEvaluatedMesh(struct Mesh *mesh) override; - - private: - bool isAnimated() const override; -}; - -#endif /* __ABC_WRITER_MBALL_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_mesh.cc b/source/blender/io/alembic/intern/abc_writer_mesh.cc deleted file mode 100644 index df1734c9de1..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_mesh.cc +++ /dev/null @@ -1,591 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_mesh.h" -#include "abc_axis_conversion.h" -#include "abc_writer_transform.h" - -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_fluidsim_types.h" - -#include "BKE_anim_data.h" -#include "BKE_key.h" -#include "BKE_lib_id.h" -#include "BKE_material.h" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_modifier.h" - -#include "bmesh.h" -#include "bmesh_tools.h" - -#include "DEG_depsgraph_query.h" - -using Alembic::Abc::FloatArraySample; -using Alembic::Abc::Int32ArraySample; -using Alembic::Abc::V2fArraySample; -using Alembic::Abc::V3fArraySample; - -using Alembic::AbcGeom::kFacevaryingScope; -using Alembic::AbcGeom::OBoolProperty; -using Alembic::AbcGeom::OCompoundProperty; -using Alembic::AbcGeom::OFaceSet; -using Alembic::AbcGeom::OFaceSetSchema; -using Alembic::AbcGeom::ON3fGeomParam; -using Alembic::AbcGeom::OPolyMesh; -using Alembic::AbcGeom::OPolyMeshSchema; -using Alembic::AbcGeom::OSubD; -using Alembic::AbcGeom::OSubDSchema; -using Alembic::AbcGeom::OV2fGeomParam; -using Alembic::AbcGeom::UInt32ArraySample; - -/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */ - -static void get_vertices(struct Mesh *mesh, std::vector &points) -{ - points.clear(); - points.resize(mesh->totvert); - - MVert *verts = mesh->mvert; - - for (int i = 0, e = mesh->totvert; i < e; i++) { - copy_yup_from_zup(points[i].getValue(), verts[i].co); - } -} - -static void get_topology(struct Mesh *mesh, - std::vector &poly_verts, - std::vector &loop_counts, - bool &r_has_flat_shaded_poly) -{ - const int num_poly = mesh->totpoly; - const int num_loops = mesh->totloop; - MLoop *mloop = mesh->mloop; - MPoly *mpoly = mesh->mpoly; - r_has_flat_shaded_poly = false; - - poly_verts.clear(); - loop_counts.clear(); - poly_verts.reserve(num_loops); - loop_counts.reserve(num_poly); - - /* NOTE: data needs to be written in the reverse order. */ - for (int i = 0; i < num_poly; i++) { - MPoly &poly = mpoly[i]; - loop_counts.push_back(poly.totloop); - - r_has_flat_shaded_poly |= (poly.flag & ME_SMOOTH) == 0; - - MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1); - - for (int j = 0; j < poly.totloop; j++, loop--) { - poly_verts.push_back(loop->v); - } - } -} - -static void get_creases(struct Mesh *mesh, - std::vector &indices, - std::vector &lengths, - std::vector &sharpnesses) -{ - const float factor = 1.0f / 255.0f; - - indices.clear(); - lengths.clear(); - sharpnesses.clear(); - - MEdge *edge = mesh->medge; - - for (int i = 0, e = mesh->totedge; i < e; i++) { - const float sharpness = static_cast(edge[i].crease) * factor; - - if (sharpness != 0.0f) { - indices.push_back(edge[i].v1); - indices.push_back(edge[i].v2); - sharpnesses.push_back(sharpness); - } - } - - lengths.resize(sharpnesses.size(), 2); -} - -static void get_loop_normals(struct Mesh *mesh, - std::vector &normals, - bool has_flat_shaded_poly) -{ - normals.clear(); - - /* If all polygons are smooth shaded, and there are no custom normals, we don't need to export - * normals at all. This is also done by other software, see T71246. */ - if (!has_flat_shaded_poly && !CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL) && - (mesh->flag & ME_AUTOSMOOTH) == 0) { - return; - } - - BKE_mesh_calc_normals_split(mesh); - const float(*lnors)[3] = static_cast(CustomData_get_layer(&mesh->ldata, CD_NORMAL)); - BLI_assert(lnors != NULL || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL"); - - normals.resize(mesh->totloop); - - /* NOTE: data needs to be written in the reverse order. */ - int abc_index = 0; - MPoly *mp = mesh->mpoly; - for (int i = 0, e = mesh->totpoly; i < e; i++, mp++) { - for (int j = mp->totloop - 1; j >= 0; j--, abc_index++) { - int blender_index = mp->loopstart + j; - copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]); - } - } -} - -/* *************** Modifiers *************** */ - -/* check if the mesh is a subsurf, ignoring disabled modifiers and - * displace if it's after subsurf. */ -static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob) -{ - ModifierData *md = static_cast(ob->modifiers.last); - - for (; md; md = md->prev) { - if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) { - continue; - } - - if (md->type == eModifierType_Subsurf) { - SubsurfModifierData *smd = reinterpret_cast(md); - - if (smd->subdivType == ME_CC_SUBSURF) { - return md; - } - } - - /* mesh is not a subsurf. break */ - if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) { - return NULL; - } - } - - return NULL; -} - -static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob) -{ - ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluidsim); - - if (md && (BKE_modifier_is_enabled(scene, md, eModifierMode_Render))) { - FluidsimModifierData *fsmd = reinterpret_cast(md); - - if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) { - return md; - } - } - - return NULL; -} - -/* ************************************************************************** */ - -AbcGenericMeshWriter::AbcGenericMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent) -{ - m_is_animated = isAnimated(); - m_subsurf_mod = NULL; - m_is_subd = false; - - /* If the object is static, use the default static time sampling. */ - if (!m_is_animated) { - time_sampling = 0; - } - - if (!m_settings.apply_subdiv) { - m_subsurf_mod = get_subsurf_modifier(m_settings.scene, m_object); - m_is_subd = (m_subsurf_mod != NULL); - } - - m_is_liquid = (get_liquid_sim_modifier(m_settings.scene, m_object) != NULL); - - while (parent->alembicXform().getChildHeader(m_name)) { - m_name.append("_"); - } - - if (m_settings.use_subdiv_schema && m_is_subd) { - OSubD subd(parent->alembicXform(), m_name, m_time_sampling); - m_subdiv_schema = subd.getSchema(); - } - else { - OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling); - m_mesh_schema = mesh.getSchema(); - - OCompoundProperty typeContainer = m_mesh_schema.getUserProperties(); - OBoolProperty type(typeContainer, "meshtype"); - type.set(m_is_subd); - } -} - -AbcGenericMeshWriter::~AbcGenericMeshWriter() -{ - if (m_subsurf_mod) { - m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; - } -} - -bool AbcGenericMeshWriter::isAnimated() const -{ - if (BKE_animdata_id_is_animated(static_cast(m_object->data))) { - return true; - } - if (BKE_key_from_object(m_object) != NULL) { - return true; - } - - /* Test modifiers. */ - ModifierData *md = static_cast(m_object->modifiers.first); - while (md) { - - if (md->type != eModifierType_Subsurf) { - return true; - } - - md = md->next; - } - - return false; -} - -void AbcGenericMeshWriter::setIsAnimated(bool is_animated) -{ - m_is_animated = is_animated; -} - -void AbcGenericMeshWriter::do_write() -{ - /* We have already stored a sample for this object. */ - if (!m_first_frame && !m_is_animated) { - return; - } - - bool needsfree; - struct Mesh *mesh = getFinalMesh(needsfree); - - try { - if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) { - writeSubD(mesh); - } - else { - writeMesh(mesh); - } - - if (needsfree) { - freeEvaluatedMesh(mesh); - } - } - catch (...) { - if (needsfree) { - freeEvaluatedMesh(mesh); - } - throw; - } -} - -void AbcGenericMeshWriter::freeEvaluatedMesh(struct Mesh *mesh) -{ - BKE_id_free(NULL, mesh); -} - -void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh) -{ - std::vector points, normals; - std::vector poly_verts, loop_counts; - std::vector velocities; - bool has_flat_shaded_poly = false; - - get_vertices(mesh, points); - get_topology(mesh, poly_verts, loop_counts, has_flat_shaded_poly); - - if (m_first_frame && m_settings.export_face_sets) { - writeFaceSets(mesh, m_mesh_schema); - } - - m_mesh_sample = OPolyMeshSchema::Sample( - V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts)); - - UVSample sample; - if (m_settings.export_uvs) { - const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata); - - if (!sample.indices.empty() && !sample.uvs.empty()) { - OV2fGeomParam::Sample uv_sample; - uv_sample.setVals(V2fArraySample(sample.uvs)); - uv_sample.setIndices(UInt32ArraySample(sample.indices)); - uv_sample.setScope(kFacevaryingScope); - - m_mesh_schema.setUVSourceName(name); - m_mesh_sample.setUVs(uv_sample); - } - - write_custom_data( - m_mesh_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV); - } - - if (m_settings.export_normals) { - get_loop_normals(mesh, normals, has_flat_shaded_poly); - - ON3fGeomParam::Sample normals_sample; - if (!normals.empty()) { - normals_sample.setScope(kFacevaryingScope); - normals_sample.setVals(V3fArraySample(normals)); - } - - m_mesh_sample.setNormals(normals_sample); - } - - if (m_is_liquid) { - getVelocities(mesh, velocities); - m_mesh_sample.setVelocities(V3fArraySample(velocities)); - } - - m_mesh_sample.setSelfBounds(bounds()); - - m_mesh_schema.set(m_mesh_sample); - - writeArbGeoParams(mesh); -} - -void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh) -{ - std::vector crease_sharpness; - std::vector points; - std::vector poly_verts, loop_counts; - std::vector crease_indices, crease_lengths; - bool has_flat_poly = false; - - get_vertices(mesh, points); - get_topology(mesh, poly_verts, loop_counts, has_flat_poly); - get_creases(mesh, crease_indices, crease_lengths, crease_sharpness); - - if (m_first_frame && m_settings.export_face_sets) { - writeFaceSets(mesh, m_subdiv_schema); - } - - m_subdiv_sample = OSubDSchema::Sample( - V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts)); - - UVSample sample; - if (m_first_frame && m_settings.export_uvs) { - const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata); - - if (!sample.indices.empty() && !sample.uvs.empty()) { - OV2fGeomParam::Sample uv_sample; - uv_sample.setVals(V2fArraySample(sample.uvs)); - uv_sample.setIndices(UInt32ArraySample(sample.indices)); - uv_sample.setScope(kFacevaryingScope); - - m_subdiv_schema.setUVSourceName(name); - m_subdiv_sample.setUVs(uv_sample); - } - - write_custom_data( - m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV); - } - - if (!crease_indices.empty()) { - m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices)); - m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths)); - m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness)); - } - - m_subdiv_sample.setSelfBounds(bounds()); - m_subdiv_schema.set(m_subdiv_sample); - - writeArbGeoParams(mesh); -} - -template void AbcGenericMeshWriter::writeFaceSets(struct Mesh *me, Schema &schema) -{ - std::map> geo_groups; - getGeoGroups(me, geo_groups); - - std::map>::iterator it; - for (it = geo_groups.begin(); it != geo_groups.end(); ++it) { - OFaceSet face_set = schema.createFaceSet(it->first); - OFaceSetSchema::Sample samp; - samp.setFaces(Int32ArraySample(it->second)); - face_set.getSchema().set(samp); - } -} - -Mesh *AbcGenericMeshWriter::getFinalMesh(bool &r_needsfree) -{ - /* We don't want subdivided mesh data */ - if (m_subsurf_mod) { - m_subsurf_mod->mode |= eModifierMode_DisableTemporary; - } - - r_needsfree = false; - - Scene *scene = DEG_get_evaluated_scene(m_settings.depsgraph); - Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object); - struct Mesh *mesh = getEvaluatedMesh(scene, ob_eval, r_needsfree); - - if (m_subsurf_mod) { - m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; - } - - if (m_settings.triangulate) { - const bool tag_only = false; - const int quad_method = m_settings.quad_method; - const int ngon_method = m_settings.ngon_method; - - struct BMeshCreateParams bmcp = {false}; - struct BMeshFromMeshParams bmfmp = {true, false, false, 0}; - BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmcp, &bmfmp); - - BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, NULL, NULL, NULL); - - Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); - BM_mesh_free(bm); - - if (r_needsfree) { - BKE_id_free(NULL, mesh); - } - - mesh = result; - r_needsfree = true; - } - - m_custom_data_config.pack_uvs = m_settings.pack_uv; - m_custom_data_config.mpoly = mesh->mpoly; - m_custom_data_config.mloop = mesh->mloop; - m_custom_data_config.totpoly = mesh->totpoly; - m_custom_data_config.totloop = mesh->totloop; - m_custom_data_config.totvert = mesh->totvert; - - return mesh; -} - -void AbcGenericMeshWriter::writeArbGeoParams(struct Mesh *me) -{ - if (m_is_liquid) { - /* We don't need anything more for liquid meshes. */ - return; - } - - if (m_first_frame && m_settings.export_vcols) { - if (m_subdiv_schema.valid()) { - write_custom_data( - m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL); - } - else { - write_custom_data( - m_mesh_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL); - } - } -} - -void AbcGenericMeshWriter::getVelocities(struct Mesh *mesh, std::vector &vels) -{ - const int totverts = mesh->totvert; - - vels.clear(); - vels.resize(totverts); - - ModifierData *md = get_liquid_sim_modifier(m_settings.scene, m_object); - FluidsimModifierData *fmd = reinterpret_cast(md); - FluidsimSettings *fss = fmd->fss; - - if (fss->meshVelocities) { - float *mesh_vels = reinterpret_cast(fss->meshVelocities); - - for (int i = 0; i < totverts; i++) { - copy_yup_from_zup(vels[i].getValue(), mesh_vels); - mesh_vels += 3; - } - } - else { - std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f)); - } -} - -void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh, - std::map> &geo_groups) -{ - const int num_poly = mesh->totpoly; - MPoly *polygons = mesh->mpoly; - - for (int i = 0; i < num_poly; i++) { - MPoly ¤t_poly = polygons[i]; - short mnr = current_poly.mat_nr; - - Material *mat = BKE_object_material_get(m_object, mnr + 1); - - if (!mat) { - continue; - } - - std::string name = get_id_name(&mat->id); - - if (geo_groups.find(name) == geo_groups.end()) { - std::vector faceArray; - geo_groups[name] = faceArray; - } - - geo_groups[name].push_back(i); - } - - if (geo_groups.size() == 0) { - Material *mat = BKE_object_material_get(m_object, 1); - - std::string name = (mat) ? get_id_name(&mat->id) : "default"; - - std::vector faceArray; - - for (int i = 0, e = mesh->totface; i < e; i++) { - faceArray.push_back(i); - } - - geo_groups[name] = faceArray; - } -} - -AbcMeshWriter::AbcMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcGenericMeshWriter(ob, parent, time_sampling, settings) -{ -} - -AbcMeshWriter::~AbcMeshWriter() -{ -} - -Mesh *AbcMeshWriter::getEvaluatedMesh(Scene *scene_eval, - Object *ob_eval, - bool &UNUSED(r_needsfree)) -{ - return mesh_get_eval_final(m_settings.depsgraph, scene_eval, ob_eval, &CD_MASK_MESH); -} diff --git a/source/blender/io/alembic/intern/abc_writer_mesh.h b/source/blender/io/alembic/intern/abc_writer_mesh.h deleted file mode 100644 index 9152a370e4f..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_mesh.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_MESH_H__ -#define __ABC_WRITER_MESH_H__ - -#include "abc_customdata.h" -#include "abc_writer_object.h" - -struct Mesh; -struct ModifierData; - -/* Writer for Alembic meshes. Does not assume the object is a mesh object. */ -class AbcGenericMeshWriter : public AbcObjectWriter { - protected: - Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema; - Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample; - - Alembic::AbcGeom::OSubDSchema m_subdiv_schema; - Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample; - - Alembic::Abc::OArrayProperty m_mat_indices; - - bool m_is_animated; - ModifierData *m_subsurf_mod; - - CDStreamConfig m_custom_data_config; - - bool m_is_liquid; - bool m_is_subd; - - public: - AbcGenericMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - ~AbcGenericMeshWriter(); - void setIsAnimated(bool is_animated); - - protected: - virtual void do_write(); - virtual bool isAnimated() const; - virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) = 0; - virtual void freeEvaluatedMesh(struct Mesh *mesh); - - Mesh *getFinalMesh(bool &r_needsfree); - - void writeMesh(struct Mesh *mesh); - void writeSubD(struct Mesh *mesh); - - void writeArbGeoParams(struct Mesh *mesh); - void getGeoGroups(struct Mesh *mesh, std::map> &geoGroups); - - /* fluid surfaces support */ - void getVelocities(struct Mesh *mesh, std::vector &vels); - - template void writeFaceSets(struct Mesh *mesh, Schema &schema); -}; - -class AbcMeshWriter : public AbcGenericMeshWriter { - public: - AbcMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - ~AbcMeshWriter(); - - protected: - virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override; -}; - -#endif /* __ABC_WRITER_MESH_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_nurbs.cc b/source/blender/io/alembic/intern/abc_writer_nurbs.cc deleted file mode 100644 index 8b4a1050d33..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_nurbs.cc +++ /dev/null @@ -1,170 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_nurbs.h" -#include "abc_axis_conversion.h" -#include "abc_writer_transform.h" - -#include "DNA_curve_types.h" -#include "DNA_object_types.h" - -#include "BLI_listbase.h" - -#include "BKE_curve.h" - -using Alembic::AbcGeom::FloatArraySample; -using Alembic::AbcGeom::OBoolProperty; -using Alembic::AbcGeom::OCompoundProperty; -using Alembic::AbcGeom::ONuPatch; -using Alembic::AbcGeom::ONuPatchSchema; - -AbcNurbsWriter::AbcNurbsWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent) -{ - m_is_animated = isAnimated(); - - /* if the object is static, use the default static time sampling */ - if (!m_is_animated) { - m_time_sampling = 0; - } - - Curve *curve = static_cast(m_object->data); - size_t numNurbs = BLI_listbase_count(&curve->nurb); - - for (size_t i = 0; i < numNurbs; i++) { - std::stringstream str; - str << m_name << '_' << i; - - while (parent->alembicXform().getChildHeader(str.str())) { - str << "_"; - } - - ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling); - m_nurbs_schema.push_back(nurbs.getSchema()); - } -} - -bool AbcNurbsWriter::isAnimated() const -{ - /* check if object has shape keys */ - Curve *cu = static_cast(m_object->data); - return (cu->key != NULL); -} - -static void get_knots(std::vector &knots, const int num_knots, float *nu_knots) -{ - if (num_knots <= 1) { - return; - } - - /* Add an extra knot at the beginning and end of the array since most apps - * require/expect them. */ - knots.reserve(num_knots + 2); - - knots.push_back(0.0f); - - for (int i = 0; i < num_knots; i++) { - knots.push_back(nu_knots[i]); - } - - knots[0] = 2.0f * knots[1] - knots[2]; - knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]); -} - -void AbcNurbsWriter::do_write() -{ - /* we have already stored a sample for this object. */ - if (!m_first_frame && !m_is_animated) { - return; - } - - if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) { - return; - } - - Curve *curve = static_cast(m_object->data); - ListBase *nulb; - - if (m_object->runtime.curve_cache->deformed_nurbs.first != NULL) { - nulb = &m_object->runtime.curve_cache->deformed_nurbs; - } - else { - nulb = BKE_curve_nurbs_get(curve); - } - - size_t count = 0; - for (Nurb *nu = static_cast(nulb->first); nu; nu = nu->next, count++) { - std::vector knotsU; - get_knots(knotsU, KNOTSU(nu), nu->knotsu); - - std::vector knotsV; - get_knots(knotsV, KNOTSV(nu), nu->knotsv); - - const int size = nu->pntsu * nu->pntsv; - std::vector positions(size); - std::vector weights(size); - - const BPoint *bp = nu->bp; - - for (int i = 0; i < size; i++, bp++) { - copy_yup_from_zup(positions[i].getValue(), bp->vec); - weights[i] = bp->vec[3]; - } - - ONuPatchSchema::Sample sample; - sample.setUOrder(nu->orderu + 1); - sample.setVOrder(nu->orderv + 1); - sample.setPositions(positions); - sample.setPositionWeights(weights); - sample.setUKnot(FloatArraySample(knotsU)); - sample.setVKnot(FloatArraySample(knotsV)); - sample.setNu(nu->pntsu); - sample.setNv(nu->pntsv); - - /* TODO(kevin): to accommodate other software we should duplicate control - * points to indicate that a NURBS is cyclic. */ - OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties(); - - if ((nu->flagu & CU_NURB_ENDPOINT) != 0) { - OBoolProperty prop(user_props, "endpoint_u"); - prop.set(true); - } - - if ((nu->flagv & CU_NURB_ENDPOINT) != 0) { - OBoolProperty prop(user_props, "endpoint_v"); - prop.set(true); - } - - if ((nu->flagu & CU_NURB_CYCLIC) != 0) { - OBoolProperty prop(user_props, "cyclic_u"); - prop.set(true); - } - - if ((nu->flagv & CU_NURB_CYCLIC) != 0) { - OBoolProperty prop(user_props, "cyclic_v"); - prop.set(true); - } - - m_nurbs_schema[count].set(sample); - } -} diff --git a/source/blender/io/alembic/intern/abc_writer_nurbs.h b/source/blender/io/alembic/intern/abc_writer_nurbs.h deleted file mode 100644 index c6a3c399b66..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_nurbs.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_NURBS_H__ -#define __ABC_WRITER_NURBS_H__ - -#include "abc_writer_object.h" - -class AbcNurbsWriter : public AbcObjectWriter { - std::vector m_nurbs_schema; - bool m_is_animated; - - public: - AbcNurbsWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - private: - virtual void do_write(); - - bool isAnimated() const; -}; - -#endif /* __ABC_WRITER_NURBS_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_object.cc b/source/blender/io/alembic/intern/abc_writer_object.cc deleted file mode 100644 index 3d280d9f0a4..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_object.cc +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_object.h" - -#include "DNA_object_types.h" - -#include "BKE_object.h" - -AbcObjectWriter::AbcObjectWriter(Object *ob, - uint32_t time_sampling, - ExportSettings &settings, - AbcObjectWriter *parent) - : m_object(ob), m_settings(settings), m_time_sampling(time_sampling), m_first_frame(true) -{ - /* This class is used as superclass for objects themselves (i.e. transforms) and for object - * data (meshes, curves, cameras, etc.). However, when writing transforms, the m_name field is - * ignored. This is a temporary tweak to get the exporter to write object data with the data - * name instead of the object name in a safe way. */ - if (m_object->data == nullptr) { - m_name = get_id_name(m_object); - } - else { - ID *ob_data = static_cast(m_object->data); - m_name = get_id_name(ob_data); - } - - if (parent) { - parent->addChild(this); - } -} - -AbcObjectWriter::~AbcObjectWriter() -{ -} - -void AbcObjectWriter::addChild(AbcObjectWriter *child) -{ - m_children.push_back(child); -} - -Imath::Box3d AbcObjectWriter::bounds() -{ - BoundBox *bb = BKE_object_boundbox_get(this->m_object); - - if (!bb) { - if (this->m_object->type != OB_CAMERA) { - ABC_LOG(m_settings.logger) << "Bounding box is null!\n"; - } - - return Imath::Box3d(); - } - - /* Convert Z-up to Y-up. This also changes which vector goes into which min/max property. */ - this->m_bounds.min.x = bb->vec[0][0]; - this->m_bounds.min.y = bb->vec[0][2]; - this->m_bounds.min.z = -bb->vec[6][1]; - - this->m_bounds.max.x = bb->vec[6][0]; - this->m_bounds.max.y = bb->vec[6][2]; - this->m_bounds.max.z = -bb->vec[0][1]; - - return this->m_bounds; -} - -void AbcObjectWriter::write() -{ - do_write(); - m_first_frame = false; -} diff --git a/source/blender/io/alembic/intern/abc_writer_object.h b/source/blender/io/alembic/intern/abc_writer_object.h deleted file mode 100644 index 830c4aee903..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_object.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_OBJECT_H__ -#define __ABC_WRITER_OBJECT_H__ - -#include -#include - -#include "abc_exporter.h" - -#include "DNA_ID.h" - -class AbcTransformWriter; - -struct Main; -struct Object; - -class AbcObjectWriter { - protected: - Object *m_object; - ExportSettings &m_settings; - - uint32_t m_time_sampling; - - Imath::Box3d m_bounds; - std::vector m_children; - - std::vector> m_props; - - bool m_first_frame; - std::string m_name; - - public: - AbcObjectWriter(Object *ob, - uint32_t time_sampling, - ExportSettings &settings, - AbcObjectWriter *parent = NULL); - - virtual ~AbcObjectWriter(); - - void addChild(AbcObjectWriter *child); - - virtual Imath::Box3d bounds(); - - void write(); - - private: - virtual void do_write() = 0; -}; - -#endif /* __ABC_WRITER_OBJECT_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_points.cc b/source/blender/io/alembic/intern/abc_writer_points.cc deleted file mode 100644 index 6be4bcfdbe7..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_points.cc +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_points.h" -#include "abc_util.h" -#include "abc_writer_mesh.h" -#include "abc_writer_transform.h" - -#include "DNA_object_types.h" -#include "DNA_particle_types.h" - -#include "BKE_lattice.h" -#include "BKE_particle.h" - -#include "BLI_math.h" - -#include "DEG_depsgraph_query.h" - -using Alembic::AbcGeom::kVertexScope; -using Alembic::AbcGeom::OPoints; -using Alembic::AbcGeom::OPointsSchema; - -/* ************************************************************************** */ - -AbcPointsWriter::AbcPointsWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings, - ParticleSystem *psys) - : AbcObjectWriter(ob, time_sampling, settings, parent) -{ - m_psys = psys; - - std::string psys_name = get_valid_abc_name(psys->name); - OPoints points(parent->alembicXform(), psys_name, m_time_sampling); - m_schema = points.getSchema(); -} - -void AbcPointsWriter::do_write() -{ - if (!m_psys) { - return; - } - - std::vector points; - std::vector velocities; - std::vector widths; - std::vector ids; - - ParticleKey state; - - ParticleSimulationData sim; - sim.depsgraph = m_settings.depsgraph; - sim.scene = m_settings.scene; - sim.ob = m_object; - sim.psys = m_psys; - - m_psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); - - uint64_t index = 0; - for (int p = 0; p < m_psys->totpart; p++) { - float pos[3], vel[3]; - - if (m_psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) { - continue; - } - - state.time = DEG_get_ctime(m_settings.depsgraph); - - if (psys_get_particle_state(&sim, p, &state, 0) == 0) { - continue; - } - - /* location */ - mul_v3_m4v3(pos, m_object->imat, state.co); - - /* velocity */ - sub_v3_v3v3(vel, state.co, m_psys->particles[p].prev_state.co); - - /* Convert Z-up to Y-up. */ - points.push_back(Imath::V3f(pos[0], pos[2], -pos[1])); - velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1])); - widths.push_back(m_psys->particles[p].size); - ids.push_back(index++); - } - - if (m_psys->lattice_deform_data) { - BKE_lattice_deform_data_destroy(m_psys->lattice_deform_data); - m_psys->lattice_deform_data = NULL; - } - - Alembic::Abc::P3fArraySample psample(points); - Alembic::Abc::UInt64ArraySample idsample(ids); - Alembic::Abc::V3fArraySample vsample(velocities); - Alembic::Abc::FloatArraySample wsample_array(widths); - Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope); - - m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample); - m_sample.setSelfBounds(bounds()); - - m_schema.set(m_sample); -} diff --git a/source/blender/io/alembic/intern/abc_writer_points.h b/source/blender/io/alembic/intern/abc_writer_points.h deleted file mode 100644 index 184a363ae6b..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_points.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_POINTS_H__ -#define __ABC_WRITER_POINTS_H__ - -#include "abc_customdata.h" -#include "abc_writer_object.h" - -struct ParticleSystem; - -/* ************************************************************************** */ - -class AbcPointsWriter : public AbcObjectWriter { - Alembic::AbcGeom::OPointsSchema m_schema; - Alembic::AbcGeom::OPointsSchema::Sample m_sample; - ParticleSystem *m_psys; - - public: - AbcPointsWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings, - ParticleSystem *psys); - - void do_write(); -}; - -#endif /* __ABC_WRITER_POINTS_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_transform.cc b/source/blender/io/alembic/intern/abc_writer_transform.cc deleted file mode 100644 index ef3e7e29db2..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_transform.cc +++ /dev/null @@ -1,124 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_transform.h" -#include "abc_axis_conversion.h" - -#include - -#include "DNA_object_types.h" - -#include "BLI_math.h" - -#include "DEG_depsgraph_query.h" - -using Alembic::AbcGeom::OObject; -using Alembic::AbcGeom::OXform; - -AbcTransformWriter::AbcTransformWriter(Object *ob, - const OObject &abc_parent, - AbcTransformWriter *parent, - unsigned int time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent), m_proxy_from(NULL) -{ - m_is_animated = hasAnimation(m_object); - - if (!m_is_animated) { - time_sampling = 0; - } - - m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling); - m_schema = m_xform.getSchema(); - - /* Blender objects can't have a parent without inheriting the transform. */ - m_inherits_xform = parent != NULL; -} - -void AbcTransformWriter::do_write() -{ - Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object); - - if (m_first_frame) { - m_visibility = Alembic::AbcGeom::CreateVisibilityProperty( - m_xform, m_xform.getSchema().getTimeSampling()); - } - - m_visibility.set(!(ob_eval->restrictflag & OB_RESTRICT_VIEWPORT)); - - if (!m_first_frame && !m_is_animated) { - return; - } - - float yup_mat[4][4]; - create_transform_matrix( - ob_eval, yup_mat, m_inherits_xform ? ABC_MATRIX_LOCAL : ABC_MATRIX_WORLD, m_proxy_from); - - /* If the parent is a camera, undo its to-Maya rotation (see below). */ - bool is_root_object = !m_inherits_xform || ob_eval->parent == nullptr; - if (!is_root_object && ob_eval->parent->type == OB_CAMERA) { - float rot_mat[4][4]; - axis_angle_to_mat4_single(rot_mat, 'X', M_PI_2); - mul_m4_m4m4(yup_mat, rot_mat, yup_mat); - } - - /* If the object is a camera, apply an extra rotation to Maya camera orientation. */ - if (ob_eval->type == OB_CAMERA) { - float rot_mat[4][4]; - axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2); - mul_m4_m4m4(yup_mat, yup_mat, rot_mat); - } - - if (is_root_object) { - /* Only apply scaling to root objects, parenting will propagate it. */ - float scale_mat[4][4]; - scale_m4_fl(scale_mat, m_settings.global_scale); - scale_mat[3][3] = m_settings.global_scale; /* also scale translation */ - mul_m4_m4m4(yup_mat, yup_mat, scale_mat); - yup_mat[3][3] /= m_settings.global_scale; /* normalise the homogeneous component */ - } - - m_matrix = convert_matrix_datatype(yup_mat); - m_sample.setMatrix(m_matrix); - - /* Always export as "inherits transform", as this is the only way in which Blender works. The - * above code has already taken care of writing the correct matrix so that this option is not - * necessary. However, certain packages (for example the USD Alembic exporter) are incompatible - * with non-inheriting transforms and will completely ignore the transform if that is used. */ - m_sample.setInheritsXforms(true); - m_schema.set(m_sample); -} - -Imath::Box3d AbcTransformWriter::bounds() -{ - Imath::Box3d bounds; - - for (int i = 0; i < m_children.size(); i++) { - Imath::Box3d box(m_children[i]->bounds()); - bounds.extendBy(box); - } - - return Imath::transform(bounds, m_matrix); -} - -bool AbcTransformWriter::hasAnimation(Object * /*ob*/) const -{ - return true; -} diff --git a/source/blender/io/alembic/intern/abc_writer_transform.h b/source/blender/io/alembic/intern/abc_writer_transform.h deleted file mode 100644 index 4397b220761..00000000000 --- a/source/blender/io/alembic/intern/abc_writer_transform.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_TRANSFORM_H__ -#define __ABC_WRITER_TRANSFORM_H__ - -#include "abc_writer_object.h" - -#include - -class AbcTransformWriter : public AbcObjectWriter { - Alembic::AbcGeom::OXform m_xform; - Alembic::AbcGeom::OXformSchema m_schema; - Alembic::AbcGeom::XformSample m_sample; - Alembic::AbcGeom::OVisibilityProperty m_visibility; - Alembic::Abc::M44d m_matrix; - - bool m_is_animated; - bool m_inherits_xform; - - public: - Object *m_proxy_from; - - public: - AbcTransformWriter(Object *ob, - const Alembic::AbcGeom::OObject &abc_parent, - AbcTransformWriter *parent, - unsigned int time_sampling, - ExportSettings &settings); - - Alembic::AbcGeom::OXform &alembicXform() - { - return m_xform; - } - virtual Imath::Box3d bounds(); - - private: - virtual void do_write(); - - bool hasAnimation(Object *ob) const; -}; - -#endif /* __ABC_WRITER_TRANSFORM_H__ */ diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index a30b15fee5f..e70f19fdbd8 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -30,13 +30,6 @@ #include "abc_reader_points.h" #include "abc_reader_transform.h" #include "abc_util.h" -#include "abc_writer_camera.h" -#include "abc_writer_curves.h" -#include "abc_writer_hair.h" -#include "abc_writer_mesh.h" -#include "abc_writer_nurbs.h" -#include "abc_writer_points.h" -#include "abc_writer_transform.h" #include "MEM_guardedalloc.h" @@ -225,195 +218,6 @@ static void find_iobject(const IObject &object, IObject &ret, const std::string ret = tmp; } -struct ExportJobData { - ViewLayer *view_layer; - Main *bmain; - wmWindowManager *wm; - - char filename[1024]; - ExportSettings settings; - - short *stop; - short *do_update; - float *progress; - - bool was_canceled; - bool export_ok; -}; - -static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) -{ - ExportJobData *data = static_cast(customdata); - - data->stop = stop; - data->do_update = do_update; - data->progress = progress; - - /* XXX annoying hack: needed to prevent data corruption when changing - * scene frame in separate threads - */ - G.is_rendering = true; - WM_set_locked_interface(data->wm, true); - G.is_break = false; - - DEG_graph_build_from_view_layer( - data->settings.depsgraph, data->bmain, data->settings.scene, data->view_layer); - BKE_scene_graph_update_tagged(data->settings.depsgraph, data->bmain); - - try { - AbcExporter exporter(data->bmain, data->filename, data->settings); - - Scene *scene = data->settings.scene; /* for the CFRA macro */ - const int orig_frame = CFRA; - - data->was_canceled = false; - exporter(do_update, progress, &data->was_canceled); - - if (CFRA != orig_frame) { - CFRA = orig_frame; - - BKE_scene_graph_update_for_newframe(data->settings.depsgraph, data->bmain); - } - - data->export_ok = !data->was_canceled; - } - catch (const std::exception &e) { - ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n'; - } - catch (...) { - ABC_LOG(data->settings.logger) << "Abc Export: unknown error...\n"; - } -} - -static void export_endjob(void *customdata) -{ - ExportJobData *data = static_cast(customdata); - - DEG_graph_free(data->settings.depsgraph); - - if (data->was_canceled && BLI_exists(data->filename)) { - BLI_delete(data->filename, false, false); - } - - std::string log = data->settings.logger.str(); - if (!log.empty()) { - std::cerr << log; - WM_report(RPT_ERROR, "Errors occurred during the export, look in the console to know more..."); - } - - G.is_rendering = false; - WM_set_locked_interface(data->wm, false); -} - -bool ABC_export(Scene *scene, - bContext *C, - const char *filepath, - const struct AlembicExportParams *params, - bool as_background_job) -{ - ExportJobData *job = static_cast( - MEM_mallocN(sizeof(ExportJobData), "ExportJobData")); - - job->view_layer = CTX_data_view_layer(C); - job->bmain = CTX_data_main(C); - job->wm = CTX_wm_manager(C); - job->export_ok = false; - BLI_strncpy(job->filename, filepath, 1024); - - /* Alright, alright, alright.... - * - * ExportJobData contains an ExportSettings containing a SimpleLogger. - * - * Since ExportJobData is a C-style struct dynamically allocated with - * MEM_mallocN (see above), its constructor is never called, therefore the - * ExportSettings constructor is not called which implies that the - * SimpleLogger one is not called either. SimpleLogger in turn does not call - * the constructor of its data members which ultimately means that its - * std::ostringstream member has a NULL pointer. To be able to properly use - * the stream's operator<<, the pointer needs to be set, therefore we have - * to properly construct everything. And this is done using the placement - * new operator as here below. It seems hackish, but I'm too lazy to - * do bigger refactor and maybe there is a better way which does not involve - * hardcore refactoring. */ - new (&job->settings) ExportSettings(); - job->settings.scene = scene; - job->settings.depsgraph = DEG_graph_new(job->bmain, scene, job->view_layer, DAG_EVAL_RENDER); - - /* TODO(Sybren): for now we only export the active scene layer. - * Later in the 2.8 development process this may be replaced by using - * a specific collection for Alembic I/O, which can then be toggled - * between "real" objects and cached Alembic files. */ - job->settings.view_layer = job->view_layer; - - job->settings.frame_start = params->frame_start; - job->settings.frame_end = params->frame_end; - job->settings.frame_samples_xform = params->frame_samples_xform; - job->settings.frame_samples_shape = params->frame_samples_shape; - job->settings.shutter_open = params->shutter_open; - job->settings.shutter_close = params->shutter_close; - - /* TODO(Sybren): For now this is ignored, until we can get selection - * detection working through Base pointers (instead of ob->flags). */ - job->settings.selected_only = params->selected_only; - - job->settings.export_face_sets = params->face_sets; - job->settings.export_normals = params->normals; - job->settings.export_uvs = params->uvs; - job->settings.export_vcols = params->vcolors; - job->settings.export_hair = params->export_hair; - job->settings.export_particles = params->export_particles; - job->settings.apply_subdiv = params->apply_subdiv; - job->settings.curves_as_mesh = params->curves_as_mesh; - job->settings.flatten_hierarchy = params->flatten_hierarchy; - - /* TODO(Sybren): visible_layer & renderable only is ignored for now, - * to be replaced with collections later in the 2.8 dev process - * (also see note above). */ - job->settings.visible_objects_only = params->visible_objects_only; - job->settings.renderable_only = params->renderable_only; - - job->settings.use_subdiv_schema = params->use_subdiv_schema; - job->settings.pack_uv = params->packuv; - job->settings.global_scale = params->global_scale; - job->settings.triangulate = params->triangulate; - job->settings.quad_method = params->quad_method; - job->settings.ngon_method = params->ngon_method; - - if (job->settings.frame_start > job->settings.frame_end) { - std::swap(job->settings.frame_start, job->settings.frame_end); - } - - bool export_ok = false; - if (as_background_job) { - wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - job->settings.scene, - "Alembic Export", - WM_JOB_PROGRESS, - WM_JOB_TYPE_ALEMBIC); - - /* setup job */ - WM_jobs_customdata_set(wm_job, job, MEM_freeN); - WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); - WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob); - - WM_jobs_start(CTX_wm_manager(C), wm_job); - } - else { - /* Fake a job context, so that we don't need NULL pointer checks while exporting. */ - short stop = 0, do_update = 0; - float progress = 0.f; - - export_startjob(job, &stop, &do_update, &progress); - export_endjob(job); - export_ok = job->export_ok; - - MEM_freeN(job); - } - - return export_ok; -} - /* ********************** Import file ********************** */ /** -- cgit v1.2.3 From 1a448c66edf26ceb4fc4f0daad7375f0ae5f84cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 19 Jun 2020 14:31:04 +0200 Subject: Cleanup: Alembic, move the C++ code to `blender::io::alembic` namespace This commit only moves code into the `blender::io::alembic` namespace, it does not move `static` functions into an anonymous namespace. No functional changes. --- source/blender/io/alembic/exporter/abc_export_capi.cc | 2 ++ source/blender/io/alembic/exporter/abc_exporter.cc | 8 ++++++++ source/blender/io/alembic/exporter/abc_exporter.h | 16 ++++++++++++---- source/blender/io/alembic/exporter/abc_writer_archive.cc | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_archive.h | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_camera.cc | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_camera.h | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_curves.cc | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_curves.h | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_hair.cc | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_hair.h | 9 +++++++++ source/blender/io/alembic/exporter/abc_writer_mball.cc | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_mball.h | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_mesh.cc | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_mesh.h | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_nurbs.cc | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_nurbs.h | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_object.cc | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_object.h | 12 ++++++++++-- source/blender/io/alembic/exporter/abc_writer_points.cc | 8 ++++++++ source/blender/io/alembic/exporter/abc_writer_points.h | 8 ++++++++ .../blender/io/alembic/exporter/abc_writer_transform.cc | 8 ++++++++ .../blender/io/alembic/exporter/abc_writer_transform.h | 8 ++++++++ source/blender/io/alembic/intern/abc_axis_conversion.cc | 11 ++++++++++- source/blender/io/alembic/intern/abc_axis_conversion.h | 8 ++++++++ source/blender/io/alembic/intern/abc_customdata.cc | 7 +++++++ source/blender/io/alembic/intern/abc_customdata.h | 7 +++++++ source/blender/io/alembic/intern/abc_reader_archive.cc | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_archive.h | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_camera.cc | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_camera.h | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_curves.cc | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_curves.h | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_mesh.cc | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_mesh.h | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_nurbs.cc | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_nurbs.h | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_object.cc | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_object.h | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_points.cc | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_points.h | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_transform.cc | 8 ++++++++ source/blender/io/alembic/intern/abc_reader_transform.h | 8 ++++++++ source/blender/io/alembic/intern/abc_util.cc | 8 ++++++++ source/blender/io/alembic/intern/abc_util.h | 14 +++++++++++--- source/blender/io/alembic/intern/alembic_capi.cc | 2 ++ 46 files changed, 366 insertions(+), 10 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc index 3f8e95a6128..e330a2e4fdb 100644 --- a/source/blender/io/alembic/exporter/abc_export_capi.cc +++ b/source/blender/io/alembic/exporter/abc_export_capi.cc @@ -49,6 +49,8 @@ extern "C" { #include "WM_types.h" } +using namespace blender::io::alembic; + struct ExportJobData { ViewLayer *view_layer; Main *bmain; diff --git a/source/blender/io/alembic/exporter/abc_exporter.cc b/source/blender/io/alembic/exporter/abc_exporter.cc index 0de6c393e5a..a3f13e31300 100644 --- a/source/blender/io/alembic/exporter/abc_exporter.cc +++ b/source/blender/io/alembic/exporter/abc_exporter.cc @@ -67,6 +67,10 @@ using Alembic::Abc::TimeSamplingPtr; /* ************************************************************************** */ +namespace blender { +namespace io { +namespace alembic { + ExportSettings::ExportSettings() : scene(NULL), view_layer(NULL), @@ -671,3 +675,7 @@ void AbcExporter::setCurrentFrame(Main *bmain, double t) m_settings.scene->r.subframe = static_cast(t) - m_settings.scene->r.cfra; BKE_scene_graph_update_for_newframe(m_settings.depsgraph, bmain); } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_exporter.h b/source/blender/io/alembic/exporter/abc_exporter.h index c1c54cb2208..f3ca1710e33 100644 --- a/source/blender/io/alembic/exporter/abc_exporter.h +++ b/source/blender/io/alembic/exporter/abc_exporter.h @@ -28,10 +28,6 @@ #include "intern/abc_util.h" -class AbcObjectWriter; -class AbcTransformWriter; -class ArchiveWriter; - struct Base; struct Depsgraph; struct Main; @@ -39,6 +35,14 @@ struct Object; struct Scene; struct ViewLayer; +namespace blender { +namespace io { +namespace alembic { + +class AbcObjectWriter; +class AbcTransformWriter; +class ArchiveWriter; + struct ExportSettings { ExportSettings(); @@ -124,4 +128,8 @@ class AbcExporter { void setCurrentFrame(Main *bmain, double t); }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_EXPORTER_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_archive.cc b/source/blender/io/alembic/exporter/abc_writer_archive.cc index 40926532f85..741a3697b38 100644 --- a/source/blender/io/alembic/exporter/abc_writer_archive.cc +++ b/source/blender/io/alembic/exporter/abc_writer_archive.cc @@ -40,6 +40,10 @@ using Alembic::Abc::ErrorHandler; using Alembic::Abc::kWrapExisting; using Alembic::Abc::OArchive; +namespace blender { +namespace io { +namespace alembic { + /* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to * have a version supporting streams. */ static OArchive create_archive(std::ostream *ostream, @@ -96,3 +100,7 @@ OArchive &ArchiveWriter::archive() { return m_archive; } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_archive.h b/source/blender/io/alembic/exporter/abc_writer_archive.h index 737717c1710..81dd387e720 100644 --- a/source/blender/io/alembic/exporter/abc_writer_archive.h +++ b/source/blender/io/alembic/exporter/abc_writer_archive.h @@ -32,6 +32,10 @@ struct Main; struct Scene; +namespace blender { +namespace io { +namespace alembic { + /* Wrappers around input and output archives. The goal is to be able to use * streams so that unicode paths work on Windows (T49112), and to make sure that * the stream objects remain valid as long as the archives are open. @@ -47,4 +51,8 @@ class ArchiveWriter { Alembic::Abc::OArchive &archive(); }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_WRITER_ARCHIVE_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.cc b/source/blender/io/alembic/exporter/abc_writer_camera.cc index 07ae81e584f..ef6d1512a96 100644 --- a/source/blender/io/alembic/exporter/abc_writer_camera.cc +++ b/source/blender/io/alembic/exporter/abc_writer_camera.cc @@ -27,6 +27,10 @@ using Alembic::AbcGeom::OCamera; using Alembic::AbcGeom::OFloatProperty; +namespace blender { +namespace io { +namespace alembic { + AbcCameraWriter::AbcCameraWriter(Object *ob, AbcTransformWriter *parent, uint32_t time_sampling, @@ -77,3 +81,7 @@ void AbcCameraWriter::do_write() m_camera_sample.setLensSqueezeRatio(1.0); m_camera_schema.set(m_camera_sample); } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.h b/source/blender/io/alembic/exporter/abc_writer_camera.h index 3b515911a48..44b80984411 100644 --- a/source/blender/io/alembic/exporter/abc_writer_camera.h +++ b/source/blender/io/alembic/exporter/abc_writer_camera.h @@ -25,6 +25,10 @@ /* ************************************************************************** */ +namespace blender { +namespace io { +namespace alembic { + class AbcCameraWriter : public AbcObjectWriter { Alembic::AbcGeom::OCameraSchema m_camera_schema; Alembic::AbcGeom::CameraSample m_camera_sample; @@ -42,4 +46,8 @@ class AbcCameraWriter : public AbcObjectWriter { virtual void do_write(); }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_WRITER_CAMERA_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.cc b/source/blender/io/alembic/exporter/abc_writer_curves.cc index 3f9a53cffc6..7c2acb9e0c9 100644 --- a/source/blender/io/alembic/exporter/abc_writer_curves.cc +++ b/source/blender/io/alembic/exporter/abc_writer_curves.cc @@ -40,6 +40,10 @@ using Alembic::AbcGeom::OInt16Property; using Alembic::AbcGeom::ON3fGeomParam; using Alembic::AbcGeom::OV2fGeomParam; +namespace blender { +namespace io { +namespace alembic { + AbcCurveWriter::AbcCurveWriter(Object *ob, AbcTransformWriter *parent, uint32_t time_sampling, @@ -186,3 +190,7 @@ Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/, r_needsfree = true; return BKE_mesh_new_nomain_from_curve(ob_eval); } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.h b/source/blender/io/alembic/exporter/abc_writer_curves.h index 83f0289dd2d..35fb063c4a5 100644 --- a/source/blender/io/alembic/exporter/abc_writer_curves.h +++ b/source/blender/io/alembic/exporter/abc_writer_curves.h @@ -27,6 +27,10 @@ #include "abc_writer_mesh.h" #include "abc_writer_object.h" +namespace blender { +namespace io { +namespace alembic { + class AbcCurveWriter : public AbcObjectWriter { Alembic::AbcGeom::OCurvesSchema m_schema; Alembic::AbcGeom::OCurvesSchema::Sample m_sample; @@ -52,4 +56,8 @@ class AbcCurveMeshWriter : public AbcGenericMeshWriter { Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree); }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_WRITER_CURVES_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.cc b/source/blender/io/alembic/exporter/abc_writer_hair.cc index ceb2eb7d316..304aa7a5ff8 100644 --- a/source/blender/io/alembic/exporter/abc_writer_hair.cc +++ b/source/blender/io/alembic/exporter/abc_writer_hair.cc @@ -44,6 +44,10 @@ using Alembic::AbcGeom::OV2fGeomParam; /* ************************************************************************** */ +namespace blender { +namespace io { +namespace alembic { + AbcHairWriter::AbcHairWriter(Object *ob, AbcTransformWriter *parent, uint32_t time_sampling, @@ -289,3 +293,7 @@ void AbcHairWriter::write_hair_child_sample(Mesh *mesh, } } } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.h b/source/blender/io/alembic/exporter/abc_writer_hair.h index 67d1b7b3d23..b6860926d44 100644 --- a/source/blender/io/alembic/exporter/abc_writer_hair.h +++ b/source/blender/io/alembic/exporter/abc_writer_hair.h @@ -23,9 +23,14 @@ #include "abc_writer_object.h" +struct Mesh; struct ParticleSettings; struct ParticleSystem; +namespace blender { +namespace io { +namespace alembic { + class AbcHairWriter : public AbcObjectWriter { ParticleSystem *m_psys; @@ -59,4 +64,8 @@ class AbcHairWriter : public AbcObjectWriter { std::vector &hvertices); }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_WRITER_HAIR_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_mball.cc b/source/blender/io/alembic/exporter/abc_writer_mball.cc index 3593acf18b0..8ae3bb64390 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mball.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mball.cc @@ -33,6 +33,10 @@ #include "BLI_utildefines.h" +namespace blender { +namespace io { +namespace alembic { + AbcMBallWriter::AbcMBallWriter(Main *bmain, Object *ob, AbcTransformWriter *parent, @@ -93,3 +97,7 @@ bool AbcMBallWriter::isBasisBall(Scene *scene, Object *ob) Object *basis_ob = BKE_mball_basis_find(scene, ob); return ob == basis_ob; } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_mball.h b/source/blender/io/alembic/exporter/abc_writer_mball.h index e3ac1e69cae..f527b2b02a0 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mball.h +++ b/source/blender/io/alembic/exporter/abc_writer_mball.h @@ -27,6 +27,10 @@ struct Main; struct Object; +namespace blender { +namespace io { +namespace alembic { + /* AbcMBallWriter converts the metaballs to meshes at every frame, * and defers to AbcGenericMeshWriter to perform the writing * to the Alembic file. Only the basis balls are exported, as this @@ -53,4 +57,8 @@ class AbcMBallWriter : public AbcGenericMeshWriter { bool isAnimated() const override; }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_WRITER_MBALL_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index b17e123b426..512768bcd98 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -58,6 +58,10 @@ using Alembic::AbcGeom::OSubDSchema; using Alembic::AbcGeom::OV2fGeomParam; using Alembic::AbcGeom::UInt32ArraySample; +namespace blender { +namespace io { +namespace alembic { + /* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */ static void get_vertices(struct Mesh *mesh, std::vector &points) @@ -589,3 +593,7 @@ Mesh *AbcMeshWriter::getEvaluatedMesh(Scene *scene_eval, { return mesh_get_eval_final(m_settings.depsgraph, scene_eval, ob_eval, &CD_MASK_MESH); } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h index 264696333a3..e4095758ba9 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.h +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h @@ -27,6 +27,10 @@ struct Mesh; struct ModifierData; +namespace blender { +namespace io { +namespace alembic { + /* Writer for Alembic meshes. Does not assume the object is a mesh object. */ class AbcGenericMeshWriter : public AbcObjectWriter { protected: @@ -88,4 +92,8 @@ class AbcMeshWriter : public AbcGenericMeshWriter { virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override; }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_WRITER_MESH_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc index c856f894f0e..fa37c1eac59 100644 --- a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc +++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc @@ -35,6 +35,10 @@ using Alembic::AbcGeom::OCompoundProperty; using Alembic::AbcGeom::ONuPatch; using Alembic::AbcGeom::ONuPatchSchema; +namespace blender { +namespace io { +namespace alembic { + AbcNurbsWriter::AbcNurbsWriter(Object *ob, AbcTransformWriter *parent, uint32_t time_sampling, @@ -168,3 +172,7 @@ void AbcNurbsWriter::do_write() m_nurbs_schema[count].set(sample); } } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.h b/source/blender/io/alembic/exporter/abc_writer_nurbs.h index c6a3c399b66..006024c4d2f 100644 --- a/source/blender/io/alembic/exporter/abc_writer_nurbs.h +++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.h @@ -23,6 +23,10 @@ #include "abc_writer_object.h" +namespace blender { +namespace io { +namespace alembic { + class AbcNurbsWriter : public AbcObjectWriter { std::vector m_nurbs_schema; bool m_is_animated; @@ -39,4 +43,8 @@ class AbcNurbsWriter : public AbcObjectWriter { bool isAnimated() const; }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_WRITER_NURBS_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_object.cc b/source/blender/io/alembic/exporter/abc_writer_object.cc index 3d280d9f0a4..97cac3a1af9 100644 --- a/source/blender/io/alembic/exporter/abc_writer_object.cc +++ b/source/blender/io/alembic/exporter/abc_writer_object.cc @@ -24,6 +24,10 @@ #include "BKE_object.h" +namespace blender { +namespace io { +namespace alembic { + AbcObjectWriter::AbcObjectWriter(Object *ob, uint32_t time_sampling, ExportSettings &settings, @@ -85,3 +89,7 @@ void AbcObjectWriter::write() do_write(); m_first_frame = false; } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_object.h b/source/blender/io/alembic/exporter/abc_writer_object.h index 830c4aee903..0496c6a3624 100644 --- a/source/blender/io/alembic/exporter/abc_writer_object.h +++ b/source/blender/io/alembic/exporter/abc_writer_object.h @@ -28,11 +28,15 @@ #include "DNA_ID.h" -class AbcTransformWriter; - struct Main; struct Object; +namespace blender { +namespace io { +namespace alembic { + +class AbcTransformWriter; + class AbcObjectWriter { protected: Object *m_object; @@ -66,4 +70,8 @@ class AbcObjectWriter { virtual void do_write() = 0; }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_WRITER_OBJECT_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_points.cc b/source/blender/io/alembic/exporter/abc_writer_points.cc index 27f51586f47..4d7f5d11c36 100644 --- a/source/blender/io/alembic/exporter/abc_writer_points.cc +++ b/source/blender/io/alembic/exporter/abc_writer_points.cc @@ -42,6 +42,10 @@ using Alembic::AbcGeom::OPointsSchema; /* ************************************************************************** */ +namespace blender { +namespace io { +namespace alembic { + AbcPointsWriter::AbcPointsWriter(Object *ob, AbcTransformWriter *parent, uint32_t time_sampling, @@ -120,3 +124,7 @@ void AbcPointsWriter::do_write() m_schema.set(m_sample); } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_points.h b/source/blender/io/alembic/exporter/abc_writer_points.h index e5c1b81f266..5763808d6a7 100644 --- a/source/blender/io/alembic/exporter/abc_writer_points.h +++ b/source/blender/io/alembic/exporter/abc_writer_points.h @@ -31,6 +31,10 @@ struct ParticleSystem; /* ************************************************************************** */ +namespace blender { +namespace io { +namespace alembic { + class AbcPointsWriter : public AbcObjectWriter { Alembic::AbcGeom::OPointsSchema m_schema; Alembic::AbcGeom::OPointsSchema::Sample m_sample; @@ -46,4 +50,8 @@ class AbcPointsWriter : public AbcObjectWriter { void do_write(); }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_WRITER_POINTS_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.cc b/source/blender/io/alembic/exporter/abc_writer_transform.cc index 46130cb8675..fa808c1b75b 100644 --- a/source/blender/io/alembic/exporter/abc_writer_transform.cc +++ b/source/blender/io/alembic/exporter/abc_writer_transform.cc @@ -32,6 +32,10 @@ using Alembic::AbcGeom::OObject; using Alembic::AbcGeom::OXform; +namespace blender { +namespace io { +namespace alembic { + AbcTransformWriter::AbcTransformWriter(Object *ob, const OObject &abc_parent, AbcTransformWriter *parent, @@ -122,3 +126,7 @@ bool AbcTransformWriter::hasAnimation(Object * /*ob*/) const { return true; } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.h b/source/blender/io/alembic/exporter/abc_writer_transform.h index 4397b220761..af50929a963 100644 --- a/source/blender/io/alembic/exporter/abc_writer_transform.h +++ b/source/blender/io/alembic/exporter/abc_writer_transform.h @@ -25,6 +25,10 @@ #include +namespace blender { +namespace io { +namespace alembic { + class AbcTransformWriter : public AbcObjectWriter { Alembic::AbcGeom::OXform m_xform; Alembic::AbcGeom::OXformSchema m_schema; @@ -57,4 +61,8 @@ class AbcTransformWriter : public AbcObjectWriter { bool hasAnimation(Object *ob) const; }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_WRITER_TRANSFORM_H__ */ diff --git a/source/blender/io/alembic/intern/abc_axis_conversion.cc b/source/blender/io/alembic/intern/abc_axis_conversion.cc index 17db5e9c99f..a74432a3540 100644 --- a/source/blender/io/alembic/intern/abc_axis_conversion.cc +++ b/source/blender/io/alembic/intern/abc_axis_conversion.cc @@ -27,6 +27,10 @@ extern "C" { #include "BLI_math_geom.h" } +namespace blender { +namespace io { +namespace alembic { + void create_swapped_rotation_matrix(float rot_x_mat[3][3], float rot_y_mat[3][3], float rot_z_mat[3][3], @@ -72,7 +76,8 @@ void create_swapped_rotation_matrix(float rot_x_mat[3][3], rot_z_mat[1][0] = -sin(rz); rot_z_mat[0][1] = sin(rz); rot_z_mat[1][1] = cos(rz); -} +} // namespace + // alembicvoidcreate_swapped_rotation_matrix(floatrot_x_mat[3][3],floatrot_y_mat[3][3],floatrot_z_mat[3][3],constfloateuler[3],AbcAxisSwapModemode) /* Convert matrix from Z=up to Y=up or vice versa. * Use yup_mat = zup_mat for in-place conversion. */ @@ -164,3 +169,7 @@ void create_transform_matrix(Object *obj, copy_m44_axis_swap(r_yup_mat, zup_mat, ABC_YUP_FROM_ZUP); } + +} // namespace alembic +} // namespace io +} // namespace blender \ No newline at end of file diff --git a/source/blender/io/alembic/intern/abc_axis_conversion.h b/source/blender/io/alembic/intern/abc_axis_conversion.h index 7fde0e92ea4..734c5ca01c9 100644 --- a/source/blender/io/alembic/intern/abc_axis_conversion.h +++ b/source/blender/io/alembic/intern/abc_axis_conversion.h @@ -30,6 +30,10 @@ struct Object; # define ABC_INLINE static inline #endif +namespace blender { +namespace io { +namespace alembic { + /* TODO(kevin): for now keeping these transformations hardcoded to make sure * everything works properly, and also because Alembic is almost exclusively * used in Y-up software, but eventually they'll be set by the user in the UI @@ -97,3 +101,7 @@ void create_transform_matrix(Object *obj, float r_transform_mat[4][4], AbcMatrixMode mode, Object *proxy_from); + +} // namespace alembic +} // namespace io +} // namespace blender \ No newline at end of file diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc index 62f6a52f7cf..f3e2342e844 100644 --- a/source/blender/io/alembic/intern/abc_customdata.cc +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -50,6 +50,9 @@ using Alembic::Abc::V2fArraySample; using Alembic::AbcGeom::OC4fGeomParam; using Alembic::AbcGeom::OV2fGeomParam; +namespace blender { +namespace io { +namespace alembic { static void get_uvs(const CDStreamConfig &config, std::vector &uvs, @@ -485,3 +488,7 @@ void read_custom_data(const std::string &iobject_full_name, } } } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h index 96b57b08681..a5f480d48c3 100644 --- a/source/blender/io/alembic/intern/abc_customdata.h +++ b/source/blender/io/alembic/intern/abc_customdata.h @@ -38,6 +38,9 @@ struct Mesh; using Alembic::Abc::ICompoundProperty; using Alembic::Abc::OCompoundProperty; +namespace blender { +namespace io { +namespace alembic { struct UVSample { std::vector uvs; @@ -112,4 +115,8 @@ void read_custom_data(const std::string &iobject_full_name, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss); +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_CUSTOMDATA_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_archive.cc b/source/blender/io/alembic/intern/abc_reader_archive.cc index d55736f732a..d7f1095f0fd 100644 --- a/source/blender/io/alembic/intern/abc_reader_archive.cc +++ b/source/blender/io/alembic/intern/abc_reader_archive.cc @@ -39,6 +39,10 @@ using Alembic::Abc::Exception; using Alembic::Abc::IArchive; using Alembic::Abc::kWrapExisting; +namespace blender { +namespace io { +namespace alembic { + static IArchive open_archive(const std::string &filename, const std::vector &input_streams) { @@ -103,3 +107,7 @@ Alembic::Abc::IObject ArchiveReader::getTop() { return m_archive.getTop(); } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/intern/abc_reader_archive.h b/source/blender/io/alembic/intern/abc_reader_archive.h index 304c876adce..6f3478aa14e 100644 --- a/source/blender/io/alembic/intern/abc_reader_archive.h +++ b/source/blender/io/alembic/intern/abc_reader_archive.h @@ -32,6 +32,10 @@ struct Main; struct Scene; +namespace blender { +namespace io { +namespace alembic { + /* Wrappers around input and output archives. The goal is to be able to use * streams so that unicode paths work on Windows (T49112), and to make sure that * the stream objects remain valid as long as the archives are open. @@ -50,4 +54,8 @@ class ArchiveReader { Alembic::Abc::IObject getTop(); }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_READER_ARCHIVE_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_camera.cc b/source/blender/io/alembic/intern/abc_reader_camera.cc index 0752534f8c2..3affb35908d 100644 --- a/source/blender/io/alembic/intern/abc_reader_camera.cc +++ b/source/blender/io/alembic/intern/abc_reader_camera.cc @@ -37,6 +37,10 @@ using Alembic::AbcGeom::IFloatProperty; using Alembic::AbcGeom::ISampleSelector; using Alembic::AbcGeom::kWrapExisting; +namespace blender { +namespace io { +namespace alembic { + AbcCameraReader::AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings) : AbcObjectReader(object, settings) { @@ -109,3 +113,7 @@ void AbcCameraReader::readObjectData(Main *bmain, const ISampleSelector &sample_ m_object = BKE_object_add_only_object(bmain, OB_CAMERA, m_object_name.c_str()); m_object->data = bcam; } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/intern/abc_reader_camera.h b/source/blender/io/alembic/intern/abc_reader_camera.h index 1d9763b0454..3aa905e36c4 100644 --- a/source/blender/io/alembic/intern/abc_reader_camera.h +++ b/source/blender/io/alembic/intern/abc_reader_camera.h @@ -23,6 +23,10 @@ #include "abc_reader_object.h" +namespace blender { +namespace io { +namespace alembic { + class AbcCameraReader : public AbcObjectReader { Alembic::AbcGeom::ICameraSchema m_schema; @@ -37,4 +41,8 @@ class AbcCameraReader : public AbcObjectReader { void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_READER_CAMERA_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc index d5e0b694294..a505dfd654b 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.cc +++ b/source/blender/io/alembic/intern/abc_reader_curves.cc @@ -54,6 +54,10 @@ using Alembic::AbcGeom::IInt16Property; using Alembic::AbcGeom::ISampleSelector; using Alembic::AbcGeom::kWrapExisting; +namespace blender { +namespace io { +namespace alembic { + AbcCurveReader::AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings) : AbcObjectReader(object, settings) { @@ -351,3 +355,7 @@ Mesh *AbcCurveReader::read_mesh(Mesh *existing_mesh, return BKE_mesh_new_nomain_from_curve(m_object); } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/intern/abc_reader_curves.h b/source/blender/io/alembic/intern/abc_reader_curves.h index eb0538308f8..bef5bec2e74 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.h +++ b/source/blender/io/alembic/intern/abc_reader_curves.h @@ -31,6 +31,10 @@ struct Curve; #define ABC_CURVE_RESOLUTION_U_PROPNAME "blender:resolution" +namespace blender { +namespace io { +namespace alembic { + class AbcCurveReader : public AbcObjectReader { Alembic::AbcGeom::ICurvesSchema m_curves_schema; @@ -53,4 +57,8 @@ class AbcCurveReader : public AbcObjectReader { const Alembic::Abc::ISampleSelector &sample_selector); }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_READER_CURVES_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 8b79a3a0aa0..a37ac84bdfb 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -59,6 +59,10 @@ using Alembic::AbcGeom::N3fArraySamplePtr; using Alembic::AbcGeom::UInt32ArraySamplePtr; using Alembic::AbcGeom::V2fArraySamplePtr; +namespace blender { +namespace io { +namespace alembic { + /* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */ /* Some helpers for mesh generation */ @@ -930,3 +934,7 @@ Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh, return config.mesh; } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h index bc95c7ec134..6acfc057151 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.h +++ b/source/blender/io/alembic/intern/abc_reader_mesh.h @@ -26,6 +26,10 @@ struct Mesh; +namespace blender { +namespace io { +namespace alembic { + class AbcMeshReader : public AbcObjectReader { Alembic::AbcGeom::IPolyMeshSchema m_schema; @@ -83,4 +87,8 @@ void read_mverts(MVert *mverts, CDStreamConfig get_config(struct Mesh *mesh); +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_READER_MESH_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc index 5b9954b3ff6..3ca3f6229ab 100644 --- a/source/blender/io/alembic/intern/abc_reader_nurbs.cc +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc @@ -44,6 +44,10 @@ using Alembic::AbcGeom::INuPatch; using Alembic::AbcGeom::INuPatchSchema; using Alembic::AbcGeom::IObject; +namespace blender { +namespace io { +namespace alembic { + AbcNurbsReader::AbcNurbsReader(const IObject &object, ImportSettings &settings) : AbcObjectReader(object, settings) { @@ -222,3 +226,7 @@ void AbcNurbsReader::getNurbsPatches(const IObject &obj) getNurbsPatches(child); } } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.h b/source/blender/io/alembic/intern/abc_reader_nurbs.h index f4284c136fb..49840e49bec 100644 --- a/source/blender/io/alembic/intern/abc_reader_nurbs.h +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.h @@ -23,6 +23,10 @@ #include "abc_reader_object.h" +namespace blender { +namespace io { +namespace alembic { + class AbcNurbsReader : public AbcObjectReader { std::vector> m_schemas; @@ -37,4 +41,8 @@ class AbcNurbsReader : public AbcObjectReader { void getNurbsPatches(const Alembic::Abc::IObject &obj); }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_READER_NURBS_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc index e5bd0771a42..39b9cd4c161 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.cc +++ b/source/blender/io/alembic/intern/abc_reader_object.cc @@ -41,6 +41,10 @@ using Alembic::AbcGeom::IObject; using Alembic::AbcGeom::IXform; using Alembic::AbcGeom::IXformSchema; +namespace blender { +namespace io { +namespace alembic { + AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings) : m_name(""), m_object_name(""), @@ -330,3 +334,7 @@ void AbcObjectReader::decref() m_refcount--; BLI_assert(m_refcount >= 0); } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h index dcc2697e0b5..d8e0e57f2f9 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.h +++ b/source/blender/io/alembic/intern/abc_reader_object.h @@ -33,6 +33,10 @@ struct Object; using Alembic::AbcCoreAbstract::chrono_t; +namespace blender { +namespace io { +namespace alembic { + struct ImportSettings { bool do_convert_mat; float conversion_mat[4][4]; @@ -166,4 +170,8 @@ class AbcObjectReader { Imath::M44d get_matrix(const Alembic::AbcGeom::IXformSchema &schema, const float time); +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_READER_OBJECT_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc index c5d08693176..b805da4daa3 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.cc +++ b/source/blender/io/alembic/intern/abc_reader_points.cc @@ -43,6 +43,10 @@ using Alembic::AbcGeom::IPoints; using Alembic::AbcGeom::IPointsSchema; using Alembic::AbcGeom::ISampleSelector; +namespace blender { +namespace io { +namespace alembic { + AbcPointsReader::AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings) : AbcObjectReader(object, settings) { @@ -153,3 +157,7 @@ struct Mesh *AbcPointsReader::read_mesh(struct Mesh *existing_mesh, return new_mesh ? new_mesh : existing_mesh; } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/intern/abc_reader_points.h b/source/blender/io/alembic/intern/abc_reader_points.h index 99881e091f9..91ddcf31de1 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.h +++ b/source/blender/io/alembic/intern/abc_reader_points.h @@ -27,6 +27,10 @@ #include "abc_customdata.h" #include "abc_reader_object.h" +namespace blender { +namespace io { +namespace alembic { + class AbcPointsReader : public AbcObjectReader { Alembic::AbcGeom::IPointsSchema m_schema; Alembic::AbcGeom::IPointsSchema::Sample m_sample; @@ -51,4 +55,8 @@ void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema, const Alembic::AbcGeom::ISampleSelector &selector, CDStreamConfig &config); +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_READER_POINTS_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_transform.cc b/source/blender/io/alembic/intern/abc_reader_transform.cc index 3df391f8432..456d1da2c68 100644 --- a/source/blender/io/alembic/intern/abc_reader_transform.cc +++ b/source/blender/io/alembic/intern/abc_reader_transform.cc @@ -29,6 +29,10 @@ using Alembic::Abc::ISampleSelector; +namespace blender { +namespace io { +namespace alembic { + AbcEmptyReader::AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings) : AbcObjectReader(object, settings) { @@ -72,3 +76,7 @@ void AbcEmptyReader::readObjectData(Main *bmain, const ISampleSelector &UNUSED(s m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_object_name.c_str()); m_object->data = NULL; } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/intern/abc_reader_transform.h b/source/blender/io/alembic/intern/abc_reader_transform.h index 6b4d23c1884..57e1f9b2334 100644 --- a/source/blender/io/alembic/intern/abc_reader_transform.h +++ b/source/blender/io/alembic/intern/abc_reader_transform.h @@ -25,6 +25,10 @@ #include +namespace blender { +namespace io { +namespace alembic { + class AbcEmptyReader : public AbcObjectReader { Alembic::AbcGeom::IXformSchema m_schema; @@ -39,4 +43,8 @@ class AbcEmptyReader : public AbcObjectReader { void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); }; +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_READER_TRANSFORM_H__ */ diff --git a/source/blender/io/alembic/intern/abc_util.cc b/source/blender/io/alembic/intern/abc_util.cc index 92f4b9532a1..04febd7bfcb 100644 --- a/source/blender/io/alembic/intern/abc_util.cc +++ b/source/blender/io/alembic/intern/abc_util.cc @@ -38,6 +38,10 @@ #include "PIL_time.h" +namespace blender { +namespace io { +namespace alembic { + std::string get_id_name(const Object *const ob) { if (!ob) { @@ -256,3 +260,7 @@ std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger) os << logger.str(); return os; } + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/intern/abc_util.h b/source/blender/io/alembic/intern/abc_util.h index 8a5763bb38b..fbad1cce306 100644 --- a/source/blender/io/alembic/intern/abc_util.h +++ b/source/blender/io/alembic/intern/abc_util.h @@ -41,12 +41,16 @@ struct CacheReader { using Alembic::Abc::chrono_t; -class AbcObjectReader; -struct ImportSettings; - struct ID; struct Object; +namespace blender { +namespace io { +namespace alembic { + +class AbcObjectReader; +struct ImportSettings; + std::string get_id_name(const ID *const id); std::string get_id_name(const Object *const ob); std::string get_valid_abc_name(const char *name); @@ -165,4 +169,8 @@ class SimpleLogger { */ std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger); +} // namespace alembic +} // namespace io +} // namespace blender + #endif /* __ABC_UTIL_H__ */ diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index e70f19fdbd8..40dd690301e 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -99,6 +99,8 @@ using Alembic::AbcGeom::XformSample; using Alembic::AbcMaterial::IMaterial; +using namespace blender::io::alembic; + struct AbcArchiveHandle { int unused; }; -- cgit v1.2.3 From 697b1736ef0549ab84b7e62d599bae85d510540d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 19 Jun 2020 15:14:40 +0200 Subject: Cleanup: Alembic, replace `#ifndef` guards with `#pragma once` No functional changes. --- source/blender/io/alembic/ABC_alembic.h | 6 +----- source/blender/io/alembic/exporter/abc_exporter.h | 6 +----- source/blender/io/alembic/exporter/abc_writer_archive.h | 6 +----- source/blender/io/alembic/exporter/abc_writer_camera.h | 6 +----- source/blender/io/alembic/exporter/abc_writer_curves.h | 6 +----- source/blender/io/alembic/exporter/abc_writer_hair.h | 6 +----- source/blender/io/alembic/exporter/abc_writer_mball.h | 6 +----- source/blender/io/alembic/exporter/abc_writer_mesh.h | 6 +----- source/blender/io/alembic/exporter/abc_writer_nurbs.h | 6 +----- source/blender/io/alembic/exporter/abc_writer_object.h | 6 +----- source/blender/io/alembic/exporter/abc_writer_points.h | 6 +----- source/blender/io/alembic/exporter/abc_writer_transform.h | 6 +----- source/blender/io/alembic/intern/abc_customdata.h | 6 +----- source/blender/io/alembic/intern/abc_reader_archive.h | 6 +----- source/blender/io/alembic/intern/abc_reader_camera.h | 6 +----- source/blender/io/alembic/intern/abc_reader_curves.h | 6 +----- source/blender/io/alembic/intern/abc_reader_mesh.h | 6 +----- source/blender/io/alembic/intern/abc_reader_nurbs.h | 6 +----- source/blender/io/alembic/intern/abc_reader_object.h | 6 +----- source/blender/io/alembic/intern/abc_reader_points.h | 6 +----- source/blender/io/alembic/intern/abc_reader_transform.h | 6 +----- source/blender/io/alembic/intern/abc_util.h | 6 +----- 22 files changed, 22 insertions(+), 110 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h index ba430752b29..ddf75aa3258 100644 --- a/source/blender/io/alembic/ABC_alembic.h +++ b/source/blender/io/alembic/ABC_alembic.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_ALEMBIC_H__ -#define __ABC_ALEMBIC_H__ - #ifdef __cplusplus extern "C" { #endif @@ -133,5 +131,3 @@ struct CacheReader *CacheReader_open_alembic_object(struct AbcArchiveHandle *han #ifdef __cplusplus } #endif - -#endif /* __ABC_ALEMBIC_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_exporter.h b/source/blender/io/alembic/exporter/abc_exporter.h index f3ca1710e33..af30f2ceb50 100644 --- a/source/blender/io/alembic/exporter/abc_exporter.h +++ b/source/blender/io/alembic/exporter/abc_exporter.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_EXPORTER_H__ -#define __ABC_EXPORTER_H__ - #include #include #include @@ -131,5 +129,3 @@ class AbcExporter { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_EXPORTER_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_archive.h b/source/blender/io/alembic/exporter/abc_writer_archive.h index 81dd387e720..db13dc0ec92 100644 --- a/source/blender/io/alembic/exporter/abc_writer_archive.h +++ b/source/blender/io/alembic/exporter/abc_writer_archive.h @@ -16,14 +16,12 @@ * The Original Code is Copyright (C) 2016 Kévin Dietrich. * All rights reserved. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_WRITER_ARCHIVE_H__ -#define __ABC_WRITER_ARCHIVE_H__ - #include #include @@ -54,5 +52,3 @@ class ArchiveWriter { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_WRITER_ARCHIVE_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.h b/source/blender/io/alembic/exporter/abc_writer_camera.h index 44b80984411..befa1e24551 100644 --- a/source/blender/io/alembic/exporter/abc_writer_camera.h +++ b/source/blender/io/alembic/exporter/abc_writer_camera.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_WRITER_CAMERA_H__ -#define __ABC_WRITER_CAMERA_H__ - #include "abc_writer_object.h" /* ************************************************************************** */ @@ -49,5 +47,3 @@ class AbcCameraWriter : public AbcObjectWriter { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_WRITER_CAMERA_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.h b/source/blender/io/alembic/exporter/abc_writer_curves.h index 35fb063c4a5..d12ebc46a22 100644 --- a/source/blender/io/alembic/exporter/abc_writer_curves.h +++ b/source/blender/io/alembic/exporter/abc_writer_curves.h @@ -16,14 +16,12 @@ * The Original Code is Copyright (C) 2016 Kévin Dietrich. * All rights reserved. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_WRITER_CURVES_H__ -#define __ABC_WRITER_CURVES_H__ - #include "abc_writer_mesh.h" #include "abc_writer_object.h" @@ -59,5 +57,3 @@ class AbcCurveMeshWriter : public AbcGenericMeshWriter { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_WRITER_CURVES_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.h b/source/blender/io/alembic/exporter/abc_writer_hair.h index b6860926d44..d7831684a12 100644 --- a/source/blender/io/alembic/exporter/abc_writer_hair.h +++ b/source/blender/io/alembic/exporter/abc_writer_hair.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_WRITER_HAIR_H__ -#define __ABC_WRITER_HAIR_H__ - #include "abc_writer_object.h" struct Mesh; @@ -67,5 +65,3 @@ class AbcHairWriter : public AbcObjectWriter { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_WRITER_HAIR_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_mball.h b/source/blender/io/alembic/exporter/abc_writer_mball.h index f527b2b02a0..d632f0bd410 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mball.h +++ b/source/blender/io/alembic/exporter/abc_writer_mball.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_WRITER_MBALL_H__ -#define __ABC_WRITER_MBALL_H__ - #include "abc_writer_mesh.h" #include "abc_writer_object.h" @@ -60,5 +58,3 @@ class AbcMBallWriter : public AbcGenericMeshWriter { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_WRITER_MBALL_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h index e4095758ba9..412c5530b32 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.h +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_WRITER_MESH_H__ -#define __ABC_WRITER_MESH_H__ - #include "abc_writer_object.h" #include "intern/abc_customdata.h" @@ -95,5 +93,3 @@ class AbcMeshWriter : public AbcGenericMeshWriter { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_WRITER_MESH_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.h b/source/blender/io/alembic/exporter/abc_writer_nurbs.h index 006024c4d2f..b2a9cf1b786 100644 --- a/source/blender/io/alembic/exporter/abc_writer_nurbs.h +++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_WRITER_NURBS_H__ -#define __ABC_WRITER_NURBS_H__ - #include "abc_writer_object.h" namespace blender { @@ -46,5 +44,3 @@ class AbcNurbsWriter : public AbcObjectWriter { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_WRITER_NURBS_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_object.h b/source/blender/io/alembic/exporter/abc_writer_object.h index 0496c6a3624..7f8e8735812 100644 --- a/source/blender/io/alembic/exporter/abc_writer_object.h +++ b/source/blender/io/alembic/exporter/abc_writer_object.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_WRITER_OBJECT_H__ -#define __ABC_WRITER_OBJECT_H__ - #include #include @@ -73,5 +71,3 @@ class AbcObjectWriter { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_WRITER_OBJECT_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_points.h b/source/blender/io/alembic/exporter/abc_writer_points.h index 5763808d6a7..7e8a3eaadcd 100644 --- a/source/blender/io/alembic/exporter/abc_writer_points.h +++ b/source/blender/io/alembic/exporter/abc_writer_points.h @@ -16,14 +16,12 @@ * The Original Code is Copyright (C) 2016 Kévin Dietrich. * All rights reserved. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_WRITER_POINTS_H__ -#define __ABC_WRITER_POINTS_H__ - #include "abc_writer_object.h" #include "intern/abc_customdata.h" @@ -53,5 +51,3 @@ class AbcPointsWriter : public AbcObjectWriter { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_WRITER_POINTS_H__ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.h b/source/blender/io/alembic/exporter/abc_writer_transform.h index af50929a963..fc997d77f4c 100644 --- a/source/blender/io/alembic/exporter/abc_writer_transform.h +++ b/source/blender/io/alembic/exporter/abc_writer_transform.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_WRITER_TRANSFORM_H__ -#define __ABC_WRITER_TRANSFORM_H__ - #include "abc_writer_object.h" #include @@ -64,5 +62,3 @@ class AbcTransformWriter : public AbcObjectWriter { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_WRITER_TRANSFORM_H__ */ diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h index a5f480d48c3..8f4accb70dc 100644 --- a/source/blender/io/alembic/intern/abc_customdata.h +++ b/source/blender/io/alembic/intern/abc_customdata.h @@ -16,14 +16,12 @@ * The Original Code is Copyright (C) 2016 Kévin Dietrich. * All rights reserved. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_CUSTOMDATA_H__ -#define __ABC_CUSTOMDATA_H__ - #include #include @@ -118,5 +116,3 @@ void read_custom_data(const std::string &iobject_full_name, } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_CUSTOMDATA_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_archive.h b/source/blender/io/alembic/intern/abc_reader_archive.h index 6f3478aa14e..aea62b46cce 100644 --- a/source/blender/io/alembic/intern/abc_reader_archive.h +++ b/source/blender/io/alembic/intern/abc_reader_archive.h @@ -16,14 +16,12 @@ * The Original Code is Copyright (C) 2016 Kévin Dietrich. * All rights reserved. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_READER_ARCHIVE_H__ -#define __ABC_READER_ARCHIVE_H__ - #include #include @@ -57,5 +55,3 @@ class ArchiveReader { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_READER_ARCHIVE_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_camera.h b/source/blender/io/alembic/intern/abc_reader_camera.h index 3aa905e36c4..b733269407b 100644 --- a/source/blender/io/alembic/intern/abc_reader_camera.h +++ b/source/blender/io/alembic/intern/abc_reader_camera.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_READER_CAMERA_H__ -#define __ABC_READER_CAMERA_H__ - #include "abc_reader_object.h" namespace blender { @@ -44,5 +42,3 @@ class AbcCameraReader : public AbcObjectReader { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_READER_CAMERA_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_curves.h b/source/blender/io/alembic/intern/abc_reader_curves.h index bef5bec2e74..7488adb9b24 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.h +++ b/source/blender/io/alembic/intern/abc_reader_curves.h @@ -16,14 +16,12 @@ * The Original Code is Copyright (C) 2016 Kévin Dietrich. * All rights reserved. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_READER_CURVES_H__ -#define __ABC_READER_CURVES_H__ - #include "abc_reader_mesh.h" #include "abc_reader_object.h" @@ -60,5 +58,3 @@ class AbcCurveReader : public AbcObjectReader { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_READER_CURVES_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h index 6acfc057151..363a74b8b5f 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.h +++ b/source/blender/io/alembic/intern/abc_reader_mesh.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_READER_MESH_H__ -#define __ABC_READER_MESH_H__ - #include "abc_customdata.h" #include "abc_reader_object.h" @@ -90,5 +88,3 @@ CDStreamConfig get_config(struct Mesh *mesh); } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_READER_MESH_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.h b/source/blender/io/alembic/intern/abc_reader_nurbs.h index 49840e49bec..738da82885d 100644 --- a/source/blender/io/alembic/intern/abc_reader_nurbs.h +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_READER_NURBS_H__ -#define __ABC_READER_NURBS_H__ - #include "abc_reader_object.h" namespace blender { @@ -44,5 +42,3 @@ class AbcNurbsReader : public AbcObjectReader { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_READER_NURBS_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h index d8e0e57f2f9..0bde60b06b5 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.h +++ b/source/blender/io/alembic/intern/abc_reader_object.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_READER_OBJECT_H__ -#define __ABC_READER_OBJECT_H__ - #include #include @@ -173,5 +171,3 @@ Imath::M44d get_matrix(const Alembic::AbcGeom::IXformSchema &schema, const float } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_READER_OBJECT_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_points.h b/source/blender/io/alembic/intern/abc_reader_points.h index 91ddcf31de1..8a970ac35b3 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.h +++ b/source/blender/io/alembic/intern/abc_reader_points.h @@ -16,14 +16,12 @@ * The Original Code is Copyright (C) 2016 Kévin Dietrich. * All rights reserved. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_READER_POINTS_H__ -#define __ABC_READER_POINTS_H__ - #include "abc_customdata.h" #include "abc_reader_object.h" @@ -58,5 +56,3 @@ void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema, } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_READER_POINTS_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_transform.h b/source/blender/io/alembic/intern/abc_reader_transform.h index 57e1f9b2334..812d3bdfc92 100644 --- a/source/blender/io/alembic/intern/abc_reader_transform.h +++ b/source/blender/io/alembic/intern/abc_reader_transform.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_READER_TRANSFORM_H__ -#define __ABC_READER_TRANSFORM_H__ - #include "abc_reader_object.h" #include @@ -46,5 +44,3 @@ class AbcEmptyReader : public AbcObjectReader { } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_READER_TRANSFORM_H__ */ diff --git a/source/blender/io/alembic/intern/abc_util.h b/source/blender/io/alembic/intern/abc_util.h index fbad1cce306..5353d7a422b 100644 --- a/source/blender/io/alembic/intern/abc_util.h +++ b/source/blender/io/alembic/intern/abc_util.h @@ -13,14 +13,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once /** \file * \ingroup balembic */ -#ifndef __ABC_UTIL_H__ -#define __ABC_UTIL_H__ - #include #include @@ -172,5 +170,3 @@ std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger); } // namespace alembic } // namespace io } // namespace blender - -#endif /* __ABC_UTIL_H__ */ -- cgit v1.2.3 From 59b523c3c98ece3c186354c3ae5300ae0a7c6b79 Mon Sep 17 00:00:00 2001 From: Stefano Bonicatti Date: Fri, 19 Jun 2020 15:19:41 +0200 Subject: Fix build with USD, Clang and Linux The important distinction for the linker flags is not between GCC and Clang, but between the macOS and Linux. Differential Revision: https://developer.blender.org/D8023 --- source/blender/io/usd/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt index fc4f45cdef8..19e16a5b328 100644 --- a/source/blender/io/usd/CMakeLists.txt +++ b/source/blender/io/usd/CMakeLists.txt @@ -100,10 +100,10 @@ endif() # Source: https://github.com/PixarAnimationStudios/USD/blob/master/BUILDING.md#linking-whole-archives if(WIN32) target_link_libraries(bf_usd INTERFACE ${USD_LIBRARIES}) -elseif(CMAKE_COMPILER_IS_GNUCXX) - target_link_libraries(bf_usd INTERFACE "-Wl,--whole-archive ${USD_LIBRARIES} -Wl,--no-whole-archive ${TBB_LIBRARIES}") -elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") +elseif(APPLE) target_link_libraries(bf_usd INTERFACE -Wl,-force_load ${USD_LIBRARIES}) +elseif(UNIX) + target_link_libraries(bf_usd INTERFACE "-Wl,--whole-archive ${USD_LIBRARIES} -Wl,--no-whole-archive ${TBB_LIBRARIES}") else() message(FATAL_ERROR "Unknown how to link USD with your compiler ${CMAKE_CXX_COMPILER_ID}") endif() -- cgit v1.2.3 From 3ceb9faf1a628dea203f41e49d99503c2f8df65b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 19 Jun 2020 15:42:53 +0200 Subject: Cleanup: IO, made some functions in `AbstractHierarchyIterator` protected These functions are not needed in the public interface, only by the `AbstractHierarchyIterator` class and its subclasses. No functional changes. --- source/blender/io/common/IO_abstract_hierarchy_iterator.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h index 8cac3f4c72d..2f0c1b270f1 100644 --- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -239,7 +239,6 @@ class AbstractHierarchyIterator { Object *duplicator, const std::set &dupli_set); - ExportChildren &graph_children(const HierarchyContext *parent_context); void context_update_for_graph_index(HierarchyContext *context, const ExportGraph::key_type &graph_index) const; @@ -260,8 +259,6 @@ class AbstractHierarchyIterator { std::string get_object_name(const Object *object) const; std::string get_object_data_name(const Object *object) const; - AbstractHierarchyWriter *get_writer(const std::string &export_path) const; - typedef AbstractHierarchyWriter *(AbstractHierarchyIterator::*create_writer_func)( const HierarchyContext *); /* Ensure that a writer exists; if it doesn't, call create_func(context). @@ -313,6 +310,9 @@ class AbstractHierarchyIterator { /* Called by release_writers() to free what the create_XXX_writer() functions allocated. */ virtual void delete_object_writer(AbstractHierarchyWriter *writer) = 0; + + AbstractHierarchyWriter *get_writer(const std::string &export_path) const; + ExportChildren &graph_children(const HierarchyContext *parent_context); }; } // namespace io -- cgit v1.2.3 From b50d01cbac5a3c1718b5e72f95c48cf65ba0ac2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 19 Jun 2020 16:08:44 +0200 Subject: Cleanup: Alembic, removed some unnecessary `extern "C" {}` declarations No functional changes. --- source/blender/io/alembic/exporter/abc_export_capi.cc | 2 -- source/blender/io/alembic/intern/abc_axis_conversion.cc | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc index e330a2e4fdb..c0d04b50acd 100644 --- a/source/blender/io/alembic/exporter/abc_export_capi.cc +++ b/source/blender/io/alembic/exporter/abc_export_capi.cc @@ -28,7 +28,6 @@ #include "MEM_guardedalloc.h" -extern "C" { #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -47,7 +46,6 @@ extern "C" { #include "WM_api.h" #include "WM_types.h" -} using namespace blender::io::alembic; diff --git a/source/blender/io/alembic/intern/abc_axis_conversion.cc b/source/blender/io/alembic/intern/abc_axis_conversion.cc index a74432a3540..cebab1f2e41 100644 --- a/source/blender/io/alembic/intern/abc_axis_conversion.cc +++ b/source/blender/io/alembic/intern/abc_axis_conversion.cc @@ -20,12 +20,10 @@ #include "abc_axis_conversion.h" -extern "C" { #include "BLI_assert.h" -#include "DNA_object_types.h" - #include "BLI_math_geom.h" -} + +#include "DNA_object_types.h" namespace blender { namespace io { -- cgit v1.2.3 From 71363dd79c49e0ebd656d5eb61ae5ccb62ece499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 19 Jun 2020 17:05:11 +0200 Subject: Cleanup: Alembic, replace `ABC_INLINE` with `BLI_INLINE` The `ABC_INLINE` macro has been in the Alembic code since it was introduced to Blender in rB61050f75b13e. It basically does the same a `BLI_INLINE`, though, so there is no need to keep it around. --- source/blender/io/alembic/intern/abc_axis_conversion.h | 16 ++++++---------- source/blender/io/alembic/intern/abc_reader_mesh.cc | 5 +++-- source/blender/io/alembic/intern/abc_util.h | 6 ------ source/blender/io/alembic/intern/alembic_capi.cc | 5 +++-- 4 files changed, 12 insertions(+), 20 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/intern/abc_axis_conversion.h b/source/blender/io/alembic/intern/abc_axis_conversion.h index 734c5ca01c9..9a19e9116be 100644 --- a/source/blender/io/alembic/intern/abc_axis_conversion.h +++ b/source/blender/io/alembic/intern/abc_axis_conversion.h @@ -22,13 +22,9 @@ * \ingroup Alembic */ -struct Object; +#include "BLI_compiler_compat.h" -#ifdef _MSC_VER -# define ABC_INLINE static __forceinline -#else -# define ABC_INLINE static inline -#endif +struct Object; namespace blender { namespace io { @@ -41,7 +37,7 @@ namespace alembic { /* Copy from Y-up to Z-up. */ -ABC_INLINE void copy_zup_from_yup(float zup[3], const float yup[3]) +BLI_INLINE void copy_zup_from_yup(float zup[3], const float yup[3]) { const float old_yup1 = yup[1]; /* in case zup == yup */ zup[0] = yup[0]; @@ -49,7 +45,7 @@ ABC_INLINE void copy_zup_from_yup(float zup[3], const float yup[3]) zup[2] = old_yup1; } -ABC_INLINE void copy_zup_from_yup(short zup[3], const short yup[3]) +BLI_INLINE void copy_zup_from_yup(short zup[3], const short yup[3]) { const short old_yup1 = yup[1]; /* in case zup == yup */ zup[0] = yup[0]; @@ -59,7 +55,7 @@ ABC_INLINE void copy_zup_from_yup(short zup[3], const short yup[3]) /* Copy from Z-up to Y-up. */ -ABC_INLINE void copy_yup_from_zup(float yup[3], const float zup[3]) +BLI_INLINE void copy_yup_from_zup(float yup[3], const float zup[3]) { const float old_zup1 = zup[1]; /* in case yup == zup */ yup[0] = zup[0]; @@ -67,7 +63,7 @@ ABC_INLINE void copy_yup_from_zup(float yup[3], const float zup[3]) yup[2] = -old_zup1; } -ABC_INLINE void copy_yup_from_zup(short yup[3], const short zup[3]) +BLI_INLINE void copy_yup_from_zup(short yup[3], const short zup[3]) { const short old_zup1 = zup[1]; /* in case yup == zup */ yup[0] = zup[0]; diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index a37ac84bdfb..756dde3783c 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -32,6 +32,7 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "BLI_compiler_compat.h" #include "BLI_math_geom.h" #include "BKE_main.h" @@ -343,7 +344,7 @@ static void process_normals(CDStreamConfig &config, } } -ABC_INLINE void read_uvs_params(CDStreamConfig &config, +BLI_INLINE void read_uvs_params(CDStreamConfig &config, AbcMeshData &abc_data, const IV2fGeomParam &uv, const ISampleSelector &selector) @@ -718,7 +719,7 @@ void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const ISampleSel /* ************************************************************************** */ -ABC_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2) +BLI_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2) { for (int i = 0, e = totedge; i < e; i++) { MEdge &edge = edges[i]; diff --git a/source/blender/io/alembic/intern/abc_util.h b/source/blender/io/alembic/intern/abc_util.h index 5353d7a422b..4689173ab5f 100644 --- a/source/blender/io/alembic/intern/abc_util.h +++ b/source/blender/io/alembic/intern/abc_util.h @@ -22,12 +22,6 @@ #include #include -#ifdef _MSC_VER -# define ABC_INLINE static __forceinline -#else -# define ABC_INLINE static inline -#endif - /** * \brief The CacheReader struct is only used for anonymous pointers, * to interface between C and C++ code. This library only creates diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index 40dd690301e..0b4b184359b 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -57,6 +57,7 @@ #include "BKE_screen.h" #undef new +#include "BLI_compiler_compat.h" #include "BLI_fileops.h" #include "BLI_ghash.h" #include "BLI_listbase.h" @@ -105,12 +106,12 @@ struct AbcArchiveHandle { int unused; }; -ABC_INLINE ArchiveReader *archive_from_handle(AbcArchiveHandle *handle) +BLI_INLINE ArchiveReader *archive_from_handle(AbcArchiveHandle *handle) { return reinterpret_cast(handle); } -ABC_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive) +BLI_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive) { return reinterpret_cast(archive); } -- cgit v1.2.3 From 25d2222c302acd7278b49f30a74adba6b9457012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 22 Jun 2020 17:53:14 +0200 Subject: Fix T77754: Crash after any alembic import undo in an empty scene Thanks @mont29 for this patch. This creates an explicit undo step after the Alembic importer has finished running. This is necessary when the importer runs as a background job. --- source/blender/io/alembic/intern/alembic_capi.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index 0b4b184359b..bca84b97749 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -51,6 +51,8 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" +#include "ED_undo.h" + /* SpaceType struct has a member called 'new' which obviously conflicts with C++ * so temporarily redefining the new keyword to make it compile. */ #define new extern_new @@ -429,6 +431,7 @@ enum { }; struct ImportJobData { + bContext *C; Main *bmain; Scene *scene; ViewLayer *view_layer; @@ -447,6 +450,7 @@ struct ImportJobData { char error_code; bool was_cancelled; bool import_ok; + bool is_background_job; }; static void import_startjob(void *user_data, short *stop, short *do_update, float *progress) @@ -630,6 +634,12 @@ static void import_endjob(void *user_data) DEG_id_tag_update(&data->scene->id, ID_RECALC_BASE_FLAGS); DEG_relations_tag_update(data->bmain); + + if (data->is_background_job) { + /* Blender already returned from the import operator, so we need to store our own extra undo + * step. */ + ED_undo_push(data->C, "Alembic Import Finished"); + } } for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { @@ -675,6 +685,7 @@ bool ABC_import(bContext *C, { /* Using new here since MEM_* functions do not call constructor to properly initialize data. */ ImportJobData *job = new ImportJobData(); + job->C = C; job->bmain = CTX_data_main(C); job->scene = CTX_data_scene(C); job->view_layer = CTX_data_view_layer(C); @@ -691,6 +702,7 @@ bool ABC_import(bContext *C, job->error_code = ABC_NO_ERROR; job->was_cancelled = false; job->archive = NULL; + job->is_background_job = as_background_job; G.is_break = false; -- cgit v1.2.3 From 374e412813ca1752fc5e4b5bb2915d69cf872cf4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 23 Jun 2020 14:58:38 +1000 Subject: Correct recent 'name' -> 'filepath' rename (missed Collada) --- source/blender/io/collada/ImageExporter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/collada/ImageExporter.cpp b/source/blender/io/collada/ImageExporter.cpp index 1c897e37a4a..51d753db03f 100644 --- a/source/blender/io/collada/ImageExporter.cpp +++ b/source/blender/io/collada/ImageExporter.cpp @@ -55,7 +55,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies) ImBuf *imbuf = BKE_image_acquire_ibuf(image, NULL, NULL); if (!imbuf) { - fprintf(stderr, "Collada export: image does not exist:\n%s\n", image->name); + fprintf(stderr, "Collada export: image does not exist:\n%s\n", image->filepath); return; } @@ -104,7 +104,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies) else { /* make absolute source path */ - BLI_strncpy(source_path, image->name, sizeof(source_path)); + BLI_strncpy(source_path, image->filepath, sizeof(source_path)); BLI_path_abs(source_path, ID_BLEND_PATH_FROM_GLOBAL(&image->id)); BLI_path_normalize(NULL, source_path); -- cgit v1.2.3 From 13a5b954c350518cf21c9beb97e2e160f7372a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 23 Jun 2020 11:07:44 +0200 Subject: Cleanup: USD, move code from `USD` to `blender::io::usd` namespace No functional changes. --- source/blender/io/usd/intern/usd_capi.cc | 22 +++++++++++++++------- .../blender/io/usd/intern/usd_exporter_context.h | 8 ++++++-- .../io/usd/intern/usd_hierarchy_iterator.cc | 8 ++++++-- .../blender/io/usd/intern/usd_hierarchy_iterator.h | 8 ++++++-- .../blender/io/usd/intern/usd_writer_abstract.cc | 8 ++++++-- source/blender/io/usd/intern/usd_writer_abstract.h | 8 ++++++-- source/blender/io/usd/intern/usd_writer_camera.cc | 8 ++++++-- source/blender/io/usd/intern/usd_writer_camera.h | 8 ++++++-- source/blender/io/usd/intern/usd_writer_hair.cc | 8 ++++++-- source/blender/io/usd/intern/usd_writer_hair.h | 8 ++++++-- source/blender/io/usd/intern/usd_writer_light.cc | 8 ++++++-- source/blender/io/usd/intern/usd_writer_light.h | 8 ++++++-- source/blender/io/usd/intern/usd_writer_mesh.cc | 8 ++++++-- source/blender/io/usd/intern/usd_writer_mesh.h | 8 ++++++-- .../blender/io/usd/intern/usd_writer_metaball.cc | 8 ++++++-- source/blender/io/usd/intern/usd_writer_metaball.h | 8 ++++++-- .../blender/io/usd/intern/usd_writer_transform.cc | 8 ++++++-- .../blender/io/usd/intern/usd_writer_transform.h | 8 ++++++-- 18 files changed, 117 insertions(+), 41 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/usd/intern/usd_capi.cc b/source/blender/io/usd/intern/usd_capi.cc index cf962446d04..2d3972410a4 100644 --- a/source/blender/io/usd/intern/usd_capi.cc +++ b/source/blender/io/usd/intern/usd_capi.cc @@ -44,7 +44,9 @@ #include "WM_api.h" #include "WM_types.h" -namespace USD { +namespace blender { +namespace io { +namespace usd { struct ExportJobData { Main *bmain; @@ -157,7 +159,9 @@ static void export_endjob(void *customdata) WM_set_locked_interface(data->wm, false); } -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender bool USD_export(bContext *C, const char *filepath, @@ -167,8 +171,8 @@ bool USD_export(bContext *C, ViewLayer *view_layer = CTX_data_view_layer(C); Scene *scene = CTX_data_scene(C); - USD::ExportJobData *job = static_cast( - MEM_mallocN(sizeof(USD::ExportJobData), "ExportJobData")); + blender::io::usd::ExportJobData *job = static_cast( + MEM_mallocN(sizeof(blender::io::usd::ExportJobData), "ExportJobData")); job->bmain = CTX_data_main(C); job->wm = CTX_wm_manager(C); @@ -186,7 +190,11 @@ bool USD_export(bContext *C, /* setup job */ WM_jobs_customdata_set(wm_job, job, MEM_freeN); WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); - WM_jobs_callbacks(wm_job, USD::export_startjob, nullptr, nullptr, USD::export_endjob); + WM_jobs_callbacks(wm_job, + blender::io::usd::export_startjob, + nullptr, + nullptr, + blender::io::usd::export_endjob); WM_jobs_start(CTX_wm_manager(C), wm_job); } @@ -195,8 +203,8 @@ bool USD_export(bContext *C, short stop = 0, do_update = 0; float progress = 0.f; - USD::export_startjob(job, &stop, &do_update, &progress); - USD::export_endjob(job); + blender::io::usd::export_startjob(job, &stop, &do_update, &progress); + blender::io::usd::export_endjob(job); export_ok = job->export_ok; MEM_freeN(job); diff --git a/source/blender/io/usd/intern/usd_exporter_context.h b/source/blender/io/usd/intern/usd_exporter_context.h index 4ae415b3d34..07a9d0fc0c5 100644 --- a/source/blender/io/usd/intern/usd_exporter_context.h +++ b/source/blender/io/usd/intern/usd_exporter_context.h @@ -27,7 +27,9 @@ struct Depsgraph; struct Object; -namespace USD { +namespace blender { +namespace io { +namespace usd { class USDHierarchyIterator; @@ -39,6 +41,8 @@ struct USDExporterContext { const USDExportParams &export_params; }; -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender #endif /* __USD_EXPORTER_CONTEXT_H__ */ diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc index 56e367dd877..388b588b331 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc @@ -41,7 +41,9 @@ #include "DNA_layer_types.h" #include "DNA_object_types.h" -namespace USD { +namespace blender { +namespace io { +namespace usd { USDHierarchyIterator::USDHierarchyIterator(Depsgraph *depsgraph, pxr::UsdStageRefPtr stage, @@ -145,4 +147,6 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_particle_writer(const Hier return nullptr; } -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.h b/source/blender/io/usd/intern/usd_hierarchy_iterator.h index c61a0d2bf07..7d750bff0cb 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.h +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.h @@ -32,7 +32,9 @@ struct Depsgraph; struct ID; struct Object; -namespace USD { +namespace blender { +namespace io { +namespace usd { using blender::io::AbstractHierarchyIterator; using blender::io::AbstractHierarchyWriter; @@ -70,6 +72,8 @@ class USDHierarchyIterator : public AbstractHierarchyIterator { USDExporterContext create_usd_export_context(const HierarchyContext *context); }; -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender #endif /* __USD_HIERARCHY_ITERATOR_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc index 76a2436ee92..a416941fb4d 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.cc +++ b/source/blender/io/usd/intern/usd_writer_abstract.cc @@ -32,7 +32,9 @@ static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal); static const pxr::TfToken surface("surface", pxr::TfToken::Immortal); } // namespace usdtokens -namespace USD { +namespace blender { +namespace io { +namespace usd { USDAbstractWriter::USDAbstractWriter(const USDExporterContext &usd_export_context) : usd_export_context_(usd_export_context), @@ -112,4 +114,6 @@ pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(Material *material) return usd_material; } -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h index 03c4c263e32..f81cf5197af 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.h +++ b/source/blender/io/usd/intern/usd_writer_abstract.h @@ -36,7 +36,9 @@ struct Material; struct Object; -namespace USD { +namespace blender { +namespace io { +namespace usd { using blender::io::AbstractHierarchyWriter; using blender::io::HierarchyContext; @@ -73,6 +75,8 @@ class USDAbstractWriter : public AbstractHierarchyWriter { pxr::UsdShadeMaterial ensure_usd_material(Material *material); }; -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender #endif /* __USD_WRITER_ABSTRACT_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_camera.cc b/source/blender/io/usd/intern/usd_writer_camera.cc index ea551a43c9f..d51eb32d3fd 100644 --- a/source/blender/io/usd/intern/usd_writer_camera.cc +++ b/source/blender/io/usd/intern/usd_writer_camera.cc @@ -28,7 +28,9 @@ #include "DNA_camera_types.h" #include "DNA_scene_types.h" -namespace USD { +namespace blender { +namespace io { +namespace usd { USDCameraWriter::USDCameraWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) { @@ -106,4 +108,6 @@ void USDCameraWriter::do_write(HierarchyContext &context) } } -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender diff --git a/source/blender/io/usd/intern/usd_writer_camera.h b/source/blender/io/usd/intern/usd_writer_camera.h index 971264ef11e..8b5795d7d9f 100644 --- a/source/blender/io/usd/intern/usd_writer_camera.h +++ b/source/blender/io/usd/intern/usd_writer_camera.h @@ -21,7 +21,9 @@ #include "usd_writer_abstract.h" -namespace USD { +namespace blender { +namespace io { +namespace usd { /* Writer for writing camera data to UsdGeomCamera. */ class USDCameraWriter : public USDAbstractWriter { @@ -33,6 +35,8 @@ class USDCameraWriter : public USDAbstractWriter { virtual void do_write(HierarchyContext &context) override; }; -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender #endif /* __USD_WRITER_CAMERA_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_hair.cc b/source/blender/io/usd/intern/usd_writer_hair.cc index d38c1032969..0e0256bdb69 100644 --- a/source/blender/io/usd/intern/usd_writer_hair.cc +++ b/source/blender/io/usd/intern/usd_writer_hair.cc @@ -26,7 +26,9 @@ #include "DNA_particle_types.h" -namespace USD { +namespace blender { +namespace io { +namespace usd { USDHairWriter::USDHairWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) { @@ -85,4 +87,6 @@ bool USDHairWriter::check_is_animated(const HierarchyContext &) const return true; } -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender diff --git a/source/blender/io/usd/intern/usd_writer_hair.h b/source/blender/io/usd/intern/usd_writer_hair.h index 1e882fa1654..cecacd0a355 100644 --- a/source/blender/io/usd/intern/usd_writer_hair.h +++ b/source/blender/io/usd/intern/usd_writer_hair.h @@ -21,7 +21,9 @@ #include "usd_writer_abstract.h" -namespace USD { +namespace blender { +namespace io { +namespace usd { /* Writer for writing hair particle data as USD curves. */ class USDHairWriter : public USDAbstractWriter { @@ -33,6 +35,8 @@ class USDHairWriter : public USDAbstractWriter { virtual bool check_is_animated(const HierarchyContext &context) const override; }; -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender #endif /* __USD_WRITER_HAIR_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_light.cc b/source/blender/io/usd/intern/usd_writer_light.cc index 0ce3ee5f8ce..19115dd1a4e 100644 --- a/source/blender/io/usd/intern/usd_writer_light.cc +++ b/source/blender/io/usd/intern/usd_writer_light.cc @@ -30,7 +30,9 @@ #include "DNA_light_types.h" #include "DNA_object_types.h" -namespace USD { +namespace blender { +namespace io { +namespace usd { USDLightWriter::USDLightWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) { @@ -107,4 +109,6 @@ void USDLightWriter::do_write(HierarchyContext &context) usd_light.CreateSpecularAttr().Set(light->spec_fac, timecode); } -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender diff --git a/source/blender/io/usd/intern/usd_writer_light.h b/source/blender/io/usd/intern/usd_writer_light.h index 349c034b6bc..73666622af1 100644 --- a/source/blender/io/usd/intern/usd_writer_light.h +++ b/source/blender/io/usd/intern/usd_writer_light.h @@ -21,7 +21,9 @@ #include "usd_writer_abstract.h" -namespace USD { +namespace blender { +namespace io { +namespace usd { class USDLightWriter : public USDAbstractWriter { public: @@ -32,6 +34,8 @@ class USDLightWriter : public USDAbstractWriter { virtual void do_write(HierarchyContext &context) override; }; -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender #endif /* __USD_WRITER_LIGHT_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc index 61337beff20..29a9734f876 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.cc +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -42,7 +42,9 @@ #include "DNA_object_fluidsim_types.h" #include "DNA_particle_types.h" -namespace USD { +namespace blender { +namespace io { +namespace usd { USDGenericMeshWriter::USDGenericMeshWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) { @@ -484,4 +486,6 @@ Mesh *USDMeshWriter::get_export_mesh(Object *object_eval, bool & /*r_needsfree*/ return BKE_object_get_evaluated_mesh(object_eval); } -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender diff --git a/source/blender/io/usd/intern/usd_writer_mesh.h b/source/blender/io/usd/intern/usd_writer_mesh.h index 4175e2b7e27..a14ceecfa53 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.h +++ b/source/blender/io/usd/intern/usd_writer_mesh.h @@ -23,7 +23,9 @@ #include -namespace USD { +namespace blender { +namespace io { +namespace usd { struct USDMeshData; @@ -61,6 +63,8 @@ class USDMeshWriter : public USDGenericMeshWriter { virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override; }; -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender #endif /* __USD_WRITER_MESH_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_metaball.cc b/source/blender/io/usd/intern/usd_writer_metaball.cc index 96bf854d327..f003fba18a4 100644 --- a/source/blender/io/usd/intern/usd_writer_metaball.cc +++ b/source/blender/io/usd/intern/usd_writer_metaball.cc @@ -34,7 +34,9 @@ #include "DNA_mesh_types.h" #include "DNA_meta_types.h" -namespace USD { +namespace blender { +namespace io { +namespace usd { USDMetaballWriter::USDMetaballWriter(const USDExporterContext &ctx) : USDGenericMeshWriter(ctx) { @@ -76,4 +78,6 @@ bool USDMetaballWriter::is_basis_ball(Scene *scene, Object *ob) const return ob == basis_ob; } -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender diff --git a/source/blender/io/usd/intern/usd_writer_metaball.h b/source/blender/io/usd/intern/usd_writer_metaball.h index 1a86daae2ae..9f51a3314a5 100644 --- a/source/blender/io/usd/intern/usd_writer_metaball.h +++ b/source/blender/io/usd/intern/usd_writer_metaball.h @@ -21,7 +21,9 @@ #include "usd_writer_mesh.h" -namespace USD { +namespace blender { +namespace io { +namespace usd { class USDMetaballWriter : public USDGenericMeshWriter { public: @@ -37,6 +39,8 @@ class USDMetaballWriter : public USDGenericMeshWriter { bool is_basis_ball(Scene *scene, Object *ob) const; }; -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender #endif /* __USD_WRITER_METABALL_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_transform.cc b/source/blender/io/usd/intern/usd_writer_transform.cc index 0694d873002..643f1a8f4b1 100644 --- a/source/blender/io/usd/intern/usd_writer_transform.cc +++ b/source/blender/io/usd/intern/usd_writer_transform.cc @@ -28,7 +28,9 @@ #include "DNA_layer_types.h" -namespace USD { +namespace blender { +namespace io { +namespace usd { USDTransformWriter::USDTransformWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) { @@ -59,4 +61,6 @@ bool USDTransformWriter::check_is_animated(const HierarchyContext &context) cons return BKE_object_moves_in_time(context.object, context.animation_check_include_parent); } -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender diff --git a/source/blender/io/usd/intern/usd_writer_transform.h b/source/blender/io/usd/intern/usd_writer_transform.h index 52c4a657f33..8b4741f1177 100644 --- a/source/blender/io/usd/intern/usd_writer_transform.h +++ b/source/blender/io/usd/intern/usd_writer_transform.h @@ -23,7 +23,9 @@ #include -namespace USD { +namespace blender { +namespace io { +namespace usd { class USDTransformWriter : public USDAbstractWriter { private: @@ -37,6 +39,8 @@ class USDTransformWriter : public USDAbstractWriter { bool check_is_animated(const HierarchyContext &context) const override; }; -} // namespace USD +} // namespace usd +} // namespace io +} // namespace blender #endif /* __USD_WRITER_TRANSFORM_H__ */ -- cgit v1.2.3 From 4b96f4783197c6bbf34230385b711d685df2b545 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 27 Jun 2020 14:34:16 +1000 Subject: Docs: correct invalid doxygen params & references --- source/blender/io/alembic/exporter/abc_exporter.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/exporter/abc_exporter.cc b/source/blender/io/alembic/exporter/abc_exporter.cc index a3f13e31300..321bb5fcaba 100644 --- a/source/blender/io/alembic/exporter/abc_exporter.cc +++ b/source/blender/io/alembic/exporter/abc_exporter.cc @@ -142,9 +142,9 @@ static bool object_type_is_exportable(Scene *scene, Object *ob) * Returns whether this object should be exported into the Alembic file. * * \param settings: export settings, used for options like 'selected only'. - * \param ob: the object's base in question. + * \param base: the object's base in question. * \param is_duplicated: Normally false; true when the object is instanced - * into the scene by a dupli-object (e.g. part of a dupligroup). + * into the scene by a dupli-object (e.g. part of a dupli-group). * This ignores selection and layer visibility, * and assumes that the dupli-object itself (e.g. the group-instantiating empty) is exported. */ -- cgit v1.2.3 From 2917df21adc8a1ce0423349909db61d22a38d451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 19 Jun 2020 16:36:10 +0200 Subject: Alembic: new exporter based on the USD exporter structure The Alembic exporter has been restructured by leverages the `AbstractHierarchyIterator` introduced by the USD exporter. The produced Alembic files have not changed much (details below), as the Alembic writing code has simply been moved from the old exporter to the new. How the export hierarchy is handled changed a lot, though, and also the way in which transforms are computed. As a result, T71395 is fixed. Differences between the old and new exporter, in terms of the produced Alembic file: - Duplicated objects now have a unique numerical suffix. - Matrices are computed differently, namely by simply computing the evaluated transform of the object relative to the evaluated transform of its export-parent. This fixes {T71395}, but otherwise should produce the same result as before (but with simpler code). Compared to the old Alembic exporter, Subdivision modifiers are now disabled in a cleaner, more efficient way (they are disabled when exporting with the "Apply Subdivisions" option is unchecked). Previously the exporter would move to a new frame, disable the modifier, evaluate the object, and enable the modifier again. This is now done before exporting starts, and modifiers are only restored when exporting ends. Some issues with the old Alembic exporter that have NOT been fixed in this patch: - Exporting NURBS patches and curves (see T49114 for example). - Exporting flattened hierarchy in combination with dupli-objects. This seems to be broken in the old Alembic exporter as well, but nobody reported this yet. Differential Revision: https://developer.blender.org/D7664 Reviewed By: Sergey --- source/blender/io/alembic/CMakeLists.txt | 19 +- source/blender/io/alembic/exporter/abc_archive.cc | 255 ++++++++ source/blender/io/alembic/exporter/abc_archive.h | 87 +++ .../blender/io/alembic/exporter/abc_export_capi.cc | 229 ++++--- source/blender/io/alembic/exporter/abc_exporter.cc | 681 --------------------- source/blender/io/alembic/exporter/abc_exporter.h | 131 ---- .../io/alembic/exporter/abc_hierarchy_iterator.cc | 261 ++++++++ .../io/alembic/exporter/abc_hierarchy_iterator.h | 90 +++ .../io/alembic/exporter/abc_subdiv_disabler.cc | 107 ++++ .../io/alembic/exporter/abc_subdiv_disabler.h | 55 ++ .../io/alembic/exporter/abc_writer_abstract.cc | 101 +++ .../io/alembic/exporter/abc_writer_abstract.h | 77 +++ .../io/alembic/exporter/abc_writer_archive.cc | 106 ---- .../io/alembic/exporter/abc_writer_archive.h | 54 -- .../io/alembic/exporter/abc_writer_camera.cc | 89 ++- .../io/alembic/exporter/abc_writer_camera.h | 31 +- .../io/alembic/exporter/abc_writer_curves.cc | 79 +-- .../io/alembic/exporter/abc_writer_curves.h | 32 +- .../blender/io/alembic/exporter/abc_writer_hair.cc | 118 ++-- .../blender/io/alembic/exporter/abc_writer_hair.h | 39 +- .../io/alembic/exporter/abc_writer_mball.cc | 67 +- .../blender/io/alembic/exporter/abc_writer_mball.h | 31 +- .../blender/io/alembic/exporter/abc_writer_mesh.cc | 578 +++++++++-------- .../blender/io/alembic/exporter/abc_writer_mesh.h | 84 +-- .../io/alembic/exporter/abc_writer_nurbs.cc | 92 +-- .../blender/io/alembic/exporter/abc_writer_nurbs.h | 33 +- .../io/alembic/exporter/abc_writer_object.cc | 95 --- .../io/alembic/exporter/abc_writer_object.h | 73 --- .../io/alembic/exporter/abc_writer_points.cc | 100 +-- .../io/alembic/exporter/abc_writer_points.h | 29 +- .../io/alembic/exporter/abc_writer_transform.cc | 119 ++-- .../io/alembic/exporter/abc_writer_transform.h | 41 +- source/blender/io/alembic/intern/alembic_capi.cc | 19 +- .../io/common/IO_abstract_hierarchy_iterator.h | 4 +- 34 files changed, 1930 insertions(+), 2076 deletions(-) create mode 100644 source/blender/io/alembic/exporter/abc_archive.cc create mode 100644 source/blender/io/alembic/exporter/abc_archive.h delete mode 100644 source/blender/io/alembic/exporter/abc_exporter.cc delete mode 100644 source/blender/io/alembic/exporter/abc_exporter.h create mode 100644 source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc create mode 100644 source/blender/io/alembic/exporter/abc_hierarchy_iterator.h create mode 100644 source/blender/io/alembic/exporter/abc_subdiv_disabler.cc create mode 100644 source/blender/io/alembic/exporter/abc_subdiv_disabler.h create mode 100644 source/blender/io/alembic/exporter/abc_writer_abstract.cc create mode 100644 source/blender/io/alembic/exporter/abc_writer_abstract.h delete mode 100644 source/blender/io/alembic/exporter/abc_writer_archive.cc delete mode 100644 source/blender/io/alembic/exporter/abc_writer_archive.h delete mode 100644 source/blender/io/alembic/exporter/abc_writer_object.cc delete mode 100644 source/blender/io/alembic/exporter/abc_writer_object.h (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt index c0c91099ad2..da36272b850 100644 --- a/source/blender/io/alembic/CMakeLists.txt +++ b/source/blender/io/alembic/CMakeLists.txt @@ -30,6 +30,7 @@ set(INC ../../makesdna ../../makesrna ../../windowmanager + ../../../../intern/clog ../../../../intern/guardedalloc ../../../../intern/utfconv ) @@ -54,16 +55,17 @@ set(SRC intern/abc_util.cc intern/alembic_capi.cc + exporter/abc_archive.cc exporter/abc_export_capi.cc - exporter/abc_exporter.cc - exporter/abc_writer_archive.cc + exporter/abc_hierarchy_iterator.cc + exporter/abc_subdiv_disabler.cc + exporter/abc_writer_abstract.cc exporter/abc_writer_camera.cc exporter/abc_writer_curves.cc exporter/abc_writer_hair.cc - exporter/abc_writer_mball.cc exporter/abc_writer_mesh.cc + exporter/abc_writer_mball.cc exporter/abc_writer_nurbs.cc - exporter/abc_writer_object.cc exporter/abc_writer_points.cc exporter/abc_writer_transform.cc @@ -80,15 +82,16 @@ set(SRC intern/abc_reader_transform.h intern/abc_util.h - exporter/abc_exporter.h - exporter/abc_writer_archive.h + exporter/abc_archive.h + exporter/abc_hierarchy_iterator.h + exporter/abc_subdiv_disabler.h + exporter/abc_writer_abstract.h exporter/abc_writer_camera.h exporter/abc_writer_curves.h exporter/abc_writer_hair.h - exporter/abc_writer_mball.h exporter/abc_writer_mesh.h + exporter/abc_writer_mball.h exporter/abc_writer_nurbs.h - exporter/abc_writer_object.h exporter/abc_writer_points.h exporter/abc_writer_transform.h ) diff --git a/source/blender/io/alembic/exporter/abc_archive.cc b/source/blender/io/alembic/exporter/abc_archive.cc new file mode 100644 index 00000000000..dbf046e6dfe --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_archive.cc @@ -0,0 +1,255 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +#include "abc_archive.h" + +#include "BKE_blender_version.h" +#include "BKE_main.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph_query.h" + +#include "DNA_scene_types.h" + +#include +#include + +namespace blender { +namespace io { +namespace alembic { + +using Alembic::Abc::ErrorHandler; +using Alembic::Abc::kWrapExisting; +using Alembic::Abc::MetaData; +using Alembic::Abc::OArchive; +using Alembic::Abc::TimeSampling; +using Alembic::Abc::TimeSamplingPtr; +using Alembic::Abc::TimeSamplingType; + +static MetaData create_abc_metadata(const Main *bmain, double scene_fps) +{ + MetaData abc_metadata; + + std::string abc_user_description(bmain->name); + if (abc_user_description.empty()) { + abc_user_description = "unknown"; + } + + abc_metadata.set(Alembic::Abc::kApplicationNameKey, "Blender"); + abc_metadata.set(Alembic::Abc::kUserDescriptionKey, abc_user_description); + abc_metadata.set("blender_version", std::string("v") + BKE_blender_version_string()); + abc_metadata.set("FramesPerTimeUnit", std::to_string(scene_fps)); + + time_t raw_time; + time(&raw_time); + char buffer[128]; + +#if defined _WIN32 || defined _WIN64 + ctime_s(buffer, 128, &raw_time); +#else + ctime_r(&raw_time, buffer); +#endif + + const std::size_t buffer_len = strlen(buffer); + if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') { + buffer[buffer_len - 1] = '\0'; + } + + abc_metadata.set(Alembic::Abc::kDateWrittenKey, buffer); + return abc_metadata; +} + +static OArchive *create_archive(std::ofstream *abc_ostream, + const std::string &filename, + MetaData &abc_metadata) +{ + /* Use stream to support unicode character paths on Windows. */ +#ifdef WIN32 + UTF16_ENCODE(filename); + std::wstring wstr(filename_16); + abc_ostream->open(wstr.c_str(), std::ios::out | std::ios::binary); + UTF16_UN_ENCODE(filename); +#else + abc_ostream->open(filename, std::ios::out | std::ios::binary); +#endif + + ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy; + + Alembic::AbcCoreOgawa::WriteArchive archive_writer; + return new OArchive(archive_writer(abc_ostream, abc_metadata), kWrapExisting, policy); +} + +/* Construct list of shutter samples. + * + * These are taken from the interval [shutter open, shutter close), + * uniformly sampled with 'nr_of_samples' samples. + * + * TODO(Sybren): test that the above interval is indeed half-open. + * + * If 'time_relative' is true, samples are returned as time (in seconds) from params.frame_start. + * If 'time_relative' is false, samples are returned as fractional frames from 0. + * */ +static void get_shutter_samples(double scene_fps, + const AlembicExportParams ¶ms, + int nr_of_samples, + bool time_relative, + std::vector &r_samples) +{ + int frame_offset = time_relative ? params.frame_start : 0; + double time_factor = time_relative ? scene_fps : 1.0; + double shutter_open = params.shutter_open; + double shutter_close = params.shutter_close; + double time_inc = (shutter_close - shutter_open) / nr_of_samples; + + /* sample between shutter open & close */ + for (int sample = 0; sample < nr_of_samples; sample++) { + double sample_time = shutter_open + time_inc * sample; + double time = (frame_offset + sample_time) / time_factor; + + r_samples.push_back(time); + } +} + +static TimeSamplingPtr create_time_sampling(double scene_fps, + const AlembicExportParams ¶ms, + int nr_of_samples) +{ + std::vector samples; + + if (params.frame_start == params.frame_end) { + return TimeSamplingPtr(new TimeSampling()); + } + + get_shutter_samples(scene_fps, params, nr_of_samples, true, samples); + + TimeSamplingType ts(static_cast(samples.size()), 1.0 / scene_fps); + return TimeSamplingPtr(new TimeSampling(ts, samples)); +} + +static void get_frames(double scene_fps, + const AlembicExportParams ¶ms, + unsigned int nr_of_samples, + std::set &r_frames) +{ + /* Get one set of shutter samples, then add those around each frame to export. */ + std::vector shutter_samples; + get_shutter_samples(scene_fps, params, nr_of_samples, false, shutter_samples); + + for (double frame = params.frame_start; frame <= params.frame_end; frame += 1.0) { + for (size_t j = 0; j < nr_of_samples; j++) { + r_frames.insert(frame + shutter_samples[j]); + } + } +} + +/* ****************************************************************** */ + +ABCArchive::ABCArchive(const Main *bmain, + const Scene *scene, + AlembicExportParams params, + std::string filename) + : archive(nullptr) +{ + double scene_fps = FPS; + MetaData abc_metadata = create_abc_metadata(bmain, scene_fps); + + // Create the Archive. + archive = create_archive(&abc_ostream_, filename, abc_metadata); + + // Create time samples for transforms and shapes. + TimeSamplingPtr ts_xform; + TimeSamplingPtr ts_shapes; + + ts_xform = create_time_sampling(scene_fps, params, params.frame_samples_xform); + time_sampling_index_transforms_ = archive->addTimeSampling(*ts_xform); + + const bool export_animation = params.frame_start != params.frame_end; + if (!export_animation || params.frame_samples_shape == params.frame_samples_xform) { + ts_shapes = ts_xform; + time_sampling_index_shapes_ = time_sampling_index_transforms_; + } + else { + ts_shapes = create_time_sampling(scene_fps, params, params.frame_samples_shape); + time_sampling_index_shapes_ = archive->addTimeSampling(*ts_shapes); + } + + // Construct the frames to export. + get_frames(scene_fps, params, params.frame_samples_xform, xform_frames_); + get_frames(scene_fps, params, params.frame_samples_shape, shape_frames_); + + // Merge all frames to get the final set of frames to export. + export_frames_.insert(xform_frames_.begin(), xform_frames_.end()); + export_frames_.insert(shape_frames_.begin(), shape_frames_.end()); + + abc_archive_bbox_ = Alembic::AbcGeom::CreateOArchiveBounds(*archive, + time_sampling_index_transforms_); +} + +ABCArchive::~ABCArchive() +{ + delete archive; +} + +uint32_t ABCArchive::time_sampling_index_transforms() const +{ + return time_sampling_index_transforms_; +} + +uint32_t ABCArchive::time_sampling_index_shapes() const +{ + return time_sampling_index_shapes_; +} + +ABCArchive::Frames::const_iterator ABCArchive::frames_begin() const +{ + return export_frames_.begin(); +} +ABCArchive::Frames::const_iterator ABCArchive::frames_end() const +{ + return export_frames_.end(); +} +size_t ABCArchive::total_frame_count() const +{ + return export_frames_.size(); +} + +bool ABCArchive::is_xform_frame(double frame) const +{ + return xform_frames_.find(frame) != xform_frames_.end(); +} +bool ABCArchive::is_shape_frame(double frame) const +{ + return shape_frames_.find(frame) != shape_frames_.end(); +} +ExportSubset ABCArchive::export_subset_for_frame(double frame) const +{ + ExportSubset subset; + subset.transforms = is_xform_frame(frame); + subset.shapes = is_shape_frame(frame); + return subset; +} + +void ABCArchive::update_bounding_box(const Imath::Box3d &bounds) +{ + abc_archive_bbox_.set(bounds); +} + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_archive.h b/source/blender/io/alembic/exporter/abc_archive.h new file mode 100644 index 00000000000..43d0acf2520 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_archive.h @@ -0,0 +1,87 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup Alembic + */ + +#pragma once + +#include "ABC_alembic.h" +#include "IO_abstract_hierarchy_iterator.h" + +#include +#include + +#include +#include +#include + +struct Main; +struct Scene; + +namespace blender { +namespace io { +namespace alembic { + +/* Container for an Alembic archive and time sampling info. + * + * Constructor arguments are used to create the correct output stream and to set the archive's + * metadata. */ +class ABCArchive { + public: + typedef std::set Frames; + + Alembic::Abc::OArchive *archive; + + ABCArchive(const Main *bmain, + const Scene *scene, + AlembicExportParams params, + std::string filename); + ~ABCArchive(); + + uint32_t time_sampling_index_transforms() const; + uint32_t time_sampling_index_shapes() const; + + Frames::const_iterator frames_begin() const; + Frames::const_iterator frames_end() const; + size_t total_frame_count() const; + + bool is_xform_frame(double frame) const; + bool is_shape_frame(double frame) const; + + ExportSubset export_subset_for_frame(double frame) const; + + void update_bounding_box(const Imath::Box3d &bounds); + + private: + std::ofstream abc_ostream_; + uint32_t time_sampling_index_transforms_; + uint32_t time_sampling_index_shapes_; + + Frames xform_frames_; + Frames shape_frames_; + Frames export_frames_; + + Alembic::Abc::OBox3dProperty abc_archive_bbox_; +}; + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc index c0d04b50acd..fbc5b2d5c02 100644 --- a/source/blender/io/alembic/exporter/abc_export_capi.cc +++ b/source/blender/io/alembic/exporter/abc_export_capi.cc @@ -18,18 +18,15 @@ */ #include "ABC_alembic.h" -#include "abc_writer_camera.h" -#include "abc_writer_curves.h" -#include "abc_writer_hair.h" -#include "abc_writer_mesh.h" -#include "abc_writer_nurbs.h" -#include "abc_writer_points.h" -#include "abc_writer_transform.h" +#include "abc_archive.h" +#include "abc_hierarchy_iterator.h" +#include "abc_subdiv_disabler.h" #include "MEM_guardedalloc.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "DNA_modifier_types.h" #include "DNA_scene_types.h" @@ -47,179 +44,163 @@ #include "WM_api.h" #include "WM_types.h" -using namespace blender::io::alembic; +#include "CLG_log.h" +static CLG_LogRef LOG = {"io.alembic"}; + +#include struct ExportJobData { - ViewLayer *view_layer; Main *bmain; + Depsgraph *depsgraph; wmWindowManager *wm; - char filename[1024]; - ExportSettings settings; - - short *stop; - short *do_update; - float *progress; + char filename[FILE_MAX]; + AlembicExportParams params; bool was_canceled; bool export_ok; }; +namespace blender { +namespace io { +namespace alembic { + +// Construct the depsgraph for exporting. +static void build_depsgraph(Depsgraph *depsgraph, Main *bmain) +{ + Scene *scene = DEG_get_input_scene(depsgraph); + ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); + DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer); +} + static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) { ExportJobData *data = static_cast(customdata); + data->was_canceled = false; - data->stop = stop; - data->do_update = do_update; - data->progress = progress; - - /* XXX annoying hack: needed to prevent data corruption when changing - * scene frame in separate threads - */ G.is_rendering = true; WM_set_locked_interface(data->wm, true); G.is_break = false; - DEG_graph_build_from_view_layer( - data->settings.depsgraph, data->bmain, data->settings.scene, data->view_layer); - BKE_scene_graph_update_tagged(data->settings.depsgraph, data->bmain); + *progress = 0.0f; + *do_update = true; - try { - AbcExporter exporter(data->bmain, data->filename, data->settings); + build_depsgraph(data->depsgraph, data->bmain); + SubdivModifierDisabler subdiv_disabler(data->depsgraph); + if (!data->params.apply_subdiv) { + subdiv_disabler.disable_modifiers(); + } + BKE_scene_graph_update_tagged(data->depsgraph, data->bmain); - Scene *scene = data->settings.scene; /* for the CFRA macro */ - const int orig_frame = CFRA; + // For restoring the current frame after exporting animation is done. + Scene *scene = DEG_get_input_scene(data->depsgraph); + const int orig_frame = CFRA; + const bool export_animation = (data->params.frame_start != data->params.frame_end); - data->was_canceled = false; - exporter(do_update, progress, &data->was_canceled); + // Create the Alembic archive. + ABCArchive abc_archive(data->bmain, scene, data->params, std::string(data->filename)); - if (CFRA != orig_frame) { - CFRA = orig_frame; + ABCHierarchyIterator iter(data->depsgraph, &abc_archive, data->params); - BKE_scene_graph_update_for_newframe(data->settings.depsgraph, data->bmain); - } + if (export_animation) { + CLOG_INFO(&LOG, 2, "Exporting animation"); + + // Writing the animated frames is not 100% of the work, but it's our best guess. + const float progress_per_frame = 1.0f / std::max(size_t(1), abc_archive.total_frame_count()); + ABCArchive::Frames::const_iterator frame_it = abc_archive.frames_begin(); + const ABCArchive::Frames::const_iterator frames_end = abc_archive.frames_end(); + + for (; frame_it != frames_end; frame_it++) { + double frame = *frame_it; + + if (G.is_break || (stop != nullptr && *stop)) { + break; + } + + // Update the scene for the next frame to render. + scene->r.cfra = static_cast(frame); + scene->r.subframe = frame - scene->r.cfra; + BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain); - data->export_ok = !data->was_canceled; + CLOG_INFO(&LOG, 2, "Exporting frame %.2f", frame); + ExportSubset export_subset = abc_archive.export_subset_for_frame(frame); + iter.set_export_subset(export_subset); + iter.iterate_and_write(); + + *progress += progress_per_frame; + *do_update = true; + } } - catch (const std::exception &e) { - ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n'; + else { + // If we're not animating, a single iteration over all objects is enough. + iter.iterate_and_write(); } - catch (...) { - ABC_LOG(data->settings.logger) << "Abc Export: unknown error...\n"; + + iter.release_writers(); + + // Finish up by going back to the keyframe that was current before we started. + if (CFRA != orig_frame) { + CFRA = orig_frame; + BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain); } + + data->export_ok = !data->was_canceled; + + *progress = 1.0f; + *do_update = true; } static void export_endjob(void *customdata) { ExportJobData *data = static_cast(customdata); - DEG_graph_free(data->settings.depsgraph); + DEG_graph_free(data->depsgraph); if (data->was_canceled && BLI_exists(data->filename)) { BLI_delete(data->filename, false, false); } - std::string log = data->settings.logger.str(); - if (!log.empty()) { - std::cerr << log; - WM_report(RPT_ERROR, "Errors occurred during the export, look in the console to know more..."); - } - G.is_rendering = false; WM_set_locked_interface(data->wm, false); } -bool ABC_export(struct Scene *scene, - struct bContext *C, +} // namespace alembic +} // namespace io +} // namespace blender + +bool ABC_export(Scene *scene, + bContext *C, const char *filepath, - const struct AlembicExportParams *params, + const AlembicExportParams *params, bool as_background_job) { + ViewLayer *view_layer = CTX_data_view_layer(C); + ExportJobData *job = static_cast( MEM_mallocN(sizeof(ExportJobData), "ExportJobData")); - job->view_layer = CTX_data_view_layer(C); job->bmain = CTX_data_main(C); job->wm = CTX_wm_manager(C); job->export_ok = false; - BLI_strncpy(job->filename, filepath, 1024); - - /* Alright, alright, alright.... - * - * ExportJobData contains an ExportSettings containing a SimpleLogger. - * - * Since ExportJobData is a C-style struct dynamically allocated with - * MEM_mallocN (see above), its constructor is never called, therefore the - * ExportSettings constructor is not called which implies that the - * SimpleLogger one is not called either. SimpleLogger in turn does not call - * the constructor of its data members which ultimately means that its - * std::ostringstream member has a NULL pointer. To be able to properly use - * the stream's operator<<, the pointer needs to be set, therefore we have - * to properly construct everything. And this is done using the placement - * new operator as here below. It seems hackish, but I'm too lazy to - * do bigger refactor and maybe there is a better way which does not involve - * hardcore refactoring. */ - new (&job->settings) ExportSettings(); - job->settings.scene = scene; - job->settings.depsgraph = DEG_graph_new(job->bmain, scene, job->view_layer, DAG_EVAL_RENDER); - - /* TODO(Sybren): for now we only export the active scene layer. - * Later in the 2.8 development process this may be replaced by using - * a specific collection for Alembic I/O, which can then be toggled - * between "real" objects and cached Alembic files. */ - job->settings.view_layer = job->view_layer; - - job->settings.frame_start = params->frame_start; - job->settings.frame_end = params->frame_end; - job->settings.frame_samples_xform = params->frame_samples_xform; - job->settings.frame_samples_shape = params->frame_samples_shape; - job->settings.shutter_open = params->shutter_open; - job->settings.shutter_close = params->shutter_close; - - /* TODO(Sybren): For now this is ignored, until we can get selection - * detection working through Base pointers (instead of ob->flags). */ - job->settings.selected_only = params->selected_only; - - job->settings.export_face_sets = params->face_sets; - job->settings.export_normals = params->normals; - job->settings.export_uvs = params->uvs; - job->settings.export_vcols = params->vcolors; - job->settings.export_hair = params->export_hair; - job->settings.export_particles = params->export_particles; - job->settings.apply_subdiv = params->apply_subdiv; - job->settings.curves_as_mesh = params->curves_as_mesh; - job->settings.flatten_hierarchy = params->flatten_hierarchy; - - /* TODO(Sybren): visible_layer & renderable only is ignored for now, - * to be replaced with collections later in the 2.8 dev process - * (also see note above). */ - job->settings.visible_objects_only = params->visible_objects_only; - job->settings.renderable_only = params->renderable_only; - - job->settings.use_subdiv_schema = params->use_subdiv_schema; - job->settings.pack_uv = params->packuv; - job->settings.global_scale = params->global_scale; - job->settings.triangulate = params->triangulate; - job->settings.quad_method = params->quad_method; - job->settings.ngon_method = params->ngon_method; - - if (job->settings.frame_start > job->settings.frame_end) { - std::swap(job->settings.frame_start, job->settings.frame_end); - } + BLI_strncpy(job->filename, filepath, sizeof(job->filename)); + + job->depsgraph = DEG_graph_new( + job->bmain, scene, view_layer, DAG_EVAL_RENDER /* TODO(Sybren): params->evaluation_mode */); + job->params = *params; bool export_ok = false; if (as_background_job) { - wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - job->settings.scene, - "Alembic Export", - WM_JOB_PROGRESS, - WM_JOB_TYPE_ALEMBIC); + wmJob *wm_job = WM_jobs_get( + job->wm, CTX_wm_window(C), scene, "Alembic Export", WM_JOB_PROGRESS, WM_JOB_TYPE_ALEMBIC); /* setup job */ WM_jobs_customdata_set(wm_job, job, MEM_freeN); WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); - WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob); + WM_jobs_callbacks(wm_job, + blender::io::alembic::export_startjob, + NULL, + NULL, + blender::io::alembic::export_endjob); WM_jobs_start(CTX_wm_manager(C), wm_job); } @@ -228,8 +209,8 @@ bool ABC_export(struct Scene *scene, short stop = 0, do_update = 0; float progress = 0.f; - export_startjob(job, &stop, &do_update, &progress); - export_endjob(job); + blender::io::alembic::export_startjob(job, &stop, &do_update, &progress); + blender::io::alembic::export_endjob(job); export_ok = job->export_ok; MEM_freeN(job); diff --git a/source/blender/io/alembic/exporter/abc_exporter.cc b/source/blender/io/alembic/exporter/abc_exporter.cc deleted file mode 100644 index 321bb5fcaba..00000000000 --- a/source/blender/io/alembic/exporter/abc_exporter.cc +++ /dev/null @@ -1,681 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_exporter.h" - -#include - -#include "abc_writer_archive.h" -#include "abc_writer_camera.h" -#include "abc_writer_curves.h" -#include "abc_writer_hair.h" -#include "abc_writer_mball.h" -#include "abc_writer_mesh.h" -#include "abc_writer_nurbs.h" -#include "abc_writer_points.h" -#include "abc_writer_transform.h" -#include "intern/abc_util.h" - -#include "DNA_camera_types.h" -#include "DNA_curve_types.h" -#include "DNA_fluid_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meta_types.h" -#include "DNA_modifier_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_space_types.h" /* for FILE_MAX */ - -#include "BLI_string.h" - -#ifdef WIN32 -/* needed for MSCV because of snprintf from BLI_string */ -# include "BLI_winstuff.h" -#endif - -#include "BKE_duplilist.h" -#include "BKE_global.h" -#include "BKE_idprop.h" -#include "BKE_layer.h" -#include "BKE_main.h" -#include "BKE_mball.h" -#include "BKE_modifier.h" -#include "BKE_particle.h" -#include "BKE_scene.h" - -#include "DEG_depsgraph_query.h" - -using Alembic::Abc::OBox3dProperty; -using Alembic::Abc::TimeSamplingPtr; - -/* ************************************************************************** */ - -namespace blender { -namespace io { -namespace alembic { - -ExportSettings::ExportSettings() - : scene(NULL), - view_layer(NULL), - depsgraph(NULL), - logger(), - selected_only(false), - visible_objects_only(false), - renderable_only(false), - frame_start(1), - frame_end(1), - frame_samples_xform(1), - frame_samples_shape(1), - shutter_open(0.0), - shutter_close(1.0), - global_scale(1.0f), - flatten_hierarchy(false), - export_normals(false), - export_uvs(false), - export_vcols(false), - export_face_sets(false), - export_vweigths(false), - export_hair(true), - export_particles(true), - apply_subdiv(false), - use_subdiv_schema(false), - export_child_hairs(true), - pack_uv(false), - triangulate(false), - quad_method(0), - ngon_method(0) -{ -} - -static bool object_is_smoke_sim(Object *ob) -{ - ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluid); - - if (md) { - FluidModifierData *smd = reinterpret_cast(md); - return (smd->type == MOD_FLUID_TYPE_DOMAIN && smd->domain && - smd->domain->type == FLUID_DOMAIN_TYPE_GAS); - } - - return false; -} - -static bool object_type_is_exportable(Scene *scene, Object *ob) -{ - switch (ob->type) { - case OB_MESH: - if (object_is_smoke_sim(ob)) { - return false; - } - - return true; - case OB_EMPTY: - case OB_CURVE: - case OB_SURF: - case OB_CAMERA: - return true; - case OB_MBALL: - return AbcMBallWriter::isBasisBall(scene, ob); - default: - return false; - } -} - -/** - * Returns whether this object should be exported into the Alembic file. - * - * \param settings: export settings, used for options like 'selected only'. - * \param base: the object's base in question. - * \param is_duplicated: Normally false; true when the object is instanced - * into the scene by a dupli-object (e.g. part of a dupli-group). - * This ignores selection and layer visibility, - * and assumes that the dupli-object itself (e.g. the group-instantiating empty) is exported. - */ -static bool export_object(const ExportSettings *const settings, - const Base *const base, - bool is_duplicated) -{ - if (!is_duplicated) { - View3D *v3d = NULL; - - /* These two tests only make sense when the object isn't being instanced - * into the scene. When it is, its exportability is determined by - * its dupli-object and the DupliObject::no_draw property. */ - if (settings->selected_only && !BASE_SELECTED(v3d, base)) { - return false; - } - // FIXME Sybren: handle these cleanly (maybe just remove code), - // now using active scene layer instead. - if (settings->visible_objects_only && !BASE_VISIBLE(v3d, base)) { - return false; - } - } - - Object *ob_eval = DEG_get_evaluated_object(settings->depsgraph, base->object); - if ((ob_eval->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0) { - /* XXX fix after 2.80: the object was not part of the depsgraph, and thus we cannot get the - * evaluated copy to export. This will be handled more elegantly in the new - * AbstractHierarchyIterator that Sybren is working on. This condition is temporary, and avoids - * a BLI_assert() failure getting the evaluated mesh of this object. */ - return false; - } - - // if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) { - // return false; - // } - - return true; -} - -/* ************************************************************************** */ - -AbcExporter::AbcExporter(Main *bmain, const char *filename, ExportSettings &settings) - : m_bmain(bmain), - m_settings(settings), - m_filename(filename), - m_trans_sampling_index(0), - m_shape_sampling_index(0), - m_writer(NULL) -{ -} - -AbcExporter::~AbcExporter() -{ - /* Free xforms map */ - m_xforms_type::iterator it_x, e_x; - for (it_x = m_xforms.begin(), e_x = m_xforms.end(); it_x != e_x; ++it_x) { - delete it_x->second; - } - - /* Free shapes vector */ - for (int i = 0, e = m_shapes.size(); i != e; i++) { - delete m_shapes[i]; - } - - delete m_writer; -} - -void AbcExporter::getShutterSamples(unsigned int nr_of_samples, - bool time_relative, - std::vector &samples) -{ - Scene *scene = m_settings.scene; /* for use in the FPS macro */ - samples.clear(); - - unsigned int frame_offset = time_relative ? m_settings.frame_start : 0; - double time_factor = time_relative ? FPS : 1.0; - double shutter_open = m_settings.shutter_open; - double shutter_close = m_settings.shutter_close; - double time_inc = (shutter_close - shutter_open) / nr_of_samples; - - /* sample between shutter open & close */ - for (int sample = 0; sample < nr_of_samples; sample++) { - double sample_time = shutter_open + time_inc * sample; - double time = (frame_offset + sample_time) / time_factor; - - samples.push_back(time); - } -} - -Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step) -{ - std::vector samples; - - if (m_settings.frame_start == m_settings.frame_end) { - return TimeSamplingPtr(new Alembic::Abc::TimeSampling()); - } - - getShutterSamples(step, true, samples); - - /* TODO(Sybren): shouldn't we use the FPS macro here? */ - Alembic::Abc::TimeSamplingType ts(static_cast(samples.size()), - 1.0 / m_settings.scene->r.frs_sec); - - return TimeSamplingPtr(new Alembic::Abc::TimeSampling(ts, samples)); -} - -void AbcExporter::getFrameSet(unsigned int nr_of_samples, std::set &frames) -{ - frames.clear(); - - std::vector shutter_samples; - - getShutterSamples(nr_of_samples, false, shutter_samples); - - for (double frame = m_settings.frame_start; frame <= m_settings.frame_end; frame += 1.0) { - for (size_t j = 0; j < nr_of_samples; j++) { - frames.insert(frame + shutter_samples[j]); - } - } -} - -void AbcExporter::operator()(short *do_update, float *progress, bool *was_canceled) -{ - std::string abc_scene_name; - - if (m_bmain->name[0] != '\0') { - char scene_file_name[FILE_MAX]; - BLI_strncpy(scene_file_name, m_bmain->name, FILE_MAX); - abc_scene_name = scene_file_name; - } - else { - abc_scene_name = "untitled"; - } - - m_writer = new ArchiveWriter(m_filename, abc_scene_name, m_settings.scene); - - /* Create time samplings for transforms and shapes. */ - - TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_samples_xform); - - m_trans_sampling_index = m_writer->archive().addTimeSampling(*trans_time); - - TimeSamplingPtr shape_time; - - if ((m_settings.frame_samples_shape == m_settings.frame_samples_xform) || - (m_settings.frame_start == m_settings.frame_end)) { - shape_time = trans_time; - m_shape_sampling_index = m_trans_sampling_index; - } - else { - shape_time = createTimeSampling(m_settings.frame_samples_shape); - m_shape_sampling_index = m_writer->archive().addTimeSampling(*shape_time); - } - - OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds( - m_writer->archive(), m_trans_sampling_index); - - createTransformWritersHierarchy(); - createShapeWriters(); - - /* Make a list of frames to export. */ - - std::set xform_frames; - getFrameSet(m_settings.frame_samples_xform, xform_frames); - - std::set shape_frames; - getFrameSet(m_settings.frame_samples_shape, shape_frames); - - /* Merge all frames needed. */ - std::set frames(xform_frames); - frames.insert(shape_frames.begin(), shape_frames.end()); - - /* Export all frames. */ - - std::set::const_iterator begin = frames.begin(); - std::set::const_iterator end = frames.end(); - - const float size = static_cast(frames.size()); - size_t i = 0; - - for (; begin != end; ++begin) { - *progress = (++i / size); - *do_update = 1; - - if (G.is_break) { - *was_canceled = true; - break; - } - - const double frame = *begin; - - /* 'frame' is offset by start frame, so need to cancel the offset. */ - setCurrentFrame(m_bmain, frame); - - if (shape_frames.count(frame) != 0) { - for (int i = 0, e = m_shapes.size(); i != e; i++) { - m_shapes[i]->write(); - } - } - - if (xform_frames.count(frame) == 0) { - continue; - } - - m_xforms_type::iterator xit, xe; - for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { - xit->second->write(); - } - - /* Save the archive 's bounding box. */ - Imath::Box3d bounds; - - for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { - Imath::Box3d box = xit->second->bounds(); - bounds.extendBy(box); - } - - archive_bounds_prop.set(bounds); - } -} - -void AbcExporter::createTransformWritersHierarchy() -{ - for (Base *base = static_cast(m_settings.view_layer->object_bases.first); base; - base = base->next) { - Object *ob = base->object; - - if (export_object(&m_settings, base, false)) { - switch (ob->type) { - case OB_LAMP: - case OB_LATTICE: - case OB_SPEAKER: - /* We do not export transforms for objects of these classes. */ - break; - default: - exploreTransform(base, ob, ob->parent, NULL); - } - } - } -} - -void AbcExporter::exploreTransform(Base *base, - Object *object, - Object *parent, - Object *dupliObParent) -{ - /* If an object isn't exported itself, its duplilist shouldn't be - * exported either. */ - if (!export_object(&m_settings, base, dupliObParent != NULL)) { - return; - } - - Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object); - if (object_type_is_exportable(m_settings.scene, ob)) { - createTransformWriter(ob, parent, dupliObParent); - } - - ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob); - - if (lb) { - DupliObject *link = static_cast(lb->first); - Object *dupli_ob = NULL; - Object *dupli_parent = NULL; - - for (; link; link = link->next) { - /* This skips things like custom bone shapes. */ - if (m_settings.renderable_only && link->no_draw) { - continue; - } - - if (link->type == OB_DUPLICOLLECTION) { - dupli_ob = link->ob; - dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob; - - exploreTransform(base, dupli_ob, dupli_parent, ob); - } - } - - free_object_duplilist(lb); - } -} - -AbcTransformWriter *AbcExporter::createTransformWriter(Object *ob, - Object *parent, - Object *dupliObParent) -{ - /* An object should not be its own parent, or we'll get infinite loops. */ - BLI_assert(ob != parent); - BLI_assert(ob != dupliObParent); - - std::string name; - if (m_settings.flatten_hierarchy) { - name = get_id_name(ob); - } - else { - name = get_object_dag_path_name(ob, dupliObParent); - } - - /* check if we have already created a transform writer for this object */ - AbcTransformWriter *my_writer = getXForm(name); - if (my_writer != NULL) { - return my_writer; - } - - AbcTransformWriter *parent_writer = NULL; - Alembic::Abc::OObject alembic_parent; - - if (m_settings.flatten_hierarchy || parent == NULL) { - /* Parentless objects still have the "top object" as parent - * in Alembic. */ - alembic_parent = m_writer->archive().getTop(); - } - else { - /* Since there are so many different ways to find parents (as evident - * in the number of conditions below), we can't really look up the - * parent by name. We'll just call createTransformWriter(), which will - * return the parent's AbcTransformWriter pointer. */ - if (parent->parent) { - if (parent == dupliObParent) { - parent_writer = createTransformWriter(parent, parent->parent, NULL); - } - else { - parent_writer = createTransformWriter(parent, parent->parent, dupliObParent); - } - } - else if (parent == dupliObParent) { - if (dupliObParent->parent == NULL) { - parent_writer = createTransformWriter(parent, NULL, NULL); - } - else { - parent_writer = createTransformWriter( - parent, dupliObParent->parent, dupliObParent->parent); - } - } - else { - parent_writer = createTransformWriter(parent, dupliObParent, dupliObParent); - } - - BLI_assert(parent_writer); - alembic_parent = parent_writer->alembicXform(); - } - - my_writer = new AbcTransformWriter( - ob, alembic_parent, parent_writer, m_trans_sampling_index, m_settings); - - /* When flattening, the matrix of the dupliobject has to be added. */ - if (m_settings.flatten_hierarchy && dupliObParent) { - my_writer->m_proxy_from = dupliObParent; - } - - m_xforms[name] = my_writer; - return my_writer; -} - -void AbcExporter::createShapeWriters() -{ - for (Base *base = static_cast(m_settings.view_layer->object_bases.first); base; - base = base->next) { - exploreObject(base, base->object, NULL); - } -} - -void AbcExporter::exploreObject(Base *base, Object *object, Object *dupliObParent) -{ - /* If an object isn't exported itself, its duplilist shouldn't be - * exported either. */ - if (!export_object(&m_settings, base, dupliObParent != NULL)) { - return; - } - - Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object); - createShapeWriter(ob, dupliObParent); - - ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob); - - if (lb) { - DupliObject *link = static_cast(lb->first); - - for (; link; link = link->next) { - /* This skips things like custom bone shapes. */ - if (m_settings.renderable_only && link->no_draw) { - continue; - } - if (link->type == OB_DUPLICOLLECTION) { - exploreObject(base, link->ob, ob); - } - } - - free_object_duplilist(lb); - } -} - -void AbcExporter::createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform) -{ - if (!m_settings.export_hair && !m_settings.export_particles) { - return; - } - - ParticleSystem *psys = static_cast(ob->particlesystem.first); - - for (; psys; psys = psys->next) { - if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) { - continue; - } - - if (m_settings.export_hair && psys->part->type == PART_HAIR) { - m_settings.export_child_hairs = true; - m_shapes.push_back(new AbcHairWriter(ob, xform, m_shape_sampling_index, m_settings, psys)); - } - else if (m_settings.export_particles && - (psys->part->type == PART_EMITTER || psys->part->type == PART_FLUID_FLIP || - psys->part->type == PART_FLUID_SPRAY || psys->part->type == PART_FLUID_BUBBLE || - psys->part->type == PART_FLUID_FOAM || psys->part->type == PART_FLUID_TRACER || - psys->part->type == PART_FLUID_SPRAYFOAM || - psys->part->type == PART_FLUID_SPRAYBUBBLE || - psys->part->type == PART_FLUID_FOAMBUBBLE || - psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE)) { - m_shapes.push_back(new AbcPointsWriter(ob, xform, m_shape_sampling_index, m_settings, psys)); - } - } -} - -void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent) -{ - if (!object_type_is_exportable(m_settings.scene, ob)) { - return; - } - - std::string name; - - if (m_settings.flatten_hierarchy) { - name = get_id_name(ob); - } - else { - name = get_object_dag_path_name(ob, dupliObParent); - } - - AbcTransformWriter *xform = getXForm(name); - - if (!xform) { - ABC_LOG(m_settings.logger) << __func__ << ": xform " << name << " is NULL\n"; - return; - } - - createParticleSystemsWriters(ob, xform); - - switch (ob->type) { - case OB_MESH: { - Mesh *me = static_cast(ob->data); - - if (!me) { - return; - } - - m_shapes.push_back(new AbcMeshWriter(ob, xform, m_shape_sampling_index, m_settings)); - break; - } - case OB_SURF: { - Curve *cu = static_cast(ob->data); - - if (!cu) { - return; - } - - AbcObjectWriter *writer; - if (m_settings.curves_as_mesh) { - writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings); - } - else { - writer = new AbcNurbsWriter(ob, xform, m_shape_sampling_index, m_settings); - } - m_shapes.push_back(writer); - break; - } - case OB_CURVE: { - Curve *cu = static_cast(ob->data); - - if (!cu) { - return; - } - - AbcObjectWriter *writer; - if (m_settings.curves_as_mesh) { - writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings); - } - else { - writer = new AbcCurveWriter(ob, xform, m_shape_sampling_index, m_settings); - } - m_shapes.push_back(writer); - break; - } - case OB_CAMERA: { - Camera *cam = static_cast(ob->data); - - if (cam->type == CAM_PERSP) { - m_shapes.push_back(new AbcCameraWriter(ob, xform, m_shape_sampling_index, m_settings)); - } - - break; - } - case OB_MBALL: { - MetaBall *mball = static_cast(ob->data); - if (!mball) { - return; - } - - m_shapes.push_back( - new AbcMBallWriter(m_bmain, ob, xform, m_shape_sampling_index, m_settings)); - break; - } - } -} - -AbcTransformWriter *AbcExporter::getXForm(const std::string &name) -{ - std::map::iterator it = m_xforms.find(name); - - if (it == m_xforms.end()) { - return NULL; - } - - return it->second; -} - -void AbcExporter::setCurrentFrame(Main *bmain, double t) -{ - m_settings.scene->r.cfra = static_cast(t); - m_settings.scene->r.subframe = static_cast(t) - m_settings.scene->r.cfra; - BKE_scene_graph_update_for_newframe(m_settings.depsgraph, bmain); -} - -} // namespace alembic -} // namespace io -} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_exporter.h b/source/blender/io/alembic/exporter/abc_exporter.h deleted file mode 100644 index af30f2ceb50..00000000000 --- a/source/blender/io/alembic/exporter/abc_exporter.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#pragma once - -/** \file - * \ingroup balembic - */ - -#include -#include -#include -#include - -#include "intern/abc_util.h" - -struct Base; -struct Depsgraph; -struct Main; -struct Object; -struct Scene; -struct ViewLayer; - -namespace blender { -namespace io { -namespace alembic { - -class AbcObjectWriter; -class AbcTransformWriter; -class ArchiveWriter; - -struct ExportSettings { - ExportSettings(); - - Scene *scene; - /** Scene layer to export; all its objects will be exported, unless selected_only=true. */ - ViewLayer *view_layer; - Depsgraph *depsgraph; - SimpleLogger logger; - - bool selected_only; - bool visible_objects_only; - bool renderable_only; - - double frame_start, frame_end; - double frame_samples_xform; - double frame_samples_shape; - double shutter_open; - double shutter_close; - float global_scale; - - bool flatten_hierarchy; - - bool export_normals; - bool export_uvs; - bool export_vcols; - bool export_face_sets; - bool export_vweigths; - bool export_hair; - bool export_particles; - - bool apply_subdiv; - bool curves_as_mesh; - bool use_subdiv_schema; - bool export_child_hairs; - bool pack_uv; - bool triangulate; - - int quad_method; - int ngon_method; -}; - -class AbcExporter { - Main *m_bmain; - ExportSettings &m_settings; - - const char *m_filename; - - unsigned int m_trans_sampling_index, m_shape_sampling_index; - - ArchiveWriter *m_writer; - - /* mapping from name to transform writer */ - typedef std::map m_xforms_type; - m_xforms_type m_xforms; - - std::vector m_shapes; - - public: - AbcExporter(Main *bmain, const char *filename, ExportSettings &settings); - ~AbcExporter(); - - void operator()(short *do_update, float *progress, bool *was_canceled); - - protected: - void getShutterSamples(unsigned int nr_of_samples, - bool time_relative, - std::vector &samples); - void getFrameSet(unsigned int nr_of_samples, std::set &frames); - - private: - Alembic::Abc::TimeSamplingPtr createTimeSampling(double step); - - void createTransformWritersHierarchy(); - AbcTransformWriter *createTransformWriter(Object *ob, Object *parent, Object *dupliObParent); - void exploreTransform(Base *base, Object *object, Object *parent, Object *dupliObParent); - void exploreObject(Base *base, Object *object, Object *dupliObParent); - void createShapeWriters(); - void createShapeWriter(Object *ob, Object *dupliObParent); - void createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform); - - AbcTransformWriter *getXForm(const std::string &name); - - void setCurrentFrame(Main *bmain, double t); -}; - -} // namespace alembic -} // namespace io -} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc new file mode 100644 index 00000000000..90004c0e85b --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc @@ -0,0 +1,261 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +#include "abc_hierarchy_iterator.h" +#include "abc_writer_abstract.h" +#include "abc_writer_camera.h" +#include "abc_writer_curves.h" +#include "abc_writer_hair.h" +#include "abc_writer_mball.h" +#include "abc_writer_mesh.h" +#include "abc_writer_nurbs.h" +#include "abc_writer_points.h" +#include "abc_writer_transform.h" + +#include +#include + +#include "BLI_assert.h" + +#include "DEG_depsgraph_query.h" + +#include "DNA_ID.h" +#include "DNA_layer_types.h" +#include "DNA_object_types.h" + +namespace blender { +namespace io { +namespace alembic { + +ABCHierarchyIterator::ABCHierarchyIterator(Depsgraph *depsgraph, + ABCArchive *abc_archive, + const AlembicExportParams ¶ms) + : AbstractHierarchyIterator(depsgraph), abc_archive_(abc_archive), params_(params) +{ +} + +void ABCHierarchyIterator::iterate_and_write() +{ + AbstractHierarchyIterator::iterate_and_write(); + update_archive_bounding_box(); +} + +void ABCHierarchyIterator::update_archive_bounding_box() +{ + Imath::Box3d bounds; + update_bounding_box_recursive(bounds, HierarchyContext::root()); + abc_archive_->update_bounding_box(bounds); +} + +void ABCHierarchyIterator::update_bounding_box_recursive(Imath::Box3d &bounds, + const HierarchyContext *context) +{ + if (context != nullptr) { + AbstractHierarchyWriter *abstract_writer = writers_[context->export_path]; + ABCAbstractWriter *abc_writer = static_cast(abstract_writer); + + if (abc_writer != nullptr) { + bounds.extendBy(abc_writer->bounding_box()); + } + } + + for (HierarchyContext *child_context : graph_children(context)) { + update_bounding_box_recursive(bounds, child_context); + } +} + +bool ABCHierarchyIterator::mark_as_weak_export(const Object *object) const +{ + if (params_.selected_only && (object->base_flag & BASE_SELECTED) == 0) { + return true; + } + /* TODO(Sybren): handle other flags too? */ + return false; +} + +void ABCHierarchyIterator::delete_object_writer(AbstractHierarchyWriter *writer) +{ + delete writer; +} + +std::string ABCHierarchyIterator::make_valid_name(const std::string &name) const +{ + std::string abc_name(name); + std::replace(abc_name.begin(), abc_name.end(), ' ', '_'); + std::replace(abc_name.begin(), abc_name.end(), '.', '_'); + std::replace(abc_name.begin(), abc_name.end(), ':', '_'); + return abc_name; +} + +AbstractHierarchyIterator::ExportGraph::key_type ABCHierarchyIterator:: + determine_graph_index_object(const HierarchyContext *context) +{ + if (params_.flatten_hierarchy) { + return std::make_pair(nullptr, nullptr); + } + + return AbstractHierarchyIterator::determine_graph_index_object(context); +} + +AbstractHierarchyIterator::ExportGraph::key_type ABCHierarchyIterator::determine_graph_index_dupli( + const HierarchyContext *context, const std::set &dupli_set) +{ + if (params_.flatten_hierarchy) { + return std::make_pair(nullptr, nullptr); + } + + return AbstractHierarchyIterator::determine_graph_index_dupli(context, dupli_set); +} + +Alembic::Abc::OObject ABCHierarchyIterator::get_alembic_parent( + const HierarchyContext *context) const +{ + Alembic::Abc::OObject parent; + + if (!context->higher_up_export_path.empty()) { + AbstractHierarchyWriter *writer = get_writer(context->higher_up_export_path); + ABCAbstractWriter *abc_writer = static_cast(writer); + parent = abc_writer->get_alembic_object(); + } + + if (!parent.valid()) { + /* An invalid parent object means "no parent", which should be translated to Alembic's top + * archive object. */ + return abc_archive_->archive->getTop(); + } + + return parent; +} + +ABCWriterConstructorArgs ABCHierarchyIterator::writer_constructor_args( + const HierarchyContext *context) const +{ + ABCWriterConstructorArgs constructor_args; + constructor_args.depsgraph = depsgraph_; + constructor_args.abc_archive = abc_archive_; + constructor_args.abc_parent = get_alembic_parent(context); + constructor_args.abc_name = context->export_name; + constructor_args.abc_path = context->export_path; + constructor_args.hierarchy_iterator = this; + constructor_args.export_params = ¶ms_; + return constructor_args; +} + +AbstractHierarchyWriter *ABCHierarchyIterator::create_transform_writer( + const HierarchyContext *context) +{ + ABCAbstractWriter *transform_writer = new ABCTransformWriter(writer_constructor_args(context)); + transform_writer->create_alembic_objects(context); + return transform_writer; +} + +AbstractHierarchyWriter *ABCHierarchyIterator::create_data_writer(const HierarchyContext *context) +{ + const ABCWriterConstructorArgs writer_args = writer_constructor_args(context); + ABCAbstractWriter *data_writer = nullptr; + + switch (context->object->type) { + case OB_MESH: + data_writer = new ABCMeshWriter(writer_args); + break; + case OB_CAMERA: + data_writer = new ABCCameraWriter(writer_args); + break; + case OB_CURVE: + if (params_.curves_as_mesh) { + data_writer = new ABCCurveMeshWriter(writer_args); + } + else { + data_writer = new ABCCurveWriter(writer_args); + } + break; + case OB_SURF: + if (params_.curves_as_mesh) { + data_writer = new ABCCurveMeshWriter(writer_args); + } + else { + data_writer = new ABCNurbsWriter(writer_args); + } + break; + case OB_MBALL: + data_writer = new ABCMetaballWriter(writer_args); + break; + + case OB_EMPTY: + case OB_LAMP: + case OB_FONT: + case OB_SPEAKER: + case OB_LIGHTPROBE: + case OB_LATTICE: + case OB_ARMATURE: + case OB_GPENCIL: + return nullptr; + case OB_TYPE_MAX: + BLI_assert(!"OB_TYPE_MAX should not be used"); + return nullptr; + } + + if (!data_writer->is_supported(context)) { + delete data_writer; + return nullptr; + } + + data_writer->create_alembic_objects(context); + return data_writer; +} + +AbstractHierarchyWriter *ABCHierarchyIterator::create_hair_writer(const HierarchyContext *context) +{ + if (!params_.export_hair) { + return nullptr; + } + + const ABCWriterConstructorArgs writer_args = writer_constructor_args(context); + ABCAbstractWriter *hair_writer = new ABCHairWriter(writer_args); + + if (!hair_writer->is_supported(context)) { + delete hair_writer; + return nullptr; + } + + hair_writer->create_alembic_objects(context); + return hair_writer; +} + +AbstractHierarchyWriter *ABCHierarchyIterator::create_particle_writer( + const HierarchyContext *context) +{ + if (!params_.export_particles) { + return nullptr; + } + + const ABCWriterConstructorArgs writer_args = writer_constructor_args(context); + std::unique_ptr particle_writer(std::make_unique(writer_args)); + + if (!particle_writer->is_supported(context)) { + return nullptr; + } + + particle_writer->create_alembic_objects(context); + return particle_writer.release(); +} + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h new file mode 100644 index 00000000000..edcb31806ba --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h @@ -0,0 +1,90 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ +#pragma once + +#include "ABC_alembic.h" +#include "abc_archive.h" + +#include "IO_abstract_hierarchy_iterator.h" + +#include + +#include +#include + +struct Depsgraph; +struct ID; +struct Object; + +namespace blender { +namespace io { +namespace alembic { + +class ABCHierarchyIterator; + +struct ABCWriterConstructorArgs { + Depsgraph *depsgraph; + ABCArchive *abc_archive; + Alembic::Abc::OObject abc_parent; + std::string abc_name; + std::string abc_path; + const ABCHierarchyIterator *hierarchy_iterator; + const AlembicExportParams *export_params; +}; + +class ABCHierarchyIterator : public AbstractHierarchyIterator { + private: + ABCArchive *abc_archive_; + const AlembicExportParams ¶ms_; + + public: + ABCHierarchyIterator(Depsgraph *depsgraph, + ABCArchive *abc_archive_, + const AlembicExportParams ¶ms); + + virtual void iterate_and_write() override; + virtual std::string make_valid_name(const std::string &name) const override; + + protected: + virtual bool mark_as_weak_export(const Object *object) const override; + + virtual ExportGraph::key_type determine_graph_index_object( + const HierarchyContext *context) override; + virtual AbstractHierarchyIterator::ExportGraph::key_type determine_graph_index_dupli( + const HierarchyContext *context, const std::set &dupli_set) override; + + virtual AbstractHierarchyWriter *create_transform_writer( + const HierarchyContext *context) override; + virtual AbstractHierarchyWriter *create_data_writer(const HierarchyContext *context) override; + virtual AbstractHierarchyWriter *create_hair_writer(const HierarchyContext *context) override; + virtual AbstractHierarchyWriter *create_particle_writer( + const HierarchyContext *context) override; + + virtual void delete_object_writer(AbstractHierarchyWriter *writer) override; + + private: + Alembic::Abc::OObject get_alembic_parent(const HierarchyContext *context) const; + ABCWriterConstructorArgs writer_constructor_args(const HierarchyContext *context) const; + void update_archive_bounding_box(); + void update_bounding_box_recursive(Imath::Box3d &bounds, const HierarchyContext *context); +}; + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc b/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc new file mode 100644 index 00000000000..7c147076975 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc @@ -0,0 +1,107 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ +#include "abc_subdiv_disabler.h" + +#include + +#include "BLI_listbase.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "DNA_layer_types.h" +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" + +#include "BKE_modifier.h" + +namespace blender { +namespace io { +namespace alembic { + +SubdivModifierDisabler::SubdivModifierDisabler(Depsgraph *depsgraph) : depsgraph_(depsgraph) +{ +} + +SubdivModifierDisabler::~SubdivModifierDisabler() +{ + for (ModifierData *modifier : disabled_modifiers_) { + modifier->mode &= ~eModifierMode_DisableTemporary; + } +} + +void SubdivModifierDisabler::disable_modifiers() +{ + Scene *scene = DEG_get_input_scene(depsgraph_); + ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph_); + + LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { + Object *object = base->object; + + if (object->type != OB_MESH) { + continue; + } + + ModifierData *subdiv = get_subdiv_modifier(scene, object); + if (subdiv == nullptr) { + continue; + } + + /* This disables more modifiers than necessary, as it doesn't take restrictions like + * "export selected objects only" into account. However, with the subsurfs disabled, + * moving to a different frame is also going to be faster, so in the end this is probably + * a good thing to do. */ + subdiv->mode |= eModifierMode_DisableTemporary; + disabled_modifiers_.insert(subdiv); + DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); + } +} + +/* Check if the mesh is a subsurf, ignoring disabled modifiers and + * displace if it's after subsurf. */ +ModifierData *SubdivModifierDisabler::get_subdiv_modifier(Scene *scene, Object *ob) +{ + ModifierData *md = static_cast(ob->modifiers.last); + + for (; md; md = md->prev) { + if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) { + continue; + } + + if (md->type == eModifierType_Subsurf) { + SubsurfModifierData *smd = reinterpret_cast(md); + + if (smd->subdivType == ME_CC_SUBSURF) { + return md; + } + } + + /* mesh is not a subsurf. break */ + if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) { + return nullptr; + } + } + + return nullptr; +} + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_subdiv_disabler.h b/source/blender/io/alembic/exporter/abc_subdiv_disabler.h new file mode 100644 index 00000000000..677847f3f63 --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_subdiv_disabler.h @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ +#pragma once + +#include + +struct Depsgraph; +struct ModifierData; +struct Object; +struct Scene; + +namespace blender { +namespace io { +namespace alembic { + +/** + * Temporarily all subdivision modifiers on mesh objects. + * The destructor restores all disabled modifiers. + * + * This is used to export unsubdivided meshes to Alembic. It is done in a separate step before the + * exporter starts iterating over all the frames, so that it only has to happen once per export. + */ +class SubdivModifierDisabler final { + private: + Depsgraph *depsgraph_; + std::set disabled_modifiers_; + + public: + explicit SubdivModifierDisabler(Depsgraph *depsgraph); + ~SubdivModifierDisabler(); + + void disable_modifiers(); + + static ModifierData *get_subdiv_modifier(Scene *scene, Object *ob); +}; + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.cc b/source/blender/io/alembic/exporter/abc_writer_abstract.cc new file mode 100644 index 00000000000..e43b394e27f --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_abstract.cc @@ -0,0 +1,101 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ +#include "abc_writer_abstract.h" +#include "abc_hierarchy_iterator.h" + +#include "BKE_animsys.h" +#include "BKE_key.h" +#include "BKE_object.h" + +#include "DNA_modifier_types.h" + +#include "CLG_log.h" +static CLG_LogRef LOG = {"io.alembic"}; + +namespace blender { +namespace io { +namespace alembic { + +using Alembic::Abc::OObject; +using Alembic::Abc::TimeSamplingPtr; + +ABCAbstractWriter::ABCAbstractWriter(const ABCWriterConstructorArgs &args) + : args_(args), + frame_has_been_written_(false), + is_animated_(false), + timesample_index_(args_.abc_archive->time_sampling_index_shapes()) +{ +} + +ABCAbstractWriter::~ABCAbstractWriter() +{ +} + +bool ABCAbstractWriter::is_supported(const HierarchyContext * /*context*/) const +{ + return true; +} + +void ABCAbstractWriter::write(HierarchyContext &context) +{ + if (!frame_has_been_written_) { + is_animated_ = (args_.export_params->frame_start != args_.export_params->frame_end) && + check_is_animated(context); + } + else if (!is_animated_) { + /* A frame has already been written, and without animation one frame is enough. */ + return; + } + + do_write(context); + + frame_has_been_written_ = true; +} + +const Imath::Box3d &ABCAbstractWriter::bounding_box() const +{ + return bounding_box_; +} + +void ABCAbstractWriter::update_bounding_box(Object *object) +{ + BoundBox *bb = BKE_object_boundbox_get(object); + + if (!bb) { + if (object->type != OB_CAMERA) { + CLOG_WARN(&LOG, "Bounding box is null!\n"); + } + bounding_box_.min.x = bounding_box_.min.y = bounding_box_.min.z = 0; + bounding_box_.max.x = bounding_box_.max.y = bounding_box_.max.z = 0; + return; + } + + /* Convert Z-up to Y-up. This also changes which vector goes into which min/max property. */ + bounding_box_.min.x = bb->vec[0][0]; + bounding_box_.min.y = bb->vec[0][2]; + bounding_box_.min.z = -bb->vec[6][1]; + + bounding_box_.max.x = bb->vec[6][0]; + bounding_box_.max.y = bb->vec[6][2]; + bounding_box_.max.z = -bb->vec[0][1]; +} + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h new file mode 100644 index 00000000000..d6fd692775e --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h @@ -0,0 +1,77 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ +#pragma once + +#include "IO_abstract_hierarchy_iterator.h" +#include "abc_hierarchy_iterator.h" + +#include +#include + +#include "DEG_depsgraph_query.h" +#include "DNA_material_types.h" + +struct Material; +struct Object; + +namespace blender { +namespace io { +namespace alembic { + +class ABCAbstractWriter : public AbstractHierarchyWriter { + protected: + const ABCWriterConstructorArgs args_; + + bool frame_has_been_written_; + bool is_animated_; + uint32_t timesample_index_; + Imath::Box3d bounding_box_; + + public: + explicit ABCAbstractWriter(const ABCWriterConstructorArgs &args); + virtual ~ABCAbstractWriter(); + + virtual void write(HierarchyContext &context) override; + + /* Returns true if the data to be written is actually supported. This would, for example, allow a + * hypothetical camera writer accept a perspective camera but reject an orthogonal one. + * + * Returning false from a transform writer will prevent the object and all its decendants from + * being exported. Returning false from a data writer (object data, hair, or particles) will + * only prevent that data from being written (and thus cause the object to be exported as an + * Empty). */ + virtual bool is_supported(const HierarchyContext *context) const; + + const Imath::Box3d &bounding_box() const; + + /* Called by AlembicHierarchyCreator after checking that the data is supported via + * is_supported(). */ + virtual void create_alembic_objects(const HierarchyContext *context) = 0; + + virtual const Alembic::Abc::OObject get_alembic_object() const = 0; + + protected: + virtual void do_write(HierarchyContext &context) = 0; + + virtual void update_bounding_box(Object *object); +}; + +} // namespace alembic +} // namespace io +} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_archive.cc b/source/blender/io/alembic/exporter/abc_writer_archive.cc deleted file mode 100644 index 741a3697b38..00000000000 --- a/source/blender/io/alembic/exporter/abc_writer_archive.cc +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_archive.h" - -#include "BKE_blender_version.h" - -#include "BLI_path_util.h" -#include "BLI_string.h" - -#include "DNA_scene_types.h" - -#ifdef WIN32 -# include "utfconv.h" -#endif - -#include - -using Alembic::Abc::ErrorHandler; -using Alembic::Abc::kWrapExisting; -using Alembic::Abc::OArchive; - -namespace blender { -namespace io { -namespace alembic { - -/* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to - * have a version supporting streams. */ -static OArchive create_archive(std::ostream *ostream, - const std::string &scene_name, - double scene_fps) -{ - Alembic::Abc::MetaData abc_metadata; - - abc_metadata.set(Alembic::Abc::kApplicationNameKey, "Blender"); - abc_metadata.set(Alembic::Abc::kUserDescriptionKey, scene_name); - abc_metadata.set("blender_version", std::string("v") + BKE_blender_version_string()); - abc_metadata.set("FramesPerTimeUnit", std::to_string(scene_fps)); - - time_t raw_time; - time(&raw_time); - char buffer[128]; - -#if defined _WIN32 || defined _WIN64 - ctime_s(buffer, 128, &raw_time); -#else - ctime_r(&raw_time, buffer); -#endif - - const std::size_t buffer_len = strlen(buffer); - if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') { - buffer[buffer_len - 1] = '\0'; - } - - abc_metadata.set(Alembic::Abc::kDateWrittenKey, buffer); - - ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy; - Alembic::AbcCoreOgawa::WriteArchive archive_writer; - return OArchive(archive_writer(ostream, abc_metadata), kWrapExisting, policy); -} - -ArchiveWriter::ArchiveWriter(const char *filename, - const std::string &abc_scene_name, - const Scene *scene) -{ - /* Use stream to support unicode character paths on Windows. */ -#ifdef WIN32 - UTF16_ENCODE(filename); - std::wstring wstr(filename_16); - m_outfile.open(wstr.c_str(), std::ios::out | std::ios::binary); - UTF16_UN_ENCODE(filename); -#else - m_outfile.open(filename, std::ios::out | std::ios::binary); -#endif - - m_archive = create_archive(&m_outfile, abc_scene_name, FPS); -} - -OArchive &ArchiveWriter::archive() -{ - return m_archive; -} - -} // namespace alembic -} // namespace io -} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_archive.h b/source/blender/io/alembic/exporter/abc_writer_archive.h deleted file mode 100644 index db13dc0ec92..00000000000 --- a/source/blender/io/alembic/exporter/abc_writer_archive.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ -#pragma once - -/** \file - * \ingroup balembic - */ - -#include -#include - -#include - -struct Main; -struct Scene; - -namespace blender { -namespace io { -namespace alembic { - -/* Wrappers around input and output archives. The goal is to be able to use - * streams so that unicode paths work on Windows (T49112), and to make sure that - * the stream objects remain valid as long as the archives are open. - */ - -class ArchiveWriter { - std::ofstream m_outfile; - Alembic::Abc::OArchive m_archive; - - public: - ArchiveWriter(const char *filename, const std::string &abc_scene_name, const Scene *scene); - - Alembic::Abc::OArchive &archive(); -}; - -} // namespace alembic -} // namespace io -} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.cc b/source/blender/io/alembic/exporter/abc_writer_camera.cc index ef6d1512a96..7e7277cb4ea 100644 --- a/source/blender/io/alembic/exporter/abc_writer_camera.cc +++ b/source/blender/io/alembic/exporter/abc_writer_camera.cc @@ -19,67 +19,90 @@ */ #include "abc_writer_camera.h" -#include "abc_writer_transform.h" +#include "abc_hierarchy_iterator.h" + +#include "BKE_camera.h" + +#include "BLI_assert.h" #include "DNA_camera_types.h" -#include "DNA_object_types.h" +#include "DNA_scene_types.h" -using Alembic::AbcGeom::OCamera; -using Alembic::AbcGeom::OFloatProperty; +#include "CLG_log.h" +static CLG_LogRef LOG = {"io.alembic"}; namespace blender { namespace io { namespace alembic { -AbcCameraWriter::AbcCameraWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent) +using Alembic::AbcGeom::CameraSample; +using Alembic::AbcGeom::OCamera; +using Alembic::AbcGeom::OFloatProperty; + +ABCCameraWriter::ABCCameraWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args) +{ +} + +bool ABCCameraWriter::is_supported(const HierarchyContext *context) const +{ + Camera *camera = static_cast(context->object->data); + return camera->type == CAM_PERSP; +} + +void ABCCameraWriter::create_alembic_objects(const HierarchyContext * /*context*/) { - OCamera camera(parent->alembicXform(), m_name, m_time_sampling); - m_camera_schema = camera.getSchema(); + CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str()); + abc_camera_ = OCamera(args_.abc_parent, args_.abc_name, timesample_index_); + abc_camera_schema_ = abc_camera_.getSchema(); + + abc_custom_data_container_ = abc_camera_schema_.getUserProperties(); + abc_stereo_distance_ = OFloatProperty( + abc_custom_data_container_, "stereoDistance", timesample_index_); + abc_eye_separation_ = OFloatProperty( + abc_custom_data_container_, "eyeSeparation", timesample_index_); +} - m_custom_data_container = m_camera_schema.getUserProperties(); - m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling); - m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling); +const Alembic::Abc::OObject ABCCameraWriter::get_alembic_object() const +{ + return abc_camera_; } -void AbcCameraWriter::do_write() +void ABCCameraWriter::do_write(HierarchyContext &context) { - Camera *cam = static_cast(m_object->data); + Camera *cam = static_cast(context.object->data); - m_stereo_distance.set(cam->stereo.convergence_distance); - m_eye_separation.set(cam->stereo.interocular_distance); + abc_stereo_distance_.set(cam->stereo.convergence_distance); + abc_eye_separation_.set(cam->stereo.interocular_distance); const double apperture_x = cam->sensor_x / 10.0; const double apperture_y = cam->sensor_y / 10.0; const double film_aspect = apperture_x / apperture_y; - m_camera_sample.setFocalLength(cam->lens); - m_camera_sample.setHorizontalAperture(apperture_x); - m_camera_sample.setVerticalAperture(apperture_y); - m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx); - m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect); - m_camera_sample.setNearClippingPlane(cam->clip_start); - m_camera_sample.setFarClippingPlane(cam->clip_end); + CameraSample camera_sample; + camera_sample.setFocalLength(cam->lens); + camera_sample.setHorizontalAperture(apperture_x); + camera_sample.setVerticalAperture(apperture_y); + camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx); + camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect); + camera_sample.setNearClippingPlane(cam->clip_start); + camera_sample.setFarClippingPlane(cam->clip_end); if (cam->dof.focus_object) { - Imath::V3f v(m_object->loc[0] - cam->dof.focus_object->loc[0], - m_object->loc[1] - cam->dof.focus_object->loc[1], - m_object->loc[2] - cam->dof.focus_object->loc[2]); - m_camera_sample.setFocusDistance(v.length()); + Imath::V3f v(context.object->loc[0] - cam->dof.focus_object->loc[0], + context.object->loc[1] - cam->dof.focus_object->loc[1], + context.object->loc[2] - cam->dof.focus_object->loc[2]); + camera_sample.setFocusDistance(v.length()); } else { - m_camera_sample.setFocusDistance(cam->dof.focus_distance); + camera_sample.setFocusDistance(cam->dof.focus_distance); } /* Blender camera does not have an fstop param, so try to find a custom prop * instead. */ - m_camera_sample.setFStop(cam->dof.aperture_fstop); + camera_sample.setFStop(cam->dof.aperture_fstop); - m_camera_sample.setLensSqueezeRatio(1.0); - m_camera_schema.set(m_camera_sample); + camera_sample.setLensSqueezeRatio(1.0); + abc_camera_schema_.set(camera_sample); } } // namespace alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.h b/source/blender/io/alembic/exporter/abc_writer_camera.h index befa1e24551..a72cfa2f357 100644 --- a/source/blender/io/alembic/exporter/abc_writer_camera.h +++ b/source/blender/io/alembic/exporter/abc_writer_camera.h @@ -19,29 +19,32 @@ * \ingroup balembic */ -#include "abc_writer_object.h" +#include "abc_writer_abstract.h" -/* ************************************************************************** */ +#include namespace blender { namespace io { namespace alembic { -class AbcCameraWriter : public AbcObjectWriter { - Alembic::AbcGeom::OCameraSchema m_camera_schema; - Alembic::AbcGeom::CameraSample m_camera_sample; - Alembic::AbcGeom::OCompoundProperty m_custom_data_container; - Alembic::AbcGeom::OFloatProperty m_stereo_distance; - Alembic::AbcGeom::OFloatProperty m_eye_separation; +class ABCCameraWriter : public ABCAbstractWriter { + private: + Alembic::AbcGeom::OCamera abc_camera_; + Alembic::AbcGeom::OCameraSchema abc_camera_schema_; + + Alembic::AbcGeom::OCompoundProperty abc_custom_data_container_; + Alembic::AbcGeom::OFloatProperty abc_stereo_distance_; + Alembic::AbcGeom::OFloatProperty abc_eye_separation_; public: - AbcCameraWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); + explicit ABCCameraWriter(const ABCWriterConstructorArgs &args); - private: - virtual void do_write(); + virtual void create_alembic_objects(const HierarchyContext *context) override; + virtual const Alembic::Abc::OObject get_alembic_object() const override; + + protected: + virtual bool is_supported(const HierarchyContext *context) const override; + virtual void do_write(HierarchyContext &context) override; }; } // namespace alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.cc b/source/blender/io/alembic/exporter/abc_writer_curves.cc index 7c2acb9e0c9..f2a46c5e4fe 100644 --- a/source/blender/io/alembic/exporter/abc_writer_curves.cc +++ b/source/blender/io/alembic/exporter/abc_writer_curves.cc @@ -22,9 +22,7 @@ */ #include "abc_writer_curves.h" -#include "abc_writer_transform.h" #include "intern/abc_axis_conversion.h" -#include "intern/abc_reader_curves.h" #include "DNA_curve_types.h" #include "DNA_object_types.h" @@ -33,6 +31,9 @@ #include "BKE_mesh.h" #include "BKE_object.h" +#include "CLG_log.h" +static CLG_LogRef LOG = {"io.alembic"}; + using Alembic::AbcGeom::OCompoundProperty; using Alembic::AbcGeom::OCurves; using Alembic::AbcGeom::OCurvesSchema; @@ -44,24 +45,32 @@ namespace blender { namespace io { namespace alembic { -AbcCurveWriter::AbcCurveWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent) +const std::string ABC_CURVE_RESOLUTION_U_PROPNAME("blender:resolution"); + +ABCCurveWriter::ABCCurveWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args) +{ +} + +void ABCCurveWriter::create_alembic_objects(const HierarchyContext *context) { - OCurves curves(parent->alembicXform(), m_name, m_time_sampling); - m_schema = curves.getSchema(); + CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str()); + abc_curve_ = OCurves(args_.abc_parent, args_.abc_name, timesample_index_); + abc_curve_schema_ = abc_curve_.getSchema(); - Curve *cu = static_cast(m_object->data); - OCompoundProperty user_props = m_schema.getUserProperties(); + Curve *cu = static_cast(context->object->data); + OCompoundProperty user_props = abc_curve_schema_.getUserProperties(); OInt16Property user_prop_resolu(user_props, ABC_CURVE_RESOLUTION_U_PROPNAME); user_prop_resolu.set(cu->resolu); } -void AbcCurveWriter::do_write() +const Alembic::Abc::OObject ABCCurveWriter::get_alembic_object() const +{ + return abc_curve_; +} + +void ABCCurveWriter::do_write(HierarchyContext &context) { - Curve *curve = static_cast(m_object->data); + Curve *curve = static_cast(context.object->data); std::vector verts; std::vector vert_counts; @@ -152,35 +161,31 @@ void AbcCurveWriter::do_write() Alembic::AbcGeom::OFloatGeomParam::Sample width_sample; width_sample.setVals(widths); - m_sample = OCurvesSchema::Sample(verts, - vert_counts, - curve_type, - periodicity, - width_sample, - OV2fGeomParam::Sample(), /* UVs */ - ON3fGeomParam::Sample(), /* normals */ - curve_basis, - weights, - orders, - knots); - - m_sample.setSelfBounds(bounds()); - m_schema.set(m_sample); + OCurvesSchema::Sample sample(verts, + vert_counts, + curve_type, + periodicity, + width_sample, + OV2fGeomParam::Sample(), /* UVs */ + ON3fGeomParam::Sample(), /* normals */ + curve_basis, + weights, + orders, + knots); + + update_bounding_box(context.object); + sample.setSelfBounds(bounding_box_); + abc_curve_schema_.set(sample); } -AbcCurveMeshWriter::AbcCurveMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcGenericMeshWriter(ob, parent, time_sampling, settings) +ABCCurveMeshWriter::ABCCurveMeshWriter(const ABCWriterConstructorArgs &args) + : ABCGenericMeshWriter(args) { } -Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/, - Object *ob_eval, - bool &r_needsfree) +Mesh *ABCCurveMeshWriter::get_export_mesh(Object *object_eval, bool &r_needsfree) { - Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); + Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object_eval); if (mesh_eval != NULL) { /* Mesh_eval only exists when generative modifiers are in use. */ r_needsfree = false; @@ -188,7 +193,7 @@ Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/, } r_needsfree = true; - return BKE_mesh_new_nomain_from_curve(ob_eval); + return BKE_mesh_new_nomain_from_curve(object_eval); } } // namespace alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.h b/source/blender/io/alembic/exporter/abc_writer_curves.h index d12ebc46a22..12a909761f5 100644 --- a/source/blender/io/alembic/exporter/abc_writer_curves.h +++ b/source/blender/io/alembic/exporter/abc_writer_curves.h @@ -22,36 +22,38 @@ * \ingroup balembic */ +#include "abc_writer_abstract.h" #include "abc_writer_mesh.h" -#include "abc_writer_object.h" + +#include namespace blender { namespace io { namespace alembic { -class AbcCurveWriter : public AbcObjectWriter { - Alembic::AbcGeom::OCurvesSchema m_schema; - Alembic::AbcGeom::OCurvesSchema::Sample m_sample; +extern const std::string ABC_CURVE_RESOLUTION_U_PROPNAME; + +class ABCCurveWriter : public ABCAbstractWriter { + private: + Alembic::AbcGeom::OCurves abc_curve_; + Alembic::AbcGeom::OCurvesSchema abc_curve_schema_; public: - AbcCurveWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); + explicit ABCCurveWriter(const ABCWriterConstructorArgs &args); + + virtual void create_alembic_objects(const HierarchyContext *context) override; + virtual const Alembic::Abc::OObject get_alembic_object() const override; protected: - void do_write(); + virtual void do_write(HierarchyContext &context) override; }; -class AbcCurveMeshWriter : public AbcGenericMeshWriter { +class ABCCurveMeshWriter : public ABCGenericMeshWriter { public: - AbcCurveMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); + ABCCurveMeshWriter(const ABCWriterConstructorArgs &args); protected: - Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree); + virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override; }; } // namespace alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.cc b/source/blender/io/alembic/exporter/abc_writer_hair.cc index 304aa7a5ff8..ac4deddd9b4 100644 --- a/source/blender/io/alembic/exporter/abc_writer_hair.cc +++ b/source/blender/io/alembic/exporter/abc_writer_hair.cc @@ -19,7 +19,6 @@ */ #include "abc_writer_hair.h" -#include "abc_writer_transform.h" #include "intern/abc_axis_conversion.h" #include @@ -35,40 +34,46 @@ #include "BKE_mesh_runtime.h" #include "BKE_particle.h" -using Alembic::Abc::P3fArraySamplePtr; +#include "CLG_log.h" +static CLG_LogRef LOG = {"io.alembic"}; +using Alembic::Abc::P3fArraySamplePtr; using Alembic::AbcGeom::OCurves; using Alembic::AbcGeom::OCurvesSchema; using Alembic::AbcGeom::ON3fGeomParam; using Alembic::AbcGeom::OV2fGeomParam; -/* ************************************************************************** */ - namespace blender { namespace io { namespace alembic { -AbcHairWriter::AbcHairWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings, - ParticleSystem *psys) - : AbcObjectWriter(ob, time_sampling, settings, parent), m_uv_warning_shown(false) +ABCHairWriter::ABCHairWriter(const ABCWriterConstructorArgs &args) + : ABCAbstractWriter(args), uv_warning_shown_(false) { - m_psys = psys; +} - std::string psys_name = get_valid_abc_name(psys->name); - OCurves curves(parent->alembicXform(), psys_name, m_time_sampling); - m_schema = curves.getSchema(); +void ABCHairWriter::create_alembic_objects(const HierarchyContext * /*context*/) +{ + CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str()); + abc_curves_ = OCurves(args_.abc_parent, args_.abc_name, timesample_index_); + abc_curves_schema_ = abc_curves_.getSchema(); } -void AbcHairWriter::do_write() +const Alembic::Abc::OObject ABCHairWriter::get_alembic_object() const { - if (!m_psys) { - return; - } - Mesh *mesh = mesh_get_eval_final( - m_settings.depsgraph, m_settings.scene, m_object, &CD_MASK_MESH); + return abc_curves_; +} + +bool ABCHairWriter::check_is_animated(const HierarchyContext & /*context*/) const +{ + /* We assume that hair particles are always animated. */ + return true; +} + +void ABCHairWriter::do_write(HierarchyContext &context) +{ + Scene *scene_eval = DEG_get_evaluated_scene(args_.depsgraph); + Mesh *mesh = mesh_get_eval_final(args_.depsgraph, scene_eval, context.object, &CD_MASK_MESH); BKE_mesh_tessface_ensure(mesh); std::vector verts; @@ -76,44 +81,45 @@ void AbcHairWriter::do_write() std::vector uv_values; std::vector norm_values; - if (m_psys->pathcache) { - ParticleSettings *part = m_psys->part; - bool export_children = m_settings.export_child_hairs && m_psys->childcache && - part->childtype != 0; + ParticleSystem *psys = context.particle_system; + if (psys->pathcache) { + ParticleSettings *part = psys->part; + bool export_children = psys->childcache && part->childtype != 0; if (!export_children || part->draw & PART_DRAW_PARENT) { - write_hair_sample(mesh, part, verts, norm_values, uv_values, hvertices); + write_hair_sample(context, mesh, verts, norm_values, uv_values, hvertices); } if (export_children) { - write_hair_child_sample(mesh, part, verts, norm_values, uv_values, hvertices); + write_hair_child_sample(context, mesh, verts, norm_values, uv_values, hvertices); } } Alembic::Abc::P3fArraySample iPos(verts); - m_sample = OCurvesSchema::Sample(iPos, hvertices); - m_sample.setBasis(Alembic::AbcGeom::kNoBasis); - m_sample.setType(Alembic::AbcGeom::kLinear); - m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic); + OCurvesSchema::Sample sample(iPos, hvertices); + sample.setBasis(Alembic::AbcGeom::kNoBasis); + sample.setType(Alembic::AbcGeom::kLinear); + sample.setWrap(Alembic::AbcGeom::kNonPeriodic); if (!uv_values.empty()) { OV2fGeomParam::Sample uv_smp; uv_smp.setVals(uv_values); - m_sample.setUVs(uv_smp); + sample.setUVs(uv_smp); } if (!norm_values.empty()) { ON3fGeomParam::Sample norm_smp; norm_smp.setVals(norm_values); - m_sample.setNormals(norm_smp); + sample.setNormals(norm_smp); } - m_sample.setSelfBounds(bounds()); - m_schema.set(m_sample); + update_bounding_box(context.object); + sample.setSelfBounds(bounding_box_); + abc_curves_schema_.set(sample); } -void AbcHairWriter::write_hair_sample(Mesh *mesh, - ParticleSettings *part, +void ABCHairWriter::write_hair_sample(const HierarchyContext &context, + Mesh *mesh, std::vector &verts, std::vector &norm_values, std::vector &uv_values, @@ -121,28 +127,30 @@ void AbcHairWriter::write_hair_sample(Mesh *mesh, { /* Get untransformed vertices, there's a xform under the hair. */ float inv_mat[4][4]; - invert_m4_m4_safe(inv_mat, m_object->obmat); + invert_m4_m4_safe(inv_mat, context.object->obmat); MTFace *mtface = mesh->mtface; MFace *mface = mesh->mface; MVert *mverts = mesh->mvert; - if ((!mtface || !mface) && !m_uv_warning_shown) { + if ((!mtface || !mface) && !uv_warning_shown_) { std::fprintf(stderr, "Warning, no UV set found for underlying geometry of %s.\n", - m_object->id.name + 2); - m_uv_warning_shown = true; + context.object->id.name + 2); + uv_warning_shown_ = true; } - ParticleData *pa = m_psys->particles; + ParticleSystem *psys = context.particle_system; + ParticleSettings *part = psys->part; + ParticleData *pa = psys->particles; int k; - ParticleCacheKey **cache = m_psys->pathcache; + ParticleCacheKey **cache = psys->pathcache; ParticleCacheKey *path; float normal[3]; Imath::V3f tmp_nor; - for (int p = 0; p < m_psys->totpart; p++, pa++) { + for (int p = 0; p < psys->totpart; p++, pa++) { /* underlying info for faces-only emission */ path = cache[p]; @@ -224,8 +232,8 @@ void AbcHairWriter::write_hair_sample(Mesh *mesh, } } -void AbcHairWriter::write_hair_child_sample(Mesh *mesh, - ParticleSettings *part, +void ABCHairWriter::write_hair_child_sample(const HierarchyContext &context, + Mesh *mesh, std::vector &verts, std::vector &norm_values, std::vector &uv_values, @@ -233,26 +241,30 @@ void AbcHairWriter::write_hair_child_sample(Mesh *mesh, { /* Get untransformed vertices, there's a xform under the hair. */ float inv_mat[4][4]; - invert_m4_m4_safe(inv_mat, m_object->obmat); + invert_m4_m4_safe(inv_mat, context.object->obmat); MTFace *mtface = mesh->mtface; MVert *mverts = mesh->mvert; - ParticleCacheKey **cache = m_psys->childcache; + ParticleSystem *psys = context.particle_system; + ParticleSettings *part = psys->part; + ParticleCacheKey **cache = psys->childcache; ParticleCacheKey *path; - ChildParticle *pc = m_psys->child; + ChildParticle *pc = psys->child; - for (int p = 0; p < m_psys->totchild; p++, pc++) { + for (int p = 0; p < psys->totchild; p++, pc++) { path = cache[p]; if (part->from == PART_FROM_FACE && part->childtype != PART_CHILD_PARTICLES && mtface) { const int num = pc->num; if (num < 0) { - ABC_LOG(m_settings.logger) - << "Warning, child particle of hair system " << m_psys->name - << " has unknown face index of geometry of " << (m_object->id.name + 2) - << ", skipping child hair." << std::endl; + CLOG_WARN( + &LOG, + "Child particle of hair system %s has unknown face index of geometry of %s, skipping " + "child hair.", + psys->name, + context.object->id.name + 2); continue; } diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.h b/source/blender/io/alembic/exporter/abc_writer_hair.h index d7831684a12..af1372a08f3 100644 --- a/source/blender/io/alembic/exporter/abc_writer_hair.h +++ b/source/blender/io/alembic/exporter/abc_writer_hair.h @@ -19,9 +19,10 @@ * \ingroup balembic */ -#include "abc_writer_object.h" +#include "abc_writer_abstract.h" +#include +#include -struct Mesh; struct ParticleSettings; struct ParticleSystem; @@ -29,33 +30,33 @@ namespace blender { namespace io { namespace alembic { -class AbcHairWriter : public AbcObjectWriter { - ParticleSystem *m_psys; - - Alembic::AbcGeom::OCurvesSchema m_schema; - Alembic::AbcGeom::OCurvesSchema::Sample m_sample; +class ABCHairWriter : public ABCAbstractWriter { + private: + Alembic::AbcGeom::OCurves abc_curves_; + Alembic::AbcGeom::OCurvesSchema abc_curves_schema_; - bool m_uv_warning_shown; + bool uv_warning_shown_; public: - AbcHairWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings, - ParticleSystem *psys); + explicit ABCHairWriter(const ABCWriterConstructorArgs &args); - private: - virtual void do_write(); + virtual void create_alembic_objects(const HierarchyContext *context) override; + virtual const Alembic::Abc::OObject get_alembic_object() const override; - void write_hair_sample(struct Mesh *mesh, - ParticleSettings *part, + protected: + virtual void do_write(HierarchyContext &context) override; + virtual bool check_is_animated(const HierarchyContext &context) const override; + + private: + void write_hair_sample(const HierarchyContext &context, + struct Mesh *mesh, std::vector &verts, std::vector &norm_values, std::vector &uv_values, std::vector &hvertices); - void write_hair_child_sample(struct Mesh *mesh, - ParticleSettings *part, + void write_hair_child_sample(const HierarchyContext &context, + struct Mesh *mesh, std::vector &verts, std::vector &norm_values, std::vector &uv_values, diff --git a/source/blender/io/alembic/exporter/abc_writer_mball.cc b/source/blender/io/alembic/exporter/abc_writer_mball.cc index 8ae3bb64390..167e392eb96 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mball.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mball.cc @@ -19,11 +19,9 @@ */ #include "abc_writer_mball.h" -#include "abc_writer_mesh.h" +#include "abc_hierarchy_iterator.h" -#include "DNA_mesh_types.h" -#include "DNA_meta_types.h" -#include "DNA_object_types.h" +#include "BLI_assert.h" #include "BKE_displist.h" #include "BKE_lib_id.h" @@ -31,68 +29,57 @@ #include "BKE_mesh.h" #include "BKE_object.h" -#include "BLI_utildefines.h" +#include "DNA_mesh_types.h" +#include "DNA_meta_types.h" namespace blender { namespace io { namespace alembic { -AbcMBallWriter::AbcMBallWriter(Main *bmain, - Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcGenericMeshWriter(ob, parent, time_sampling, settings), m_bmain(bmain) +ABCMetaballWriter::ABCMetaballWriter(const ABCWriterConstructorArgs &args) + : ABCGenericMeshWriter(args) { - m_is_animated = isAnimated(); } -AbcMBallWriter::~AbcMBallWriter() +bool ABCMetaballWriter::is_supported(const HierarchyContext *context) const { + Scene *scene = DEG_get_input_scene(args_.depsgraph); + bool supported = is_basis_ball(scene, context->object) && + ABCGenericMeshWriter::is_supported(context); + return supported; } -bool AbcMBallWriter::isAnimated() const +bool ABCMetaballWriter::check_is_animated(const HierarchyContext & /*context*/) const { + /* We assume that metaballs are always animated, as the current object may + * not be animated but another ball in the same group may be. */ return true; } -Mesh *AbcMBallWriter::getEvaluatedMesh(Scene * /*scene_eval*/, Object *ob_eval, bool &r_needsfree) +bool ABCMetaballWriter::export_as_subdivision_surface(Object * /*ob_eval*/) const { - Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); - if (mesh_eval != NULL) { + /* Metaballs should be exported to subdivision surfaces, if the export options allow. */ + return true; +} + +Mesh *ABCMetaballWriter::get_export_mesh(Object *object_eval, bool &r_needsfree) +{ + Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object_eval); + if (mesh_eval != nullptr) { /* Mesh_eval only exists when generative modifiers are in use. */ r_needsfree = false; return mesh_eval; } r_needsfree = true; - - /* The approach below is copied from BKE_mesh_new_from_object() */ - Mesh *tmpmesh = BKE_mesh_add(m_bmain, ((ID *)m_object->data)->name + 2); - BLI_assert(tmpmesh != NULL); - - /* BKE_mesh_add gives us a user count we don't need */ - id_us_min(&tmpmesh->id); - - ListBase disp = {NULL, NULL}; - /* TODO(sergey): This is gonna to work for until Depsgraph - * only contains for_render flag. As soon as CoW is - * implemented, this is to be rethought. - */ - BKE_displist_make_mball_forRender(m_settings.depsgraph, m_settings.scene, m_object, &disp); - BKE_mesh_from_metaball(&disp, tmpmesh); - BKE_displist_free(&disp); - - BKE_mesh_texspace_copy_from_object(tmpmesh, m_object); - - return tmpmesh; + return BKE_mesh_new_from_object(args_.depsgraph, object_eval, false); } -void AbcMBallWriter::freeEvaluatedMesh(struct Mesh *mesh) +void ABCMetaballWriter::free_export_mesh(Mesh *mesh) { - BKE_id_free(m_bmain, mesh); + BKE_id_free(nullptr, mesh); } -bool AbcMBallWriter::isBasisBall(Scene *scene, Object *ob) +bool ABCMetaballWriter::is_basis_ball(Scene *scene, Object *ob) const { Object *basis_ob = BKE_mball_basis_find(scene, ob); return ob == basis_ob; diff --git a/source/blender/io/alembic/exporter/abc_writer_mball.h b/source/blender/io/alembic/exporter/abc_writer_mball.h index d632f0bd410..90d8c4d4b15 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mball.h +++ b/source/blender/io/alembic/exporter/abc_writer_mball.h @@ -20,39 +20,24 @@ */ #include "abc_writer_mesh.h" -#include "abc_writer_object.h" - -struct Main; -struct Object; namespace blender { namespace io { namespace alembic { -/* AbcMBallWriter converts the metaballs to meshes at every frame, - * and defers to AbcGenericMeshWriter to perform the writing - * to the Alembic file. Only the basis balls are exported, as this - * results in the entire shape as one mesh. */ -class AbcMBallWriter : public AbcGenericMeshWriter { - Main *m_bmain; - +class ABCMetaballWriter : public ABCGenericMeshWriter { public: - explicit AbcMBallWriter(Main *bmain, - Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - ~AbcMBallWriter(); - - static bool isBasisBall(Scene *scene, Object *ob); + explicit ABCMetaballWriter(const ABCWriterConstructorArgs &args); protected: - Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override; - void freeEvaluatedMesh(struct Mesh *mesh) override; + virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override; + virtual void free_export_mesh(Mesh *mesh) override; + virtual bool is_supported(const HierarchyContext *context) const override; + virtual bool check_is_animated(const HierarchyContext &context) const override; + virtual bool export_as_subdivision_surface(Object *ob_eval) const override; private: - bool isAnimated() const override; + bool is_basis_ball(Scene *scene, Object *ob) const; }; } // namespace alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index 512768bcd98..07196f2b81f 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -19,29 +19,37 @@ */ #include "abc_writer_mesh.h" -#include "abc_writer_transform.h" +#include "abc_hierarchy_iterator.h" #include "intern/abc_axis_conversion.h" -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_fluidsim_types.h" +#include "BLI_assert.h" +#include "BLI_math_vector.h" -#include "BKE_anim_data.h" -#include "BKE_key.h" +#include "BKE_customdata.h" #include "BKE_lib_id.h" #include "BKE_material.h" #include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" #include "BKE_modifier.h" +#include "BKE_object.h" #include "bmesh.h" #include "bmesh_tools.h" -#include "DEG_depsgraph_query.h" +#include "DEG_depsgraph.h" + +#include "DNA_layer_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_fluidsim_types.h" +#include "DNA_particle_types.h" + +#include "CLG_log.h" +static CLG_LogRef LOG = {"io.alembic"}; using Alembic::Abc::FloatArraySample; using Alembic::Abc::Int32ArraySample; +using Alembic::Abc::OObject; using Alembic::Abc::V2fArraySample; using Alembic::Abc::V3fArraySample; @@ -64,136 +72,77 @@ namespace alembic { /* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */ -static void get_vertices(struct Mesh *mesh, std::vector &points) -{ - points.clear(); - points.resize(mesh->totvert); - - MVert *verts = mesh->mvert; - - for (int i = 0, e = mesh->totvert; i < e; i++) { - copy_yup_from_zup(points[i].getValue(), verts[i].co); - } -} - +static void get_vertices(struct Mesh *mesh, std::vector &points); static void get_topology(struct Mesh *mesh, std::vector &poly_verts, std::vector &loop_counts, - bool &r_has_flat_shaded_poly) -{ - const int num_poly = mesh->totpoly; - const int num_loops = mesh->totloop; - MLoop *mloop = mesh->mloop; - MPoly *mpoly = mesh->mpoly; - r_has_flat_shaded_poly = false; - - poly_verts.clear(); - loop_counts.clear(); - poly_verts.reserve(num_loops); - loop_counts.reserve(num_poly); - - /* NOTE: data needs to be written in the reverse order. */ - for (int i = 0; i < num_poly; i++) { - MPoly &poly = mpoly[i]; - loop_counts.push_back(poly.totloop); - - r_has_flat_shaded_poly |= (poly.flag & ME_SMOOTH) == 0; - - MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1); - - for (int j = 0; j < poly.totloop; j++, loop--) { - poly_verts.push_back(loop->v); - } - } -} - + bool &r_has_flat_shaded_poly); static void get_creases(struct Mesh *mesh, std::vector &indices, std::vector &lengths, - std::vector &sharpnesses) -{ - const float factor = 1.0f / 255.0f; + std::vector &sharpnesses); +static void get_loop_normals(struct Mesh *mesh, + std::vector &normals, + bool has_flat_shaded_poly); - indices.clear(); - lengths.clear(); - sharpnesses.clear(); +ABCGenericMeshWriter::ABCGenericMeshWriter(const ABCWriterConstructorArgs &args) + : ABCAbstractWriter(args), is_subd_(false) +{ +} - MEdge *edge = mesh->medge; +void ABCGenericMeshWriter::create_alembic_objects(const HierarchyContext *context) +{ + if (!args_.export_params->apply_subdiv && export_as_subdivision_surface(context->object)) { + is_subd_ = args_.export_params->use_subdiv_schema; + } - for (int i = 0, e = mesh->totedge; i < e; i++) { - const float sharpness = static_cast(edge[i].crease) * factor; + if (is_subd_) { + CLOG_INFO(&LOG, 2, "exporting OSubD %s", args_.abc_path.c_str()); + abc_subdiv_ = OSubD(args_.abc_parent, args_.abc_name, timesample_index_); + abc_subdiv_schema_ = abc_subdiv_.getSchema(); + } + else { + CLOG_INFO(&LOG, 2, "exporting OPolyMesh %s", args_.abc_path.c_str()); + abc_poly_mesh_ = OPolyMesh(args_.abc_parent, args_.abc_name, timesample_index_); + abc_poly_mesh_schema_ = abc_poly_mesh_.getSchema(); - if (sharpness != 0.0f) { - indices.push_back(edge[i].v1); - indices.push_back(edge[i].v2); - sharpnesses.push_back(sharpness); - } + OCompoundProperty typeContainer = abc_poly_mesh_.getSchema().getUserProperties(); + OBoolProperty type(typeContainer, "meshtype"); + type.set(subsurf_modifier_ == nullptr); } - lengths.resize(sharpnesses.size(), 2); + Scene *scene_eval = DEG_get_evaluated_scene(args_.depsgraph); + liquid_sim_modifier_ = get_liquid_sim_modifier(scene_eval, context->object); } -static void get_loop_normals(struct Mesh *mesh, - std::vector &normals, - bool has_flat_shaded_poly) +ABCGenericMeshWriter::~ABCGenericMeshWriter() { - normals.clear(); - - /* If all polygons are smooth shaded, and there are no custom normals, we don't need to export - * normals at all. This is also done by other software, see T71246. */ - if (!has_flat_shaded_poly && !CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL) && - (mesh->flag & ME_AUTOSMOOTH) == 0) { - return; - } - - BKE_mesh_calc_normals_split(mesh); - const float(*lnors)[3] = static_cast(CustomData_get_layer(&mesh->ldata, CD_NORMAL)); - BLI_assert(lnors != NULL || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL"); - - normals.resize(mesh->totloop); +} - /* NOTE: data needs to be written in the reverse order. */ - int abc_index = 0; - MPoly *mp = mesh->mpoly; - for (int i = 0, e = mesh->totpoly; i < e; i++, mp++) { - for (int j = mp->totloop - 1; j >= 0; j--, abc_index++) { - int blender_index = mp->loopstart + j; - copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]); - } +const Alembic::Abc::OObject ABCGenericMeshWriter::get_alembic_object() const +{ + if (is_subd_) { + return abc_subdiv_; } + return abc_poly_mesh_; } -/* *************** Modifiers *************** */ - -/* check if the mesh is a subsurf, ignoring disabled modifiers and - * displace if it's after subsurf. */ -static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob) +bool ABCGenericMeshWriter::export_as_subdivision_surface(Object *ob_eval) const { - ModifierData *md = static_cast(ob->modifiers.last); + ModifierData *md = static_cast(ob_eval->modifiers.last); for (; md; md = md->prev) { - if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) { - continue; - } - - if (md->type == eModifierType_Subsurf) { - SubsurfModifierData *smd = reinterpret_cast(md); - - if (smd->subdivType == ME_CC_SUBSURF) { - return md; - } - } - - /* mesh is not a subsurf. break */ - if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) { - return NULL; + /* This modifier has been temporarily disabled by SubdivModifierDisabler, + * so this indicates this is to be exported as subdivision surface. */ + if (md->type == eModifierType_Subsurf && (md->mode & eModifierMode_DisableTemporary)) { + return true; } } - return NULL; + return false; } -static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob) +ModifierData *ABCGenericMeshWriter::get_liquid_sim_modifier(Scene *scene, Object *ob) { ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluidsim); @@ -205,122 +154,99 @@ static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob) } } - return NULL; + return nullptr; } -/* ************************************************************************** */ - -AbcGenericMeshWriter::AbcGenericMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent) +bool ABCGenericMeshWriter::is_supported(const HierarchyContext *context) const { - m_is_animated = isAnimated(); - m_subsurf_mod = NULL; - m_is_subd = false; - - /* If the object is static, use the default static time sampling. */ - if (!m_is_animated) { - time_sampling = 0; - } + Object *object = context->object; + bool is_dupli = context->duplicator != nullptr; + int base_flag; - if (!m_settings.apply_subdiv) { - m_subsurf_mod = get_subsurf_modifier(m_settings.scene, m_object); - m_is_subd = (m_subsurf_mod != NULL); + if (is_dupli) { + /* Construct the object's base flags from its dupli-parent, just like is done in + * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing + * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents + * copying the Object for every dupli. */ + base_flag = object->base_flag; + object->base_flag = context->duplicator->base_flag | BASE_FROM_DUPLI; } - m_is_liquid = (get_liquid_sim_modifier(m_settings.scene, m_object) != NULL); + int visibility = BKE_object_visibility( + object, DAG_EVAL_RENDER /* TODO(Sybren): add evaluation mode to export options? */); - while (parent->alembicXform().getChildHeader(m_name)) { - m_name.append("_"); + if (is_dupli) { + object->base_flag = base_flag; } - if (m_settings.use_subdiv_schema && m_is_subd) { - OSubD subd(parent->alembicXform(), m_name, m_time_sampling); - m_subdiv_schema = subd.getSchema(); - } - else { - OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling); - m_mesh_schema = mesh.getSchema(); - - OCompoundProperty typeContainer = m_mesh_schema.getUserProperties(); - OBoolProperty type(typeContainer, "meshtype"); - type.set(m_is_subd); - } + return (visibility & OB_VISIBLE_SELF) != 0; } -AbcGenericMeshWriter::~AbcGenericMeshWriter() +void ABCGenericMeshWriter::do_write(HierarchyContext &context) { - if (m_subsurf_mod) { - m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; - } -} + Object *object = context.object; + bool needsfree = false; -bool AbcGenericMeshWriter::isAnimated() const -{ - if (BKE_animdata_id_is_animated(static_cast(m_object->data))) { - return true; - } - if (BKE_key_from_object(m_object) != NULL) { - return true; - } + Mesh *mesh = get_export_mesh(object, needsfree); - /* Test modifiers. */ - ModifierData *md = static_cast(m_object->modifiers.first); - while (md) { + if (mesh == nullptr) { + return; + } - if (md->type != eModifierType_Subsurf) { - return true; - } + if (args_.export_params->triangulate) { + const bool tag_only = false; + const int quad_method = args_.export_params->quad_method; + const int ngon_method = args_.export_params->ngon_method; - md = md->next; - } + struct BMeshCreateParams bmcp = {false}; + struct BMeshFromMeshParams bmfmp = {true, false, false, 0}; + BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmcp, &bmfmp); - return false; -} + BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, nullptr, nullptr, nullptr); -void AbcGenericMeshWriter::setIsAnimated(bool is_animated) -{ - m_is_animated = is_animated; -} + Mesh *triangulated_mesh = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh); + BM_mesh_free(bm); -void AbcGenericMeshWriter::do_write() -{ - /* We have already stored a sample for this object. */ - if (!m_first_frame && !m_is_animated) { - return; + if (needsfree) { + free_export_mesh(mesh); + } + mesh = triangulated_mesh; + needsfree = true; } - bool needsfree; - struct Mesh *mesh = getFinalMesh(needsfree); + m_custom_data_config.pack_uvs = args_.export_params->packuv; + m_custom_data_config.mpoly = mesh->mpoly; + m_custom_data_config.mloop = mesh->mloop; + m_custom_data_config.totpoly = mesh->totpoly; + m_custom_data_config.totloop = mesh->totloop; + m_custom_data_config.totvert = mesh->totvert; try { - if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) { - writeSubD(mesh); + if (is_subd_) { + write_subd(context, mesh); } else { - writeMesh(mesh); + write_mesh(context, mesh); } if (needsfree) { - freeEvaluatedMesh(mesh); + free_export_mesh(mesh); } } catch (...) { if (needsfree) { - freeEvaluatedMesh(mesh); + free_export_mesh(mesh); } throw; } } -void AbcGenericMeshWriter::freeEvaluatedMesh(struct Mesh *mesh) +void ABCGenericMeshWriter::free_export_mesh(Mesh *mesh) { - BKE_id_free(NULL, mesh); + BKE_id_free(nullptr, mesh); } -void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh) +void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) { std::vector points, normals; std::vector poly_verts, loop_counts; @@ -330,32 +256,33 @@ void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh) get_vertices(mesh, points); get_topology(mesh, poly_verts, loop_counts, has_flat_shaded_poly); - if (m_first_frame && m_settings.export_face_sets) { - writeFaceSets(mesh, m_mesh_schema); + if (!frame_has_been_written_ && args_.export_params->face_sets) { + write_face_sets(context.object, mesh, abc_poly_mesh_schema_); } - m_mesh_sample = OPolyMeshSchema::Sample( + OPolyMeshSchema::Sample mesh_sample = OPolyMeshSchema::Sample( V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts)); - UVSample sample; - if (m_settings.export_uvs) { - const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata); + UVSample uvs_and_indices; - if (!sample.indices.empty() && !sample.uvs.empty()) { + if (!frame_has_been_written_ && args_.export_params->uvs) { + const char *name = get_uv_sample(uvs_and_indices, m_custom_data_config, &mesh->ldata); + + if (!uvs_and_indices.indices.empty() && !uvs_and_indices.uvs.empty()) { OV2fGeomParam::Sample uv_sample; - uv_sample.setVals(V2fArraySample(sample.uvs)); - uv_sample.setIndices(UInt32ArraySample(sample.indices)); + uv_sample.setVals(V2fArraySample(uvs_and_indices.uvs)); + uv_sample.setIndices(UInt32ArraySample(uvs_and_indices.indices)); uv_sample.setScope(kFacevaryingScope); - m_mesh_schema.setUVSourceName(name); - m_mesh_sample.setUVs(uv_sample); + abc_poly_mesh_schema_.setUVSourceName(name); + mesh_sample.setUVs(uv_sample); } write_custom_data( - m_mesh_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV); + abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV); } - if (m_settings.export_normals) { + if (args_.export_params->normals) { get_loop_normals(mesh, normals, has_flat_shaded_poly); ON3fGeomParam::Sample normals_sample; @@ -364,22 +291,23 @@ void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh) normals_sample.setVals(V3fArraySample(normals)); } - m_mesh_sample.setNormals(normals_sample); + mesh_sample.setNormals(normals_sample); } - if (m_is_liquid) { - getVelocities(mesh, velocities); - m_mesh_sample.setVelocities(V3fArraySample(velocities)); + if (liquid_sim_modifier_ != nullptr) { + get_velocities(mesh, velocities); + mesh_sample.setVelocities(V3fArraySample(velocities)); } - m_mesh_sample.setSelfBounds(bounds()); + update_bounding_box(context.object); + mesh_sample.setSelfBounds(bounding_box_); - m_mesh_schema.set(m_mesh_sample); + abc_poly_mesh_schema_.set(mesh_sample); - writeArbGeoParams(mesh); + write_arb_geo_params(mesh); } -void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh) +void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *mesh) { std::vector crease_sharpness; std::vector points; @@ -391,15 +319,15 @@ void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh) get_topology(mesh, poly_verts, loop_counts, has_flat_poly); get_creases(mesh, crease_indices, crease_lengths, crease_sharpness); - if (m_first_frame && m_settings.export_face_sets) { - writeFaceSets(mesh, m_subdiv_schema); + if (!frame_has_been_written_ && args_.export_params->face_sets) { + write_face_sets(context.object, mesh, abc_subdiv_schema_); } - m_subdiv_sample = OSubDSchema::Sample( + OSubDSchema::Sample subdiv_sample = OSubDSchema::Sample( V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts)); UVSample sample; - if (m_first_frame && m_settings.export_uvs) { + if (!frame_has_been_written_ && args_.export_params->uvs) { const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata); if (!sample.indices.empty() && !sample.uvs.empty()) { @@ -408,30 +336,32 @@ void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh) uv_sample.setIndices(UInt32ArraySample(sample.indices)); uv_sample.setScope(kFacevaryingScope); - m_subdiv_schema.setUVSourceName(name); - m_subdiv_sample.setUVs(uv_sample); + abc_subdiv_schema_.setUVSourceName(name); + subdiv_sample.setUVs(uv_sample); } write_custom_data( - m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV); + abc_subdiv_schema_.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV); } if (!crease_indices.empty()) { - m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices)); - m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths)); - m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness)); + subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices)); + subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths)); + subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness)); } - m_subdiv_sample.setSelfBounds(bounds()); - m_subdiv_schema.set(m_subdiv_sample); + update_bounding_box(context.object); + subdiv_sample.setSelfBounds(bounding_box_); + abc_subdiv_schema_.set(subdiv_sample); - writeArbGeoParams(mesh); + write_arb_geo_params(mesh); } -template void AbcGenericMeshWriter::writeFaceSets(struct Mesh *me, Schema &schema) +template +void ABCGenericMeshWriter::write_face_sets(Object *object, struct Mesh *mesh, Schema &schema) { std::map> geo_groups; - getGeoGroups(me, geo_groups); + get_geo_groups(object, mesh, geo_groups); std::map>::iterator it; for (it = geo_groups.begin(); it != geo_groups.end(); ++it) { @@ -442,83 +372,35 @@ template void AbcGenericMeshWriter::writeFaceSets(struct Mesh * } } -Mesh *AbcGenericMeshWriter::getFinalMesh(bool &r_needsfree) +void ABCGenericMeshWriter::write_arb_geo_params(struct Mesh *me) { - /* We don't want subdivided mesh data */ - if (m_subsurf_mod) { - m_subsurf_mod->mode |= eModifierMode_DisableTemporary; - } - - r_needsfree = false; - - Scene *scene = DEG_get_evaluated_scene(m_settings.depsgraph); - Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object); - struct Mesh *mesh = getEvaluatedMesh(scene, ob_eval, r_needsfree); - - if (m_subsurf_mod) { - m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; - } - - if (m_settings.triangulate) { - const bool tag_only = false; - const int quad_method = m_settings.quad_method; - const int ngon_method = m_settings.ngon_method; - - struct BMeshCreateParams bmcp = {false}; - struct BMeshFromMeshParams bmfmp = {true, false, false, 0}; - BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmcp, &bmfmp); - - BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, NULL, NULL, NULL); - - Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); - BM_mesh_free(bm); - - if (r_needsfree) { - BKE_id_free(NULL, mesh); - } - - mesh = result; - r_needsfree = true; + if (liquid_sim_modifier_ != nullptr) { + /* We don't need anything more for liquid meshes. */ + return; } - m_custom_data_config.pack_uvs = m_settings.pack_uv; - m_custom_data_config.mpoly = mesh->mpoly; - m_custom_data_config.mloop = mesh->mloop; - m_custom_data_config.totpoly = mesh->totpoly; - m_custom_data_config.totloop = mesh->totloop; - m_custom_data_config.totvert = mesh->totvert; - - return mesh; -} - -void AbcGenericMeshWriter::writeArbGeoParams(struct Mesh *me) -{ - if (m_is_liquid) { - /* We don't need anything more for liquid meshes. */ + if (frame_has_been_written_ || !args_.export_params->vcolors) { return; } - if (m_first_frame && m_settings.export_vcols) { - if (m_subdiv_schema.valid()) { - write_custom_data( - m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL); - } - else { - write_custom_data( - m_mesh_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL); - } + OCompoundProperty arb_geom_params; + if (is_subd_) { + arb_geom_params = abc_subdiv_.getSchema().getArbGeomParams(); + } + else { + arb_geom_params = abc_poly_mesh_.getSchema().getArbGeomParams(); } + write_custom_data(arb_geom_params, m_custom_data_config, &me->ldata, CD_MLOOPCOL); } -void AbcGenericMeshWriter::getVelocities(struct Mesh *mesh, std::vector &vels) +void ABCGenericMeshWriter::get_velocities(struct Mesh *mesh, std::vector &vels) { const int totverts = mesh->totvert; vels.clear(); vels.resize(totverts); - ModifierData *md = get_liquid_sim_modifier(m_settings.scene, m_object); - FluidsimModifierData *fmd = reinterpret_cast(md); + FluidsimModifierData *fmd = reinterpret_cast(liquid_sim_modifier_); FluidsimSettings *fss = fmd->fss; if (fss->meshVelocities) { @@ -534,8 +416,9 @@ void AbcGenericMeshWriter::getVelocities(struct Mesh *mesh, std::vector> &geo_groups) +void ABCGenericMeshWriter::get_geo_groups(Object *object, + struct Mesh *mesh, + std::map> &geo_groups) { const int num_poly = mesh->totpoly; MPoly *polygons = mesh->mpoly; @@ -544,13 +427,13 @@ void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh, MPoly ¤t_poly = polygons[i]; short mnr = current_poly.mat_nr; - Material *mat = BKE_object_material_get(m_object, mnr + 1); + Material *mat = BKE_object_material_get(object, mnr + 1); if (!mat) { continue; } - std::string name = get_id_name(&mat->id); + std::string name = args_.hierarchy_iterator->get_id_name(&mat->id); if (geo_groups.find(name) == geo_groups.end()) { std::vector faceArray; @@ -561,9 +444,9 @@ void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh, } if (geo_groups.size() == 0) { - Material *mat = BKE_object_material_get(m_object, 1); + Material *mat = BKE_object_material_get(object, 1); - std::string name = (mat) ? get_id_name(&mat->id) : "default"; + std::string name = (mat) ? args_.hierarchy_iterator->get_id_name(&mat->id) : "default"; std::vector faceArray; @@ -575,23 +458,114 @@ void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh, } } -AbcMeshWriter::AbcMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcGenericMeshWriter(ob, parent, time_sampling, settings) +/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */ + +static void get_vertices(struct Mesh *mesh, std::vector &points) +{ + points.clear(); + points.resize(mesh->totvert); + + MVert *verts = mesh->mvert; + + for (int i = 0, e = mesh->totvert; i < e; i++) { + copy_yup_from_zup(points[i].getValue(), verts[i].co); + } +} + +static void get_topology(struct Mesh *mesh, + std::vector &poly_verts, + std::vector &loop_counts, + bool &r_has_flat_shaded_poly) +{ + const int num_poly = mesh->totpoly; + const int num_loops = mesh->totloop; + MLoop *mloop = mesh->mloop; + MPoly *mpoly = mesh->mpoly; + r_has_flat_shaded_poly = false; + + poly_verts.clear(); + loop_counts.clear(); + poly_verts.reserve(num_loops); + loop_counts.reserve(num_poly); + + /* NOTE: data needs to be written in the reverse order. */ + for (int i = 0; i < num_poly; i++) { + MPoly &poly = mpoly[i]; + loop_counts.push_back(poly.totloop); + + r_has_flat_shaded_poly |= (poly.flag & ME_SMOOTH) == 0; + + MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1); + + for (int j = 0; j < poly.totloop; j++, loop--) { + poly_verts.push_back(loop->v); + } + } +} + +static void get_creases(struct Mesh *mesh, + std::vector &indices, + std::vector &lengths, + std::vector &sharpnesses) +{ + const float factor = 1.0f / 255.0f; + + indices.clear(); + lengths.clear(); + sharpnesses.clear(); + + MEdge *edge = mesh->medge; + + for (int i = 0, e = mesh->totedge; i < e; i++) { + const float sharpness = static_cast(edge[i].crease) * factor; + + if (sharpness != 0.0f) { + indices.push_back(edge[i].v1); + indices.push_back(edge[i].v2); + sharpnesses.push_back(sharpness); + } + } + + lengths.resize(sharpnesses.size(), 2); +} + +static void get_loop_normals(struct Mesh *mesh, + std::vector &normals, + bool has_flat_shaded_poly) { + normals.clear(); + + /* If all polygons are smooth shaded, and there are no custom normals, we don't need to export + * normals at all. This is also done by other software, see T71246. */ + if (!has_flat_shaded_poly && !CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL) && + (mesh->flag & ME_AUTOSMOOTH) == 0) { + return; + } + + BKE_mesh_calc_normals_split(mesh); + const float(*lnors)[3] = static_cast(CustomData_get_layer(&mesh->ldata, CD_NORMAL)); + BLI_assert(lnors != nullptr || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL"); + + normals.resize(mesh->totloop); + + /* NOTE: data needs to be written in the reverse order. */ + int abc_index = 0; + MPoly *mp = mesh->mpoly; + for (int i = 0, e = mesh->totpoly; i < e; i++, mp++) { + for (int j = mp->totloop - 1; j >= 0; j--, abc_index++) { + int blender_index = mp->loopstart + j; + copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]); + } + } } -AbcMeshWriter::~AbcMeshWriter() +ABCMeshWriter::ABCMeshWriter(const ABCWriterConstructorArgs &args) : ABCGenericMeshWriter(args) { } -Mesh *AbcMeshWriter::getEvaluatedMesh(Scene *scene_eval, - Object *ob_eval, - bool &UNUSED(r_needsfree)) +Mesh *ABCMeshWriter::get_export_mesh(Object *object_eval, bool & /*r_needsfree*/) { - return mesh_get_eval_final(m_settings.depsgraph, scene_eval, ob_eval, &CD_MASK_MESH); + return BKE_object_get_evaluated_mesh(object_eval); } } // namespace alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h index 412c5530b32..3a592255a70 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.h +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h @@ -19,75 +19,75 @@ * \ingroup balembic */ -#include "abc_writer_object.h" +#include "abc_writer_abstract.h" #include "intern/abc_customdata.h" -struct Mesh; +#include +#include + struct ModifierData; namespace blender { namespace io { namespace alembic { -/* Writer for Alembic meshes. Does not assume the object is a mesh object. */ -class AbcGenericMeshWriter : public AbcObjectWriter { - protected: - Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema; - Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample; - - Alembic::AbcGeom::OSubDSchema m_subdiv_schema; - Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample; +/* Writer for Alembic geometry. Does not assume the object is a mesh object. */ +class ABCGenericMeshWriter : public ABCAbstractWriter { + private: + /* Either polymesh or subd is used, depending on is_subd_. + * References to the schema must be kept, or Alembic will not properly write. */ + Alembic::AbcGeom::OPolyMesh abc_poly_mesh_; + Alembic::AbcGeom::OPolyMeshSchema abc_poly_mesh_schema_; - Alembic::Abc::OArrayProperty m_mat_indices; + Alembic::AbcGeom::OSubD abc_subdiv_; + Alembic::AbcGeom::OSubDSchema abc_subdiv_schema_; - bool m_is_animated; - ModifierData *m_subsurf_mod; + /* Determines whether a poly mesh or a subdivision surface is exported. + * The value is set by an export option but only true if there is a subsdivision modifier on the + * exported object. */ + bool is_subd_; + ModifierData *subsurf_modifier_; + ModifierData *liquid_sim_modifier_; CDStreamConfig m_custom_data_config; - bool m_is_liquid; - bool m_is_subd; - public: - AbcGenericMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); + explicit ABCGenericMeshWriter(const ABCWriterConstructorArgs &args); + virtual ~ABCGenericMeshWriter(); - ~AbcGenericMeshWriter(); - void setIsAnimated(bool is_animated); + virtual void create_alembic_objects(const HierarchyContext *context) override; + virtual const Alembic::Abc::OObject get_alembic_object() const; protected: - virtual void do_write(); - virtual bool isAnimated() const; - virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) = 0; - virtual void freeEvaluatedMesh(struct Mesh *mesh); + virtual bool is_supported(const HierarchyContext *context) const override; + virtual void do_write(HierarchyContext &context) override; - Mesh *getFinalMesh(bool &r_needsfree); + virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) = 0; + virtual void free_export_mesh(Mesh *mesh); - void writeMesh(struct Mesh *mesh); - void writeSubD(struct Mesh *mesh); + virtual bool export_as_subdivision_surface(Object *ob_eval) const; - void writeArbGeoParams(struct Mesh *mesh); - void getGeoGroups(struct Mesh *mesh, std::map> &geoGroups); + private: + void write_mesh(HierarchyContext &context, Mesh *mesh); + void write_subd(HierarchyContext &context, Mesh *mesh); + template void write_face_sets(Object *object, Mesh *mesh, Schema &schema); - /* fluid surfaces support */ - void getVelocities(struct Mesh *mesh, std::vector &vels); + ModifierData *get_liquid_sim_modifier(Scene *scene_eval, Object *ob_eval); - template void writeFaceSets(struct Mesh *mesh, Schema &schema); + void write_arb_geo_params(Mesh *me); + void get_velocities(Mesh *mesh, std::vector &vels); + void get_geo_groups(Object *object, + Mesh *mesh, + std::map> &geo_groups); }; -class AbcMeshWriter : public AbcGenericMeshWriter { +/* Writer for Alembic geometry of Blender Mesh objects. */ +class ABCMeshWriter : public ABCGenericMeshWriter { public: - AbcMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - ~AbcMeshWriter(); + ABCMeshWriter(const ABCWriterConstructorArgs &args); protected: - virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override; + virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override; }; } // namespace alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc index fa37c1eac59..1fd382214a6 100644 --- a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc +++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc @@ -19,7 +19,6 @@ */ #include "abc_writer_nurbs.h" -#include "abc_writer_transform.h" #include "intern/abc_axis_conversion.h" #include "DNA_curve_types.h" @@ -29,52 +28,70 @@ #include "BKE_curve.h" +#include "CLG_log.h" +static CLG_LogRef LOG = {"io.alembic"}; + +namespace blender { +namespace io { +namespace alembic { + +using Alembic::Abc::OObject; using Alembic::AbcGeom::FloatArraySample; using Alembic::AbcGeom::OBoolProperty; using Alembic::AbcGeom::OCompoundProperty; using Alembic::AbcGeom::ONuPatch; using Alembic::AbcGeom::ONuPatchSchema; -namespace blender { -namespace io { -namespace alembic { +ABCNurbsWriter::ABCNurbsWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args) +{ +} -AbcNurbsWriter::AbcNurbsWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent) +void ABCNurbsWriter::create_alembic_objects(const HierarchyContext *context) { - m_is_animated = isAnimated(); + Curve *curve = static_cast(context->object->data); + size_t num_nurbs = BLI_listbase_count(&curve->nurb); + OObject abc_parent = args_.abc_parent; + const char *abc_parent_path = abc_parent.getFullName().c_str(); - /* if the object is static, use the default static time sampling */ - if (!m_is_animated) { - m_time_sampling = 0; - } + for (size_t i = 0; i < num_nurbs; i++) { + std::stringstream patch_name_stream; + patch_name_stream << args_.abc_name << '_' << i; - Curve *curve = static_cast(m_object->data); - size_t numNurbs = BLI_listbase_count(&curve->nurb); + while (abc_parent.getChildHeader(patch_name_stream.str())) { + patch_name_stream << "_"; + } - for (size_t i = 0; i < numNurbs; i++) { - std::stringstream str; - str << m_name << '_' << i; + std::string patch_name = patch_name_stream.str(); + CLOG_INFO(&LOG, 2, "exporting %s/%s", abc_parent_path, patch_name.c_str()); - while (parent->alembicXform().getChildHeader(str.str())) { - str << "_"; - } + ONuPatch nurbs(abc_parent, patch_name.c_str(), timesample_index_); + abc_nurbs_.push_back(nurbs); + abc_nurbs_schemas_.push_back(nurbs.getSchema()); + } +} - ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling); - m_nurbs_schema.push_back(nurbs.getSchema()); +const OObject ABCNurbsWriter::get_alembic_object() const +{ + if (abc_nurbs_.empty()) { + return OObject(); } + /* For parenting purposes within the Alembic file, all NURBS patches are equal, so just use the + * first one. */ + return abc_nurbs_[0]; } -bool AbcNurbsWriter::isAnimated() const +bool ABCNurbsWriter::check_is_animated(const HierarchyContext &context) const { - /* check if object has shape keys */ - Curve *cu = static_cast(m_object->data); + /* Check if object has shape keys. */ + Curve *cu = static_cast(context.object->data); return (cu->key != NULL); } +bool ABCNurbsWriter::is_supported(const HierarchyContext *context) const +{ + return ELEM(context->object->type, OB_SURF, OB_CURVE); +} + static void get_knots(std::vector &knots, const int num_knots, float *nu_knots) { if (num_knots <= 1) { @@ -95,22 +112,13 @@ static void get_knots(std::vector &knots, const int num_knots, float *nu_ knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]); } -void AbcNurbsWriter::do_write() +void ABCNurbsWriter::do_write(HierarchyContext &context) { - /* we have already stored a sample for this object. */ - if (!m_first_frame && !m_is_animated) { - return; - } - - if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) { - return; - } - - Curve *curve = static_cast(m_object->data); + Curve *curve = static_cast(context.object->data); ListBase *nulb; - if (m_object->runtime.curve_cache->deformed_nurbs.first != NULL) { - nulb = &m_object->runtime.curve_cache->deformed_nurbs; + if (context.object->runtime.curve_cache->deformed_nurbs.first != NULL) { + nulb = &context.object->runtime.curve_cache->deformed_nurbs; } else { nulb = BKE_curve_nurbs_get(curve); @@ -147,7 +155,7 @@ void AbcNurbsWriter::do_write() /* TODO(kevin): to accommodate other software we should duplicate control * points to indicate that a NURBS is cyclic. */ - OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties(); + OCompoundProperty user_props = abc_nurbs_schemas_[count].getUserProperties(); if ((nu->flagu & CU_NURB_ENDPOINT) != 0) { OBoolProperty prop(user_props, "endpoint_u"); @@ -169,7 +177,7 @@ void AbcNurbsWriter::do_write() prop.set(true); } - m_nurbs_schema[count].set(sample); + abc_nurbs_schemas_[count].set(sample); } } diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.h b/source/blender/io/alembic/exporter/abc_writer_nurbs.h index b2a9cf1b786..23af4c40556 100644 --- a/source/blender/io/alembic/exporter/abc_writer_nurbs.h +++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.h @@ -19,26 +19,37 @@ * \ingroup balembic */ -#include "abc_writer_object.h" +#include "abc_writer_abstract.h" +#include "abc_writer_mesh.h" +#include namespace blender { namespace io { namespace alembic { -class AbcNurbsWriter : public AbcObjectWriter { - std::vector m_nurbs_schema; - bool m_is_animated; +class ABCNurbsWriter : public ABCAbstractWriter { + private: + std::vector abc_nurbs_; + std::vector abc_nurbs_schemas_; public: - AbcNurbsWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); + explicit ABCNurbsWriter(const ABCWriterConstructorArgs &args); - private: - virtual void do_write(); + virtual void create_alembic_objects(const HierarchyContext *context) override; + virtual const Alembic::Abc::OObject get_alembic_object() const override; + + protected: + virtual bool is_supported(const HierarchyContext *context) const override; + virtual void do_write(HierarchyContext &context) override; + virtual bool check_is_animated(const HierarchyContext &context) const override; +}; + +class ABCNurbsMeshWriter : public ABCGenericMeshWriter { + public: + explicit ABCNurbsMeshWriter(const ABCWriterConstructorArgs &args); - bool isAnimated() const; + protected: + virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override; }; } // namespace alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_object.cc b/source/blender/io/alembic/exporter/abc_writer_object.cc deleted file mode 100644 index 97cac3a1af9..00000000000 --- a/source/blender/io/alembic/exporter/abc_writer_object.cc +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_object.h" - -#include "DNA_object_types.h" - -#include "BKE_object.h" - -namespace blender { -namespace io { -namespace alembic { - -AbcObjectWriter::AbcObjectWriter(Object *ob, - uint32_t time_sampling, - ExportSettings &settings, - AbcObjectWriter *parent) - : m_object(ob), m_settings(settings), m_time_sampling(time_sampling), m_first_frame(true) -{ - /* This class is used as superclass for objects themselves (i.e. transforms) and for object - * data (meshes, curves, cameras, etc.). However, when writing transforms, the m_name field is - * ignored. This is a temporary tweak to get the exporter to write object data with the data - * name instead of the object name in a safe way. */ - if (m_object->data == nullptr) { - m_name = get_id_name(m_object); - } - else { - ID *ob_data = static_cast(m_object->data); - m_name = get_id_name(ob_data); - } - - if (parent) { - parent->addChild(this); - } -} - -AbcObjectWriter::~AbcObjectWriter() -{ -} - -void AbcObjectWriter::addChild(AbcObjectWriter *child) -{ - m_children.push_back(child); -} - -Imath::Box3d AbcObjectWriter::bounds() -{ - BoundBox *bb = BKE_object_boundbox_get(this->m_object); - - if (!bb) { - if (this->m_object->type != OB_CAMERA) { - ABC_LOG(m_settings.logger) << "Bounding box is null!\n"; - } - - return Imath::Box3d(); - } - - /* Convert Z-up to Y-up. This also changes which vector goes into which min/max property. */ - this->m_bounds.min.x = bb->vec[0][0]; - this->m_bounds.min.y = bb->vec[0][2]; - this->m_bounds.min.z = -bb->vec[6][1]; - - this->m_bounds.max.x = bb->vec[6][0]; - this->m_bounds.max.y = bb->vec[6][2]; - this->m_bounds.max.z = -bb->vec[0][1]; - - return this->m_bounds; -} - -void AbcObjectWriter::write() -{ - do_write(); - m_first_frame = false; -} - -} // namespace alembic -} // namespace io -} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_object.h b/source/blender/io/alembic/exporter/abc_writer_object.h deleted file mode 100644 index 7f8e8735812..00000000000 --- a/source/blender/io/alembic/exporter/abc_writer_object.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#pragma once - -/** \file - * \ingroup balembic - */ - -#include -#include - -#include "abc_exporter.h" - -#include "DNA_ID.h" - -struct Main; -struct Object; - -namespace blender { -namespace io { -namespace alembic { - -class AbcTransformWriter; - -class AbcObjectWriter { - protected: - Object *m_object; - ExportSettings &m_settings; - - uint32_t m_time_sampling; - - Imath::Box3d m_bounds; - std::vector m_children; - - std::vector> m_props; - - bool m_first_frame; - std::string m_name; - - public: - AbcObjectWriter(Object *ob, - uint32_t time_sampling, - ExportSettings &settings, - AbcObjectWriter *parent = NULL); - - virtual ~AbcObjectWriter(); - - void addChild(AbcObjectWriter *child); - - virtual Imath::Box3d bounds(); - - void write(); - - private: - virtual void do_write() = 0; -}; - -} // namespace alembic -} // namespace io -} // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_points.cc b/source/blender/io/alembic/exporter/abc_writer_points.cc index 4d7f5d11c36..19870e39a90 100644 --- a/source/blender/io/alembic/exporter/abc_writer_points.cc +++ b/source/blender/io/alembic/exporter/abc_writer_points.cc @@ -22,9 +22,6 @@ */ #include "abc_writer_points.h" -#include "abc_writer_mesh.h" -#include "abc_writer_transform.h" -#include "intern/abc_util.h" #include "DNA_object_types.h" #include "DNA_particle_types.h" @@ -36,81 +33,102 @@ #include "DEG_depsgraph_query.h" +#include "CLG_log.h" +static CLG_LogRef LOG = {"io.alembic"}; + +namespace blender { +namespace io { +namespace alembic { + using Alembic::AbcGeom::kVertexScope; using Alembic::AbcGeom::OPoints; using Alembic::AbcGeom::OPointsSchema; -/* ************************************************************************** */ +ABCPointsWriter::ABCPointsWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args) +{ +} -namespace blender { -namespace io { -namespace alembic { +void ABCPointsWriter::create_alembic_objects(const HierarchyContext * /*context*/) +{ + CLOG_INFO(&LOG, 2, "exporting OPoints %s", args_.abc_path.c_str()); + abc_points_ = OPoints(args_.abc_parent, args_.abc_name, timesample_index_); + abc_points_schema_ = abc_points_.getSchema(); +} -AbcPointsWriter::AbcPointsWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings, - ParticleSystem *psys) - : AbcObjectWriter(ob, time_sampling, settings, parent) +const Alembic::Abc::OObject ABCPointsWriter::get_alembic_object() const { - m_psys = psys; + return abc_points_; +} - std::string psys_name = get_valid_abc_name(psys->name); - OPoints points(parent->alembicXform(), psys_name, m_time_sampling); - m_schema = points.getSchema(); +bool ABCPointsWriter::is_supported(const HierarchyContext *context) const +{ + return ELEM(context->particle_system->part->type, + PART_EMITTER, + PART_FLUID_FLIP, + PART_FLUID_SPRAY, + PART_FLUID_BUBBLE, + PART_FLUID_FOAM, + PART_FLUID_TRACER, + PART_FLUID_SPRAYFOAM, + PART_FLUID_SPRAYBUBBLE, + PART_FLUID_FOAMBUBBLE, + PART_FLUID_SPRAYFOAMBUBBLE); } -void AbcPointsWriter::do_write() +bool ABCPointsWriter::check_is_animated(const HierarchyContext & /*context*/) const { - if (!m_psys) { - return; - } + /* We assume that particles are always animated. */ + return true; +} + +void ABCPointsWriter::do_write(HierarchyContext &context) +{ + BLI_assert(context.particle_system != nullptr); std::vector points; std::vector velocities; std::vector widths; std::vector ids; + ParticleSystem *psys = context.particle_system; ParticleKey state; - ParticleSimulationData sim; - sim.depsgraph = m_settings.depsgraph; - sim.scene = m_settings.scene; - sim.ob = m_object; - sim.psys = m_psys; + sim.depsgraph = args_.depsgraph; + sim.scene = DEG_get_evaluated_scene(args_.depsgraph); + sim.ob = context.object; + sim.psys = psys; - m_psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); + psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); uint64_t index = 0; - for (int p = 0; p < m_psys->totpart; p++) { + for (int p = 0; p < psys->totpart; p++) { float pos[3], vel[3]; - if (m_psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) { + if (psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) { continue; } - state.time = DEG_get_ctime(m_settings.depsgraph); - + state.time = DEG_get_ctime(args_.depsgraph); if (psys_get_particle_state(&sim, p, &state, 0) == 0) { continue; } /* location */ - mul_v3_m4v3(pos, m_object->imat, state.co); + mul_v3_m4v3(pos, context.object->imat, state.co); /* velocity */ - sub_v3_v3v3(vel, state.co, m_psys->particles[p].prev_state.co); + sub_v3_v3v3(vel, state.co, psys->particles[p].prev_state.co); /* Convert Z-up to Y-up. */ points.push_back(Imath::V3f(pos[0], pos[2], -pos[1])); velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1])); - widths.push_back(m_psys->particles[p].size); + widths.push_back(psys->particles[p].size); ids.push_back(index++); } - if (m_psys->lattice_deform_data) { - BKE_lattice_deform_data_destroy(m_psys->lattice_deform_data); - m_psys->lattice_deform_data = NULL; + if (psys->lattice_deform_data) { + BKE_lattice_deform_data_destroy(psys->lattice_deform_data); + psys->lattice_deform_data = NULL; } Alembic::Abc::P3fArraySample psample(points); @@ -119,10 +137,10 @@ void AbcPointsWriter::do_write() Alembic::Abc::FloatArraySample wsample_array(widths); Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope); - m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample); - m_sample.setSelfBounds(bounds()); - - m_schema.set(m_sample); + OPointsSchema::Sample sample(psample, idsample, vsample, wsample); + update_bounding_box(context.object); + sample.setSelfBounds(bounding_box_); + abc_points_schema_.set(sample); } } // namespace alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_points.h b/source/blender/io/alembic/exporter/abc_writer_points.h index 7e8a3eaadcd..03800b80acf 100644 --- a/source/blender/io/alembic/exporter/abc_writer_points.h +++ b/source/blender/io/alembic/exporter/abc_writer_points.h @@ -22,30 +22,29 @@ * \ingroup balembic */ -#include "abc_writer_object.h" -#include "intern/abc_customdata.h" +#include "abc_writer_abstract.h" -struct ParticleSystem; - -/* ************************************************************************** */ +#include namespace blender { namespace io { namespace alembic { -class AbcPointsWriter : public AbcObjectWriter { - Alembic::AbcGeom::OPointsSchema m_schema; - Alembic::AbcGeom::OPointsSchema::Sample m_sample; - ParticleSystem *m_psys; +class ABCPointsWriter : public ABCAbstractWriter { + Alembic::AbcGeom::OPoints abc_points_; + Alembic::AbcGeom::OPointsSchema abc_points_schema_; public: - AbcPointsWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings, - ParticleSystem *psys); + explicit ABCPointsWriter(const ABCWriterConstructorArgs &args); + + virtual void create_alembic_objects(const HierarchyContext *context) override; + virtual const Alembic::Abc::OObject get_alembic_object() const override; + + virtual bool is_supported(const HierarchyContext *context) const override; - void do_write(); + protected: + virtual bool check_is_animated(const HierarchyContext &context) const override; + virtual void do_write(HierarchyContext &context) override; }; } // namespace alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.cc b/source/blender/io/alembic/exporter/abc_writer_transform.cc index fa808c1b75b..65d6b7c5b41 100644 --- a/source/blender/io/alembic/exporter/abc_writer_transform.cc +++ b/source/blender/io/alembic/exporter/abc_writer_transform.cc @@ -19,112 +19,95 @@ */ #include "abc_writer_transform.h" +#include "abc_hierarchy_iterator.h" #include "intern/abc_axis_conversion.h" +#include "intern/abc_util.h" -#include +#include "BKE_object.h" -#include "DNA_object_types.h" +#include "BLI_math_matrix.h" +#include "BLI_math_rotation.h" -#include "BLI_math.h" +#include "DNA_layer_types.h" -#include "DEG_depsgraph_query.h" - -using Alembic::AbcGeom::OObject; -using Alembic::AbcGeom::OXform; +#include "CLG_log.h" +static CLG_LogRef LOG = {"io.alembic"}; namespace blender { namespace io { namespace alembic { -AbcTransformWriter::AbcTransformWriter(Object *ob, - const OObject &abc_parent, - AbcTransformWriter *parent, - unsigned int time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent), m_proxy_from(NULL) -{ - m_is_animated = hasAnimation(m_object); - - if (!m_is_animated) { - time_sampling = 0; - } - - m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling); - m_schema = m_xform.getSchema(); +using Alembic::Abc::OObject; +using Alembic::AbcGeom::OXform; +using Alembic::AbcGeom::OXformSchema; +using Alembic::AbcGeom::XformSample; - /* Blender objects can't have a parent without inheriting the transform. */ - m_inherits_xform = parent != NULL; +ABCTransformWriter::ABCTransformWriter(const ABCWriterConstructorArgs &args) + : ABCAbstractWriter(args) +{ + timesample_index_ = args_.abc_archive->time_sampling_index_transforms(); } -void AbcTransformWriter::do_write() +void ABCTransformWriter::create_alembic_objects(const HierarchyContext * /*context*/) { - Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object); - - if (m_first_frame) { - m_visibility = Alembic::AbcGeom::CreateVisibilityProperty( - m_xform, m_xform.getSchema().getTimeSampling()); - } - - m_visibility.set(!(ob_eval->restrictflag & OB_RESTRICT_VIEWPORT)); + CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str()); + abc_xform_ = OXform(args_.abc_parent, args_.abc_name, timesample_index_); + abc_xform_schema_ = abc_xform_.getSchema(); +} - if (!m_first_frame && !m_is_animated) { - return; - } +void ABCTransformWriter::do_write(HierarchyContext &context) +{ + float parent_relative_matrix[4][4]; // The object matrix relative to the parent. + mul_m4_m4m4(parent_relative_matrix, context.parent_matrix_inv_world, context.matrix_world); - float yup_mat[4][4]; - create_transform_matrix( - ob_eval, yup_mat, m_inherits_xform ? ABC_MATRIX_LOCAL : ABC_MATRIX_WORLD, m_proxy_from); + // After this, parent_relative_matrix uses Y=up. + copy_m44_axis_swap(parent_relative_matrix, parent_relative_matrix, ABC_YUP_FROM_ZUP); /* If the parent is a camera, undo its to-Maya rotation (see below). */ - bool is_root_object = !m_inherits_xform || ob_eval->parent == nullptr; - if (!is_root_object && ob_eval->parent->type == OB_CAMERA) { + bool is_root_object = context.export_parent == nullptr; + if (!is_root_object && context.export_parent->type == OB_CAMERA) { float rot_mat[4][4]; axis_angle_to_mat4_single(rot_mat, 'X', M_PI_2); - mul_m4_m4m4(yup_mat, rot_mat, yup_mat); + mul_m4_m4m4(parent_relative_matrix, rot_mat, parent_relative_matrix); } /* If the object is a camera, apply an extra rotation to Maya camera orientation. */ - if (ob_eval->type == OB_CAMERA) { + if (context.object->type == OB_CAMERA) { float rot_mat[4][4]; axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2); - mul_m4_m4m4(yup_mat, yup_mat, rot_mat); + mul_m4_m4m4(parent_relative_matrix, parent_relative_matrix, rot_mat); } if (is_root_object) { /* Only apply scaling to root objects, parenting will propagate it. */ float scale_mat[4][4]; - scale_m4_fl(scale_mat, m_settings.global_scale); - scale_mat[3][3] = m_settings.global_scale; /* also scale translation */ - mul_m4_m4m4(yup_mat, yup_mat, scale_mat); - yup_mat[3][3] /= m_settings.global_scale; /* normalise the homogeneous component */ + scale_m4_fl(scale_mat, args_.export_params->global_scale); + scale_mat[3][3] = args_.export_params->global_scale; /* also scale translation */ + mul_m4_m4m4(parent_relative_matrix, parent_relative_matrix, scale_mat); + parent_relative_matrix[3][3] /= + args_.export_params->global_scale; /* normalise the homogeneous component */ } - m_matrix = convert_matrix_datatype(yup_mat); - m_sample.setMatrix(m_matrix); - - /* Always export as "inherits transform", as this is the only way in which Blender works. The - * above code has already taken care of writing the correct matrix so that this option is not - * necessary. However, certain packages (for example the USD Alembic exporter) are incompatible - * with non-inheriting transforms and will completely ignore the transform if that is used. */ - m_sample.setInheritsXforms(true); - m_schema.set(m_sample); + XformSample xform_sample; + xform_sample.setMatrix(convert_matrix_datatype(parent_relative_matrix)); + xform_sample.setInheritsXforms(true); + abc_xform_schema_.set(xform_sample); } -Imath::Box3d AbcTransformWriter::bounds() +const OObject ABCTransformWriter::get_alembic_object() const { - Imath::Box3d bounds; - - for (int i = 0; i < m_children.size(); i++) { - Imath::Box3d box(m_children[i]->bounds()); - bounds.extendBy(box); - } - - return Imath::transform(bounds, m_matrix); + return abc_xform_; } -bool AbcTransformWriter::hasAnimation(Object * /*ob*/) const +bool ABCTransformWriter::check_is_animated(const HierarchyContext &context) const { - return true; + if (context.duplicator != NULL) { + /* This object is being duplicated, so could be emitted by a particle system and thus + * influenced by forces. TODO(Sybren): Make this more strict. Probably better to get from the + * depsgraph whether this object instance has a time source. */ + return true; + } + return BKE_object_moves_in_time(context.object, context.animation_check_include_parent); } } // namespace alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.h b/source/blender/io/alembic/exporter/abc_writer_transform.h index fc997d77f4c..950bff39c29 100644 --- a/source/blender/io/alembic/exporter/abc_writer_transform.h +++ b/source/blender/io/alembic/exporter/abc_writer_transform.h @@ -19,44 +19,27 @@ * \ingroup balembic */ -#include "abc_writer_object.h" +#include "abc_writer_abstract.h" -#include +#include namespace blender { namespace io { namespace alembic { -class AbcTransformWriter : public AbcObjectWriter { - Alembic::AbcGeom::OXform m_xform; - Alembic::AbcGeom::OXformSchema m_schema; - Alembic::AbcGeom::XformSample m_sample; - Alembic::AbcGeom::OVisibilityProperty m_visibility; - Alembic::Abc::M44d m_matrix; - - bool m_is_animated; - bool m_inherits_xform; - - public: - Object *m_proxy_from; +class ABCTransformWriter : public ABCAbstractWriter { + private: + Alembic::AbcGeom::OXform abc_xform_; + Alembic::AbcGeom::OXformSchema abc_xform_schema_; public: - AbcTransformWriter(Object *ob, - const Alembic::AbcGeom::OObject &abc_parent, - AbcTransformWriter *parent, - unsigned int time_sampling, - ExportSettings &settings); - - Alembic::AbcGeom::OXform &alembicXform() - { - return m_xform; - } - virtual Imath::Box3d bounds(); - - private: - virtual void do_write(); + explicit ABCTransformWriter(const ABCWriterConstructorArgs &args); + virtual void create_alembic_objects(const HierarchyContext *context) override; - bool hasAnimation(Object *ob) const; + protected: + virtual void do_write(HierarchyContext &context) override; + virtual bool check_is_animated(const HierarchyContext &context) const override; + virtual const Alembic::Abc::OObject get_alembic_object() const override; }; } // namespace alembic diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index bca84b97749..5932791ecf4 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -70,36 +70,19 @@ #include "WM_api.h" #include "WM_types.h" -using Alembic::Abc::Int32ArraySamplePtr; using Alembic::Abc::ObjectHeader; - -using Alembic::AbcGeom::kWrapExisting; -using Alembic::AbcGeom::MetaData; -using Alembic::AbcGeom::P3fArraySamplePtr; - using Alembic::AbcGeom::ICamera; -using Alembic::AbcGeom::ICompoundProperty; using Alembic::AbcGeom::ICurves; -using Alembic::AbcGeom::ICurvesSchema; using Alembic::AbcGeom::IFaceSet; using Alembic::AbcGeom::ILight; -using Alembic::AbcGeom::IN3fArrayProperty; -using Alembic::AbcGeom::IN3fGeomParam; using Alembic::AbcGeom::INuPatch; using Alembic::AbcGeom::IObject; using Alembic::AbcGeom::IPoints; -using Alembic::AbcGeom::IPointsSchema; using Alembic::AbcGeom::IPolyMesh; -using Alembic::AbcGeom::IPolyMeshSchema; using Alembic::AbcGeom::ISampleSelector; using Alembic::AbcGeom::ISubD; -using Alembic::AbcGeom::IV2fGeomParam; using Alembic::AbcGeom::IXform; -using Alembic::AbcGeom::IXformSchema; -using Alembic::AbcGeom::N3fArraySamplePtr; -using Alembic::AbcGeom::V3fArraySamplePtr; -using Alembic::AbcGeom::XformSample; - +using Alembic::AbcGeom::MetaData; using Alembic::AbcMaterial::IMaterial; using namespace blender::io::alembic; diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h index 2f0c1b270f1..5f84fd48b71 100644 --- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -197,8 +197,8 @@ class AbstractHierarchyIterator { /* Iterate over the depsgraph, create writers, and tell the writers to write. * Main entry point for the AbstractHierarchyIterator, must be called for every to-be-exported - * frame. */ - void iterate_and_write(); + * (sub)frame. */ + virtual void iterate_and_write(); /* Release all writers. Call after all frames have been exported. */ void release_writers(); -- cgit v1.2.3 From a6775efb4f26531c299b805915f5865b06fad75b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 30 Jun 2020 13:09:40 +0200 Subject: Alembic exporter: Fix Windows build errors Some Windows-specific code needed adjustment after 2917df21adc8a1ce. --- source/blender/io/alembic/exporter/abc_archive.cc | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/exporter/abc_archive.cc b/source/blender/io/alembic/exporter/abc_archive.cc index dbf046e6dfe..5fbf74f0705 100644 --- a/source/blender/io/alembic/exporter/abc_archive.cc +++ b/source/blender/io/alembic/exporter/abc_archive.cc @@ -30,6 +30,13 @@ #include #include +#ifdef WIN32 +# include "BLI_path_util.h" +# include "BLI_string.h" + +# include "utfconv.h" +#endif + namespace blender { namespace io { namespace alembic { @@ -81,10 +88,13 @@ static OArchive *create_archive(std::ofstream *abc_ostream, { /* Use stream to support unicode character paths on Windows. */ #ifdef WIN32 - UTF16_ENCODE(filename); - std::wstring wstr(filename_16); + char filename_cstr[FILE_MAX]; + BLI_strncpy(filename_cstr, filename.c_str(), FILE_MAX); + + UTF16_ENCODE(filename_cstr); + std::wstring wstr(filename_cstr_16); abc_ostream->open(wstr.c_str(), std::ios::out | std::ios::binary); - UTF16_UN_ENCODE(filename); + UTF16_UN_ENCODE(filename_cstr); #else abc_ostream->open(filename, std::ios::out | std::ios::binary); #endif -- cgit v1.2.3 From 36d6aa428f4848cfb153b34bdc4921ee9cabb227 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 1 Jul 2020 13:12:24 +1000 Subject: Cleanup: spelling --- source/blender/io/alembic/exporter/abc_writer_abstract.h | 2 +- source/blender/io/alembic/exporter/abc_writer_mesh.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'source/blender/io') diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h index d6fd692775e..a83373a567a 100644 --- a/source/blender/io/alembic/exporter/abc_writer_abstract.h +++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h @@ -52,7 +52,7 @@ class ABCAbstractWriter : public AbstractHierarchyWriter { /* Returns true if the data to be written is actually supported. This would, for example, allow a * hypothetical camera writer accept a perspective camera but reject an orthogonal one. * - * Returning false from a transform writer will prevent the object and all its decendants from + * Returning false from a transform writer will prevent the object and all its descendants from * being exported. Returning false from a data writer (object data, hair, or particles) will * only prevent that data from being written (and thus cause the object to be exported as an * Empty). */ diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h index 3a592255a70..bf4d4e9b9d8 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.h +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h @@ -43,7 +43,7 @@ class ABCGenericMeshWriter : public ABCAbstractWriter { Alembic::AbcGeom::OSubDSchema abc_subdiv_schema_; /* Determines whether a poly mesh or a subdivision surface is exported. - * The value is set by an export option but only true if there is a subsdivision modifier on the + * The value is set by an export option but only true if there is a subdivision modifier on the * exported object. */ bool is_subd_; ModifierData *subsurf_modifier_; -- cgit v1.2.3