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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/nodes')
-rw-r--r--source/blender/nodes/CMakeLists.txt9
-rw-r--r--source/blender/nodes/NOD_geometry.h2
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh16
-rw-r--r--source/blender/nodes/NOD_static_types.h2
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc172
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc107
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc13
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc190
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc29
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc259
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc50
-rw-r--r--source/blender/nodes/intern/node_tree_multi_function.cc58
16 files changed, 851 insertions, 97 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index ed4d658eb4f..405a8dcbf46 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -146,6 +146,7 @@ set(SRC
geometry/nodes/node_geo_attribute_fill.cc
geometry/nodes/node_geo_attribute_math.cc
geometry/nodes/node_geo_attribute_mix.cc
+ geometry/nodes/node_geo_attribute_sample_texture.cc
geometry/nodes/node_geo_attribute_randomize.cc
geometry/nodes/node_geo_attribute_vector_math.cc
geometry/nodes/node_geo_boolean.cc
@@ -159,6 +160,7 @@ set(SRC
geometry/nodes/node_geo_point_scale.cc
geometry/nodes/node_geo_point_separate.cc
geometry/nodes/node_geo_point_translate.cc
+ geometry/nodes/node_geo_points_to_volume.cc
geometry/nodes/node_geo_subdivision_surface.cc
geometry/nodes/node_geo_transform.cc
geometry/nodes/node_geo_triangulate.cc
@@ -366,4 +368,11 @@ if(WITH_OPENSUBDIV)
add_definitions(-DWITH_OPENSUBDIV)
endif()
+if(WITH_OPENVDB)
+ list(APPEND INC_SYS
+ ${OPENVDB_INCLUDE_DIRS}
+ )
+ add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS})
+endif()
+
blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 523d0cfa24d..d78f76e0b52 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -47,6 +47,8 @@ void register_node_type_geo_attribute_mix(void);
void register_node_type_geo_attribute_color_ramp(void);
void register_node_type_geo_point_rotate(void);
void register_node_type_geo_align_rotation_to_vector(void);
+void register_node_type_geo_sample_texture(void);
+void register_node_type_geo_points_to_volume(void);
#ifdef __cplusplus
}
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index f278d6b4107..454c9e96246 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -24,12 +24,16 @@
#include "DNA_node_types.h"
+struct Depsgraph;
+
namespace blender::nodes {
using bke::BooleanReadAttribute;
using bke::BooleanWriteAttribute;
using bke::Color4fReadAttribute;
using bke::Color4fWriteAttribute;
+using bke::Float2ReadAttribute;
+using bke::Float2WriteAttribute;
using bke::Float3ReadAttribute;
using bke::Float3WriteAttribute;
using bke::FloatReadAttribute;
@@ -54,18 +58,21 @@ class GeoNodeExecParams {
GValueMap<StringRef> &output_values_;
const PersistentDataHandleMap &handle_map_;
const Object *self_object_;
+ Depsgraph *depsgraph_;
public:
GeoNodeExecParams(const bNode &node,
GValueMap<StringRef> &input_values,
GValueMap<StringRef> &output_values,
const PersistentDataHandleMap &handle_map,
- const Object *self_object)
+ const Object *self_object,
+ Depsgraph *depsgraph)
: node_(node),
input_values_(input_values),
output_values_(output_values),
handle_map_(handle_map),
- self_object_(self_object)
+ self_object_(self_object),
+ depsgraph_(depsgraph)
{
}
@@ -163,6 +170,11 @@ class GeoNodeExecParams {
return self_object_;
}
+ Depsgraph *depsgraph() const
+ {
+ return depsgraph_;
+ }
+
/**
* Creates a read-only attribute based on node inputs. The method automatically detects which
* input with the given name is available.
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index e91b385a87e..cc2f6a294f2 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -289,6 +289,8 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_m
DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "")
DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, def_geo_attribute_sample_texture, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc
index 53af6073793..daaccf4450b 100644
--- a/source/blender/nodes/geometry/node_geometry_util.cc
+++ b/source/blender/nodes/geometry/node_geometry_util.cc
@@ -54,6 +54,8 @@ static int attribute_data_type_complexity(const CustomDataType data_type)
return 1;
case CD_PROP_FLOAT:
return 2;
+ case CD_PROP_FLOAT2:
+ return 3;
case CD_PROP_FLOAT3:
return 4;
case CD_PROP_COLOR:
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
index 20a2b2127c2..194b062021d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
@@ -51,7 +51,7 @@ static bNodeSocketTemplate geo_node_attribute_compare_out[] = {
static void geo_node_attribute_compare_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeAttributeCompare *data = (NodeAttributeCompare *)MEM_callocN(sizeof(NodeAttributeCompare),
- "attribute mix node");
+ __func__);
data->operation = NODE_FLOAT_COMPARE_GREATER_THAN;
data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
index f8ec9124db3..f3fc45fc1be 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
@@ -34,6 +34,8 @@ static bNodeSocketTemplate geo_node_attribute_math_in[] = {
{SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_STRING, N_("B")},
{SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_STRING, N_("C")},
+ {SOCK_FLOAT, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_STRING, N_("Result")},
{-1, ""},
};
@@ -51,45 +53,132 @@ static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node)
data->operation = NODE_MATH_ADD;
data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ data->input_type_c = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
node->storage = data;
}
+static bool operation_use_input_c(const NodeMathOperation operation)
+{
+ return ELEM(operation,
+ NODE_MATH_MULTIPLY_ADD,
+ NODE_MATH_SMOOTH_MIN,
+ NODE_MATH_SMOOTH_MAX,
+ NODE_MATH_WRAP,
+ NODE_MATH_COMPARE);
+}
+
+static bool operation_use_input_b(const NodeMathOperation operation)
+{
+ switch (operation) {
+ case NODE_MATH_ADD:
+ case NODE_MATH_SUBTRACT:
+ case NODE_MATH_MULTIPLY:
+ case NODE_MATH_DIVIDE:
+ case NODE_MATH_POWER:
+ case NODE_MATH_LOGARITHM:
+ case NODE_MATH_MINIMUM:
+ case NODE_MATH_MAXIMUM:
+ case NODE_MATH_LESS_THAN:
+ case NODE_MATH_GREATER_THAN:
+ case NODE_MATH_MODULO:
+ case NODE_MATH_ARCTAN2:
+ case NODE_MATH_SNAP:
+ case NODE_MATH_WRAP:
+ case NODE_MATH_COMPARE:
+ case NODE_MATH_MULTIPLY_ADD:
+ case NODE_MATH_PINGPONG:
+ case NODE_MATH_SMOOTH_MIN:
+ case NODE_MATH_SMOOTH_MAX:
+ return true;
+ case NODE_MATH_SINE:
+ case NODE_MATH_COSINE:
+ case NODE_MATH_TANGENT:
+ case NODE_MATH_ARCSINE:
+ case NODE_MATH_ARCCOSINE:
+ case NODE_MATH_ARCTANGENT:
+ case NODE_MATH_ROUND:
+ case NODE_MATH_ABSOLUTE:
+ case NODE_MATH_FLOOR:
+ case NODE_MATH_CEIL:
+ case NODE_MATH_FRACTION:
+ case NODE_MATH_SQRT:
+ case NODE_MATH_INV_SQRT:
+ case NODE_MATH_SIGN:
+ case NODE_MATH_EXPONENT:
+ case NODE_MATH_RADIANS:
+ case NODE_MATH_DEGREES:
+ case NODE_MATH_SINH:
+ case NODE_MATH_COSH:
+ case NODE_MATH_TANH:
+ case NODE_MATH_TRUNC:
+ return false;
+ }
+ BLI_assert(false);
+ return false;
+}
+
namespace blender::nodes {
static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeAttributeMath *node_storage = (NodeAttributeMath *)node->storage;
+ NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage;
+ NodeMathOperation operation = static_cast<NodeMathOperation>(node_storage.operation);
update_attribute_input_socket_availabilities(
- *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
+ *node, "A", (GeometryNodeAttributeInputMode)node_storage.input_type_a);
update_attribute_input_socket_availabilities(
- *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
+ *node,
+ "B",
+ (GeometryNodeAttributeInputMode)node_storage.input_type_b,
+ operation_use_input_b(operation));
+ update_attribute_input_socket_availabilities(
+ *node,
+ "C",
+ (GeometryNodeAttributeInputMode)node_storage.input_type_c,
+ operation_use_input_c(operation));
}
-static void do_math_operation(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
- FloatWriteAttribute result,
- const int operation)
+static void do_math_operation(Span<float> span_a,
+ Span<float> span_b,
+ Span<float> span_c,
+ MutableSpan<float> span_result,
+ const NodeMathOperation operation)
{
- const int size = input_a.size();
-
- Span<float> span_a = input_a.get_span();
- Span<float> span_b = input_b.get_span();
- MutableSpan<float> span_result = result.get_span_for_write_only();
+ bool success = try_dispatch_float_math_fl_fl_fl_to_fl(
+ operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
+ for (const int i : IndexRange(span_result.size())) {
+ span_result[i] = math_function(span_a[i], span_b[i], span_c[i]);
+ }
+ });
+ BLI_assert(success);
+ UNUSED_VARS_NDEBUG(success);
+}
+static void do_math_operation(Span<float> span_a,
+ Span<float> span_b,
+ MutableSpan<float> span_result,
+ const NodeMathOperation operation)
+{
bool success = try_dispatch_float_math_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float in1 = span_a[i];
- const float in2 = span_b[i];
- const float out = math_function(in1, in2);
- span_result[i] = out;
+ for (const int i : IndexRange(span_result.size())) {
+ span_result[i] = math_function(span_a[i], span_b[i]);
}
});
+ BLI_assert(success);
+ UNUSED_VARS_NDEBUG(success);
+}
- result.apply_span();
-
- /* The operation is not supported by this node currently. */
+static void do_math_operation(Span<float> span_input,
+ MutableSpan<float> span_result,
+ const NodeMathOperation operation)
+{
+ bool success = try_dispatch_float_math_fl_to_fl(
+ operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
+ for (const int i : IndexRange(span_result.size())) {
+ span_result[i] = math_function(span_input[i]);
+ }
+ });
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
@@ -98,7 +187,7 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
{
const bNode &node = params.node();
const NodeAttributeMath *node_storage = (const NodeAttributeMath *)node.storage;
- const int operation = node_storage->operation;
+ const NodeMathOperation operation = static_cast<NodeMathOperation>(node_storage->operation);
/* The result type of this node is always float. */
const CustomDataType result_type = CD_PROP_FLOAT;
@@ -115,15 +204,44 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
ReadAttributePtr attribute_a = params.get_input_attribute(
"A", component, result_domain, result_type, nullptr);
- ReadAttributePtr attribute_b = params.get_input_attribute(
- "B", component, result_domain, result_type, nullptr);
- if (!attribute_a || !attribute_b) {
- /* Attribute wasn't found. */
+ if (!attribute_a) {
return;
}
- do_math_operation(*attribute_a, *attribute_b, *attribute_result, operation);
- attribute_result.save();
+ /* Note that passing the data with `get_span<float>()` works
+ * because the attributes were accessed with #CD_PROP_FLOAT. */
+ if (operation_use_input_b(operation)) {
+ ReadAttributePtr attribute_b = params.get_input_attribute(
+ "B", component, result_domain, result_type, nullptr);
+ if (!attribute_b) {
+ return;
+ }
+ if (operation_use_input_c(operation)) {
+ ReadAttributePtr attribute_c = params.get_input_attribute(
+ "C", component, result_domain, result_type, nullptr);
+ if (!attribute_c) {
+ return;
+ }
+ do_math_operation(attribute_a->get_span<float>(),
+ attribute_b->get_span<float>(),
+ attribute_c->get_span<float>(),
+ attribute_result->get_span_for_write_only<float>(),
+ operation);
+ }
+ else {
+ do_math_operation(attribute_a->get_span<float>(),
+ attribute_b->get_span<float>(),
+ attribute_result->get_span_for_write_only<float>(),
+ operation);
+ }
+ }
+ else {
+ do_math_operation(attribute_a->get_span<float>(),
+ attribute_result->get_span_for_write_only<float>(),
+ operation);
+ }
+
+ attribute_result.apply_span_and_save();
}
static void geo_node_attribute_math_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
index 0e7bb25e659..3ee7df7fe72 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
@@ -50,8 +50,7 @@ static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode
bNodeSocket *sock_min_float = sock_max_vector->next;
bNodeSocket *sock_max_float = sock_min_float->next;
- const int data_type = node->custom1;
-
+ const CustomDataType data_type = static_cast<CustomDataType>(node->custom1);
nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3);
nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3);
nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT);
@@ -86,8 +85,11 @@ static void randomize_attribute_bool(BooleanWriteAttribute attribute,
attribute.apply_span();
}
-static void randomize_attribute_float(
- FloatWriteAttribute attribute, float min, float max, Span<uint32_t> hashes, const int seed)
+static void randomize_attribute_float(FloatWriteAttribute attribute,
+ const float min,
+ const float max,
+ Span<uint32_t> hashes,
+ const int seed)
{
MutableSpan<float> attribute_span = attribute.get_span();
for (const int i : IndexRange(attribute.size())) {
@@ -97,8 +99,11 @@ static void randomize_attribute_float(
attribute.apply_span();
}
-static void randomize_attribute_float3(
- Float3WriteAttribute attribute, float3 min, float3 max, Span<uint32_t> hashes, const int seed)
+static void randomize_attribute_float3(Float3WriteAttribute attribute,
+ const float3 min,
+ const float3 max,
+ Span<uint32_t> hashes,
+ const int seed)
{
MutableSpan<float3> attribute_span = attribute.get_span();
for (const int i : IndexRange(attribute.size())) {
@@ -129,8 +134,7 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
}
else {
/* If there is no "id" attribute for per-point variation, just create it here. */
- RandomNumberGenerator rng;
- rng.seed(0);
+ RandomNumberGenerator rng(0);
for (const int i : hashes.index_range()) {
hashes[i] = rng.get_uint32();
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
new file mode 100644
index 00000000000..66495bfa53b
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
@@ -0,0 +1,107 @@
+/*
+ * 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 "BLI_compiler_attrs.h"
+
+#include "DNA_texture_types.h"
+
+#include "BKE_texture.h"
+
+#include "RE_texture.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Mapping")},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_sample_texture_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void execute_on_component(GeometryComponent &component, const GeoNodeExecParams &params)
+{
+ const bNode &node = params.node();
+ Tex *texture = reinterpret_cast<Tex *>(node.id);
+ const std::string result_attribute_name = params.get_input<std::string>("Result");
+
+ if (texture == nullptr) {
+ return;
+ }
+
+ const std::string mapping_name = params.get_input<std::string>("Mapping");
+ if (!component.attribute_exists(mapping_name)) {
+ return;
+ }
+
+ OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
+ result_attribute_name, ATTR_DOMAIN_POINT, CD_PROP_COLOR);
+ if (!attribute_out) {
+ return;
+ }
+
+ Float3ReadAttribute mapping_attribute = component.attribute_get_for_read<float3>(
+ mapping_name, ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ MutableSpan<Color4f> colors = attribute_out->get_span<Color4f>();
+ for (const int i : IndexRange(mapping_attribute.size())) {
+ TexResult texture_result = {0};
+ const float3 position = mapping_attribute[i];
+ /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */
+ const float3 remapped_position = position * 2.0f - float3(1.0f);
+ BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
+ colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
+ }
+ attribute_out.apply_span_and_save();
+}
+
+static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (geometry_set.has<MeshComponent>()) {
+ execute_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_sample_texture()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype,
+ GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE,
+ "Attribute Sample Texture",
+ NODE_CLASS_ATTRIBUTE,
+ 0);
+ node_type_size_preset(&ntype, NODE_SIZE_LARGE);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_sample_texture_in, geo_node_attribute_sample_texture_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index 42402e23fa5..384094b6d8c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -138,17 +138,17 @@ static void determine_final_data_type_and_domain(Span<const GeometryComponent *>
CustomDataType *r_type,
AttributeDomain *r_domain)
{
+ Vector<CustomDataType> data_types;
for (const GeometryComponent *component : components) {
ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name);
if (attribute) {
- /* TODO: Use data type with most information. */
- *r_type = bke::cpp_type_to_custom_data_type(attribute->cpp_type());
+ data_types.append(attribute->custom_data_type());
/* TODO: Use highest priority domain. */
*r_domain = attribute->domain();
- return;
}
}
- BLI_assert(false);
+
+ *r_type = attribute_data_type_highest_complexity(data_types);
}
static void fill_new_attribute(Span<const GeometryComponent *> src_components,
@@ -243,6 +243,14 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo
}
}
+static void join_components(Span<const VolumeComponent *> src_components, GeometrySet &result)
+{
+ /* Not yet supported. Joining volume grids with the same name requires resampling of at least one
+ * of the grids. The cell size of the resulting volume has to be determined somehow. */
+ VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>();
+ UNUSED_VARS(src_components, dst_component);
+}
+
template<typename Component>
static void join_component_type(Span<const GeometrySet *> src_geometry_sets, GeometrySet &result)
{
@@ -275,6 +283,7 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params)
join_component_type<MeshComponent>(src_geometry_sets, geometry_set_result);
join_component_type<PointCloudComponent>(src_geometry_sets, geometry_set_result);
join_component_type<InstancesComponent>(src_geometry_sets, geometry_set_result);
+ join_component_type<VolumeComponent>(src_geometry_sets, geometry_set_result);
params.set_output("Geometry", std::move(geometry_set_result));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
index ab5e4f8964a..d713c191d5d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -19,6 +19,7 @@
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
+#include "BKE_volume.h"
#include "BLI_math_matrix.h"
@@ -86,6 +87,18 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
mesh_component.copy_vertex_group_names_from_object(*object);
}
}
+ if (object->type == OB_VOLUME) {
+ InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+
+ if (transform_space_relative) {
+ instances.add_instance(object, transform);
+ }
+ else {
+ float unit_transform[4][4];
+ unit_m4(unit_transform);
+ instances.add_instance(object, unit_transform);
+ }
+ }
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
index 2f1aa276532..eaf13b94eb9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -26,6 +26,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
+#include "BKE_attribute_math.hh"
#include "BKE_bvhutils.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
@@ -217,13 +218,141 @@ BLI_NOINLINE static void eliminate_points_based_on_mask(Span<bool> elimination_m
}
}
-BLI_NOINLINE static void compute_remaining_point_data(const Mesh &mesh,
- Span<float3> bary_coords,
- Span<int> looptri_indices,
- MutableSpan<float3> r_normals,
- MutableSpan<int> r_ids,
- MutableSpan<float3> r_rotations)
+template<typename T>
+BLI_NOINLINE static void interpolate_attribute_point(const Mesh &mesh,
+ const Span<float3> bary_coords,
+ const Span<int> looptri_indices,
+ const Span<T> data_in,
+ MutableSpan<T> data_out)
{
+ BLI_assert(data_in.size() == mesh.totvert);
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const float3 &bary_coord = bary_coords[i];
+
+ const int v0_index = mesh.mloop[looptri.tri[0]].v;
+ const int v1_index = mesh.mloop[looptri.tri[1]].v;
+ const int v2_index = mesh.mloop[looptri.tri[2]].v;
+
+ const T &v0 = data_in[v0_index];
+ const T &v1 = data_in[v1_index];
+ const T &v2 = data_in[v2_index];
+
+ const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
+ data_out[i] = interpolated_value;
+ }
+}
+
+template<typename T>
+BLI_NOINLINE static void interpolate_attribute_corner(const Mesh &mesh,
+ const Span<float3> bary_coords,
+ const Span<int> looptri_indices,
+ const Span<T> data_in,
+ MutableSpan<T> data_out)
+{
+ BLI_assert(data_in.size() == mesh.totloop);
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const float3 &bary_coord = bary_coords[i];
+
+ const int loop_index_0 = looptri.tri[0];
+ const int loop_index_1 = looptri.tri[1];
+ const int loop_index_2 = looptri.tri[2];
+
+ const T &v0 = data_in[loop_index_0];
+ const T &v1 = data_in[loop_index_1];
+ const T &v2 = data_in[loop_index_2];
+
+ const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
+ data_out[i] = interpolated_value;
+ }
+}
+
+BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
+ Span<float3> bary_coords,
+ Span<int> looptri_indices,
+ const StringRef attribute_name,
+ const ReadAttribute &attribute_in,
+ GeometryComponent &component)
+{
+ const CustomDataType data_type = attribute_in.custom_data_type();
+ const AttributeDomain domain = attribute_in.domain();
+ if (!ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) {
+ /* Not supported currently. */
+ return;
+ }
+
+ OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
+ attribute_name, ATTR_DOMAIN_POINT, data_type);
+ if (!attribute_out) {
+ return;
+ }
+
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+
+ Span data_in = attribute_in.get_span<T>();
+ MutableSpan data_out = attribute_out->get_span_for_write_only<T>();
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT: {
+ interpolate_attribute_point<T>(mesh, bary_coords, looptri_indices, data_in, data_out);
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ interpolate_attribute_corner<T>(mesh, bary_coords, looptri_indices, data_in, data_out);
+ break;
+ }
+ default: {
+ BLI_assert(false);
+ break;
+ }
+ }
+ });
+ attribute_out.apply_span_and_save();
+}
+
+BLI_NOINLINE static void interpolate_existing_attributes(const MeshComponent &mesh_component,
+ GeometryComponent &component,
+ Span<float3> bary_coords,
+ Span<int> looptri_indices)
+{
+ const Mesh &mesh = *mesh_component.get_for_read();
+
+ Set<std::string> attribute_names = mesh_component.attribute_names();
+ for (StringRefNull attribute_name : attribute_names) {
+ if (ELEM(attribute_name, "position", "normal", "id")) {
+ continue;
+ }
+
+ ReadAttributePtr attribute_in = mesh_component.attribute_try_get_for_read(attribute_name);
+ interpolate_attribute(
+ mesh, bary_coords, looptri_indices, attribute_name, *attribute_in, component);
+ }
+}
+
+BLI_NOINLINE static void compute_special_attributes(const Mesh &mesh,
+ GeometryComponent &component,
+ Span<float3> bary_coords,
+ Span<int> looptri_indices)
+{
+ OutputAttributePtr id_attribute = component.attribute_try_get_for_output(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ OutputAttributePtr normal_attribute = component.attribute_try_get_for_output(
+ "normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
+ "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+
+ MutableSpan<int> ids = id_attribute->get_span_for_write_only<int>();
+ MutableSpan<float3> normals = normal_attribute->get_span_for_write_only<float3>();
+ MutableSpan<float3> rotations = rotation_attribute->get_span_for_write_only<float3>();
+
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
for (const int i : bary_coords.index_range()) {
const int looptri_index = looptri_indices[i];
@@ -237,10 +366,24 @@ BLI_NOINLINE static void compute_remaining_point_data(const Mesh &mesh,
const float3 v1_pos = mesh.mvert[v1_index].co;
const float3 v2_pos = mesh.mvert[v2_index].co;
- r_ids[i] = (int)(bary_coord.hash()) + looptri_index;
- normal_tri_v3(r_normals[i], v0_pos, v1_pos, v2_pos);
- r_rotations[i] = normal_to_euler_rotation(r_normals[i]);
+ ids[i] = (int)(bary_coord.hash()) + looptri_index;
+ normal_tri_v3(normals[i], v0_pos, v1_pos, v2_pos);
+ rotations[i] = normal_to_euler_rotation(normals[i]);
}
+
+ id_attribute.apply_span_and_save();
+ normal_attribute.apply_span_and_save();
+ rotation_attribute.apply_span_and_save();
+}
+
+BLI_NOINLINE static void add_remaining_point_attributes(const MeshComponent &mesh_component,
+ GeometryComponent &component,
+ Span<float3> bary_coords,
+ Span<int> looptri_indices)
+{
+ interpolate_existing_attributes(mesh_component, component, bary_coords, looptri_indices);
+ compute_special_attributes(
+ *mesh_component.get_for_read(), component, bary_coords, looptri_indices);
}
static void sample_mesh_surface_with_minimum_distance(const Mesh &mesh,
@@ -315,11 +458,6 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
break;
}
const int tot_points = positions.size();
- Array<float3> normals(tot_points);
- Array<int> stable_ids(tot_points);
- Array<float3> rotations(tot_points);
- compute_remaining_point_data(
- *mesh_in, bary_coords, looptri_indices, normals, stable_ids, rotations);
PointCloud *pointcloud = BKE_pointcloud_new_nomain(tot_points);
memcpy(pointcloud->co, positions.data(), sizeof(float3) * tot_points);
@@ -332,29 +470,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
geometry_set_out.get_component_for_write<PointCloudComponent>();
point_component.replace(pointcloud);
- {
- OutputAttributePtr stable_id_attribute = point_component.attribute_try_get_for_output(
- "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
- MutableSpan<int> stable_ids_span = stable_id_attribute->get_span<int>();
- stable_ids_span.copy_from(stable_ids);
- stable_id_attribute.apply_span_and_save();
- }
-
- {
- OutputAttributePtr normals_attribute = point_component.attribute_try_get_for_output(
- "normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- MutableSpan<float3> normals_span = normals_attribute->get_span<float3>();
- normals_span.copy_from(normals);
- normals_attribute.apply_span_and_save();
- }
-
- {
- OutputAttributePtr rotations_attribute = point_component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- MutableSpan<float3> rotations_span = rotations_attribute->get_span<float3>();
- rotations_span.copy_from(rotations);
- rotations_attribute.apply_span_and_save();
- }
+ add_remaining_point_attributes(mesh_component, point_component, bary_coords, looptri_indices);
params.set_output("Geometry", std::move(geometry_set_out));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
index a96dc710427..3bd8c355124 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
@@ -47,8 +47,10 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
bNodeSocket *collection_socket = object_socket->next;
bNodeSocket *seed_socket = collection_socket->next;
- GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node->custom1;
- const bool use_whole_collection = node->custom2 == 0;
+ NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node->storage;
+ GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node_storage->instance_type;
+ const bool use_whole_collection = (node_storage->flag &
+ GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0;
nodeSetSocketAvailability(object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT);
nodeSetSocketAvailability(collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION);
@@ -79,6 +81,8 @@ static void get_instanced_data__collection(
MutableSpan<std::optional<InstancedData>> r_instances_data)
{
const bNode &node = params.node();
+ NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage;
+
bke::PersistentCollectionHandle collection_handle =
params.get_input<bke::PersistentCollectionHandle>("Collection");
Collection *collection = params.handle_map().lookup(collection_handle);
@@ -86,7 +90,8 @@ static void get_instanced_data__collection(
return;
}
- const bool use_whole_collection = node.custom2 == 0;
+ const bool use_whole_collection = (node_storage->flag &
+ GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0;
if (use_whole_collection) {
InstancedData instance;
instance.type = INSTANCE_DATA_TYPE_COLLECTION;
@@ -128,8 +133,9 @@ static Array<std::optional<InstancedData>> get_instanced_data(const GeoNodeExecP
const int amount)
{
const bNode &node = params.node();
- const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node.custom1;
-
+ NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage;
+ const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)
+ node_storage->instance_type;
Array<std::optional<InstancedData>> instances_data(amount);
switch (type) {
@@ -189,6 +195,16 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set_out));
}
+
+static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN(
+ sizeof(NodeGeometryPointInstance), __func__);
+ data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT;
+ data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION;
+ node->storage = data;
+}
+
} // namespace blender::nodes
void register_node_type_geo_point_instance()
@@ -197,6 +213,9 @@ void register_node_type_geo_point_instance()
geo_node_type_base(&ntype, GEO_NODE_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out);
+ node_type_init(&ntype, blender::nodes::geo_node_point_instance_init);
+ node_type_storage(
+ &ntype, "NodeGeometryPointInstance", node_free_standard_storage, node_copy_standard_storage);
node_type_update(&ntype, blender::nodes::geo_node_point_instance_update);
ntype.geometry_node_execute = blender::nodes::geo_node_point_instance_exec;
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
new file mode 100644
index 00000000000..b90ef2034a8
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
@@ -0,0 +1,259 @@
+/*
+ * 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/LevelSetUtil.h>
+# include <openvdb/tools/ParticlesToLevelSet.h>
+#endif
+
+#include "node_geometry_util.hh"
+
+#include "BKE_lib_id.h"
+#include "BKE_volume.h"
+
+static bNodeSocketTemplate geo_node_points_to_volume_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
+ {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX},
+ {SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
+ {SOCK_STRING, N_("Radius")},
+ {SOCK_FLOAT, N_("Radius"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_point_translate_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+#ifdef WITH_OPENVDB
+namespace {
+/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */
+struct ParticleList {
+ using PosType = openvdb::Vec3R;
+
+ Span<float3> positions;
+ Span<float> radii;
+
+ size_t size() const
+ {
+ return (size_t)positions.size();
+ }
+
+ void getPos(size_t n, openvdb::Vec3R &xyz) const
+ {
+ xyz = &positions[n].x;
+ }
+
+ void getPosRad(size_t n, openvdb::Vec3R &xyz, openvdb::Real &radius) const
+ {
+ xyz = &positions[n].x;
+ radius = radii[n];
+ }
+};
+} // namespace
+
+static openvdb::FloatGrid::Ptr generate_volume_from_points(const Span<float3> positions,
+ const Span<float> radii,
+ const float density)
+{
+ /* 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 narrow-band level set grid based on the positions and radii. */
+ openvdb::tools::ParticlesToLevelSet op{*new_grid};
+ /* Don't ignore particles based on their radius. */
+ op.setRmin(0.0f);
+ op.setRmax(FLT_MAX);
+ ParticleList particles{positions, radii};
+ 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;
+}
+
+static float compute_voxel_size(const GeoNodeExecParams &params,
+ Span<float3> positions,
+ const float radius)
+{
+ const NodeGeometryPointsToVolume &storage =
+ *(const NodeGeometryPointsToVolume *)params.node().storage;
+
+ if (storage.resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE) {
+ return params.get_input<float>("Voxel Size");
+ }
+
+ if (positions.is_empty()) {
+ return 0.0f;
+ }
+
+ float3 min, max;
+ INIT_MINMAX(min, max);
+ minmax_v3v3_v3_array(min, max, (float(*)[3])positions.data(), positions.size());
+
+ const float voxel_amount = params.get_input<float>("Voxel Amount");
+ if (voxel_amount <= 1) {
+ return 0.0f;
+ }
+
+ /* The voxel size adapts to the final size of the volume. */
+ const float diagonal = float3::distance(min, max);
+ const float extended_diagonal = diagonal + 2.0f * radius;
+ const float voxel_size = extended_diagonal / voxel_amount;
+ return voxel_size;
+}
+
+static void gather_point_data_from_component(const GeoNodeExecParams &params,
+ const GeometryComponent &component,
+ Vector<float3> &r_positions,
+ Vector<float> &r_radii)
+{
+ Float3ReadAttribute positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ FloatReadAttribute radii = params.get_input_attribute<float>(
+ "Radius", component, ATTR_DOMAIN_POINT, 0.0f);
+
+ r_positions.extend(positions.get_span());
+ r_radii.extend(radii.get_span());
+}
+
+static void convert_to_grid_index_space(const float voxel_size,
+ MutableSpan<float3> positions,
+ MutableSpan<float> radii)
+{
+ const float voxel_size_inv = 1.0f / voxel_size;
+ for (const int i : positions.index_range()) {
+ positions[i] *= voxel_size_inv;
+ /* Better align generated grid with source points. */
+ positions[i] -= float3(0.5f);
+ radii[i] *= voxel_size_inv;
+ }
+}
+
+static void initialize_volume_component_from_points(const GeometrySet &geometry_set_in,
+ GeometrySet &geometry_set_out,
+ const GeoNodeExecParams &params)
+{
+ Vector<float3> positions;
+ Vector<float> radii;
+
+ if (geometry_set_in.has<MeshComponent>()) {
+ gather_point_data_from_component(
+ params, *geometry_set_in.get_component_for_read<MeshComponent>(), positions, radii);
+ }
+ if (geometry_set_in.has<PointCloudComponent>()) {
+ gather_point_data_from_component(
+ params, *geometry_set_in.get_component_for_read<PointCloudComponent>(), positions, radii);
+ }
+
+ const float max_radius = *std::max_element(radii.begin(), radii.end());
+ const float voxel_size = compute_voxel_size(params, positions, max_radius);
+ if (voxel_size == 0.0f || positions.is_empty()) {
+ return;
+ }
+
+ Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr);
+ BKE_volume_init_grids(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));
+
+ 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);
+ /* This merge is cheap, because the #density_grid is empty. */
+ density_grid->merge(*new_grid);
+ density_grid->transform().postScale(voxel_size);
+
+ VolumeComponent &volume_component = geometry_set_out.get_component_for_write<VolumeComponent>();
+ volume_component.replace(volume);
+}
+#endif
+
+static void geo_node_points_to_volume_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set_out;
+
+#ifdef WITH_OPENVDB
+ initialize_volume_component_from_points(geometry_set_in, geometry_set_out, params);
+#endif
+
+ params.set_output("Geometry", std::move(geometry_set_out));
+}
+
+static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN(
+ sizeof(NodeGeometryPointsToVolume), __func__);
+ data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
+ data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node->storage = data;
+
+ bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius");
+ bNodeSocketValueString *radius_attribute_socket_value =
+ (bNodeSocketValueString *)radius_attribute_socket->default_value;
+ STRNCPY(radius_attribute_socket_value->value, "radius");
+}
+
+static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage;
+ bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
+ bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
+ nodeSetSocketAvailability(voxel_amount_socket,
+ data->resolution_mode ==
+ GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT);
+ nodeSetSocketAvailability(
+ voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
+
+ update_attribute_input_socket_availabilities(
+ *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius);
+}
+
+} // namespace blender::nodes
+
+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);
+ node_type_socket_templates(&ntype, geo_node_points_to_volume_in, geo_node_point_translate_out);
+ node_type_storage(&ntype,
+ "NodeGeometryPointsToVolume",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ node_type_size(&ntype, 170, 120, 700);
+ node_type_init(&ntype, blender::nodes::geo_node_points_to_volume_init);
+ node_type_update(&ntype, blender::nodes::geo_node_points_to_volume_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_points_to_volume_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
index 1fcd1063993..539a7551be9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -14,11 +14,19 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#ifdef WITH_OPENVDB
+# include <openvdb/openvdb.h>
+#endif
+
#include "BLI_math_matrix.h"
#include "DNA_pointcloud_types.h"
+#include "DNA_volume_types.h"
#include "BKE_mesh.h"
+#include "BKE_volume.h"
+
+#include "DEG_depsgraph_query.h"
#include "node_geometry_util.hh"
@@ -109,6 +117,43 @@ static void transform_instances(InstancesComponent &instances,
}
}
+static void transform_volume(Volume *volume,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale,
+ GeoNodeExecParams &params)
+{
+#ifdef WITH_OPENVDB
+ /* Scaling an axis to zero is not supported for volumes. */
+ const float3 limited_scale = {
+ (scale.x == 0.0f) ? FLT_EPSILON : scale.x,
+ (scale.y == 0.0f) ? FLT_EPSILON : scale.y,
+ (scale.z == 0.0f) ? FLT_EPSILON : scale.z,
+ };
+
+ Main *bmain = DEG_get_bmain(params.depsgraph());
+ BKE_volume_load(volume, bmain);
+
+ float matrix[4][4];
+ loc_eul_size_to_mat4(matrix, translation, rotation, limited_scale);
+
+ openvdb::Mat4s vdb_matrix;
+ memcpy(vdb_matrix.asPointer(), matrix, sizeof(float[4][4]));
+ openvdb::Mat4d vdb_matrix_d{vdb_matrix};
+
+ const int num_grids = BKE_volume_num_grids(volume);
+ for (const int i : IndexRange(num_grids)) {
+ VolumeGrid *volume_grid = BKE_volume_grid_get(volume, i);
+
+ openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false);
+ openvdb::math::Transform &grid_transform = grid->transform();
+ grid_transform.postMult(vdb_matrix_d);
+ }
+#else
+ UNUSED_VARS(volume, translation, rotation, scale, params);
+#endif
+}
+
static void geo_node_transform_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -131,6 +176,11 @@ static void geo_node_transform_exec(GeoNodeExecParams params)
transform_instances(instances, translation, rotation, scale);
}
+ if (geometry_set.has_volume()) {
+ Volume *volume = geometry_set.get_volume_for_write();
+ transform_volume(volume, translation, rotation, scale, params);
+ }
+
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc
index 2e4196af156..c2391667e86 100644
--- a/source/blender/nodes/intern/node_tree_multi_function.cc
+++ b/source/blender/nodes/intern/node_tree_multi_function.cc
@@ -19,6 +19,7 @@
#include "FN_multi_function_network_evaluation.hh"
#include "BLI_color.hh"
+#include "BLI_float2.hh"
#include "BLI_float3.hh"
namespace blender::nodes {
@@ -191,27 +192,58 @@ static void add_implicit_conversion(DataTypeConversions &conversions,
static DataTypeConversions create_implicit_conversions()
{
DataTypeConversions conversions;
- add_implicit_conversion<float, int32_t>(conversions);
+ add_implicit_conversion<float, float2>(conversions);
add_implicit_conversion<float, float3>(conversions);
- add_implicit_conversion<int32_t, float>(conversions);
+ add_implicit_conversion<float, int32_t>(conversions);
add_implicit_conversion<float, bool>(conversions);
- add_implicit_conversion<bool, float>(conversions);
- add_implicit_conversion<float3, float>(
- conversions, "Vector Length", [](float3 a) { return a.length(); });
- add_implicit_conversion<int32_t, float3>(
- conversions, "int32 to float3", [](int32_t a) { return float3((float)a); });
- add_implicit_conversion<float3, Color4f>(
- conversions, "float3 to Color4f", [](float3 a) { return Color4f(a.x, a.y, a.z, 1.0f); });
- add_implicit_conversion<Color4f, float3>(
- conversions, "Color4f to float3", [](Color4f a) { return float3(a.r, a.g, a.b); });
add_implicit_conversion<float, Color4f>(
conversions, "float to Color4f", [](float a) { return Color4f(a, a, a, 1.0f); });
- add_implicit_conversion<Color4f, float>(
- conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); });
+
+ add_implicit_conversion<float2, float3>(
+ conversions, "float2 to float3", [](float2 a) { return float3(a.x, a.y, 0.0f); });
+ add_implicit_conversion<float2, float>(
+ conversions, "float2 to float", [](float2 a) { return a.length(); });
+ add_implicit_conversion<float2, int32_t>(
+ conversions, "float2 to int32_t", [](float2 a) { return (int32_t)a.length(); });
+ add_implicit_conversion<float2, bool>(
+ conversions, "float2 to bool", [](float2 a) { return a.length_squared() == 0.0f; });
+ add_implicit_conversion<float2, Color4f>(
+ conversions, "float2 to Color4f", [](float2 a) { return Color4f(a.x, a.y, 0.0f, 1.0f); });
+
add_implicit_conversion<float3, bool>(
conversions, "float3 to boolean", [](float3 a) { return a.length_squared() == 0.0f; });
+ add_implicit_conversion<float3, float>(
+ conversions, "Vector Length", [](float3 a) { return a.length(); });
+ add_implicit_conversion<float3, int32_t>(
+ conversions, "float3 to int32_t", [](float3 a) { return (int)a.length(); });
+ add_implicit_conversion<float3, float2>(conversions);
+ add_implicit_conversion<float3, Color4f>(
+ conversions, "float3 to Color4f", [](float3 a) { return Color4f(a.x, a.y, a.z, 1.0f); });
+
+ add_implicit_conversion<int32_t, bool>(conversions);
+ add_implicit_conversion<int32_t, float>(conversions);
+ add_implicit_conversion<int32_t, float2>(
+ conversions, "int32 to float2", [](int32_t a) { return float2((float)a); });
+ add_implicit_conversion<int32_t, float3>(
+ conversions, "int32 to float3", [](int32_t a) { return float3((float)a); });
+
+ add_implicit_conversion<bool, float>(conversions);
+ add_implicit_conversion<bool, int32_t>(conversions);
+ add_implicit_conversion<bool, float2>(
+ conversions, "boolean to float2", [](bool a) { return (a) ? float2(1.0f) : float2(0.0f); });
add_implicit_conversion<bool, float3>(
conversions, "boolean to float3", [](bool a) { return (a) ? float3(1.0f) : float3(0.0f); });
+ add_implicit_conversion<bool, Color4f>(conversions, "boolean to Color4f", [](bool a) {
+ return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f);
+ });
+
+ add_implicit_conversion<Color4f, float>(
+ conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); });
+ add_implicit_conversion<Color4f, float2>(
+ conversions, "Color4f to float2", [](Color4f a) { return float2(a.r, a.g); });
+ add_implicit_conversion<Color4f, float3>(
+ conversions, "Color4f to float3", [](Color4f a) { return float3(a.r, a.g, a.b); });
+
return conversions;
}