diff options
Diffstat (limited to 'source/blender/io')
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) {'<', "<"}, {'>', ">"}, {'"', """}, {'\'', "'"}, {'&', "&"}}; 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__ */ |