Compare commits
42 Commits
tmp-volume
...
geometry-n
Author | SHA1 | Date | |
---|---|---|---|
7bea1e1223 | |||
25b462de8e | |||
c107387a18 | |||
92ad540026 | |||
47d0b2f14e | |||
05f500580b | |||
71b0721ba5 | |||
be5bd5f7b4 | |||
5890953bad | |||
98223022e6 | |||
f6b162eca0 | |||
51e51caf54 | |||
9547f110eb | |||
572dd65614 | |||
8242567574 | |||
cedbf38588 | |||
00541898a1 | |||
86ea25aa0d | |||
64a7957321 | |||
aa0b4be77f | |||
50c8114590 | |||
8ef80ad9fd | |||
e47c652bbf | |||
7ef0e7ded7 | |||
ff6df9a22d | |||
e537bb57a3 | |||
990e0fd319 | |||
a3dcbcb6e7 | |||
e0a833bbbf | |||
5bbba1984d | |||
e9f6482be7 | |||
e43e8f09bb | |||
10ed118edb | |||
a079d7db28 | |||
c8c64d4081 | |||
2399f66468 | |||
53f0047f06 | |||
fb03f87005 | |||
4f2642cada | |||
b7225df456 | |||
5987b4cc68 | |||
aa48e1cd66 |
@@ -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=[
|
||||
|
@@ -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;
|
||||
|
@@ -46,6 +46,7 @@
|
||||
*/
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
|
@@ -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. */
|
||||
|
@@ -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};
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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", "")
|
||||
|
@@ -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();
|
||||
|
@@ -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 ¶ms,
|
||||
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 ¶ms)
|
||||
@@ -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. */
|
||||
|
@@ -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);
|
||||
|
@@ -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 ¶ms)
|
||||
{
|
||||
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);
|
||||
}
|
165
source/blender/nodes/geometry/nodes/node_geo_level_set_filter.cc
Normal file
165
source/blender/nodes/geometry/nodes/node_geo_level_set_filter.cc
Normal 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 ¶ms)
|
||||
{
|
||||
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);
|
||||
}
|
209
source/blender/nodes/geometry/nodes/node_geo_level_set_morph.cc
Normal file
209
source/blender/nodes/geometry/nodes/node_geo_level_set_morph.cc
Normal 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 ¶ms)
|
||||
{
|
||||
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);
|
||||
}
|
@@ -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 ¶ms)
|
||||
{
|
||||
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);
|
||||
}
|
@@ -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 ¶ms)
|
||||
{
|
||||
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);
|
||||
}
|
@@ -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 ¶ms)
|
||||
{
|
||||
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);
|
||||
}
|
@@ -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 ¶ms)
|
||||
{
|
||||
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);
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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 ¶ms,
|
||||
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,
|
||||
|
@@ -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:
|
||||
|
294
source/blender/nodes/geometry/nodes/node_geo_sample_volume.cc
Normal file
294
source/blender/nodes/geometry/nodes/node_geo_sample_volume.cc
Normal 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);
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user