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:
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;