Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2020-07-15 15:18:30 +0300
committerClément Foucault <foucault.clem@gmail.com>2020-07-15 15:23:35 +0300
commite8f8c13d4b76ba587ef7cf33370b286d4fbd36bc (patch)
tree371472ae220ad8740b310aaa8f4c5746448302c5 /source/blender/io
parent0c062a9e082130212447c2b67e8e16b8a2e622d1 (diff)
parent44bb73e765a6f79bc14a46449368f83e572d8bad (diff)
PointCloud: Initial rendering support for Workbenchtmp-pointcloud-render
Also includes outline overlays. Removes the temp overlay drawing We make the geometry follow camera like billboards this uses less geometry. Currently we use half octahedron for now. Goal would be to use icospheres. This patch also optimize the case when pointcloud has uniform radius. However we should premultiply the radius prop by the default radius beforehand to avoid a multiplication on CPU. Differential Revision: https://developer.blender.org/D8301
Diffstat (limited to 'source/blender/io')
-rw-r--r--source/blender/io/alembic/CMakeLists.txt4
-rw-r--r--source/blender/io/alembic/exporter/abc_export_capi.cc7
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc11
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.h4
-rw-r--r--source/blender/io/avi/intern/avi_mjpeg.c15
-rw-r--r--source/blender/io/avi/intern/avi_mjpeg.h5
-rw-r--r--source/blender/io/avi/intern/avi_rgb.c5
-rw-r--r--source/blender/io/avi/intern/avi_rgb.h5
-rw-r--r--source/blender/io/collada/ArmatureImporter.cpp4
-rw-r--r--source/blender/io/collada/MeshImporter.cpp2
-rw-r--r--source/blender/io/collada/MeshImporter.h2
-rw-r--r--source/blender/io/collada/TransformWriter.cpp6
-rw-r--r--source/blender/io/collada/TransformWriter.h5
-rw-r--r--source/blender/io/collada/collada_utils.cpp2
-rw-r--r--source/blender/io/collada/collada_utils.h2
-rw-r--r--source/blender/io/common/CMakeLists.txt5
-rw-r--r--source/blender/io/common/IO_abstract_hierarchy_iterator.h47
-rw-r--r--source/blender/io/common/IO_dupli_persistent_id.hh68
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator.cc84
-rw-r--r--source/blender/io/common/intern/dupli_parent_finder.cc104
-rw-r--r--source/blender/io/common/intern/dupli_parent_finder.hh62
-rw-r--r--source/blender/io/common/intern/dupli_persistent_id.cc167
-rw-r--r--source/blender/io/common/intern/object_identifier.cc116
-rw-r--r--source/blender/io/usd/intern/usd_capi.cc7
24 files changed, 660 insertions, 79 deletions
diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt
index da36272b850..681d6d04acd 100644
--- a/source/blender/io/alembic/CMakeLists.txt
+++ b/source/blender/io/alembic/CMakeLists.txt
@@ -63,8 +63,8 @@ set(SRC
exporter/abc_writer_camera.cc
exporter/abc_writer_curves.cc
exporter/abc_writer_hair.cc
- exporter/abc_writer_mesh.cc
exporter/abc_writer_mball.cc
+ exporter/abc_writer_mesh.cc
exporter/abc_writer_nurbs.cc
exporter/abc_writer_points.cc
exporter/abc_writer_transform.cc
@@ -89,8 +89,8 @@ set(SRC
exporter/abc_writer_camera.h
exporter/abc_writer_curves.h
exporter/abc_writer_hair.h
- exporter/abc_writer_mesh.h
exporter/abc_writer_mball.h
+ exporter/abc_writer_mesh.h
exporter/abc_writer_nurbs.h
exporter/abc_writer_points.h
exporter/abc_writer_transform.h
diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc
index fbc5b2d5c02..98c551c635c 100644
--- a/source/blender/io/alembic/exporter/abc_export_capi.cc
+++ b/source/blender/io/alembic/exporter/abc_export_capi.cc
@@ -73,7 +73,12 @@ static void build_depsgraph(Depsgraph *depsgraph, Main *bmain)
DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer);
}
-static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
+static void export_startjob(void *customdata,
+ /* Cannot be const, this function implements wm_jobs_start_callback.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ short *stop,
+ short *do_update,
+ float *progress)
{
ExportJobData *data = static_cast<ExportJobData *>(customdata);
data->was_canceled = false;
diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
index 90004c0e85b..c83eaf3eede 100644
--- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
+++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
@@ -107,20 +107,23 @@ AbstractHierarchyIterator::ExportGraph::key_type ABCHierarchyIterator::
determine_graph_index_object(const HierarchyContext *context)
{
if (params_.flatten_hierarchy) {
- return std::make_pair(nullptr, nullptr);
+ return ObjectIdentifier::for_graph_root();
}
return AbstractHierarchyIterator::determine_graph_index_object(context);
}
AbstractHierarchyIterator::ExportGraph::key_type ABCHierarchyIterator::determine_graph_index_dupli(
- const HierarchyContext *context, const std::set<Object *> &dupli_set)
+ const HierarchyContext *context,
+ const DupliObject *dupli_object,
+ const DupliParentFinder &dupli_parent_finder)
{
if (params_.flatten_hierarchy) {
- return std::make_pair(nullptr, nullptr);
+ return ObjectIdentifier::for_graph_root();
}
- return AbstractHierarchyIterator::determine_graph_index_dupli(context, dupli_set);
+ return AbstractHierarchyIterator::determine_graph_index_dupli(
+ context, dupli_object, dupli_parent_finder);
}
Alembic::Abc::OObject ABCHierarchyIterator::get_alembic_parent(
diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
index edcb31806ba..3fe2d2c43d2 100644
--- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
+++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
@@ -67,7 +67,9 @@ class ABCHierarchyIterator : public AbstractHierarchyIterator {
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<Object *> &dupli_set) override;
+ const HierarchyContext *context,
+ const DupliObject *dupli_object,
+ const DupliParentFinder &dupli_parent_finder) override;
virtual AbstractHierarchyWriter *create_transform_writer(
const HierarchyContext *context) override;
diff --git a/source/blender/io/avi/intern/avi_mjpeg.c b/source/blender/io/avi/intern/avi_mjpeg.c
index 70ddca28060..75059c202e5 100644
--- a/source/blender/io/avi/intern/avi_mjpeg.c
+++ b/source/blender/io/avi/intern/avi_mjpeg.c
@@ -20,7 +20,7 @@
/** \file
* \ingroup avi
*
- * This is external code. Converts between avi and mpeg/jpeg.
+ * This is external code. Converts between AVI and MPEG/JPEG.
*/
#include <stdlib.h>
@@ -39,7 +39,9 @@
#include "avi_mjpeg.h"
static void jpegmemdestmgr_build(j_compress_ptr cinfo, unsigned char *buffer, size_t bufsize);
-static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, unsigned char *buffer, size_t bufsize);
+static void jpegmemsrcmgr_build(j_decompress_ptr dinfo,
+ const unsigned char *buffer,
+ size_t bufsize);
static size_t numbytes;
@@ -381,7 +383,10 @@ static void deinterlace(int odd, unsigned char *to, unsigned char *from, int wid
}
}
-void *avi_converter_from_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size)
+void *avi_converter_from_mjpeg(AviMovie *movie,
+ int stream,
+ unsigned char *buffer,
+ const size_t *size)
{
int deint;
unsigned char *buf;
@@ -553,7 +558,9 @@ static void jpegmemsrcmgr_term_source(j_decompress_ptr dinfo)
MEM_freeN(dinfo->src);
}
-static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, unsigned char *buffer, size_t bufsize)
+static void jpegmemsrcmgr_build(j_decompress_ptr dinfo,
+ const unsigned char *buffer,
+ size_t bufsize)
{
dinfo->src = MEM_mallocN(sizeof(*(dinfo->src)), "avi.jpegmemsrcmgr_build");
diff --git a/source/blender/io/avi/intern/avi_mjpeg.h b/source/blender/io/avi/intern/avi_mjpeg.h
index 30e46bf1d0c..13153fa41f0 100644
--- a/source/blender/io/avi/intern/avi_mjpeg.h
+++ b/source/blender/io/avi/intern/avi_mjpeg.h
@@ -24,7 +24,10 @@
#ifndef __AVI_MJPEG_H__
#define __AVI_MJPEG_H__
-void *avi_converter_from_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size);
+void *avi_converter_from_mjpeg(AviMovie *movie,
+ int stream,
+ unsigned char *buffer,
+ const size_t *size);
void *avi_converter_to_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size);
#endif /* __AVI_MJPEG_H__ */
diff --git a/source/blender/io/avi/intern/avi_rgb.c b/source/blender/io/avi/intern/avi_rgb.c
index 6f4f33d72d1..44542af96ae 100644
--- a/source/blender/io/avi/intern/avi_rgb.c
+++ b/source/blender/io/avi/intern/avi_rgb.c
@@ -37,7 +37,10 @@
/* implementation */
-void *avi_converter_from_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size)
+void *avi_converter_from_avi_rgb(AviMovie *movie,
+ int stream,
+ unsigned char *buffer,
+ const size_t *size)
{
unsigned char *buf;
AviBitmapInfoHeader *bi;
diff --git a/source/blender/io/avi/intern/avi_rgb.h b/source/blender/io/avi/intern/avi_rgb.h
index 7c8ce590d27..3a37fad94e1 100644
--- a/source/blender/io/avi/intern/avi_rgb.h
+++ b/source/blender/io/avi/intern/avi_rgb.h
@@ -24,7 +24,10 @@
#ifndef __AVI_RGB_H__
#define __AVI_RGB_H__
-void *avi_converter_from_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size);
+void *avi_converter_from_avi_rgb(AviMovie *movie,
+ int stream,
+ unsigned char *buffer,
+ const size_t *size);
void *avi_converter_to_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size);
#endif /* __AVI_RGB_H__ */
diff --git a/source/blender/io/collada/ArmatureImporter.cpp b/source/blender/io/collada/ArmatureImporter.cpp
index 3755b71f300..bd5bd913a18 100644
--- a/source/blender/io/collada/ArmatureImporter.cpp
+++ b/source/blender/io/collada/ArmatureImporter.cpp
@@ -969,8 +969,8 @@ void ArmatureImporter::make_shape_keys(bContext *C)
/* insert other shape keys */
for (int i = 0; i < morphTargetIds.getCount(); i++) {
- /* better to have a separate map of morph objects,
- * This'll do for now since only mesh morphing is imported */
+ /* Better to have a separate map of morph objects,
+ * This will do for now since only mesh morphing is imported. */
Mesh *me = this->mesh_importer->get_mesh_by_geom_uid(morphTargetIds[i]);
diff --git a/source/blender/io/collada/MeshImporter.cpp b/source/blender/io/collada/MeshImporter.cpp
index 495af60488b..9fbb7324f8f 100644
--- a/source/blender/io/collada/MeshImporter.cpp
+++ b/source/blender/io/collada/MeshImporter.cpp
@@ -205,7 +205,7 @@ MeshImporter::MeshImporter(
}
bool MeshImporter::set_poly_indices(
- MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count)
+ MPoly *mpoly, MLoop *mloop, int loop_index, const unsigned int *indices, int loop_count)
{
mpoly->loopstart = loop_index;
mpoly->totloop = loop_count;
diff --git a/source/blender/io/collada/MeshImporter.h b/source/blender/io/collada/MeshImporter.h
index 2f2a18ff11a..18e56e8f9df 100644
--- a/source/blender/io/collada/MeshImporter.h
+++ b/source/blender/io/collada/MeshImporter.h
@@ -105,7 +105,7 @@ class MeshImporter : public MeshImporterBase {
std::multimap<COLLADAFW::UniqueId, COLLADAFW::UniqueId> materials_mapped_to_geom;
bool set_poly_indices(
- MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count);
+ MPoly *mpoly, MLoop *mloop, int loop_index, const unsigned int *indices, int loop_count);
void set_face_uv(MLoopUV *mloopuv,
UVDataWrapper &uvs,
diff --git a/source/blender/io/collada/TransformWriter.cpp b/source/blender/io/collada/TransformWriter.cpp
index 0a66db72cb9..b7455837379 100644
--- a/source/blender/io/collada/TransformWriter.cpp
+++ b/source/blender/io/collada/TransformWriter.cpp
@@ -129,9 +129,9 @@ void TransformWriter::add_node_transform_identity(COLLADASW::Node &node,
}
void TransformWriter::add_transform(COLLADASW::Node &node,
- float loc[3],
- float rot[3],
- float scale[3])
+ const float loc[3],
+ const float rot[3],
+ const float scale[3])
{
node.addScale("scale", scale[0], scale[1], scale[2]);
node.addRotateZ("rotationZ", RAD2DEGF(rot[2]));
diff --git a/source/blender/io/collada/TransformWriter.h b/source/blender/io/collada/TransformWriter.h
index 3c71fc9d36e..db8ef3f5ee2 100644
--- a/source/blender/io/collada/TransformWriter.h
+++ b/source/blender/io/collada/TransformWriter.h
@@ -42,7 +42,10 @@ class TransformWriter {
void add_node_transform_identity(COLLADASW::Node &node, BCExportSettings &export_settings);
private:
- void add_transform(COLLADASW::Node &node, float loc[3], float rot[3], float scale[3]);
+ void add_transform(COLLADASW::Node &node,
+ const float loc[3],
+ const float rot[3],
+ const float scale[3]);
};
#endif
diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp
index e74808df466..2c54a49198a 100644
--- a/source/blender/io/collada/collada_utils.cpp
+++ b/source/blender/io/collada/collada_utils.cpp
@@ -603,7 +603,7 @@ float BoneExtended::get_roll()
return this->roll;
}
-void BoneExtended::set_tail(float vec[])
+void BoneExtended::set_tail(const float vec[])
{
this->tail[0] = vec[0];
this->tail[1] = vec[1];
diff --git a/source/blender/io/collada/collada_utils.h b/source/blender/io/collada/collada_utils.h
index b1ec2c8b81a..11a9376294b 100644
--- a/source/blender/io/collada/collada_utils.h
+++ b/source/blender/io/collada/collada_utils.h
@@ -343,7 +343,7 @@ class BoneExtended {
bool has_roll();
float get_roll();
- void set_tail(float vec[]);
+ void set_tail(const float vec[]);
float *get_tail();
bool has_tail();
diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt
index 4ed6f12762e..708f24ca0e2 100644
--- a/source/blender/io/common/CMakeLists.txt
+++ b/source/blender/io/common/CMakeLists.txt
@@ -31,8 +31,13 @@ set(INC_SYS
set(SRC
intern/abstract_hierarchy_iterator.cc
+ intern/dupli_parent_finder.cc
+ intern/dupli_persistent_id.cc
+ intern/object_identifier.cc
IO_abstract_hierarchy_iterator.h
+ IO_dupli_persistent_id.hh
+ intern/dupli_parent_finder.hh
)
set(LIB
diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
index 5f84fd48b71..2669f137fd4 100644
--- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h
+++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
@@ -36,6 +36,8 @@
#ifndef __ABSTRACT_HIERARCHY_ITERATOR_H__
#define __ABSTRACT_HIERARCHY_ITERATOR_H__
+#include "IO_dupli_persistent_id.hh"
+
#include <map>
#include <set>
#include <string>
@@ -52,6 +54,7 @@ namespace blender {
namespace io {
class AbstractHierarchyWriter;
+class DupliParentFinder;
/* HierarchyContext structs are created by the AbstractHierarchyIterator. Each HierarchyContext
* struct contains everything necessary to export a single object to a file. */
@@ -60,6 +63,7 @@ struct HierarchyContext {
Object *object; /* Evaluated object. */
Object *export_parent;
Object *duplicator;
+ PersistentID persistent_id;
float matrix_world[4][4];
std::string export_name;
@@ -161,6 +165,35 @@ class EnsuredWriter {
AbstractHierarchyWriter *operator->();
};
+/* Unique identifier for a (potentially duplicated) object.
+ *
+ * Instances of this class serve as key in the export graph of the
+ * AbstractHierarchyIterator. */
+class ObjectIdentifier {
+ public:
+ Object *object;
+ Object *duplicated_by; /* nullptr for real objects. */
+ PersistentID persistent_id;
+
+ protected:
+ ObjectIdentifier(Object *object, Object *duplicated_by, const PersistentID &persistent_id);
+
+ public:
+ ObjectIdentifier(const ObjectIdentifier &other);
+ ~ObjectIdentifier();
+
+ static ObjectIdentifier for_graph_root();
+ static ObjectIdentifier for_real_object(Object *object);
+ static ObjectIdentifier for_hierarchy_context(const HierarchyContext *context);
+ static ObjectIdentifier for_duplicated_object(const DupliObject *dupli_object,
+ Object *duplicated_by);
+
+ bool is_root() const;
+};
+
+bool operator<(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b);
+bool operator==(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b);
+
/* 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.
*
@@ -172,14 +205,10 @@ class AbstractHierarchyIterator {
public:
/* Mapping from export path to writer. */
typedef std::map<std::string, AbstractHierarchyWriter *> 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<Object *, Object *> DupliAndDuplicator;
/* All the children of some object, as per the export hierarchy. */
typedef std::set<HierarchyContext *> ExportChildren;
/* Mapping from an object and its duplicator to the object's export-children. */
- typedef std::map<DupliAndDuplicator, ExportChildren> ExportGraph;
+ typedef std::map<ObjectIdentifier, ExportChildren> 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<ID *, std::string> ExportPathMap;
@@ -237,7 +266,7 @@ class AbstractHierarchyIterator {
void visit_object(Object *object, Object *export_parent, bool weak_export);
void visit_dupli_object(DupliObject *dupli_object,
Object *duplicator,
- const std::set<Object *> &dupli_set);
+ const DupliParentFinder &dupli_parent_finder);
void context_update_for_graph_index(HierarchyContext *context,
const ExportGraph::key_type &graph_index) const;
@@ -291,8 +320,10 @@ class AbstractHierarchyIterator {
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<Object *> &dupli_set);
+ virtual ExportGraph::key_type determine_graph_index_dupli(
+ const HierarchyContext *context,
+ const DupliObject *dupli_object,
+ const DupliParentFinder &dupli_parent_finder);
/* 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
diff --git a/source/blender/io/common/IO_dupli_persistent_id.hh b/source/blender/io/common/IO_dupli_persistent_id.hh
new file mode 100644
index 00000000000..5dc54164684
--- /dev/null
+++ b/source/blender/io/common/IO_dupli_persistent_id.hh
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+#ifndef __IO_COMMON_DUPLI_PERSISTENT_ID_H__
+#define __IO_COMMON_DUPLI_PERSISTENT_ID_H__
+
+#include "BKE_duplilist.h"
+
+#include "DNA_object_types.h" /* For MAX_DUPLI_RECUR */
+
+#include <array>
+#include <optional>
+#include <ostream>
+
+namespace blender::io {
+
+/* Wrapper for DupliObject::persistent_id that can act as a map key. */
+class PersistentID {
+ protected:
+ constexpr static int array_length_ = MAX_DUPLI_RECUR;
+ typedef std::array<int, array_length_> PIDArray;
+ PIDArray persistent_id_;
+
+ explicit PersistentID(const PIDArray &persistent_id_values);
+
+ public:
+ PersistentID();
+ explicit PersistentID(const DupliObject *dupli_ob);
+
+ /* Return true iff the persistent IDs are the same, ignoring the first digit. */
+ bool is_from_same_instancer_as(const PersistentID &other) const;
+
+ /* Construct the persistent ID of this instance's instancer. */
+ PersistentID instancer_pid() const;
+
+ /* Construct a string representation by reversing the persistent ID.
+ * In case of a duplicator that is duplicated itself as well, this
+ * results in strings like:
+ * "3" for the duplicated duplicator, and
+ * "3-0", "3-1", etc. for its duplis. */
+ std::string as_object_name_suffix() const;
+
+ friend bool operator==(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b);
+ friend bool operator<(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b);
+ friend std::ostream &operator<<(std::ostream &os, const PersistentID &persistent_id);
+
+ private:
+ void copy_values_from(const PIDArray &persistent_id_values);
+};
+
+} // namespace blender::io
+
+#endif // __IO_COMMON_DUPLI_PARENT_FINDER_H__
diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
index dce6b8e178b..3622c1eb7cd 100644
--- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
+++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
@@ -17,6 +17,7 @@
* All rights reserved.
*/
#include "IO_abstract_hierarchy_iterator.h"
+#include "dupli_parent_finder.hh"
#include <iostream>
#include <limits.h>
@@ -200,9 +201,9 @@ void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &grap
{
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;
+ const ObjectIdentifier &parent_info = map_iter.first;
+ const Object *const export_parent = parent_info.object;
+ const Object *const duplicator = parent_info.duplicated_by;
if (duplicator != nullptr) {
printf(" DU %s (as dupped by %s):\n",
@@ -217,7 +218,7 @@ void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &grap
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->export_name.c_str(),
child_ctx->weak_export ? " (weak)" : "",
child_ctx->original_export_path.empty() ?
"" :
@@ -225,7 +226,7 @@ void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &grap
}
else {
printf(" - %s (dup by %s%s) %s\n",
- child_ctx->object->id.name + 2,
+ child_ctx->export_name.c_str(),
child_ctx->duplicator->id.name + 2,
child_ctx->weak_export ? ", weak" : "",
child_ctx->original_export_path.empty() ?
@@ -234,7 +235,7 @@ void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &grap
}
}
}
- printf(" (Total graph size: %zu objects\n", total_graph_size);
+ printf(" (Total graph size: %zu objects)\n", total_graph_size);
}
void AbstractHierarchyIterator::export_graph_construct()
@@ -257,22 +258,21 @@ void AbstractHierarchyIterator::export_graph_construct()
// 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<Object *> dupli_set;
+ DupliParentFinder dupli_parent_finder;
+
LISTBASE_FOREACH (DupliObject *, dupli_object, lb) {
+ PersistentID persistent_id(dupli_object);
if (!should_visit_dupli_object(dupli_object)) {
continue;
}
- dupli_set.insert(dupli_object->ob);
+ dupli_parent_finder.insert(dupli_object);
}
LISTBASE_FOREACH (DupliObject *, dupli_object, lb) {
if (!should_visit_dupli_object(dupli_object)) {
continue;
}
-
- visit_dupli_object(dupli_object, object, dupli_set);
+ visit_dupli_object(dupli_object, object, dupli_parent_finder);
}
}
@@ -291,29 +291,30 @@ void AbstractHierarchyIterator::connect_loose_objects()
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));
+ ObjectIdentifier child_oid = ObjectIdentifier::for_hierarchy_context(child);
+ loose_objects_graph.erase(child_oid);
}
}
// The root of the hierarchy is always found, so it's never considered 'loose'.
- loose_objects_graph.erase(std::make_pair(nullptr, nullptr));
+ loose_objects_graph.erase(ObjectIdentifier::for_graph_root());
// 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;
+ const ObjectIdentifier &graph_key = map_iter.first;
+ Object *object = graph_key.object;
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));
+ ObjectIdentifier::for_real_object(object->parent));
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 <nullptr, nullptr> as root and thus will cause a break.
+ // root as nullptr and thus will cause a break above.
BLI_assert(object->parent != nullptr);
object = object->parent;
@@ -326,10 +327,8 @@ static bool remove_weak_subtrees(const HierarchyContext *context,
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 ObjectIdentifier map_key = ObjectIdentifier::for_hierarchy_context(context);
- const AbstractHierarchyIterator::DupliAndDuplicator map_key = std::make_pair(object, duplicator);
AbstractHierarchyIterator::ExportGraph::const_iterator child_iterator;
child_iterator = input_graph.find(map_key);
@@ -399,7 +398,7 @@ void AbstractHierarchyIterator::visit_object(Object *object,
// 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);
+ ExportGraph::key_type object_key = ObjectIdentifier::for_real_object(object);
if (export_graph_.find(object_key) == export_graph_.end()) {
export_graph_[object_key] = ExportChildren();
}
@@ -408,16 +407,17 @@ void AbstractHierarchyIterator::visit_object(Object *object,
AbstractHierarchyIterator::ExportGraph::key_type AbstractHierarchyIterator::
determine_graph_index_object(const HierarchyContext *context)
{
- return std::make_pair(context->export_parent, nullptr);
+ return ObjectIdentifier::for_real_object(context->export_parent);
}
void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object,
Object *duplicator,
- const std::set<Object *> &dupli_set)
+ const DupliParentFinder &dupli_parent_finder)
{
HierarchyContext *context = new HierarchyContext();
context->object = dupli_object->ob;
context->duplicator = duplicator;
+ context->persistent_id = PersistentID(dupli_object);
context->weak_export = false;
context->export_path = "";
context->original_export_path = "";
@@ -427,38 +427,36 @@ void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object,
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());
+ std::stringstream export_name_stream;
+ export_name_stream << get_object_name(context->object) << "-"
+ << context->persistent_id.as_object_name_suffix();
+ context->export_name = make_valid_name(export_name_stream.str());
- ExportGraph::key_type graph_index = determine_graph_index_dupli(context, dupli_set);
+ ExportGraph::key_type graph_index = determine_graph_index_dupli(
+ context, dupli_object, dupli_parent_finder);
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<Object *> &dupli_set)
+ const DupliObject *dupli_object,
+ const DupliParentFinder &dupli_parent_finder)
{
- /* 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. */
+ const DupliObject *dupli_parent = dupli_parent_finder.find_suitable_export_parent(dupli_object);
- 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);
+ if (dupli_parent != nullptr) {
+ return ObjectIdentifier::for_duplicated_object(dupli_parent, context->duplicator);
}
- return std::make_pair(context->duplicator, nullptr);
+ return ObjectIdentifier::for_real_object(context->duplicator);
}
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;
+ context->export_parent = graph_index.object;
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
@@ -470,11 +468,7 @@ void AbstractHierarchyIterator::context_update_for_graph_index(
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)];
+ return export_graph_[ObjectIdentifier::for_hierarchy_context(context)];
}
void AbstractHierarchyIterator::determine_export_paths(const HierarchyContext *parent_context)
diff --git a/source/blender/io/common/intern/dupli_parent_finder.cc b/source/blender/io/common/intern/dupli_parent_finder.cc
new file mode 100644
index 00000000000..73e33eff164
--- /dev/null
+++ b/source/blender/io/common/intern/dupli_parent_finder.cc
@@ -0,0 +1,104 @@
+/*
+ * 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 "dupli_parent_finder.hh"
+
+#include "BLI_utildefines.h"
+
+#include <iostream>
+
+namespace blender::io {
+
+DupliParentFinder::DupliParentFinder()
+{
+}
+
+DupliParentFinder::~DupliParentFinder()
+{
+}
+
+void DupliParentFinder::insert(const DupliObject *dupli_ob)
+{
+ dupli_set_.insert(dupli_ob->ob);
+
+ PersistentID dupli_pid(dupli_ob);
+ pid_to_dupli_[dupli_pid] = dupli_ob;
+ instancer_pid_to_duplis_[dupli_pid.instancer_pid()].insert(dupli_ob);
+}
+
+bool DupliParentFinder::is_duplicated(const Object *object) const
+{
+ return dupli_set_.find(object) != dupli_set_.end();
+}
+
+const DupliObject *DupliParentFinder::find_suitable_export_parent(
+ const DupliObject *dupli_ob) const
+{
+ if (dupli_ob->ob->parent != nullptr) {
+ const DupliObject *parent = find_duplicated_parent(dupli_ob);
+ if (parent != nullptr) {
+ return parent;
+ }
+ }
+
+ return find_instancer(dupli_ob);
+}
+
+const DupliObject *DupliParentFinder::find_duplicated_parent(const DupliObject *dupli_ob) const
+{
+ const PersistentID dupli_pid(dupli_ob);
+ PersistentID parent_pid = dupli_pid.instancer_pid();
+
+ const Object *parent_ob = dupli_ob->ob->parent;
+ BLI_assert(parent_ob != nullptr);
+
+ InstancerPIDToDuplisMap::const_iterator found = instancer_pid_to_duplis_.find(parent_pid);
+ if (found == instancer_pid_to_duplis_.end()) {
+ /* Unexpected, as there should be at least one entry here, for the dupli_ob itself. */
+ return nullptr;
+ }
+
+ for (const DupliObject *potential_parent_dupli : found->second) {
+ if (potential_parent_dupli->ob != parent_ob) {
+ continue;
+ }
+
+ PersistentID potential_parent_pid(potential_parent_dupli);
+ if (potential_parent_pid.is_from_same_instancer_as(dupli_pid)) {
+ return potential_parent_dupli;
+ }
+ }
+ return nullptr;
+}
+
+const DupliObject *DupliParentFinder::find_instancer(const DupliObject *dupli_ob) const
+{
+ PersistentID dupli_pid(dupli_ob);
+ PersistentID parent_pid = dupli_pid.instancer_pid();
+
+ PIDToDupliMap::const_iterator found = pid_to_dupli_.find(parent_pid);
+ if (found == pid_to_dupli_.end()) {
+ return nullptr;
+ }
+
+ const DupliObject *instancer_dupli = found->second;
+ return instancer_dupli;
+}
+
+} // namespace blender::io
diff --git a/source/blender/io/common/intern/dupli_parent_finder.hh b/source/blender/io/common/intern/dupli_parent_finder.hh
new file mode 100644
index 00000000000..e7e628665ee
--- /dev/null
+++ b/source/blender/io/common/intern/dupli_parent_finder.hh
@@ -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.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+#ifndef __IO_COMMON_DUPLI_PARENT_FINDER_H__
+#define __IO_COMMON_DUPLI_PARENT_FINDER_H__
+
+#include "IO_dupli_persistent_id.hh"
+
+#include "BKE_duplilist.h"
+
+#include <map>
+#include <set>
+
+namespace blender::io {
+
+/* Find relations between duplicated objects. This class should be instanced for a single real
+ * object, and fed its dupli-objects. */
+class DupliParentFinder final {
+ private:
+ /* To check whether an Object * is instanced by this duplicator. */
+ std::set<const Object *> dupli_set_;
+
+ /* To find the DupliObject given its Persistent ID. */
+ typedef std::map<const PersistentID, const DupliObject *> PIDToDupliMap;
+ PIDToDupliMap pid_to_dupli_;
+
+ /* Mapping from instancer PID to duplis instanced by it. */
+ typedef std::map<const PersistentID, std::set<const DupliObject *>> InstancerPIDToDuplisMap;
+ InstancerPIDToDuplisMap instancer_pid_to_duplis_;
+
+ public:
+ DupliParentFinder();
+ ~DupliParentFinder();
+
+ void insert(const DupliObject *dupli_ob);
+
+ bool is_duplicated(const Object *object) const;
+ const DupliObject *find_suitable_export_parent(const DupliObject *dupli_ob) const;
+
+ private:
+ const DupliObject *find_duplicated_parent(const DupliObject *dupli_ob) const;
+ const DupliObject *find_instancer(const DupliObject *dupli_ob) const;
+};
+
+} // namespace blender::io
+
+#endif \ No newline at end of file
diff --git a/source/blender/io/common/intern/dupli_persistent_id.cc b/source/blender/io/common/intern/dupli_persistent_id.cc
new file mode 100644
index 00000000000..b40cd83ef44
--- /dev/null
+++ b/source/blender/io/common/intern/dupli_persistent_id.cc
@@ -0,0 +1,167 @@
+/*
+ * 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 "dupli_parent_finder.hh"
+
+#include <climits>
+#include <cstring>
+#include <ostream>
+#include <sstream>
+
+namespace blender::io {
+
+PersistentID::PersistentID()
+{
+ persistent_id_[0] = INT_MAX;
+}
+
+PersistentID::PersistentID(const DupliObject *dupli_ob)
+{
+ for (int index = 0; index < array_length_; ++index) {
+ persistent_id_[index] = dupli_ob->persistent_id[index];
+ }
+}
+
+PersistentID::PersistentID(const PIDArray &persistent_id_values)
+{
+ persistent_id_ = persistent_id_values;
+}
+
+bool PersistentID::is_from_same_instancer_as(const PersistentID &other) const
+{
+ if (persistent_id_[0] == INT_MAX || other.persistent_id_[0] == INT_MAX) {
+ /* Either one or the other is not instanced at all, so definitely not from the same instancer.
+ */
+ return false;
+ }
+
+ /* Start at index 1 to skip the first digit. */
+ for (int index = 1; index < array_length_; ++index) {
+ const int pid_digit_a = persistent_id_[index];
+ const int pid_digit_b = other.persistent_id_[index];
+
+ if (pid_digit_a != pid_digit_b) {
+ return false;
+ }
+
+ if (pid_digit_a == INT_MAX) {
+ /* Both persistent IDs were identical so far, and this marks the end of the useful data. */
+ break;
+ }
+ }
+ return true;
+}
+
+PersistentID PersistentID::instancer_pid() const
+{
+ if (persistent_id_[0] == INT_MAX) {
+ return PersistentID();
+ }
+
+ /* Left-shift the entire PID by 1. */
+ PIDArray new_pid_values;
+ int index;
+ for (index = 0; index < array_length_ - 1; ++index) {
+ new_pid_values[index] = persistent_id_[index + 1];
+ }
+ new_pid_values[index] = INT_MAX;
+
+ return PersistentID(new_pid_values);
+}
+
+std::string PersistentID::as_object_name_suffix() const
+{
+ std::stringstream stream;
+
+ /* Find one past the last index. */
+ int index;
+ for (index = 0; index < array_length_ && persistent_id_[index] < INT_MAX; ++index) {
+ ;
+ }
+
+ /* Iterate backward to construct the string. */
+ --index;
+ for (; index >= 0; --index) {
+ stream << persistent_id_[index];
+ if (index > 0) {
+ stream << "-";
+ }
+ }
+
+ return stream.str();
+}
+
+bool operator<(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b)
+{
+ const PersistentID::PIDArray &pid_a = persistent_id_a.persistent_id_;
+ const PersistentID::PIDArray &pid_b = persistent_id_b.persistent_id_;
+
+ for (int index = 0; index < PersistentID::array_length_; ++index) {
+ const int pid_digit_a = pid_a[index];
+ const int pid_digit_b = pid_b[index];
+
+ if (pid_digit_a != pid_digit_b) {
+ return pid_digit_a < pid_digit_b;
+ }
+
+ if (pid_a[index] == INT_MAX) {
+ break;
+ }
+ }
+ /* Both Persistent IDs were equal, so not less-than. */
+ return false;
+}
+
+bool operator==(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b)
+{
+ const PersistentID::PIDArray &pid_a = persistent_id_a.persistent_id_;
+ const PersistentID::PIDArray &pid_b = persistent_id_b.persistent_id_;
+
+ for (int index = 0; index < PersistentID::array_length_; ++index) {
+ const int pid_digit_a = pid_a[index];
+ const int pid_digit_b = pid_b[index];
+
+ if (pid_digit_a != pid_digit_b) {
+ return false;
+ }
+
+ if (pid_a[index] == INT_MAX) {
+ break;
+ }
+ }
+ return true;
+}
+
+std::ostream &operator<<(std::ostream &os, const PersistentID &persistent_id)
+{
+ if (persistent_id.persistent_id_[0] == INT_MAX) {
+ return os;
+ }
+
+ const PersistentID::PIDArray &pid_array = persistent_id.persistent_id_;
+ for (int index = 0; index < PersistentID::array_length_ && pid_array[index] < INT_MAX; ++index) {
+ if (index > 0) {
+ os << "-";
+ }
+ os << pid_array[index];
+ }
+ return os;
+}
+
+} // namespace blender::io
diff --git a/source/blender/io/common/intern/object_identifier.cc b/source/blender/io/common/intern/object_identifier.cc
new file mode 100644
index 00000000000..696bc5d2c34
--- /dev/null
+++ b/source/blender/io/common/intern/object_identifier.cc
@@ -0,0 +1,116 @@
+/*
+ * 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 "BKE_duplilist.h"
+
+extern "C" {
+#include <limits.h> /* For INT_MAX. */
+}
+#include <cstring>
+#include <sstream>
+
+namespace blender {
+namespace io {
+
+ObjectIdentifier::ObjectIdentifier(Object *object,
+ Object *duplicated_by,
+ const PersistentID &persistent_id)
+ : object(object), duplicated_by(duplicated_by), persistent_id(persistent_id)
+{
+}
+
+ObjectIdentifier::ObjectIdentifier(const ObjectIdentifier &other)
+ : object(other.object), duplicated_by(other.duplicated_by), persistent_id(other.persistent_id)
+{
+}
+
+ObjectIdentifier::~ObjectIdentifier()
+{
+}
+
+ObjectIdentifier ObjectIdentifier::for_real_object(Object *object)
+{
+ return ObjectIdentifier(object, nullptr, PersistentID());
+}
+
+ObjectIdentifier ObjectIdentifier::for_hierarchy_context(const HierarchyContext *context)
+{
+ if (context == nullptr) {
+ return for_graph_root();
+ }
+ if (context->duplicator != nullptr) {
+ return ObjectIdentifier(context->object, context->duplicator, context->persistent_id);
+ }
+ return for_real_object(context->object);
+}
+
+ObjectIdentifier ObjectIdentifier::for_duplicated_object(const DupliObject *dupli_object,
+ Object *duplicated_by)
+{
+ return ObjectIdentifier(dupli_object->ob, duplicated_by, PersistentID(dupli_object));
+}
+
+ObjectIdentifier ObjectIdentifier::for_graph_root()
+{
+ return ObjectIdentifier(nullptr, nullptr, PersistentID());
+}
+
+bool ObjectIdentifier::is_root() const
+{
+ return object == nullptr;
+}
+
+bool operator<(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b)
+{
+ if (obj_ident_a.object != obj_ident_b.object) {
+ return obj_ident_a.object < obj_ident_b.object;
+ }
+
+ if (obj_ident_a.duplicated_by != obj_ident_b.duplicated_by) {
+ return obj_ident_a.duplicated_by < obj_ident_b.duplicated_by;
+ }
+
+ if (obj_ident_a.duplicated_by == nullptr) {
+ /* Both are real objects, no need to check the persistent ID. */
+ return false;
+ }
+
+ /* Same object, both are duplicated, use the persistent IDs to determine order. */
+ return obj_ident_a.persistent_id < obj_ident_b.persistent_id;
+}
+
+bool operator==(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b)
+{
+ if (obj_ident_a.object != obj_ident_b.object) {
+ return false;
+ }
+ if (obj_ident_a.duplicated_by != obj_ident_b.duplicated_by) {
+ return false;
+ }
+ if (obj_ident_a.duplicated_by == nullptr) {
+ return true;
+ }
+
+ /* Same object, both are duplicated, use the persistent IDs to determine equality. */
+ return obj_ident_a.persistent_id == obj_ident_b.persistent_id;
+}
+
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/usd/intern/usd_capi.cc b/source/blender/io/usd/intern/usd_capi.cc
index 2d3972410a4..98aef62f38e 100644
--- a/source/blender/io/usd/intern/usd_capi.cc
+++ b/source/blender/io/usd/intern/usd_capi.cc
@@ -59,7 +59,12 @@ struct ExportJobData {
bool export_ok;
};
-static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
+static void export_startjob(void *customdata,
+ /* Cannot be const, this function implements wm_jobs_start_callback.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ short *stop,
+ short *do_update,
+ float *progress)
{
ExportJobData *data = static_cast<ExportJobData *>(customdata);
data->export_ok = false;