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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2020-12-02 15:25:25 +0300
committerJacques Lucke <jacques@blender.org>2020-12-02 17:38:47 +0300
commit6be56c13e96048cbc494ba5473a8deaf2cf5a6f8 (patch)
tree83435d439b3d106eb1405b2b815bf1d5aa1fdd43 /source/blender/nodes
parentddbe3274eff68523547bc8eb70dd95c3d411b89b (diff)
Geometry Nodes: initial scattering and geometry nodes
This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
Diffstat (limited to 'source/blender/nodes')
-rw-r--r--source/blender/nodes/CMakeLists.txt38
-rw-r--r--source/blender/nodes/NOD_derived_node_tree.hh53
-rw-r--r--source/blender/nodes/NOD_geometry.h43
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh155
-rw-r--r--source/blender/nodes/NOD_math_functions.hh200
-rw-r--r--source/blender/nodes/NOD_node_tree_multi_function.hh51
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh8
-rw-r--r--source/blender/nodes/NOD_static_types.h11
-rw-r--r--source/blender/nodes/NOD_type_callbacks.hh36
-rw-r--r--source/blender/nodes/function/node_function_util.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_compare.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_random_float.cc2
-rw-r--r--source/blender/nodes/geometry/node_geometry_exec.cc (renamed from source/blender/nodes/NOD_simulation.h)14
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc (renamed from source/blender/nodes/simulation/node_simulation_tree.cc)20
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.cc (renamed from source/blender/nodes/simulation/node_simulation_util.cc)10
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh (renamed from source/blender/nodes/simulation/node_simulation_util.h)8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc167
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc152
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_common.cc (renamed from source/blender/nodes/simulation/nodes/node_sim_common.cc)12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc96
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc276
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc94
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc139
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc87
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc160
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc113
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc145
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc79
-rw-r--r--source/blender/nodes/intern/derived_node_tree.cc16
-rw-r--r--source/blender/nodes/intern/math_functions.cc117
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc103
-rw-r--r--source/blender/nodes/intern/node_socket.cc55
-rw-r--r--source/blender/nodes/intern/node_tree_multi_function.cc227
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc42
-rw-r--r--source/blender/nodes/intern/type_callbacks.cc76
-rw-r--r--source/blender/nodes/shader/node_shader_util.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.cc314
37 files changed, 2721 insertions, 404 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index f88d50167ac..a367f40dca7 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -24,11 +24,12 @@ set(INC
function
intern
shader
- simulation
+ geometry
texture
../blenkernel
../blenlib
../blentranslation
+ ../bmesh
../depsgraph
../functions
../gpu
@@ -137,6 +138,22 @@ set(SRC
function/nodes/node_fn_switch.cc
function/node_function_util.cc
+ geometry/nodes/node_geo_attribute_math.cc
+ geometry/nodes/node_geo_common.cc
+ geometry/nodes/node_geo_boolean.cc
+ geometry/nodes/node_geo_edge_split.cc
+ geometry/nodes/node_geo_join_geometry.cc
+ geometry/nodes/node_geo_object_info.cc
+ geometry/nodes/node_geo_subdivision_surface.cc
+ geometry/nodes/node_geo_point_distribute.cc
+ geometry/nodes/node_geo_point_instance.cc
+ geometry/nodes/node_geo_random_attribute.cc
+ geometry/nodes/node_geo_transform.cc
+ geometry/nodes/node_geo_triangulate.cc
+ geometry/node_geometry_exec.cc
+ geometry/node_geometry_tree.cc
+ geometry/node_geometry_util.cc
+
shader/nodes/node_shader_add_shader.c
shader/nodes/node_shader_ambient_occlusion.c
shader/nodes/node_shader_attribute.c
@@ -230,10 +247,6 @@ set(SRC
shader/node_shader_tree.c
shader/node_shader_util.c
- simulation/nodes/node_sim_common.cc
- simulation/node_simulation_tree.cc
- simulation/node_simulation_util.cc
-
texture/nodes/node_texture_at.c
texture/nodes/node_texture_bricks.c
texture/nodes/node_texture_checker.c
@@ -261,18 +274,21 @@ set(SRC
texture/node_texture_util.c
intern/derived_node_tree.cc
+ intern/math_functions.cc
intern/node_common.c
intern/node_exec.c
+ intern/node_geometry_exec.cc
intern/node_socket.cc
intern/node_tree_dependencies.cc
intern/node_tree_multi_function.cc
intern/node_tree_ref.cc
intern/node_util.c
+ intern/type_callbacks.cc
composite/node_composite_util.h
function/node_function_util.hh
shader/node_shader_util.h
- simulation/node_simulation_util.h
+ geometry/node_geometry_util.hh
texture/node_texture_util.h
NOD_common.h
@@ -283,10 +299,12 @@ set(SRC
NOD_node_tree_multi_function.hh
NOD_node_tree_ref.hh
NOD_shader.h
- NOD_simulation.h
+ NOD_geometry.h
+ NOD_math_functions.hh
NOD_socket.h
NOD_static_types.h
NOD_texture.h
+ NOD_type_callbacks.hh
intern/node_common.h
intern/node_exec.h
intern/node_util.h
@@ -295,6 +313,7 @@ set(SRC
set(LIB
bf_functions
bf_intern_sky
+ bf_bmesh
)
if(WITH_PYTHON)
@@ -326,9 +345,12 @@ if(WITH_COMPOSITOR)
add_definitions(-DWITH_COMPOSITOR)
endif()
-
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_OPENSUBDIV)
+ add_definitions(-DWITH_OPENSUBDIV)
+endif()
+
blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh
index 9c71ae79cf2..087bfac4442 100644
--- a/source/blender/nodes/NOD_derived_node_tree.hh
+++ b/source/blender/nodes/NOD_derived_node_tree.hh
@@ -31,6 +31,8 @@
#include "NOD_node_tree_ref.hh"
+#include "BLI_vector_set.hh"
+
namespace blender::nodes {
class DSocket;
@@ -65,6 +67,8 @@ class DSocket : NonCopyable, NonMovable {
PointerRNA *rna() const;
StringRefNull idname() const;
StringRefNull name() const;
+ StringRefNull identifier() const;
+ bNodeSocketType *typeinfo() const;
const SocketRef &socket_ref() const;
bNodeSocket *bsocket() const;
@@ -147,6 +151,8 @@ class DNode : NonCopyable, NonMovable {
PointerRNA *rna() const;
StringRefNull idname() const;
StringRefNull name() const;
+ bNode *bnode() const;
+ bNodeType *typeinfo() const;
private:
void destruct_with_sockets();
@@ -180,11 +186,15 @@ class DerivedNodeTree : NonCopyable, NonMovable {
Vector<DOutputSocket *> output_sockets_;
MultiValueMap<const bNodeType *, DNode *> nodes_by_type_;
+ VectorSet<const NodeTreeRef *> used_node_tree_refs_;
+ bNodeTree *btree_;
public:
DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs);
~DerivedNodeTree();
+ bNodeTree *btree() const;
+
Span<const DNode *> nodes() const;
Span<const DNode *> nodes_by_type(StringRefNull idname) const;
Span<const DNode *> nodes_by_type(const bNodeType *nodetype) const;
@@ -195,6 +205,10 @@ class DerivedNodeTree : NonCopyable, NonMovable {
Span<const DGroupInput *> group_inputs() const;
+ Span<const NodeTreeRef *> used_node_tree_refs() const;
+
+ bool has_link_cycles() const;
+
std::string to_dot() const;
private:
@@ -229,6 +243,15 @@ class DerivedNodeTree : NonCopyable, NonMovable {
Vector<DParentNode *> &&all_parent_nodes);
};
+namespace derived_node_tree_types {
+using nodes::DerivedNodeTree;
+using nodes::DGroupInput;
+using nodes::DInputSocket;
+using nodes::DNode;
+using nodes::DOutputSocket;
+using nodes::DParentNode;
+}; // namespace derived_node_tree_types
+
/* --------------------------------------------------------------------
* DSocket inline methods.
*/
@@ -288,6 +311,16 @@ inline StringRefNull DSocket::name() const
return socket_ref_->name();
}
+inline StringRefNull DSocket::identifier() const
+{
+ return socket_ref_->identifier();
+}
+
+inline bNodeSocketType *DSocket::typeinfo() const
+{
+ return socket_ref_->bsocket()->typeinfo;
+}
+
inline const SocketRef &DSocket::socket_ref() const
{
return *socket_ref_;
@@ -445,6 +478,16 @@ inline StringRefNull DNode::name() const
return node_ref_->name();
}
+inline bNode *DNode::bnode() const
+{
+ return node_ref_->bnode();
+}
+
+inline bNodeType *DNode::typeinfo() const
+{
+ return node_ref_->bnode()->typeinfo;
+}
+
/* --------------------------------------------------------------------
* DParentNode inline methods.
*/
@@ -468,6 +511,11 @@ inline int DParentNode::id() const
* DerivedNodeTree inline methods.
*/
+inline bNodeTree *DerivedNodeTree::btree() const
+{
+ return btree_;
+}
+
inline Span<const DNode *> DerivedNodeTree::nodes() const
{
return nodes_by_id_;
@@ -504,4 +552,9 @@ inline Span<const DGroupInput *> DerivedNodeTree::group_inputs() const
return group_inputs_;
}
+inline Span<const NodeTreeRef *> DerivedNodeTree::used_node_tree_refs() const
+{
+ return used_node_tree_refs_;
+}
+
} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
new file mode 100644
index 00000000000..0532547375f
--- /dev/null
+++ b/source/blender/nodes/NOD_geometry.h
@@ -0,0 +1,43 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct bNodeTreeType *ntreeType_Geometry;
+
+void register_node_tree_type_geo(void);
+
+void register_node_type_geo_group(void);
+
+void register_node_type_geo_boolean(void);
+void register_node_type_geo_edge_split(void);
+void register_node_type_geo_transform(void);
+void register_node_type_geo_subdivision_surface(void);
+void register_node_type_geo_triangulate(void);
+void register_node_type_geo_point_distribute(void);
+void register_node_type_geo_point_instance(void);
+void register_node_type_geo_object_info(void);
+void register_node_type_geo_random_attribute(void);
+void register_node_type_geo_attribute_math(void);
+void register_node_type_geo_join_geometry(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
new file mode 100644
index 00000000000..2b95f76d06b
--- /dev/null
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "FN_generic_value_map.hh"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_geometry_set.hh"
+#include "BKE_persistent_data_handle.hh"
+
+#include "DNA_node_types.h"
+
+namespace blender::nodes {
+
+using bke::Float3ReadAttribute;
+using bke::Float3WriteAttribute;
+using bke::FloatReadAttribute;
+using bke::FloatWriteAttribute;
+using bke::PersistentDataHandleMap;
+using bke::PersistentObjectHandle;
+using bke::ReadAttribute;
+using bke::ReadAttributePtr;
+using bke::WriteAttribute;
+using bke::WriteAttributePtr;
+using fn::CPPType;
+using fn::GMutablePointer;
+using fn::GValueMap;
+
+class GeoNodeExecParams {
+ private:
+ const bNode &node_;
+ GValueMap<StringRef> &input_values_;
+ GValueMap<StringRef> &output_values_;
+ const PersistentDataHandleMap &handle_map_;
+ const Object *self_object_;
+
+ public:
+ GeoNodeExecParams(const bNode &node,
+ GValueMap<StringRef> &input_values,
+ GValueMap<StringRef> &output_values,
+ const PersistentDataHandleMap &handle_map,
+ const Object *self_object)
+ : node_(node),
+ input_values_(input_values),
+ output_values_(output_values),
+ handle_map_(handle_map),
+ self_object_(self_object)
+ {
+ }
+
+ /**
+ * Get the input value for the input socket with the given identifier.
+ *
+ * The node calling becomes responsible for destructing the value before it is done
+ * executing. This method can only be called once for each identifier.
+ */
+ GMutablePointer extract_input(StringRef identifier)
+ {
+#ifdef DEBUG
+ this->check_extract_input(identifier);
+#endif
+ return input_values_.extract(identifier);
+ }
+
+ /**
+ * Get the input value for the input socket with the given identifier.
+ *
+ * This method can only be called once for each identifier.
+ */
+ template<typename T> T extract_input(StringRef identifier)
+ {
+#ifdef DEBUG
+ this->check_extract_input(identifier, &CPPType::get<T>());
+#endif
+ return input_values_.extract<T>(identifier);
+ }
+
+ /**
+ * Get the input value for the input socket with the given identifier.
+ *
+ * This makes a copy of the value, which is fine for most types but should be avoided for
+ * geometry sets.
+ */
+ template<typename T> T get_input(StringRef identifier) const
+ {
+#ifdef DEBUG
+ this->check_extract_input(identifier, &CPPType::get<T>());
+#endif
+ return input_values_.lookup<T>(identifier);
+ }
+
+ /**
+ * Move-construct a new value based on the given value and store it for the given socket
+ * identifier.
+ */
+ void set_output_by_move(StringRef identifier, GMutablePointer value)
+ {
+#ifdef DEBUG
+ BLI_assert(value.type() != nullptr);
+ BLI_assert(value.get() != nullptr);
+ this->check_set_output(identifier, *value.type());
+#endif
+ output_values_.add_new_by_move(identifier, value);
+ }
+
+ /**
+ * Store the output value for the given socket identifier.
+ */
+ template<typename T> void set_output(StringRef identifier, T &&value)
+ {
+#ifdef DEBUG
+ this->check_set_output(identifier, CPPType::get<std::decay_t<T>>());
+#endif
+ output_values_.add_new(identifier, std::forward<T>(value));
+ }
+
+ /**
+ * Get the node that is currently being executed.
+ */
+ const bNode &node() const
+ {
+ return node_;
+ }
+
+ const PersistentDataHandleMap &handle_map() const
+ {
+ return handle_map_;
+ }
+
+ const Object *self_object() const
+ {
+ return self_object_;
+ }
+
+ private:
+ /* Utilities for detecting common errors at when using this class. */
+ void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr) const;
+ void check_set_output(StringRef identifier, const CPPType &value_type) const;
+};
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh
new file mode 100644
index 00000000000..70e4174a844
--- /dev/null
+++ b/source/blender/nodes/NOD_math_functions.hh
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "DNA_node_types.h"
+
+#include "BLI_math_base_safe.h"
+#include "BLI_math_rotation.h"
+#include "BLI_string_ref.hh"
+
+namespace blender::nodes {
+
+struct FloatMathOperationInfo {
+ StringRefNull title_case_name;
+ StringRefNull shader_name;
+
+ FloatMathOperationInfo() = delete;
+ FloatMathOperationInfo(StringRefNull title_case_name, StringRefNull shader_name)
+ : title_case_name(title_case_name), shader_name(shader_name)
+ {
+ }
+};
+
+const FloatMathOperationInfo *get_float_math_operation_info(const int operation);
+
+/**
+ * This calls the `callback` with two arguments:
+ * 1. The math function that takes a float as input and outputs a new float.
+ * 2. A #FloatMathOperationInfo struct reference.
+ * Returns true when the callback has been called, otherwise false.
+ *
+ * The math function that is passed to the callback is actually a lambda function that is different
+ * for every operation. Therefore, if the callback is templated on the math function, it will get
+ * instantiated for every operation separately. This has two benefits:
+ * - The compiler can optimize the callback for every operation separately.
+ * - A static variable declared in the callback will be generated for every operation separately.
+ *
+ * If separate instantiations are not desired, the callback can also take a function pointer with
+ * the following signature as input instead: float (*math_function)(float a).
+ */
+template<typename Callback>
+inline bool try_dispatch_float_math_fl_to_fl(const int operation, Callback &&callback)
+{
+ const FloatMathOperationInfo *info = get_float_math_operation_info(operation);
+ if (info == nullptr) {
+ return false;
+ }
+
+ /* This is just an utility function to keep the individual cases smaller. */
+ auto dispatch = [&](auto math_function) -> bool {
+ callback(math_function, *info);
+ return true;
+ };
+
+ switch (operation) {
+ case NODE_MATH_EXPONENT:
+ return dispatch([](float a) { return expf(a); });
+ case NODE_MATH_SQRT:
+ return dispatch([](float a) { return safe_sqrtf(a); });
+ case NODE_MATH_INV_SQRT:
+ return dispatch([](float a) { return safe_inverse_sqrtf(a); });
+ case NODE_MATH_ABSOLUTE:
+ return dispatch([](float a) { return fabs(a); });
+ case NODE_MATH_RADIANS:
+ return dispatch([](float a) { return (float)DEG2RAD(a); });
+ case NODE_MATH_DEGREES:
+ return dispatch([](float a) { return (float)RAD2DEG(a); });
+ case NODE_MATH_SIGN:
+ return dispatch([](float a) { return compatible_signf(a); });
+ case NODE_MATH_ROUND:
+ return dispatch([](float a) { return floorf(a + 0.5f); });
+ case NODE_MATH_FLOOR:
+ return dispatch([](float a) { return floorf(a); });
+ case NODE_MATH_CEIL:
+ return dispatch([](float a) { return ceilf(a); });
+ case NODE_MATH_FRACTION:
+ return dispatch([](float a) { return a - floorf(a); });
+ case NODE_MATH_TRUNC:
+ return dispatch([](float a) { return a >= 0.0f ? floorf(a) : ceilf(a); });
+ case NODE_MATH_SINE:
+ return dispatch([](float a) { return sinf(a); });
+ case NODE_MATH_COSINE:
+ return dispatch([](float a) { return cosf(a); });
+ case NODE_MATH_TANGENT:
+ return dispatch([](float a) { return tanf(a); });
+ case NODE_MATH_SINH:
+ return dispatch([](float a) { return sinhf(a); });
+ case NODE_MATH_COSH:
+ return dispatch([](float a) { return coshf(a); });
+ case NODE_MATH_TANH:
+ return dispatch([](float a) { return tanhf(a); });
+ case NODE_MATH_ARCSINE:
+ return dispatch([](float a) { return safe_asinf(a); });
+ case NODE_MATH_ARCCOSINE:
+ return dispatch([](float a) { return safe_acosf(a); });
+ case NODE_MATH_ARCTANGENT:
+ return dispatch([](float a) { return atanf(a); });
+ }
+ return false;
+}
+
+/**
+ * This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
+ */
+template<typename Callback>
+inline bool try_dispatch_float_math_fl_fl_to_fl(const int operation, Callback &&callback)
+{
+ const FloatMathOperationInfo *info = get_float_math_operation_info(operation);
+ if (info == nullptr) {
+ return false;
+ }
+
+ /* This is just an utility function to keep the individual cases smaller. */
+ auto dispatch = [&](auto math_function) -> bool {
+ callback(math_function, *info);
+ return true;
+ };
+
+ switch (operation) {
+ case NODE_MATH_ADD:
+ return dispatch([](float a, float b) { return a + b; });
+ case NODE_MATH_SUBTRACT:
+ return dispatch([](float a, float b) { return a - b; });
+ case NODE_MATH_MULTIPLY:
+ return dispatch([](float a, float b) { return a * b; });
+ case NODE_MATH_DIVIDE:
+ return dispatch([](float a, float b) { return safe_divide(a, b); });
+ case NODE_MATH_POWER:
+ return dispatch([](float a, float b) { return safe_powf(a, b); });
+ case NODE_MATH_LOGARITHM:
+ return dispatch([](float a, float b) { return safe_logf(a, b); });
+ case NODE_MATH_MINIMUM:
+ return dispatch([](float a, float b) { return std::min(a, b); });
+ case NODE_MATH_MAXIMUM:
+ return dispatch([](float a, float b) { return std::max(a, b); });
+ case NODE_MATH_LESS_THAN:
+ return dispatch([](float a, float b) { return (float)(a < b); });
+ case NODE_MATH_GREATER_THAN:
+ return dispatch([](float a, float b) { return (float)(a > b); });
+ case NODE_MATH_MODULO:
+ return dispatch([](float a, float b) { return safe_modf(a, b); });
+ case NODE_MATH_SNAP:
+ return dispatch([](float a, float b) { return floorf(safe_divide(a, b)) * b; });
+ case NODE_MATH_ARCTAN2:
+ return dispatch([](float a, float b) { return atan2f(a, b); });
+ case NODE_MATH_PINGPONG:
+ return dispatch([](float a, float b) { return pingpongf(a, b); });
+ }
+ return false;
+}
+
+/**
+ * This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
+ */
+template<typename Callback>
+inline bool try_dispatch_float_math_fl_fl_fl_to_fl(const int operation, Callback &&callback)
+{
+ const FloatMathOperationInfo *info = get_float_math_operation_info(operation);
+ if (info == nullptr) {
+ return false;
+ }
+
+ /* This is just an utility function to keep the individual cases smaller. */
+ auto dispatch = [&](auto math_function) -> bool {
+ callback(math_function, *info);
+ return true;
+ };
+
+ switch (operation) {
+ case NODE_MATH_MULTIPLY_ADD:
+ return dispatch([](float a, float b, float c) { return a * b + c; });
+ case NODE_MATH_COMPARE:
+ return dispatch([](float a, float b, float c) -> float {
+ return ((a == b) || (fabsf(a - b) <= fmaxf(c, FLT_EPSILON))) ? 1.0f : 0.0f;
+ });
+ case NODE_MATH_SMOOTH_MIN:
+ return dispatch([](float a, float b, float c) { return smoothminf(a, b, c); });
+ case NODE_MATH_SMOOTH_MAX:
+ return dispatch([](float a, float b, float c) { return -smoothminf(-a, -b, -c); });
+ case NODE_MATH_WRAP:
+ return dispatch([](float a, float b, float c) { return wrapf(a, b, c); });
+ }
+ return false;
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh
index 25787231afa..552ef5509fa 100644
--- a/source/blender/nodes/NOD_node_tree_multi_function.hh
+++ b/source/blender/nodes/NOD_node_tree_multi_function.hh
@@ -26,21 +26,12 @@
#include "FN_multi_function_network.hh"
#include "NOD_derived_node_tree.hh"
+#include "NOD_type_callbacks.hh"
#include "BLI_resource_collector.hh"
namespace blender::nodes {
-/* Maybe this should be moved to BKE_node.h. */
-inline bool is_multi_function_data_socket(const bNodeSocket *bsocket)
-{
- if (bsocket->typeinfo->get_mf_data_type != nullptr) {
- BLI_assert(bsocket->typeinfo->expand_in_mf_network != nullptr);
- return true;
- }
- return false;
-}
-
/**
* A MFNetworkTreeMap maps various components of a DerivedNodeTree to components of a
* fn::MFNetwork. This is necessary for further processing of a multi-function network that has
@@ -149,7 +140,7 @@ class MFNetworkTreeMap {
if (!dsocket->is_available()) {
continue;
}
- if (!is_multi_function_data_socket(dsocket->bsocket())) {
+ if (!socket_is_mf_data_socket(*dsocket->bsocket()->typeinfo)) {
continue;
}
fn::MFSocket *socket = sockets[used_sockets];
@@ -299,6 +290,11 @@ class SocketMFNetworkBuilder : public MFNetworkBuilderBase {
{
this->construct_generator_fn<fn::CustomMF_Constant<T>>(std::move(value));
}
+ void set_constant_value(const CPPType &type, const void *value)
+ {
+ /* The value has live as long as the generated mf network. */
+ this->construct_generator_fn<fn::CustomMF_GenericConstant>(type, value);
+ }
template<typename T, typename... Args> void construct_generator_fn(Args &&... args)
{
@@ -397,4 +393,37 @@ MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
const DerivedNodeTree &tree,
ResourceCollector &resources);
+using MultiFunctionByNode = Map<const DNode *, const fn::MultiFunction *>;
+MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
+ ResourceCollector &resources);
+
+class DataTypeConversions {
+ private:
+ Map<std::pair<fn::MFDataType, fn::MFDataType>, const fn::MultiFunction *> conversions_;
+
+ public:
+ void add(fn::MFDataType from_type, fn::MFDataType to_type, const fn::MultiFunction &fn)
+ {
+ conversions_.add_new({from_type, to_type}, &fn);
+ }
+
+ const fn::MultiFunction *get_conversion(fn::MFDataType from, fn::MFDataType to) const
+ {
+ return conversions_.lookup_default({from, to}, nullptr);
+ }
+
+ bool is_convertible(const CPPType &from_type, const CPPType &to_type) const
+ {
+ return conversions_.contains(
+ {fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)});
+ }
+
+ void convert(const CPPType &from_type,
+ const CPPType &to_type,
+ const void *from_value,
+ void *to_value) const;
+};
+
+const DataTypeConversions &get_implicit_type_conversions();
+
} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh
index 3b085248a39..ebdd8b7fe49 100644
--- a/source/blender/nodes/NOD_node_tree_ref.hh
+++ b/source/blender/nodes/NOD_node_tree_ref.hh
@@ -101,6 +101,7 @@ class SocketRef : NonCopyable, NonMovable {
StringRefNull idname() const;
StringRefNull name() const;
+ StringRefNull identifier() const;
bNodeSocket *bsocket() const;
bNode *bnode() const;
@@ -176,6 +177,8 @@ class NodeTreeRef : NonCopyable, NonMovable {
Span<const InputSocketRef *> input_sockets() const;
Span<const OutputSocketRef *> output_sockets() const;
+ bool has_link_cycles() const;
+
bNodeTree *btree() const;
std::string to_dot() const;
@@ -272,6 +275,11 @@ inline StringRefNull SocketRef::name() const
return bsocket_->name;
}
+inline StringRefNull SocketRef::identifier() const
+{
+ return bsocket_->identifier;
+}
+
inline bNodeSocket *SocketRef::bsocket() const
{
return bsocket_;
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 0173706b570..09e0908140c 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -266,6 +266,17 @@ DefNode(FunctionNode, FN_NODE_COMBINE_STRINGS, 0, "COMBINE_STRINGS
DefNode(FunctionNode, FN_NODE_OBJECT_TRANSFORMS, 0, "OBJECT_TRANSFORMS", ObjectTransforms, "Object Transforms", "")
DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
+DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
+DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
+DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
+DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
+DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
+DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, 0, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
+DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, 0, "POINT_INSTANCE", PointInstance, "Point Instance", "")
+DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "")
+DefNode(GeometryNode, GEO_NODE_RANDOM_ATTRIBUTE, def_geo_random_attribute, "RANDOM_ATTRIBUTE", RandomAttribute, "Random Attribute", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
+DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/NOD_type_callbacks.hh b/source/blender/nodes/NOD_type_callbacks.hh
new file mode 100644
index 00000000000..d1a4bd3ad7a
--- /dev/null
+++ b/source/blender/nodes/NOD_type_callbacks.hh
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include "BKE_node.h"
+
+#include "FN_multi_function_data_type.hh"
+
+namespace blender::nodes {
+
+using fn::CPPType;
+using fn::MFDataType;
+
+const CPPType *socket_cpp_type_get(const bNodeSocketType &stype);
+std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype);
+bool socket_is_mf_data_socket(const bNodeSocketType &stype);
+bool socket_cpp_value_get(const bNodeSocket &socket, void *r_value);
+void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder);
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/function/node_function_util.cc b/source/blender/nodes/function/node_function_util.cc
index 342c330a8fa..05b452e61a5 100644
--- a/source/blender/nodes/function/node_function_util.cc
+++ b/source/blender/nodes/function/node_function_util.cc
@@ -20,7 +20,7 @@
bool fn_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
{
/* Function nodes are only supported in simulation node trees so far. */
- return STREQ(ntree->idname, "SimulationNodeTree");
+ return STREQ(ntree->idname, "GeometryNodeTree");
}
void fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
index f8bd9a30940..3d4006b5953 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
@@ -100,7 +100,7 @@ void register_node_type_fn_float_compare()
{
static bNodeType ntype;
- fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Boolean Math", 0, 0);
+ fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Float Compare", 0, 0);
node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out);
node_type_label(&ntype, node_float_compare_label);
node_type_update(&ntype, node_float_compare_update);
diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc
index 584c544946e..f3401a2c00d 100644
--- a/source/blender/nodes/function/nodes/node_fn_random_float.cc
+++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc
@@ -21,7 +21,7 @@
static bNodeSocketTemplate fn_node_random_float_in[] = {
{SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE},
{SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE},
- {SOCK_INT, N_("Seed")},
+ {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};
diff --git a/source/blender/nodes/NOD_simulation.h b/source/blender/nodes/geometry/node_geometry_exec.cc
index 6b3d51b46a9..64a04d79076 100644
--- a/source/blender/nodes/NOD_simulation.h
+++ b/source/blender/nodes/geometry/node_geometry_exec.cc
@@ -14,18 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#pragma once
+#include "NOD_geometry_exec.hh"
-#ifdef __cplusplus
-extern "C" {
-#endif
+MAKE_CPP_TYPE(GeometrySet, GeometrySet);
-extern struct bNodeTreeType *ntreeType_Simulation;
+namespace blender::nodes {
-void register_node_tree_type_sim(void);
-
-void register_node_type_sim_group(void);
-
-#ifdef __cplusplus
}
-#endif
diff --git a/source/blender/nodes/simulation/node_simulation_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index eb3257d7e66..d4a9805f311 100644
--- a/source/blender/nodes/simulation/node_simulation_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -18,7 +18,7 @@
#include "MEM_guardedalloc.h"
-#include "NOD_simulation.h"
+#include "NOD_geometry.h"
#include "BKE_node.h"
@@ -28,18 +28,18 @@
#include "RNA_access.h"
-bNodeTreeType *ntreeType_Simulation;
+bNodeTreeType *ntreeType_Geometry;
-void register_node_tree_type_sim(void)
+void register_node_tree_type_geo(void)
{
- bNodeTreeType *tt = ntreeType_Simulation = static_cast<bNodeTreeType *>(
- MEM_callocN(sizeof(bNodeTreeType), "simulation node tree type"));
- tt->type = NTREE_SIMULATION;
- strcpy(tt->idname, "SimulationNodeTree");
- strcpy(tt->ui_name, N_("Simulation Editor"));
+ bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>(
+ MEM_callocN(sizeof(bNodeTreeType), "geometry node tree type"));
+ tt->type = NTREE_GEOMETRY;
+ strcpy(tt->idname, "GeometryNodeTree");
+ strcpy(tt->ui_name, N_("Geometry Node Editor"));
tt->ui_icon = 0; /* defined in drawnode.c */
- strcpy(tt->ui_description, N_("Simulation nodes"));
- tt->rna_ext.srna = &RNA_SimulationNodeTree;
+ strcpy(tt->ui_description, N_("Geometry nodes"));
+ tt->rna_ext.srna = &RNA_GeometryNodeTree;
ntreeTypeAdd(tt);
}
diff --git a/source/blender/nodes/simulation/node_simulation_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc
index ae875335da8..e8d2494f91d 100644
--- a/source/blender/nodes/simulation/node_simulation_util.cc
+++ b/source/blender/nodes/geometry/node_geometry_util.cc
@@ -14,16 +14,16 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_simulation_util.h"
+#include "node_geometry_util.hh"
#include "node_util.h"
-bool sim_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool geo_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
{
- return STREQ(ntree->idname, "SimulationNodeTree");
+ return STREQ(ntree->idname, "GeometryNodeTree");
}
-void sim_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
+void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
{
node_type_base(ntype, type, name, nclass, flag);
- ntype->poll = sim_node_poll_default;
+ ntype->poll = geo_node_poll_default;
}
diff --git a/source/blender/nodes/simulation/node_simulation_util.h b/source/blender/nodes/geometry/node_geometry_util.hh
index 76a10715cff..bb26763642b 100644
--- a/source/blender/nodes/simulation/node_simulation_util.h
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -18,6 +18,7 @@
#include <string.h>
+#include "BLI_float3.hh"
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
@@ -28,10 +29,11 @@
#include "BLT_translation.h"
-#include "NOD_simulation.h"
+#include "NOD_geometry.h"
+#include "NOD_geometry_exec.hh"
#include "node_util.h"
-void sim_node_type_base(
+void geo_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
-bool sim_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool geo_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
new file mode 100644
index 00000000000..5e2830d2f4e
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
@@ -0,0 +1,167 @@
+/*
+ * 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 "node_geometry_util.hh"
+
+#include "BKE_attribute.h"
+#include "BKE_attribute_access.hh"
+
+#include "BLI_array.hh"
+#include "BLI_math_base_safe.h"
+#include "BLI_rand.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "NOD_math_functions.hh"
+
+static bNodeSocketTemplate geo_node_attribute_math_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute A")},
+ {SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_STRING, N_("Attribute B")},
+ {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_math_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = NODE_MATH_ADD;
+ node->custom2 = GEO_NODE_USE_ATTRIBUTE_A | GEO_NODE_USE_ATTRIBUTE_B;
+}
+
+static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sock_attribute_a = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ bNodeSocket *sock_float_a = sock_attribute_a->next;
+ bNodeSocket *sock_attribute_b = sock_float_a->next;
+ bNodeSocket *sock_float_b = sock_attribute_b->next;
+
+ GeometryNodeUseAttributeFlag flag = static_cast<GeometryNodeUseAttributeFlag>(node->custom2);
+
+ nodeSetSocketAvailability(sock_attribute_a, flag & GEO_NODE_USE_ATTRIBUTE_A);
+ nodeSetSocketAvailability(sock_attribute_b, flag & GEO_NODE_USE_ATTRIBUTE_B);
+ nodeSetSocketAvailability(sock_float_a, !(flag & GEO_NODE_USE_ATTRIBUTE_A));
+ nodeSetSocketAvailability(sock_float_b, !(flag & GEO_NODE_USE_ATTRIBUTE_B));
+}
+
+namespace blender::nodes {
+
+static void do_math_operation(const FloatReadAttribute &input_a,
+ const FloatReadAttribute &input_b,
+ FloatWriteAttribute result,
+ const int 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();
+
+ 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;
+ }
+ });
+
+ result.apply_span();
+
+ /* The operation is not supported by this node currently. */
+ BLI_assert(success);
+ UNUSED_VARS_NDEBUG(success);
+}
+
+static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecParams &params)
+{
+ const bNode &node = params.node();
+ const int operation = node.custom1;
+
+ /* The result type of this node is always float. */
+ const CustomDataType result_type = CD_PROP_FLOAT;
+ /* The result domain is always point for now. */
+ const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
+
+ /* Get result attribute first, in case it has to overwrite one of the existing attributes. */
+ const std::string result_name = params.get_input<std::string>("Result");
+ WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write(
+ result_name, result_domain, result_type);
+ if (!attribute_result) {
+ return;
+ }
+
+ GeometryNodeUseAttributeFlag flag = static_cast<GeometryNodeUseAttributeFlag>(node.custom2);
+
+ auto get_input_attribute = [&](GeometryNodeUseAttributeFlag use_flag,
+ StringRef attribute_socket_identifier,
+ StringRef value_socket_identifier) {
+ if (flag & use_flag) {
+ const std::string attribute_name = params.get_input<std::string>(
+ attribute_socket_identifier);
+ return component.attribute_try_get_for_read(attribute_name, result_domain, result_type);
+ }
+ const float value = params.get_input<float>(value_socket_identifier);
+ return component.attribute_get_constant_for_read(result_domain, result_type, &value);
+ };
+
+ ReadAttributePtr attribute_a = get_input_attribute(GEO_NODE_USE_ATTRIBUTE_A, "Attribute A", "A");
+ ReadAttributePtr attribute_b = get_input_attribute(GEO_NODE_USE_ATTRIBUTE_B, "Attribute B", "B");
+
+ if (!attribute_a || !attribute_b) {
+ /* Attribute wasn't found. */
+ return;
+ }
+
+ do_math_operation(
+ std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
+}
+
+static void geo_node_attribute_math_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (geometry_set.has<MeshComponent>()) {
+ attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_math()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_MATH, "Attribute Math", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_attribute_math_in, geo_node_attribute_math_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_math_exec;
+ node_type_update(&ntype, geo_node_attribute_math_update);
+ node_type_init(&ntype, geo_node_attribute_math_init);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
new file mode 100644
index 00000000000..a0ba8e3bf81
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -0,0 +1,152 @@
+/*
+ * 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 "MEM_guardedalloc.h"
+
+#include "BLI_alloca.h"
+#include "BLI_math_matrix.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
+
+#include "RNA_enum_types.h"
+
+#include "BKE_mesh.h"
+
+#include "bmesh.h"
+#include "tools/bmesh_boolean.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_boolean_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry A")},
+ {SOCK_GEOMETRY, N_("Geometry B")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_boolean_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
+{
+ return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 1 : 0;
+}
+
+static Mesh *mesh_boolean_calc(const Mesh *mesh_a, const Mesh *mesh_b, int boolean_mode)
+{
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh_a, mesh_b);
+
+ BMesh *bm;
+ {
+ struct BMeshCreateParams bmesh_create_params = {0};
+ bmesh_create_params.use_toolflags = false;
+ bm = BM_mesh_create(&allocsize, &bmesh_create_params);
+ }
+
+ {
+ struct BMeshFromMeshParams bmesh_from_mesh_params = {0};
+ bmesh_from_mesh_params.calc_face_normal = true;
+ BM_mesh_bm_from_me(bm, mesh_b, &bmesh_from_mesh_params);
+ BM_mesh_bm_from_me(bm, mesh_a, &bmesh_from_mesh_params);
+ }
+
+ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
+ int tottri;
+ BMLoop *(*looptris)[3] = (BMLoop *
+ (*)[3])(MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__));
+ BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
+
+ const int i_faces_end = mesh_b->totpoly;
+
+ /* We need face normals because of 'BM_face_split_edgenet'
+ * we could calculate on the fly too (before calling split). */
+
+ int i = 0;
+ BMIter iter;
+ BMFace *bm_face;
+ BM_ITER_MESH (bm_face, &iter, bm, BM_FACES_OF_MESH) {
+ normalize_v3(bm_face->no);
+
+ /* Temp tag to test which side split faces are from. */
+ BM_elem_flag_enable(bm_face, BM_ELEM_DRAW);
+
+ i++;
+ if (i == i_faces_end) {
+ break;
+ }
+ }
+
+ BM_mesh_boolean(
+ bm, looptris, tottri, bm_face_isect_pair, nullptr, 2, false, false, boolean_mode);
+
+ Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh_a);
+ BM_mesh_free(bm);
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ MEM_freeN(looptris);
+
+ return result;
+}
+
+namespace blender::nodes {
+static void geo_node_boolean_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set_in_a = params.extract_input<GeometrySet>("Geometry A");
+ GeometrySet geometry_set_in_b = params.extract_input<GeometrySet>("Geometry B");
+ GeometrySet geometry_set_out;
+
+ GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)params.node().custom1;
+ if (operation < 0 || operation > 2) {
+ BLI_assert(false);
+ params.set_output("Geometry", std::move(geometry_set_out));
+ return;
+ }
+
+ const Mesh *mesh_in_a = geometry_set_in_a.get_mesh_for_read();
+ const Mesh *mesh_in_b = geometry_set_in_b.get_mesh_for_read();
+
+ if (mesh_in_a == nullptr || mesh_in_b == nullptr) {
+ if (operation == GEO_NODE_BOOLEAN_UNION) {
+ if (mesh_in_a != nullptr) {
+ params.set_output("Geometry", geometry_set_in_a);
+ }
+ else {
+ params.set_output("Geometry", geometry_set_in_b);
+ }
+ }
+ else {
+ params.set_output("Geometry", geometry_set_in_a);
+ }
+ return;
+ }
+
+ Mesh *mesh_out = mesh_boolean_calc(mesh_in_a, mesh_in_b, operation);
+ geometry_set_out = GeometrySet::create_with_mesh(mesh_out);
+
+ params.set_output("Geometry", std::move(geometry_set_out));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_boolean()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_boolean_in, geo_node_boolean_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_boolean_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/simulation/nodes/node_sim_common.cc b/source/blender/nodes/geometry/nodes/node_geo_common.cc
index fd26d450129..8adc3962698 100644
--- a/source/blender/nodes/simulation/nodes/node_sim_common.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_common.cc
@@ -16,23 +16,23 @@
#include "BKE_node.h"
-#include "NOD_simulation.h"
+#include "NOD_geometry.h"
#include "NOD_common.h"
#include "node_common.h"
-#include "node_simulation_util.h"
+#include "node_geometry_util.hh"
-void register_node_type_sim_group(void)
+void register_node_type_geo_group(void)
{
static bNodeType ntype;
- node_type_base_custom(&ntype, "SimulationNodeGroup", "Group", 0, 0);
+ node_type_base_custom(&ntype, "GeometryNodeGroup", "Group", 0, 0);
ntype.type = NODE_GROUP;
- ntype.poll = sim_node_poll_default;
+ ntype.poll = geo_node_poll_default;
ntype.poll_instance = node_group_poll_instance;
ntype.insert_link = node_insert_link_default;
ntype.update_internal_links = node_update_internal_links_default;
- ntype.rna_ext.srna = RNA_struct_find("SimulationNodeGroup");
+ ntype.rna_ext.srna = RNA_struct_find("GeometryNodeGroup");
BLI_assert(ntype.rna_ext.srna != nullptr);
RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
new file mode 100644
index 00000000000..22e75b3fe03
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -0,0 +1,96 @@
+/*
+ * 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_math_base.h"
+#include "BLI_math_rotation.h"
+
+#include "DNA_modifier_types.h"
+
+#include "node_geometry_util.hh"
+
+extern "C" {
+Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd);
+}
+
+static bNodeSocketTemplate geo_node_edge_split_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_BOOLEAN, N_("Edge Angle"), true},
+ {SOCK_FLOAT,
+ N_("Angle"),
+ DEG2RADF(30.0f),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ DEG2RADF(180.0f),
+ PROP_ANGLE},
+ {SOCK_BOOLEAN, N_("Sharp Edges")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_edge_split_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+static void geo_node_edge_split_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (!geometry_set.has_mesh()) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ const bool use_sharp_flag = params.extract_input<bool>("Sharp Edges");
+ const bool use_edge_angle = params.extract_input<bool>("Edge Angle");
+
+ if (!use_edge_angle && !use_sharp_flag) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ const float split_angle = params.extract_input<float>("Angle");
+ const Mesh *mesh_in = geometry_set.get_mesh_for_read();
+
+ /* Use modifier struct to pass arguments to the modifier code. */
+ EdgeSplitModifierData emd;
+ memset(&emd, 0, sizeof(EdgeSplitModifierData));
+ emd.split_angle = split_angle;
+ if (use_edge_angle) {
+ emd.flags = MOD_EDGESPLIT_FROMANGLE;
+ }
+ if (use_sharp_flag) {
+ emd.flags |= MOD_EDGESPLIT_FROMFLAG;
+ }
+
+ Mesh *mesh_out = doEdgeSplit(mesh_in, &emd);
+ geometry_set.replace_mesh(mesh_out);
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_edge_split()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_EDGE_SPLIT, "Edge Split", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_edge_split_in, geo_node_edge_split_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_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
new file mode 100644
index 00000000000..3bf560f86f5
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -0,0 +1,276 @@
+/*
+ * 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 "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_pointcloud.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_join_geometry_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_join_geometry_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent *> src_components)
+{
+ int totverts = 0;
+ int totloops = 0;
+ int totedges = 0;
+ int totpolys = 0;
+
+ for (const MeshComponent *mesh_component : src_components) {
+ const Mesh *mesh = mesh_component->get_for_read();
+ totverts += mesh->totvert;
+ totloops += mesh->totloop;
+ totedges += mesh->totedge;
+ totpolys += mesh->totpoly;
+ }
+
+ const Mesh *first_input_mesh = src_components[0]->get_for_read();
+ Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
+ BKE_mesh_copy_settings(new_mesh, first_input_mesh);
+
+ int vert_offset = 0;
+ int loop_offset = 0;
+ int edge_offset = 0;
+ int poly_offset = 0;
+ for (const MeshComponent *mesh_component : src_components) {
+ const Mesh *mesh = mesh_component->get_for_read();
+ if (mesh == nullptr) {
+ continue;
+ }
+
+ for (const int i : IndexRange(mesh->totvert)) {
+ const MVert &old_vert = mesh->mvert[i];
+ MVert &new_vert = new_mesh->mvert[vert_offset + i];
+ new_vert = old_vert;
+ }
+
+ for (const int i : IndexRange(mesh->totedge)) {
+ const MEdge &old_edge = mesh->medge[i];
+ MEdge &new_edge = new_mesh->medge[edge_offset + i];
+ new_edge = old_edge;
+ new_edge.v1 += vert_offset;
+ new_edge.v2 += vert_offset;
+ }
+ for (const int i : IndexRange(mesh->totloop)) {
+ const MLoop &old_loop = mesh->mloop[i];
+ MLoop &new_loop = new_mesh->mloop[loop_offset + i];
+ new_loop = old_loop;
+ new_loop.v += vert_offset;
+ new_loop.e += edge_offset;
+ }
+ for (const int i : IndexRange(mesh->totpoly)) {
+ const MPoly &old_poly = mesh->mpoly[i];
+ MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
+ new_poly = old_poly;
+ new_poly.loopstart += loop_offset;
+ }
+
+ vert_offset += mesh->totvert;
+ loop_offset += mesh->totloop;
+ edge_offset += mesh->totedge;
+ poly_offset += mesh->totpoly;
+ }
+
+ return new_mesh;
+}
+
+template<typename Component>
+static Array<const GeometryComponent *> to_base_components(Span<const Component *> components)
+{
+ return components;
+}
+
+static Set<std::string> find_all_attribute_names(Span<const GeometryComponent *> components)
+{
+ Set<std::string> attribute_names;
+ for (const GeometryComponent *component : components) {
+ Set<std::string> names = component->attribute_names();
+ for (const std::string &name : names) {
+ attribute_names.add(name);
+ }
+ }
+ return attribute_names;
+}
+
+static void determine_final_data_type_and_domain(Span<const GeometryComponent *> components,
+ StringRef attribute_name,
+ CustomDataType *r_type,
+ AttributeDomain *r_domain)
+{
+ 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());
+ /* TODO: Use highest priority domain. */
+ *r_domain = attribute->domain();
+ return;
+ }
+ }
+ BLI_assert(false);
+}
+
+static void fill_new_attribute(Span<const GeometryComponent *> src_components,
+ StringRef attribute_name,
+ const CustomDataType data_type,
+ const AttributeDomain domain,
+ fn::GMutableSpan dst_span)
+{
+ const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
+ BLI_assert(cpp_type != nullptr);
+
+ int offset = 0;
+ for (const GeometryComponent *component : src_components) {
+ const int domain_size = component->attribute_domain_size(domain);
+ ReadAttributePtr read_attribute = component->attribute_get_for_read(
+ attribute_name, domain, data_type, nullptr);
+
+ fn::GSpan src_span = read_attribute->get_span();
+ const void *src_buffer = src_span.data();
+ void *dst_buffer = dst_span[offset];
+ cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size);
+
+ offset += domain_size;
+ }
+}
+
+static void join_attributes(Span<const GeometryComponent *> src_components,
+ GeometryComponent &result,
+ Span<StringRef> ignored_attributes = {})
+{
+ Set<std::string> attribute_names = find_all_attribute_names(src_components);
+ for (StringRef name : ignored_attributes) {
+ attribute_names.remove(name);
+ }
+
+ for (const std::string &attribute_name : attribute_names) {
+ CustomDataType data_type;
+ AttributeDomain domain;
+ determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain);
+
+ result.attribute_try_create(attribute_name, domain, data_type);
+ WriteAttributePtr write_attribute = result.attribute_try_get_for_write(attribute_name);
+ if (!write_attribute ||
+ &write_attribute->cpp_type() != bke::custom_data_type_to_cpp_type(data_type) ||
+ write_attribute->domain() != domain) {
+ continue;
+ }
+ fn::GMutableSpan dst_span = write_attribute->get_span();
+ fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span);
+ write_attribute->apply_span();
+ }
+}
+
+static void join_components(Span<const MeshComponent *> src_components, GeometrySet &result)
+{
+ Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(src_components);
+
+ MeshComponent &dst_component = result.get_component_for_write<MeshComponent>();
+ dst_component.replace(new_mesh);
+
+ /* The position attribute is handled above already. */
+ join_attributes(to_base_components(src_components), dst_component, {"position"});
+}
+
+static void join_components(Span<const PointCloudComponent *> src_components, GeometrySet &result)
+{
+ int totpoints = 0;
+ for (const PointCloudComponent *pointcloud_component : src_components) {
+ totpoints += pointcloud_component->attribute_domain_size(ATTR_DOMAIN_POINT);
+ }
+
+ PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>();
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoints);
+ dst_component.replace(pointcloud);
+
+ join_attributes(to_base_components(src_components), dst_component);
+}
+
+static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result)
+{
+ InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
+ for (const InstancesComponent *component : src_components) {
+ const int size = component->instances_amount();
+ Span<const Object *> objects = component->objects();
+ Span<float3> positions = component->positions();
+ Span<float3> rotations = component->rotations();
+ Span<float3> scales = component->scales();
+ for (const int i : IndexRange(size)) {
+ dst_component.add_instance(objects[i], positions[i], rotations[i], scales[i]);
+ }
+ }
+}
+
+template<typename Component>
+static void join_component_type(Span<const GeometrySet *> src_geometry_sets, GeometrySet &result)
+{
+ Vector<const Component *> components;
+ for (const GeometrySet *geometry_set : src_geometry_sets) {
+ const Component *component = geometry_set->get_component_for_read<Component>();
+ if (component != nullptr && !component->is_empty()) {
+ components.append(component);
+ }
+ }
+
+ if (components.size() == 0) {
+ return;
+ }
+ if (components.size() == 1) {
+ result.add(*components[0]);
+ return;
+ }
+ join_components(components, result);
+}
+
+static void geo_node_join_geometry_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set_a = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set_b = params.extract_input<GeometrySet>("Geometry_001");
+ GeometrySet geometry_set_result;
+
+ std::array<const GeometrySet *, 2> src_geometry_sets = {&geometry_set_a, &geometry_set_b};
+
+ 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);
+
+ params.set_output("Geometry", std::move(geometry_set_result));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_join_geometry()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_JOIN_GEOMETRY, "Join Geometry", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_join_geometry_in, geo_node_join_geometry_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_join_geometry_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
new file mode 100644
index 00000000000..8d80e1ce40a
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -0,0 +1,94 @@
+/*
+ * 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 "node_geometry_util.hh"
+
+#include "BKE_mesh.h"
+#include "BKE_mesh_wrapper.h"
+#include "BKE_modifier.h"
+
+#include "BLI_math_matrix.h"
+
+static bNodeSocketTemplate geo_node_object_info_in[] = {
+ {SOCK_OBJECT, N_("Object")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_object_info_out[] = {
+ {SOCK_VECTOR, N_("Location")},
+ {SOCK_VECTOR, N_("Rotation")},
+ {SOCK_VECTOR, N_("Scale")},
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+static void geo_node_object_info_exec(GeoNodeExecParams params)
+{
+ bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
+ "Object");
+ Object *object = params.handle_map().lookup(object_handle);
+
+ float3 location = {0, 0, 0};
+ float3 rotation = {0, 0, 0};
+ float3 scale = {0, 0, 0};
+ GeometrySet geometry_set;
+
+ const Object *self_object = params.self_object();
+
+ if (object != nullptr) {
+ float quaternion[4];
+ mat4_decompose(location, quaternion, scale, object->obmat);
+ quat_to_eul(rotation, quaternion);
+
+ if (object != self_object) {
+ if (object->type == OB_MESH) {
+ Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object, false);
+ if (mesh != nullptr) {
+ BKE_mesh_wrapper_ensure_mdata(mesh);
+
+ /* Make a copy because the life time of the other mesh might be shorter. */
+ Mesh *copied_mesh = BKE_mesh_copy_for_eval(mesh, false);
+
+ /* Transform into the local space of the object that is being modified. */
+ float transform[4][4];
+ mul_m4_m4m4(transform, self_object->imat, object->obmat);
+ BKE_mesh_transform(copied_mesh, transform, true);
+
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_component.replace(copied_mesh);
+ mesh_component.copy_vertex_group_names_from_object(*object);
+ }
+ }
+ }
+ }
+
+ params.set_output("Location", location);
+ params.set_output("Rotation", rotation);
+ params.set_output("Scale", scale);
+ params.set_output("Geometry", geometry_set);
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_object_info()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_OBJECT_INFO, "Object Info", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_object_info_in, geo_node_object_info_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_object_info_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
new file mode 100644
index 00000000000..7f94ca35e6e
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -0,0 +1,139 @@
+/*
+ * 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_float3.hh"
+#include "BLI_hash.h"
+#include "BLI_math_vector.h"
+#include "BLI_rand.hh"
+#include "BLI_span.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_deform.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_pointcloud.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_point_distribute_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_FLOAT, N_("Density"), 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
+ {SOCK_STRING, N_("Density Attribute")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_point_distribute_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static Vector<float3> scatter_points_from_mesh(const Mesh *mesh,
+ const float density,
+ const FloatReadAttribute &density_factors)
+{
+ /* This only updates a cache and can be considered to be logically const. */
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(mesh));
+ const int looptris_len = BKE_mesh_runtime_looptri_len(mesh);
+
+ Vector<float3> points;
+
+ for (const int looptri_index : IndexRange(looptris_len)) {
+ const MLoopTri &looptri = looptris[looptri_index];
+ 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 float3 v0_pos = mesh->mvert[v0_index].co;
+ const float3 v1_pos = mesh->mvert[v1_index].co;
+ const float3 v2_pos = mesh->mvert[v2_index].co;
+ const float v0_density_factor = std::max(0.0f, density_factors[v0_index]);
+ const float v1_density_factor = std::max(0.0f, density_factors[v1_index]);
+ const float v2_density_factor = std::max(0.0f, density_factors[v2_index]);
+ const float looptri_density_factor = (v0_density_factor + v1_density_factor +
+ v2_density_factor) /
+ 3.0f;
+ const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
+
+ const int looptri_seed = BLI_hash_int(looptri_index);
+ RandomNumberGenerator looptri_rng(looptri_seed);
+
+ const float points_amount_fl = area * density * looptri_density_factor;
+ const float add_point_probability = fractf(points_amount_fl);
+ const bool add_point = add_point_probability > looptri_rng.get_float();
+ const int point_amount = (int)points_amount_fl + (int)add_point;
+
+ for (int i = 0; i < point_amount; i++) {
+ const float3 bary_coords = looptri_rng.get_barycentric_coordinates();
+ float3 point_pos;
+ interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coords);
+ points.append(point_pos);
+ }
+ }
+
+ return points;
+}
+
+static void geo_node_point_distribute_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set_out;
+
+ if (!geometry_set.has_mesh()) {
+ params.set_output("Geometry", std::move(geometry_set_out));
+ return;
+ }
+
+ const float density = params.extract_input<float>("Density");
+ const std::string density_attribute = params.extract_input<std::string>("Density Attribute");
+
+ if (density <= 0.0f) {
+ params.set_output("Geometry", std::move(geometry_set_out));
+ return;
+ }
+
+ const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
+ const Mesh *mesh_in = mesh_component.get_for_read();
+
+ const FloatReadAttribute density_factors = mesh_component.attribute_get_for_read<float>(
+ density_attribute, ATTR_DOMAIN_POINT, 1.0f);
+
+ Vector<float3> points = scatter_points_from_mesh(mesh_in, density, density_factors);
+
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(points.size());
+ memcpy(pointcloud->co, points.data(), sizeof(float3) * points.size());
+ for (const int i : points.index_range()) {
+ *(float3 *)(pointcloud->co + i) = points[i];
+ pointcloud->radius[i] = 0.05f;
+ }
+
+ geometry_set_out.replace_pointcloud(pointcloud);
+ params.set_output("Geometry", std::move(geometry_set_out));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_point_distribute()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_POINT_DISTRIBUTE, "Point Distribute", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_point_distribute_in, geo_node_point_distribute_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
new file mode 100644
index 00000000000..bb8f1ff4909
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
@@ -0,0 +1,87 @@
+/*
+ * 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 "BKE_mesh.h"
+#include "BKE_persistent_data_handle.hh"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_point_instance_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_OBJECT, N_("Object")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_point_instance_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void add_instances_from_geometry_component(InstancesComponent &instances,
+ const GeometryComponent &src_geometry,
+ Object *object)
+{
+ Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ Float3ReadAttribute rotations = src_geometry.attribute_get_for_read<float3>(
+ "rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
+ Float3ReadAttribute scales = src_geometry.attribute_get_for_read<float3>(
+ "scale", ATTR_DOMAIN_POINT, {1, 1, 1});
+
+ for (const int i : IndexRange(positions.size())) {
+ instances.add_instance(object, positions[i], rotations[i], scales[i]);
+ }
+}
+
+static void geo_node_point_instance_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set_out;
+
+ bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
+ "Object");
+ Object *object = params.handle_map().lookup(object_handle);
+
+ if (object != nullptr && object != params.self_object()) {
+ InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
+ if (geometry_set.has<MeshComponent>()) {
+ add_instances_from_geometry_component(
+ instances, *geometry_set.get_component_for_read<MeshComponent>(), object);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ add_instances_from_geometry_component(
+ instances, *geometry_set.get_component_for_read<PointCloudComponent>(), object);
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set_out));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_point_instance()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_POINT_INSTANCE, "Point Instance", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_point_instance_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc
new file mode 100644
index 00000000000..68ea5481028
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc
@@ -0,0 +1,160 @@
+/*
+ * 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 "node_geometry_util.hh"
+
+#include "BLI_rand.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_pointcloud_types.h"
+
+static bNodeSocketTemplate geo_node_random_attribute_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_random_attribute_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_random_attribute_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = CD_PROP_FLOAT;
+}
+
+static void geo_node_random_attribute_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
+ bNodeSocket *sock_max_vector = sock_min_vector->next;
+ bNodeSocket *sock_min_float = sock_max_vector->next;
+ bNodeSocket *sock_max_float = sock_min_float->next;
+
+ const int data_type = 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);
+ nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT);
+}
+
+namespace blender::nodes {
+
+static void randomize_attribute(FloatWriteAttribute &attribute,
+ float min,
+ float max,
+ RandomNumberGenerator &rng)
+{
+ MutableSpan<float> attribute_span = attribute.get_span();
+ for (const int i : IndexRange(attribute.size())) {
+ const float value = rng.get_float() * (max - min) + min;
+ attribute_span[i] = value;
+ }
+ attribute.apply_span();
+}
+
+static void randomize_attribute(Float3WriteAttribute &attribute,
+ float3 min,
+ float3 max,
+ RandomNumberGenerator &rng)
+{
+ MutableSpan<float3> attribute_span = attribute.get_span();
+ for (const int i : IndexRange(attribute.size())) {
+ const float x = rng.get_float();
+ const float y = rng.get_float();
+ const float z = rng.get_float();
+ const float3 value = float3(x, y, z) * (max - min) + min;
+ attribute_span[i] = value;
+ }
+ attribute.apply_span();
+}
+
+static void randomize_attribute(GeometryComponent &component,
+ const GeoNodeExecParams &params,
+ RandomNumberGenerator &rng)
+{
+ const bNode &node = params.node();
+ const CustomDataType data_type = static_cast<CustomDataType>(node.custom1);
+ const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2);
+ const std::string attribute_name = params.get_input<std::string>("Attribute");
+ if (attribute_name.empty()) {
+ return;
+ }
+
+ WriteAttributePtr attribute = component.attribute_try_ensure_for_write(
+ attribute_name, domain, data_type);
+ if (!attribute) {
+ return;
+ }
+
+ switch (data_type) {
+ case CD_PROP_FLOAT: {
+ FloatWriteAttribute float_attribute = std::move(attribute);
+ const float min_value = params.get_input<float>("Min_001");
+ const float max_value = params.get_input<float>("Max_001");
+ randomize_attribute(float_attribute, min_value, max_value, rng);
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ Float3WriteAttribute float3_attribute = std::move(attribute);
+ const float3 min_value = params.get_input<float3>("Min");
+ const float3 max_value = params.get_input<float3>("Max");
+ randomize_attribute(float3_attribute, min_value, max_value, rng);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void geo_node_random_attribute_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ const int seed = params.get_input<int>("Seed");
+
+ if (geometry_set.has<MeshComponent>()) {
+ RandomNumberGenerator rng;
+ rng.seed_random(seed);
+ randomize_attribute(geometry_set.get_component_for_write<MeshComponent>(), params, rng);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ RandomNumberGenerator rng;
+ rng.seed_random(seed + 3245231);
+ randomize_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params, rng);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_random_attribute()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_RANDOM_ATTRIBUTE, "Random Attribute", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_random_attribute_in, geo_node_random_attribute_out);
+ node_type_init(&ntype, geo_node_random_attribute_init);
+ node_type_update(&ntype, geo_node_random_attribute_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
new file mode 100644
index 00000000000..dccdf94243e
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -0,0 +1,113 @@
+/*
+ * 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 "MEM_guardedalloc.h"
+
+#include "BKE_subdiv.h"
+#include "BKE_subdiv_mesh.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_subdivision_surface_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6},
+ {SOCK_BOOLEAN, N_("Use Creases")},
+ {SOCK_BOOLEAN, N_("Boundary Smooth")},
+ {SOCK_BOOLEAN, N_("Smooth UVs")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_subdivision_surface_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (!geometry_set.has_mesh()) {
+ params.set_output("Geometry", geometry_set);
+ return;
+ }
+
+#ifndef WITH_OPENSUBDIV
+ /* Return input geometry if Blender is built without OpenSubdiv. */
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+#else
+ const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30);
+
+ /* Only process subdivion if level is greater than 0. */
+ if (subdiv_level == 0) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ const bool use_crease = params.extract_input<bool>("Use Creases");
+ const bool boundary_smooth = params.extract_input<bool>("Boundary Smooth");
+ const bool smooth_uvs = params.extract_input<bool>("Smooth UVs");
+ const Mesh *mesh_in = geometry_set.get_mesh_for_read();
+
+ /* Initialize mesh settings. */
+ SubdivToMeshSettings mesh_settings;
+ mesh_settings.resolution = (1 << subdiv_level) + 1;
+ mesh_settings.use_optimal_display = false;
+
+ /* Initialize subdivision settings. */
+ SubdivSettings subdiv_settings;
+ subdiv_settings.is_simple = false;
+ subdiv_settings.is_adaptive = false;
+ subdiv_settings.use_creases = use_crease;
+ subdiv_settings.level = subdiv_level;
+
+ subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf(
+ boundary_smooth);
+ subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
+ smooth_uvs);
+
+ /* Apply subdivision to mesh. */
+ Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in);
+
+ /* In case of bad topology, skip to input mesh. */
+ if (subdiv == nullptr) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in);
+
+ geometry_set.replace_mesh(mesh_out);
+
+ // BKE_subdiv_stats_print(&subdiv->stats);
+ BKE_subdiv_free(subdiv);
+
+ params.set_output("Geometry", std::move(geometry_set));
+#endif
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_subdivision_surface()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", 0, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_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
new file mode 100644
index 00000000000..6360a3dd9f7
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -0,0 +1,145 @@
+/*
+ * 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_math_matrix.h"
+
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_transform_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
+ {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
+ {SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_transform_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static bool use_translate(const float3 rotation, const float3 scale)
+{
+ if (compare_ff(rotation.length_squared(), 0.0f, 1e-9f) != 1) {
+ return false;
+ }
+ if (compare_ff(scale.x, 1.0f, 1e-9f) != 1 || compare_ff(scale.y, 1.0f, 1e-9f) != 1 ||
+ compare_ff(scale.z, 1.0f, 1e-9f) != 1) {
+ return false;
+ }
+ return true;
+}
+
+static void transform_mesh(Mesh *mesh,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale)
+{
+ /* Use only translation if rotation and scale are zero. */
+ if (use_translate(rotation, scale)) {
+ BKE_mesh_translate(mesh, translation, true);
+ }
+ else {
+ float mat[4][4];
+ loc_eul_size_to_mat4(mat, translation, rotation, scale);
+ BKE_mesh_transform(mesh, mat, true);
+ BKE_mesh_calc_normals(mesh);
+ }
+}
+
+static void transform_pointcloud(PointCloud *pointcloud,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale)
+{
+ /* Use only translation if rotation and scale don't apply. */
+ if (use_translate(rotation, scale)) {
+ for (int i = 0; i < pointcloud->totpoint; i++) {
+ add_v3_v3(pointcloud->co[i], translation);
+ }
+ }
+ else {
+ float mat[4][4];
+ loc_eul_size_to_mat4(mat, translation, rotation, scale);
+ for (int i = 0; i < pointcloud->totpoint; i++) {
+ mul_m4_v3(mat, pointcloud->co[i]);
+ }
+ }
+}
+
+static void transform_instances(InstancesComponent &instances,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale)
+{
+ MutableSpan<float3> positions = instances.positions();
+
+ /* Use only translation if rotation and scale don't apply. */
+ if (use_translate(rotation, scale)) {
+ for (float3 &position : positions) {
+ add_v3_v3(position, translation);
+ }
+ }
+ else {
+ float mat[4][4];
+ loc_eul_size_to_mat4(mat, translation, rotation, scale);
+ for (float3 &position : positions) {
+ mul_m4_v3(mat, position);
+ }
+ }
+}
+
+static void geo_node_transform_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ const float3 translation = params.extract_input<float3>("Translation");
+ const float3 rotation = params.extract_input<float3>("Rotation");
+ const float3 scale = params.extract_input<float3>("Scale");
+
+ if (geometry_set.has_mesh()) {
+ Mesh *mesh = geometry_set.get_mesh_for_write();
+ transform_mesh(mesh, translation, rotation, scale);
+ }
+
+ if (geometry_set.has_pointcloud()) {
+ PointCloud *pointcloud = geometry_set.get_pointcloud_for_write();
+ transform_pointcloud(pointcloud, translation, rotation, scale);
+ }
+
+ if (geometry_set.has_instances()) {
+ InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ transform_instances(instances, translation, rotation, scale);
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_transform()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_TRANSFORM, "Transform", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_transform_in, geo_node_transform_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_transform_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
new file mode 100644
index 00000000000..cec717e4a61
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -0,0 +1,79 @@
+/*
+ * 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 "DNA_node_types.h"
+
+#include "RNA_enum_types.h"
+
+#include "node_geometry_util.hh"
+
+extern "C" {
+Mesh *triangulate_mesh(Mesh *mesh,
+ const int quad_method,
+ const int ngon_method,
+ const int min_vertices,
+ const int flag);
+}
+
+static bNodeSocketTemplate geo_node_triangulate_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_INT, N_("Minimum Vertices"), 4, 0, 0, 0, 4, 10000},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_triangulate_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ node->custom1 = GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE;
+ node->custom2 = GEO_NODE_TRIANGULATE_NGON_BEAUTY;
+}
+
+namespace blender::nodes {
+static void geo_node_triangulate_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ const int min_vertices = std::max(params.extract_input<int>("Minimum Vertices"), 4);
+
+ GeometryNodeTriangulateQuads quad_method = static_cast<GeometryNodeTriangulateQuads>(
+ params.node().custom1);
+ GeometryNodeTriangulateNGons ngon_method = static_cast<GeometryNodeTriangulateNGons>(
+ params.node().custom2);
+
+ /* #triangulate_mesh might modify the input mesh currently. */
+ Mesh *mesh_in = geometry_set.get_mesh_for_write();
+ if (mesh_in != nullptr) {
+ Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0);
+ geometry_set.replace_mesh(mesh_out);
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_triangulate()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_TRIANGULATE, "Triangulate", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_triangulate_in, geo_node_triangulate_out);
+ node_type_init(&ntype, geo_triangulate_init);
+ ntype.geometry_node_execute = blender::nodes::geo_node_triangulate_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc
index 4612a479ebc..5a380897e3f 100644
--- a/source/blender/nodes/intern/derived_node_tree.cc
+++ b/source/blender/nodes/intern/derived_node_tree.cc
@@ -28,9 +28,12 @@ static const NodeTreeRef &get_tree_ref(NodeTreeRefMap &node_tree_refs, bNodeTree
[&]() { return std::make_unique<NodeTreeRef>(btree); });
}
-DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs)
+DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs) : btree_(btree)
{
+ BLI_assert(btree != nullptr);
+
const NodeTreeRef &main_tree_ref = get_tree_ref(node_tree_refs, btree);
+ used_node_tree_refs_.add_new(&main_tree_ref);
Vector<DNode *> all_nodes;
Vector<DGroupInput *> all_group_inputs;
@@ -137,6 +140,7 @@ BLI_NOINLINE void DerivedNodeTree::expand_group_node(DNode &group_node,
}
const NodeTreeRef &group_ref = get_tree_ref(node_tree_refs, btree);
+ used_node_tree_refs_.add(&group_ref);
DParentNode &parent = *allocator_.construct<DParentNode>();
parent.id_ = all_parent_nodes.append_and_get_index(&parent);
@@ -358,6 +362,16 @@ DerivedNodeTree::~DerivedNodeTree()
}
}
+bool DerivedNodeTree::has_link_cycles() const
+{
+ for (const NodeTreeRef *tree : used_node_tree_refs_) {
+ if (tree->has_link_cycles()) {
+ return true;
+ }
+ }
+ return false;
+}
+
static dot::Cluster *get_cluster_for_parent(dot::DirectedGraph &graph,
Map<const DParentNode *, dot::Cluster *> &clusters,
const DParentNode *parent)
diff --git a/source/blender/nodes/intern/math_functions.cc b/source/blender/nodes/intern/math_functions.cc
new file mode 100644
index 00000000000..cc5e9547a96
--- /dev/null
+++ b/source/blender/nodes/intern/math_functions.cc
@@ -0,0 +1,117 @@
+/*
+ * 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 "NOD_math_functions.hh"
+
+namespace blender::nodes {
+
+const FloatMathOperationInfo *get_float_math_operation_info(const int operation)
+{
+
+#define RETURN_OPERATION_INFO(title_case_name, shader_name) \
+ { \
+ static const FloatMathOperationInfo info{title_case_name, shader_name}; \
+ return &info; \
+ } \
+ ((void)0)
+
+ switch (operation) {
+ case NODE_MATH_ADD:
+ RETURN_OPERATION_INFO("Add", "math_add");
+ case NODE_MATH_SUBTRACT:
+ RETURN_OPERATION_INFO("Subtract", "math_subtract");
+ case NODE_MATH_MULTIPLY:
+ RETURN_OPERATION_INFO("Multiply", "math_multiply");
+ case NODE_MATH_DIVIDE:
+ RETURN_OPERATION_INFO("Divide", "math_divide");
+ case NODE_MATH_SINE:
+ RETURN_OPERATION_INFO("Sine", "math_sine");
+ case NODE_MATH_COSINE:
+ RETURN_OPERATION_INFO("Cosine", "math_cosine");
+ case NODE_MATH_ARCSINE:
+ RETURN_OPERATION_INFO("Arc Sine", "math_arcsine");
+ case NODE_MATH_ARCCOSINE:
+ RETURN_OPERATION_INFO("Arc Cosine", "math_arccosine");
+ case NODE_MATH_ARCTANGENT:
+ RETURN_OPERATION_INFO("Arc Tangent", "math_arctangent");
+ case NODE_MATH_POWER:
+ RETURN_OPERATION_INFO("Power", "math_power");
+ case NODE_MATH_LOGARITHM:
+ RETURN_OPERATION_INFO("Logarithm", "math_logarithm");
+ case NODE_MATH_MINIMUM:
+ RETURN_OPERATION_INFO("Minimum", "math_minimum");
+ case NODE_MATH_MAXIMUM:
+ RETURN_OPERATION_INFO("Maximum", "math_maximum");
+ case NODE_MATH_ROUND:
+ RETURN_OPERATION_INFO("Round", "math_round");
+ case NODE_MATH_LESS_THAN:
+ RETURN_OPERATION_INFO("Less Than", "math_less_than");
+ case NODE_MATH_GREATER_THAN:
+ RETURN_OPERATION_INFO("Greater Than", "math_greater_than");
+ case NODE_MATH_MODULO:
+ RETURN_OPERATION_INFO("Modulo", "math_modulo");
+ case NODE_MATH_ABSOLUTE:
+ RETURN_OPERATION_INFO("Absolute", "math_absolute");
+ case NODE_MATH_ARCTAN2:
+ RETURN_OPERATION_INFO("Arc Tangent 2", "math_arctan2");
+ case NODE_MATH_FLOOR:
+ RETURN_OPERATION_INFO("Floor", "math_floor");
+ case NODE_MATH_CEIL:
+ RETURN_OPERATION_INFO("Ceil", "math_ceil");
+ case NODE_MATH_FRACTION:
+ RETURN_OPERATION_INFO("Fraction", "math_fraction");
+ case NODE_MATH_SQRT:
+ RETURN_OPERATION_INFO("Sqrt", "math_sqrt");
+ case NODE_MATH_INV_SQRT:
+ RETURN_OPERATION_INFO("Inverse Sqrt", "math_inversesqrt");
+ case NODE_MATH_SIGN:
+ RETURN_OPERATION_INFO("Sign", "math_sign");
+ case NODE_MATH_EXPONENT:
+ RETURN_OPERATION_INFO("Exponent", "math_exponent");
+ case NODE_MATH_RADIANS:
+ RETURN_OPERATION_INFO("Radians", "math_radians");
+ case NODE_MATH_DEGREES:
+ RETURN_OPERATION_INFO("Degrees", "math_degrees");
+ case NODE_MATH_SINH:
+ RETURN_OPERATION_INFO("Hyperbolic Sine", "math_sinh");
+ case NODE_MATH_COSH:
+ RETURN_OPERATION_INFO("Hyperbolic Cosine", "math_cosh");
+ case NODE_MATH_TANH:
+ RETURN_OPERATION_INFO("Hyperbolic Tangent", "math_tanh");
+ case NODE_MATH_TRUNC:
+ RETURN_OPERATION_INFO("Truncate", "math_trunc");
+ case NODE_MATH_SNAP:
+ RETURN_OPERATION_INFO("Snap", "math_snap");
+ case NODE_MATH_WRAP:
+ RETURN_OPERATION_INFO("Wrap", "math_wrap");
+ case NODE_MATH_COMPARE:
+ RETURN_OPERATION_INFO("Compare", "math_compare");
+ case NODE_MATH_MULTIPLY_ADD:
+ RETURN_OPERATION_INFO("Multiply Add", "math_multiply_add");
+ case NODE_MATH_PINGPONG:
+ RETURN_OPERATION_INFO("Ping Pong", "math_pingpong");
+ case NODE_MATH_SMOOTH_MIN:
+ RETURN_OPERATION_INFO("Smooth Min", "math_smoothmin");
+ case NODE_MATH_SMOOTH_MAX:
+ RETURN_OPERATION_INFO("Smooth Max", "math_smoothmax");
+ }
+
+#undef RETURN_OPERATION_INFO
+
+ return nullptr;
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
new file mode 100644
index 00000000000..50292cb8cfb
--- /dev/null
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -0,0 +1,103 @@
+/*
+ * 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 "NOD_geometry_exec.hh"
+#include "NOD_type_callbacks.hh"
+
+namespace blender::nodes {
+
+void GeoNodeExecParams::check_extract_input(StringRef identifier,
+ const CPPType *requested_type) const
+{
+ bNodeSocket *found_socket = nullptr;
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node_.inputs) {
+ if (identifier == socket->identifier) {
+ found_socket = socket;
+ break;
+ }
+ }
+ if (found_socket == nullptr) {
+ std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n";
+ std::cout << "Possible identifiers are: ";
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node_.inputs) {
+ if ((socket->flag & SOCK_UNAVAIL) == 0) {
+ std::cout << "'" << socket->identifier << "', ";
+ }
+ }
+ std::cout << "\n";
+ BLI_assert(false);
+ }
+ else if (found_socket->flag & SOCK_UNAVAIL) {
+ std::cout << "The socket corresponding to the identifier '" << identifier
+ << "' is disabled.\n";
+ BLI_assert(false);
+ }
+ else if (!input_values_.contains(identifier)) {
+ std::cout << "The identifier '" << identifier
+ << "' is valid, but there is no value for it anymore.\n";
+ std::cout << "Most likely it has been extracted before.\n";
+ BLI_assert(false);
+ }
+ else if (requested_type != nullptr) {
+ const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
+ if (*requested_type != expected_type) {
+ std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '"
+ << expected_type.name() << "'.\n";
+ BLI_assert(false);
+ }
+ }
+}
+
+void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &value_type) const
+{
+ bNodeSocket *found_socket = nullptr;
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node_.outputs) {
+ if (identifier == socket->identifier) {
+ found_socket = socket;
+ break;
+ }
+ }
+ if (found_socket == nullptr) {
+ std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n";
+ std::cout << "Possible identifiers are: ";
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node_.outputs) {
+ if ((socket->flag & SOCK_UNAVAIL) == 0) {
+ std::cout << "'" << socket->identifier << "', ";
+ }
+ }
+ std::cout << "\n";
+ BLI_assert(false);
+ }
+ else if (found_socket->flag & SOCK_UNAVAIL) {
+ std::cout << "The socket corresponding to the identifier '" << identifier
+ << "' is disabled.\n";
+ BLI_assert(false);
+ }
+ else if (output_values_.contains(identifier)) {
+ std::cout << "The identifier '" << identifier << "' has been set already.\n";
+ BLI_assert(false);
+ }
+ else {
+ const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
+ if (value_type != expected_type) {
+ std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '"
+ << expected_type.name() << "'.\n";
+ BLI_assert(false);
+ }
+ }
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index b3803b20ac1..d4b1df2f3f0 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -32,6 +32,7 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_node.h"
#include "BKE_persistent_data_handle.hh"
@@ -552,10 +553,9 @@ static bNodeSocketType *make_socket_type_virtual()
static bNodeSocketType *make_socket_type_bool()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE);
- socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<bool>(); };
- socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
- bool value = builder.socket_default_value<bNodeSocketValueBoolean>()->value;
- builder.set_constant_value(value);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<bool>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value;
};
return socktype;
}
@@ -563,10 +563,9 @@ static bNodeSocketType *make_socket_type_bool()
static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype);
- socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<float>(); };
- socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
- float value = builder.socket_default_value<bNodeSocketValueFloat>()->value;
- builder.set_constant_value(value);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<float>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value;
};
return socktype;
}
@@ -574,10 +573,9 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype);
- socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<int>(); };
- socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
- int value = builder.socket_default_value<bNodeSocketValueInt>()->value;
- builder.set_constant_value(value);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<int>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value;
};
return socktype;
}
@@ -585,12 +583,9 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype);
- socktype->get_mf_data_type = []() {
- return blender::fn::MFDataType::ForSingle<blender::float3>();
- };
- socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
- blender::float3 value = builder.socket_default_value<bNodeSocketValueVector>()->value;
- builder.set_constant_value(value);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::float3>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value;
};
return socktype;
}
@@ -598,12 +593,9 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
static bNodeSocketType *make_socket_type_rgba()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE);
- socktype->get_mf_data_type = []() {
- return blender::fn::MFDataType::ForSingle<blender::Color4f>();
- };
- socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
- blender::Color4f value = builder.socket_default_value<bNodeSocketValueRGBA>()->value;
- builder.set_constant_value(value);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::Color4f>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(blender::Color4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
};
return socktype;
}
@@ -611,10 +603,9 @@ static bNodeSocketType *make_socket_type_rgba()
static bNodeSocketType *make_socket_type_string()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE);
- socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<std::string>(); };
- socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
- std::string value = builder.socket_default_value<bNodeSocketValueString>()->value;
- builder.set_constant_value(value);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<std::string>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value);
};
return socktype;
}
@@ -661,9 +652,9 @@ MAKE_CPP_TYPE(PersistentObjectHandle, blender::bke::PersistentObjectHandle);
static bNodeSocketType *make_socket_type_object()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE);
- socktype->get_mf_data_type = []() {
+ socktype->get_cpp_type = []() {
/* Objects are not passed along as raw pointers, but as handles. */
- return blender::fn::MFDataType::ForSingle<blender::bke::PersistentObjectHandle>();
+ return &blender::fn::CPPType::get<blender::bke::PersistentObjectHandle>();
};
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value;
@@ -675,6 +666,10 @@ static bNodeSocketType *make_socket_type_object()
static bNodeSocketType *make_socket_type_geometry()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_GEOMETRY, PROP_NONE);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<GeometrySet>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &UNUSED(socket), void *r_value) {
+ new (r_value) GeometrySet();
+ };
return socktype;
}
diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc
index 09a80fd23f4..8440e996651 100644
--- a/source/blender/nodes/intern/node_tree_multi_function.cc
+++ b/source/blender/nodes/intern/node_tree_multi_function.cc
@@ -16,21 +16,13 @@
#include "NOD_node_tree_multi_function.hh"
+#include "FN_multi_function_network_evaluation.hh"
+
#include "BLI_color.hh"
#include "BLI_float3.hh"
namespace blender::nodes {
-/* Maybe this should be moved to BKE_node.h. */
-static std::optional<fn::MFDataType> try_get_multi_function_data_type_of_socket(
- const bNodeSocket *bsocket)
-{
- if (bsocket->typeinfo->get_mf_data_type == nullptr) {
- return {};
- }
- return bsocket->typeinfo->get_mf_data_type();
-}
-
const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
{
Vector<fn::MFDataType, 10> input_types;
@@ -38,8 +30,7 @@ const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
for (const DInputSocket *dsocket : dnode_.inputs()) {
if (dsocket->is_available()) {
- std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
- dsocket->bsocket());
+ std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
if (data_type.has_value()) {
input_types.append(*data_type);
}
@@ -47,8 +38,7 @@ const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
}
for (const DOutputSocket *dsocket : dnode_.outputs()) {
if (dsocket->is_available()) {
- std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
- dsocket->bsocket());
+ std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
if (data_type.has_value()) {
output_types.append(*data_type);
}
@@ -70,8 +60,7 @@ static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &d
for (const DInputSocket *dsocket : dnode.inputs()) {
if (dsocket->is_available()) {
- std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
- dsocket->bsocket());
+ std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
if (data_type.has_value()) {
input_types.append(*data_type);
input_names.append(dsocket->name());
@@ -86,8 +75,7 @@ static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &d
for (const DOutputSocket *dsocket : dnode.outputs()) {
if (dsocket->is_available()) {
- std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
- dsocket->bsocket());
+ std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
if (data_type.has_value()) {
output_types.append(*data_type);
output_names.append(dsocket->name());
@@ -106,12 +94,12 @@ static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &d
static bool has_data_sockets(const DNode &dnode)
{
for (const DInputSocket *socket : dnode.inputs()) {
- if (is_multi_function_data_socket(socket->bsocket())) {
+ if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) {
return true;
}
}
for (const DOutputSocket *socket : dnode.outputs()) {
- if (is_multi_function_data_socket(socket->bsocket())) {
+ if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) {
return true;
}
}
@@ -140,7 +128,7 @@ static void insert_group_inputs(CommonMFNetworkBuilderData &common)
{
for (const DGroupInput *group_input : common.tree.group_inputs()) {
bNodeSocket *bsocket = group_input->bsocket();
- if (is_multi_function_data_socket(bsocket)) {
+ if (socket_is_mf_data_socket(*bsocket->typeinfo)) {
bNodeSocketType *socktype = bsocket->typeinfo;
BLI_assert(socktype->expand_in_mf_network != nullptr);
@@ -171,44 +159,43 @@ static fn::MFOutputSocket *try_find_origin(CommonMFNetworkBuilderData &common,
if (!from_dsocket.is_available()) {
return nullptr;
}
- if (is_multi_function_data_socket(from_dsocket.bsocket())) {
+ if (socket_is_mf_data_socket(*from_dsocket.bsocket()->typeinfo)) {
return &common.network_map.lookup(from_dsocket);
}
return nullptr;
}
const DGroupInput &from_group_input = *from_group_inputs[0];
- if (is_multi_function_data_socket(from_group_input.bsocket())) {
+ if (socket_is_mf_data_socket(*from_group_input.bsocket()->typeinfo)) {
return &common.network_map.lookup(from_group_input);
}
return nullptr;
}
-using ImplicitConversionsMap =
- Map<std::pair<fn::MFDataType, fn::MFDataType>, const fn::MultiFunction *>;
-
template<typename From, typename To>
-static void add_implicit_conversion(ImplicitConversionsMap &map)
+static void add_implicit_conversion(DataTypeConversions &conversions)
{
static fn::CustomMF_Convert<From, To> function;
- map.add({fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>()}, &function);
+ conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
}
template<typename From, typename To, typename ConversionF>
-static void add_implicit_conversion(ImplicitConversionsMap &map,
+static void add_implicit_conversion(DataTypeConversions &conversions,
StringRef name,
ConversionF conversion)
{
static fn::CustomMF_SI_SO<From, To> function{name, conversion};
- map.add({fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>()}, &function);
+ conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
}
-static ImplicitConversionsMap get_implicit_conversions()
+static DataTypeConversions create_implicit_conversions()
{
- ImplicitConversionsMap conversions;
+ DataTypeConversions conversions;
add_implicit_conversion<float, int32_t>(conversions);
add_implicit_conversion<float, float3>(conversions);
add_implicit_conversion<int32_t, float>(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>(
@@ -220,11 +207,26 @@ static ImplicitConversionsMap get_implicit_conversions()
return conversions;
}
-static const fn::MultiFunction *try_get_conversion_function(fn::MFDataType from, fn::MFDataType to)
+const DataTypeConversions &get_implicit_type_conversions()
{
- static const ImplicitConversionsMap conversions = get_implicit_conversions();
- const fn::MultiFunction *function = conversions.lookup_default({from, to}, nullptr);
- return function;
+ static const DataTypeConversions conversions = create_implicit_conversions();
+ return conversions;
+}
+
+void DataTypeConversions::convert(const CPPType &from_type,
+ const CPPType &to_type,
+ const void *from_value,
+ void *to_value) const
+{
+ const fn::MultiFunction *fn = this->get_conversion(MFDataType::ForSingle(from_type),
+ MFDataType::ForSingle(to_type));
+ BLI_assert(fn != nullptr);
+
+ fn::MFContextBuilder context;
+ fn::MFParamsBuilder params{*fn, 1};
+ params.add_readonly_single_input(fn::GSpan(from_type, from_value, 1));
+ params.add_uninitialized_single_output(fn::GMutableSpan(to_type, to_value, 1));
+ fn->call({0}, params, context);
}
static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common,
@@ -253,7 +255,7 @@ static void insert_links(CommonMFNetworkBuilderData &common)
if (!to_dsocket->is_linked()) {
continue;
}
- if (!is_multi_function_data_socket(to_dsocket->bsocket())) {
+ if (!socket_is_mf_data_socket(*to_dsocket->bsocket()->typeinfo)) {
continue;
}
@@ -269,7 +271,8 @@ static void insert_links(CommonMFNetworkBuilderData &common)
fn::MFDataType from_type = from_socket->data_type();
if (from_type != to_type) {
- const fn::MultiFunction *conversion_fn = try_get_conversion_function(from_type, to_type);
+ const fn::MultiFunction *conversion_fn = get_implicit_type_conversions().get_conversion(
+ from_type, to_type);
if (conversion_fn != nullptr) {
fn::MFNode &node = common.network.add_function(*conversion_fn);
common.network.add_link(*from_socket, node.input(0));
@@ -308,7 +311,7 @@ static void insert_unlinked_inputs(CommonMFNetworkBuilderData &common)
Vector<const DInputSocket *> unlinked_data_inputs;
for (const DInputSocket *dsocket : common.tree.input_sockets()) {
if (dsocket->is_available()) {
- if (is_multi_function_data_socket(dsocket->bsocket())) {
+ if (socket_is_mf_data_socket(*dsocket->bsocket()->typeinfo)) {
if (!dsocket->is_linked()) {
insert_unlinked_input(common, *dsocket);
}
@@ -340,4 +343,150 @@ MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
return network_map;
}
+/**
+ * A single node is allowed to expand into multiple nodes before evaluation. Depending on what
+ * nodes it expands to, it belongs a different type of the ones below.
+ */
+enum class NodeExpandType {
+ SingleFunctionNode,
+ MultipleFunctionNodes,
+ HasDummyNodes,
+};
+
+/**
+ * Checks how the given node expanded in the multi-function network. If it is only a single
+ * function node, the corresponding function is returned as well.
+ */
+static NodeExpandType get_node_expand_type(MFNetworkTreeMap &network_map,
+ const DNode &dnode,
+ const fn::MultiFunction **r_single_function)
+{
+ const fn::MFFunctionNode *single_function_node = nullptr;
+ bool has_multiple_nodes = false;
+ bool has_dummy_nodes = false;
+
+ auto check_mf_node = [&](fn::MFNode &mf_node) {
+ if (mf_node.is_function()) {
+ if (single_function_node == nullptr) {
+ single_function_node = &mf_node.as_function();
+ }
+ if (&mf_node != single_function_node) {
+ has_multiple_nodes = true;
+ }
+ }
+ else {
+ BLI_assert(mf_node.is_dummy());
+ has_dummy_nodes = true;
+ }
+ };
+
+ for (const DInputSocket *dsocket : dnode.inputs()) {
+ if (dsocket->is_available()) {
+ for (fn::MFInputSocket *mf_input : network_map.lookup(*dsocket)) {
+ check_mf_node(mf_input->node());
+ }
+ }
+ }
+ for (const DOutputSocket *dsocket : dnode.outputs()) {
+ if (dsocket->is_available()) {
+ fn::MFOutputSocket &mf_output = network_map.lookup(*dsocket);
+ check_mf_node(mf_output.node());
+ }
+ }
+
+ if (has_dummy_nodes) {
+ return NodeExpandType::HasDummyNodes;
+ }
+ if (has_multiple_nodes) {
+ return NodeExpandType::MultipleFunctionNodes;
+ }
+ *r_single_function = &single_function_node->function();
+ return NodeExpandType::SingleFunctionNode;
+}
+
+static const fn::MultiFunction &create_function_for_node_that_expands_into_multiple(
+ const DNode &dnode,
+ fn::MFNetwork &network,
+ MFNetworkTreeMap &network_map,
+ ResourceCollector &resources)
+{
+ Vector<const fn::MFOutputSocket *> dummy_fn_inputs;
+ for (const DInputSocket *dsocket : dnode.inputs()) {
+ if (dsocket->is_available()) {
+ MFDataType data_type = *socket_mf_type_get(*dsocket->typeinfo());
+ fn::MFOutputSocket &fn_input = network.add_input(data_type.to_string(), data_type);
+ for (fn::MFInputSocket *mf_input : network_map.lookup(*dsocket)) {
+ network.add_link(fn_input, *mf_input);
+ dummy_fn_inputs.append(&fn_input);
+ }
+ }
+ }
+ Vector<const fn::MFInputSocket *> dummy_fn_outputs;
+ for (const DOutputSocket *dsocket : dnode.outputs()) {
+ if (dsocket->is_available()) {
+ fn::MFOutputSocket &mf_output = network_map.lookup(*dsocket);
+ MFDataType data_type = mf_output.data_type();
+ fn::MFInputSocket &fn_output = network.add_output(data_type.to_string(), data_type);
+ network.add_link(mf_output, fn_output);
+ dummy_fn_outputs.append(&fn_output);
+ }
+ }
+
+ fn::MFNetworkEvaluator &fn_evaluator = resources.construct<fn::MFNetworkEvaluator>(
+ __func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs));
+ return fn_evaluator;
+}
+
+/**
+ * Returns a single multi-function for every node that supports it. This makes it easier to reuse
+ * the multi-function implementation of nodes in different contexts.
+ */
+MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
+ ResourceCollector &resources)
+{
+ /* Build a network that nodes can insert themselves into. However, the individual nodes are not
+ * connected. */
+ fn::MFNetwork &network = resources.construct<fn::MFNetwork>(__func__);
+ MFNetworkTreeMap network_map{tree, network};
+ MultiFunctionByNode functions_by_node;
+
+ CommonMFNetworkBuilderData common{resources, network, network_map, tree};
+
+ for (const DNode *dnode : tree.nodes()) {
+ const bNodeType *node_type = dnode->typeinfo();
+ if (node_type->expand_in_mf_network == nullptr) {
+ /* This node does not have a multi-function implementation. */
+ continue;
+ }
+
+ NodeMFNetworkBuilder builder{common, *dnode};
+ node_type->expand_in_mf_network(builder);
+
+ const fn::MultiFunction *single_function = nullptr;
+ const NodeExpandType expand_type = get_node_expand_type(network_map, *dnode, &single_function);
+
+ switch (expand_type) {
+ case NodeExpandType::HasDummyNodes: {
+ /* Dummy nodes cannot be executed, so skip them. */
+ break;
+ }
+ case NodeExpandType::SingleFunctionNode: {
+ /* This is the common case. Most nodes just expand to a single function. */
+ functions_by_node.add_new(dnode, single_function);
+ break;
+ }
+ case NodeExpandType::MultipleFunctionNodes: {
+ /* If a node expanded into multiple functions, a new function has to be created that
+ * combines those. */
+ const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple(
+ *dnode, network, network_map, resources);
+ functions_by_node.add_new(dnode, &fn);
+ break;
+ }
+ }
+ }
+
+ return functions_by_node;
+}
+
} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
index 96ad1e0280e..9dcd90f9f50 100644
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -137,6 +137,48 @@ void NodeTreeRef::find_targets_skipping_reroutes(OutputSocketRef &socket,
}
}
+static bool has_link_cycles_recursive(const NodeRef &node,
+ MutableSpan<bool> visited,
+ MutableSpan<bool> is_in_stack)
+{
+ const int node_id = node.id();
+ if (is_in_stack[node_id]) {
+ return true;
+ }
+ if (visited[node_id]) {
+ return false;
+ }
+
+ visited[node_id] = true;
+ is_in_stack[node_id] = true;
+
+ for (const OutputSocketRef *from_socket : node.outputs()) {
+ for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
+ const NodeRef &to_node = to_socket->node();
+ if (has_link_cycles_recursive(to_node, visited, is_in_stack)) {
+ return true;
+ }
+ }
+ }
+
+ is_in_stack[node_id] = false;
+ return false;
+}
+
+bool NodeTreeRef::has_link_cycles() const
+{
+ const int node_amount = nodes_by_id_.size();
+ Array<bool> visited(node_amount, false);
+ Array<bool> is_in_stack(node_amount, false);
+
+ for (const NodeRef *node : nodes_by_id_) {
+ if (has_link_cycles_recursive(*node, visited, is_in_stack)) {
+ return true;
+ }
+ }
+ return false;
+}
+
std::string NodeTreeRef::to_dot() const
{
dot::DirectedGraph digraph;
diff --git a/source/blender/nodes/intern/type_callbacks.cc b/source/blender/nodes/intern/type_callbacks.cc
new file mode 100644
index 00000000000..1f207f880bc
--- /dev/null
+++ b/source/blender/nodes/intern/type_callbacks.cc
@@ -0,0 +1,76 @@
+/*
+ * 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 "NOD_node_tree_multi_function.hh"
+#include "NOD_type_callbacks.hh"
+
+namespace blender::nodes {
+
+const CPPType *socket_cpp_type_get(const bNodeSocketType &stype)
+{
+ if (stype.get_cpp_type != nullptr) {
+ return stype.get_cpp_type();
+ }
+ return nullptr;
+}
+
+std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype)
+{
+ const CPPType *cpp_type = socket_cpp_type_get(stype);
+ if (cpp_type != nullptr) {
+ return MFDataType::ForSingle(*cpp_type);
+ }
+ return {};
+}
+
+bool socket_is_mf_data_socket(const bNodeSocketType &stype)
+{
+ if (!socket_mf_type_get(stype).has_value()) {
+ return false;
+ }
+ if (stype.expand_in_mf_network == nullptr && stype.get_cpp_value == nullptr) {
+ return false;
+ }
+ return true;
+}
+
+bool socket_cpp_value_get(const bNodeSocket &socket, void *r_value)
+{
+ if (socket.typeinfo->get_cpp_value != nullptr) {
+ socket.typeinfo->get_cpp_value(socket, r_value);
+ return true;
+ }
+ return false;
+}
+
+void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder)
+{
+ bNodeSocket &socket = builder.bsocket();
+ if (socket.typeinfo->expand_in_mf_network != nullptr) {
+ socket.typeinfo->expand_in_mf_network(builder);
+ }
+ else if (socket.typeinfo->get_cpp_value != nullptr) {
+ const CPPType &type = *socket_cpp_type_get(*socket.typeinfo);
+ void *buffer = builder.resources().linear_allocator().allocate(type.size(), type.alignment());
+ socket.typeinfo->get_cpp_value(socket, buffer);
+ builder.set_constant_value(type, buffer);
+ }
+ else {
+ BLI_assert(false);
+ }
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/node_shader_util.c b/source/blender/nodes/shader/node_shader_util.c
index 4464a61c48c..25d6aef69e5 100644
--- a/source/blender/nodes/shader/node_shader_util.c
+++ b/source/blender/nodes/shader/node_shader_util.c
@@ -34,7 +34,7 @@ bool sh_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
static bool sh_fn_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
{
- return STREQ(ntree->idname, "ShaderNodeTree") || STREQ(ntree->idname, "SimulationNodeTree");
+ return STREQ(ntree->idname, "ShaderNodeTree") || STREQ(ntree->idname, "GeometryNodeTree");
}
void sh_node_type_base(
diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc
index e7bbadfbcb0..f54914ceba9 100644
--- a/source/blender/nodes/shader/nodes/node_shader_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_math.cc
@@ -23,6 +23,8 @@
#include "node_shader_util.h"
+#include "NOD_math_functions.hh"
+
/* **************** SCALAR MATH ******************** */
static bNodeSocketTemplate sh_node_math_in[] = {
{SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
@@ -34,93 +36,15 @@ static bNodeSocketTemplate sh_node_math_out[] = {{SOCK_FLOAT, N_("Value")}, {-1,
static const char *gpu_shader_get_name(int mode)
{
- switch (mode) {
- case NODE_MATH_ADD:
- return "math_add";
- case NODE_MATH_SUBTRACT:
- return "math_subtract";
- case NODE_MATH_MULTIPLY:
- return "math_multiply";
- case NODE_MATH_DIVIDE:
- return "math_divide";
- case NODE_MATH_MULTIPLY_ADD:
- return "math_multiply_add";
-
- case NODE_MATH_POWER:
- return "math_power";
- case NODE_MATH_LOGARITHM:
- return "math_logarithm";
- case NODE_MATH_EXPONENT:
- return "math_exponent";
- case NODE_MATH_SQRT:
- return "math_sqrt";
- case NODE_MATH_INV_SQRT:
- return "math_inversesqrt";
- case NODE_MATH_ABSOLUTE:
- return "math_absolute";
- case NODE_MATH_RADIANS:
- return "math_radians";
- case NODE_MATH_DEGREES:
- return "math_degrees";
-
- case NODE_MATH_MINIMUM:
- return "math_minimum";
- case NODE_MATH_MAXIMUM:
- return "math_maximum";
- case NODE_MATH_LESS_THAN:
- return "math_less_than";
- case NODE_MATH_GREATER_THAN:
- return "math_greater_than";
- case NODE_MATH_SIGN:
- return "math_sign";
- case NODE_MATH_COMPARE:
- return "math_compare";
- case NODE_MATH_SMOOTH_MIN:
- return "math_smoothmin";
- case NODE_MATH_SMOOTH_MAX:
- return "math_smoothmax";
-
- case NODE_MATH_ROUND:
- return "math_round";
- case NODE_MATH_FLOOR:
- return "math_floor";
- case NODE_MATH_CEIL:
- return "math_ceil";
- case NODE_MATH_FRACTION:
- return "math_fraction";
- case NODE_MATH_MODULO:
- return "math_modulo";
- case NODE_MATH_TRUNC:
- return "math_trunc";
- case NODE_MATH_SNAP:
- return "math_snap";
- case NODE_MATH_WRAP:
- return "math_wrap";
- case NODE_MATH_PINGPONG:
- return "math_pingpong";
-
- case NODE_MATH_SINE:
- return "math_sine";
- case NODE_MATH_COSINE:
- return "math_cosine";
- case NODE_MATH_TANGENT:
- return "math_tangent";
- case NODE_MATH_SINH:
- return "math_sinh";
- case NODE_MATH_COSH:
- return "math_cosh";
- case NODE_MATH_TANH:
- return "math_tanh";
- case NODE_MATH_ARCSINE:
- return "math_arcsine";
- case NODE_MATH_ARCCOSINE:
- return "math_arccosine";
- case NODE_MATH_ARCTANGENT:
- return "math_arctangent";
- case NODE_MATH_ARCTAN2:
- return "math_arctan2";
+ const blender::nodes::FloatMathOperationInfo *info =
+ blender::nodes::get_float_math_operation_info(mode);
+ if (!info) {
+ return nullptr;
+ }
+ if (info->shader_name.is_empty()) {
+ return nullptr;
}
- return nullptr;
+ return info->shader_name.c_str();
}
static int gpu_shader_math(GPUMaterial *mat,
@@ -149,201 +73,39 @@ static const blender::fn::MultiFunction &get_base_multi_function(
blender::nodes::NodeMFNetworkBuilder &builder)
{
const int mode = builder.bnode().custom1;
- switch (mode) {
- case NODE_MATH_ADD: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
- "Add", [](float a, float b) { return a + b; }};
- return fn;
- }
- case NODE_MATH_SUBTRACT: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
- "Subtract", [](float a, float b) { return a - b; }};
- return fn;
- }
- case NODE_MATH_MULTIPLY: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
- "Multiply", [](float a, float b) { return a * b; }};
- return fn;
- }
- case NODE_MATH_DIVIDE: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{"Divide", safe_divide};
- return fn;
- }
- case NODE_MATH_MULTIPLY_ADD: {
- static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
- "Multiply Add", [](float a, float b, float c) { return a * b + c; }};
- return fn;
- }
- case NODE_MATH_POWER: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{"Power", safe_powf};
- return fn;
- }
- case NODE_MATH_LOGARITHM: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{"Logarithm", safe_logf};
- return fn;
- }
- case NODE_MATH_EXPONENT: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Exponent", expf};
- return fn;
- }
- case NODE_MATH_SQRT: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Sqrt", safe_sqrtf};
- return fn;
- }
- case NODE_MATH_INV_SQRT: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Inverse Sqrt", safe_inverse_sqrtf};
- return fn;
- };
- case NODE_MATH_ABSOLUTE: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Absolute",
- [](float a) { return fabs(a); }};
- return fn;
- }
- case NODE_MATH_RADIANS: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Radians",
- [](float a) { return DEG2RAD(a); }};
- return fn;
- }
- case NODE_MATH_DEGREES: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Degrees",
- [](float a) { return RAD2DEG(a); }};
- return fn;
- }
+ const blender::fn::MultiFunction *base_fn = nullptr;
- case NODE_MATH_MINIMUM: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
- "Minimum", [](float a, float b) { return std::min(a, b); }};
- return fn;
- }
- case NODE_MATH_MAXIMUM: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
- "Maximum", [](float a, float b) { return std::max(a, b); }};
- return fn;
- }
- case NODE_MATH_LESS_THAN: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
- "Less Than", [](float a, float b) { return (float)(a < b); }};
- return fn;
- }
- case NODE_MATH_GREATER_THAN: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
- "Greater Than", [](float a, float b) { return (float)(a > b); }};
- return fn;
- }
- case NODE_MATH_SIGN: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{
- "Sign", [](float a) { return compatible_signf(a); }};
- return fn;
- }
- case NODE_MATH_COMPARE: {
- static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
- "Compare", [](float a, float b, float c) -> float {
- return ((a == b) || (fabsf(a - b) <= fmaxf(c, FLT_EPSILON))) ? 1.0f : 0.0f;
- }};
- return fn;
- }
- case NODE_MATH_SMOOTH_MIN: {
- return builder.get_not_implemented_fn();
- }
- case NODE_MATH_SMOOTH_MAX: {
- return builder.get_not_implemented_fn();
- }
-
- case NODE_MATH_ROUND: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{
- "Round", [](float a) { return floorf(a + 0.5f); }};
- return fn;
- }
- case NODE_MATH_FLOOR: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Floor",
- [](float a) { return floorf(a); }};
- return fn;
- }
- case NODE_MATH_CEIL: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Ceil",
- [](float a) { return ceilf(a); }};
- return fn;
- }
- case NODE_MATH_FRACTION: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Fraction",
- [](float a) { return a - floorf(a); }};
- return fn;
- }
- case NODE_MATH_MODULO: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
- "Modulo", [](float a, float b) { return safe_modf(a, b); }};
- return fn;
- }
- case NODE_MATH_TRUNC: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{
- "Trunc", [](float a) { return a >= 0.0f ? floorf(a) : ceilf(a); }};
- return fn;
- }
- case NODE_MATH_SNAP: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
- "Snap", [](float a, float b) { return floorf(safe_divide(a, b)) * b; }};
- return fn;
- }
- case NODE_MATH_WRAP: {
- return builder.get_not_implemented_fn();
- }
- case NODE_MATH_PINGPONG: {
- return builder.get_not_implemented_fn();
- }
+ blender::nodes::try_dispatch_float_math_fl_to_fl(
+ mode, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{info.title_case_name, function};
+ base_fn = &fn;
+ });
+ if (base_fn != nullptr) {
+ return *base_fn;
+ }
- case NODE_MATH_SINE: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Sine", [](float a) { return sinf(a); }};
- return fn;
- }
- case NODE_MATH_COSINE: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Cosine",
- [](float a) { return cosf(a); }};
- return fn;
- }
- case NODE_MATH_TANGENT: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Tangent",
- [](float a) { return tanf(a); }};
- return fn;
- }
- case NODE_MATH_SINH: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Hyperbolic Sine",
- [](float a) { return sinhf(a); }};
- return fn;
- }
- case NODE_MATH_COSH: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Hyperbolic Cosine",
- [](float a) { return coshf(a); }};
- return fn;
- }
- case NODE_MATH_TANH: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Hyperbolic Tangent",
- [](float a) { return tanhf(a); }};
- return fn;
- }
- case NODE_MATH_ARCSINE: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Arc Sine", safe_asinf};
- return fn;
- }
- case NODE_MATH_ARCCOSINE: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Arc Cosine", safe_acosf};
- return fn;
- }
- case NODE_MATH_ARCTANGENT: {
- static blender::fn::CustomMF_SI_SO<float, float> fn{"Arc Tangent",
- [](float a) { return atanf(a); }};
- return fn;
- }
- case NODE_MATH_ARCTAN2: {
- static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
- "Arc Tangent 2", [](float a, float b) { return atan2f(a, b); }};
- return fn;
- }
+ blender::nodes::try_dispatch_float_math_fl_fl_to_fl(
+ mode, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{info.title_case_name,
+ function};
+ base_fn = &fn;
+ });
+ if (base_fn != nullptr) {
+ return *base_fn;
+ }
- default:
- BLI_assert(false);
- return builder.get_not_implemented_fn();
+ blender::nodes::try_dispatch_float_math_fl_fl_fl_to_fl(
+ mode, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
+ info.title_case_name, function};
+ base_fn = &fn;
+ });
+ if (base_fn != nullptr) {
+ return *base_fn;
}
+
+ return builder.get_not_implemented_fn();
}
static void sh_node_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)