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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2021-01-21 12:32:42 +0300
committerJacques Lucke <jacques@blender.org>2021-01-21 12:32:42 +0300
commit793547e7d1401ad545e0450b80c7bafd521c60e1 (patch)
tree74ebdf40005711eb545fd1889bcf8226dd5bcafd
parent985bc08688b7fb58a910171c63a1417580cffece (diff)
Geometry Nodes: initial support for volumes
For the most part, this just adds boilerplate code for volume support in geometry nodes: * Add `VolumeComponent` next to `MeshComponent`, etc. * Support `VolumeComponent` in depsgraph object iterator. Furthermore, I added initial volume support in a few nodes: * The Object Info node outputs an object instance when the input is a volume object (that will be the same for mesh objects soonish, to avoid copies). * Support transforming a `VolumeComponent` in the Transform node. * Support the `VolumeComponent` in Join Geometry nodes, but only when just one of the inputs has a volume component for now. Right now, there is no way to create a `VolumeComponent`, because the Object Info node outputs an object instance. The `VolumeComponent` will be necessary for upcoming nodes, which will generate volumes on the fly. Viewport selection does not work correctly with `VolumeComponent`s currently. I don't know why that is. That can be figured out a bit later, once we can actually create new volumes in geometry nodes. Ref T84604. Differential Revision: https://developer.blender.org/D10147
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh27
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc103
-rw-r--r--source/blender/blenkernel/intern/volume.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query_iter.cc24
-rw-r--r--source/blender/nodes/CMakeLists.txt7
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc50
8 files changed, 233 insertions, 0 deletions
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index ad5a5d57045..adc08f699fd 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -36,6 +36,7 @@ struct Collection;
struct Mesh;
struct Object;
struct PointCloud;
+struct Volume;
/* Each geometry component has a specific type. The type determines what kind of data the component
* stores. Functions modifying a geometry will usually just modify a subset of the component types.
@@ -44,6 +45,7 @@ enum class GeometryComponentType {
Mesh = 0,
PointCloud = 1,
Instances = 2,
+ Volume = 3,
};
enum class GeometryOwnershipType {
@@ -319,10 +321,13 @@ struct GeometrySet {
bool has_mesh() const;
bool has_pointcloud() const;
bool has_instances() const;
+ bool has_volume() const;
const Mesh *get_mesh_for_read() const;
const PointCloud *get_pointcloud_for_read() const;
+ const Volume *get_volume_for_read() const;
Mesh *get_mesh_for_write();
PointCloud *get_pointcloud_for_write();
+ Volume *get_volume_for_write();
/* Utility methods for replacement. */
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
@@ -464,3 +469,25 @@ class InstancesComponent : public GeometryComponent {
static constexpr inline GeometryComponentType static_type = GeometryComponentType::Instances;
};
+
+/** A geometry component that stores volume grids. */
+class VolumeComponent : public GeometryComponent {
+ private:
+ Volume *volume_ = nullptr;
+ GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
+
+ public:
+ VolumeComponent();
+ ~VolumeComponent();
+ GeometryComponent *copy() const override;
+
+ void clear();
+ bool has_volume() const;
+ void replace(Volume *volume, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ Volume *release();
+
+ const Volume *get_for_read() const;
+ Volume *get_for_write();
+
+ static constexpr inline GeometryComponentType static_type = GeometryComponentType::Volume;
+};
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index 81958b81213..7a3239babd4 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -19,6 +19,7 @@
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_pointcloud.h"
+#include "BKE_volume.h"
#include "DNA_object_types.h"
@@ -51,6 +52,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
return new PointCloudComponent();
case GeometryComponentType::Instances:
return new InstancesComponent();
+ case GeometryComponentType::Volume:
+ return new VolumeComponent();
}
BLI_assert(false);
return nullptr;
@@ -201,6 +204,13 @@ const PointCloud *GeometrySet::get_pointcloud_for_read() const
return (component == nullptr) ? nullptr : component->get_for_read();
}
+/* Returns a read-only volume or null. */
+const Volume *GeometrySet::get_volume_for_read() const
+{
+ const VolumeComponent *component = this->get_component_for_read<VolumeComponent>();
+ return (component == nullptr) ? nullptr : component->get_for_read();
+}
+
/* Returns true when the geometry set has a point cloud component that has a point cloud. */
bool GeometrySet::has_pointcloud() const
{
@@ -215,6 +225,13 @@ bool GeometrySet::has_instances() const
return component != nullptr && component->instances_amount() >= 1;
}
+/* Returns true when the geometry set has a volume component that has a volume. */
+bool GeometrySet::has_volume() const
+{
+ const VolumeComponent *component = this->get_component_for_read<VolumeComponent>();
+ return component != nullptr && component->has_volume();
+}
+
/* Create a new geometry set that only contains the given mesh. */
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -262,6 +279,13 @@ PointCloud *GeometrySet::get_pointcloud_for_write()
return component.get_for_write();
}
+/* Returns a mutable volume or null. No ownership is transferred. */
+Volume *GeometrySet::get_volume_for_write()
+{
+ VolumeComponent &component = this->get_component_for_write<VolumeComponent>();
+ return component.get_for_write();
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -567,6 +591,85 @@ bool InstancesComponent::is_empty() const
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Volume Component
+ * \{ */
+
+VolumeComponent::VolumeComponent() : GeometryComponent(GeometryComponentType::Volume)
+{
+}
+
+VolumeComponent::~VolumeComponent()
+{
+ this->clear();
+}
+
+GeometryComponent *VolumeComponent::copy() const
+{
+ VolumeComponent *new_component = new VolumeComponent();
+ if (volume_ != nullptr) {
+ new_component->volume_ = BKE_volume_copy_for_eval(volume_, false);
+ new_component->ownership_ = GeometryOwnershipType::Owned;
+ }
+ return new_component;
+}
+
+void VolumeComponent::clear()
+{
+ BLI_assert(this->is_mutable());
+ if (volume_ != nullptr) {
+ if (ownership_ == GeometryOwnershipType::Owned) {
+ BKE_id_free(nullptr, volume_);
+ }
+ volume_ = nullptr;
+ }
+}
+
+bool VolumeComponent::has_volume() const
+{
+ return volume_ != nullptr;
+}
+
+/* Clear the component and replace it with the new volume. */
+void VolumeComponent::replace(Volume *volume, GeometryOwnershipType ownership)
+{
+ BLI_assert(this->is_mutable());
+ this->clear();
+ volume_ = volume;
+ ownership_ = ownership;
+}
+
+/* Return the volume and clear the component. The caller takes over responsibility for freeing the
+ * volume (if the component was responsible before). */
+Volume *VolumeComponent::release()
+{
+ BLI_assert(this->is_mutable());
+ Volume *volume = volume_;
+ volume_ = nullptr;
+ return volume;
+}
+
+/* Get the volume from this component. This method can be used by multiple threads at the same
+ * time. Therefore, the returned volume should not be modified. No ownership is transferred. */
+const Volume *VolumeComponent::get_for_read() const
+{
+ return volume_;
+}
+
+/* Get the volume from this component. This method can only be used when the component is mutable,
+ * i.e. it is not shared. The returned volume can be modified. No ownership is transferred. */
+Volume *VolumeComponent::get_for_write()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ == GeometryOwnershipType::ReadOnly) {
+ volume_ = BKE_volume_copy_for_eval(volume_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+ return volume_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name C API
* \{ */
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 9e7a3736141..0c64295b55b 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -523,6 +523,8 @@ static void volume_copy_data(Main *UNUSED(bmain),
volume_dst->runtime.grids = OBJECT_GUARDED_NEW(VolumeGridVector, grids_src);
}
#endif
+
+ volume_dst->batch_cache = nullptr;
}
static void volume_free_data(ID *id)
diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
index e472d82f2ee..c9780b9b129 100644
--- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
@@ -193,6 +193,30 @@ bool deg_iterator_components_step(BLI_Iterator *iter)
}
}
+ /* The volume component. */
+ if (data->geometry_component_id == 2) {
+ data->geometry_component_id++;
+
+ /* Don't use a temporary object for this component, when the owner is a volume object. */
+ if (data->geometry_component_owner->type == OB_VOLUME) {
+ iter->current = data->geometry_component_owner;
+ return true;
+ }
+
+ const VolumeComponent *component = geometry_set->get_component_for_read<VolumeComponent>();
+ if (component != nullptr) {
+ const Volume *volume = component->get_for_read();
+
+ Object *temp_object = &data->temp_geometry_component_object;
+ *temp_object = *data->geometry_component_owner;
+ temp_object->type = OB_VOLUME;
+ temp_object->data = (void *)volume;
+ temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id;
+ iter->current = temp_object;
+ return true;
+ }
+ }
+
data->geometry_component_owner = nullptr;
return false;
}
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index ed4d658eb4f..9057e4c6be8 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -366,4 +366,11 @@ if(WITH_OPENSUBDIV)
add_definitions(-DWITH_OPENSUBDIV)
endif()
+if(WITH_OPENVDB)
+ list(APPEND INC_SYS
+ ${OPENVDB_INCLUDE_DIRS}
+ )
+ add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS})
+endif()
+
blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index 75736ae714a..b9c9e5bd02a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -228,6 +228,14 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo
}
}
+static void join_components(Span<const VolumeComponent *> src_components, GeometrySet &result)
+{
+ /* Not yet supported. Joining volume grids with the same name requires resampling of at least one
+ * of the grids. The cell size of the resulting volume has to be determined somehow. */
+ VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>();
+ UNUSED_VARS(src_components, dst_component);
+}
+
template<typename Component>
static void join_component_type(Span<const GeometrySet *> src_geometry_sets, GeometrySet &result)
{
@@ -260,6 +268,7 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params)
join_component_type<MeshComponent>(src_geometry_sets, geometry_set_result);
join_component_type<PointCloudComponent>(src_geometry_sets, geometry_set_result);
join_component_type<InstancesComponent>(src_geometry_sets, geometry_set_result);
+ join_component_type<VolumeComponent>(src_geometry_sets, geometry_set_result);
params.set_output("Geometry", std::move(geometry_set_result));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
index ab5e4f8964a..fe0303a5f1c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -19,6 +19,7 @@
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
+#include "BKE_volume.h"
#include "BLI_math_matrix.h"
@@ -86,6 +87,16 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
mesh_component.copy_vertex_group_names_from_object(*object);
}
}
+ if (object->type == OB_VOLUME) {
+ InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+
+ if (transform_space_relative) {
+ instances.add_instance(object, location, rotation, scale);
+ }
+ else {
+ instances.add_instance(object, {0, 0, 0});
+ }
+ }
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
index 4fe61dff72d..251fa0866ad 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -14,11 +14,19 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#ifdef WITH_OPENVDB
+# include <openvdb/openvdb.h>
+#endif
+
#include "BLI_math_matrix.h"
#include "DNA_pointcloud_types.h"
+#include "DNA_volume_types.h"
#include "BKE_mesh.h"
+#include "BKE_volume.h"
+
+#include "DEG_depsgraph_query.h"
#include "node_geometry_util.hh"
@@ -116,6 +124,43 @@ static void transform_instances(InstancesComponent &instances,
}
}
+static void transform_volume(Volume *volume,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale,
+ GeoNodeExecParams &params)
+{
+#ifdef WITH_OPENVDB
+ /* Scaling an axis to zero is not supported for volumes. */
+ const float3 limited_scale = {
+ (scale.x == 0.0f) ? FLT_EPSILON : scale.x,
+ (scale.y == 0.0f) ? FLT_EPSILON : scale.y,
+ (scale.z == 0.0f) ? FLT_EPSILON : scale.z,
+ };
+
+ Main *bmain = DEG_get_bmain(params.depsgraph());
+ BKE_volume_load(volume, bmain);
+
+ float matrix[4][4];
+ loc_eul_size_to_mat4(matrix, translation, rotation, limited_scale);
+
+ openvdb::Mat4s vdb_matrix;
+ memcpy(vdb_matrix.asPointer(), matrix, sizeof(float[4][4]));
+ openvdb::Mat4d vdb_matrix_d{vdb_matrix};
+
+ const int num_grids = BKE_volume_num_grids(volume);
+ for (const int i : IndexRange(num_grids)) {
+ VolumeGrid *volume_grid = BKE_volume_grid_get(volume, i);
+
+ openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false);
+ openvdb::math::Transform &grid_transform = grid->transform();
+ grid_transform.postMult(vdb_matrix_d);
+ }
+#else
+ UNUSED_VARS(volume, translation, rotation, scale, params);
+#endif
+}
+
static void geo_node_transform_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -138,6 +183,11 @@ static void geo_node_transform_exec(GeoNodeExecParams params)
transform_instances(instances, translation, rotation, scale);
}
+ if (geometry_set.has_volume()) {
+ Volume *volume = geometry_set.get_volume_for_write();
+ transform_volume(volume, translation, rotation, scale, params);
+ }
+
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes