From 7813379c5a0a146140e2d18ae72c021111822c66 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Sun, 1 Oct 2023 20:10:15 +1300 Subject: [PATCH 01/14] Rebase ontop of origin/main --- source/blender/io/usd/CMakeLists.txt | 1 + .../usd/intern/usd_reader_pointinstancer.cc | 186 ++++++++++++++++++ .../io/usd/intern/usd_reader_pointinstancer.h | 43 ++++ .../blender/io/usd/intern/usd_reader_stage.cc | 5 + 4 files changed, 235 insertions(+) create mode 100644 source/blender/io/usd/intern/usd_reader_pointinstancer.cc create mode 100644 source/blender/io/usd/intern/usd_reader_pointinstancer.h diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt index fa20dc94941..cb10bc2f46a 100644 --- a/source/blender/io/usd/CMakeLists.txt +++ b/source/blender/io/usd/CMakeLists.txt @@ -111,6 +111,7 @@ set(SRC intern/usd_reader_stage.cc intern/usd_reader_volume.cc intern/usd_reader_xform.cc + intern/usd_reader_pointinstancer.cc intern/usd_skel_convert.cc usd.h diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc new file mode 100644 index 00000000000..bc02aedb858 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc @@ -0,0 +1,186 @@ +/* + * 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) 2021 NVIDIA Corporation. + * All rights reserved. + */ + +#include "usd_reader_pointinstancer.h" + +#include "BKE_node_tree_update.h" +#include "BKE_modifier.h" +#include "BKE_pointcloud.h" +#include "BKE_object.h" +#include "BKE_attribute.hh" +#include "BKE_node_runtime.hh" +#include "BLI_math_quaternion.hh" + +#include + +#include + +namespace blender::io::usd { + +USDPointInstancerReader::USDPointInstancerReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDXformReader(prim, import_params, settings) +{ +} + +bool USDPointInstancerReader::valid() const +{ + return prim_.IsValid() && prim_.IsA(); +} + +void USDPointInstancerReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + void* point_cloud = BKE_pointcloud_add(bmain, name_.c_str()); + this->object_ = BKE_object_add_only_object(bmain, OB_POINTCLOUD, name_.c_str()); + this->object_->data = point_cloud; +} + +void USDPointInstancerReader::read_object_data(Main *bmain, const double motionSampleTime) { + PointCloud *base_point_cloud = static_cast(object_->data); + + pxr::UsdGeomPointInstancer point_instancer_prim(prim_); + + if (!point_instancer_prim) { + return; + } + + pxr::VtArray positions; + pxr::VtArray scales; + pxr::VtArray orientations; + pxr::VtArray proto_indices; + point_instancer_prim.GetPositionsAttr().Get(&positions); + point_instancer_prim.GetScalesAttr().Get(&scales); + point_instancer_prim.GetOrientationsAttr().Get(&orientations); + point_instancer_prim.GetProtoIndicesAttr().Get(&proto_indices); + + PointCloud *point_cloud = BKE_pointcloud_new_nomain(positions.size()); + + auto positions_span = point_cloud->positions_for_write(); + + for (int i = 0; i < positions.size(); i++) { + positions_span[i] = float3(positions[i][0], positions[i][1], positions[i][2]); + } + + auto scales_attribute = point_cloud->attributes_for_write().lookup_or_add_for_write_only_span("scales", ATTR_DOMAIN_POINT); + + for (int i = 0; i < scales.size(); i++) { + scales_attribute.span[i] = float3(scales[i][0], scales[i][1], scales[i][2]); + } + + scales_attribute.span.save(); + + auto orientations_attribute = point_cloud->attributes_for_write().lookup_or_add_for_write_only_span("orientations", ATTR_DOMAIN_POINT); + + for (int i = 0; i < orientations.size(); i++) { + orientations_attribute.span[i] = math::Quaternion(orientations[i].GetImaginary()[0], orientations[i].GetImaginary()[1], orientations[i].GetImaginary()[2], orientations[i].GetReal()); + } + + orientations_attribute.span.save(); + + auto proto_indices_attribute = point_cloud->attributes_for_write().lookup_or_add_for_write_only_span("proto_indices", ATTR_DOMAIN_POINT); + + for (int i = 0; i < proto_indices.size(); i++) { + proto_indices_attribute.span[i] = proto_indices[i]; + } + + proto_indices_attribute.span.save(); + + BKE_pointcloud_nomain_to_pointcloud(point_cloud, base_point_cloud); + + ModifierData *md = BKE_modifier_new(eModifierType_Nodes); + BLI_addtail(&object_->modifiers, md); + NodesModifierData &nmd = *reinterpret_cast(md); + nmd.node_group = ntreeAddTree(NULL, "Instances", "GeometryNodeTree"); + IDProperty *nmd_properties = nmd.settings.properties; + + bNodeTree *ntree = nmd.node_group; + + ntree->tree_interface.add_socket("Geometry", + "", + "NodeSocketGeometry", + NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT, + nullptr + ); + bNode *group_input = nodeAddStaticNode(NULL, ntree, NODE_GROUP_INPUT); + group_input->locx = -400.0f; + bNode *group_output = nodeAddStaticNode(NULL, ntree, NODE_GROUP_OUTPUT); + group_output->locx = 500.0f; + group_output->flag |= NODE_DO_OUTPUT; + + bNode *instance_on_points_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_INSTANCE_ON_POINTS); + instance_on_points_node->locx = 300.0f; + bNode *rotation_to_euler_node = nodeAddStaticNode(NULL, ntree, FN_NODE_ROTATION_TO_EULER); + rotation_to_euler_node->locx = 100.0f; + rotation_to_euler_node->locy = -100.0f; + + bNode *collection_info_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_COLLECTION_INFO); + collection_info_node->locx = 100.0f; + collection_info_node->locy = 100.0f; + bool seperate_children = true; + nodeFindSocket(collection_info_node, SOCK_IN, "Separate Children")->default_value = (void*)&seperate_children; + + BKE_ntree_update_main_tree(bmain, ntree, nullptr); + + nodeAddLink(ntree, + group_input, + static_cast(group_input->outputs.first), + instance_on_points_node, + nodeFindSocket(instance_on_points_node, SOCK_IN, "Points") + ); + + nodeAddLink(ntree, + rotation_to_euler_node, + nodeFindSocket(rotation_to_euler_node, SOCK_OUT, "Euler"), + instance_on_points_node, + nodeFindSocket(instance_on_points_node, SOCK_IN, "Rotation")); + + nodeAddLink(ntree, + collection_info_node, + nodeFindSocket(collection_info_node, SOCK_OUT, "Instances"), + instance_on_points_node, + nodeFindSocket(instance_on_points_node, SOCK_IN, "Instance")); + + nodeAddLink(ntree, + instance_on_points_node, + nodeFindSocket(instance_on_points_node, SOCK_OUT, "Instances"), + group_output, + static_cast(group_output->inputs.first)); + + BKE_ntree_update_main_tree(bmain, ntree, nullptr); + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +pxr::SdfPathVector USDPointInstancerReader::proto_paths() const { + pxr::SdfPathVector paths; + + pxr::UsdGeomPointInstancer point_instancer_prim(prim_); + + if (!point_instancer_prim) { + return paths; + } + + point_instancer_prim.GetPrototypesRel().GetTargets(&paths); + + return paths; +} + + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.h b/source/blender/io/usd/intern/usd_reader_pointinstancer.h new file mode 100644 index 00000000000..bf974f8b76a --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.h @@ -0,0 +1,43 @@ +/* + * 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) 2021 NVIDIA Corporation. + * All rights reserved. + */ +#pragma once + +#include "usd_reader_xform.h" + +namespace blender::io::usd { + +/* Wraps the UsdGeomPointInstancer schema. Creates a Blender point cloud object. */ + +class USDPointInstancerReader : public USDXformReader { + + public: + USDPointInstancerReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings); + + bool valid() const override; + + void create_object(Main *bmain, double motionSampleTime) override; + + void read_object_data(Main *bmain, double motionSampleTime) override; + + pxr::SdfPathVector proto_paths() const; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc index a908e5b11d6..cb941466653 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.cc +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -14,6 +14,7 @@ #include "usd_reader_skeleton.h" #include "usd_reader_volume.h" #include "usd_reader_xform.h" +#include "usd_reader_pointinstancer.h" #include #include @@ -29,6 +30,7 @@ #include #include #include +#include #if PXR_VERSION >= 2111 # include @@ -80,6 +82,9 @@ USDPrimReader *USDStageReader::create_reader_if_allowed(const pxr::UsdPrim &prim if (params_.import_shapes && is_primitive_prim(prim)) { return new USDShapeReader(prim, params_, settings_); } + if (prim.IsA()) { + return new USDPointInstancerReader(prim, params_, settings_); + } if (params_.import_cameras && prim.IsA()) { return new USDCameraReader(prim, params_, settings_); } -- 2.30.2 From 57de56256e805c2140b147e3b1049fea0cb2c731 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Fri, 20 Oct 2023 13:05:54 +1300 Subject: [PATCH 02/14] Sample at the first position time sample if provided, use size checks for scales/orientations/proto indices --- .../usd/intern/usd_reader_pointinstancer.cc | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc index bc02aedb858..f896ca8888b 100644 --- a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc @@ -65,10 +65,20 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS pxr::VtArray scales; pxr::VtArray orientations; pxr::VtArray proto_indices; - point_instancer_prim.GetPositionsAttr().Get(&positions); - point_instancer_prim.GetScalesAttr().Get(&scales); - point_instancer_prim.GetOrientationsAttr().Get(&orientations); - point_instancer_prim.GetProtoIndicesAttr().Get(&proto_indices); + std::vector time_samples; + + point_instancer_prim.GetPositionsAttr().GetTimeSamples(&time_samples); + + double sample_time = motionSampleTime; + + if (!time_samples.empty()) { + sample_time = time_samples[0]; + } + + point_instancer_prim.GetPositionsAttr().Get(&positions, sample_time); + point_instancer_prim.GetScalesAttr().Get(&scales, sample_time); + point_instancer_prim.GetOrientationsAttr().Get(&orientations, sample_time); + point_instancer_prim.GetProtoIndicesAttr().Get(&proto_indices, sample_time); PointCloud *point_cloud = BKE_pointcloud_new_nomain(positions.size()); @@ -80,7 +90,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS auto scales_attribute = point_cloud->attributes_for_write().lookup_or_add_for_write_only_span("scales", ATTR_DOMAIN_POINT); - for (int i = 0; i < scales.size(); i++) { + for (int i = 0; i < std::min(positions.size(), scales.size()); i++) { scales_attribute.span[i] = float3(scales[i][0], scales[i][1], scales[i][2]); } @@ -88,7 +98,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS auto orientations_attribute = point_cloud->attributes_for_write().lookup_or_add_for_write_only_span("orientations", ATTR_DOMAIN_POINT); - for (int i = 0; i < orientations.size(); i++) { + for (int i = 0; i < std::min(positions.size(), orientations.size()); i++) { orientations_attribute.span[i] = math::Quaternion(orientations[i].GetImaginary()[0], orientations[i].GetImaginary()[1], orientations[i].GetImaginary()[2], orientations[i].GetReal()); } @@ -96,7 +106,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS auto proto_indices_attribute = point_cloud->attributes_for_write().lookup_or_add_for_write_only_span("proto_indices", ATTR_DOMAIN_POINT); - for (int i = 0; i < proto_indices.size(); i++) { + for (int i = 0; i < std::min(positions.size(), proto_indices.size()); i++) { proto_indices_attribute.span[i] = proto_indices[i]; } @@ -104,6 +114,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS BKE_pointcloud_nomain_to_pointcloud(point_cloud, base_point_cloud); + /* ModifierData *md = BKE_modifier_new(eModifierType_Nodes); BLI_addtail(&object_->modifiers, md); NodesModifierData &nmd = *reinterpret_cast(md); @@ -164,6 +175,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS static_cast(group_output->inputs.first)); BKE_ntree_update_main_tree(bmain, ntree, nullptr); + */ USDXformReader::read_object_data(bmain, motionSampleTime); } -- 2.30.2 From c735c3fbc63473378666eadc2bd6a105fd88a3f0 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Fri, 20 Oct 2023 15:59:09 +1300 Subject: [PATCH 03/14] Use default values for scales and orientations --- .../io/usd/intern/usd_reader_pointinstancer.cc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc index a334309e76e..24f108feed8 100644 --- a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc @@ -90,16 +90,24 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS auto scales_attribute = point_cloud->attributes_for_write().lookup_or_add_for_write_only_span("scales", ATTR_DOMAIN_POINT); - for (int i = 0; i < std::min(positions.size(), scales.size()); i++) { - scales_attribute.span[i] = float3(scales[i][0], scales[i][1], scales[i][2]); + for (int i = 0; i < positions.size(); i++) { + if (i < scales.size()) { + scales_attribute.span[i] = float3(scales[i][0], scales[i][1], scales[i][2]); + } else { + scales_attribute.span[i] = float3(1.0); + } } scales_attribute.span.save(); auto orientations_attribute = point_cloud->attributes_for_write().lookup_or_add_for_write_only_span("orientations", ATTR_DOMAIN_POINT); - for (int i = 0; i < std::min(positions.size(), orientations.size()); i++) { - orientations_attribute.span[i] = math::Quaternion(orientations[i].GetImaginary()[0], orientations[i].GetImaginary()[1], orientations[i].GetImaginary()[2], orientations[i].GetReal()); + for (int i = 0; i < positions.size(); i++) { + if (i < orientations.size()) { + orientations_attribute.span[i] = math::Quaternion(orientations[i].GetImaginary()[0], orientations[i].GetImaginary()[1], orientations[i].GetImaginary()[2], orientations[i].GetReal()); + } else { + orientations_attribute.span[i] = math::Quaternion(); + } } orientations_attribute.span.save(); -- 2.30.2 From 3a146dd6778f96e5ecf2963e5118547a4544e79b Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Sat, 2 Dec 2023 22:21:25 +1300 Subject: [PATCH 04/14] Update headers --- source/blender/io/usd/intern/usd_reader_pointinstancer.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc index 24f108feed8..ce0bb20c8db 100644 --- a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc @@ -19,8 +19,8 @@ #include "usd_reader_pointinstancer.h" -#include "BKE_node_tree_update.h" -#include "BKE_modifier.h" +#include "BKE_node_tree_update.hh" +#include "BKE_modifier.hh" #include "BKE_pointcloud.h" #include "BKE_object.hh" #include "BKE_attribute.hh" -- 2.30.2 From be7098ebea6006f00eecad49a02ef342186672b4 Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Mon, 15 Jan 2024 20:45:20 -0500 Subject: [PATCH 05/14] USD: point instancer import updates Added usd_reader_pointinstancer.h to CMakeLists. Creating readers for point instancer prototypes. Creating collections containing point instancer prototype objects. Finished setup of the point cloud geometry nodes modifier node tree, as follows: Creating Named Attribute nodes for reading the "orientations", "scales" and "proto_indices" point attributes; setting the required flags on the Collection Info and Instance On Points nodes; setting the prototype collection object on the Collection Info node. Added a USDPrimReader::is_in_instancer_proto_ boolean flag which identifies readers of prims that are in point instancer prototypes. The flag simplifies management of collections and is used to determine whether global transforms should be applied to root objects. Fixed formatting. --- source/blender/io/usd/CMakeLists.txt | 1 + .../blender/io/usd/intern/usd_capi_import.cc | 5 +- .../usd/intern/usd_reader_pointinstancer.cc | 350 ++++++++++++------ .../io/usd/intern/usd_reader_pointinstancer.h | 18 +- .../blender/io/usd/intern/usd_reader_prim.cc | 8 +- .../blender/io/usd/intern/usd_reader_prim.h | 13 + .../blender/io/usd/intern/usd_reader_stage.cc | 199 ++++++++-- .../blender/io/usd/intern/usd_reader_stage.h | 53 ++- .../blender/io/usd/intern/usd_reader_xform.cc | 2 +- 9 files changed, 477 insertions(+), 172 deletions(-) diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt index 54dc97ee129..c72f313323b 100644 --- a/source/blender/io/usd/CMakeLists.txt +++ b/source/blender/io/usd/CMakeLists.txt @@ -153,6 +153,7 @@ set(SRC intern/usd_reader_stage.h intern/usd_reader_volume.h intern/usd_reader_xform.h + intern/usd_reader_pointinstancer.h intern/usd_skel_convert.h intern/usd_skel_root_utils.h ) diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index 8bebf9a9c95..3bdfe5c54f0 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -304,7 +304,7 @@ static void import_startjob(void *customdata, wmJobWorkerStatus *worker_status) data->archive = archive; - archive->collect_readers(data->bmain); + archive->collect_readers(); if (data->params.import_materials && data->params.import_all_materials) { archive->import_all_materials(data->bmain); @@ -412,7 +412,7 @@ static void import_endjob(void *customdata) if (!reader) { continue; } - if (reader->prim().IsInPrototype()) { + if (reader->is_in_proto()) { /* Skip prototype prims, as these are added to prototype collections. */ continue; } @@ -429,6 +429,7 @@ static void import_endjob(void *customdata) if (!reader) { continue; } + Object *ob = reader->object(); if (!ob) { continue; diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc index ce0bb20c8db..78f5b44a6db 100644 --- a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc @@ -19,23 +19,47 @@ #include "usd_reader_pointinstancer.h" -#include "BKE_node_tree_update.hh" -#include "BKE_modifier.hh" -#include "BKE_pointcloud.h" -#include "BKE_object.hh" #include "BKE_attribute.hh" +#include "BKE_lib_id.hh" +#include "BKE_modifier.hh" #include "BKE_node_runtime.hh" +#include "BKE_node_tree_update.hh" +#include "BKE_object.hh" +#include "BKE_pointcloud.hh" #include "BLI_math_quaternion.hh" +#include "BLI_string.h" + +#include "DNA_collection_types.h" #include #include +/** + * Create a node to read a geometry attribute of the given name and type. + */ +static bNode *add_input_named_attrib_node(bNodeTree *ntree, const char *name, int8_t prop_type) +{ + bNode *node = nodeAddStaticNode(NULL, ntree, GEO_NODE_INPUT_NAMED_ATTRIBUTE); + BLI_assert(node); + + NodeGeometryInputNamedAttribute *input_attrib_node_data = + reinterpret_cast(node->storage); + input_attrib_node_data->data_type = prop_type; + + bNodeSocket *socket = nodeFindSocket(node, SOCK_IN, "Name"); + BLI_assert(socket); + bNodeSocketValueString *str_value = static_cast(socket->default_value); + BLI_strncpy(str_value->value, name, MAX_NAME); + + return node; +} + namespace blender::io::usd { USDPointInstancerReader::USDPointInstancerReader(const pxr::UsdPrim &prim, - const USDImportParams &import_params, - const ImportSettings &settings) + const USDImportParams &import_params, + const ImportSettings &settings) : USDXformReader(prim, import_params, settings) { } @@ -47,160 +71,244 @@ bool USDPointInstancerReader::valid() const void USDPointInstancerReader::create_object(Main *bmain, const double /* motionSampleTime */) { - void* point_cloud = BKE_pointcloud_add(bmain, name_.c_str()); + void *point_cloud = BKE_pointcloud_add(bmain, name_.c_str()); this->object_ = BKE_object_add_only_object(bmain, OB_POINTCLOUD, name_.c_str()); this->object_->data = point_cloud; } -void USDPointInstancerReader::read_object_data(Main *bmain, const double motionSampleTime) { - PointCloud *base_point_cloud = static_cast(object_->data); +void USDPointInstancerReader::read_object_data(Main *bmain, const double motionSampleTime) +{ + PointCloud *base_point_cloud = static_cast(object_->data); - pxr::UsdGeomPointInstancer point_instancer_prim(prim_); + pxr::UsdGeomPointInstancer point_instancer_prim(prim_); - if (!point_instancer_prim) { - return; + if (!point_instancer_prim) { + return; + } + + pxr::VtArray positions; + pxr::VtArray scales; + pxr::VtArray orientations; + pxr::VtArray proto_indices; + std::vector time_samples; + + point_instancer_prim.GetPositionsAttr().GetTimeSamples(&time_samples); + + double sample_time = motionSampleTime; + + if (!time_samples.empty()) { + sample_time = time_samples[0]; + } + + point_instancer_prim.GetPositionsAttr().Get(&positions, sample_time); + point_instancer_prim.GetScalesAttr().Get(&scales, sample_time); + point_instancer_prim.GetOrientationsAttr().Get(&orientations, sample_time); + point_instancer_prim.GetProtoIndicesAttr().Get(&proto_indices, sample_time); + + PointCloud *point_cloud = BKE_pointcloud_new_nomain(positions.size()); + + auto positions_span = point_cloud->positions_for_write(); + + for (int i = 0; i < positions.size(); i++) { + positions_span[i] = float3(positions[i][0], positions[i][1], positions[i][2]); + } + + auto scales_attribute = + point_cloud->attributes_for_write().lookup_or_add_for_write_only_span( + "scales", bke::AttrDomain::Point); + + for (int i = 0; i < positions.size(); i++) { + if (i < scales.size()) { + scales_attribute.span[i] = float3(scales[i][0], scales[i][1], scales[i][2]); } - - pxr::VtArray positions; - pxr::VtArray scales; - pxr::VtArray orientations; - pxr::VtArray proto_indices; - std::vector time_samples; - - point_instancer_prim.GetPositionsAttr().GetTimeSamples(&time_samples); - - double sample_time = motionSampleTime; - - if (!time_samples.empty()) { - sample_time = time_samples[0]; + else { + scales_attribute.span[i] = float3(1.0); } + } - point_instancer_prim.GetPositionsAttr().Get(&positions, sample_time); - point_instancer_prim.GetScalesAttr().Get(&scales, sample_time); - point_instancer_prim.GetOrientationsAttr().Get(&orientations, sample_time); - point_instancer_prim.GetProtoIndicesAttr().Get(&proto_indices, sample_time); + scales_attribute.span.save(); - PointCloud *point_cloud = BKE_pointcloud_new_nomain(positions.size()); + auto orientations_attribute = + point_cloud->attributes_for_write().lookup_or_add_for_write_only_span( + "orientations", bke::AttrDomain::Point); - auto positions_span = point_cloud->positions_for_write(); - - for (int i = 0; i < positions.size(); i++) { - positions_span[i] = float3(positions[i][0], positions[i][1], positions[i][2]); + for (int i = 0; i < positions.size(); i++) { + if (i < orientations.size()) { + orientations_attribute.span[i] = math::Quaternion(orientations[i].GetReal(), + orientations[i].GetImaginary()[0], + orientations[i].GetImaginary()[1], + orientations[i].GetImaginary()[2]); } - - auto scales_attribute = point_cloud->attributes_for_write().lookup_or_add_for_write_only_span("scales", ATTR_DOMAIN_POINT); - - for (int i = 0; i < positions.size(); i++) { - if (i < scales.size()) { - scales_attribute.span[i] = float3(scales[i][0], scales[i][1], scales[i][2]); - } else { - scales_attribute.span[i] = float3(1.0); - } + else { + orientations_attribute.span[i] = math::Quaternion(); } + } - scales_attribute.span.save(); + orientations_attribute.span.save(); - auto orientations_attribute = point_cloud->attributes_for_write().lookup_or_add_for_write_only_span("orientations", ATTR_DOMAIN_POINT); + auto proto_indices_attribute = + point_cloud->attributes_for_write().lookup_or_add_for_write_only_span( + "proto_indices", bke::AttrDomain::Point); - for (int i = 0; i < positions.size(); i++) { - if (i < orientations.size()) { - orientations_attribute.span[i] = math::Quaternion(orientations[i].GetImaginary()[0], orientations[i].GetImaginary()[1], orientations[i].GetImaginary()[2], orientations[i].GetReal()); - } else { - orientations_attribute.span[i] = math::Quaternion(); - } - } + for (int i = 0; i < std::min(positions.size(), proto_indices.size()); i++) { + proto_indices_attribute.span[i] = proto_indices[i]; + } - orientations_attribute.span.save(); + proto_indices_attribute.span.save(); - auto proto_indices_attribute = point_cloud->attributes_for_write().lookup_or_add_for_write_only_span("proto_indices", ATTR_DOMAIN_POINT); + BKE_pointcloud_nomain_to_pointcloud(point_cloud, base_point_cloud); - for (int i = 0; i < std::min(positions.size(), proto_indices.size()); i++) { - proto_indices_attribute.span[i] = proto_indices[i]; - } + ModifierData *md = BKE_modifier_new(eModifierType_Nodes); + BLI_addtail(&object_->modifiers, md); + NodesModifierData &nmd = *reinterpret_cast(md); + nmd.node_group = ntreeAddTree(NULL, "Instances", "GeometryNodeTree"); - proto_indices_attribute.span.save(); + bNodeTree *ntree = nmd.node_group; - BKE_pointcloud_nomain_to_pointcloud(point_cloud, base_point_cloud); + ntree->tree_interface.add_socket("Geometry", + "", + "NodeSocketGeometry", + NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT, + nullptr); + bNode *group_input = nodeAddStaticNode(NULL, ntree, NODE_GROUP_INPUT); + group_input->locx = -400.0f; + bNode *group_output = nodeAddStaticNode(NULL, ntree, NODE_GROUP_OUTPUT); + group_output->locx = 500.0f; + group_output->flag |= NODE_DO_OUTPUT; - /* - ModifierData *md = BKE_modifier_new(eModifierType_Nodes); - BLI_addtail(&object_->modifiers, md); - NodesModifierData &nmd = *reinterpret_cast(md); - nmd.node_group = ntreeAddTree(NULL, "Instances", "GeometryNodeTree"); - IDProperty *nmd_properties = nmd.settings.properties; + bNode *instance_on_points_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_INSTANCE_ON_POINTS); + instance_on_points_node->locx = 300.0f; + bNodeSocket *socket = nodeFindSocket(instance_on_points_node, SOCK_IN, "Pick Instance"); + ((bNodeSocketValueBoolean *)socket->default_value)->value = true; - bNodeTree *ntree = nmd.node_group; + bNode *collection_info_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_COLLECTION_INFO); + collection_info_node->locx = 100.0f; + collection_info_node->locy = -100.0f; + socket = nodeFindSocket(collection_info_node, SOCK_IN, "Separate Children"); + ((bNodeSocketValueBoolean *)socket->default_value)->value = true; - ntree->tree_interface.add_socket("Geometry", - "", - "NodeSocketGeometry", - NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT, - nullptr - ); - bNode *group_input = nodeAddStaticNode(NULL, ntree, NODE_GROUP_INPUT); - group_input->locx = -400.0f; - bNode *group_output = nodeAddStaticNode(NULL, ntree, NODE_GROUP_OUTPUT); - group_output->locx = 500.0f; - group_output->flag |= NODE_DO_OUTPUT; + bNode *indices_attrib_node = add_input_named_attrib_node(ntree, "proto_indices", CD_PROP_INT32); + indices_attrib_node->locx = 100.0f; + indices_attrib_node->locy = -300.0f; - bNode *instance_on_points_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_INSTANCE_ON_POINTS); - instance_on_points_node->locx = 300.0f; - bNode *rotation_to_euler_node = nodeAddStaticNode(NULL, ntree, FN_NODE_ROTATION_TO_EULER); - rotation_to_euler_node->locx = 100.0f; - rotation_to_euler_node->locy = -100.0f; + bNode *rotation_to_euler_node = nodeAddStaticNode(NULL, ntree, FN_NODE_ROTATION_TO_EULER); + rotation_to_euler_node->locx = 100.0f; + rotation_to_euler_node->locy = -500.0f; - bNode *collection_info_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_COLLECTION_INFO); - collection_info_node->locx = 100.0f; - collection_info_node->locy = 100.0f; - bool seperate_children = true; - nodeFindSocket(collection_info_node, SOCK_IN, "Separate Children")->default_value = (void*)&seperate_children; + bNode *rotation_attrib_node = add_input_named_attrib_node( + ntree, "orientations", CD_PROP_QUATERNION); + rotation_attrib_node->locx = -100.0f; + rotation_attrib_node->locy = -500.0f; - BKE_ntree_update_main_tree(bmain, ntree, nullptr); + bNode *scale_attrib_node = add_input_named_attrib_node(ntree, "scales", CD_PROP_FLOAT3); + scale_attrib_node->locx = 100.0f; + scale_attrib_node->locy = -700.0f; - nodeAddLink(ntree, - group_input, - static_cast(group_input->outputs.first), - instance_on_points_node, - nodeFindSocket(instance_on_points_node, SOCK_IN, "Points") - ); + nodeAddLink(ntree, + group_input, + static_cast(group_input->outputs.first), + instance_on_points_node, + nodeFindSocket(instance_on_points_node, SOCK_IN, "Points")); - nodeAddLink(ntree, - rotation_to_euler_node, - nodeFindSocket(rotation_to_euler_node, SOCK_OUT, "Euler"), - instance_on_points_node, - nodeFindSocket(instance_on_points_node, SOCK_IN, "Rotation")); + nodeAddLink(ntree, + indices_attrib_node, + nodeFindSocket(indices_attrib_node, SOCK_OUT, "Attribute"), + instance_on_points_node, + nodeFindSocket(instance_on_points_node, SOCK_IN, "Instance Index")); - nodeAddLink(ntree, - collection_info_node, - nodeFindSocket(collection_info_node, SOCK_OUT, "Instances"), - instance_on_points_node, - nodeFindSocket(instance_on_points_node, SOCK_IN, "Instance")); + nodeAddLink(ntree, + rotation_attrib_node, + nodeFindSocket(rotation_attrib_node, SOCK_OUT, "Attribute"), + rotation_to_euler_node, + nodeFindSocket(rotation_to_euler_node, SOCK_IN, "Rotation")); - nodeAddLink(ntree, - instance_on_points_node, - nodeFindSocket(instance_on_points_node, SOCK_OUT, "Instances"), - group_output, - static_cast(group_output->inputs.first)); + nodeAddLink(ntree, + scale_attrib_node, + nodeFindSocket(scale_attrib_node, SOCK_OUT, "Attribute"), + instance_on_points_node, + nodeFindSocket(instance_on_points_node, SOCK_IN, "Scale")); - BKE_ntree_update_main_tree(bmain, ntree, nullptr); - */ + nodeAddLink(ntree, + rotation_to_euler_node, + nodeFindSocket(rotation_to_euler_node, SOCK_OUT, "Euler"), + instance_on_points_node, + nodeFindSocket(instance_on_points_node, SOCK_IN, "Rotation")); - USDXformReader::read_object_data(bmain, motionSampleTime); + nodeAddLink(ntree, + collection_info_node, + nodeFindSocket(collection_info_node, SOCK_OUT, "Instances"), + instance_on_points_node, + nodeFindSocket(instance_on_points_node, SOCK_IN, "Instance")); + + nodeAddLink(ntree, + instance_on_points_node, + nodeFindSocket(instance_on_points_node, SOCK_OUT, "Instances"), + group_output, + static_cast(group_output->inputs.first)); + + BKE_ntree_update_main_tree(bmain, ntree, nullptr); + + USDXformReader::read_object_data(bmain, motionSampleTime); } -pxr::SdfPathVector USDPointInstancerReader::proto_paths() const { - pxr::SdfPathVector paths; +pxr::SdfPathVector USDPointInstancerReader::proto_paths() const +{ + pxr::SdfPathVector paths; - pxr::UsdGeomPointInstancer point_instancer_prim(prim_); - - if (!point_instancer_prim) { - return paths; - } - - point_instancer_prim.GetPrototypesRel().GetTargets(&paths); + pxr::UsdGeomPointInstancer point_instancer_prim(prim_); + if (!point_instancer_prim) { return paths; + } + + point_instancer_prim.GetPrototypesRel().GetTargets(&paths); + + return paths; } +void USDPointInstancerReader::set_collection(Main *bmain, Collection *coll) +{ + if (!object_) { + return; + } + + BLI_assert(coll); + + ModifierData *mod_data = BKE_modifiers_findby_type(this->object_, eModifierType_Nodes); + if (!mod_data) { + BLI_assert_unreachable(); + return; + } + + NodesModifierData *nmd = reinterpret_cast(mod_data); + if (!nmd) { + BLI_assert_unreachable(); + return; + } + + bNodeTree *ntree = nmd->node_group; + if (!ntree) { + BLI_assert_unreachable(); + return; + } + + bNode *collection_node = nodeFindNodebyName(ntree, "Collection Info"); + if (!collection_node) { + BLI_assert_unreachable(); + return; + } + + bNodeSocket *sock = nodeFindSocket(collection_node, SOCK_IN, "Collection"); + if (!sock) { + BLI_assert_unreachable(); + return; + } + + bNodeSocketValueCollection *socket_data = (bNodeSocketValueCollection *)sock->default_value; + socket_data->value = coll; + id_us_plus(&coll->id); + + BKE_ntree_update_main_tree(bmain, ntree, nullptr); +} } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.h b/source/blender/io/usd/intern/usd_reader_pointinstancer.h index bf974f8b76a..e620f80dafb 100644 --- a/source/blender/io/usd/intern/usd_reader_pointinstancer.h +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.h @@ -20,6 +20,8 @@ #include "usd_reader_xform.h" +struct Collection; + namespace blender::io::usd { /* Wraps the UsdGeomPointInstancer schema. Creates a Blender point cloud object. */ @@ -28,8 +30,8 @@ class USDPointInstancerReader : public USDXformReader { public: USDPointInstancerReader(const pxr::UsdPrim &prim, - const USDImportParams &import_params, - const ImportSettings &settings); + const USDImportParams &import_params, + const ImportSettings &settings); bool valid() const override; @@ -38,6 +40,18 @@ class USDPointInstancerReader : public USDXformReader { void read_object_data(Main *bmain, double motionSampleTime) override; pxr::SdfPathVector proto_paths() const; + + /** + * Set the given collection on the Collection Info + * node referenced by the geometry nodes modifier + * on the object created by the reader. This assumes + * create_object() and read_object_data() have already + * been called. + * + * \param bmain: Pointer to Main + * \param coll: The collection to set + */ + void set_collection(Main *bmain, Collection *coll); }; } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_prim.cc b/source/blender/io/usd/intern/usd_reader_prim.cc index af0cd95bc66..e8ab92fc6c8 100644 --- a/source/blender/io/usd/intern/usd_reader_prim.cc +++ b/source/blender/io/usd/intern/usd_reader_prim.cc @@ -21,7 +21,8 @@ USDPrimReader::USDPrimReader(const pxr::UsdPrim &prim, import_params_(import_params), parent_reader_(nullptr), settings_(&settings), - refcount_(0) + refcount_(0), + is_in_instancer_proto_(false) { } @@ -63,4 +64,9 @@ void USDPrimReader::decref() BLI_assert(refcount_ >= 0); } +bool USDPrimReader::is_in_proto() const +{ + return prim_ && (prim_.IsInPrototype() || is_in_instancer_proto_); +} + } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_prim.h b/source/blender/io/usd/intern/usd_reader_prim.h index 169ed520143..11788efe18e 100644 --- a/source/blender/io/usd/intern/usd_reader_prim.h +++ b/source/blender/io/usd/intern/usd_reader_prim.h @@ -94,6 +94,7 @@ class USDPrimReader { USDPrimReader *parent_reader_; const ImportSettings *settings_; int refcount_; + bool is_in_instancer_proto_; public: USDPrimReader(const pxr::UsdPrim &prim, @@ -147,6 +148,18 @@ class USDPrimReader { { return prim_path_; } + + void set_is_in_instancer_proto(bool flag) + { + is_in_instancer_proto_ = flag; + } + + bool is_in_instancer_proto() const + { + return is_in_instancer_proto_; + } + + bool is_in_proto() const; }; } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc index 0ada58b3e6a..743205a73f6 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.cc +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -10,12 +10,12 @@ #include "usd_reader_material.h" #include "usd_reader_mesh.h" #include "usd_reader_nurbs.h" +#include "usd_reader_pointinstancer.h" #include "usd_reader_prim.h" #include "usd_reader_shape.h" #include "usd_reader_skeleton.h" #include "usd_reader_volume.h" #include "usd_reader_xform.h" -#include "usd_reader_pointinstancer.h" #include #include @@ -27,11 +27,11 @@ #include #include #include +#include #include #include #include #include -#include #if PXR_VERSION >= 2111 # include @@ -56,10 +56,25 @@ #include "WM_api.hh" +#include + static CLG_LogRef LOG = {"io.usd"}; namespace blender::io::usd { +static void decref(USDPrimReader *reader) +{ + if (!reader) { + return; + } + + reader->decref(); + + if (reader->refcount() == 0) { + delete reader; + } +} + /** * Create a collection with the given parent and name. */ @@ -97,6 +112,29 @@ static void set_instance_collection( } } +static void collect_point_instancer_proto_paths(const pxr::UsdPrim &prim, UsdPathSet &r_paths) +{ + /* Note that we use custom filter flags to allow traversing undefined prims, + * because prototype prims may be defined as overs which are skipped by the + * default predicate. */ + pxr::Usd_PrimFlagsConjunction filter_flags = pxr::UsdPrimIsActive && pxr::UsdPrimIsLoaded && + !pxr::UsdPrimIsAbstract; + + pxr::UsdPrimSiblingRange children = prim.GetFilteredChildren(filter_flags); + + for (const auto &child_prim : children) { + if (pxr::UsdGeomPointInstancer instancer = pxr::UsdGeomPointInstancer(child_prim)) { + pxr::SdfPathVector paths; + instancer.GetPrototypesRel().GetTargets(&paths); + for (const pxr::SdfPath &path : paths) { + r_paths.add(path); + } + } + + collect_point_instancer_proto_paths(child_prim, r_paths); + } +} + USDStageReader::USDStageReader(pxr::UsdStageRefPtr stage, const USDImportParams ¶ms, const ImportSettings &settings) @@ -106,7 +144,6 @@ USDStageReader::USDStageReader(pxr::UsdStageRefPtr stage, USDStageReader::~USDStageReader() { - clear_proto_readers(); clear_readers(); } @@ -305,8 +342,9 @@ static bool merge_with_parent(USDPrimReader *reader) return true; } -USDPrimReader *USDStageReader::collect_readers(Main *bmain, - const pxr::UsdPrim &prim, +USDPrimReader *USDStageReader::collect_readers(const pxr::UsdPrim &prim, + const std::optional pruned_prims, + const bool defined_prims_only, blender::Vector &r_readers) { if (prim.IsA()) { @@ -321,18 +359,29 @@ USDPrimReader *USDStageReader::collect_readers(Main *bmain, } } - pxr::Usd_PrimFlagsPredicate filter_predicate = pxr::UsdPrimDefaultPredicate; + pxr::Usd_PrimFlagsConjunction filter_flags = pxr::UsdPrimIsActive && pxr::UsdPrimIsLoaded && + !pxr::UsdPrimIsAbstract; + if (defined_prims_only) { + filter_flags &= pxr::UsdPrimIsDefined; + } + + pxr::Usd_PrimFlagsPredicate filter_predicate(filter_flags); if (!params_.support_scene_instancing) { filter_predicate = pxr::UsdTraverseInstanceProxies(filter_predicate); } - pxr::UsdPrimSiblingRange children = prim.GetFilteredChildren(filter_predicate); - blender::Vector child_readers; - for (const auto &childPrim : children) { - if (USDPrimReader *child_reader = collect_readers(bmain, childPrim, r_readers)) { + pxr::UsdPrimSiblingRange children = prim.GetFilteredChildren(filter_predicate); + + for (const auto &child_prim : children) { + if (pruned_prims && pruned_prims->contains(child_prim.GetPath())) { + continue; + } + if (USDPrimReader *child_reader = collect_readers( + child_prim, pruned_prims, defined_prims_only, r_readers)) + { child_readers.append(child_reader); } } @@ -385,20 +434,25 @@ USDPrimReader *USDStageReader::collect_readers(Main *bmain, return reader; } -void USDStageReader::collect_readers(Main *bmain) +void USDStageReader::collect_readers() { if (!valid()) { return; } clear_readers(); - clear_proto_readers(); + + /* Identify paths to point instancer prototypes, as these will be converted + * in a separate pass over the stage. */ + std::optional instancer_proto_paths = collect_point_instancer_proto_paths(); /* Iterate through the stage. */ pxr::UsdPrim root = stage_->GetPseudoRoot(); stage_->SetInterpolationType(pxr::UsdInterpolationType::UsdInterpolationTypeHeld); - collect_readers(bmain, root, readers_); + + /* Create readers, skipping over prototype prims in this pass. */ + collect_readers(root, instancer_proto_paths, true, readers_); if (params_.support_scene_instancing) { /* Collect the scene-graph instance prototypes. */ @@ -406,7 +460,7 @@ void USDStageReader::collect_readers(Main *bmain) for (const pxr::UsdPrim &proto_prim : protos) { blender::Vector proto_readers; - collect_readers(bmain, proto_prim, proto_readers); + collect_readers(proto_prim, instancer_proto_paths, true, proto_readers); proto_readers_.add(proto_prim.GetPath(), proto_readers); for (USDPrimReader *reader : proto_readers) { @@ -415,6 +469,10 @@ void USDStageReader::collect_readers(Main *bmain) } } } + + if (instancer_proto_paths) { + create_point_instancer_proto_readers(*instancer_proto_paths); + } } void USDStageReader::process_armature_modifiers() const @@ -522,39 +580,23 @@ void USDStageReader::fake_users_for_unused_materials() void USDStageReader::clear_readers() { for (USDPrimReader *reader : readers_) { - if (!reader) { - continue; - } - - reader->decref(); - - if (reader->refcount() == 0) { - delete reader; - } + decref(reader); } - readers_.clear(); -} -void USDStageReader::clear_proto_readers() -{ for (const auto item : proto_readers_.items()) { - for (USDPrimReader *reader : item.value) { - - if (!reader) { - continue; - } - - reader->decref(); - - if (reader->refcount() == 0) { - delete reader; - } + decref(reader); } } - proto_readers_.clear(); + + for (const auto item : instancer_proto_readers_.items()) { + for (USDPrimReader *reader : item.value) { + decref(reader); + } + } + instancer_proto_readers_.clear(); } void USDStageReader::sort_readers() @@ -569,7 +611,7 @@ void USDStageReader::sort_readers() void USDStageReader::create_proto_collections(Main *bmain, Collection *parent_collection) { - if (proto_readers_.is_empty()) { + if (proto_readers_.is_empty() && instancer_proto_readers_.is_empty()) { return; } @@ -620,6 +662,83 @@ void USDStageReader::create_proto_collections(Main *bmain, Collection *parent_co BKE_collection_object_add(bmain, collection, ob); } } + + /* Create collections for the point instancer prototypes. */ + for (USDPrimReader *reader : readers_) { + USDPointInstancerReader *instancer_reader = dynamic_cast(reader); + + if (!instancer_reader) { + continue; + } + + const pxr::SdfPath &instancer_path = reader->prim().GetPath(); + Collection *instancer_protos_coll = create_collection( + bmain, all_protos_collection, instancer_path.GetName().c_str()); + + for (const pxr::SdfPath &proto_path : instancer_reader->proto_paths()) { + Collection *proto_coll = create_collection(bmain, instancer_protos_coll, "proto"); + blender::Vector proto_readers = instancer_proto_readers_.lookup_default( + proto_path, {}); + for (USDPrimReader *reader : proto_readers) { + Object *ob = reader->object(); + if (!ob) { + continue; + } + BKE_collection_object_add(bmain, proto_coll, ob); + } + } + + instancer_reader->set_collection(bmain, instancer_protos_coll); + } +} + +void USDStageReader::create_point_instancer_proto_readers(const UsdPathSet &proto_paths) +{ + if (proto_paths.is_empty()) { + return; + } + + for (const pxr::SdfPath &path : proto_paths) { + + pxr::UsdPrim proto_prim = stage_->GetPrimAtPath(path); + + if (!proto_prim) { + continue; + } + + blender::Vector proto_readers; + + /* Note that point instancer prototypes may be defined as overs, so + * we must call collect readers with argument defined_prims_only = false. */ + collect_readers(proto_prim, proto_paths, false /* include undefined prims */, proto_readers); + + instancer_proto_readers_.add(path, proto_readers); + + for (USDPrimReader *reader : proto_readers) { + reader->set_is_in_instancer_proto(true); + readers_.append(reader); + reader->incref(); + } + } +} + +std::optional USDStageReader::collect_point_instancer_proto_paths() const +{ + if (!stage_) { + return std::nullopt; + } + + UsdPathSet result; + + io::usd::collect_point_instancer_proto_paths(stage_->GetPseudoRoot(), result); + + std::vector protos = stage_->GetPrototypes(); + + for (const pxr::UsdPrim &proto_prim : protos) { + io::usd::collect_point_instancer_proto_paths(proto_prim, result); + } + + return result.is_empty() ? std::nullopt : std::make_optional(result); } } // Namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_stage.h b/source/blender/io/usd/intern/usd_reader_stage.h index 09e2178cc97..2788f5b2f09 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.h +++ b/source/blender/io/usd/intern/usd_reader_stage.h @@ -7,6 +7,8 @@ struct Main; #include "WM_types.hh" +#include "BLI_set.hh" + #include "usd.h" #include "usd_hash_types.h" #include "usd_reader_prim.h" @@ -20,12 +22,16 @@ struct ImportSettings; namespace blender::io::usd { +class USDPointInstancerReader; + /** * Map a USD prototype prim path to the list of readers that convert * the prototype data. */ using ProtoReaderMap = blender::Map>; +using UsdPathSet = blender::Set; + class USDStageReader { protected: @@ -42,6 +48,9 @@ class USDStageReader { /* Readers for scene-graph instance prototypes. */ ProtoReaderMap proto_readers_; + /* Readers for point instancer prototypes. */ + ProtoReaderMap instancer_proto_readers_; + public: USDStageReader(pxr::UsdStageRefPtr stage, const USDImportParams ¶ms, @@ -53,7 +62,7 @@ class USDStageReader { USDPrimReader *create_reader(const pxr::UsdPrim &prim); - void collect_readers(struct Main *bmain); + void collect_readers(); /** * Complete setting up the armature modifiers that @@ -95,10 +104,9 @@ class USDStageReader { return params_.worker_status ? params_.worker_status->reports : nullptr; } + /** Clear all cached reader collections. */ void clear_readers(); - void clear_proto_readers(); - const blender::Vector &readers() const { return readers_; @@ -112,8 +120,26 @@ class USDStageReader { void create_proto_collections(Main *bmain, Collection *parent_collection); private: - USDPrimReader *collect_readers(Main *bmain, - const pxr::UsdPrim &prim, + /** + * Create readers for the subtree rooted at the given prim and append the + * new readers in r_readers. + * + * \param prim: Root of the subtree to convert to readers + * \param pruned_prims: Optional set of paths to prune when iterating + * over the stage during conversion. I.e., these + * prims and their descendents will not be converted + * to readers. + * \param defined_prims_only: If true, only defined prims will be converted, + * skipping abstract and over prims. This should + * be set to false when converting point instancer + * prototype prims, which can be declared as overs. + * \param r_readers: Readers created for the prims in the converted subtree. + * \return: A pointer to the reader created for the given prim or null if + * the prim cannot be converted. + */ + USDPrimReader *collect_readers(const pxr::UsdPrim &prim, + const std::optional pruned_prims, + bool defined_prims_only, blender::Vector &r_readers); /** @@ -139,6 +165,23 @@ class USDStageReader { * procedural shape, such as UsdGeomCube. */ bool is_primitive_prim(const pxr::UsdPrim &prim) const; + + /** + * Iterate over the stage and return the paths of all prototype + * primitives references by point instancers. + * + * \return: The prototype paths, or nullopt if the scene + * does not contain any point instancers. + */ + std::optional collect_point_instancer_proto_paths() const; + + /** + * Populate the instancer_proto_readers_ map for the prototype prims + * in the given set. For each prototype path, this function will + * create readers for the prims in the subtree rooted at the prototype + * prim. + */ + void create_point_instancer_proto_readers(const UsdPathSet &proto_paths); }; }; // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_xform.cc b/source/blender/io/usd/intern/usd_reader_xform.cc index c75f16739a8..068028446a9 100644 --- a/source/blender/io/usd/intern/usd_reader_xform.cc +++ b/source/blender/io/usd/intern/usd_reader_xform.cc @@ -118,7 +118,7 @@ bool USDXformReader::is_root_xform_prim() const return false; } - if (prim_.IsInPrototype()) { + if (is_in_proto()) { /* We don't consider prototypes to be root prims, * because we never want to apply global scaling * or rotations to the prototypes themselves. */ -- 2.30.2 From 0bed40031100b65db80c5380e83a51194dc078a6 Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Mon, 22 Jan 2024 11:08:44 -0500 Subject: [PATCH 06/14] USD: addressed review comments --- .../usd/intern/usd_reader_pointinstancer.cc | 154 +++++++----------- .../io/usd/intern/usd_reader_pointinstancer.h | 21 +-- .../blender/io/usd/intern/usd_reader_stage.cc | 8 +- 3 files changed, 66 insertions(+), 117 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc index 78f5b44a6db..d5c420305f1 100644 --- a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc @@ -1,21 +1,6 @@ -/* - * 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. +/* SPDX-FileCopyrightText: 2023 Blender Authors * - * 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) 2021 NVIDIA Corporation. - * All rights reserved. - */ + * SPDX-License-Identifier: GPL-2.0-or-later */ #include "usd_reader_pointinstancer.h" @@ -35,28 +20,23 @@ #include +namespace blender::io::usd { + /** * Create a node to read a geometry attribute of the given name and type. */ static bNode *add_input_named_attrib_node(bNodeTree *ntree, const char *name, int8_t prop_type) { - bNode *node = nodeAddStaticNode(NULL, ntree, GEO_NODE_INPUT_NAMED_ATTRIBUTE); - BLI_assert(node); - - NodeGeometryInputNamedAttribute *input_attrib_node_data = - reinterpret_cast(node->storage); - input_attrib_node_data->data_type = prop_type; + bNode *node = nodeAddStaticNode(nullptr, ntree, GEO_NODE_INPUT_NAMED_ATTRIBUTE); + auto *storage = reinterpret_cast(node->storage); + storage->data_type = prop_type; bNodeSocket *socket = nodeFindSocket(node, SOCK_IN, "Name"); - BLI_assert(socket); bNodeSocketValueString *str_value = static_cast(socket->default_value); BLI_strncpy(str_value->value, name, MAX_NAME); - return node; } -namespace blender::io::usd { - USDPointInstancerReader::USDPointInstancerReader(const pxr::UsdPrim &prim, const USDImportParams &import_params, const ImportSettings &settings) @@ -107,50 +87,54 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS PointCloud *point_cloud = BKE_pointcloud_new_nomain(positions.size()); - auto positions_span = point_cloud->positions_for_write(); + MutableSpan positions_span = point_cloud->positions_for_write(); - for (int i = 0; i < positions.size(); i++) { + for (int i = 0; i < positions.size(); ++i) { positions_span[i] = float3(positions[i][0], positions[i][1], positions[i][2]); } - auto scales_attribute = - point_cloud->attributes_for_write().lookup_or_add_for_write_only_span( - "scales", bke::AttrDomain::Point); + bke::MutableAttributeAccessor attributes = point_cloud->attributes_for_write(); - for (int i = 0; i < positions.size(); i++) { - if (i < scales.size()) { - scales_attribute.span[i] = float3(scales[i][0], scales[i][1], scales[i][2]); - } - else { - scales_attribute.span[i] = float3(1.0); - } + bke::SpanAttributeWriter scales_attribute = + attributes.lookup_or_add_for_write_only_span("scale", bke::AttrDomain::Point); + + /* Here and below, handle the case where instancing attributes are empty or + * not of the expected size. */ + if (scales.size() < positions.size()) { + scales_attribute.span.fill(float3(1.0f)); + } + + for (const int i : IndexRange(std::min(scales.size(), positions.size()))) { + scales_attribute.span[i] = float3(scales[i][0], scales[i][1], scales[i][2]); } scales_attribute.span.save(); - auto orientations_attribute = - point_cloud->attributes_for_write().lookup_or_add_for_write_only_span( - "orientations", bke::AttrDomain::Point); + bke::SpanAttributeWriter orientations_attribute = + attributes.lookup_or_add_for_write_only_span("orientation", + bke::AttrDomain::Point); - for (int i = 0; i < positions.size(); i++) { - if (i < orientations.size()) { - orientations_attribute.span[i] = math::Quaternion(orientations[i].GetReal(), - orientations[i].GetImaginary()[0], - orientations[i].GetImaginary()[1], - orientations[i].GetImaginary()[2]); - } - else { - orientations_attribute.span[i] = math::Quaternion(); - } + if (orientations.size() < positions.size()) { + orientations_attribute.span.fill(math::Quaternion::identity()); + } + + for (const int i : IndexRange(std::min(orientations.size(), positions.size()))) { + orientations_attribute.span[i] = math::Quaternion(orientations[i].GetReal(), + orientations[i].GetImaginary()[0], + orientations[i].GetImaginary()[1], + orientations[i].GetImaginary()[2]); } orientations_attribute.span.save(); - auto proto_indices_attribute = - point_cloud->attributes_for_write().lookup_or_add_for_write_only_span( - "proto_indices", bke::AttrDomain::Point); + bke::SpanAttributeWriter proto_indices_attribute = + attributes.lookup_or_add_for_write_only_span("proto_index", bke::AttrDomain::Point); - for (int i = 0; i < std::min(positions.size(), proto_indices.size()); i++) { + if (proto_indices.size() < positions.size()) { + proto_indices_attribute.span.fill(0); + } + + for (const int i : IndexRange(std::min(proto_indices.size(), positions.size()))) { proto_indices_attribute.span[i] = proto_indices[i]; } @@ -161,7 +145,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS ModifierData *md = BKE_modifier_new(eModifierType_Nodes); BLI_addtail(&object_->modifiers, md); NodesModifierData &nmd = *reinterpret_cast(md); - nmd.node_group = ntreeAddTree(NULL, "Instances", "GeometryNodeTree"); + nmd.node_group = ntreeAddTree(nullptr, "Instances", "GeometryNodeTree"); bNodeTree *ntree = nmd.node_group; @@ -170,37 +154,33 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT, nullptr); - bNode *group_input = nodeAddStaticNode(NULL, ntree, NODE_GROUP_INPUT); + bNode *group_input = nodeAddStaticNode(nullptr, ntree, NODE_GROUP_INPUT); group_input->locx = -400.0f; - bNode *group_output = nodeAddStaticNode(NULL, ntree, NODE_GROUP_OUTPUT); + bNode *group_output = nodeAddStaticNode(nullptr, ntree, NODE_GROUP_OUTPUT); group_output->locx = 500.0f; group_output->flag |= NODE_DO_OUTPUT; - bNode *instance_on_points_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_INSTANCE_ON_POINTS); + bNode *instance_on_points_node = nodeAddStaticNode(nullptr, ntree, GEO_NODE_INSTANCE_ON_POINTS); instance_on_points_node->locx = 300.0f; bNodeSocket *socket = nodeFindSocket(instance_on_points_node, SOCK_IN, "Pick Instance"); ((bNodeSocketValueBoolean *)socket->default_value)->value = true; - bNode *collection_info_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_COLLECTION_INFO); + bNode *collection_info_node = nodeAddStaticNode(nullptr, ntree, GEO_NODE_COLLECTION_INFO); collection_info_node->locx = 100.0f; collection_info_node->locy = -100.0f; socket = nodeFindSocket(collection_info_node, SOCK_IN, "Separate Children"); ((bNodeSocketValueBoolean *)socket->default_value)->value = true; - bNode *indices_attrib_node = add_input_named_attrib_node(ntree, "proto_indices", CD_PROP_INT32); + bNode *indices_attrib_node = add_input_named_attrib_node(ntree, "proto_index", CD_PROP_INT32); indices_attrib_node->locx = 100.0f; indices_attrib_node->locy = -300.0f; - bNode *rotation_to_euler_node = nodeAddStaticNode(NULL, ntree, FN_NODE_ROTATION_TO_EULER); - rotation_to_euler_node->locx = 100.0f; - rotation_to_euler_node->locy = -500.0f; - bNode *rotation_attrib_node = add_input_named_attrib_node( - ntree, "orientations", CD_PROP_QUATERNION); - rotation_attrib_node->locx = -100.0f; + ntree, "orientation", CD_PROP_QUATERNION); + rotation_attrib_node->locx = 100.0f; rotation_attrib_node->locy = -500.0f; - bNode *scale_attrib_node = add_input_named_attrib_node(ntree, "scales", CD_PROP_FLOAT3); + bNode *scale_attrib_node = add_input_named_attrib_node(ntree, "scale", CD_PROP_FLOAT3); scale_attrib_node->locx = 100.0f; scale_attrib_node->locy = -700.0f; @@ -216,12 +196,6 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS instance_on_points_node, nodeFindSocket(instance_on_points_node, SOCK_IN, "Instance Index")); - nodeAddLink(ntree, - rotation_attrib_node, - nodeFindSocket(rotation_attrib_node, SOCK_OUT, "Attribute"), - rotation_to_euler_node, - nodeFindSocket(rotation_to_euler_node, SOCK_IN, "Rotation")); - nodeAddLink(ntree, scale_attrib_node, nodeFindSocket(scale_attrib_node, SOCK_OUT, "Attribute"), @@ -229,8 +203,8 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS nodeFindSocket(instance_on_points_node, SOCK_IN, "Scale")); nodeAddLink(ntree, - rotation_to_euler_node, - nodeFindSocket(rotation_to_euler_node, SOCK_OUT, "Euler"), + rotation_attrib_node, + nodeFindSocket(rotation_attrib_node, SOCK_OUT, "Attribute"), instance_on_points_node, nodeFindSocket(instance_on_points_node, SOCK_IN, "Rotation")); @@ -266,25 +240,18 @@ pxr::SdfPathVector USDPointInstancerReader::proto_paths() const return paths; } -void USDPointInstancerReader::set_collection(Main *bmain, Collection *coll) +void USDPointInstancerReader::set_collection(Main *bmain, Collection &coll) { - if (!object_) { - return; - } + /* create_object() should have been called already. */ + BLI_assert(object_); - BLI_assert(coll); - - ModifierData *mod_data = BKE_modifiers_findby_type(this->object_, eModifierType_Nodes); - if (!mod_data) { + ModifierData *md = BKE_modifiers_findby_type(this->object_, eModifierType_Nodes); + if (!md) { BLI_assert_unreachable(); return; } - NodesModifierData *nmd = reinterpret_cast(mod_data); - if (!nmd) { - BLI_assert_unreachable(); - return; - } + NodesModifierData *nmd = reinterpret_cast(md); bNodeTree *ntree = nmd->node_group; if (!ntree) { @@ -304,9 +271,10 @@ void USDPointInstancerReader::set_collection(Main *bmain, Collection *coll) return; } - bNodeSocketValueCollection *socket_data = (bNodeSocketValueCollection *)sock->default_value; - socket_data->value = coll; - id_us_plus(&coll->id); + bNodeSocketValueCollection *socket_data = static_cast( + sock->default_value); + socket_data->value = &coll; + id_us_plus(&coll.id); BKE_ntree_update_main_tree(bmain, ntree, nullptr); } diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.h b/source/blender/io/usd/intern/usd_reader_pointinstancer.h index e620f80dafb..c08a34763de 100644 --- a/source/blender/io/usd/intern/usd_reader_pointinstancer.h +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.h @@ -1,21 +1,6 @@ -/* - * 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. +/* SPDX-FileCopyrightText: 2023 Blender Authors * - * 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) 2021 NVIDIA Corporation. - * All rights reserved. - */ + * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "usd_reader_xform.h" @@ -51,7 +36,7 @@ class USDPointInstancerReader : public USDXformReader { * \param bmain: Pointer to Main * \param coll: The collection to set */ - void set_collection(Main *bmain, Collection *coll); + void set_collection(Main *bmain, Collection &coll); }; } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc index 743205a73f6..3b5190b3649 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.cc +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -54,10 +54,6 @@ #include "DNA_collection_types.h" #include "DNA_material_types.h" -#include "WM_api.hh" - -#include - static CLG_LogRef LOG = {"io.usd"}; namespace blender::io::usd { @@ -688,7 +684,7 @@ void USDStageReader::create_proto_collections(Main *bmain, Collection *parent_co } } - instancer_reader->set_collection(bmain, instancer_protos_coll); + instancer_reader->set_collection(bmain, *instancer_protos_coll); } } @@ -706,7 +702,7 @@ void USDStageReader::create_point_instancer_proto_readers(const UsdPathSet &prot continue; } - blender::Vector proto_readers; + Vector proto_readers; /* Note that point instancer prototypes may be defined as overs, so * we must call collect readers with argument defined_prims_only = false. */ -- 2.30.2 From 3fadb7495ed9b8944f603c71dc73e61d6926e7f9 Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Tue, 23 Jan 2024 13:13:38 -0500 Subject: [PATCH 07/14] USD import: support point instancer masking Now reading the point instancer boolean mask values into a "mask" point attribute which is set as the Selection input of the Instance on Points node. --- .../usd/intern/usd_reader_pointinstancer.cc | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc index d5c420305f1..e8d8792ee1f 100644 --- a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc @@ -85,6 +85,8 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS point_instancer_prim.GetOrientationsAttr().Get(&orientations, sample_time); point_instancer_prim.GetProtoIndicesAttr().Get(&proto_indices, sample_time); + std::vector mask = point_instancer_prim.ComputeMaskAtTime(sample_time); + PointCloud *point_cloud = BKE_pointcloud_new_nomain(positions.size()); MutableSpan positions_span = point_cloud->positions_for_write(); @@ -140,6 +142,19 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS proto_indices_attribute.span.save(); + bke::SpanAttributeWriter mask_attribute = + attributes.lookup_or_add_for_write_only_span("mask", bke::AttrDomain::Point); + + if (mask.size() < positions.size()) { + mask_attribute.span.fill(true); + } + + for (const int i : IndexRange(std::min(mask.size(), positions.size()))) { + mask_attribute.span[i] = mask[i]; + } + + mask_attribute.span.save(); + BKE_pointcloud_nomain_to_pointcloud(point_cloud, base_point_cloud); ModifierData *md = BKE_modifier_new(eModifierType_Nodes); @@ -165,24 +180,28 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS bNodeSocket *socket = nodeFindSocket(instance_on_points_node, SOCK_IN, "Pick Instance"); ((bNodeSocketValueBoolean *)socket->default_value)->value = true; + bNode *mask_attrib_node = add_input_named_attrib_node(ntree, "mask", CD_PROP_BOOL); + mask_attrib_node->locx = 100.0f; + mask_attrib_node->locy = -100.0f; + bNode *collection_info_node = nodeAddStaticNode(nullptr, ntree, GEO_NODE_COLLECTION_INFO); collection_info_node->locx = 100.0f; - collection_info_node->locy = -100.0f; + collection_info_node->locy = -300.0f; socket = nodeFindSocket(collection_info_node, SOCK_IN, "Separate Children"); ((bNodeSocketValueBoolean *)socket->default_value)->value = true; bNode *indices_attrib_node = add_input_named_attrib_node(ntree, "proto_index", CD_PROP_INT32); indices_attrib_node->locx = 100.0f; - indices_attrib_node->locy = -300.0f; + indices_attrib_node->locy = -500.0f; bNode *rotation_attrib_node = add_input_named_attrib_node( ntree, "orientation", CD_PROP_QUATERNION); rotation_attrib_node->locx = 100.0f; - rotation_attrib_node->locy = -500.0f; + rotation_attrib_node->locy = -700.0f; bNode *scale_attrib_node = add_input_named_attrib_node(ntree, "scale", CD_PROP_FLOAT3); scale_attrib_node->locx = 100.0f; - scale_attrib_node->locy = -700.0f; + scale_attrib_node->locy = -900.0f; nodeAddLink(ntree, group_input, @@ -190,6 +209,12 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS instance_on_points_node, nodeFindSocket(instance_on_points_node, SOCK_IN, "Points")); + nodeAddLink(ntree, + mask_attrib_node, + nodeFindSocket(mask_attrib_node, SOCK_OUT, "Attribute"), + instance_on_points_node, + nodeFindSocket(instance_on_points_node, SOCK_IN, "Selection")); + nodeAddLink(ntree, indices_attrib_node, nodeFindSocket(indices_attrib_node, SOCK_OUT, "Attribute"), -- 2.30.2 From 5fb8357fc66bcc4c7e716085f6a6e9a5a091ee4f Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Tue, 23 Jan 2024 13:28:05 -0500 Subject: [PATCH 08/14] USD: replaced static cast per review comment --- source/blender/io/usd/intern/usd_reader_pointinstancer.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc index e8d8792ee1f..1d117e5c74a 100644 --- a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc @@ -178,7 +178,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS bNode *instance_on_points_node = nodeAddStaticNode(nullptr, ntree, GEO_NODE_INSTANCE_ON_POINTS); instance_on_points_node->locx = 300.0f; bNodeSocket *socket = nodeFindSocket(instance_on_points_node, SOCK_IN, "Pick Instance"); - ((bNodeSocketValueBoolean *)socket->default_value)->value = true; + socket->default_value_typed()->value = true; bNode *mask_attrib_node = add_input_named_attrib_node(ntree, "mask", CD_PROP_BOOL); mask_attrib_node->locx = 100.0f; @@ -188,7 +188,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS collection_info_node->locx = 100.0f; collection_info_node->locy = -300.0f; socket = nodeFindSocket(collection_info_node, SOCK_IN, "Separate Children"); - ((bNodeSocketValueBoolean *)socket->default_value)->value = true; + socket->default_value_typed()->value = true; bNode *indices_attrib_node = add_input_named_attrib_node(ntree, "proto_index", CD_PROP_INT32); indices_attrib_node->locx = 100.0f; -- 2.30.2 From 95b015984e7c84a47d01297e957d549967deb155 Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Tue, 23 Jan 2024 18:19:26 -0500 Subject: [PATCH 09/14] USD: bug fixes Now calling SpanAttributeWriter::finish(). Fixed issue with extra user count on proto collection. --- .../io/usd/intern/usd_reader_pointinstancer.cc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc index 1d117e5c74a..6ffd3ff6186 100644 --- a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc @@ -110,7 +110,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS scales_attribute.span[i] = float3(scales[i][0], scales[i][1], scales[i][2]); } - scales_attribute.span.save(); + scales_attribute.finish(); bke::SpanAttributeWriter orientations_attribute = attributes.lookup_or_add_for_write_only_span("orientation", @@ -127,7 +127,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS orientations[i].GetImaginary()[2]); } - orientations_attribute.span.save(); + orientations_attribute.finish(); bke::SpanAttributeWriter proto_indices_attribute = attributes.lookup_or_add_for_write_only_span("proto_index", bke::AttrDomain::Point); @@ -140,7 +140,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS proto_indices_attribute.span[i] = proto_indices[i]; } - proto_indices_attribute.span.save(); + proto_indices_attribute.finish(); bke::SpanAttributeWriter mask_attribute = attributes.lookup_or_add_for_write_only_span("mask", bke::AttrDomain::Point); @@ -153,7 +153,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS mask_attribute.span[i] = mask[i]; } - mask_attribute.span.save(); + mask_attribute.finish(); BKE_pointcloud_nomain_to_pointcloud(point_cloud, base_point_cloud); @@ -298,10 +298,11 @@ void USDPointInstancerReader::set_collection(Main *bmain, Collection &coll) bNodeSocketValueCollection *socket_data = static_cast( sock->default_value); - socket_data->value = &coll; - id_us_plus(&coll.id); - BKE_ntree_update_main_tree(bmain, ntree, nullptr); + if (socket_data->value != &coll) { + socket_data->value = &coll; + BKE_ntree_update_main_tree(bmain, ntree, nullptr); + } } } // namespace blender::io::usd -- 2.30.2 From 3dd14a43a31859763ebea503ce2da563f77aac92 Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Tue, 23 Jan 2024 21:14:19 -0500 Subject: [PATCH 10/14] USD: fix point instancer node treee mem leak. --- source/blender/io/usd/intern/usd_reader_pointinstancer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc index 6ffd3ff6186..b7a1faddd86 100644 --- a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc @@ -160,7 +160,7 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS ModifierData *md = BKE_modifier_new(eModifierType_Nodes); BLI_addtail(&object_->modifiers, md); NodesModifierData &nmd = *reinterpret_cast(md); - nmd.node_group = ntreeAddTree(nullptr, "Instances", "GeometryNodeTree"); + nmd.node_group = ntreeAddTree(bmain, "Instances", "GeometryNodeTree"); bNodeTree *ntree = nmd.node_group; -- 2.30.2 From 1009a198100ddacc0a5277bd9d03aa113c760cee Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Mon, 29 Jan 2024 15:20:49 -0500 Subject: [PATCH 11/14] USD: addressed review comments --- .../usd/intern/usd_reader_pointinstancer.cc | 2 ++ .../blender/io/usd/intern/usd_reader_stage.cc | 22 +++++++++---------- .../blender/io/usd/intern/usd_reader_stage.h | 10 ++++----- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc index b7a1faddd86..297f028696f 100644 --- a/source/blender/io/usd/intern/usd_reader_pointinstancer.cc +++ b/source/blender/io/usd/intern/usd_reader_pointinstancer.cc @@ -247,6 +247,8 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS BKE_ntree_update_main_tree(bmain, ntree, nullptr); + BKE_object_modifier_set_active(object_, md); + USDXformReader::read_object_data(bmain, motionSampleTime); } diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc index 3b5190b3649..a5430a4b87a 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.cc +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -339,7 +339,7 @@ static bool merge_with_parent(USDPrimReader *reader) } USDPrimReader *USDStageReader::collect_readers(const pxr::UsdPrim &prim, - const std::optional pruned_prims, + const UsdPathSet &pruned_prims, const bool defined_prims_only, blender::Vector &r_readers) { @@ -372,7 +372,7 @@ USDPrimReader *USDStageReader::collect_readers(const pxr::UsdPrim &prim, pxr::UsdPrimSiblingRange children = prim.GetFilteredChildren(filter_predicate); for (const auto &child_prim : children) { - if (pruned_prims && pruned_prims->contains(child_prim.GetPath())) { + if (pruned_prims.contains(child_prim.GetPath())) { continue; } if (USDPrimReader *child_reader = collect_readers( @@ -440,7 +440,7 @@ void USDStageReader::collect_readers() /* Identify paths to point instancer prototypes, as these will be converted * in a separate pass over the stage. */ - std::optional instancer_proto_paths = collect_point_instancer_proto_paths(); + UsdPathSet instancer_proto_paths = collect_point_instancer_proto_paths(); /* Iterate through the stage. */ pxr::UsdPrim root = stage_->GetPseudoRoot(); @@ -466,8 +466,8 @@ void USDStageReader::collect_readers() } } - if (instancer_proto_paths) { - create_point_instancer_proto_readers(*instancer_proto_paths); + if (!instancer_proto_paths.is_empty()) { + create_point_instancer_proto_readers(instancer_proto_paths); } } @@ -718,14 +718,14 @@ void USDStageReader::create_point_instancer_proto_readers(const UsdPathSet &prot } } -std::optional USDStageReader::collect_point_instancer_proto_paths() const +UsdPathSet USDStageReader::collect_point_instancer_proto_paths() const { - if (!stage_) { - return std::nullopt; - } - UsdPathSet result; + if (!stage_) { + return result; + } + io::usd::collect_point_instancer_proto_paths(stage_->GetPseudoRoot(), result); std::vector protos = stage_->GetPrototypes(); @@ -734,7 +734,7 @@ std::optional USDStageReader::collect_point_instancer_proto_paths() io::usd::collect_point_instancer_proto_paths(proto_prim, result); } - return result.is_empty() ? std::nullopt : std::make_optional(result); + return result; } } // Namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_stage.h b/source/blender/io/usd/intern/usd_reader_stage.h index 2788f5b2f09..ac3c3406069 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.h +++ b/source/blender/io/usd/intern/usd_reader_stage.h @@ -125,8 +125,8 @@ class USDStageReader { * new readers in r_readers. * * \param prim: Root of the subtree to convert to readers - * \param pruned_prims: Optional set of paths to prune when iterating - * over the stage during conversion. I.e., these + * \param pruned_prims: Set of paths to prune when iterating over + * the stage during conversion. I.e., these * prims and their descendents will not be converted * to readers. * \param defined_prims_only: If true, only defined prims will be converted, @@ -138,7 +138,7 @@ class USDStageReader { * the prim cannot be converted. */ USDPrimReader *collect_readers(const pxr::UsdPrim &prim, - const std::optional pruned_prims, + const UsdPathSet &pruned_prims, bool defined_prims_only, blender::Vector &r_readers); @@ -170,10 +170,10 @@ class USDStageReader { * Iterate over the stage and return the paths of all prototype * primitives references by point instancers. * - * \return: The prototype paths, or nullopt if the scene + * \return: The prototype paths, or an empty path set if the scene * does not contain any point instancers. */ - std::optional collect_point_instancer_proto_paths() const; + UsdPathSet collect_point_instancer_proto_paths() const; /** * Populate the instancer_proto_readers_ map for the prototype prims -- 2.30.2 From 11b2e2bc6b8f6674630584ed87a5945786d70654 Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Tue, 30 Jan 2024 11:23:21 -0500 Subject: [PATCH 12/14] USD: make pruned_prims argument a pointer Changed USDStageReader::collect_readers pruned_prims argument from a reference to a pointer. --- source/blender/io/usd/intern/usd_reader_stage.cc | 10 +++++----- source/blender/io/usd/intern/usd_reader_stage.h | 11 ++++++----- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc index a5430a4b87a..3d2413f1b66 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.cc +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -339,7 +339,7 @@ static bool merge_with_parent(USDPrimReader *reader) } USDPrimReader *USDStageReader::collect_readers(const pxr::UsdPrim &prim, - const UsdPathSet &pruned_prims, + const UsdPathSet *pruned_prims, const bool defined_prims_only, blender::Vector &r_readers) { @@ -372,7 +372,7 @@ USDPrimReader *USDStageReader::collect_readers(const pxr::UsdPrim &prim, pxr::UsdPrimSiblingRange children = prim.GetFilteredChildren(filter_predicate); for (const auto &child_prim : children) { - if (pruned_prims.contains(child_prim.GetPath())) { + if (pruned_prims && pruned_prims->contains(child_prim.GetPath())) { continue; } if (USDPrimReader *child_reader = collect_readers( @@ -448,7 +448,7 @@ void USDStageReader::collect_readers() stage_->SetInterpolationType(pxr::UsdInterpolationType::UsdInterpolationTypeHeld); /* Create readers, skipping over prototype prims in this pass. */ - collect_readers(root, instancer_proto_paths, true, readers_); + collect_readers(root, &instancer_proto_paths, true, readers_); if (params_.support_scene_instancing) { /* Collect the scene-graph instance prototypes. */ @@ -456,7 +456,7 @@ void USDStageReader::collect_readers() for (const pxr::UsdPrim &proto_prim : protos) { blender::Vector proto_readers; - collect_readers(proto_prim, instancer_proto_paths, true, proto_readers); + collect_readers(proto_prim, &instancer_proto_paths, true, proto_readers); proto_readers_.add(proto_prim.GetPath(), proto_readers); for (USDPrimReader *reader : proto_readers) { @@ -706,7 +706,7 @@ void USDStageReader::create_point_instancer_proto_readers(const UsdPathSet &prot /* Note that point instancer prototypes may be defined as overs, so * we must call collect readers with argument defined_prims_only = false. */ - collect_readers(proto_prim, proto_paths, false /* include undefined prims */, proto_readers); + collect_readers(proto_prim, &proto_paths, false /* include undefined prims */, proto_readers); instancer_proto_readers_.add(path, proto_readers); diff --git a/source/blender/io/usd/intern/usd_reader_stage.h b/source/blender/io/usd/intern/usd_reader_stage.h index ac3c3406069..23e88acf7da 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.h +++ b/source/blender/io/usd/intern/usd_reader_stage.h @@ -125,10 +125,11 @@ class USDStageReader { * new readers in r_readers. * * \param prim: Root of the subtree to convert to readers - * \param pruned_prims: Set of paths to prune when iterating over - * the stage during conversion. I.e., these - * prims and their descendents will not be converted - * to readers. + * \param pruned_prims: Optional pointer to set of paths to prune when + * iterating over the stage during conversion. I.e., + * these prims and their descendents will not be + * converted to readers. May be null if no pruning + * is desired. * \param defined_prims_only: If true, only defined prims will be converted, * skipping abstract and over prims. This should * be set to false when converting point instancer @@ -138,7 +139,7 @@ class USDStageReader { * the prim cannot be converted. */ USDPrimReader *collect_readers(const pxr::UsdPrim &prim, - const UsdPathSet &pruned_prims, + const UsdPathSet *pruned_prims, bool defined_prims_only, blender::Vector &r_readers); -- 2.30.2 From 36dd5ce5b34e239374ec0d2a1b2b171cf3d53571 Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Tue, 30 Jan 2024 18:21:08 -0500 Subject: [PATCH 13/14] USD: improve protoype collection names Now adding the prototype index to the point instancer prototype collection names. The index will help identify the collections and will also ensure that the collection names maintain the correct alphabetical order, so that the original prototype index remains valid. (The Collection Info geometry node orders collections alphabetically for instancing, so we can't use an arbitrary collection name.) --- .../blender/io/usd/intern/usd_reader_stage.cc | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc index 3d2413f1b66..18e8813932b 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.cc +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -41,6 +41,7 @@ #endif #include "BLI_map.hh" +#include "BLI_math_base.h" #include "BLI_sort.hh" #include "BLI_string.h" @@ -54,6 +55,8 @@ #include "DNA_collection_types.h" #include "DNA_material_types.h" +#include + static CLG_LogRef LOG = {"io.usd"}; namespace blender::io::usd { @@ -660,6 +663,20 @@ void USDStageReader::create_proto_collections(Main *bmain, Collection *parent_co } /* Create collections for the point instancer prototypes. */ + + /* For every point instancer reader, create a "prototypes" collection and set it + * on the Collection Info node referenced by the geometry nodes modifier created by + * the reader. We also create collections containing prototype geometry as children + * of the "prototypes" collection. These child collections will be indexed for + * instancing by the Instance on Points geometry node. + * + * Note that the prototype collections will be ordered alphabetically by the Collection + * Info node. We must therefore take care to generate collection names that will maintain + * the original prototype order, so that the prototype indices will remain valid. We use + * the naming convention proto_, where the index suffix may be zero padded (e.g., + * "proto_00", "proto_01", "proto_02", etc.). + */ + for (USDPrimReader *reader : readers_) { USDPointInstancerReader *instancer_reader = dynamic_cast(reader); @@ -671,8 +688,22 @@ void USDStageReader::create_proto_collections(Main *bmain, Collection *parent_co Collection *instancer_protos_coll = create_collection( bmain, all_protos_collection, instancer_path.GetName().c_str()); + /* Determine the max number of digits we will need for the possibly zero-padded + * string representing the prototype index. */ + int max_index_digits = integer_digits_i(instancer_reader->proto_paths().size()); + + int proto_index = 0; + for (const pxr::SdfPath &proto_path : instancer_reader->proto_paths()) { - Collection *proto_coll = create_collection(bmain, instancer_protos_coll, "proto"); + BLI_assert(max_index_digits > 0); + + /* Format the collection name to follow the proto_ pattern. */ + std::ostringstream ss; + ss << std::setw(max_index_digits) << std::setfill('0') << proto_index; + std::string coll_name = "proto_" + ss.str(); + + /* Create the collection and populate it with the prototype objects. */ + Collection *proto_coll = create_collection(bmain, instancer_protos_coll, coll_name.c_str()); blender::Vector proto_readers = instancer_proto_readers_.lookup_default( proto_path, {}); for (USDPrimReader *reader : proto_readers) { @@ -682,6 +713,7 @@ void USDStageReader::create_proto_collections(Main *bmain, Collection *parent_co } BKE_collection_object_add(bmain, proto_coll, ob); } + ++proto_index; } instancer_reader->set_collection(bmain, *instancer_protos_coll); -- 2.30.2 From 6b8a77e7a1581ba5b6b862f470fafd2f0d324e6a Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Wed, 31 Jan 2024 16:54:32 -0500 Subject: [PATCH 14/14] USD: pruned_pims argument now a reference --- source/blender/io/usd/intern/usd_reader_stage.cc | 10 +++++----- source/blender/io/usd/intern/usd_reader_stage.h | 11 +++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc index 18e8813932b..9d595c72c46 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.cc +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -342,7 +342,7 @@ static bool merge_with_parent(USDPrimReader *reader) } USDPrimReader *USDStageReader::collect_readers(const pxr::UsdPrim &prim, - const UsdPathSet *pruned_prims, + const UsdPathSet &pruned_prims, const bool defined_prims_only, blender::Vector &r_readers) { @@ -375,7 +375,7 @@ USDPrimReader *USDStageReader::collect_readers(const pxr::UsdPrim &prim, pxr::UsdPrimSiblingRange children = prim.GetFilteredChildren(filter_predicate); for (const auto &child_prim : children) { - if (pruned_prims && pruned_prims->contains(child_prim.GetPath())) { + if (pruned_prims.contains(child_prim.GetPath())) { continue; } if (USDPrimReader *child_reader = collect_readers( @@ -451,7 +451,7 @@ void USDStageReader::collect_readers() stage_->SetInterpolationType(pxr::UsdInterpolationType::UsdInterpolationTypeHeld); /* Create readers, skipping over prototype prims in this pass. */ - collect_readers(root, &instancer_proto_paths, true, readers_); + collect_readers(root, instancer_proto_paths, true, readers_); if (params_.support_scene_instancing) { /* Collect the scene-graph instance prototypes. */ @@ -459,7 +459,7 @@ void USDStageReader::collect_readers() for (const pxr::UsdPrim &proto_prim : protos) { blender::Vector proto_readers; - collect_readers(proto_prim, &instancer_proto_paths, true, proto_readers); + collect_readers(proto_prim, instancer_proto_paths, true, proto_readers); proto_readers_.add(proto_prim.GetPath(), proto_readers); for (USDPrimReader *reader : proto_readers) { @@ -738,7 +738,7 @@ void USDStageReader::create_point_instancer_proto_readers(const UsdPathSet &prot /* Note that point instancer prototypes may be defined as overs, so * we must call collect readers with argument defined_prims_only = false. */ - collect_readers(proto_prim, &proto_paths, false /* include undefined prims */, proto_readers); + collect_readers(proto_prim, proto_paths, false /* include undefined prims */, proto_readers); instancer_proto_readers_.add(path, proto_readers); diff --git a/source/blender/io/usd/intern/usd_reader_stage.h b/source/blender/io/usd/intern/usd_reader_stage.h index 23e88acf7da..dc71c0c45e2 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.h +++ b/source/blender/io/usd/intern/usd_reader_stage.h @@ -125,11 +125,10 @@ class USDStageReader { * new readers in r_readers. * * \param prim: Root of the subtree to convert to readers - * \param pruned_prims: Optional pointer to set of paths to prune when - * iterating over the stage during conversion. I.e., - * these prims and their descendents will not be - * converted to readers. May be null if no pruning - * is desired. + * \param pruned_prims: Set of paths to prune when iterating over the + * stage during conversion. I.e., these prims + * and their descendents will not be converted to + * readers. * \param defined_prims_only: If true, only defined prims will be converted, * skipping abstract and over prims. This should * be set to false when converting point instancer @@ -139,7 +138,7 @@ class USDStageReader { * the prim cannot be converted. */ USDPrimReader *collect_readers(const pxr::UsdPrim &prim, - const UsdPathSet *pruned_prims, + const UsdPathSet &pruned_prims, bool defined_prims_only, blender::Vector &r_readers); -- 2.30.2