1
1

Compare commits

...

42 Commits

Author SHA1 Message Date
7bea1e1223 Run level set to mask once per nested geometry, process all grids 2021-10-31 22:33:57 -05:00
25b462de8e WIP changes to sample volume node 2021-10-31 22:33:29 -05:00
c107387a18 Merge branch 'master' into geometry-nodes-level-set-nodes 2021-10-30 18:43:41 -05:00
92ad540026 Fixes for compiling and starting Blender 2021-10-25 13:29:06 -05:00
47d0b2f14e Merge branch 'master' into geometry-nodes-level-set-nodes 2021-10-25 13:13:54 -05:00
05f500580b Attempt temporary workaround for build issue 2021-09-08 22:50:54 -05:00
71b0721ba5 Merge branch 'master' into geometry-nodes-level-set-nodes 2021-09-08 21:05:54 -05:00
be5bd5f7b4 Merge branch 'master' into geometry-nodes-level-set-nodes 2021-09-08 17:30:32 -05:00
5890953bad Attempt to fix build error on windows 2021-09-05 16:30:10 -05:00
98223022e6 Various small fixes and cleanups 2021-09-05 16:09:04 -05:00
f6b162eca0 Fix incorrect values used to construct Ray 2021-09-05 12:17:52 -05:00
51e51caf54 Fix incorrect socket name in sphere primitive node 2021-09-05 12:17:32 -05:00
9547f110eb Merge branch 'master' into geometry-nodes-level-set-nodes 2021-09-05 11:20:24 -05:00
572dd65614 Only allow float grids in boolean node
Should resolve compile warning for bool comparison in one of the VDB headers
2021-09-02 14:43:04 -05:00
8242567574 Initial support for instances and multiple grids in the volume to mesh node
Non-identity transforms do not work properly yet,
and there are other internal TODOs
2021-09-02 13:59:27 -05:00
cedbf38588 Cleanup: Use new socket declaration API 2021-09-02 11:08:16 -05:00
00541898a1 Merge branch 'master' into geometry-nodes-level-set-nodes 2021-09-01 23:49:20 -05:00
86ea25aa0d Rename existing boolean node to "Mesh Boolean" 2021-08-26 23:20:08 -05:00
64a7957321 Merge branch 'master' into geometry-nodes-level-set-nodes 2021-08-26 23:19:02 -05:00
aa0b4be77f Support level sets in the proximity node 2021-08-25 23:24:58 -05:00
50c8114590 Merge branch 'master' into geometry-nodes-level-set-nodes 2021-08-25 18:03:59 -05:00
8ef80ad9fd Merge branch 'master' into geometry-nodes-level-set-nodes 2021-08-24 21:33:59 -05:00
e47c652bbf Fix sphere node input units 2021-08-13 23:44:00 -05:00
7ef0e7ded7 Add incomplete support for level sets in the raycast node 2021-08-13 23:43:47 -05:00
ff6df9a22d Merge branch 'master' into geometry-nodes-level-set-nodes 2021-08-13 21:47:19 -05:00
e537bb57a3 Add volume sample node and level set to mask node 2021-08-13 20:29:47 -05:00
990e0fd319 Add level set morph node 2021-08-13 15:03:23 -05:00
a3dcbcb6e7 Add two volume primitive nodes 2021-08-13 11:30:31 -05:00
e0a833bbbf Cleanup: Use stringref type 2021-08-13 11:30:22 -05:00
5bbba1984d Cleanup: Remove commented code 2021-08-13 11:29:50 -05:00
e9f6482be7 Cleanup: Use "to_static_type" for filter operation 2021-08-13 11:29:37 -05:00
e43e8f09bb Fix compile error 2021-08-13 09:30:23 -05:00
10ed118edb Merge branch 'master' into geometry-nodes-level-set-nodes 2021-08-13 00:38:18 -05:00
a079d7db28 Rename "Points to Volume" to "Points to Level Set" 2021-08-11 09:40:53 -05:00
c8c64d4081 Make sure non-procedural volumes are loaded, add debug timers 2021-08-11 09:20:10 -05:00
2399f66468 Add level set to fog volume node 2021-08-11 09:19:40 -05:00
53f0047f06 Merge branch 'master' into geometry-nodes-level-set-nodes 2021-08-10 15:56:37 -05:00
fb03f87005 Merge branch 'master' into geometry-nodes-level-set-nodes 2021-08-09 23:24:08 -05:00
4f2642cada Implement second boolean operand resampling, cleanup 2021-08-05 23:54:03 -05:00
b7225df456 Add volume grid "to_static_type" utility 2021-08-05 23:52:52 -05:00
5987b4cc68 Merge branch 'master' into geometry-nodes-level-set-nodes 2021-08-05 21:14:24 -05:00
aa48e1cd66 Geometry Nodes: Level Set Nodes (WIP)
Level sets are representations of all of the input values where a
function's output is the same. If you imagine a "position to density"
function, the corresponding level set describes a 3D surface. Disrete
Level Sets have some benefits over meshes because they always describe
a manifold shape, which makes some operations simpler. In particular,
Boolean (CSG) operations become quite straightforward. In a procedural
context, they also have the benefit of easily change-able resolution.

The OpenVDB library comes with a set of tools that getting useful
functionality out of this data structure relatively simple. This patch
adds initial versions of three level set nodes:

* **Mesh to Level Set**: Like the "Mesh to Volume" node, but creates a level set instead
** Currently the grid name is hardcoded to `level_set`
** Currently only voxel size input is available
* **Level Set Boolean**: Provides the union, difference, and intersect operations.
** The existing Boolean node should be renamed to "Mesh Boolean"
** Missing is resampling of the second input level set for a transformation.
* **Level Set Filter**: Provides "Grow", "Shrink", and "Smooth" operations.
** There are some other filters provided in the OpenVDB toolkit,
   I'm still trying to figure out their use cases though.

This patch is meant to show some possibility and give anyone interested
the chance to test. Another issue is that Blender doesn't have the ability
to display level sets at the moment, which is the idea behind this task: T88599

Differential Revision: https://developer.blender.org/D12100
2021-08-01 00:58:43 -04:00
31 changed files with 1978 additions and 58 deletions

View File

@@ -767,6 +767,17 @@ geometry_node_categories = [
NodeItem("GeometryNodeLegacyVolumeToMesh", poll=geometry_nodes_legacy_poll),
NodeItem("GeometryNodeVolumeToMesh"),
NodeItem("GeometryNodeMeshToLevelSet"),
NodeItem("GeometryNodeLevelSetBoolean"),
NodeItem("GeometryNodeLevelSetFilter"),
NodeItem("GeometryNodeLevelSetToFogVolume"),
NodeItem("GeometryNodeLevelSetToMask"),
NodeItem("GeometryNodeLevelSetMorph"),
NodeItem("GeometryNodeVolumeSample"),
]),
GeometryNodeCategory("GEO_PRIMITIVES_VOLUME", "Volume Primitives", items=[
NodeItem("GeometryNodeLevelSetSphere"),
NodeItem("GeometryNodeLevelSetPlatonic"),
]),
GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items),
GeometryNodeCategory("GEO_LAYOUT", "Layout", items=[

View File

@@ -332,6 +332,8 @@ struct GeometrySet {
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
static GeometrySet create_with_curve(
CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
static GeometrySet create_with_volume(
Volume *volume, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/* Utility methods for access. */
bool has_mesh() const;

View File

@@ -46,6 +46,7 @@
*/
#include "BLI_compiler_attrs.h"
#include "DNA_userdef_types.h"
#ifdef __cplusplus
extern "C" {

View File

@@ -1551,6 +1551,15 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_VOLUME_TO_MESH 1133
#define GEO_NODE_INPUT_ID 1134
#define GEO_NODE_SET_ID 1135
#define GEO_NODE_MESH_TO_LEVEL_SET 1136
#define GEO_NODE_LEVEL_SET_BOOLEAN 1137
#define GEO_NODE_LEVEL_SET_FILTER 1138
#define GEO_NODE_LEVEL_SET_TO_FOG_VOLUME 1139
#define GEO_NODE_LEVEL_SET_PRIMITIVE_SPHERE 1140
#define GEO_NODE_LEVEL_SET_PRIMITIVE_PLATONIC 1141
#define GEO_NODE_LEVEL_SET_MORPH 1142
#define GEO_NODE_SAMPLE_VOLUME 1143
#define GEO_NODE_LEVEL_SET_TO_MASK 1144
/** \} */

View File

@@ -89,6 +89,7 @@ const VolumeGrid *BKE_volume_grid_get_for_read(const struct Volume *volume, int
VolumeGrid *BKE_volume_grid_get_for_write(struct Volume *volume, int grid_index);
const VolumeGrid *BKE_volume_grid_active_get_for_read(const struct Volume *volume);
const VolumeGrid *BKE_volume_grid_find_for_read(const struct Volume *volume, const char *name);
const VolumeGrid *BKE_volume_grid_find_for_write(struct Volume *volume, const char *name);
/* Grid
*
@@ -183,11 +184,53 @@ openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_metadata(const struct Vo
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const struct Volume *volume,
const struct VolumeGrid *grid);
openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const struct Volume *volume,
struct VolumeGrid *grid,
const bool clear);
struct VolumeGrid *grid);
VolumeGridType BKE_volume_grid_type_openvdb(const openvdb::GridBase &grid);
namespace blender::bke::volume {
template<typename Func>
inline void to_static_type(const VolumeGridType data_type, const Func &func)
{
switch (data_type) {
case VOLUME_GRID_FLOAT:
func(openvdb::FloatGrid());
break;
case VOLUME_GRID_VECTOR_FLOAT:
func(openvdb::Vec3fGrid());
break;
case VOLUME_GRID_BOOLEAN:
func(openvdb::BoolGrid());
break;
case VOLUME_GRID_DOUBLE:
func(openvdb::DoubleGrid());
break;
case VOLUME_GRID_INT:
func(openvdb::Int32Grid());
break;
case VOLUME_GRID_VECTOR_INT:
func(openvdb::Vec3IGrid());
break;
case VOLUME_GRID_VECTOR_DOUBLE:
func(openvdb::Vec3dGrid());
break;
case VOLUME_GRID_STRING:
func(openvdb::StringGrid());
break;
case VOLUME_GRID_MASK:
func(openvdb::MaskGrid());
break;
case VOLUME_GRID_POINTS:
func(openvdb::points::PointDataGrid());
break;
default:
BLI_assert_unreachable();
break;
}
}
} // namespace blender::bke::volume
template<typename OpType>
auto BKE_volume_grid_type_operation(const VolumeGridType grid_type, OpType &&op)
{

View File

@@ -382,6 +382,15 @@ GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipTy
return geometry_set;
}
/* Create a new geometry set that only contains the given volume. */
GeometrySet GeometrySet::create_with_volume(Volume *volume, GeometryOwnershipType ownership)
{
GeometrySet geometry_set;
VolumeComponent &component = geometry_set.get_component_for_write<VolumeComponent>();
component.replace(volume, ownership);
return geometry_set;
}
/* Clear the existing mesh and replace it with the given one. */
void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{

View File

@@ -5826,6 +5826,13 @@ static void registerGeometryNodes()
register_node_type_geo_instances_to_points();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
register_node_type_geo_level_set_boolean();
register_node_type_geo_level_set_filter();
register_node_type_geo_level_set_morph();
register_node_type_geo_level_set_primitive_platonic();
register_node_type_geo_level_set_primitive_sphere();
register_node_type_geo_level_set_to_fog_volume();
register_node_type_geo_level_set_to_mask();
register_node_type_geo_material_replace();
register_node_type_geo_material_selection();
register_node_type_geo_mesh_primitive_circle();
@@ -5838,6 +5845,7 @@ static void registerGeometryNodes()
register_node_type_geo_mesh_primitive_uv_sphere();
register_node_type_geo_mesh_subdivide();
register_node_type_geo_mesh_to_curve();
register_node_type_geo_mesh_to_level_set();
register_node_type_geo_mesh_to_points();
register_node_type_geo_object_info();
register_node_type_geo_point_distribute();
@@ -5876,6 +5884,7 @@ static void registerGeometryNodes()
register_node_type_geo_translate_instances();
register_node_type_geo_triangulate();
register_node_type_geo_viewer();
register_node_type_geo_sample_volume();
register_node_type_geo_volume_to_mesh();
}

View File

@@ -1239,6 +1239,19 @@ const VolumeGrid *BKE_volume_grid_find_for_read(const Volume *volume, const char
return nullptr;
}
const VolumeGrid *BKE_volume_grid_find_for_write(struct Volume *volume, const char *name)
{
int num_grids = BKE_volume_num_grids(volume);
for (int i = 0; i < num_grids; i++) {
const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
if (STREQ(BKE_volume_grid_name(grid), name)) {
return grid;
}
}
return nullptr;
}
/* Grid Loading */
bool BKE_volume_grid_load(const Volume *volume, const VolumeGrid *grid)
@@ -1557,18 +1570,11 @@ openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const Volume *volum
return grid->grid();
}
openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const Volume *volume,
VolumeGrid *grid,
const bool clear)
openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const Volume *volume, VolumeGrid *grid)
{
const char *volume_name = volume->id.name + 2;
if (clear) {
grid->clear_reference(volume_name);
}
else {
VolumeGridVector &grids = *volume->runtime.grids;
grid->duplicate_reference(volume_name, grids.filepath);
}
VolumeGridVector &grids = *volume->runtime.grids;
grid->duplicate_reference(volume_name, grids.filepath);
return grid->grid();
}

View File

@@ -1384,6 +1384,35 @@ typedef struct NodeGeometryVolumeToMesh {
uint8_t resolution_mode;
} NodeGeometryVolumeToMesh;
typedef struct NodeGeometrySampleVolume {
/* CustomDataType. */
int8_t data_type;
/* GeometryNodeSampleVolumeInterpolation. */
int8_t interpolation;
} NodeGeometrySampleVolume;
typedef struct NodeGeometryLevelSetBoolean {
/* GeometryNodeBooleanOperation */
uint8_t operation;
} NodeGeometryLevelSetBoolean;
typedef struct NodeGeometryLevelSetMorph {
/* GeometryNodeLevelSetSpacialScheme */
uint8_t spatial_scheme;
/* GeometryNodeLevelSetTemporalScheme */
uint8_t temporal_scheme;
} NodeGeometryLevelSetMorph;
typedef struct NodeGeometryLevelSetFilter {
/* GeometryNodeFilterOperation */
uint8_t operation;
} NodeGeometryLevelSetFilter;
typedef struct NodeGeometryLevelSetPlatonic {
/* NodeGeometryPlatonicShape */
uint8_t shape;
} NodeGeometryLevelSetPlatonic;
typedef struct NodeAttributeCombineXYZ {
/* GeometryNodeAttributeInputMode. */
uint8_t input_type_x;
@@ -2201,6 +2230,40 @@ typedef enum GeometryNodeCurveFilletMode {
GEO_NODE_CURVE_FILLET_POLY = 1,
} GeometryNodeCurveFilletMode;
typedef enum GeometryNodeFilterOperation {
GEO_NODE_LEVEL_SET_FILTER_GAUSSIAN = 0,
GEO_NODE_LEVEL_SET_FILTER_OFFSET = 1,
GEO_NODE_LEVEL_SET_FILTER_MEDIAN = 2,
GEO_NODE_LEVEL_SET_FILTER_MEAN = 3,
GEO_NODE_LEVEL_SET_FILTER_MEAN_CURVATURE = 4,
GEO_NODE_LEVEL_SET_FILTER_LAPLACIAN = 5,
} GeometryNodeFilterOperation;
typedef enum NodeGeometryPlatonicShape {
GEO_NODE_PLATONIC_TETRAHEDRON = 4,
GEO_NODE_PLATONIC_CUBE = 6,
GEO_NODE_PLATONIC_OCTAHEDRON = 8,
GEO_NODE_PLATONIC_DODECAHEDRON = 12,
GEO_NODE_PLATONIC_ICOSAHEDRON = 20,
} NodeGeometryPlatonicShape;
typedef enum GeometryNodeLevelSetSpatialScheme {
GEO_NODE_LEVEL_SET_MORPH_SPATIAL_FIRST = 0,
GEO_NODE_LEVEL_SET_MORPH_SPATIAL_HJWENO5 = 1,
} GeometryNodeLevelSetSpatialScheme;
typedef enum GeometryNodeLevelSetTemporalScheme {
GEO_NODE_LEVEL_SET_MORPH_SPATIAL_FORWARD_EULER = 0,
GEO_NODE_LEVEL_SET_MORPH_SPATIAL_2ND = 1,
GEO_NODE_LEVEL_SET_MORPH_SPATIAL_3RD = 2,
} GeometryNodeLevelSetTemporalScheme;
typedef enum GeometryNodeSampleVolumeInterpolation {
GEO_NODE_VOLUME_SAMPLE_NEAREST = 0,
GEO_NODE_VOLUME_SAMPLE_LINEAR = 1,
GEO_NODE_VOLUME_SAMPLE_QUADRATIC = 2,
} GeometryNodeSampleVolumeInterpolation;
typedef enum GeometryNodeAttributeTransferMapMode {
GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0,
GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST = 1,

View File

@@ -10360,6 +10360,126 @@ static void def_geo_volume_to_mesh(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_level_set_boolean(StructRNA *srna)
{
PropertyRNA *prop;
static EnumPropertyItem operation_items[] = {
{GEO_NODE_BOOLEAN_INTERSECT,
"INTERSECT",
0,
"Intersect",
"The output is the volume contained by both inputs"},
{GEO_NODE_BOOLEAN_UNION,
"UNION",
0,
"Union",
"The output is the volume contained by either the first input or the second"},
{GEO_NODE_BOOLEAN_DIFFERENCE,
"DIFFERENCE",
0,
"Difference",
"The output is the volume contained by the first input but not the second"},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryLevelSetBoolean", "storage");
prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, operation_items);
RNA_def_property_ui_text(prop, "Operation", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_level_set_filter(StructRNA *srna)
{
PropertyRNA *prop;
static EnumPropertyItem operation_items[] = {
{GEO_NODE_LEVEL_SET_FILTER_OFFSET,
"OFFSET",
0,
"Offset",
"Expand or contract the level set surface"},
{GEO_NODE_LEVEL_SET_FILTER_GAUSSIAN,
"GAUSSIAN",
0,
"Gaussian",
"Smooth the level set surface"},
{GEO_NODE_LEVEL_SET_FILTER_MEDIAN, "MEDIAN", 0, "median", "???"},
{GEO_NODE_LEVEL_SET_FILTER_MEAN, "MEAN", 0, "Mean", "???"},
{GEO_NODE_LEVEL_SET_FILTER_MEAN_CURVATURE, "MEAN_CURVATURE", 0, "Mean Curvature", "???"},
{GEO_NODE_LEVEL_SET_FILTER_LAPLACIAN, "LAPLACIAN", 0, "Laplacian", "???"},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryLevelSetFilter", "storage");
prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, operation_items);
RNA_def_property_ui_text(prop, "Operation", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_level_set_morph(StructRNA *srna)
{
PropertyRNA *prop;
static EnumPropertyItem spatial_scheme_items[] = {
{GEO_NODE_LEVEL_SET_MORPH_SPATIAL_FIRST, "FIRST", 0, "First", "1st-order biased gradient"},
{GEO_NODE_LEVEL_SET_MORPH_SPATIAL_HJWENO5,
"HJWENO5",
0,
"HJWENO5",
"5th-order HJ-WENO biased gradient"},
{0, NULL, 0, NULL, NULL},
};
static EnumPropertyItem temporal_scheme_items[] = {
{GEO_NODE_LEVEL_SET_MORPH_SPATIAL_FORWARD_EULER, "FORWARD_EULER", 0, "Forward Euler", ""},
{GEO_NODE_LEVEL_SET_MORPH_SPATIAL_2ND, "2ND", 0, "Second Order", "2nd order Runge-Kutta"},
{GEO_NODE_LEVEL_SET_MORPH_SPATIAL_3RD, "3RD", 0, "Third Order", "3rd order Runge-Kutta"},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryLevelSetMorph", "storage");
prop = RNA_def_property(srna, "spatial_scheme", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, spatial_scheme_items);
RNA_def_property_ui_text(prop, "Spatial Scheme", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "temporal_scheme", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, temporal_scheme_items);
RNA_def_property_ui_text(prop, "Temporal Scheme", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_level_set_primitive_platonic(StructRNA *srna)
{
PropertyRNA *prop;
static EnumPropertyItem shape_items[] = {
{GEO_NODE_PLATONIC_TETRAHEDRON, "TETRAHEDRON", 0, "Tetrahedron", "Create a 4-sided shape"},
{GEO_NODE_PLATONIC_CUBE, "CUBE", 0, "Cube", "Create a 6-sided cube"},
{GEO_NODE_PLATONIC_OCTAHEDRON, "OCTAHEDRON", 0, "Octahedron", "Create a 8-sided shape"},
{GEO_NODE_PLATONIC_DODECAHEDRON,
"DODECAHEDRON",
0,
"Dodecahedron",
"Create a 12-sided shape"},
{GEO_NODE_PLATONIC_ICOSAHEDRON, "ICOSAHEDRON", 0, "Icosahedron", "Create a 20-sided shape"},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryLevelSetPlatonic", "storage");
prop = RNA_def_property(srna, "shape", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, shape_items);
RNA_def_property_ui_text(prop, "Shape", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_attribute_combine_xyz(StructRNA *srna)
{
PropertyRNA *prop;

View File

@@ -49,6 +49,7 @@
#include "BLI_float4x4.hh"
#include "BLI_index_range.hh"
#include "BLI_span.hh"
#include "BLI_timeit.hh"
#include "RNA_access.h"
@@ -222,6 +223,9 @@ static Volume *mesh_to_volume(ModifierData *md,
if (mesh == nullptr) {
return input_volume;
}
SCOPED_TIMER(__func__);
BKE_mesh_wrapper_ensure_mdata(mesh);
const float4x4 mesh_to_own_object_space_transform = float4x4(ctx->object->imat) *
@@ -258,7 +262,7 @@ static Volume *mesh_to_volume(ModifierData *md,
Volume *volume = BKE_volume_new_for_eval(input_volume);
VolumeGrid *c_density_grid = BKE_volume_grid_add(volume, "density", VOLUME_GRID_FLOAT);
openvdb::FloatGrid::Ptr density_grid = openvdb::gridPtrCast<openvdb::FloatGrid>(
BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false));
BKE_volume_grid_openvdb_for_write(volume, c_density_grid));
/* Merge the generated grid into the density grid. Should be cheap because density_grid has just
* been created as well. */

View File

@@ -299,7 +299,7 @@ static void displace_volume(ModifierData *md, const ModifierEvalContext *ctx, Vo
VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, grid_index);
BLI_assert(volume_grid != nullptr);
openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false);
openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid);
VolumeGridType grid_type = BKE_volume_grid_type(volume_grid);
DisplaceGridOp displace_grid_op{*grid, *vdmd, *ctx};

View File

@@ -247,6 +247,13 @@ set(SRC
geometry/nodes/node_geo_instances_to_points.cc
geometry/nodes/node_geo_is_viewport.cc
geometry/nodes/node_geo_join_geometry.cc
geometry/nodes/node_geo_level_set_boolean.cc
geometry/nodes/node_geo_level_set_filter.cc
geometry/nodes/node_geo_level_set_morph.cc
geometry/nodes/node_geo_level_set_primitive_platonic.cc
geometry/nodes/node_geo_level_set_primitive_sphere.cc
geometry/nodes/node_geo_level_set_to_fog_volume.cc
geometry/nodes/node_geo_level_set_to_mask.cc
geometry/nodes/node_geo_material_replace.cc
geometry/nodes/node_geo_material_selection.cc
geometry/nodes/node_geo_mesh_primitive_circle.cc
@@ -259,6 +266,7 @@ set(SRC
geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
geometry/nodes/node_geo_mesh_subdivide.cc
geometry/nodes/node_geo_mesh_to_curve.cc
geometry/nodes/node_geo_mesh_to_level_set.cc
geometry/nodes/node_geo_mesh_to_points.cc
geometry/nodes/node_geo_object_info.cc
geometry/nodes/node_geo_points_to_vertices.cc
@@ -290,6 +298,7 @@ set(SRC
geometry/nodes/node_geo_translate_instances.cc
geometry/nodes/node_geo_triangulate.cc
geometry/nodes/node_geo_viewer.cc
geometry/nodes/node_geo_sample_volume.cc
geometry/nodes/node_geo_volume_to_mesh.cc
geometry/node_geometry_exec.cc

View File

@@ -114,6 +114,13 @@ void register_node_type_geo_instance_on_points(void);
void register_node_type_geo_instances_to_points(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);
void register_node_type_geo_level_set_boolean(void);
void register_node_type_geo_level_set_filter(void);
void register_node_type_geo_level_set_morph(void);
void register_node_type_geo_level_set_primitive_platonic(void);
void register_node_type_geo_level_set_primitive_sphere(void);
void register_node_type_geo_level_set_to_fog_volume(void);
void register_node_type_geo_level_set_to_mask(void);
void register_node_type_geo_material_replace(void);
void register_node_type_geo_material_selection(void);
void register_node_type_geo_mesh_primitive_circle(void);
@@ -126,6 +133,7 @@ void register_node_type_geo_mesh_primitive_line(void);
void register_node_type_geo_mesh_primitive_uv_sphere(void);
void register_node_type_geo_mesh_subdivide(void);
void register_node_type_geo_mesh_to_curve(void);
void register_node_type_geo_mesh_to_level_set(void);
void register_node_type_geo_mesh_to_points(void);
void register_node_type_geo_object_info(void);
void register_node_type_geo_point_distribute(void);
@@ -166,6 +174,7 @@ void register_node_type_geo_translate_instances(void);
void register_node_type_geo_triangulate(void);
void register_node_type_geo_viewer(void);
void register_node_type_geo_volume_to_mesh(void);
void register_node_type_geo_sample_volume(void);
#ifdef __cplusplus
}

View File

@@ -367,6 +367,13 @@ DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", Inst
DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS", InstancesToPoints, "Instances to Points", "")
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
DefNode(GeometryNode, GEO_NODE_LEVEL_SET_BOOLEAN , def_geo_level_set_boolean, "LEVEL_SET_BOOLEAN", LevelSetBoolean, "Level Set Boolean", "")
DefNode(GeometryNode, GEO_NODE_LEVEL_SET_FILTER , def_geo_level_set_filter, "LEVEL_SET_FILTER", LevelSetFilter, "Level Set Filter", "")
DefNode(GeometryNode, GEO_NODE_LEVEL_SET_MORPH , def_geo_level_set_morph, "LEVEL_SET_MORPH", LevelSetMorph, "Level Set Morph", "")
DefNode(GeometryNode, GEO_NODE_LEVEL_SET_PRIMITIVE_PLATONIC , def_geo_level_set_primitive_platonic, "LEVEL_SET_PRIMITIVE_PLATONIC", LevelSetPlatonic, "Level Set Platonic", "")
DefNode(GeometryNode, GEO_NODE_LEVEL_SET_PRIMITIVE_SPHERE , 0, "LEVEL_SET_PRIMITIVE_SPHERE", LevelSetSphere, "Level Set Sphere", "")
DefNode(GeometryNode, GEO_NODE_LEVEL_SET_TO_FOG_VOLUME , 0, "LEVEL_SET_TO_FOG", LevelSetToFogVolume, "Level Set to Fog Volume", "")
DefNode(GeometryNode, GEO_NODE_LEVEL_SET_TO_MASK , 0, "LEVEL_SET_TO_Mask", LevelSetToMask, "Level Set to Mask", "")
DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "")
DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "")
@@ -378,6 +385,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
DefNode(GeometryNode, GEO_NODE_MESH_TO_LEVEL_SET , 0, "MESH_TO_LEVEL_SET", MeshToLevelSet, "Mesh to Level Set", "")
DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "")
@@ -386,10 +394,12 @@ DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proxim
DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "")
DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "")
DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "")
DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "")
DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "")
DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "")
DefNode(GeometryNode, GEO_NODE_SAMPLE_VOLUME, 0, "SAMPLE_VOLUME", SampleVolume, "Sample Volume", "")
DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "")

View File

@@ -16,6 +16,8 @@
#pragma once
#include "BLI_index_mask.hh"
#include "FN_multi_function.hh"
namespace blender::nodes {
@@ -76,6 +78,8 @@ class DataTypeConversions {
fn::GVArrayPtr try_convert(fn::GVArrayPtr varray, const CPPType &to_type) const;
fn::GVMutableArrayPtr try_convert(fn::GVMutableArrayPtr varray, const CPPType &to_type) const;
void try_convert(fn::GSpan src, fn::GMutableSpan dst, IndexMask mask) const;
};
const DataTypeConversions &get_implicit_type_conversions();

View File

@@ -20,6 +20,11 @@
#include "DNA_mesh_types.h"
#include "BKE_bvhutils.h"
#include "BKE_volume.h"
#ifdef WITH_OPENVDB
# include <openvdb/tools/VolumeToSpheres.h>
#endif
#include "UI_interface.h"
#include "UI_resources.h"
@@ -137,6 +142,64 @@ static void calculate_pointcloud_proximity(const VArray<float3> &positions,
free_bvhtree_from_pointcloud(&bvh_data);
}
#ifdef WITH_OPENVDB
static void calculate_level_set_proximity(const GeoNodeExecParams &params,
const VArray<float3> &positions,
const Volume &volume,
MutableSpan<float> r_distances,
MutableSpan<float3> r_locations)
{
const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(&volume, 0);
if (volume_grid == nullptr) {
params.error_message_add(NodeWarningType::Error, TIP_("Volume is empty"));
return;
}
openvdb::GridBase::ConstPtr grid_base = BKE_volume_grid_openvdb_for_read(&volume, volume_grid);
if (grid_base->getGridClass() != openvdb::GridClass::GRID_LEVEL_SET) {
params.error_message_add(NodeWarningType::Error, TIP_("Only level set volumes are supported"));
}
const int size = positions.size();
bke::volume::to_static_type(BKE_volume_grid_type(volume_grid), [&](auto dummy) {
using GridType = decltype(dummy);
if constexpr (std::is_same_v<GridType, openvdb::FloatGrid>) {
const GridType &grid = static_cast<const GridType &>(*grid_base);
auto csp = openvdb::tools::ClosestSurfacePoint<GridType>::create(grid);
if (!csp) {
return;
}
std::vector<openvdb::Vec3R> nearest(size);
for (const int i : IndexRange(size)) {
nearest[i] = openvdb::Vec3R(positions[i].x, positions[i].y, positions[i].z);
}
std::vector<float> distances;
if (!csp->searchAndReplace(nearest, distances)) {
return;
}
BLI_assert(distances.size() == size);
threading::parallel_for(IndexRange(size), 2048, [&](IndexRange range) {
for (const int i : range) {
/* TODO: Stupid to square and then sqrt later, consider refactoring this. */
const float distance = distances[i] * distances[i];
if (distance < r_distances[i]) {
r_distances[i] = distance;
if (!r_locations.is_empty()) {
r_locations[i] = float3(nearest[i].x(), nearest[i].y(), nearest[i].z());
}
}
}
});
}
});
}
#endif
static void attribute_calc_proximity(GeometryComponent &component,
GeometrySet &target,
GeoNodeExecParams &params)
@@ -188,6 +251,13 @@ static void attribute_calc_proximity(GeometryComponent &component,
positions, *target.get_pointcloud_for_read(), distances, locations);
}
#ifdef WITH_OPENVDB
if (target.has_volume()) {
calculate_level_set_proximity(
params, positions, *target.get_volume_for_read(), distances, locations);
}
#endif
if (distance_attribute) {
/* Squared distances are used above to speed up comparisons,
* so do the square roots now if necessary for the output attribute. */

View File

@@ -227,7 +227,7 @@ static void initialize_volume_component_from_points(const GeometrySet &geometry_
VolumeGrid *c_density_grid = BKE_volume_grid_add(volume, "density", VOLUME_GRID_FLOAT);
openvdb::FloatGrid::Ptr density_grid = openvdb::gridPtrCast<openvdb::FloatGrid>(
BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false));
BKE_volume_grid_openvdb_for_write(volume, c_density_grid));
const float density = params.get_input<float>("Density");
convert_to_grid_index_space(voxel_size, positions, radii);

View File

@@ -0,0 +1,190 @@
/*
* 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.
*/
#ifdef WITH_OPENVDB
# include <openvdb/openvdb.h>
# include <openvdb/tools/Composite.h>
# include <openvdb/tools/GridTransformer.h>
#endif
#include "BKE_volume.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "DEG_depsgraph_query.h"
#include "node_geometry_util.hh"
namespace blender::nodes {
static void geo_node_level_set_boolean_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Level Set 1");
b.add_input<decl::Geometry>("Level Set 2");
b.add_output<decl::Geometry>("Level Set");
}
static void geo_node_level_set_boolean_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
}
static void geo_node_level_set_boolean_init(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryLevelSetBoolean *data = (NodeGeometryLevelSetBoolean *)MEM_callocN(
sizeof(NodeGeometryLevelSetBoolean), __func__);
data->operation = GEO_NODE_BOOLEAN_UNION;
node->storage = data;
}
#ifdef WITH_OPENVDB
static void level_set_boolean(Volume &volume_a,
const Volume &volume_b,
const GeometryNodeBooleanOperation operation,
const GeoNodeExecParams &params)
{
VolumeGrid *volume_grid_a = BKE_volume_grid_get_for_write(&volume_a, 0);
const VolumeGrid *volume_grid_b = BKE_volume_grid_get_for_read(&volume_b, 0);
if (ELEM(nullptr, volume_grid_a, volume_grid_b)) {
if (volume_grid_a == nullptr) {
params.error_message_add(NodeWarningType::Info, TIP_("Volume 1 is empty"));
}
if (volume_grid_b == nullptr) {
params.error_message_add(NodeWarningType::Info, TIP_("Volume 2 is empty"));
}
return;
}
openvdb::GridBase::Ptr grid_base_a = BKE_volume_grid_openvdb_for_write(&volume_a, volume_grid_a);
openvdb::GridBase::ConstPtr grid_base_b = BKE_volume_grid_openvdb_for_read(&volume_b,
volume_grid_b);
if (grid_base_a->getGridClass() != openvdb::GridClass::GRID_LEVEL_SET ||
grid_base_b->getGridClass() != openvdb::GridClass::GRID_LEVEL_SET) {
if (grid_base_a->getGridClass() != openvdb::GridClass::GRID_LEVEL_SET) {
params.error_message_add(NodeWarningType::Error, TIP_("Volume 1 is not a level set"));
}
if (grid_base_b->getGridClass() != openvdb::GridClass::GRID_LEVEL_SET) {
params.error_message_add(NodeWarningType::Error, TIP_("Volume 2 is not a level set"));
}
return;
}
const VolumeGridType grid_type_a = BKE_volume_grid_type(volume_grid_a);
const VolumeGridType grid_type_b = BKE_volume_grid_type(volume_grid_b);
if (grid_type_a != grid_type_b) {
params.error_message_add(NodeWarningType::Error, TIP_("Volume grid types do not match"));
return;
}
bke::volume::to_static_type(grid_type_a, [&](auto dummy) {
using GridType = decltype(dummy);
if constexpr (std::is_same_v<GridType, openvdb::FloatGrid>) {
GridType &grid_a = static_cast<GridType &>(*grid_base_a);
const GridType &grid_b = static_cast<const GridType &>(*grid_base_b);
openvdb::GridBase::Ptr grid_b_resampled_base = GridType::create();
GridType &grid_b_resampled = static_cast<GridType &>(*grid_b_resampled_base);
grid_b_resampled.setTransform(grid_base_a->constTransform().copy());
/* TODO: COnsider using doResampleToMatch in some cases, which doesn't handle scaling or
* non-affine transformations but should be faster. */
openvdb::tools::resampleToMatch<openvdb::tools::BoxSampler>(grid_b, grid_b_resampled);
openvdb::tools::pruneLevelSet(grid_b_resampled.tree());
switch (operation) {
case GEO_NODE_BOOLEAN_INTERSECT:
openvdb::tools::csgIntersection(grid_a, grid_b_resampled);
break;
case GEO_NODE_BOOLEAN_UNION:
openvdb::tools::csgUnion(grid_a, grid_b_resampled);
break;
case GEO_NODE_BOOLEAN_DIFFERENCE:
openvdb::tools::csgDifference(grid_a, grid_b_resampled);
break;
}
}
});
}
#endif /* WITH_OPENVDB */
static void geo_node_level_set_boolean_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set_a = params.extract_input<GeometrySet>("Level Set 1");
#ifdef WITH_OPENVDB
GeometrySet geometry_set_b = params.extract_input<GeometrySet>("Level Set 2");
const NodeGeometryLevelSetBoolean &storage =
*(const NodeGeometryLevelSetBoolean *)params.node().storage;
const GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)storage.operation;
SCOPED_TIMER(__func__);
Volume *volume_a = geometry_set_a.get_volume_for_write();
const Volume *volume_b = geometry_set_b.get_volume_for_read();
if (volume_a == nullptr && volume_b == nullptr) {
params.set_output("Level Set", GeometrySet());
return;
}
if (ELEM(operation, GEO_NODE_BOOLEAN_DIFFERENCE, GEO_NODE_BOOLEAN_UNION)) {
if (volume_a == nullptr) {
params.set_output("Level Set", std::move(geometry_set_b));
return;
}
}
if (operation == GEO_NODE_BOOLEAN_UNION) {
if (volume_b == nullptr) {
params.set_output("Level Set", std::move(geometry_set_a));
return;
}
}
const Main *bmain = DEG_get_bmain(params.depsgraph());
BKE_volume_load(volume_a, bmain);
BKE_volume_load(volume_b, bmain);
level_set_boolean(*volume_a, *volume_b, operation, params);
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
#endif
params.set_output("Level Set", std::move(geometry_set_a));
}
} // namespace blender::nodes
void register_node_type_geo_level_set_boolean()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_LEVEL_SET_BOOLEAN, "Level Set Boolean", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_level_set_boolean_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_level_set_boolean_exec;
node_type_storage(&ntype,
"NodeGeometryLevelSetBoolean",
node_free_standard_storage,
node_copy_standard_storage);
node_type_init(&ntype, blender::nodes::geo_node_level_set_boolean_init);
ntype.draw_buttons = blender::nodes::geo_node_level_set_boolean_layout;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,165 @@
/*
* 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.
*/
#ifdef WITH_OPENVDB
# include <openvdb/tools/GridTransformer.h>
# include <openvdb/tools/LevelSetFilter.h>
#endif
#include "BKE_volume.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "DEG_depsgraph_query.h"
#include "node_geometry_util.hh"
namespace blender::nodes {
static void geo_node_level_set_filter_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Level Set");
b.add_input<decl::Float>("Distance").default_value(0.1f).subtype(PROP_DISTANCE);
b.add_input<decl::Int>("Width").default_value(1).min(128);
b.add_output<decl::Geometry>("Level Set");
}
static void geo_node_level_set_filter_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
}
static void geo_node_level_set_filter_init(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryLevelSetFilter *data = (NodeGeometryLevelSetFilter *)MEM_callocN(
sizeof(NodeGeometryLevelSetFilter), __func__);
data->operation = GEO_NODE_LEVEL_SET_FILTER_OFFSET;
node->storage = data;
}
static void geo_node_level_set_filter_update(bNodeTree *UNUSED(ntree), bNode *node)
{
const NodeGeometryLevelSetFilter &data = *(const NodeGeometryLevelSetFilter *)node->storage;
const GeometryNodeFilterOperation operation = (GeometryNodeFilterOperation)data.operation;
bNodeSocket *distance_socket = ((bNodeSocket *)node->inputs.first)->next;
bNodeSocket *width_socket = distance_socket->next;
nodeSetSocketAvailability(distance_socket, operation == GEO_NODE_LEVEL_SET_FILTER_OFFSET);
nodeSetSocketAvailability(width_socket,
ELEM(operation,
GEO_NODE_LEVEL_SET_FILTER_GAUSSIAN,
GEO_NODE_LEVEL_SET_FILTER_MEDIAN,
GEO_NODE_LEVEL_SET_FILTER_MEAN));
}
#ifdef WITH_OPENVDB
static void level_set_filter(Volume &volume,
const GeometryNodeFilterOperation operation,
const GeoNodeExecParams &params)
{
VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(&volume, 0);
if (volume_grid == nullptr) {
params.error_message_add(NodeWarningType::Error, TIP_("Volume is empty"));
return;
}
openvdb::GridBase::Ptr grid_base = BKE_volume_grid_openvdb_for_write(&volume, volume_grid);
bke::volume::to_static_type(BKE_volume_grid_type_openvdb(*grid_base), [&](auto dummy) {
using GridType = decltype(dummy);
if constexpr (std::is_same_v<GridType, openvdb::FloatGrid>) {
GridType &grid = static_cast<GridType &>(*grid_base);
openvdb::tools::LevelSetFilter<GridType> filter(grid);
switch (operation) {
case GEO_NODE_LEVEL_SET_FILTER_GAUSSIAN:
filter.gaussian(params.get_input<int>("Width"));
break;
case GEO_NODE_LEVEL_SET_FILTER_OFFSET:
filter.offset(-params.get_input<float>("Distance"));
break;
case GEO_NODE_LEVEL_SET_FILTER_MEDIAN:
filter.median(params.get_input<int>("Width"));
break;
case GEO_NODE_LEVEL_SET_FILTER_MEAN:
filter.mean(params.get_input<int>("Width"));
break;
case GEO_NODE_LEVEL_SET_FILTER_MEAN_CURVATURE:
filter.meanCurvature();
break;
case GEO_NODE_LEVEL_SET_FILTER_LAPLACIAN:
filter.laplacian();
break;
}
}
else {
params.error_message_add(NodeWarningType::Error,
TIP_("Filter operations only support float grids"));
}
});
}
#endif /* WITH_OPENVDB */
static void geo_node_level_set_filter_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Level Set");
#ifdef WITH_OPENVDB
Volume *volume = geometry_set.get_volume_for_write();
if (volume == nullptr) {
params.set_output("Level Set", std::move(geometry_set));
return;
}
const Main *bmain = DEG_get_bmain(params.depsgraph());
BKE_volume_load(volume, bmain);
const NodeGeometryLevelSetFilter &data =
*(const NodeGeometryLevelSetFilter *)params.node().storage;
const GeometryNodeFilterOperation operation = (GeometryNodeFilterOperation)data.operation;
level_set_filter(*volume, operation, params);
#endif
params.set_output("Level Set", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_level_set_filter()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_LEVEL_SET_FILTER, "Level Set Filter", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_level_set_filter_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_level_set_filter_exec;
node_type_storage(&ntype,
"NodeGeometryLevelSetFilter",
node_free_standard_storage,
node_copy_standard_storage);
node_type_init(&ntype, blender::nodes::geo_node_level_set_filter_init);
ntype.draw_buttons = blender::nodes::geo_node_level_set_filter_layout;
ntype.updatefunc = blender::nodes::geo_node_level_set_filter_update;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,209 @@
/*
* 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.
*/
#ifdef WITH_OPENVDB
# include <openvdb/openvdb.h>
# include <openvdb/tools/GridTransformer.h>
# include <openvdb/tools/LevelSetMorph.h>
#endif
#include "BKE_volume.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "DEG_depsgraph_query.h"
#include "node_geometry_util.hh"
namespace blender::nodes {
static void geo_node_level_set_morph_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Source");
b.add_input<decl::Geometry>("Target");
b.add_input<decl::Float>("Factor").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_output<decl::Geometry>("Result");
}
static void geo_node_level_set_morph_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "spatial_scheme", 0, "", ICON_NONE);
uiItemR(layout, ptr, "temporal_scheme", 0, "", ICON_NONE);
}
static void geo_node_level_set_morph_init(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryLevelSetMorph *data = (NodeGeometryLevelSetMorph *)MEM_callocN(
sizeof(NodeGeometryLevelSetMorph), __func__);
data->spatial_scheme = GEO_NODE_LEVEL_SET_MORPH_SPATIAL_HJWENO5;
data->temporal_scheme = GEO_NODE_LEVEL_SET_MORPH_SPATIAL_2ND;
node->storage = data;
}
#ifdef WITH_OPENVDB
static openvdb::math::TemporalIntegrationScheme temporal_scheme_to_openvdb(
const GeometryNodeLevelSetTemporalScheme value)
{
switch (value) {
case GEO_NODE_LEVEL_SET_MORPH_SPATIAL_FORWARD_EULER:
return openvdb::math::TVD_RK1;
case GEO_NODE_LEVEL_SET_MORPH_SPATIAL_2ND:
return openvdb::math::TVD_RK2;
case GEO_NODE_LEVEL_SET_MORPH_SPATIAL_3RD:
return openvdb::math::TVD_RK3;
}
BLI_assert_unreachable();
return openvdb::math::TVD_RK1;
}
static openvdb::math::BiasedGradientScheme spatial_scheme_to_openvdb(
const GeometryNodeLevelSetSpatialScheme value)
{
switch (value) {
case GEO_NODE_LEVEL_SET_MORPH_SPATIAL_FIRST:
return openvdb::math::FIRST_BIAS;
case GEO_NODE_LEVEL_SET_MORPH_SPATIAL_HJWENO5:
return openvdb::math::HJWENO5_BIAS;
}
BLI_assert_unreachable();
return openvdb::math::FIRST_BIAS;
}
static void level_set_morph(Volume &volume_a,
const Volume &volume_b,
const float factor,
const openvdb::math::BiasedGradientScheme spatial_scheme,
const openvdb::math::TemporalIntegrationScheme temporal_scheme,
const GeoNodeExecParams &params)
{
VolumeGrid *volume_grid_a = BKE_volume_grid_get_for_write(&volume_a, 0);
const VolumeGrid *volume_grid_b = BKE_volume_grid_get_for_read(&volume_b, 0);
if (ELEM(nullptr, volume_grid_a, volume_grid_b)) {
if (volume_grid_a == nullptr) {
params.error_message_add(NodeWarningType::Info, TIP_("Volume 1 is empty"));
}
if (volume_grid_b == nullptr) {
params.error_message_add(NodeWarningType::Info, TIP_("Volume 2 is empty"));
}
return;
}
openvdb::GridBase::Ptr grid_base_a = BKE_volume_grid_openvdb_for_write(&volume_a, volume_grid_a);
openvdb::GridBase::ConstPtr grid_base_b = BKE_volume_grid_openvdb_for_read(&volume_b,
volume_grid_b);
if (grid_base_a->getGridClass() != openvdb::GridClass::GRID_LEVEL_SET ||
grid_base_b->getGridClass() != openvdb::GridClass::GRID_LEVEL_SET) {
if (grid_base_a->getGridClass() != openvdb::GridClass::GRID_LEVEL_SET) {
params.error_message_add(NodeWarningType::Error, TIP_("Volume 1 is not a level set"));
}
if (grid_base_b->getGridClass() != openvdb::GridClass::GRID_LEVEL_SET) {
params.error_message_add(NodeWarningType::Error, TIP_("Volume 2 is not a level set"));
}
return;
}
const VolumeGridType grid_type_a = BKE_volume_grid_type(volume_grid_a);
const VolumeGridType grid_type_b = BKE_volume_grid_type(volume_grid_b);
if (grid_type_a != grid_type_b) {
params.error_message_add(NodeWarningType::Error, TIP_("Volume grid types do not match"));
return;
}
const bool needs_resample = grid_base_a->transform() != grid_base_b->transform();
bke::volume::to_static_type(grid_type_a, [&](auto dummy) {
using GridType = decltype(dummy);
if constexpr (std::is_same_v<GridType, openvdb::FloatGrid>) {
GridType &grid_a = static_cast<GridType &>(*grid_base_a);
const GridType &grid_b = static_cast<const GridType &>(*grid_base_b);
typename GridType::Ptr grid_b_resampled;
if (needs_resample) {
grid_b_resampled = GridType::create();
grid_b_resampled->setTransform(grid_base_a->constTransform().copy());
openvdb::tools::resampleToMatch<openvdb::tools::BoxSampler>(grid_b, *grid_b_resampled);
openvdb::tools::pruneLevelSet(grid_b_resampled->tree());
}
openvdb::tools::LevelSetMorphing<GridType> morph(
grid_a, needs_resample ? *grid_b_resampled : grid_b);
morph.setTemporalScheme(temporal_scheme);
morph.setSpatialScheme(spatial_scheme);
morph.advect(0.0f, factor / grid_a.voxelSize().length());
}
});
}
#endif /* WITH_OPENVDB */
static void geo_node_level_set_morph_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set_a = params.extract_input<GeometrySet>("Source");
#ifdef WITH_OPENVDB
GeometrySet geometry_set_b = params.extract_input<GeometrySet>("Target");
SCOPED_TIMER(__func__);
const NodeGeometryLevelSetMorph &storage =
*(const NodeGeometryLevelSetMorph *)params.node().storage;
const GeometryNodeLevelSetSpatialScheme spatial_scheme = (GeometryNodeLevelSetSpatialScheme)
storage.spatial_scheme;
const GeometryNodeLevelSetTemporalScheme temporal_scheme = (GeometryNodeLevelSetTemporalScheme)
storage.temporal_scheme;
Volume *volume_a = geometry_set_a.get_volume_for_write();
const Volume *volume_b = geometry_set_b.get_volume_for_read();
if (volume_a == nullptr || volume_b == nullptr) {
params.set_output("Result", std::move(geometry_set_a));
return;
}
const Main *bmain = DEG_get_bmain(params.depsgraph());
BKE_volume_load(volume_a, bmain);
BKE_volume_load(volume_b, bmain);
level_set_morph(*volume_a,
*volume_b,
params.extract_input<float>("Factor"),
spatial_scheme_to_openvdb(spatial_scheme),
temporal_scheme_to_openvdb(temporal_scheme),
params);
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
#endif
params.set_output("Result", std::move(geometry_set_a));
}
} // namespace blender::nodes
void register_node_type_geo_level_set_morph()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_LEVEL_SET_MORPH, "Level Set Morph", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_level_set_morph_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_level_set_morph_exec;
node_type_storage(
&ntype, "NodeGeometryLevelSetMorph", node_free_standard_storage, node_copy_standard_storage);
node_type_init(&ntype, blender::nodes::geo_node_level_set_morph_init);
ntype.draw_buttons = blender::nodes::geo_node_level_set_morph_layout;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,108 @@
/*
* 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.
*/
#ifdef WITH_OPENVDB
# include <openvdb/tools/LevelSetPlatonic.h>
#endif
#include "BKE_lib_id.h"
#include "BKE_volume.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
namespace blender::nodes {
static void geo_node_level_set_primitive_platonic_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>("Size").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
b.add_input<decl::Vector>("Center").subtype(PROP_TRANSLATION);
b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
b.add_output<decl::Geometry>("Level Set");
}
static void geo_node_level_set_primitive_platonic_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "shape", 0, "", ICON_NONE);
}
static void geo_node_level_set_primitive_platonic_init(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryLevelSetPlatonic *data = (NodeGeometryLevelSetPlatonic *)MEM_callocN(
sizeof(NodeGeometryLevelSetPlatonic), __func__);
data->shape = GEO_NODE_PLATONIC_CUBE;
node->storage = data;
}
#ifdef WITH_OPENVDB
static Volume *level_set_primitive_platonic(const NodeGeometryPlatonicShape shape,
GeoNodeExecParams &params)
{
Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr);
BKE_volume_init_grids(volume);
const float3 center = params.get_input<float3>("Center");
openvdb::FloatGrid::Ptr grid = openvdb::tools::createLevelSetPlatonic<openvdb::FloatGrid>(
static_cast<int>(shape),
params.extract_input<float>("Size"),
openvdb::Vec3f(center.x, center.y, center.z),
params.extract_input<float>("Voxel Size"));
BKE_volume_grid_add_vdb(volume, "level_set", std::move(grid));
return volume;
}
#endif /* WITH_OPENVDB */
static void geo_node_level_set_primitive_platonic_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
const NodeGeometryLevelSetPlatonic &data =
*(const NodeGeometryLevelSetPlatonic *)params.node().storage;
const NodeGeometryPlatonicShape shape = (NodeGeometryPlatonicShape)data.shape;
Volume *volume = level_set_primitive_platonic(shape, params);
params.set_output("Level Set", GeometrySet::create_with_volume(volume));
#else
params.set_output("Level Set", GeometrySet());
#endif
}
} // namespace blender::nodes
void register_node_type_geo_level_set_primitive_platonic()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_LEVEL_SET_PRIMITIVE_PLATONIC, "Level Set Platonic", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_level_set_primitive_platonic_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_level_set_primitive_platonic_exec;
node_type_storage(&ntype,
"NodeGeometryLevelSetPlatonic",
node_free_standard_storage,
node_copy_standard_storage);
node_type_init(&ntype, blender::nodes::geo_node_level_set_primitive_platonic_init);
ntype.draw_buttons = blender::nodes::geo_node_level_set_primitive_platonic_layout;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,83 @@
/*
* 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.
*/
#ifdef WITH_OPENVDB
# include <openvdb/tools/LevelSetSphere.h>
#endif
#include "BKE_lib_id.h"
#include "BKE_volume.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "DEG_depsgraph_query.h"
#include "node_geometry_util.hh"
namespace blender::nodes {
static void geo_node_level_set_primitive_sphere_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
b.add_input<decl::Vector>("Center").subtype(PROP_TRANSLATION);
b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
b.add_output<decl::Geometry>("Level Set");
}
#ifdef WITH_OPENVDB
static Volume *level_set_primitive_sphere(GeoNodeExecParams &params)
{
Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr);
BKE_volume_init_grids(volume);
const float3 center = params.get_input<float3>("Center");
openvdb::FloatGrid::Ptr grid = openvdb::tools::createLevelSetSphere<openvdb::FloatGrid>(
params.extract_input<float>("Radius"),
openvdb::Vec3f(center.x, center.y, center.z),
params.extract_input<float>("Voxel Size"));
BKE_volume_grid_add_vdb(volume, "level_set", std::move(grid));
return volume;
}
#endif /* WITH_OPENVDB */
static void geo_node_level_set_primitive_sphere_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
Volume *volume = level_set_primitive_sphere(params);
params.set_output("Level Set", GeometrySet::create_with_volume(volume));
#else
params.set_output("Level Set", GeometrySet());
#endif
}
} // namespace blender::nodes
void register_node_type_geo_level_set_primitive_sphere()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_LEVEL_SET_PRIMITIVE_SPHERE, "Level Set Sphere", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_level_set_primitive_sphere_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_level_set_primitive_sphere_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,106 @@
/*
* 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.
*/
#ifdef WITH_OPENVDB
# include <openvdb/tools/LevelSetUtil.h>
#endif
#include "BKE_volume.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "DEG_depsgraph_query.h"
#include "node_geometry_util.hh"
namespace blender::nodes {
static void geo_node_level_set_to_fog_volume_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Level Set");
b.add_input<decl::Float>("Density").default_value(1.0f).min(0.0f);
b.add_output<decl::Geometry>("Fog Volume");
}
#ifdef WITH_OPENVDB
static void level_set_to_fog_volume(Volume &volume, const GeoNodeExecParams &params)
{
VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(&volume, 0);
if (volume_grid == nullptr) {
params.error_message_add(NodeWarningType::Error, TIP_("Volume is empty"));
return;
}
openvdb::GridBase::Ptr grid_base = BKE_volume_grid_openvdb_for_write(&volume, volume_grid);
if (grid_base->getGridClass() != openvdb::GridClass::GRID_LEVEL_SET) {
params.error_message_add(NodeWarningType::Error, TIP_("Volume is not a level set"));
}
bke::volume::to_static_type(BKE_volume_grid_type(volume_grid), [&](auto dummy) {
using GridType = decltype(dummy);
if constexpr (std::is_same_v<GridType, openvdb::FloatGrid>) {
GridType &grid = static_cast<GridType &>(*grid_base);
openvdb::tools::sdfToFogVolume(grid);
const float density = params.get_input<float>("Density");
if (density != 1.0f) {
openvdb::tools::foreach (grid.beginValueOn(),
[&](const openvdb::FloatGrid::ValueOnIter &iter) {
iter.modifyValue([&](float &value) { value *= density; });
});
}
}
});
}
#endif /* WITH_OPENVDB */
static void geo_node_level_set_to_fog_volume_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Level Set");
#ifdef WITH_OPENVDB
Volume *volume = geometry_set.get_volume_for_write();
const Main *bmain = DEG_get_bmain(params.depsgraph());
BKE_volume_load(volume, bmain);
if (volume == nullptr) {
params.set_output("Level Set", std::move(geometry_set));
return;
}
level_set_to_fog_volume(*volume, params);
#endif
params.set_output("Fog Volume", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_level_set_to_fog_volume()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_LEVEL_SET_TO_FOG_VOLUME, "Level Set to Fog Volume", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_level_set_to_fog_volume_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_level_set_to_fog_volume_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,114 @@
/*
* 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.
*/
#ifdef WITH_OPENVDB
# include <openvdb/tools/LevelSetUtil.h>
#endif
#include "BKE_lib_id.h"
#include "BKE_volume.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "DEG_depsgraph_query.h"
#include "node_geometry_util.hh"
namespace blender::nodes {
static void geo_node_level_set_to_mask_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Level Set");
b.add_output<decl::Geometry>("Mask Volume");
}
#ifdef WITH_OPENVDB
static Volume *level_set_to_mask(const Volume &volume, const GeoNodeExecParams &params)
{
if (BKE_volume_num_grids(&volume) == 0) {
params.error_message_add(NodeWarningType::Error, TIP_("Volume is empty"));
return nullptr;
}
Volume *mask_volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr);
BKE_volume_init_grids(mask_volume);
for (const int i : IndexRange(BKE_volume_num_grids(&volume))) {
const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(&volume, i);
openvdb::GridBase::ConstPtr grid_base = BKE_volume_grid_openvdb_for_read(&volume, volume_grid);
if (grid_base->getGridClass() != openvdb::GridClass::GRID_LEVEL_SET) {
params.error_message_add(NodeWarningType::Error,
std::string(TIP_("Volume grid is not a level set: ")) +
BKE_volume_grid_name(volume_grid));
}
bke::volume::to_static_type(BKE_volume_grid_type(volume_grid), [&](auto dummy) {
using GridType = decltype(dummy);
if constexpr (std::is_same_v<GridType, openvdb::FloatGrid>) {
const GridType &grid = static_cast<const GridType &>(*grid_base);
openvdb::BoolGrid::Ptr mask_grid = openvdb::tools::sdfInteriorMask(grid);
BKE_volume_grid_add_vdb(*mask_volume, "mask_" + i, std::move(mask_grid));
}
});
}
return mask_volume;
}
#endif /* WITH_OPENVDB */
static void geo_node_level_set_to_mask_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
GeometrySet geometry_set = params.extract_input<GeometrySet>("Level Set");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
const Volume *volume = geometry_set.get_volume_for_read();
if (volume == nullptr) {
params.set_output("Mask Volume", std::move(geometry_set));
return;
}
const Main *bmain = DEG_get_bmain(params.depsgraph());
BKE_volume_load(volume, bmain);
Volume *mask_volume = level_set_to_mask(*volume, params);
geometry_set.replace_volume(mask_volume);
});
params.set_output("Mask Volume", geometry_set);
#else
params.set_output("Mask Volume", GeometrySet());
#endif
}
} // namespace blender::nodes
void register_node_type_geo_level_set_to_mask()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_LEVEL_SET_TO_MASK, "Level Set to Mask", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_level_set_to_mask_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_level_set_to_mask_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,158 @@
/*
* 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.
*/
#ifdef WITH_OPENVDB
# include <openvdb/tools/GridTransformer.h>
# include <openvdb/tools/VolumeToMesh.h>
#endif
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_volume.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
using blender::bke::GeometryInstanceGroup;
namespace blender::nodes {
static void geo_node_mesh_to_level_set_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Mesh");
b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
b.add_output<decl::Geometry>("Level Set");
}
#ifdef WITH_OPENVDB
static openvdb::FloatGrid::Ptr meshes_to_level_set_grid(
const Span<GeometryInstanceGroup> set_groups, const float voxel_size)
{
const float voxel_size_inv = 1.0f / voxel_size;
/* Count the vertex and triangle size of all input meshes to avoid reallocating the vectors. */
int vert_size = 0;
int tri_size = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const Mesh &mesh = *set.get_mesh_for_read();
vert_size += mesh.totvert * set_group.transforms.size();
tri_size += BKE_mesh_runtime_looptri_len(&mesh) * set_group.transforms.size();
}
int vert_index = 0;
int tri_index = 0;
std::vector<openvdb::Vec3s> positions(vert_size);
std::vector<openvdb::Vec3I> triangles(tri_size);
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const Mesh &mesh = *set.get_mesh_for_read();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
const Span<MLoop> mloop{mesh.mloop, mesh.totloop};
for (const float4x4 &transform : set_group.transforms) {
const int vert_offset = vert_index;
for (const int i : IndexRange(mesh.totvert)) {
const float3 co = transform * float3(mesh.mvert[i].co);
/* Better align generated grid with source points. */
const float3 index_co = co * voxel_size_inv - float3(0.5f);
positions[vert_index++] = openvdb::Vec3s(index_co.x, index_co.y, index_co.z);
}
for (const int i : IndexRange(looptris.size())) {
const MLoopTri &loop_tri = looptris[i];
triangles[tri_index++] = openvdb::Vec3I(vert_offset + mloop[loop_tri.tri[0]].v,
vert_offset + mloop[loop_tri.tri[1]].v,
vert_offset + mloop[loop_tri.tri[2]].v);
}
}
}
openvdb::FloatGrid::Ptr grid
= openvdb::tools::meshToLevelSet<openvdb::FloatGrid>({}, positions, triangles);
grid->transform().postScale(voxel_size);
return grid;
}
static Volume *meshes_to_level_set_volume(const Span<GeometryInstanceGroup> set_groups,
const float voxel_size)
{
Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr);
BKE_volume_init_grids(volume);
openvdb::FloatGrid::Ptr new_grid = meshes_to_level_set_grid(set_groups, voxel_size);
BKE_volume_grid_add_vdb(volume, "level_set", std::move(new_grid));
return volume;
}
#endif /* WITH_OPENVDB */
static void geo_node_mesh_to_level_set_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
Vector<GeometryInstanceGroup> set_groups;
bke::geometry_set_gather_instances(geometry_set, set_groups);
if (set_groups.is_empty()) {
params.set_output("Level Set", GeometrySet());
return;
}
/* Remove any set inputs that don't contain a mesh, to avoid checking later on. */
for (int i = set_groups.size() - 1; i >= 0; i--) {
const GeometrySet &set = set_groups[i].geometry_set;
if (!set.has_mesh()) {
set_groups.remove_and_reorder(i);
}
}
if (set_groups.is_empty()) {
params.set_output("Level Set", GeometrySet());
return;
}
#ifdef WITH_OPENVDB
const float voxel_size = params.get_input<float>("Voxel Size");
Volume *volume = meshes_to_level_set_volume(set_groups, voxel_size);
params.set_output("Level Set", GeometrySet::create_with_volume(volume));
#else
params.set_output("Level Set", GeometrySet());
#endif
}
} // namespace blender::nodes
void register_node_type_geo_mesh_to_level_set()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_MESH_TO_LEVEL_SET, "Mesh to Level Set", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_mesh_to_level_set_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_level_set_exec;
nodeRegisterType(&ntype);
}

View File

@@ -32,8 +32,7 @@ namespace blender::nodes {
static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Points"));
b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f);
b.add_input<decl::Geometry>(N_("Geometry"));
b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f);
b.add_input<decl::Float>(N_("Radius"))
@@ -101,12 +100,10 @@ struct ParticleList {
} // namespace
static openvdb::FloatGrid::Ptr generate_volume_from_points(const Span<float3> positions,
const Span<float> radii,
const float density)
const Span<float> radii)
{
/* Create a new grid that will be filled. #ParticlesToLevelSet requires the background value to
* be positive. It will be set to zero later on. */
openvdb::FloatGrid::Ptr new_grid = openvdb::FloatGrid::create(1.0f);
/* Create a new grid that will be filled. */
openvdb::FloatGrid::Ptr new_grid = openvdb::createLevelSet<openvdb::FloatGrid>();
/* Create a narrow-band level set grid based on the positions and radii. */
openvdb::tools::ParticlesToLevelSet op{*new_grid};
@@ -117,15 +114,6 @@ static openvdb::FloatGrid::Ptr generate_volume_from_points(const Span<float3> po
op.rasterizeSpheres(particles);
op.finalize();
/* Convert the level set to a fog volume. This also sets the background value to zero. Inside the
* fog there will be a density of 1. */
openvdb::tools::sdfToFogVolume(*new_grid);
/* Take the desired density into account. */
openvdb::tools::foreach (new_grid->beginValueOn(),
[&](const openvdb::FloatGrid::ValueOnIter &iter) {
iter.modifyValue([&](float &value) { value *= density; });
});
return new_grid;
}
@@ -222,14 +210,13 @@ static void initialize_volume_component_from_points(GeoNodeExecParams &params,
Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr);
BKE_volume_init_grids(volume);
const float density = params.get_input<float>("Density");
convert_to_grid_index_space(voxel_size, positions, radii);
openvdb::FloatGrid::Ptr new_grid = generate_volume_from_points(positions, radii, density);
openvdb::FloatGrid::Ptr new_grid = generate_volume_from_points(positions, radii);
new_grid->transform().postScale(voxel_size);
BKE_volume_grid_add_vdb(*volume, "density", std::move(new_grid));
BKE_volume_grid_add_vdb(*volume, "level_set", std::move(new_grid));
r_geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES});
r_geometry_set.replace_volume(volume);
VolumeComponent &volume_component = r_geometry_set.get_component_for_write<VolumeComponent>();
volume_component.replace(volume);
}
#endif
@@ -256,7 +243,7 @@ void register_node_type_geo_points_to_volume()
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0);
&ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Level Set", NODE_CLASS_GEOMETRY, 0);
node_type_storage(&ntype,
"NodeGeometryPointsToVolume",
node_free_standard_storage,

View File

@@ -16,9 +16,15 @@
#include "DNA_mesh_types.h"
#ifdef WITH_OPENVDB
# include <openvdb/tools/RayIntersector.h>
# include <openvdb/tools/VolumeToMesh.h>
#endif
#include "BKE_attribute_math.hh"
#include "BKE_bvhutils.h"
#include "BKE_mesh_sample.hh"
#include "BKE_volume.h"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -119,6 +125,72 @@ static eAttributeMapMode get_map_mode(GeometryNodeRaycastMapMode map_mode)
}
}
#ifdef WITH_OPENVDB
static const openvdb::FloatGrid *geometry_retrieve_level_set(const GeometrySet &geometry)
{
const Volume *volume = geometry.get_volume_for_read();
if (volume == nullptr) {
return {};
}
const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, 0);
if (volume_grid == nullptr) {
return {};
}
openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
if (grid->empty()) {
return {};
}
if (grid->getGridClass() != openvdb::GridClass::GRID_LEVEL_SET) {
return {};
}
if (grid->isType<openvdb::FloatGrid>()) {
return {};
}
return static_cast<const openvdb::FloatGrid *>(grid.get());
}
static void raycast_to_level_set(IndexMask mask,
const openvdb::FloatGrid &level_set,
const VArray<float3> &ray_origins,
const VArray<float3> &ray_directions,
const VArray<float> &ray_lengths,
const MutableSpan<bool> r_hit,
const MutableSpan<float3> r_hit_positions,
const MutableSpan<float3> r_hit_normals,
const MutableSpan<float> r_hit_distances)
{
openvdb::tools::LevelSetRayIntersector intersector(level_set);
for (const int i : mask) {
const openvdb::math::Vec3s origin(ray_origins[i].x, ray_origins[i].y, ray_origins[i].z);
const openvdb::math::Vec3s dir(ray_directions[i].x, ray_directions[i].y, ray_directions[i].z);
const openvdb::math::Ray<double> ray(
origin,
dir.unitSafe(),
std::numeric_limits<double>::epsilon(),
std::max(std::numeric_limits<float>::epsilon(), ray_lengths[i]));
openvdb::math::Vec3d hit_position(0);
openvdb::math::Vec3d hit_normal(0);
const bool hit = intersector.intersectsWS(ray, hit_position, hit_normal);
if (!r_hit.is_empty()) {
r_hit[i] = hit;
}
if (!r_hit_positions.is_empty()) {
r_hit_positions[i] = float3(hit_position.x(), hit_position.y(), hit_position.z());
}
if (!r_hit_normals.is_empty()) {
r_hit_normals[i] = float3(hit_normal.x(), hit_normal.y(), hit_normal.z());
}
if (!r_hit_distances.is_empty()) {
r_hit_distances[i] = float3::distance(r_hit_positions[i], ray_origins[i]);
}
}
}
#endif
static void raycast_to_mesh(IndexMask mask,
const Mesh &mesh,
const VArray<float3> &ray_origins,
@@ -252,30 +324,48 @@ class RaycastFunction : public fn::MultiFunction {
hit_indices.reinitialize(mask.min_array_size());
}
BLI_assert(target_.has_mesh());
const Mesh &mesh = *target_.get_mesh_for_read();
const VArray<float3> &origins = params.readonly_single_input<float3>(0, "Source Position");
const VArray<float3> &directions = params.readonly_single_input<float3>(1, "Ray Direction");
const VArray<float> &lengths = params.readonly_single_input<float>(2, "Ray Length");
MutableSpan<bool> is_hit = params.uninitialized_single_output_if_required<bool>(3, "Is Hit");
MutableSpan<float3> hit_normal = params.uninitialized_single_output_if_required<float3>(
5, "Hit Normal");
MutableSpan<float> hit_distance = params.uninitialized_single_output_if_required<float>(
6, "Distance");
int hit_count = 0;
raycast_to_mesh(mask,
mesh,
params.readonly_single_input<float3>(0, "Source Position"),
params.readonly_single_input<float3>(1, "Ray Direction"),
params.readonly_single_input<float>(2, "Ray Length"),
params.uninitialized_single_output_if_required<bool>(3, "Is Hit"),
hit_indices,
hit_positions,
params.uninitialized_single_output_if_required<float3>(5, "Hit Normal"),
params.uninitialized_single_output_if_required<float>(6, "Distance"),
hit_count);
const Mesh *mesh = target_.get_mesh_for_read();
if (mesh) {
hit_count = 0;
raycast_to_mesh(mask,
*mesh,
origins,
directions,
lengths,
is_hit,
hit_indices,
hit_positions,
hit_normal,
hit_distance,
hit_count);
}
if (target_data_) {
IndexMask hit_mask;
Vector<int64_t> hit_mask_indices;
#ifdef WITH_OPENVDB
const openvdb::FloatGrid *level_set = geometry_retrieve_level_set(target_);
const bool has_level_set = bool(level_set);
#else
const bool has_level_set = false;
#endif
IndexMask hit_mask;
Vector<int64_t> hit_mask_indices;
if (target_data_ || has_level_set) {
if (hit_count < mask.size()) {
/* Not all rays hit the target. Create a corrected mask to avoid transferring attribute
* data to invalid indices. An alternative would be handling -1 indices in a separate case
* in #MeshAttributeInterpolator, but since it already has an IndexMask in its constructor,
* it's simpler to use that. */
* data to invalid indices. An alternative would be handling -1 indices in a separate
* case in #MeshAttributeInterpolator, but since it already has an IndexMask in its
* constructor, it's simpler to use that. */
hit_mask_indices.reserve(hit_count);
for (const int64_t i : mask) {
if (hit_indices[i] != -1) {
@@ -287,14 +377,30 @@ class RaycastFunction : public fn::MultiFunction {
else {
hit_mask = mask;
}
}
if (target_data_) {
GMutableSpan result = params.uninitialized_single_output_if_required(7, "Attribute");
if (!result.is_empty()) {
MeshAttributeInterpolator interp(&mesh, hit_mask, hit_positions, hit_indices);
MeshAttributeInterpolator interp(mesh, hit_mask, hit_positions, hit_indices);
result.type().fill_assign_indices(result.type().default_value(), result.data(), mask);
interp.sample_data(*target_data_, domain_, get_map_mode(mapping_), result);
}
}
#ifdef WITH_OPENVDB
if (has_level_set) {
raycast_to_level_set(hit_mask,
*level_set,
origins,
directions,
lengths,
is_hit,
hit_positions,
hit_normal,
hit_distance);
}
#endif
}
private:

View File

@@ -0,0 +1,294 @@
/*
* 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.
*/
#include "DEG_depsgraph_query.h"
#ifdef WITH_OPENVDB
# include <openvdb/tools/GridTransformer.h>
# include <openvdb/tools/VolumeToMesh.h>
#endif
#include "BKE_lib_id.h"
#include "BKE_volume.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "FN_generic_array.hh"
#include "NOD_type_conversions.hh"
#include "node_geometry_util.hh"
namespace blender::nodes {
static void geo_node_sample_volume_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Volume"))
.only_realized_data()
.supported_type(GEO_COMPONENT_TYPE_VOLUME);
b.add_input<decl::Vector>(N_("Position")).implicit_field();
b.add_output<decl::Vector>(N_("Value")).dependent_field({1, 2, 3, 4, 5, 6});
b.add_output<decl::Float>(N_("Value"), "Value_001").dependent_field({1, 2, 3, 4, 5, 6});
b.add_output<decl::Bool>(N_("Value"), "Value_002").dependent_field({1, 2, 3, 4, 5, 6});
b.add_output<decl::Int>(N_("Value"), "Value_003").dependent_field({1, 2, 3, 4, 5, 6});
}
static void geo_node_sample_volume_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE);
}
static void geo_node_sample_volume_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometrySampleVolume *data = (NodeGeometrySampleVolume *)MEM_callocN(
sizeof(NodeGeometrySampleVolume), __func__);
data->interpolation = GEO_NODE_VOLUME_SAMPLE_LINEAR;
data->data_type = CD_PROP_FLOAT;
node->storage = data;
}
static void geo_node_sample_volume_update(bNodeTree *UNUSED(ntree), bNode *node)
{
const NodeGeometrySampleVolume &data = *(const NodeGeometrySampleVolume *)node->storage;
const CustomDataType data_type = static_cast<CustomDataType>(data.data_type);
bNodeSocket *out_socket_vector = (bNodeSocket *)BLI_findlink(&node->outputs, 4);
bNodeSocket *out_socket_float = out_socket_vector->next;
bNodeSocket *out_socket_color4f = out_socket_float->next;
bNodeSocket *out_socket_boolean = out_socket_color4f->next;
bNodeSocket *out_socket_int32 = out_socket_boolean->next;
nodeSetSocketAvailability(out_socket_vector, data_type == CD_PROP_FLOAT3);
nodeSetSocketAvailability(out_socket_float, data_type == CD_PROP_FLOAT);
nodeSetSocketAvailability(out_socket_color4f, data_type == CD_PROP_COLOR);
nodeSetSocketAvailability(out_socket_boolean, data_type == CD_PROP_BOOL);
nodeSetSocketAvailability(out_socket_int32, data_type == CD_PROP_INT32);
}
#ifdef WITH_OPENVDB
using openvdb::GridBase;
static const CPPType *grid_type_to_sample_type(GridBase::ConstPtr grid)
{
switch (BKE_volume_grid_type_openvdb(*grid)) {
case VOLUME_GRID_MASK:
case VOLUME_GRID_BOOLEAN:
return &CPPType::get<bool>();
case VOLUME_GRID_FLOAT:
return &CPPType::get<float>();
case VOLUME_GRID_INT:
return &CPPType::get<int>();
case VOLUME_GRID_VECTOR_FLOAT:
return &CPPType::get<float3>();
case VOLUME_GRID_UNKNOWN:
case VOLUME_GRID_INT64:
case VOLUME_GRID_STRING:
case VOLUME_GRID_VECTOR_DOUBLE:
case VOLUME_GRID_DOUBLE:
case VOLUME_GRID_VECTOR_INT:
case VOLUME_GRID_POINTS:
return nullptr;
}
BLI_assert_unreachable();
return nullptr;
}
static void sample_grid(GridBase::ConstPtr base_grid,
IndexMask mask,
Span<float3> positions,
GMutableSpan result)
{
bke::volume::to_static_type(BKE_volume_grid_type_openvdb(*base_grid), [&](auto dummy) {
using GridType = decltype(dummy);
const GridType &grid = static_cast<const GridType &>(*base_grid);
openvdb::tools::GridSampler<GridType, openvdb::tools::BoxSampler> sampler(grid);
if constexpr (std::is_same_v<GridType, openvdb::FloatGrid>) {
MutableSpan<float> dst = result.typed<float>();
for (const int64_t i : mask) {
dst[i] = sampler.wsSample({positions[i].x, positions[i].y, positions[i].z});
}
}
else if constexpr (std::is_same_v<GridType, openvdb::Int32Grid>) {
MutableSpan<int> dst = result.typed<int>();
for (const int64_t i : mask) {
dst[i] = sampler.wsSample({positions[i].x, positions[i].y, positions[i].z});
}
}
else if constexpr (std::is_same_v<GridType, openvdb::BoolGrid> ||
std::is_same_v<GridType, openvdb::MaskGrid>) {
MutableSpan<bool> dst = result.typed<bool>();
for (const int64_t i : mask) {
dst[i] = sampler.wsSample({positions[i].x, positions[i].y, positions[i].z});
}
}
else if constexpr (std::is_same_v<GridType, openvdb::Vec3fGrid>) {
MutableSpan<float3> dst = result.typed<float3>();
for (const int64_t i : mask) {
const openvdb::Vec3f value = sampler.wsSample(
{positions[i].x, positions[i].y, positions[i].z});
dst[i] = float3(value.x(), value.y(), value.z());
}
}
});
}
static void combine_data_with_result(GSpan data, GMutableSpan result)
{
BLI_assert(data.type() == result.type());
}
class SampleVolumeFunction : public fn::MultiFunction {
private:
GeometrySet geometry_set_;
const CPPType *type_;
fn::MFSignature signature_;
public:
SampleVolumeFunction(GeometrySet geometry_set, const CPPType &type)
: geometry_set_(std::move(geometry_set)), type_(&type)
{
static fn::MFSignature signature = create_signature();
signature_ = this->create_signature();
this->set_signature(&signature_);
}
fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Sample Volume"};
signature.single_input<float3>("Position");
signature.single_output("Value", *type_);
return signature.build();
}
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position");
const VArray_Span<float3> positions_span{positions};
GMutableSpan result = params.uninitialized_single_output(1, "Value");
const VolumeComponent *component = geometry_set_.get_component_for_read<VolumeComponent>();
const Volume *volume = component->get_for_read();
for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
const CPPType *grid_type = grid_type_to_sample_type(grid);
if (grid_type == nullptr) {
continue;
}
if (grid_type == type_) {
sample_grid(grid, mask, positions_span, result);
}
else {
fn::GArray<> data_grid_type{*grid_type, mask.min_array_size()};
sample_grid(grid, mask, positions_span, data_grid_type.as_mutable_span());
fn::GArray<> data_converted{*grid_type, mask.min_array_size()};
const DataTypeConversions &conversions = get_implicit_type_conversions();
conversions.try_convert(data_grid_type.as_span(), data_converted.as_mutable_span(), mask);
combine_data_with_result(data_converted, result);
}
}
}
};
#endif
static void geo_node_sample_volume_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
const bNode &node = params.node();
const NodeGeometrySampleVolume &storage = *(const NodeGeometrySampleVolume *)node.storage;
const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
auto return_default = [&]() {
switch (data_type) {
case CD_PROP_FLOAT3:
params.set_output("Value", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
break;
case CD_PROP_FLOAT:
params.set_output("Value_001", fn::make_constant_field<float>(0.0f));
break;
case CD_PROP_BOOL:
params.set_output("Value_002", fn::make_constant_field<bool>(false));
break;
case CD_PROP_INT32:
params.set_output("Value_003", fn::make_constant_field<int>(0));
break;
default:
BLI_assert_unreachable();
}
};
#ifdef WITH_OPENVDB
const VolumeComponent *component = geometry_set.get_component_for_read<VolumeComponent>();
if (component == nullptr) {
return return_default();
}
const Volume *volume = component->get_for_read();
if (volume == nullptr) {
return return_default();
}
const Main *bmain = DEG_get_bmain(params.depsgraph());
BKE_volume_load(volume, bmain);
auto sample_fn = std::make_unique<SampleVolumeFunction>(
std::move(geometry_set), bke::custom_data_type_to_cpp_type(data_type));
auto sample_op = std::make_shared<FieldOperation>(
FieldOperation(std::move(sample_fn), {params.get_input<Field<float3>>("Position")}));
switch (data_type) {
case CD_PROP_FLOAT3:
params.set_output("Value", Field<float3>(sample_op));
break;
case CD_PROP_FLOAT:
params.set_output("Value_001", Field<float>(sample_op));
break;
case CD_PROP_BOOL:
params.set_output("Value_002", Field<bool>(sample_op));
break;
case CD_PROP_INT32:
params.set_output("Value_003", Field<int>(sample_op));
break;
default:
BLI_assert_unreachable();
}
#else
#endif
}
} // namespace blender::nodes
void register_node_type_geo_sample_volume()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_SAMPLE_VOLUME, "Sample Volume", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_sample_volume_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_sample_volume_exec;
nodeRegisterType(&ntype);
}

View File

@@ -139,7 +139,7 @@ static void transform_volume(Volume &volume, const float4x4 &transform, const De
for (const int i : IndexRange(num_grids)) {
VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(&volume, i);
openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, volume_grid, false);
openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, volume_grid);
openvdb::math::Transform &grid_transform = grid->transform();
grid_transform.postMult(vdb_matrix_d);
}

View File

@@ -346,4 +346,25 @@ fn::GVMutableArrayPtr DataTypeConversions::try_convert(fn::GVMutableArrayPtr var
std::move(varray), to_type, *this);
}
void DataTypeConversions::try_convert(fn::GSpan src, fn::GMutableSpan dst, IndexMask mask) const
{
BLI_assert(src.size() == dst.size());
const CPPType &from_type = src.type();
const CPPType &to_type = dst.type();
if (from_type == to_type) {
return dst.type().copy_assign_n(src.data(), dst.data(), src.size());
}
const fn::MultiFunction *fn = this->get_conversion_multi_function(
fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type));
if (fn == nullptr) {
return to_type.fill_assign_n(to_type.default_value(), dst.data(), dst.size());
}
fn::MFParamsBuilder mf_params{*fn, &mask};
fn::MFContextBuilder mf_context;
mf_params.add_readonly_single_input(src);
mf_params.add_uninitialized_single_output(dst);
fn->call(mask, mf_params, mf_context);
}
} // namespace blender::nodes