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/ABC_alembic.h10
-rw-r--r--source/blender/io/alembic/CMakeLists.txt4
-rw-r--r--source/blender/io/alembic/exporter/abc_export_capi.cc44
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc13
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.h6
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_curves.cc6
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_nurbs.cc2
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_transform.cc3
-rw-r--r--source/blender/io/alembic/intern/abc_axis_conversion.cc2
-rw-r--r--source/blender/io/alembic/intern/abc_axis_conversion.h2
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.cc2
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.cc2
-rw-r--r--source/blender/io/alembic/intern/alembic_capi.cc146
-rw-r--r--source/blender/io/avi/AVI_avi.h5
-rw-r--r--source/blender/io/avi/intern/avi_endian.h5
-rw-r--r--source/blender/io/avi/intern/avi_intern.h5
-rw-r--r--source/blender/io/avi/intern/avi_mjpeg.c15
-rw-r--r--source/blender/io/avi/intern/avi_mjpeg.h10
-rw-r--r--source/blender/io/avi/intern/avi_rgb.c5
-rw-r--r--source/blender/io/avi/intern/avi_rgb.h10
-rw-r--r--source/blender/io/avi/intern/avi_rgb32.h5
-rw-r--r--source/blender/io/collada/AnimationClipExporter.h5
-rw-r--r--source/blender/io/collada/AnimationExporter.cpp2
-rw-r--r--source/blender/io/collada/AnimationExporter.h5
-rw-r--r--source/blender/io/collada/AnimationImporter.cpp4
-rw-r--r--source/blender/io/collada/AnimationImporter.h5
-rw-r--r--source/blender/io/collada/ArmatureExporter.h5
-rw-r--r--source/blender/io/collada/ArmatureImporter.cpp4
-rw-r--r--source/blender/io/collada/ArmatureImporter.h5
-rw-r--r--source/blender/io/collada/BCAnimationCurve.h5
-rw-r--r--source/blender/io/collada/BCAnimationSampler.h5
-rw-r--r--source/blender/io/collada/BCMath.cpp23
-rw-r--r--source/blender/io/collada/BCMath.h5
-rw-r--r--source/blender/io/collada/BCSampleData.h5
-rw-r--r--source/blender/io/collada/BlenderContext.h5
-rw-r--r--source/blender/io/collada/BlenderTypes.h5
-rw-r--r--source/blender/io/collada/CameraExporter.h5
-rw-r--r--source/blender/io/collada/ControllerExporter.h5
-rw-r--r--source/blender/io/collada/DocumentExporter.h5
-rw-r--r--source/blender/io/collada/DocumentImporter.h5
-rw-r--r--source/blender/io/collada/EffectExporter.h5
-rw-r--r--source/blender/io/collada/ErrorHandler.h5
-rw-r--r--source/blender/io/collada/ExportSettings.h5
-rw-r--r--source/blender/io/collada/ExtraHandler.h5
-rw-r--r--source/blender/io/collada/ExtraTags.h5
-rw-r--r--source/blender/io/collada/GeometryExporter.h5
-rw-r--r--source/blender/io/collada/ImageExporter.h5
-rw-r--r--source/blender/io/collada/ImportSettings.h5
-rw-r--r--source/blender/io/collada/InstanceWriter.h5
-rw-r--r--source/blender/io/collada/LightExporter.h5
-rw-r--r--source/blender/io/collada/MaterialExporter.h5
-rw-r--r--source/blender/io/collada/Materials.h5
-rw-r--r--source/blender/io/collada/MeshImporter.cpp2
-rw-r--r--source/blender/io/collada/MeshImporter.h7
-rw-r--r--source/blender/io/collada/SceneExporter.h5
-rw-r--r--source/blender/io/collada/SkinInfo.h5
-rw-r--r--source/blender/io/collada/TransformReader.h5
-rw-r--r--source/blender/io/collada/TransformWriter.cpp6
-rw-r--r--source/blender/io/collada/TransformWriter.h10
-rw-r--r--source/blender/io/collada/collada.h5
-rw-r--r--source/blender/io/collada/collada_internal.cpp2
-rw-r--r--source/blender/io/collada/collada_internal.h5
-rw-r--r--source/blender/io/collada/collada_utils.cpp9
-rw-r--r--source/blender/io/collada/collada_utils.h7
-rw-r--r--source/blender/io/common/CMakeLists.txt19
-rw-r--r--source/blender/io/common/IO_abstract_hierarchy_iterator.h61
-rw-r--r--source/blender/io/common/IO_dupli_persistent_id.hh65
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator.cc108
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc319
-rw-r--r--source/blender/io/common/intern/dupli_parent_finder.cc104
-rw-r--r--source/blender/io/common/intern/dupli_parent_finder.hh59
-rw-r--r--source/blender/io/common/intern/dupli_persistent_id.cc167
-rw-r--r--source/blender/io/common/intern/hierarchy_context_order_test.cc127
-rw-r--r--source/blender/io/common/intern/object_identifier.cc116
-rw-r--r--source/blender/io/common/intern/object_identifier_test.cc234
-rw-r--r--source/blender/io/usd/CMakeLists.txt12
-rw-r--r--source/blender/io/usd/intern/usd_capi.cc7
-rw-r--r--source/blender/io/usd/intern/usd_exporter_context.h5
-rw-r--r--source/blender/io/usd/intern/usd_hierarchy_iterator.cc8
-rw-r--r--source/blender/io/usd/intern/usd_hierarchy_iterator.h7
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.h5
-rw-r--r--source/blender/io/usd/intern/usd_writer_camera.h5
-rw-r--r--source/blender/io/usd/intern/usd_writer_hair.cc2
-rw-r--r--source/blender/io/usd/intern/usd_writer_hair.h5
-rw-r--r--source/blender/io/usd/intern/usd_writer_light.h5
-rw-r--r--source/blender/io/usd/intern/usd_writer_mesh.cc2
-rw-r--r--source/blender/io/usd/intern/usd_writer_mesh.h5
-rw-r--r--source/blender/io/usd/intern/usd_writer_metaball.h5
-rw-r--r--source/blender/io/usd/intern/usd_writer_transform.cc3
-rw-r--r--source/blender/io/usd/intern/usd_writer_transform.h5
-rw-r--r--source/blender/io/usd/tests/usd_stage_creation_test.cc70
-rw-r--r--source/blender/io/usd/usd.h5
92 files changed, 1736 insertions, 337 deletions
diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h
index ddf75aa3258..67f8aeb0a67 100644
--- a/source/blender/io/alembic/ABC_alembic.h
+++ b/source/blender/io/alembic/ABC_alembic.h
@@ -128,6 +128,16 @@ struct CacheReader *CacheReader_open_alembic_object(struct AbcArchiveHandle *han
struct Object *object,
const char *object_path);
+bool ABC_has_vec3_array_property_named(struct CacheReader *reader, const char *name);
+
+/* r_vertex_velocities should point to a preallocated array of num_vertices floats */
+int ABC_read_velocity_cache(struct CacheReader *reader,
+ const char *velocity_name,
+ float time,
+ float fps,
+ int num_vertices,
+ float *r_vertex_velocities);
+
#ifdef __cplusplus
}
#endif
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..8c5f3d89870 100644
--- a/source/blender/io/alembic/exporter/abc_export_capi.cc
+++ b/source/blender/io/alembic/exporter/abc_export_capi.cc
@@ -48,6 +48,7 @@
static CLG_LogRef LOG = {"io.alembic"};
#include <algorithm>
+#include <memory>
struct ExportJobData {
Main *bmain;
@@ -73,7 +74,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;
@@ -98,17 +104,41 @@ static void export_startjob(void *customdata, short *stop, short *do_update, flo
const bool export_animation = (data->params.frame_start != data->params.frame_end);
// Create the Alembic archive.
- ABCArchive abc_archive(data->bmain, scene, data->params, std::string(data->filename));
+ std::unique_ptr<ABCArchive> abc_archive;
+ try {
+ abc_archive = std::make_unique<ABCArchive>(
+ data->bmain, scene, data->params, std::string(data->filename));
+ }
+ catch (const std::exception &ex) {
+ std::stringstream error_message_stream;
+ error_message_stream << "Error writing to " << data->filename;
+ const std::string &error_message = error_message_stream.str();
+
+ // The exception message can be very cryptic (just "iostream error" on Linux, for example), so
+ // better not to include it in the report.
+ CLOG_ERROR(&LOG, "%s: %s", error_message.c_str(), ex.what());
+ WM_report(RPT_ERROR, error_message.c_str());
+ data->export_ok = false;
+ return;
+ }
+ catch (...) {
+ // Unknown exception class, so we cannot include its message.
+ std::stringstream error_message_stream;
+ error_message_stream << "Unknown error writing to " << data->filename;
+ WM_report(RPT_ERROR, error_message_stream.str().c_str());
+ data->export_ok = false;
+ return;
+ }
- ABCHierarchyIterator iter(data->depsgraph, &abc_archive, data->params);
+ ABCHierarchyIterator iter(data->depsgraph, abc_archive.get(), data->params);
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();
+ 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;
@@ -123,7 +153,7 @@ static void export_startjob(void *customdata, short *stop, short *do_update, flo
BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain);
CLOG_INFO(&LOG, 2, "Exporting frame %.2f", frame);
- ExportSubset export_subset = abc_archive.export_subset_for_frame(frame);
+ ExportSubset export_subset = abc_archive->export_subset_for_frame(frame);
iter.set_export_subset(export_subset);
iter.iterate_and_write();
diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
index 90004c0e85b..5b1b1b60b48 100644
--- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
+++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
@@ -89,7 +89,7 @@ bool ABCHierarchyIterator::mark_as_weak_export(const Object *object) const
return false;
}
-void ABCHierarchyIterator::delete_object_writer(AbstractHierarchyWriter *writer)
+void ABCHierarchyIterator::release_writer(AbstractHierarchyWriter *writer)
{
delete writer;
}
@@ -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..bd7e3f27c67 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;
@@ -76,7 +78,7 @@ class ABCHierarchyIterator : public AbstractHierarchyIterator {
virtual AbstractHierarchyWriter *create_particle_writer(
const HierarchyContext *context) override;
- virtual void delete_object_writer(AbstractHierarchyWriter *writer) override;
+ virtual void release_writer(AbstractHierarchyWriter *writer) override;
private:
Alembic::Abc::OObject get_alembic_parent(const HierarchyContext *context) const;
diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.cc b/source/blender/io/alembic/exporter/abc_writer_curves.cc
index f2a46c5e4fe..6f185020b58 100644
--- a/source/blender/io/alembic/exporter/abc_writer_curves.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_curves.cc
@@ -80,9 +80,9 @@ void ABCCurveWriter::do_write(HierarchyContext &context)
std::vector<uint8_t> orders;
Imath::V3f temp_vert;
- Alembic::AbcGeom::BasisType curve_basis;
- Alembic::AbcGeom::CurveType curve_type;
- Alembic::AbcGeom::CurvePeriodicity periodicity;
+ Alembic::AbcGeom::BasisType curve_basis = Alembic::AbcGeom::kNoBasis;
+ Alembic::AbcGeom::CurveType curve_type = Alembic::AbcGeom::kVariableOrder;
+ Alembic::AbcGeom::CurvePeriodicity periodicity = Alembic::AbcGeom::kNonPeriodic;
Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
for (; nurbs; nurbs = nurbs->next) {
diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
index 1fd382214a6..14cb7773e67 100644
--- a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
@@ -64,7 +64,7 @@ void ABCNurbsWriter::create_alembic_objects(const HierarchyContext *context)
std::string patch_name = patch_name_stream.str();
CLOG_INFO(&LOG, 2, "exporting %s/%s", abc_parent_path, patch_name.c_str());
- ONuPatch nurbs(abc_parent, patch_name.c_str(), timesample_index_);
+ ONuPatch nurbs(abc_parent, patch_name, timesample_index_);
abc_nurbs_.push_back(nurbs);
abc_nurbs_schemas_.push_back(nurbs.getSchema());
}
diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.cc b/source/blender/io/alembic/exporter/abc_writer_transform.cc
index 65d6b7c5b41..39af99c142c 100644
--- a/source/blender/io/alembic/exporter/abc_writer_transform.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_transform.cc
@@ -107,6 +107,9 @@ bool ABCTransformWriter::check_is_animated(const HierarchyContext &context) cons
* depsgraph whether this object instance has a time source. */
return true;
}
+ if (check_has_physics(context)) {
+ return true;
+ }
return BKE_object_moves_in_time(context.object, context.animation_check_include_parent);
}
diff --git a/source/blender/io/alembic/intern/abc_axis_conversion.cc b/source/blender/io/alembic/intern/abc_axis_conversion.cc
index cebab1f2e41..396c8fdb28b 100644
--- a/source/blender/io/alembic/intern/abc_axis_conversion.cc
+++ b/source/blender/io/alembic/intern/abc_axis_conversion.cc
@@ -170,4 +170,4 @@ void create_transform_matrix(Object *obj,
} // namespace alembic
} // namespace io
-} // namespace blender \ No newline at end of file
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_axis_conversion.h b/source/blender/io/alembic/intern/abc_axis_conversion.h
index 9a19e9116be..645d9fc783b 100644
--- a/source/blender/io/alembic/intern/abc_axis_conversion.h
+++ b/source/blender/io/alembic/intern/abc_axis_conversion.h
@@ -100,4 +100,4 @@ void create_transform_matrix(Object *obj,
} // namespace alembic
} // namespace io
-} // namespace blender \ No newline at end of file
+} // namespace blender
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc
index 756dde3783c..98130eb28cd 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.cc
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc
@@ -104,7 +104,7 @@ static void assign_materials(Main *bmain,
for (; it != mat_index_map.end(); ++it) {
std::string mat_name = it->first;
- mat_iter = mat_map.find(mat_name.c_str());
+ mat_iter = mat_map.find(mat_name);
Material *assigned_mat;
diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc
index 39b9cd4c161..06b0c07f4c5 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.cc
+++ b/source/blender/io/alembic/intern/abc_reader_object.cc
@@ -236,7 +236,7 @@ Alembic::AbcGeom::IXform AbcObjectReader::xform()
* parent Alembic object should contain the transform. */
IObject abc_parent = m_iobject.getParent();
- /* The archive's top object can be recognised by not having a parent. */
+ /* The archive's top object can be recognized by not having a parent. */
if (abc_parent.getParent() && IXform::matches(abc_parent.getMetaData())) {
try {
return IXform(abc_parent, Alembic::AbcGeom::kWrapExisting);
diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc
index 7cde2d4fe73..eba7f64db02 100644
--- a/source/blender/io/alembic/intern/alembic_capi.cc
+++ b/source/blender/io/alembic/intern/alembic_capi.cc
@@ -22,6 +22,7 @@
#include <Alembic/AbcMaterial/IMaterial.h>
+#include "abc_axis_conversion.h"
#include "abc_reader_archive.h"
#include "abc_reader_camera.h"
#include "abc_reader_curves.h"
@@ -47,18 +48,13 @@
#include "BKE_lib_id.h"
#include "BKE_object.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#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
-#include "BKE_screen.h"
-#undef new
-
#include "BLI_compiler_compat.h"
#include "BLI_fileops.h"
#include "BLI_ghash.h"
@@ -70,7 +66,10 @@
#include "WM_api.h"
#include "WM_types.h"
+using Alembic::Abc::IV3fArrayProperty;
using Alembic::Abc::ObjectHeader;
+using Alembic::Abc::PropertyHeader;
+using Alembic::Abc::V3fArraySamplePtr;
using Alembic::AbcGeom::ICamera;
using Alembic::AbcGeom::ICurves;
using Alembic::AbcGeom::IFaceSet;
@@ -79,9 +78,11 @@ using Alembic::AbcGeom::INuPatch;
using Alembic::AbcGeom::IObject;
using Alembic::AbcGeom::IPoints;
using Alembic::AbcGeom::IPolyMesh;
+using Alembic::AbcGeom::IPolyMeshSchema;
using Alembic::AbcGeom::ISampleSelector;
using Alembic::AbcGeom::ISubD;
using Alembic::AbcGeom::IXform;
+using Alembic::AbcGeom::kWrapExisting;
using Alembic::AbcGeom::MetaData;
using Alembic::AbcMaterial::IMaterial;
@@ -859,3 +860,136 @@ CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle,
return reinterpret_cast<CacheReader *>(abc_reader);
}
+
+/* ************************************************************************** */
+
+static const PropertyHeader *get_property_header(const IPolyMeshSchema &schema, const char *name)
+{
+ const PropertyHeader *prop_header = schema.getPropertyHeader(name);
+
+ if (prop_header) {
+ return prop_header;
+ }
+
+ ICompoundProperty prop = schema.getArbGeomParams();
+
+ if (!has_property(prop, name)) {
+ return nullptr;
+ }
+
+ return prop.getPropertyHeader(name);
+}
+
+bool ABC_has_vec3_array_property_named(struct CacheReader *reader, const char *name)
+{
+ AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
+
+ if (!abc_reader) {
+ return false;
+ }
+
+ IObject iobject = abc_reader->iobject();
+
+ if (!iobject.valid()) {
+ return false;
+ }
+
+ const ObjectHeader &header = iobject.getHeader();
+
+ if (!IPolyMesh::matches(header)) {
+ return false;
+ }
+
+ IPolyMesh mesh(iobject, kWrapExisting);
+ IPolyMeshSchema schema = mesh.getSchema();
+
+ const PropertyHeader *prop_header = get_property_header(schema, name);
+
+ if (!prop_header) {
+ return false;
+ }
+
+ return IV3fArrayProperty::matches(prop_header->getMetaData());
+}
+
+static V3fArraySamplePtr get_velocity_prop(const IPolyMeshSchema &schema,
+ const ISampleSelector &iss,
+ const std::string &name)
+{
+ const PropertyHeader *prop_header = schema.getPropertyHeader(name);
+
+ if (prop_header) {
+ const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(schema, name, 0);
+ return velocity_prop.getValue(iss);
+ }
+
+ ICompoundProperty prop = schema.getArbGeomParams();
+
+ if (!has_property(prop, name)) {
+ return V3fArraySamplePtr();
+ }
+
+ const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(prop, name, 0);
+
+ if (velocity_prop) {
+ return velocity_prop.getValue(iss);
+ }
+
+ return V3fArraySamplePtr();
+}
+
+int ABC_read_velocity_cache(CacheReader *reader,
+ const char *velocity_name,
+ const float time,
+ float velocity_scale,
+ int num_vertices,
+ float *r_vertex_velocities)
+{
+ AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
+
+ if (!abc_reader) {
+ return -1;
+ }
+
+ IObject iobject = abc_reader->iobject();
+
+ if (!iobject.valid()) {
+ return -1;
+ }
+
+ const ObjectHeader &header = iobject.getHeader();
+
+ if (!IPolyMesh::matches(header)) {
+ return -1;
+ }
+
+ IPolyMesh mesh(iobject, kWrapExisting);
+ IPolyMeshSchema schema = mesh.getSchema();
+ ISampleSelector sample_sel(time);
+ const IPolyMeshSchema::Sample sample = schema.getValue(sample_sel);
+
+ V3fArraySamplePtr velocities = get_velocity_prop(schema, sample_sel, velocity_name);
+
+ if (!velocities) {
+ return -1;
+ }
+
+ float vel[3];
+
+ int num_velocity_vectors = static_cast<int>(velocities->size());
+
+ if (num_velocity_vectors != num_vertices) {
+ return -1;
+ }
+
+ for (size_t i = 0; i < velocities->size(); ++i) {
+ const Imath::V3f &vel_in = (*velocities)[i];
+ copy_zup_from_yup(vel, vel_in.getValue());
+
+ mul_v3_fl(vel, velocity_scale);
+
+ copy_v3_v3(r_vertex_velocities + i * 3, vel);
+ }
+
+ return num_vertices;
+}
diff --git a/source/blender/io/avi/AVI_avi.h b/source/blender/io/avi/AVI_avi.h
index 4f3aa720da3..ba1ef9261a0 100644
--- a/source/blender/io/avi/AVI_avi.h
+++ b/source/blender/io/avi/AVI_avi.h
@@ -39,8 +39,7 @@
* code. So we keep it like this.
*/
-#ifndef __AVI_AVI_H__
-#define __AVI_AVI_H__
+#pragma once
#include "BLI_sys_types.h"
#include <stdio.h> /* for FILE */
@@ -295,5 +294,3 @@ AviError AVI_write_frame(AviMovie *movie, int frame_num, ...);
* Unused but still external
*/
AviError AVI_print_error(AviError error);
-
-#endif /* __AVI_AVI_H__ */
diff --git a/source/blender/io/avi/intern/avi_endian.h b/source/blender/io/avi/intern/avi_endian.h
index d1253f488e7..88c13d17950 100644
--- a/source/blender/io/avi/intern/avi_endian.h
+++ b/source/blender/io/avi/intern/avi_endian.h
@@ -23,8 +23,7 @@
* This is external code.
*/
-#ifndef __AVI_ENDIAN_H__
-#define __AVI_ENDIAN_H__
+#pragma once
#define AVI_RAW 0
#define AVI_CHUNK 1
@@ -36,5 +35,3 @@
#define AVI_MJPEGU 7
void awrite(AviMovie *movie, void *datain, int block, int size, FILE *fp, int type);
-
-#endif /* __AVI_ENDIAN_H__ */
diff --git a/source/blender/io/avi/intern/avi_intern.h b/source/blender/io/avi/intern/avi_intern.h
index 6ce91ce7f70..28ab999f6e3 100644
--- a/source/blender/io/avi/intern/avi_intern.h
+++ b/source/blender/io/avi/intern/avi_intern.h
@@ -21,8 +21,7 @@
* \ingroup avi
*/
-#ifndef __AVI_INTERN_H__
-#define __AVI_INTERN_H__
+#pragma once
#include <stdio.h> /* for FILE */
@@ -61,5 +60,3 @@ int avi_get_data_id(AviFormat format, int stream);
int avi_get_format_type(AviFormat format);
int avi_get_format_fcc(AviFormat format);
int avi_get_format_compression(AviFormat format);
-
-#endif
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..3ee1b611f70 100644
--- a/source/blender/io/avi/intern/avi_mjpeg.h
+++ b/source/blender/io/avi/intern/avi_mjpeg.h
@@ -21,10 +21,10 @@
* \ingroup avi
*/
-#ifndef __AVI_MJPEG_H__
-#define __AVI_MJPEG_H__
+#pragma once
-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..aac8a2dffbf 100644
--- a/source/blender/io/avi/intern/avi_rgb.h
+++ b/source/blender/io/avi/intern/avi_rgb.h
@@ -21,10 +21,10 @@
* \ingroup avi
*/
-#ifndef __AVI_RGB_H__
-#define __AVI_RGB_H__
+#pragma once
-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/avi/intern/avi_rgb32.h b/source/blender/io/avi/intern/avi_rgb32.h
index eb4b9ca4e21..675b1ced41e 100644
--- a/source/blender/io/avi/intern/avi_rgb32.h
+++ b/source/blender/io/avi/intern/avi_rgb32.h
@@ -21,10 +21,7 @@
* \ingroup avi
*/
-#ifndef __AVI_RGB32_H__
-#define __AVI_RGB32_H__
+#pragma once
void *avi_converter_from_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size);
void *avi_converter_to_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size);
-
-#endif /* __AVI_RGB32_H__ */
diff --git a/source/blender/io/collada/AnimationClipExporter.h b/source/blender/io/collada/AnimationClipExporter.h
index 9d782eac094..a19fcb4d372 100644
--- a/source/blender/io/collada/AnimationClipExporter.h
+++ b/source/blender/io/collada/AnimationClipExporter.h
@@ -14,8 +14,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#ifndef __ANIMATIONCLIPEXPORTER_H__
-#define __ANIMATIONCLIPEXPORTER_H__
+#pragma once
#include <math.h>
#include <stdio.h>
@@ -49,5 +48,3 @@ class AnimationClipExporter : COLLADASW::LibraryAnimationClips {
void exportAnimationClips(Scene *sce);
};
-
-#endif /* __ANIMATIONCLIPEXPORTER_H__ */
diff --git a/source/blender/io/collada/AnimationExporter.cpp b/source/blender/io/collada/AnimationExporter.cpp
index c25b4ea543b..1515e5413f0 100644
--- a/source/blender/io/collada/AnimationExporter.cpp
+++ b/source/blender/io/collada/AnimationExporter.cpp
@@ -230,7 +230,7 @@ void AnimationExporter::export_matrix_animation(Object *ob, BCAnimationSampler &
std::string name = encode_xml(id_name(ob));
std::string action_name = (action == NULL) ? name + "-action" : id_name(action);
std::string channel_type = "transform";
- std::string axis = "";
+ std::string axis;
std::string id = bc_get_action_id(action_name, name, channel_type, axis);
std::string target = translate_id(name) + '/' + channel_type;
diff --git a/source/blender/io/collada/AnimationExporter.h b/source/blender/io/collada/AnimationExporter.h
index b4564eb7b2d..48279b0c867 100644
--- a/source/blender/io/collada/AnimationExporter.h
+++ b/source/blender/io/collada/AnimationExporter.h
@@ -14,8 +14,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#ifndef __ANIMATIONEXPORTER_H__
-#define __ANIMATIONEXPORTER_H__
+#pragma once
#include <math.h>
#include <stdio.h>
@@ -258,5 +257,3 @@ class AnimationExporter : COLLADASW::LibraryAnimations {
void export_morph_animation(Object *ob, BCAnimationSampler &sampler);
#endif
};
-
-#endif /* __ANIMATIONEXPORTER_H__ */
diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp
index 0e211f08d1f..b53aa95a11f 100644
--- a/source/blender/io/collada/AnimationImporter.cpp
+++ b/source/blender/io/collada/AnimationImporter.cpp
@@ -306,7 +306,7 @@ bool AnimationImporter::write_animation(const COLLADAFW::Animation *anim)
animation_to_fcurves(curve);
break;
default:
- /* TODO there're also CARDINAL, HERMITE, BSPLINE and STEP types */
+ /* TODO there are also CARDINAL, HERMITE, BSPLINE and STEP types. */
fprintf(stderr,
"CARDINAL, HERMITE and BSPLINE anim interpolation types not supported yet.\n");
break;
@@ -967,8 +967,6 @@ void AnimationImporter::apply_matrix_curves(Object *ob,
else {
ob->rotmode = ROT_MODE_QUAT;
}
-
- return;
}
/*
diff --git a/source/blender/io/collada/AnimationImporter.h b/source/blender/io/collada/AnimationImporter.h
index 51041c6ee3e..5c6f9400286 100644
--- a/source/blender/io/collada/AnimationImporter.h
+++ b/source/blender/io/collada/AnimationImporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __ANIMATIONIMPORTER_H__
-#define __ANIMATIONIMPORTER_H__
+#pragma once
#include <map>
#include <vector>
@@ -250,5 +249,3 @@ class AnimationImporter : private TransformReader, public AnimationImporterBase
void extra_data_importer(std::string elementName);
};
-
-#endif
diff --git a/source/blender/io/collada/ArmatureExporter.h b/source/blender/io/collada/ArmatureExporter.h
index 3bc9dfe2639..193dc9cde3a 100644
--- a/source/blender/io/collada/ArmatureExporter.h
+++ b/source/blender/io/collada/ArmatureExporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __ARMATUREEXPORTER_H__
-#define __ARMATUREEXPORTER_H__
+#pragma once
#include <list>
#include <string>
@@ -103,5 +102,3 @@ class ArmatureExporter : public COLLADASW::LibraryControllers,
void write_bone_URLs(COLLADASW::InstanceController &ins, Object *ob_arm, Bone *bone);
};
-
-#endif
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/ArmatureImporter.h b/source/blender/io/collada/ArmatureImporter.h
index 7393b882f4b..a1c4a25b80f 100644
--- a/source/blender/io/collada/ArmatureImporter.h
+++ b/source/blender/io/collada/ArmatureImporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __ARMATUREIMPORTER_H__
-#define __ARMATUREIMPORTER_H__
+#pragma once
#include "COLLADAFWMorphController.h"
#include "COLLADAFWNode.h"
@@ -181,5 +180,3 @@ class ArmatureImporter : private TransformReader {
void set_tags_map(TagsMap &tags_map);
};
-
-#endif
diff --git a/source/blender/io/collada/BCAnimationCurve.h b/source/blender/io/collada/BCAnimationCurve.h
index e0216ee6849..779678bca1d 100644
--- a/source/blender/io/collada/BCAnimationCurve.h
+++ b/source/blender/io/collada/BCAnimationCurve.h
@@ -17,8 +17,7 @@
* All rights reserved.
*/
-#ifndef __BCANIMATIONCURVE_H__
-#define __BCANIMATIONCURVE_H__
+#pragma once
#include "BCSampleData.h"
#include "collada_utils.h"
@@ -147,5 +146,3 @@ class BCAnimationCurve {
};
typedef std::map<BCCurveKey, BCAnimationCurve *> BCAnimationCurveMap;
-
-#endif /* __BCANIMATIONCURVE_H__ */
diff --git a/source/blender/io/collada/BCAnimationSampler.h b/source/blender/io/collada/BCAnimationSampler.h
index 3273ac8e0a0..5ab4be3722c 100644
--- a/source/blender/io/collada/BCAnimationSampler.h
+++ b/source/blender/io/collada/BCAnimationSampler.h
@@ -14,8 +14,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#ifndef __BCANIMATIONSAMPLER_H__
-#define __BCANIMATIONSAMPLER_H__
+#pragma once
#include "BCAnimationCurve.h"
#include "BCSampleData.h"
@@ -190,5 +189,3 @@ class BCAnimationSampler {
ListBase *conlist,
std::set<Object *> &animated_objects);
};
-
-#endif /* __BCANIMATIONSAMPLER_H__ */
diff --git a/source/blender/io/collada/BCMath.cpp b/source/blender/io/collada/BCMath.cpp
index e8765fa2bcd..a03e2ad6488 100644
--- a/source/blender/io/collada/BCMath.cpp
+++ b/source/blender/io/collada/BCMath.cpp
@@ -72,27 +72,30 @@ BCMatrix::BCMatrix(BC_global_forward_axis global_forward_axis, BC_global_up_axis
set_transform(mat);
}
-void BCMatrix::add_transform(const Matrix &mat, bool inverse)
+void BCMatrix::add_transform(const Matrix &mat, bool inverted)
{
- add_transform(this->matrix, mat, this->matrix, inverse);
+ add_transform(this->matrix, mat, this->matrix, inverted);
}
-void BCMatrix::add_transform(const BCMatrix &mat, bool inverse)
+void BCMatrix::add_transform(const BCMatrix &mat, bool inverted)
{
- add_transform(this->matrix, mat.matrix, this->matrix, inverse);
+ add_transform(this->matrix, mat.matrix, this->matrix, inverted);
}
-void BCMatrix::apply_transform(const BCMatrix &mat, bool inverse)
+void BCMatrix::apply_transform(const BCMatrix &mat, bool inverted)
{
- apply_transform(this->matrix, mat.matrix, this->matrix, inverse);
+ apply_transform(this->matrix, mat.matrix, this->matrix, inverted);
}
-void BCMatrix::add_transform(Matrix &to, const Matrix &transform, const Matrix &from, bool inverse)
+void BCMatrix::add_transform(Matrix &to,
+ const Matrix &transform,
+ const Matrix &from,
+ bool inverted)
{
- if (inverse) {
+ if (inverted) {
Matrix globinv;
invert_m4_m4(globinv, transform);
- add_transform(to, globinv, from, /*inverse=*/false);
+ add_transform(to, globinv, from, /*inverted=*/false);
}
else {
mul_m4_m4m4(to, transform, from);
@@ -107,7 +110,7 @@ void BCMatrix::apply_transform(Matrix &to,
Matrix globinv;
invert_m4_m4(globinv, transform);
if (inverse) {
- add_transform(to, globinv, from, /*inverse=*/false);
+ add_transform(to, globinv, from, /*inverted=*/false);
}
else {
mul_m4_m4m4(to, transform, from);
diff --git a/source/blender/io/collada/BCMath.h b/source/blender/io/collada/BCMath.h
index 38158751740..79a0ea941cd 100644
--- a/source/blender/io/collada/BCMath.h
+++ b/source/blender/io/collada/BCMath.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __BCMATH_H__
-#define __BCMATH_H__
+#pragma once
#include "BlenderTypes.h"
@@ -105,5 +104,3 @@ class BCMatrix {
static void sanitize(DMatrix &matrix, int precision);
static void transpose(Matrix &matrix);
};
-
-#endif /* __BCMATH_H__ */
diff --git a/source/blender/io/collada/BCSampleData.h b/source/blender/io/collada/BCSampleData.h
index 6f3ca9135b3..39e34355b72 100644
--- a/source/blender/io/collada/BCSampleData.h
+++ b/source/blender/io/collada/BCSampleData.h
@@ -17,8 +17,7 @@
* All rights reserved.
*/
-#ifndef __BCSAMPLEDATA_H__
-#define __BCSAMPLEDATA_H__
+#pragma once
#include <algorithm>
#include <map>
@@ -62,5 +61,3 @@ class BCSample {
typedef std::map<Object *, BCSample *> BCSampleMap;
typedef std::map<int, const BCSample *> BCFrameSampleMap;
typedef std::map<int, const BCMatrix *> BCMatrixSampleMap;
-
-#endif /* __BCSAMPLEDATA_H__ */
diff --git a/source/blender/io/collada/BlenderContext.h b/source/blender/io/collada/BlenderContext.h
index bf6fde134fa..9163b30c86f 100644
--- a/source/blender/io/collada/BlenderContext.h
+++ b/source/blender/io/collada/BlenderContext.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __BLENDERCONTEXT_H__
-#define __BLENDERCONTEXT_H__
+#pragma once
#include "BKE_context.h"
#include "BKE_main.h"
@@ -68,5 +67,3 @@ class BlenderContext {
Main *get_main();
};
#endif
-
-#endif
diff --git a/source/blender/io/collada/BlenderTypes.h b/source/blender/io/collada/BlenderTypes.h
index 0e024be2374..63b0471ef6f 100644
--- a/source/blender/io/collada/BlenderTypes.h
+++ b/source/blender/io/collada/BlenderTypes.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __BLENDERTYPES_H__
-#define __BLENDERTYPES_H__
+#pragma once
typedef float(Vector)[3];
typedef float(Quat)[4];
@@ -44,5 +43,3 @@ typedef enum BC_global_up_axis {
BC_GLOBAL_UP_MINUS_Y = 4,
BC_GLOBAL_UP_MINUS_Z = 5
} BC_global_up_axis;
-
-#endif
diff --git a/source/blender/io/collada/CameraExporter.h b/source/blender/io/collada/CameraExporter.h
index 0dda6392d03..cc4f7aee165 100644
--- a/source/blender/io/collada/CameraExporter.h
+++ b/source/blender/io/collada/CameraExporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __CAMERAEXPORTER_H__
-#define __CAMERAEXPORTER_H__
+#pragma once
#include "COLLADASWLibraryCameras.h"
#include "COLLADASWStreamWriter.h"
@@ -40,5 +39,3 @@ class CamerasExporter : COLLADASW::LibraryCameras {
bool exportBlenderProfile(COLLADASW::Camera &cla, Camera *cam);
BCExportSettings &export_settings;
};
-
-#endif
diff --git a/source/blender/io/collada/ControllerExporter.h b/source/blender/io/collada/ControllerExporter.h
index fb84af6ebc9..753538d8e98 100644
--- a/source/blender/io/collada/ControllerExporter.h
+++ b/source/blender/io/collada/ControllerExporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __CONTROLLEREXPORTER_H__
-#define __CONTROLLEREXPORTER_H__
+#pragma once
#include <list>
#include <string>
@@ -133,5 +132,3 @@ class ControllerExporter : public COLLADASW::LibraryControllers,
void write_bone_URLs(COLLADASW::InstanceController &ins, Object *ob_arm, Bone *bone);
};
-
-#endif
diff --git a/source/blender/io/collada/DocumentExporter.h b/source/blender/io/collada/DocumentExporter.h
index 1fe52420534..850eb4fbbb2 100644
--- a/source/blender/io/collada/DocumentExporter.h
+++ b/source/blender/io/collada/DocumentExporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __DOCUMENTEXPORTER_H__
-#define __DOCUMENTEXPORTER_H__
+#pragma once
#include "BlenderContext.h"
#include "collada.h"
@@ -38,5 +37,3 @@ class DocumentExporter {
BCExportSettings export_settings;
KeyImageMap key_image_map;
};
-
-#endif
diff --git a/source/blender/io/collada/DocumentImporter.h b/source/blender/io/collada/DocumentImporter.h
index d9be4880b35..e382a44c3c2 100644
--- a/source/blender/io/collada/DocumentImporter.h
+++ b/source/blender/io/collada/DocumentImporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __DOCUMENTIMPORTER_H__
-#define __DOCUMENTIMPORTER_H__
+#pragma once
#include "COLLADAFWColor.h"
#include "COLLADAFWController.h"
@@ -168,5 +167,3 @@ class DocumentImporter : COLLADAFW::IWriter {
void report_unknown_reference(const COLLADAFW::Node &node, const std::string object_type);
};
-
-#endif
diff --git a/source/blender/io/collada/EffectExporter.h b/source/blender/io/collada/EffectExporter.h
index 36d02bb1c8f..c844d93b040 100644
--- a/source/blender/io/collada/EffectExporter.h
+++ b/source/blender/io/collada/EffectExporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __EFFECTEXPORTER_H__
-#define __EFFECTEXPORTER_H__
+#pragma once
#include <string>
#include <vector>
@@ -85,5 +84,3 @@ class EffectsExporter : COLLADASW::LibraryEffects {
Scene *scene;
bContext *mContext;
};
-
-#endif
diff --git a/source/blender/io/collada/ErrorHandler.h b/source/blender/io/collada/ErrorHandler.h
index 0c082a3b9dd..60253a08cda 100644
--- a/source/blender/io/collada/ErrorHandler.h
+++ b/source/blender/io/collada/ErrorHandler.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __ERRORHANDLER_H__
-#define __ERRORHANDLER_H__
+#pragma once
#include <algorithm> // sort()
#include <map>
@@ -53,5 +52,3 @@ class ErrorHandler : public COLLADASaxFWL::IErrorHandler {
/** Hold error status. */
bool mError;
};
-
-#endif /* __ERRORHANDLER_H__ */
diff --git a/source/blender/io/collada/ExportSettings.h b/source/blender/io/collada/ExportSettings.h
index 477f0b8b678..2f647cefa8b 100644
--- a/source/blender/io/collada/ExportSettings.h
+++ b/source/blender/io/collada/ExportSettings.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __EXPORTSETTINGS_H__
-#define __EXPORTSETTINGS_H__
+#pragma once
#include "BLI_linklist.h"
#include "BlenderContext.h"
@@ -291,5 +290,3 @@ class BCExportSettings {
};
#endif
-
-#endif
diff --git a/source/blender/io/collada/ExtraHandler.h b/source/blender/io/collada/ExtraHandler.h
index 04ac963b530..8f98e1dec1b 100644
--- a/source/blender/io/collada/ExtraHandler.h
+++ b/source/blender/io/collada/ExtraHandler.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __EXTRAHANDLER_H__
-#define __EXTRAHANDLER_H__
+#pragma once
#include <algorithm> // sort()
#include <map>
@@ -79,5 +78,3 @@ class ExtraHandler : public COLLADASaxFWL::IExtraDataCallbackHandler {
ExtraTags *currentExtraTags;
std::string currentElement;
};
-
-#endif /* __EXTRAHANDLER_H__ */
diff --git a/source/blender/io/collada/ExtraTags.h b/source/blender/io/collada/ExtraTags.h
index c806b8fa904..a72cdeb32a8 100644
--- a/source/blender/io/collada/ExtraTags.h
+++ b/source/blender/io/collada/ExtraTags.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __EXTRATAGS_H__
-#define __EXTRATAGS_H__
+#pragma once
#include <map>
#include <string>
@@ -73,5 +72,3 @@ class ExtraTags {
/** Get text data for tag as a string. */
std::string asString(std::string tag, bool *ok);
};
-
-#endif /* __EXTRATAGS_H__ */
diff --git a/source/blender/io/collada/GeometryExporter.h b/source/blender/io/collada/GeometryExporter.h
index 5090158177f..d91f3d1ec5a 100644
--- a/source/blender/io/collada/GeometryExporter.h
+++ b/source/blender/io/collada/GeometryExporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __GEOMETRYEXPORTER_H__
-#define __GEOMETRYEXPORTER_H__
+#pragma once
#include <set>
#include <string>
@@ -136,5 +135,3 @@ struct GeometryFunctor {
}
}
};
-
-#endif
diff --git a/source/blender/io/collada/ImageExporter.h b/source/blender/io/collada/ImageExporter.h
index 20f9b95a512..e1fdcc12272 100644
--- a/source/blender/io/collada/ImageExporter.h
+++ b/source/blender/io/collada/ImageExporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __IMAGEEXPORTER_H__
-#define __IMAGEEXPORTER_H__
+#pragma once
#include <string>
#include <vector>
@@ -47,5 +46,3 @@ class ImagesExporter : COLLADASW::LibraryImages {
KeyImageMap &key_image_map;
void export_UV_Image(Image *image, bool use_texture_copies);
};
-
-#endif
diff --git a/source/blender/io/collada/ImportSettings.h b/source/blender/io/collada/ImportSettings.h
index 608d8bff882..e8a5bf8ce81 100644
--- a/source/blender/io/collada/ImportSettings.h
+++ b/source/blender/io/collada/ImportSettings.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __IMPORTSETTINGS_H__
-#define __IMPORTSETTINGS_H__
+#pragma once
typedef struct ImportSettings {
bool import_units;
@@ -30,5 +29,3 @@ typedef struct ImportSettings {
char *filepath;
bool keep_bind_info;
} ImportSettings;
-
-#endif
diff --git a/source/blender/io/collada/InstanceWriter.h b/source/blender/io/collada/InstanceWriter.h
index cfec1cf7006..c79d6691842 100644
--- a/source/blender/io/collada/InstanceWriter.h
+++ b/source/blender/io/collada/InstanceWriter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __INSTANCEWRITER_H__
-#define __INSTANCEWRITER_H__
+#pragma once
#include "COLLADASWBindMaterial.h"
@@ -31,5 +30,3 @@ class InstanceWriter {
Object *ob,
bool active_uv_only);
};
-
-#endif
diff --git a/source/blender/io/collada/LightExporter.h b/source/blender/io/collada/LightExporter.h
index a5c7f5c6dee..90256691c06 100644
--- a/source/blender/io/collada/LightExporter.h
+++ b/source/blender/io/collada/LightExporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __LIGHTEXPORTER_H__
-#define __LIGHTEXPORTER_H__
+#pragma once
#include "COLLADASWLibraryLights.h"
#include "COLLADASWStreamWriter.h"
@@ -40,5 +39,3 @@ class LightsExporter : COLLADASW::LibraryLights {
bool exportBlenderProfile(COLLADASW::Light &cla, Light *la);
BCExportSettings &export_settings;
};
-
-#endif
diff --git a/source/blender/io/collada/MaterialExporter.h b/source/blender/io/collada/MaterialExporter.h
index babb113567f..7d40347097c 100644
--- a/source/blender/io/collada/MaterialExporter.h
+++ b/source/blender/io/collada/MaterialExporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __MATERIALEXPORTER_H__
-#define __MATERIALEXPORTER_H__
+#pragma once
#include <string>
#include <vector>
@@ -93,5 +92,3 @@ struct MaterialFunctor {
gf.forEachMeshObjectInExportSet<ForEachMaterialFunctor<Functor>>(sce, matfunc, export_set);
}
};
-
-#endif
diff --git a/source/blender/io/collada/Materials.h b/source/blender/io/collada/Materials.h
index e1d12246a2b..f671a00758d 100644
--- a/source/blender/io/collada/Materials.h
+++ b/source/blender/io/collada/Materials.h
@@ -14,8 +14,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#ifndef __MATERIALS_H__
-#define __MATERIALS_H__
+#pragma once
#include <map>
#include <string>
@@ -70,5 +69,3 @@ class MaterialNode {
COLLADAFW::ColorOrTexture &cot,
COLLADAFW::FloatOrParam &val);
};
-
-#endif /* __MATERIALS_H__ */
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..a63cb0da987 100644
--- a/source/blender/io/collada/MeshImporter.h
+++ b/source/blender/io/collada/MeshImporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __MESHIMPORTER_H__
-#define __MESHIMPORTER_H__
+#pragma once
#include <map>
#include <vector>
@@ -105,7 +104,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,
@@ -177,5 +176,3 @@ class MeshImporter : public MeshImporterBase {
bool write_geometry(const COLLADAFW::Geometry *geom);
std::string *get_geometry_name(const std::string &mesh_name);
};
-
-#endif
diff --git a/source/blender/io/collada/SceneExporter.h b/source/blender/io/collada/SceneExporter.h
index 3ea6a9fac8e..5b8ec37152f 100644
--- a/source/blender/io/collada/SceneExporter.h
+++ b/source/blender/io/collada/SceneExporter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __SCENEEXPORTER_H__
-#define __SCENEEXPORTER_H__
+#pragma once
#include <math.h>
#include <stdio.h>
@@ -110,5 +109,3 @@ class SceneExporter : COLLADASW::LibraryVisualScenes,
void writeNodeList(std::vector<Object *> &child_objects, Object *parent);
void writeNode(Object *ob);
};
-
-#endif
diff --git a/source/blender/io/collada/SkinInfo.h b/source/blender/io/collada/SkinInfo.h
index e8b13f86e9c..3b94e403b6d 100644
--- a/source/blender/io/collada/SkinInfo.h
+++ b/source/blender/io/collada/SkinInfo.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __SKININFO_H__
-#define __SKININFO_H__
+#pragma once
#include <map>
#include <vector>
@@ -126,5 +125,3 @@ class SkinInfo {
bool find_node_in_tree(COLLADAFW::Node *node, COLLADAFW::Node *tree_root);
};
-
-#endif
diff --git a/source/blender/io/collada/TransformReader.h b/source/blender/io/collada/TransformReader.h
index 5a778e6aba5..f8a73ce3912 100644
--- a/source/blender/io/collada/TransformReader.h
+++ b/source/blender/io/collada/TransformReader.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __TRANSFORMREADER_H__
-#define __TRANSFORMREADER_H__
+#pragma once
#include "COLLADAFWMatrix.h"
#include "COLLADAFWNode.h"
@@ -68,5 +67,3 @@ class TransformReader {
void dae_scale_to_v3(COLLADAFW::Transformation *tm, float v[3]);
void dae_vector3_to_v3(const COLLADABU::Math::Vector3 &v3, float v[3]);
};
-
-#endif
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..200f641b064 100644
--- a/source/blender/io/collada/TransformWriter.h
+++ b/source/blender/io/collada/TransformWriter.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __TRANSFORMWRITER_H__
-#define __TRANSFORMWRITER_H__
+#pragma once
#include "COLLADASWNode.h"
@@ -42,7 +41,8 @@ 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.h b/source/blender/io/collada/collada.h
index d8e498ef4b2..2668be6153b 100644
--- a/source/blender/io/collada/collada.h
+++ b/source/blender/io/collada/collada.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __COLLADA_H__
-#define __COLLADA_H__
+#pragma once
#include <stdlib.h>
@@ -46,5 +45,3 @@ int collada_export(struct bContext *C, ExportSettings *export_settings);
#ifdef __cplusplus
}
#endif
-
-#endif
diff --git a/source/blender/io/collada/collada_internal.cpp b/source/blender/io/collada/collada_internal.cpp
index f123e8ea31f..b3fa9ba1251 100644
--- a/source/blender/io/collada/collada_internal.cpp
+++ b/source/blender/io/collada/collada_internal.cpp
@@ -277,7 +277,7 @@ std::string encode_xml(std::string xml)
{'<', "&lt;"}, {'>', "&gt;"}, {'"', "&quot;"}, {'\'', "&apos;"}, {'&', "&amp;"}};
std::map<char, std::string>::const_iterator it;
- std::string encoded_xml = "";
+ std::string encoded_xml;
for (unsigned int i = 0; i < xml.size(); i++) {
char c = xml.at(i);
diff --git a/source/blender/io/collada/collada_internal.h b/source/blender/io/collada/collada_internal.h
index d9fe0ba0b58..de4e1d7e0d4 100644
--- a/source/blender/io/collada/collada_internal.h
+++ b/source/blender/io/collada/collada_internal.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __COLLADA_INTERNAL_H__
-#define __COLLADA_INTERNAL_H__
+#pragma once
#include <map>
#include <string>
@@ -94,5 +93,3 @@ extern std::string get_morph_id(Object *ob);
extern std::string get_effect_id(Material *mat);
extern std::string get_material_id(Material *mat);
-
-#endif /* __COLLADA_INTERNAL_H__ */
diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp
index d2e05c7ae5b..df35a1d2780 100644
--- a/source/blender/io/collada/collada_utils.cpp
+++ b/source/blender/io/collada/collada_utils.cpp
@@ -528,9 +528,7 @@ BoneExtensionManager::~BoneExtensionManager()
for (BoneExtensionMap::iterator ext_it = extended_bones->begin();
ext_it != extended_bones->end();
++ext_it) {
- if (ext_it->second != NULL) {
- delete ext_it->second;
- }
+ delete ext_it->second;
}
extended_bones->clear();
delete extended_bones;
@@ -605,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];
@@ -674,8 +672,7 @@ void BoneExtended::set_bone_layers(std::string layerString, std::vector<std::str
std::string BoneExtended::get_bone_layers(int bitfield)
{
- std::string result = "";
- std::string sep = "";
+ std::string sep;
int bit = 1u;
std::ostringstream ss;
diff --git a/source/blender/io/collada/collada_utils.h b/source/blender/io/collada/collada_utils.h
index b1ec2c8b81a..657a82f70d2 100644
--- a/source/blender/io/collada/collada_utils.h
+++ b/source/blender/io/collada/collada_utils.h
@@ -18,8 +18,7 @@
* \ingroup collada
*/
-#ifndef __COLLADA_UTILS_H__
-#define __COLLADA_UTILS_H__
+#pragma once
#include "COLLADAFWColorOrTexture.h"
#include "COLLADAFWFloatOrDoubleArray.h"
@@ -343,7 +342,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();
@@ -393,5 +392,3 @@ COLLADASW::ColorOrTexture bc_get_cot_from_shader(bNode *shader,
COLLADASW::ColorOrTexture bc_get_cot(float r, float g, float b, float a);
COLLADASW::ColorOrTexture bc_get_cot(Color col, bool with_alpha = true);
-
-#endif
diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt
index 4ed6f12762e..d3341767f8b 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
@@ -43,3 +48,17 @@ set(LIB
blender_add_lib(bf_io_common "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
target_link_libraries(bf_io_common INTERFACE)
+
+if(WITH_GTESTS)
+ set(TEST_SRC
+ intern/abstract_hierarchy_iterator_test.cc
+ intern/hierarchy_context_order_test.cc
+ intern/object_identifier_test.cc
+ )
+ set(TEST_LIB
+ bf_blenloader_test
+ bf_io_common
+ )
+ include(GTestTesting)
+ blender_add_test_lib(bf_io_common_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
+endif()
diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
index 5f84fd48b71..d0d9d72b880 100644
--- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h
+++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
@@ -33,8 +33,9 @@
* Selections like "selected only" or "no hair systems" are left to concrete subclasses.
*/
-#ifndef __ABSTRACT_HIERARCHY_ITERATOR_H__
-#define __ABSTRACT_HIERARCHY_ITERATOR_H__
+#pragma once
+
+#include "IO_dupli_persistent_id.hh"
#include <map>
#include <set>
@@ -52,6 +53,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 +62,7 @@ struct HierarchyContext {
Object *object; /* Evaluated object. */
Object *export_parent;
Object *duplicator;
+ PersistentID persistent_id;
float matrix_world[4][4];
std::string export_name;
@@ -125,7 +128,14 @@ class AbstractHierarchyWriter {
// but wasn't used while exporting the current frame (for example, a particle-instanced mesh of
// which the particle is no longer alive).
protected:
+ /* Return true if the data written by this writer changes over time.
+ * Note that this function assumes this is an object data writer. Transform writers should not
+ * call this but implement their own logic. */
virtual bool check_is_animated(const HierarchyContext &context) const;
+
+ /* Helper functions for animation checks. */
+ static bool check_has_physics(const HierarchyContext &context);
+ static bool check_has_deforming_physics(const HierarchyContext &context);
};
/* Determines which subset of the writers actually gets to write. */
@@ -161,6 +171,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 +211,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 +272,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 +326,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
@@ -309,7 +346,7 @@ class AbstractHierarchyIterator {
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;
+ virtual void release_writer(AbstractHierarchyWriter *writer) = 0;
AbstractHierarchyWriter *get_writer(const std::string &export_path) const;
ExportChildren &graph_children(const HierarchyContext *parent_context);
@@ -317,5 +354,3 @@ class AbstractHierarchyIterator {
} // namespace io
} // namespace blender
-
-#endif /* __ABSTRACT_HIERARCHY_ITERATOR_H__ */
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..6fabafd9d51
--- /dev/null
+++ b/source/blender/io/common/IO_dupli_persistent_id.hh
@@ -0,0 +1,65 @@
+/*
+ * 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 "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
diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
index dce6b8e178b..fbefc8c8e7e 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>
@@ -38,6 +39,7 @@
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_particle_types.h"
+#include "DNA_rigidbody_types.h"
#include "DEG_depsgraph_query.h"
@@ -126,6 +128,9 @@ bool AbstractHierarchyWriter::check_is_animated(const HierarchyContext &context)
if (BKE_key_from_object(object) != nullptr) {
return true;
}
+ if (check_has_deforming_physics(context)) {
+ return true;
+ }
/* Test modifiers. */
/* TODO(Sybren): replace this with a check on the depsgraph to properly check for dependency on
@@ -141,6 +146,18 @@ bool AbstractHierarchyWriter::check_is_animated(const HierarchyContext &context)
return false;
}
+bool AbstractHierarchyWriter::check_has_physics(const HierarchyContext &context)
+{
+ const RigidBodyOb *rbo = context.object->rigidbody_object;
+ return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE;
+}
+
+bool AbstractHierarchyWriter::check_has_deforming_physics(const HierarchyContext &context)
+{
+ const RigidBodyOb *rbo = context.object->rigidbody_object;
+ return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0;
+}
+
AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph)
: depsgraph_(depsgraph), writers_(), export_subset_({true, true})
{
@@ -148,6 +165,12 @@ AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph)
AbstractHierarchyIterator::~AbstractHierarchyIterator()
{
+ /* release_writers() cannot be called here directly, as it calls into the pure-virtual
+ * release_writer() function. By the time this destructor is called, the subclass that implements
+ * that pure-virtual function is already destructed. */
+ BLI_assert(
+ writers_.empty() ||
+ !"release_writers() should be called before the AbstractHierarchyIterator goes out of scope");
}
void AbstractHierarchyIterator::iterate_and_write()
@@ -164,7 +187,7 @@ void AbstractHierarchyIterator::iterate_and_write()
void AbstractHierarchyIterator::release_writers()
{
for (WriterMap::value_type it : writers_) {
- delete_object_writer(it.second);
+ release_writer(it.second);
}
writers_.clear();
}
@@ -200,9 +223,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 +240,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 +248,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 +257,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 +280,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 +313,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 +349,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 +420,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 +429,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 +449,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 +490,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/abstract_hierarchy_iterator_test.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc
new file mode 100644
index 00000000000..2bc7560e177
--- /dev/null
+++ b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc
@@ -0,0 +1,319 @@
+/*
+ * 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 "blenloader/blendfile_loading_base_test.h"
+
+#include "BLI_math.h"
+#include "DEG_depsgraph.h"
+#include "DNA_object_types.h"
+
+#include <map>
+#include <set>
+
+namespace blender::io {
+
+namespace {
+
+/* Mapping from ID.name to set of export hierarchy path. Duplicated objects can be exported
+ * multiple times with different export paths, hence the set. */
+typedef std::map<std::string, std::set<std::string>> used_writers;
+
+class TestHierarchyWriter : public AbstractHierarchyWriter {
+ public:
+ std::string writer_type;
+ used_writers &writers_map;
+
+ TestHierarchyWriter(const std::string &writer_type, used_writers &writers_map)
+ : writer_type(writer_type), writers_map(writers_map)
+ {
+ }
+
+ void write(HierarchyContext &context) override
+ {
+ const char *id_name = context.object->id.name;
+ used_writers::mapped_type &writers = writers_map[id_name];
+
+ if (writers.find(context.export_path) != writers.end()) {
+ ADD_FAILURE() << "Unexpectedly found another " << writer_type << " writer for " << id_name
+ << " to export to " << context.export_path;
+ }
+ writers.insert(context.export_path);
+ }
+};
+
+} // namespace
+
+class TestingHierarchyIterator : public AbstractHierarchyIterator {
+ public: /* Public so that the test cases can directly inspect the created writers. */
+ used_writers transform_writers;
+ used_writers data_writers;
+ used_writers hair_writers;
+ used_writers particle_writers;
+
+ explicit TestingHierarchyIterator(Depsgraph *depsgraph) : AbstractHierarchyIterator(depsgraph)
+ {
+ }
+ virtual ~TestingHierarchyIterator()
+ {
+ release_writers();
+ }
+
+ protected:
+ AbstractHierarchyWriter *create_transform_writer(const HierarchyContext * /*context*/) override
+ {
+ return new TestHierarchyWriter("transform", transform_writers);
+ }
+ AbstractHierarchyWriter *create_data_writer(const HierarchyContext * /*context*/) override
+ {
+ return new TestHierarchyWriter("data", data_writers);
+ }
+ AbstractHierarchyWriter *create_hair_writer(const HierarchyContext * /*context*/) override
+ {
+ return new TestHierarchyWriter("hair", hair_writers);
+ }
+ AbstractHierarchyWriter *create_particle_writer(const HierarchyContext * /*context*/) override
+ {
+ return new TestHierarchyWriter("particle", particle_writers);
+ }
+
+ void release_writer(AbstractHierarchyWriter *writer) override
+ {
+ delete writer;
+ }
+};
+
+class USDHierarchyIteratorTest : public BlendfileLoadingBaseTest {
+ protected:
+ TestingHierarchyIterator *iterator;
+
+ virtual void SetUp()
+ {
+ BlendfileLoadingBaseTest::SetUp();
+ iterator = nullptr;
+ }
+
+ virtual void TearDown()
+ {
+ iterator_free();
+ BlendfileLoadingBaseTest::TearDown();
+ }
+
+ /* Create a test iterator. */
+ void iterator_create()
+ {
+ iterator = new TestingHierarchyIterator(depsgraph);
+ }
+ /* Free the test iterator if it is not nullptr. */
+ void iterator_free()
+ {
+ if (iterator == nullptr) {
+ return;
+ }
+ delete iterator;
+ iterator = nullptr;
+ }
+};
+
+TEST_F(USDHierarchyIteratorTest, ExportHierarchyTest)
+{
+ /* Load the test blend file. */
+ if (!blendfile_load("usd/usd_hierarchy_export_test.blend")) {
+ return;
+ }
+ depsgraph_create(DAG_EVAL_RENDER);
+ iterator_create();
+
+ iterator->iterate_and_write();
+
+ // Mapping from object name to set of export paths.
+ used_writers expected_transforms = {
+ {"OBCamera", {"/Camera"}},
+ {"OBDupli1", {"/Dupli1"}},
+ {"OBDupli2", {"/ParentOfDupli2/Dupli2"}},
+ {"OBGEO_Ear_L",
+ {"/Dupli1/GEO_Head-0/GEO_Ear_L-1",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_L",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_L-1"}},
+ {"OBGEO_Ear_R",
+ {"/Dupli1/GEO_Head-0/GEO_Ear_R-2",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_R",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_R-2"}},
+ {"OBGEO_Head",
+ {"/Dupli1/GEO_Head-0",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0"}},
+ {"OBGEO_Nose",
+ {"/Dupli1/GEO_Head-0/GEO_Nose-3",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Nose",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Nose-3"}},
+ {"OBGround plane", {"/Ground plane"}},
+ {"OBOutsideDupliGrandParent", {"/Ground plane/OutsideDupliGrandParent"}},
+ {"OBOutsideDupliParent", {"/Ground plane/OutsideDupliGrandParent/OutsideDupliParent"}},
+ {"OBParentOfDupli2", {"/ParentOfDupli2"}}};
+ EXPECT_EQ(expected_transforms, iterator->transform_writers);
+
+ used_writers expected_data = {
+ {"OBCamera", {"/Camera/Camera"}},
+ {"OBGEO_Ear_L",
+ {"/Dupli1/GEO_Head-0/GEO_Ear_L-1/Ear",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_L/Ear",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_L-1/Ear"}},
+ {"OBGEO_Ear_R",
+ {"/Dupli1/GEO_Head-0/GEO_Ear_R-2/Ear",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_R/Ear",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_R-2/Ear"}},
+ {"OBGEO_Head",
+ {"/Dupli1/GEO_Head-0/Face",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/Face",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/Face"}},
+ {"OBGEO_Nose",
+ {"/Dupli1/GEO_Head-0/GEO_Nose-3/Nose",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Nose/Nose",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Nose-3/Nose"}},
+ {"OBGround plane", {"/Ground plane/Plane"}},
+ {"OBParentOfDupli2", {"/ParentOfDupli2/Icosphere"}},
+ };
+
+ EXPECT_EQ(expected_data, iterator->data_writers);
+
+ // The scene has no hair or particle systems.
+ EXPECT_EQ(0, iterator->hair_writers.size());
+ EXPECT_EQ(0, iterator->particle_writers.size());
+
+ // On the second iteration, everything should be written as well.
+ // This tests the default value of iterator->export_subset_.
+ iterator->transform_writers.clear();
+ iterator->data_writers.clear();
+ iterator->iterate_and_write();
+ EXPECT_EQ(expected_transforms, iterator->transform_writers);
+ EXPECT_EQ(expected_data, iterator->data_writers);
+}
+
+TEST_F(USDHierarchyIteratorTest, ExportSubsetTest)
+{
+ // The scene has no hair or particle systems, and this is already covered by ExportHierarchyTest,
+ // so not included here. Update this test when hair & particle systems are included.
+
+ /* Load the test blend file. */
+ if (!blendfile_load("usd/usd_hierarchy_export_test.blend")) {
+ return;
+ }
+ depsgraph_create(DAG_EVAL_RENDER);
+ iterator_create();
+
+ // Mapping from object name to set of export paths.
+ used_writers expected_transforms = {
+ {"OBCamera", {"/Camera"}},
+ {"OBDupli1", {"/Dupli1"}},
+ {"OBDupli2", {"/ParentOfDupli2/Dupli2"}},
+ {"OBGEO_Ear_L",
+ {"/Dupli1/GEO_Head-0/GEO_Ear_L-1",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_L",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_L-1"}},
+ {"OBGEO_Ear_R",
+ {"/Dupli1/GEO_Head-0/GEO_Ear_R-2",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_R",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_R-2"}},
+ {"OBGEO_Head",
+ {"/Dupli1/GEO_Head-0",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0"}},
+ {"OBGEO_Nose",
+ {"/Dupli1/GEO_Head-0/GEO_Nose-3",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Nose",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Nose-3"}},
+ {"OBGround plane", {"/Ground plane"}},
+ {"OBOutsideDupliGrandParent", {"/Ground plane/OutsideDupliGrandParent"}},
+ {"OBOutsideDupliParent", {"/Ground plane/OutsideDupliGrandParent/OutsideDupliParent"}},
+ {"OBParentOfDupli2", {"/ParentOfDupli2"}}};
+
+ used_writers expected_data = {
+ {"OBCamera", {"/Camera/Camera"}},
+ {"OBGEO_Ear_L",
+ {"/Dupli1/GEO_Head-0/GEO_Ear_L-1/Ear",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_L/Ear",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_L-1/Ear"}},
+ {"OBGEO_Ear_R",
+ {"/Dupli1/GEO_Head-0/GEO_Ear_R-2/Ear",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_R/Ear",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_R-2/Ear"}},
+ {"OBGEO_Head",
+ {"/Dupli1/GEO_Head-0/Face",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/Face",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/Face"}},
+ {"OBGEO_Nose",
+ {"/Dupli1/GEO_Head-0/GEO_Nose-3/Nose",
+ "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Nose/Nose",
+ "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Nose-3/Nose"}},
+ {"OBGround plane", {"/Ground plane/Plane"}},
+ {"OBParentOfDupli2", {"/ParentOfDupli2/Icosphere"}},
+ };
+
+ // Even when only asking an export of transforms, on the first frame everything should be
+ // exported.
+ {
+ ExportSubset export_subset = {0};
+ export_subset.transforms = true;
+ export_subset.shapes = false;
+ iterator->set_export_subset(export_subset);
+ }
+ iterator->iterate_and_write();
+ EXPECT_EQ(expected_transforms, iterator->transform_writers);
+ EXPECT_EQ(expected_data, iterator->data_writers);
+
+ // Clear data to prepare for the next iteration.
+ iterator->transform_writers.clear();
+ iterator->data_writers.clear();
+
+ // Second iteration, should only write transforms now.
+ iterator->iterate_and_write();
+ EXPECT_EQ(expected_transforms, iterator->transform_writers);
+ EXPECT_EQ(0, iterator->data_writers.size());
+
+ // Clear data to prepare for the next iteration.
+ iterator->transform_writers.clear();
+ iterator->data_writers.clear();
+
+ // Third iteration, should only write data now.
+ {
+ ExportSubset export_subset = {0};
+ export_subset.transforms = false;
+ export_subset.shapes = true;
+ iterator->set_export_subset(export_subset);
+ }
+ iterator->iterate_and_write();
+ EXPECT_EQ(0, iterator->transform_writers.size());
+ EXPECT_EQ(expected_data, iterator->data_writers);
+
+ // Clear data to prepare for the next iteration.
+ iterator->transform_writers.clear();
+ iterator->data_writers.clear();
+
+ // Fourth iteration, should export everything now.
+ {
+ ExportSubset export_subset = {0};
+ export_subset.transforms = true;
+ export_subset.shapes = true;
+ iterator->set_export_subset(export_subset);
+ }
+ iterator->iterate_and_write();
+ EXPECT_EQ(expected_transforms, iterator->transform_writers);
+ EXPECT_EQ(expected_data, iterator->data_writers);
+}
+} // namespace blender::io
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..3dcf037bb5e
--- /dev/null
+++ b/source/blender/io/common/intern/dupli_parent_finder.hh
@@ -0,0 +1,59 @@
+/*
+ * 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_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
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/hierarchy_context_order_test.cc b/source/blender/io/common/intern/hierarchy_context_order_test.cc
new file mode 100644
index 00000000000..f4e2b81b39b
--- /dev/null
+++ b/source/blender/io/common/intern/hierarchy_context_order_test.cc
@@ -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.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+#include "IO_abstract_hierarchy_iterator.h"
+
+#include "testing/testing.h"
+
+#include "BLI_utildefines.h"
+
+namespace blender::io {
+
+namespace {
+
+Object *fake_pointer(int value)
+{
+ return static_cast<Object *>(POINTER_FROM_INT(value));
+}
+
+} // namespace
+
+class HierarchyContextOrderTest : public testing::Test {
+};
+
+TEST_F(HierarchyContextOrderTest, ObjectPointerTest)
+{
+ HierarchyContext ctx_a = {0};
+ ctx_a.object = fake_pointer(1);
+ ctx_a.duplicator = nullptr;
+
+ HierarchyContext ctx_b = {0};
+ ctx_b.object = fake_pointer(2);
+ ctx_b.duplicator = nullptr;
+
+ EXPECT_LT(ctx_a, ctx_b);
+ EXPECT_FALSE(ctx_b < ctx_a);
+ EXPECT_FALSE(ctx_a < ctx_a);
+}
+
+TEST_F(HierarchyContextOrderTest, DuplicatorPointerTest)
+{
+ HierarchyContext ctx_a = {0};
+ ctx_a.object = fake_pointer(1);
+ ctx_a.duplicator = fake_pointer(1);
+ ctx_a.export_name = "A";
+
+ HierarchyContext ctx_b = {0};
+ ctx_b.object = fake_pointer(1);
+ ctx_b.duplicator = fake_pointer(1);
+ ctx_b.export_name = "B";
+
+ EXPECT_LT(ctx_a, ctx_b);
+ EXPECT_FALSE(ctx_b < ctx_a);
+ EXPECT_FALSE(ctx_a < ctx_a);
+}
+
+TEST_F(HierarchyContextOrderTest, ExportParentTest)
+{
+ HierarchyContext ctx_a = {0};
+ ctx_a.object = fake_pointer(1);
+ ctx_a.export_parent = fake_pointer(1);
+
+ HierarchyContext ctx_b = {0};
+ ctx_b.object = fake_pointer(1);
+ ctx_b.export_parent = fake_pointer(2);
+
+ EXPECT_LT(ctx_a, ctx_b);
+ EXPECT_FALSE(ctx_b < ctx_a);
+ EXPECT_FALSE(ctx_a < ctx_a);
+}
+
+TEST_F(HierarchyContextOrderTest, TransitiveTest)
+{
+ HierarchyContext ctx_a = {0};
+ ctx_a.object = fake_pointer(1);
+ ctx_a.export_parent = fake_pointer(1);
+ ctx_a.duplicator = nullptr;
+ ctx_a.export_name = "A";
+
+ HierarchyContext ctx_b = {0};
+ ctx_b.object = fake_pointer(2);
+ ctx_b.export_parent = nullptr;
+ ctx_b.duplicator = fake_pointer(1);
+ ctx_b.export_name = "B";
+
+ HierarchyContext ctx_c = {0};
+ ctx_c.object = fake_pointer(2);
+ ctx_c.export_parent = fake_pointer(2);
+ ctx_c.duplicator = fake_pointer(1);
+ ctx_c.export_name = "C";
+
+ HierarchyContext ctx_d = {0};
+ ctx_d.object = fake_pointer(2);
+ ctx_d.export_parent = fake_pointer(3);
+ ctx_d.duplicator = nullptr;
+ ctx_d.export_name = "D";
+
+ EXPECT_LT(ctx_a, ctx_b);
+ EXPECT_LT(ctx_a, ctx_c);
+ EXPECT_LT(ctx_a, ctx_d);
+ EXPECT_LT(ctx_b, ctx_c);
+ EXPECT_LT(ctx_b, ctx_d);
+ EXPECT_LT(ctx_c, ctx_d);
+
+ EXPECT_FALSE(ctx_b < ctx_a);
+ EXPECT_FALSE(ctx_c < ctx_a);
+ EXPECT_FALSE(ctx_d < ctx_a);
+ EXPECT_FALSE(ctx_c < ctx_b);
+ EXPECT_FALSE(ctx_d < ctx_b);
+ EXPECT_FALSE(ctx_d < ctx_c);
+}
+
+} // 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/common/intern/object_identifier_test.cc b/source/blender/io/common/intern/object_identifier_test.cc
new file mode 100644
index 00000000000..2b565876f22
--- /dev/null
+++ b/source/blender/io/common/intern/object_identifier_test.cc
@@ -0,0 +1,234 @@
+/*
+ * 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 "testing/testing.h"
+
+#include "BLI_utildefines.h"
+
+#include <climits>
+
+namespace blender::io {
+
+namespace {
+
+/* Return object pointer for use in tests. This makes it possible to reliably test for
+ * order/equality functions while using hard-coded values for simplicity. */
+Object *fake_pointer(int value)
+{
+ return static_cast<Object *>(POINTER_FROM_INT(value));
+}
+
+/* PersistentID subclass for use in tests, making it easier to construct test values. */
+class TestPersistentID : public PersistentID {
+ public:
+ TestPersistentID(int value0,
+ int value1,
+ int value2,
+ int value3,
+ int value4,
+ int value5,
+ int value6,
+ int value7)
+ {
+ persistent_id_[0] = value0;
+ persistent_id_[1] = value1;
+ persistent_id_[2] = value2;
+ persistent_id_[3] = value3;
+ persistent_id_[4] = value4;
+ persistent_id_[5] = value5;
+ persistent_id_[6] = value6;
+ persistent_id_[7] = value7;
+ }
+ TestPersistentID(int value0, int value1, int value2)
+ : TestPersistentID(value0, value1, value2, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX)
+ {
+ }
+ TestPersistentID(int value0, int value1) : TestPersistentID(value0, value1, INT_MAX)
+ {
+ }
+ explicit TestPersistentID(int value0) : TestPersistentID(value0, INT_MAX)
+ {
+ }
+};
+
+/* ObjectIdentifier subclass for use in tests, making it easier to construct test values. */
+class TestObjectIdentifier : public ObjectIdentifier {
+ public:
+ TestObjectIdentifier(Object *object, Object *duplicated_by, const PersistentID &persistent_id)
+ : ObjectIdentifier(object, duplicated_by, persistent_id)
+ {
+ }
+};
+
+} // namespace
+
+class ObjectIdentifierOrderTest : public testing::Test {
+};
+
+TEST_F(ObjectIdentifierOrderTest, graph_root)
+{
+ ObjectIdentifier id_root_1 = ObjectIdentifier::for_graph_root();
+ ObjectIdentifier id_root_2 = ObjectIdentifier::for_graph_root();
+ EXPECT_TRUE(id_root_1 == id_root_2);
+ EXPECT_FALSE(id_root_1 < id_root_2);
+ EXPECT_FALSE(id_root_2 < id_root_1);
+
+ ObjectIdentifier id_a = ObjectIdentifier::for_real_object(fake_pointer(1));
+ EXPECT_FALSE(id_root_1 == id_a);
+ EXPECT_TRUE(id_root_1 < id_a);
+ EXPECT_FALSE(id_a < id_root_1);
+
+ ObjectIdentifier id_accidental_root = ObjectIdentifier::for_real_object(nullptr);
+ EXPECT_TRUE(id_root_1 == id_accidental_root);
+ EXPECT_FALSE(id_root_1 < id_accidental_root);
+ EXPECT_FALSE(id_accidental_root < id_root_1);
+}
+
+TEST_F(ObjectIdentifierOrderTest, real_objects)
+{
+ ObjectIdentifier id_a = ObjectIdentifier::for_real_object(fake_pointer(1));
+ ObjectIdentifier id_b = ObjectIdentifier::for_real_object(fake_pointer(2));
+ EXPECT_FALSE(id_a == id_b);
+ EXPECT_TRUE(id_a < id_b);
+}
+
+TEST_F(ObjectIdentifierOrderTest, duplicated_objects)
+{
+ ObjectIdentifier id_real_a = ObjectIdentifier::for_real_object(fake_pointer(1));
+ TestObjectIdentifier id_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0));
+ TestObjectIdentifier id_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0));
+ TestObjectIdentifier id_different_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(1));
+
+ EXPECT_FALSE(id_real_a == id_dupli_a);
+ EXPECT_FALSE(id_dupli_a == id_dupli_b);
+ EXPECT_TRUE(id_real_a < id_dupli_a);
+ EXPECT_TRUE(id_real_a < id_dupli_b);
+ EXPECT_TRUE(id_dupli_a < id_dupli_b);
+ EXPECT_TRUE(id_dupli_a < id_different_dupli_b);
+
+ EXPECT_FALSE(id_dupli_b == id_different_dupli_b);
+ EXPECT_FALSE(id_dupli_a == id_different_dupli_b);
+ EXPECT_TRUE(id_dupli_b < id_different_dupli_b);
+ EXPECT_FALSE(id_different_dupli_b < id_dupli_b);
+}
+
+TEST_F(ObjectIdentifierOrderTest, behaviour_as_map_keys)
+{
+ ObjectIdentifier id_root = ObjectIdentifier::for_graph_root();
+ ObjectIdentifier id_another_root = ObjectIdentifier::for_graph_root();
+ ObjectIdentifier id_real_a = ObjectIdentifier::for_real_object(fake_pointer(1));
+ TestObjectIdentifier id_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0));
+ TestObjectIdentifier id_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0));
+ AbstractHierarchyIterator::ExportGraph graph;
+
+ /* This inserts the keys with default values. */
+ graph[id_root];
+ graph[id_real_a];
+ graph[id_dupli_a];
+ graph[id_dupli_b];
+ graph[id_another_root];
+
+ EXPECT_EQ(4, graph.size());
+
+ graph.erase(id_another_root);
+ EXPECT_EQ(3, graph.size());
+
+ TestObjectIdentifier id_another_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0));
+ graph.erase(id_another_dupli_b);
+ EXPECT_EQ(2, graph.size());
+}
+
+TEST_F(ObjectIdentifierOrderTest, map_copy_and_update)
+{
+ ObjectIdentifier id_root = ObjectIdentifier::for_graph_root();
+ ObjectIdentifier id_real_a = ObjectIdentifier::for_real_object(fake_pointer(1));
+ TestObjectIdentifier id_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0));
+ TestObjectIdentifier id_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0));
+ TestObjectIdentifier id_dupli_c(fake_pointer(1), fake_pointer(3), TestPersistentID(1));
+ AbstractHierarchyIterator::ExportGraph graph;
+
+ /* This inserts the keys with default values. */
+ graph[id_root];
+ graph[id_real_a];
+ graph[id_dupli_a];
+ graph[id_dupli_b];
+ graph[id_dupli_c];
+ EXPECT_EQ(5, graph.size());
+
+ AbstractHierarchyIterator::ExportGraph graph_copy = graph;
+ EXPECT_EQ(5, graph_copy.size());
+
+ // Updating a value in a copy should not update the original.
+ HierarchyContext ctx1;
+ HierarchyContext ctx2;
+ ctx1.object = fake_pointer(1);
+ ctx2.object = fake_pointer(2);
+
+ graph_copy[id_root].insert(&ctx1);
+ EXPECT_EQ(0, graph[id_root].size());
+
+ // Deleting a key in the copy should not update the original.
+ graph_copy.erase(id_dupli_c);
+ EXPECT_EQ(4, graph_copy.size());
+ EXPECT_EQ(5, graph.size());
+}
+
+class PersistentIDTest : public testing::Test {
+};
+
+TEST_F(PersistentIDTest, is_from_same_instancer)
+{
+ PersistentID child_id_a = TestPersistentID(42, 327);
+ PersistentID child_id_b = TestPersistentID(17, 327);
+ PersistentID child_id_c = TestPersistentID(17);
+
+ EXPECT_TRUE(child_id_a.is_from_same_instancer_as(child_id_b));
+ EXPECT_FALSE(child_id_a.is_from_same_instancer_as(child_id_c));
+}
+
+TEST_F(PersistentIDTest, instancer_id)
+{
+ PersistentID child_id = TestPersistentID(42, 327);
+
+ PersistentID expect_instancer_id = TestPersistentID(327);
+ EXPECT_EQ(expect_instancer_id, child_id.instancer_pid());
+
+ PersistentID empty_id;
+ EXPECT_EQ(empty_id, child_id.instancer_pid().instancer_pid());
+
+ EXPECT_LT(child_id, expect_instancer_id);
+ EXPECT_LT(expect_instancer_id, empty_id);
+}
+
+TEST_F(PersistentIDTest, as_object_name_suffix)
+{
+ EXPECT_EQ("", PersistentID().as_object_name_suffix());
+ EXPECT_EQ("47", TestPersistentID(47).as_object_name_suffix());
+ EXPECT_EQ("327-47", TestPersistentID(47, 327).as_object_name_suffix());
+ EXPECT_EQ("42-327-47", TestPersistentID(47, 327, 42).as_object_name_suffix());
+
+ EXPECT_EQ("7-6-5-4-3-2-1-0", TestPersistentID(0, 1, 2, 3, 4, 5, 6, 7).as_object_name_suffix());
+
+ EXPECT_EQ("0-0-0", TestPersistentID(0, 0, 0).as_object_name_suffix());
+ EXPECT_EQ("0-0", TestPersistentID(0, 0).as_object_name_suffix());
+ EXPECT_EQ("-3--2--1", TestPersistentID(-1, -2, -3).as_object_name_suffix());
+}
+
+} // namespace blender::io
diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt
index 19e16a5b328..79b15c60b94 100644
--- a/source/blender/io/usd/CMakeLists.txt
+++ b/source/blender/io/usd/CMakeLists.txt
@@ -109,3 +109,15 @@ else()
endif()
target_link_libraries(bf_usd INTERFACE ${TBB_LIBRARIES})
+
+if(WITH_GTESTS)
+ set(TEST_SRC
+ tests/usd_stage_creation_test.cc
+ )
+ set(TEST_INC
+ )
+ set(TEST_LIB
+ )
+ include(GTestTesting)
+ blender_add_test_lib(bf_io_usd_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
+endif()
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;
diff --git a/source/blender/io/usd/intern/usd_exporter_context.h b/source/blender/io/usd/intern/usd_exporter_context.h
index 07a9d0fc0c5..5513768b527 100644
--- a/source/blender/io/usd/intern/usd_exporter_context.h
+++ b/source/blender/io/usd/intern/usd_exporter_context.h
@@ -16,8 +16,7 @@
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
*/
-#ifndef __USD_EXPORTER_CONTEXT_H__
-#define __USD_EXPORTER_CONTEXT_H__
+#pragma once
#include "usd.h"
@@ -44,5 +43,3 @@ struct USDExporterContext {
} // 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 388b588b331..39fbef70e81 100644
--- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
+++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
@@ -34,6 +34,7 @@
#include "BKE_duplilist.h"
#include "BLI_assert.h"
+#include "BLI_utildefines.h"
#include "DEG_depsgraph_query.h"
@@ -60,7 +61,7 @@ bool USDHierarchyIterator::mark_as_weak_export(const Object *object) const
return false;
}
-void USDHierarchyIterator::delete_object_writer(AbstractHierarchyWriter *writer)
+void USDHierarchyIterator::release_writer(AbstractHierarchyWriter *writer)
{
delete static_cast<USDAbstractWriter *>(writer);
}
@@ -72,7 +73,7 @@ std::string USDHierarchyIterator::make_valid_name(const std::string &name) const
void USDHierarchyIterator::set_export_frame(float frame_nr)
{
- // The USD stage is already set up to have FPS timecodes per frame.
+ /* The USD stage is already set up to have FPS time-codes per frame. */
export_time_ = pxr::UsdTimeCode(frame_nr);
}
@@ -142,7 +143,8 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_hair_writer(const Hierarch
return new USDHairWriter(create_usd_export_context(context));
}
-AbstractHierarchyWriter *USDHierarchyIterator::create_particle_writer(const HierarchyContext *)
+AbstractHierarchyWriter *USDHierarchyIterator::create_particle_writer(
+ const HierarchyContext *UNUSED(context))
{
return nullptr;
}
diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.h b/source/blender/io/usd/intern/usd_hierarchy_iterator.h
index 7d750bff0cb..03e80ce735a 100644
--- a/source/blender/io/usd/intern/usd_hierarchy_iterator.h
+++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.h
@@ -16,8 +16,7 @@
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
*/
-#ifndef __USD_HIERARCHY_ITERATOR_H__
-#define __USD_HIERARCHY_ITERATOR_H__
+#pragma once
#include "IO_abstract_hierarchy_iterator.h"
#include "usd.h"
@@ -66,7 +65,7 @@ class USDHierarchyIterator : public AbstractHierarchyIterator {
virtual AbstractHierarchyWriter *create_particle_writer(
const HierarchyContext *context) override;
- virtual void delete_object_writer(AbstractHierarchyWriter *writer) override;
+ virtual void release_writer(AbstractHierarchyWriter *writer) override;
private:
USDExporterContext create_usd_export_context(const HierarchyContext *context);
@@ -75,5 +74,3 @@ class USDHierarchyIterator : public AbstractHierarchyIterator {
} // namespace usd
} // namespace io
} // namespace blender
-
-#endif /* __USD_HIERARCHY_ITERATOR_H__ */
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h
index f81cf5197af..a689deaf0d8 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.h
+++ b/source/blender/io/usd/intern/usd_writer_abstract.h
@@ -16,8 +16,7 @@
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
*/
-#ifndef __USD_WRITER_ABSTRACT_H__
-#define __USD_WRITER_ABSTRACT_H__
+#pragma once
#include "IO_abstract_hierarchy_iterator.h"
#include "usd_exporter_context.h"
@@ -78,5 +77,3 @@ class USDAbstractWriter : public AbstractHierarchyWriter {
} // namespace usd
} // namespace io
} // namespace blender
-
-#endif /* __USD_WRITER_ABSTRACT_H__ */
diff --git a/source/blender/io/usd/intern/usd_writer_camera.h b/source/blender/io/usd/intern/usd_writer_camera.h
index 8b5795d7d9f..1c613d7879b 100644
--- a/source/blender/io/usd/intern/usd_writer_camera.h
+++ b/source/blender/io/usd/intern/usd_writer_camera.h
@@ -16,8 +16,7 @@
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
*/
-#ifndef __USD_WRITER_CAMERA_H__
-#define __USD_WRITER_CAMERA_H__
+#pragma once
#include "usd_writer_abstract.h"
@@ -38,5 +37,3 @@ class USDCameraWriter : public USDAbstractWriter {
} // 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 0e0256bdb69..0fd5c4ce727 100644
--- a/source/blender/io/usd/intern/usd_writer_hair.cc
+++ b/source/blender/io/usd/intern/usd_writer_hair.cc
@@ -82,7 +82,7 @@ void USDHairWriter::do_write(HierarchyContext &context)
}
}
-bool USDHairWriter::check_is_animated(const HierarchyContext &) const
+bool USDHairWriter::check_is_animated(const HierarchyContext &UNUSED(context)) const
{
return true;
}
diff --git a/source/blender/io/usd/intern/usd_writer_hair.h b/source/blender/io/usd/intern/usd_writer_hair.h
index cecacd0a355..b9a28013875 100644
--- a/source/blender/io/usd/intern/usd_writer_hair.h
+++ b/source/blender/io/usd/intern/usd_writer_hair.h
@@ -16,8 +16,7 @@
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
*/
-#ifndef __USD_WRITER_HAIR_H__
-#define __USD_WRITER_HAIR_H__
+#pragma once
#include "usd_writer_abstract.h"
@@ -38,5 +37,3 @@ class USDHairWriter : public USDAbstractWriter {
} // namespace usd
} // namespace io
} // namespace blender
-
-#endif /* __USD_WRITER_HAIR_H__ */
diff --git a/source/blender/io/usd/intern/usd_writer_light.h b/source/blender/io/usd/intern/usd_writer_light.h
index 73666622af1..082050ad071 100644
--- a/source/blender/io/usd/intern/usd_writer_light.h
+++ b/source/blender/io/usd/intern/usd_writer_light.h
@@ -16,8 +16,7 @@
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
*/
-#ifndef __USD_WRITER_LIGHT_H__
-#define __USD_WRITER_LIGHT_H__
+#pragma once
#include "usd_writer_abstract.h"
@@ -37,5 +36,3 @@ class USDLightWriter : public USDAbstractWriter {
} // 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 29a9734f876..bd2c549e729 100644
--- a/source/blender/io/usd/intern/usd_writer_mesh.cc
+++ b/source/blender/io/usd/intern/usd_writer_mesh.cc
@@ -338,7 +338,7 @@ void USDGenericMeshWriter::assign_materials(const HierarchyContext &context,
* https://github.com/PixarAnimationStudios/USD/issues/542 for more info. */
bool mesh_material_bound = false;
pxr::UsdShadeMaterialBindingAPI material_binding_api(usd_mesh.GetPrim());
- for (short mat_num = 0; mat_num < context.object->totcol; mat_num++) {
+ for (int mat_num = 0; mat_num < context.object->totcol; mat_num++) {
Material *material = BKE_object_material_get(context.object, mat_num + 1);
if (material == nullptr) {
continue;
diff --git a/source/blender/io/usd/intern/usd_writer_mesh.h b/source/blender/io/usd/intern/usd_writer_mesh.h
index a14ceecfa53..078daa05501 100644
--- a/source/blender/io/usd/intern/usd_writer_mesh.h
+++ b/source/blender/io/usd/intern/usd_writer_mesh.h
@@ -16,8 +16,7 @@
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
*/
-#ifndef __USD_WRITER_MESH_H__
-#define __USD_WRITER_MESH_H__
+#pragma once
#include "usd_writer_abstract.h"
@@ -66,5 +65,3 @@ class USDMeshWriter : public USDGenericMeshWriter {
} // namespace usd
} // namespace io
} // namespace blender
-
-#endif /* __USD_WRITER_MESH_H__ */
diff --git a/source/blender/io/usd/intern/usd_writer_metaball.h b/source/blender/io/usd/intern/usd_writer_metaball.h
index 9f51a3314a5..216f5a2638f 100644
--- a/source/blender/io/usd/intern/usd_writer_metaball.h
+++ b/source/blender/io/usd/intern/usd_writer_metaball.h
@@ -16,8 +16,7 @@
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
-#ifndef __USD_WRITER_METABALL_H__
-#define __USD_WRITER_METABALL_H__
+#pragma once
#include "usd_writer_mesh.h"
@@ -42,5 +41,3 @@ class USDMetaballWriter : public USDGenericMeshWriter {
} // 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 643f1a8f4b1..49983115455 100644
--- a/source/blender/io/usd/intern/usd_writer_transform.cc
+++ b/source/blender/io/usd/intern/usd_writer_transform.cc
@@ -58,6 +58,9 @@ bool USDTransformWriter::check_is_animated(const HierarchyContext &context) cons
* depsgraph whether this object instance has a time source. */
return true;
}
+ if (check_has_physics(context)) {
+ return true;
+ }
return BKE_object_moves_in_time(context.object, context.animation_check_include_parent);
}
diff --git a/source/blender/io/usd/intern/usd_writer_transform.h b/source/blender/io/usd/intern/usd_writer_transform.h
index 8b4741f1177..39a1f20e7e8 100644
--- a/source/blender/io/usd/intern/usd_writer_transform.h
+++ b/source/blender/io/usd/intern/usd_writer_transform.h
@@ -16,8 +16,7 @@
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
*/
-#ifndef __USD_WRITER_TRANSFORM_H__
-#define __USD_WRITER_TRANSFORM_H__
+#pragma once
#include "usd_writer_abstract.h"
@@ -42,5 +41,3 @@ class USDTransformWriter : public USDAbstractWriter {
} // namespace usd
} // namespace io
} // namespace blender
-
-#endif /* __USD_WRITER_TRANSFORM_H__ */
diff --git a/source/blender/io/usd/tests/usd_stage_creation_test.cc b/source/blender/io/usd/tests/usd_stage_creation_test.cc
new file mode 100644
index 00000000000..e6bd0bab6ce
--- /dev/null
+++ b/source/blender/io/usd/tests/usd_stage_creation_test.cc
@@ -0,0 +1,70 @@
+/*
+ * 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 "testing/testing.h"
+#include <pxr/usd/usd/stage.h>
+
+#include <string>
+
+#include "BLI_path_util.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_appdir.h"
+
+extern "C" {
+/* Workaround to make it possible to pass a path at runtime to USD. See creator.c. */
+void usd_initialise_plugin_path(const char *datafiles_usd_path);
+}
+
+namespace blender::io::usd {
+
+class USDStageCreationTest : public testing::Test {
+};
+
+TEST_F(USDStageCreationTest, JSONFileLoadingTest)
+{
+ const std::string &release_dir = blender::tests::flags_test_release_dir();
+ if (release_dir.empty()) {
+ FAIL();
+ }
+
+ char usd_datafiles_dir[FILE_MAX];
+ BLI_path_join(usd_datafiles_dir, FILE_MAX, release_dir.c_str(), "datafiles", "usd", nullptr);
+
+ usd_initialise_plugin_path(usd_datafiles_dir);
+
+ /* Simply the ability to create a USD Stage for a specific filename means that the extension
+ * has been recognized by the USD library, and that a USD plugin has been loaded to write such
+ * files. Practically, this is a test to see whether the USD JSON files can be found and
+ * loaded. */
+ std::string filename = "usd-stage-creation-test.usdc";
+ pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(filename);
+ if (usd_stage != nullptr) {
+ /* Even though we don't call `usd_stage->SaveFile()`, a file is still created on the
+ * file-system when we call CreateNew(). It's immediately closed, though,
+ * so we can safely call `unlink()` here. */
+ unlink(filename.c_str());
+ }
+ else {
+ FAIL() << "unable to find suitable USD plugin to write " << filename
+ << "; re-run with the environment variable PXR_PATH_DEBUG non-empty to see which paths "
+ "are considered.";
+ }
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h
index eee98521289..f2826cd1d7c 100644
--- a/source/blender/io/usd/usd.h
+++ b/source/blender/io/usd/usd.h
@@ -17,8 +17,7 @@
* All rights reserved.
*/
-#ifndef __USD_H__
-#define __USD_H__
+#pragma once
#include "DEG_depsgraph.h"
@@ -59,5 +58,3 @@ int USD_get_version(void);
#ifdef __cplusplus
}
#endif
-
-#endif /* __USD_H__ */