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:
-rw-r--r--release/scripts/startup/bl_operators/__init__.py2
-rw-r--r--release/scripts/startup/bl_operators/geometry_nodes.py60
-rw-r--r--release/scripts/startup/bl_operators/simulation.py41
-rw-r--r--release/scripts/startup/bl_ui/space_node.py11
-rw-r--r--release/scripts/startup/nodeitems_builtins.py62
-rw-r--r--source/blender/blenkernel/BKE_attribute.h6
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh255
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.h42
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh373
-rw-r--r--source/blender/blenkernel/BKE_modifier.h7
-rw-r--r--source/blender/blenkernel/BKE_node.h41
-rw-r--r--source/blender/blenkernel/BKE_pointcloud.h2
-rw-r--r--source/blender/blenkernel/CMakeLists.txt5
-rw-r--r--source/blender/blenkernel/intern/attribute.c4
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc1080
-rw-r--r--source/blender/blenkernel/intern/fluid.c2
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc554
-rw-r--r--source/blender/blenkernel/intern/hair.c4
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c2
-rw-r--r--source/blender/blenkernel/intern/node.c49
-rw-r--r--source/blender/blenkernel/intern/object.c17
-rw-r--r--source/blender/blenkernel/intern/object_dupli.c48
-rw-r--r--source/blender/blenkernel/intern/pointcache.c3
-rw-r--r--source/blender/blenkernel/intern/pointcloud.cc125
-rw-r--r--source/blender/blenkernel/intern/simulation.cc4
-rw-r--r--source/blender/blenlib/BLI_rand.h6
-rw-r--r--source/blender/blenlib/BLI_rand.hh1
-rw-r--r--source/blender/blenlib/BLI_user_counter.hh158
-rw-r--r--source/blender/blenlib/CMakeLists.txt1
-rw-r--r--source/blender/blenlib/intern/rand.cc25
-rw-r--r--source/blender/blenloader/intern/versioning_290.c22
-rw-r--r--source/blender/depsgraph/CMakeLists.txt1
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_build.h3
-rw-r--r--source/blender/depsgraph/intern/depsgraph_build.cc12
-rw-r--r--source/blender/editors/include/ED_node.h2
-rw-r--r--source/blender/editors/object/object_modifier.c5
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt3
-rw-r--r--source/blender/editors/space_node/drawnode.c65
-rw-r--r--source/blender/editors/space_node/node_draw.c3
-rw-r--r--source/blender/editors/space_node/node_edit.c14
-rw-r--r--source/blender/editors/space_node/node_group.c8
-rw-r--r--source/blender/editors/space_node/node_relationships.c5
-rw-r--r--source/blender/editors/space_node/space_node.c2
-rw-r--r--source/blender/functions/CMakeLists.txt1
-rw-r--r--source/blender/functions/FN_generic_value_map.hh123
-rw-r--r--source/blender/functions/FN_multi_function.hh13
-rw-r--r--source/blender/makesdna/DNA_modifier_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h13
-rw-r--r--source/blender/makesdna/DNA_node_types.h39
-rw-r--r--source/blender/makesdna/DNA_object_types.h8
-rw-r--r--source/blender/makesdna/intern/dna_defaults.c4
-rw-r--r--source/blender/makesrna/RNA_access.h7
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt1
-rw-r--r--source/blender/makesrna/intern/rna_access.c4
-rw-r--r--source/blender/makesrna/intern/rna_attribute.c7
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c78
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c284
-rw-r--r--source/blender/makesrna/intern/rna_space.c48
-rw-r--r--source/blender/modifiers/CMakeLists.txt5
-rw-r--r--source/blender/modifiers/MOD_modifiertypes.h2
-rw-r--r--source/blender/modifiers/MOD_nodes.h (renamed from source/blender/nodes/NOD_simulation.h)10
-rw-r--r--source/blender/modifiers/intern/MOD_edgesplit.c5
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc1088
-rw-r--r--source/blender/modifiers/intern/MOD_simulation.cc194
-rw-r--r--source/blender/modifiers/intern/MOD_triangulate.c16
-rw-r--r--source/blender/modifiers/intern/MOD_util.c2
-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.cc23
-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
103 files changed, 7335 insertions, 888 deletions
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
index eff88c835e7..71b2de41d9e 100644
--- a/release/scripts/startup/bl_operators/__init__.py
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -31,6 +31,7 @@ _modules = [
"console",
"constraint",
"file",
+ "geometry_nodes",
"image",
"mesh",
"node",
@@ -42,7 +43,6 @@ _modules = [
"rigidbody",
"screen_play_rendered_anim",
"sequencer",
- "simulation",
"userpref",
"uvcalc_follow_active",
"uvcalc_lightmap",
diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py
new file mode 100644
index 00000000000..374bd4161a7
--- /dev/null
+++ b/release/scripts/startup/bl_operators/geometry_nodes.py
@@ -0,0 +1,60 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# 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.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+import bpy
+
+
+def geometry_modifier_poll(context) -> bool:
+ ob = context.object
+
+ # Test object support for geometry node modifier (No volume or hair object support yet)
+ if not ob or ob.type not in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'POINTCLOUD'}:
+ return False
+
+ return True
+
+
+class NewGeometryNodeTree(bpy.types.Operator):
+ """Create a new geometry node tree"""
+ bl_idname = "node.new_geometry_node_tree"
+ bl_label = "New Geometry Node Tree"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return geometry_modifier_poll(context)
+
+ def execute(self, context):
+ group = bpy.data.node_groups.new("Geometry Nodes", 'GeometryNodeTree')
+ group.inputs.new('NodeSocketGeometry', "Geometry")
+ group.outputs.new('NodeSocketGeometry', "Geometry")
+ input_node = group.nodes.new('NodeGroupInput')
+ output_node = group.nodes.new('NodeGroupOutput')
+ output_node.is_active_output = True
+
+ input_node.location.x = -200 - input_node.width
+ output_node.location.x = 200
+
+ group.links.new(output_node.inputs[0], input_node.outputs[0])
+
+ return {'FINISHED'}
+
+
+classes = (
+ NewGeometryNodeTree,
+)
diff --git a/release/scripts/startup/bl_operators/simulation.py b/release/scripts/startup/bl_operators/simulation.py
deleted file mode 100644
index 0981baa5941..00000000000
--- a/release/scripts/startup/bl_operators/simulation.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# 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.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-import bpy
-
-
-class NewSimulation(bpy.types.Operator):
- """Create a new simulation data block and edit it in the opened simulation editor"""
-
- bl_idname = "simulation.new"
- bl_label = "New Simulation"
- bl_options = {'REGISTER', 'UNDO'}
-
- @classmethod
- def poll(cls, context):
- return context.area.type == 'NODE_EDITOR' and context.space_data.tree_type == 'SimulationNodeTree'
-
- def execute(self, context):
- simulation = bpy.data.simulations.new("Simulation")
- context.space_data.simulation = simulation
- return {'FINISHED'}
-
-
-classes = (
- NewSimulation,
-)
diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py
index c0c38c02c63..4cd38831cdf 100644
--- a/release/scripts/startup/bl_ui/space_node.py
+++ b/release/scripts/startup/bl_ui/space_node.py
@@ -151,13 +151,10 @@ class NODE_HT_header(Header):
if snode_id:
layout.prop(snode_id, "use_nodes")
- elif snode.tree_type == 'SimulationNodeTree':
- row = layout.row(align=True)
- row.prop(snode, "simulation", text="")
- row.operator("simulation.new", text="", icon='ADD')
- simulation = snode.simulation
- if simulation:
- row.prop(snode.simulation, "use_fake_user", text="")
+ elif snode.tree_type == 'GeometryNodeTree':
+ NODE_MT_editor_menus.draw_collapsible(context, layout)
+ layout.separator_spacer()
+ layout.template_ID(snode, "node_tree", new="node.new_geometry_node_tree")
else:
# Custom node tree is edited as independent ID block
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 8d2b6198fd5..b1789776728 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -58,11 +58,11 @@ class TextureNodeCategory(SortedNodeCategory):
context.space_data.tree_type == 'TextureNodeTree')
-class SimulationNodeCategory(SortedNodeCategory):
+class GeometryNodeCategory(SortedNodeCategory):
@classmethod
def poll(cls, context):
return (context.space_data.type == 'NODE_EDITOR' and
- context.space_data.tree_type == 'SimulationNodeTree')
+ context.space_data.tree_type == 'GeometryNodeTree')
# menu entry for node group tools
@@ -77,11 +77,11 @@ node_tree_group_type = {
'CompositorNodeTree': 'CompositorNodeGroup',
'ShaderNodeTree': 'ShaderNodeGroup',
'TextureNodeTree': 'TextureNodeGroup',
- 'SimulationNodeTree': 'SimulationNodeGroup',
+ 'GeometryNodeTree': 'GeometryNodeGroup',
}
-# generic node group items generator for shader, compositor, simulation and texture node groups
+# generic node group items generator for shader, compositor, geometry and texture node groups
def node_group_items(context):
if context is None:
return
@@ -483,13 +483,55 @@ def not_implemented_node(idname):
return NodeItem(idname, label=label)
-simulation_node_categories = [
- # Simulation Nodes
- SimulationNodeCategory("SIM_GROUP", "Group", items=node_group_items),
- SimulationNodeCategory("SIM_LAYOUT", "Layout", items=[
+geometry_node_categories = [
+ # Geometry Nodes
+ GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
+ NodeItem("GeometryNodeRandomAttribute"),
+ NodeItem("GeometryNodeAttributeMath"),
+ ]),
+ GeometryNodeCategory("GEO_COLOR", "Color", items=[
+ NodeItem("ShaderNodeValToRGB"),
+ NodeItem("ShaderNodeSeparateRGB"),
+ NodeItem("ShaderNodeCombineRGB"),
+ ]),
+ GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[
+ NodeItem("GeometryNodeTransform"),
+ NodeItem("GeometryNodeBoolean"),
+ NodeItem("GeometryNodeJoinGeometry"),
+ ]),
+ GeometryNodeCategory("GEO_INPUT", "Input", items=[
+ NodeItem("GeometryNodeObjectInfo"),
+ NodeItem("FunctionNodeRandomFloat"),
+ NodeItem("ShaderNodeValue"),
+ ]),
+ GeometryNodeCategory("GEO_MESH", "Mesh", items=[
+ NodeItem("GeometryNodeTriangulate"),
+ NodeItem("GeometryNodeEdgeSplit"),
+ NodeItem("GeometryNodeSubdivisionSurface"),
+ ]),
+ GeometryNodeCategory("GEO_POINT", "Point", items=[
+ NodeItem("GeometryNodePointDistribute"),
+ NodeItem("GeometryNodePointInstance"),
+ ]),
+ GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
+ NodeItem("ShaderNodeMapRange"),
+ NodeItem("ShaderNodeClamp"),
+ NodeItem("ShaderNodeMath"),
+ NodeItem("FunctionNodeBooleanMath"),
+ NodeItem("FunctionNodeFloatCompare"),
+ ]),
+ GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
+ NodeItem("ShaderNodeSeparateXYZ"),
+ NodeItem("ShaderNodeCombineXYZ"),
+ NodeItem("ShaderNodeVectorMath"),
+ ]),
+ GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items),
+ GeometryNodeCategory("GEO_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),
NodeItem("NodeReroute"),
]),
+ # NodeItem("FunctionNodeCombineStrings"),
+ # NodeItem("FunctionNodeGroupInstanceID"),
]
@@ -497,14 +539,14 @@ def register():
nodeitems_utils.register_node_categories('SHADER', shader_node_categories)
nodeitems_utils.register_node_categories('COMPOSITING', compositor_node_categories)
nodeitems_utils.register_node_categories('TEXTURE', texture_node_categories)
- nodeitems_utils.register_node_categories('SIMULATION', simulation_node_categories)
+ nodeitems_utils.register_node_categories('GEOMETRY', geometry_node_categories)
def unregister():
nodeitems_utils.unregister_node_categories('SHADER')
nodeitems_utils.unregister_node_categories('COMPOSITING')
nodeitems_utils.unregister_node_categories('TEXTURE')
- nodeitems_utils.unregister_node_categories('SIMULATION')
+ nodeitems_utils.unregister_node_categories('GEOMETRY')
if __name__ == "__main__":
diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h
index aab962d42a6..55a841d8fd1 100644
--- a/source/blender/blenkernel/BKE_attribute.h
+++ b/source/blender/blenkernel/BKE_attribute.h
@@ -40,13 +40,11 @@ struct ReportList;
/* Attribute.domain */
typedef enum AttributeDomain {
- ATTR_DOMAIN_VERTEX = 0, /* Mesh Vertex */
+ ATTR_DOMAIN_POINT = 0, /* Mesh, Hair or PointCloud Point */
ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */
ATTR_DOMAIN_CORNER = 2, /* Mesh Corner */
ATTR_DOMAIN_POLYGON = 3, /* Mesh Polygon */
-
- ATTR_DOMAIN_POINT = 4, /* Hair or PointCloud Point */
- ATTR_DOMAIN_CURVE = 5, /* Hair Curve */
+ ATTR_DOMAIN_CURVE = 4, /* Hair Curve */
ATTR_DOMAIN_NUM
} AttributeDomain;
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
new file mode 100644
index 00000000000..e58fba36342
--- /dev/null
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -0,0 +1,255 @@
+/*
+ * 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 <mutex>
+
+#include "FN_cpp_type.hh"
+#include "FN_spans.hh"
+
+#include "BKE_attribute.h"
+
+#include "BLI_float3.hh"
+
+struct Mesh;
+
+namespace blender::bke {
+
+using fn::CPPType;
+
+/**
+ * This class offers an indirection for reading an attribute.
+ * This is useful for the following reasons:
+ * - Blender does not store all attributes the same way.
+ * The simplest case are custom data layers with primitive types.
+ * A bit more complex are mesh attributes like the position of vertices,
+ * which are embedded into the MVert struct.
+ * Even more complex to access are vertex weights.
+ * - Sometimes attributes are stored on one domain, but we want to access
+ * the attribute on a different domain. Therefore, we have to interpolate
+ * between the domains.
+ */
+class ReadAttribute {
+ protected:
+ const AttributeDomain domain_;
+ const CPPType &cpp_type_;
+ const int64_t size_;
+
+ /* Protects the span below, so that no two threads initialize it at the same time. */
+ mutable std::mutex span_mutex_;
+ /* When it is not null, it points to the attribute array or a temporary array that contains all
+ * the attribute values. */
+ mutable void *array_buffer_ = nullptr;
+ /* Is true when the buffer above is owned by the attribute accessor. */
+ mutable bool array_is_temporary_ = false;
+
+ public:
+ ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
+ : domain_(domain), cpp_type_(cpp_type), size_(size)
+ {
+ }
+
+ virtual ~ReadAttribute();
+
+ AttributeDomain domain() const
+ {
+ return domain_;
+ }
+
+ const CPPType &cpp_type() const
+ {
+ return cpp_type_;
+ }
+
+ int64_t size() const
+ {
+ return size_;
+ }
+
+ void get(const int64_t index, void *r_value) const
+ {
+ BLI_assert(index < size_);
+ this->get_internal(index, r_value);
+ }
+
+ /* Get a span that contains all attribute values. */
+ fn::GSpan get_span() const;
+
+ protected:
+ /* r_value is expected to be uninitialized. */
+ virtual void get_internal(const int64_t index, void *r_value) const = 0;
+
+ virtual void initialize_span() const;
+};
+
+/**
+ * This exists for similar reasons as the ReadAttribute class, except that
+ * it does not deal with interpolation between domains.
+ */
+class WriteAttribute {
+ protected:
+ const AttributeDomain domain_;
+ const CPPType &cpp_type_;
+ const int64_t size_;
+
+ /* When not null, this points either to the attribute array or to a temporary array. */
+ void *array_buffer_ = nullptr;
+ /* True, when the buffer points to a temporary array. */
+ bool array_is_temporary_ = false;
+ /* This helps to protect agains forgetting to apply changes done to the array. */
+ bool array_should_be_applied_ = false;
+
+ public:
+ WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
+ : domain_(domain), cpp_type_(cpp_type), size_(size)
+ {
+ }
+
+ virtual ~WriteAttribute();
+
+ AttributeDomain domain() const
+ {
+ return domain_;
+ }
+
+ const CPPType &cpp_type() const
+ {
+ return cpp_type_;
+ }
+
+ int64_t size() const
+ {
+ return size_;
+ }
+
+ void get(const int64_t index, void *r_value) const
+ {
+ BLI_assert(index < size_);
+ this->get_internal(index, r_value);
+ }
+
+ void set(const int64_t index, const void *value)
+ {
+ BLI_assert(index < size_);
+ this->set_internal(index, value);
+ }
+
+ /* Get a span that new attribute values can be written into. When all values have been changed,
+ * #apply_span has to be called. The span might not contain the original attribute values. */
+ fn::GMutableSpan get_span();
+ /* Write the changes to the span into the actual attribute, if they aren't already. */
+ void apply_span();
+
+ protected:
+ virtual void get_internal(const int64_t index, void *r_value) const = 0;
+ virtual void set_internal(const int64_t index, const void *value) = 0;
+
+ virtual void initialize_span();
+ virtual void apply_span_if_necessary();
+};
+
+using ReadAttributePtr = std::unique_ptr<ReadAttribute>;
+using WriteAttributePtr = std::unique_ptr<WriteAttribute>;
+
+/* This provides type safe access to an attribute. */
+template<typename T> class TypedReadAttribute {
+ private:
+ ReadAttributePtr attribute_;
+
+ public:
+ TypedReadAttribute(ReadAttributePtr attribute) : attribute_(std::move(attribute))
+ {
+ BLI_assert(attribute_);
+ BLI_assert(attribute_->cpp_type().is<T>());
+ }
+
+ int64_t size() const
+ {
+ return attribute_->size();
+ }
+
+ T operator[](const int64_t index) const
+ {
+ BLI_assert(index < attribute_->size());
+ T value;
+ value.~T();
+ attribute_->get(index, &value);
+ return value;
+ }
+
+ /* Get a span to that contains all attribute values for faster and more convenient access. */
+ Span<T> get_span() const
+ {
+ return attribute_->get_span().template typed<T>();
+ }
+};
+
+/* This provides type safe access to an attribute. */
+template<typename T> class TypedWriteAttribute {
+ private:
+ WriteAttributePtr attribute_;
+
+ public:
+ TypedWriteAttribute(WriteAttributePtr attribute) : attribute_(std::move(attribute))
+ {
+ BLI_assert(attribute_);
+ BLI_assert(attribute_->cpp_type().is<T>());
+ }
+
+ int64_t size() const
+ {
+ return attribute_->size();
+ }
+
+ T operator[](const int64_t index) const
+ {
+ BLI_assert(index < attribute_->size());
+ T value;
+ value.~T();
+ attribute_->get(index, &value);
+ return value;
+ }
+
+ void set(const int64_t index, const T &value)
+ {
+ attribute_->set(index, &value);
+ }
+
+ /* Get a span that new values can be written into. Once all values have been updated #apply_span
+ * has to be called. The span might *not* contain the initial attribute values, so one should
+ * generally only write to the span. */
+ MutableSpan<T> get_span()
+ {
+ return attribute_->get_span().typed<T>();
+ }
+
+ /* Write back all changes to the actual attribute, if necessary. */
+ void apply_span()
+ {
+ attribute_->apply_span();
+ }
+};
+
+using FloatReadAttribute = TypedReadAttribute<float>;
+using Float3ReadAttribute = TypedReadAttribute<float3>;
+using FloatWriteAttribute = TypedWriteAttribute<float>;
+using Float3WriteAttribute = TypedWriteAttribute<float3>;
+
+const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
+CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h
new file mode 100644
index 00000000000..026f4d39d51
--- /dev/null
+++ b/source/blender/blenkernel/BKE_geometry_set.h
@@ -0,0 +1,42 @@
+/*
+ * 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
+
+/** \file
+ * \ingroup bke
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Object;
+struct GeometrySet;
+
+void BKE_geometry_set_free(struct GeometrySet *geometry_set);
+
+bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set);
+
+int BKE_geometry_set_instances(const struct GeometrySet *geometry_set,
+ float (**r_positions)[3],
+ float (**r_rotations)[3],
+ float (**r_scales)[3],
+ struct Object ***r_objects);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
new file mode 100644
index 00000000000..ef3ae3c381c
--- /dev/null
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -0,0 +1,373 @@
+/*
+ * 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
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <atomic>
+#include <iostream>
+
+#include "BLI_float3.hh"
+#include "BLI_hash.hh"
+#include "BLI_map.hh"
+#include "BLI_set.hh"
+#include "BLI_user_counter.hh"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_geometry_set.h"
+
+struct Mesh;
+struct PointCloud;
+struct Object;
+
+/* Each geometry component has a specific type. The type determines what kind of data the component
+ * stores. Functions modifying a geometry will usually just modify a subset of the component types.
+ */
+enum class GeometryComponentType {
+ Mesh = 0,
+ PointCloud = 1,
+ Instances = 2,
+};
+
+enum class GeometryOwnershipType {
+ /* The geometry is owned. This implies that it can be changed. */
+ Owned = 0,
+ /* The geometry can be changed, but someone else is responsible for freeing it. */
+ Editable = 1,
+ /* The geometry cannot be changed and someone else is responsible for freeing it. */
+ ReadOnly = 2,
+};
+
+/* Make it possible to use the component type as key in hash tables. */
+namespace blender {
+template<> struct DefaultHash<GeometryComponentType> {
+ uint64_t operator()(const GeometryComponentType &value) const
+ {
+ return (uint64_t)value;
+ }
+};
+} // namespace blender
+
+/**
+ * This is the base class for specialized geometry component types.
+ */
+class GeometryComponent {
+ private:
+ /* The reference count has two purposes. When it becomes zero, the component is freed. When it is
+ * larger than one, the component becomes immutable. */
+ mutable std::atomic<int> users_ = 1;
+ GeometryComponentType type_;
+
+ public:
+ GeometryComponent(GeometryComponentType type);
+ virtual ~GeometryComponent();
+ static GeometryComponent *create(GeometryComponentType component_type);
+
+ /* The returned component should be of the same type as the type this is called on. */
+ virtual GeometryComponent *copy() const = 0;
+
+ void user_add() const;
+ void user_remove() const;
+ bool is_mutable() const;
+
+ GeometryComponentType type() const;
+
+ /* Returns true when the geometry component supports this attribute domain. */
+ virtual bool attribute_domain_supported(const AttributeDomain domain) const;
+ /* Returns true when the given data type is supported in the given domain. */
+ virtual bool attribute_domain_with_type_supported(const AttributeDomain domain,
+ const CustomDataType data_type) const;
+ /* Can only be used with supported domain types. */
+ virtual int attribute_domain_size(const AttributeDomain domain) const;
+ /* Attributes with these names cannot be created or removed via this api. */
+ virtual bool attribute_is_builtin(const blender::StringRef attribute_name) const;
+
+ /* Get read-only access to the highest priority attribute with the given name.
+ * Returns null if the attribute does not exist. */
+ virtual blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ const blender::StringRef attribute_name) const;
+
+ /* Get read and write access to the highest priority attribute with the given name.
+ * Returns null if the attribute does not exist. */
+ virtual blender::bke::WriteAttributePtr attribute_try_get_for_write(
+ const blender::StringRef attribute_name);
+
+ /* Get a read-only attribute for the domain based on the given attribute. This can be used to
+ * interpolate from one domain to another.
+ * Returns null if the interpolation is not implemented. */
+ virtual blender::bke::ReadAttributePtr attribute_try_adapt_domain(
+ blender::bke::ReadAttributePtr attribute, const AttributeDomain domain) const;
+
+ /* Returns true when the attribute has been deleted. */
+ virtual bool attribute_try_delete(const blender::StringRef attribute_name);
+
+ /* Returns true when the attribute has been created. */
+ virtual bool attribute_try_create(const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type);
+
+ virtual blender::Set<std::string> attribute_names() const;
+ virtual bool is_empty() const;
+
+ /* Get a read-only attribute for the given domain and data type.
+ * Returns null when it does not exist. */
+ blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type) const;
+
+ /* Get a read-only attribute for the given domain and data type.
+ * Returns a constant attribute based on the default value if the attribute does not exist.
+ * Never returns null. */
+ blender::bke::ReadAttributePtr attribute_get_for_read(const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value) const;
+
+ /* Get a typed read-only attribute for the given domain and type. */
+ template<typename T>
+ blender::bke::TypedReadAttribute<T> attribute_get_for_read(
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const T &default_value) const
+ {
+ const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
+ const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ return this->attribute_get_for_read(attribute_name, domain, type, &default_value);
+ }
+
+ /* Get a read-only dummy attribute that always returns the same value. */
+ blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *value) const;
+
+ /* Get a read-only dummy attribute that always returns the same value. */
+ template<typename T>
+ blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain,
+ const T &value) const
+ {
+ const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
+ const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ return this->attribute_get_constant_for_read(domain, type, &value);
+ }
+
+ /**
+ * Returns the attribute with the given parameters if it exists.
+ * If an exact match does not exist, other attributes with the same name are deleted and a new
+ * attribute is created if possible.
+ */
+ blender::bke::WriteAttributePtr attribute_try_ensure_for_write(
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type);
+};
+
+template<typename T>
+inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryComponent, T>;
+
+/**
+ * A geometry set contains zero or more geometry components. There is at most one component of each
+ * type. Individual components might be shared between multiple geometries. Shared components are
+ * copied automatically when write access is requested.
+ *
+ * Copying a geometry set is a relatively cheap operation, because it does not copy the referenced
+ * geometry components.
+ */
+struct GeometrySet {
+ private:
+ using GeometryComponentPtr = blender::UserCounter<class GeometryComponent>;
+ blender::Map<GeometryComponentType, GeometryComponentPtr> components_;
+
+ public:
+ GeometryComponent &get_component_for_write(GeometryComponentType component_type);
+ template<typename Component> Component &get_component_for_write()
+ {
+ BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
+ return static_cast<Component &>(this->get_component_for_write(Component::static_type));
+ }
+
+ const GeometryComponent *get_component_for_read(GeometryComponentType component_type) const;
+ template<typename Component> const Component *get_component_for_read() const
+ {
+ BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
+ return static_cast<const Component *>(get_component_for_read(Component::static_type));
+ }
+
+ bool has(const GeometryComponentType component_type) const;
+ template<typename Component> bool has() const
+ {
+ BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
+ return this->has(Component::static_type);
+ }
+
+ void remove(const GeometryComponentType component_type);
+ template<typename Component> void remove()
+ {
+ BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
+ return this->remove(Component::static_type);
+ }
+
+ void add(const GeometryComponent &component);
+
+ void compute_boundbox_without_instances(blender::float3 *r_min, blender::float3 *r_max) const;
+
+ friend std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set);
+ friend bool operator==(const GeometrySet &a, const GeometrySet &b);
+ uint64_t hash() const;
+
+ /* Utility methods for creation. */
+ static GeometrySet create_with_mesh(
+ Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ static GeometrySet create_with_pointcloud(
+ PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+
+ /* Utility methods for access. */
+ bool has_mesh() const;
+ bool has_pointcloud() const;
+ bool has_instances() const;
+ const Mesh *get_mesh_for_read() const;
+ const PointCloud *get_pointcloud_for_read() const;
+ Mesh *get_mesh_for_write();
+ PointCloud *get_pointcloud_for_write();
+
+ /* Utility methods for replacement. */
+ void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ void replace_pointcloud(PointCloud *pointcloud,
+ GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+};
+
+/** A geometry component that can store a mesh. */
+class MeshComponent : public GeometryComponent {
+ private:
+ Mesh *mesh_ = nullptr;
+ GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
+ /* Due to historical design choices, vertex group data is stored in the mesh, but the vertex
+ * group names are stored on an object. Since we don't have an object here, we copy over the
+ * names into this map. */
+ blender::Map<std::string, int> vertex_group_names_;
+
+ public:
+ MeshComponent();
+ ~MeshComponent();
+ GeometryComponent *copy() const override;
+
+ void clear();
+ bool has_mesh() const;
+ void replace(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ Mesh *release();
+
+ void copy_vertex_group_names_from_object(const struct Object &object);
+
+ const Mesh *get_for_read() const;
+ Mesh *get_for_write();
+
+ bool attribute_domain_supported(const AttributeDomain domain) const final;
+ bool attribute_domain_with_type_supported(const AttributeDomain domain,
+ const CustomDataType data_type) const final;
+ int attribute_domain_size(const AttributeDomain domain) const final;
+ bool attribute_is_builtin(const blender::StringRef attribute_name) const final;
+
+ blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ const blender::StringRef attribute_name) const final;
+ blender::bke::WriteAttributePtr attribute_try_get_for_write(
+ const blender::StringRef attribute_name) final;
+
+ bool attribute_try_delete(const blender::StringRef attribute_name) final;
+ bool attribute_try_create(const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type) final;
+
+ blender::Set<std::string> attribute_names() const final;
+ bool is_empty() const final;
+
+ static constexpr inline GeometryComponentType static_type = GeometryComponentType::Mesh;
+};
+
+/** A geometry component that stores a point cloud. */
+class PointCloudComponent : public GeometryComponent {
+ private:
+ PointCloud *pointcloud_ = nullptr;
+ GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
+
+ public:
+ PointCloudComponent();
+ ~PointCloudComponent();
+ GeometryComponent *copy() const override;
+
+ void clear();
+ bool has_pointcloud() const;
+ void replace(PointCloud *pointcloud,
+ GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ PointCloud *release();
+
+ const PointCloud *get_for_read() const;
+ PointCloud *get_for_write();
+
+ bool attribute_domain_supported(const AttributeDomain domain) const final;
+ bool attribute_domain_with_type_supported(const AttributeDomain domain,
+ const CustomDataType data_type) const final;
+ int attribute_domain_size(const AttributeDomain domain) const final;
+ bool attribute_is_builtin(const blender::StringRef attribute_name) const final;
+
+ blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ const blender::StringRef attribute_name) const final;
+ blender::bke::WriteAttributePtr attribute_try_get_for_write(
+ const blender::StringRef attribute_name) final;
+
+ bool attribute_try_delete(const blender::StringRef attribute_name) final;
+ bool attribute_try_create(const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type) final;
+
+ blender::Set<std::string> attribute_names() const final;
+ bool is_empty() const final;
+
+ static constexpr inline GeometryComponentType static_type = GeometryComponentType::PointCloud;
+};
+
+/** A geometry component that stores instances. */
+class InstancesComponent : public GeometryComponent {
+ private:
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<blender::float3> rotations_;
+ blender::Vector<blender::float3> scales_;
+ blender::Vector<const Object *> objects_;
+
+ public:
+ InstancesComponent();
+ ~InstancesComponent() = default;
+ GeometryComponent *copy() const override;
+
+ void clear();
+ void add_instance(const Object *object,
+ blender::float3 position,
+ blender::float3 rotation = {0, 0, 0},
+ blender::float3 scale = {1, 1, 1});
+
+ blender::Span<const Object *> objects() const;
+ blender::Span<blender::float3> positions() const;
+ blender::Span<blender::float3> rotations() const;
+ blender::Span<blender::float3> scales() const;
+ blender::MutableSpan<blender::float3> positions();
+ int instances_amount() const;
+
+ bool is_empty() const final;
+
+ static constexpr inline GeometryComponentType static_type = GeometryComponentType::Instances;
+};
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index a2c3787bcd2..2491f0953c0 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -43,6 +43,7 @@ struct ModifierData;
struct Object;
struct Scene;
struct bArmature;
+struct GeometrySet;
typedef enum {
/* Should not be used, only for None modifier type */
@@ -246,9 +247,9 @@ typedef struct ModifierTypeInfo {
struct Hair *(*modifyHair)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct Hair *hair);
- struct PointCloud *(*modifyPointCloud)(struct ModifierData *md,
- const struct ModifierEvalContext *ctx,
- struct PointCloud *pointcloud);
+ void (*modifyPointCloud)(struct ModifierData *md,
+ const struct ModifierEvalContext *ctx,
+ struct GeometrySet *geometry_set);
struct Volume *(*modifyVolume)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct Volume *volume);
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index e6fb9d86b0a..905316a937d 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -112,20 +112,26 @@ namespace blender {
namespace nodes {
class SocketMFNetworkBuilder;
class NodeMFNetworkBuilder;
+class GeoNodeExecParams;
} // namespace nodes
namespace fn {
+class CPPType;
class MFDataType;
-}
+} // namespace fn
} // namespace blender
using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder);
-using SocketGetMFDataTypeFunction = blender::fn::MFDataType (*)();
+using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params);
+using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)();
+using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value);
using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetworkBuilder &builder);
#else
typedef void *NodeExpandInMFNetworkFunction;
-typedef void *SocketGetMFDataTypeFunction;
typedef void *SocketExpandInMFNetworkFunction;
+typedef void *NodeGeometryExecFunction;
+typedef void *SocketGetCPPTypeFunction;
+typedef void *SocketGetCPPValueFunction;
#endif
/**
@@ -181,10 +187,12 @@ typedef struct bNodeSocketType {
/* Callback to free the socket type. */
void (*free_self)(struct bNodeSocketType *stype);
- /* Returns the multi-function data type of this socket type. */
- SocketGetMFDataTypeFunction get_mf_data_type;
/* Expands the socket into a multi-function node that outputs the socket value. */
SocketExpandInMFNetworkFunction expand_in_mf_network;
+ /* Return the CPPType of this socket. */
+ SocketGetCPPTypeFunction get_cpp_type;
+ /* Get the value of this socket in a generic way. */
+ SocketGetCPPValueFunction get_cpp_value;
} bNodeSocketType;
typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context,
@@ -302,6 +310,9 @@ typedef struct bNodeType {
/* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */
NodeExpandInMFNetworkFunction expand_in_mf_network;
+ /* Execute a geometry node. */
+ NodeGeometryExecFunction geometry_node_execute;
+
/* RNA integration */
ExtensionRNA rna_ext;
} bNodeType;
@@ -438,7 +449,7 @@ bool ntreeHasType(const struct bNodeTree *ntree, int type);
bool ntreeHasTree(const struct bNodeTree *ntree, const struct bNodeTree *lookup);
void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree);
void ntreeUpdateAllNew(struct Main *main);
-void ntreeUpdateAllUsers(struct Main *main, struct ID *ngroup);
+void ntreeUpdateAllUsers(struct Main *main, struct bNodeTree *ngroup);
void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***deplist, int *totnodes);
@@ -1323,6 +1334,24 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Geometry Nodes
+ * \{ */
+
+#define GEO_NODE_TRIANGULATE 1000
+#define GEO_NODE_EDGE_SPLIT 1001
+#define GEO_NODE_TRANSFORM 1002
+#define GEO_NODE_BOOLEAN 1003
+#define GEO_NODE_POINT_DISTRIBUTE 1004
+#define GEO_NODE_POINT_INSTANCE 1005
+#define GEO_NODE_SUBDIVISION_SURFACE 1006
+#define GEO_NODE_OBJECT_INFO 1007
+#define GEO_NODE_RANDOM_ATTRIBUTE 1008
+#define GEO_NODE_ATTRIBUTE_MATH 1009
+#define GEO_NODE_JOIN_GEOMETRY 1010
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Function Nodes
* \{ */
diff --git a/source/blender/blenkernel/BKE_pointcloud.h b/source/blender/blenkernel/BKE_pointcloud.h
index 985a8cc3ca7..d2d390dc786 100644
--- a/source/blender/blenkernel/BKE_pointcloud.h
+++ b/source/blender/blenkernel/BKE_pointcloud.h
@@ -38,8 +38,10 @@ extern const char *POINTCLOUD_ATTR_RADIUS;
void *BKE_pointcloud_add(struct Main *bmain, const char *name);
void *BKE_pointcloud_add_default(struct Main *bmain, const char *name);
+struct PointCloud *BKE_pointcloud_new_nomain(const int totpoint);
struct BoundBox *BKE_pointcloud_boundbox_get(struct Object *ob);
+void BKE_pointcloud_minmax(const struct PointCloud *pointcloud, float r_min[3], float r_max[3]);
void BKE_pointcloud_update_customdata_pointers(struct PointCloud *pointcloud);
bool BKE_pointcloud_customdata_required(struct PointCloud *pointcloud,
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index c89a45b2a43..1a90d6fadd3 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -78,6 +78,7 @@ set(SRC
intern/armature_deform.c
intern/armature_update.c
intern/attribute.c
+ intern/attribute_access.cc
intern/autoexec.c
intern/blender.c
intern/blender_copybuffer.c
@@ -124,6 +125,7 @@ set(SRC
intern/fmodifier.c
intern/font.c
intern/freestyle.c
+ intern/geometry_set.cc
intern/gpencil.c
intern/gpencil_curve.c
intern/gpencil_geom.c
@@ -266,6 +268,7 @@ set(SRC
BKE_appdir.h
BKE_armature.h
BKE_attribute.h
+ BKE_attribute_access.hh
BKE_autoexec.h
BKE_blender.h
BKE_blender_copybuffer.h
@@ -311,6 +314,8 @@ set(SRC
BKE_fluid.h
BKE_font.h
BKE_freestyle.h
+ BKE_geometry_set.h
+ BKE_geometry_set.hh
BKE_global.h
BKE_gpencil.h
BKE_gpencil_curve.h
diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c
index 9ad73133f9e..d8fd3a19303 100644
--- a/source/blender/blenkernel/intern/attribute.c
+++ b/source/blender/blenkernel/intern/attribute.c
@@ -63,8 +63,8 @@ static void get_domains(ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
}
case ID_ME: {
Mesh *mesh = (Mesh *)id;
- info[ATTR_DOMAIN_VERTEX].customdata = &mesh->vdata;
- info[ATTR_DOMAIN_VERTEX].length = mesh->totvert;
+ info[ATTR_DOMAIN_POINT].customdata = &mesh->vdata;
+ info[ATTR_DOMAIN_POINT].length = mesh->totvert;
info[ATTR_DOMAIN_EDGE].customdata = &mesh->edata;
info[ATTR_DOMAIN_EDGE].length = mesh->totedge;
info[ATTR_DOMAIN_CORNER].customdata = &mesh->ldata;
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
new file mode 100644
index 00000000000..2345c834be4
--- /dev/null
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -0,0 +1,1080 @@
+/*
+ * 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 <utility>
+
+#include "BKE_attribute_access.hh"
+#include "BKE_customdata.h"
+#include "BKE_deform.h"
+#include "BKE_geometry_set.hh"
+#include "BKE_mesh.h"
+#include "BKE_pointcloud.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BLI_color.hh"
+#include "BLI_float2.hh"
+#include "BLI_span.hh"
+
+#include "CLG_log.h"
+
+#include "NOD_node_tree_multi_function.hh"
+
+static CLG_LogRef LOG = {"bke.attribute_access"};
+
+using blender::float3;
+using blender::Set;
+using blender::StringRef;
+using blender::bke::ReadAttributePtr;
+using blender::bke::WriteAttributePtr;
+
+/* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */
+extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id);
+
+namespace blender::bke {
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Accessor implementations
+ * \{ */
+
+ReadAttribute::~ReadAttribute()
+{
+ if (array_is_temporary_ && array_buffer_ != nullptr) {
+ cpp_type_.destruct_n(array_buffer_, size_);
+ MEM_freeN(array_buffer_);
+ }
+}
+
+fn::GSpan ReadAttribute::get_span() const
+{
+ if (size_ == 0) {
+ return fn::GSpan(cpp_type_);
+ }
+ if (array_buffer_ == nullptr) {
+ std::lock_guard lock{span_mutex_};
+ if (array_buffer_ == nullptr) {
+ this->initialize_span();
+ }
+ }
+ return fn::GSpan(cpp_type_, array_buffer_, size_);
+}
+
+void ReadAttribute::initialize_span() const
+{
+ const int element_size = cpp_type_.size();
+ array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
+ array_is_temporary_ = true;
+ for (const int i : IndexRange(size_)) {
+ this->get_internal(i, POINTER_OFFSET(array_buffer_, i * element_size));
+ }
+}
+
+WriteAttribute::~WriteAttribute()
+{
+ if (array_should_be_applied_) {
+ CLOG_ERROR(&LOG, "Forgot to call apply_span.");
+ }
+ if (array_is_temporary_ && array_buffer_ != nullptr) {
+ cpp_type_.destruct_n(array_buffer_, size_);
+ MEM_freeN(array_buffer_);
+ }
+}
+
+/**
+ * Get a mutable span that can be modified. When all modifications to the attribute are done,
+ * #apply_span_if_necessary should be called.
+ */
+fn::GMutableSpan WriteAttribute::get_span()
+{
+ if (size_ == 0) {
+ return fn::GMutableSpan(cpp_type_);
+ }
+ if (array_buffer_ == nullptr) {
+ this->initialize_span();
+ }
+ array_should_be_applied_ = true;
+ return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
+}
+
+void WriteAttribute::initialize_span()
+{
+ array_buffer_ = MEM_mallocN_aligned(cpp_type_.size() * size_, cpp_type_.alignment(), __func__);
+ array_is_temporary_ = true;
+ /* This does nothing for trivial types, but is necessary for general correctness. */
+ cpp_type_.construct_default_n(array_buffer_, size_);
+}
+
+void WriteAttribute::apply_span()
+{
+ this->apply_span_if_necessary();
+ array_should_be_applied_ = false;
+}
+
+void WriteAttribute::apply_span_if_necessary()
+{
+ /* Only works when the span has been initialized beforehand. */
+ BLI_assert(array_buffer_ != nullptr);
+
+ const int element_size = cpp_type_.size();
+ for (const int i : IndexRange(size_)) {
+ this->set_internal(i, POINTER_OFFSET(array_buffer_, i * element_size));
+ }
+}
+
+class VertexWeightWriteAttribute final : public WriteAttribute {
+ private:
+ MDeformVert *dverts_;
+ const int dvert_index_;
+
+ public:
+ VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
+ : WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
+ dverts_(dverts),
+ dvert_index_(dvert_index)
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ get_internal(dverts_, dvert_index_, index, r_value);
+ }
+
+ void set_internal(const int64_t index, const void *value) override
+ {
+ MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
+ weight->weight = *reinterpret_cast<const float *>(value);
+ }
+
+ static void get_internal(const MDeformVert *dverts,
+ const int dvert_index,
+ const int64_t index,
+ void *r_value)
+ {
+ if (dverts == nullptr) {
+ *(float *)r_value = 0.0f;
+ return;
+ }
+ const MDeformVert &dvert = dverts[index];
+ for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
+ if (weight.def_nr == dvert_index) {
+ *(float *)r_value = weight.weight;
+ return;
+ }
+ }
+ *(float *)r_value = 0.0f;
+ }
+};
+
+class VertexWeightReadAttribute final : public ReadAttribute {
+ private:
+ const MDeformVert *dverts_;
+ const int dvert_index_;
+
+ public:
+ VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
+ : ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
+ dverts_(dverts),
+ dvert_index_(dvert_index)
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
+ }
+};
+
+template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
+ private:
+ MutableSpan<T> data_;
+
+ public:
+ ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data)
+ : WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ new (r_value) T(data_[index]);
+ }
+
+ void set_internal(const int64_t index, const void *value) override
+ {
+ data_[index] = *reinterpret_cast<const T *>(value);
+ }
+
+ void initialize_span() override
+ {
+ array_buffer_ = data_.data();
+ array_is_temporary_ = false;
+ }
+
+ void apply_span_if_necessary() override
+ {
+ /* Do nothing, because the span contains the attribute itself already. */
+ }
+};
+
+template<typename T> class ArrayReadAttribute final : public ReadAttribute {
+ private:
+ Span<T> data_;
+
+ public:
+ ArrayReadAttribute(AttributeDomain domain, Span<T> data)
+ : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ new (r_value) T(data_[index]);
+ }
+
+ void initialize_span() const override
+ {
+ /* The data will not be modified, so this const_cast is fine. */
+ array_buffer_ = const_cast<T *>(data_.data());
+ array_is_temporary_ = false;
+ }
+};
+
+template<typename StructT, typename ElemT, typename GetFuncT, typename SetFuncT>
+class DerivedArrayWriteAttribute final : public WriteAttribute {
+ private:
+ MutableSpan<StructT> data_;
+ GetFuncT get_function_;
+ SetFuncT set_function_;
+
+ public:
+ DerivedArrayWriteAttribute(AttributeDomain domain,
+ MutableSpan<StructT> data,
+ GetFuncT get_function,
+ SetFuncT set_function)
+ : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()),
+ data_(data),
+ get_function_(std::move(get_function)),
+ set_function_(std::move(set_function))
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ const StructT &struct_value = data_[index];
+ const ElemT value = get_function_(struct_value);
+ new (r_value) ElemT(value);
+ }
+
+ void set_internal(const int64_t index, const void *value) override
+ {
+ StructT &struct_value = data_[index];
+ const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
+ set_function_(struct_value, typed_value);
+ }
+};
+
+template<typename StructT, typename ElemT, typename GetFuncT>
+class DerivedArrayReadAttribute final : public ReadAttribute {
+ private:
+ Span<StructT> data_;
+ GetFuncT get_function_;
+
+ public:
+ DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data, GetFuncT get_function)
+ : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()),
+ data_(data),
+ get_function_(std::move(get_function))
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ const StructT &struct_value = data_[index];
+ const ElemT value = get_function_(struct_value);
+ new (r_value) ElemT(value);
+ }
+};
+
+class ConstantReadAttribute final : public ReadAttribute {
+ private:
+ void *value_;
+
+ public:
+ ConstantReadAttribute(AttributeDomain domain,
+ const int64_t size,
+ const CPPType &type,
+ const void *value)
+ : ReadAttribute(domain, type, size)
+ {
+ value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
+ type.copy_to_uninitialized(value, value_);
+ }
+
+ ~ConstantReadAttribute()
+ {
+ this->cpp_type_.destruct(value_);
+ MEM_freeN(value_);
+ }
+
+ void get_internal(const int64_t UNUSED(index), void *r_value) const override
+ {
+ this->cpp_type_.copy_to_uninitialized(value_, r_value);
+ }
+
+ void initialize_span() const override
+ {
+ const int element_size = cpp_type_.size();
+ array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
+ array_is_temporary_ = true;
+ cpp_type_.fill_uninitialized(value_, array_buffer_, size_);
+ }
+};
+
+class ConvertedReadAttribute final : public ReadAttribute {
+ private:
+ const CPPType &from_type_;
+ const CPPType &to_type_;
+ ReadAttributePtr base_attribute_;
+ const nodes::DataTypeConversions &conversions_;
+
+ static constexpr int MaxValueSize = 64;
+ static constexpr int MaxValueAlignment = 64;
+
+ public:
+ ConvertedReadAttribute(ReadAttributePtr base_attribute, const CPPType &to_type)
+ : ReadAttribute(base_attribute->domain(), to_type, base_attribute->size()),
+ from_type_(base_attribute->cpp_type()),
+ to_type_(to_type),
+ base_attribute_(std::move(base_attribute)),
+ conversions_(nodes::get_implicit_type_conversions())
+ {
+ if (from_type_.size() > MaxValueSize || from_type_.alignment() > MaxValueAlignment) {
+ throw std::runtime_error(
+ "type is larger than expected, the buffer size has to be increased");
+ }
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ AlignedBuffer<MaxValueSize, MaxValueAlignment> buffer;
+ base_attribute_->get(index, buffer.ptr());
+ conversions_.convert(from_type_, to_type_, buffer.ptr(), r_value);
+ }
+};
+
+/** \} */
+
+const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type)
+{
+ switch (type) {
+ case CD_PROP_FLOAT:
+ return &CPPType::get<float>();
+ case CD_PROP_FLOAT2:
+ return &CPPType::get<float2>();
+ case CD_PROP_FLOAT3:
+ return &CPPType::get<float3>();
+ case CD_PROP_INT32:
+ return &CPPType::get<int>();
+ case CD_PROP_COLOR:
+ return &CPPType::get<Color4f>();
+ default:
+ return nullptr;
+ }
+ return nullptr;
+}
+
+CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type)
+{
+ if (type.is<float>()) {
+ return CD_PROP_FLOAT;
+ }
+ if (type.is<float2>()) {
+ return CD_PROP_FLOAT2;
+ }
+ if (type.is<float3>()) {
+ return CD_PROP_FLOAT3;
+ }
+ if (type.is<int>()) {
+ return CD_PROP_INT32;
+ }
+ if (type.is<Color4f>()) {
+ return CD_PROP_COLOR;
+ }
+ return static_cast<CustomDataType>(-1);
+}
+
+} // namespace blender::bke
+
+/* -------------------------------------------------------------------- */
+/** \name Utilities for Accessing Attributes
+ * \{ */
+
+static ReadAttributePtr read_attribute_from_custom_data(const CustomData &custom_data,
+ const int size,
+ const StringRef attribute_name,
+ const AttributeDomain domain)
+{
+ using namespace blender;
+ using namespace blender::bke;
+ for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) {
+ if (layer.name != nullptr && layer.name == attribute_name) {
+ switch (layer.type) {
+ case CD_PROP_FLOAT:
+ return std::make_unique<ArrayReadAttribute<float>>(
+ domain, Span(static_cast<float *>(layer.data), size));
+ case CD_PROP_FLOAT2:
+ return std::make_unique<ArrayReadAttribute<float2>>(
+ domain, Span(static_cast<float2 *>(layer.data), size));
+ case CD_PROP_FLOAT3:
+ return std::make_unique<ArrayReadAttribute<float3>>(
+ domain, Span(static_cast<float3 *>(layer.data), size));
+ case CD_PROP_INT32:
+ return std::make_unique<ArrayReadAttribute<int>>(
+ domain, Span(static_cast<int *>(layer.data), size));
+ case CD_PROP_COLOR:
+ return std::make_unique<ArrayReadAttribute<Color4f>>(
+ domain, Span(static_cast<Color4f *>(layer.data), size));
+ }
+ }
+ }
+ return {};
+}
+
+static WriteAttributePtr write_attribute_from_custom_data(
+ CustomData &custom_data,
+ const int size,
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const std::function<void()> &update_customdata_pointers)
+{
+
+ using namespace blender;
+ using namespace blender::bke;
+ for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) {
+ if (layer.name != nullptr && layer.name == attribute_name) {
+ const void *data_before = layer.data;
+ /* The data layer might be shared with someone else. Since the caller wants to modify it, we
+ * copy it first. */
+ CustomData_duplicate_referenced_layer_named(&custom_data, layer.type, layer.name, size);
+ if (data_before != layer.data) {
+ update_customdata_pointers();
+ }
+ switch (layer.type) {
+ case CD_PROP_FLOAT:
+ return std::make_unique<ArrayWriteAttribute<float>>(
+ domain, MutableSpan(static_cast<float *>(layer.data), size));
+ case CD_PROP_FLOAT2:
+ return std::make_unique<ArrayWriteAttribute<float2>>(
+ domain, MutableSpan(static_cast<float2 *>(layer.data), size));
+ case CD_PROP_FLOAT3:
+ return std::make_unique<ArrayWriteAttribute<float3>>(
+ domain, MutableSpan(static_cast<float3 *>(layer.data), size));
+ case CD_PROP_INT32:
+ return std::make_unique<ArrayWriteAttribute<int>>(
+ domain, MutableSpan(static_cast<int *>(layer.data), size));
+ case CD_PROP_COLOR:
+ return std::make_unique<ArrayWriteAttribute<Color4f>>(
+ domain, MutableSpan(static_cast<Color4f *>(layer.data), size));
+ }
+ }
+ }
+ return {};
+}
+
+/* Returns true when the layer was found and is deleted. */
+static bool delete_named_custom_data_layer(CustomData &custom_data,
+ const StringRef attribute_name,
+ const int size)
+{
+ for (const int index : blender::IndexRange(custom_data.totlayer)) {
+ const CustomDataLayer &layer = custom_data.layers[index];
+ if (layer.name == attribute_name) {
+ CustomData_free_layer(&custom_data, layer.type, size, index);
+ return true;
+ }
+ }
+ return false;
+}
+
+static void get_custom_data_layer_attribute_names(const CustomData &custom_data,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ Set<std::string> &r_names)
+{
+ for (const CustomDataLayer &layer : blender::Span(custom_data.layers, custom_data.totlayer)) {
+ if (component.attribute_domain_with_type_supported(domain,
+ static_cast<CustomDataType>(layer.type))) {
+ r_names.add(layer.name);
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Component
+ * \{ */
+
+bool GeometryComponent::attribute_domain_supported(const AttributeDomain UNUSED(domain)) const
+{
+ return false;
+}
+
+bool GeometryComponent::attribute_domain_with_type_supported(
+ const AttributeDomain UNUSED(domain), const CustomDataType UNUSED(data_type)) const
+{
+ return false;
+}
+
+int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain)) const
+{
+ BLI_assert(false);
+ return 0;
+}
+
+bool GeometryComponent::attribute_is_builtin(const StringRef UNUSED(attribute_name)) const
+{
+ return true;
+}
+
+ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
+ const StringRef UNUSED(attribute_name)) const
+{
+ return {};
+}
+
+ReadAttributePtr GeometryComponent::attribute_try_adapt_domain(ReadAttributePtr attribute,
+ const AttributeDomain domain) const
+{
+ if (attribute && attribute->domain() == domain) {
+ return attribute;
+ }
+ return {};
+}
+
+WriteAttributePtr GeometryComponent::attribute_try_get_for_write(
+ const StringRef UNUSED(attribute_name))
+{
+ return {};
+}
+
+bool GeometryComponent::attribute_try_delete(const StringRef UNUSED(attribute_name))
+{
+ return false;
+}
+
+bool GeometryComponent::attribute_try_create(const StringRef UNUSED(attribute_name),
+ const AttributeDomain UNUSED(domain),
+ const CustomDataType UNUSED(data_type))
+{
+ return false;
+}
+
+Set<std::string> GeometryComponent::attribute_names() const
+{
+ return {};
+}
+
+static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute,
+ const blender::fn::CPPType &to_type)
+{
+ const blender::fn::CPPType &from_type = attribute->cpp_type();
+ if (from_type == to_type) {
+ return attribute;
+ }
+
+ const blender::nodes::DataTypeConversions &conversions =
+ blender::nodes::get_implicit_type_conversions();
+ if (!conversions.is_convertible(from_type, to_type)) {
+ return {};
+ }
+
+ return std::make_unique<blender::bke::ConvertedReadAttribute>(std::move(attribute), to_type);
+}
+
+ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type) const
+{
+ if (!this->attribute_domain_with_type_supported(domain, data_type)) {
+ return {};
+ }
+
+ ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ if (!attribute) {
+ return {};
+ }
+
+ if (attribute->domain() != domain) {
+ attribute = this->attribute_try_adapt_domain(std::move(attribute), domain);
+ if (!attribute) {
+ return {};
+ }
+ }
+
+ const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ BLI_assert(cpp_type != nullptr);
+ if (attribute->cpp_type() != *cpp_type) {
+ attribute = try_adapt_data_type(std::move(attribute), *cpp_type);
+ if (!attribute) {
+ return {};
+ }
+ }
+
+ return attribute;
+}
+
+ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value) const
+{
+ BLI_assert(this->attribute_domain_with_type_supported(domain, data_type));
+
+ ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name, domain, data_type);
+ if (attribute) {
+ return attribute;
+ }
+ return this->attribute_get_constant_for_read(domain, data_type, default_value);
+}
+
+blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read(
+ const AttributeDomain domain, const CustomDataType data_type, const void *value) const
+{
+ BLI_assert(this->attribute_domain_supported(domain));
+ const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ BLI_assert(cpp_type != nullptr);
+ if (value == nullptr) {
+ value = cpp_type->default_value();
+ }
+ const int domain_size = this->attribute_domain_size(domain);
+ return std::make_unique<blender::bke::ConstantReadAttribute>(
+ domain, domain_size, *cpp_type, value);
+}
+
+WriteAttributePtr GeometryComponent::attribute_try_ensure_for_write(const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type)
+{
+ const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ BLI_assert(cpp_type != nullptr);
+
+ WriteAttributePtr attribute = this->attribute_try_get_for_write(attribute_name);
+ if (attribute && attribute->domain() == domain && attribute->cpp_type() == *cpp_type) {
+ return attribute;
+ }
+
+ if (attribute) {
+ if (!this->attribute_try_delete(attribute_name)) {
+ return {};
+ }
+ }
+ if (!this->attribute_domain_with_type_supported(domain, data_type)) {
+ return {};
+ }
+ if (!this->attribute_try_create(attribute_name, domain, data_type)) {
+ return {};
+ }
+ return this->attribute_try_get_for_write(attribute_name);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Point Cloud Component
+ * \{ */
+
+bool PointCloudComponent::attribute_domain_supported(const AttributeDomain domain) const
+{
+ return domain == ATTR_DOMAIN_POINT;
+}
+
+bool PointCloudComponent::attribute_domain_with_type_supported(
+ const AttributeDomain domain, const CustomDataType data_type) const
+{
+ return domain == ATTR_DOMAIN_POINT && ELEM(data_type,
+ CD_PROP_FLOAT,
+ CD_PROP_FLOAT2,
+ CD_PROP_FLOAT3,
+ CD_PROP_INT32,
+ CD_PROP_COLOR);
+}
+
+int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) const
+{
+ BLI_assert(domain == ATTR_DOMAIN_POINT);
+ UNUSED_VARS_NDEBUG(domain);
+ if (pointcloud_ == nullptr) {
+ return 0;
+ }
+ return pointcloud_->totpoint;
+}
+
+bool PointCloudComponent::attribute_is_builtin(const StringRef attribute_name) const
+{
+ return attribute_name == "position";
+}
+
+ReadAttributePtr PointCloudComponent::attribute_try_get_for_read(
+ const StringRef attribute_name) const
+{
+ if (pointcloud_ == nullptr) {
+ return {};
+ }
+
+ return read_attribute_from_custom_data(
+ pointcloud_->pdata, pointcloud_->totpoint, attribute_name, ATTR_DOMAIN_POINT);
+}
+
+WriteAttributePtr PointCloudComponent::attribute_try_get_for_write(const StringRef attribute_name)
+{
+ PointCloud *pointcloud = this->get_for_write();
+ if (pointcloud == nullptr) {
+ return {};
+ }
+
+ return write_attribute_from_custom_data(
+ pointcloud->pdata, pointcloud->totpoint, attribute_name, ATTR_DOMAIN_POINT, [&]() {
+ BKE_pointcloud_update_customdata_pointers(pointcloud);
+ });
+}
+
+bool PointCloudComponent::attribute_try_delete(const StringRef attribute_name)
+{
+ if (this->attribute_is_builtin(attribute_name)) {
+ return false;
+ }
+ PointCloud *pointcloud = this->get_for_write();
+ if (pointcloud == nullptr) {
+ return false;
+ }
+ delete_named_custom_data_layer(pointcloud->pdata, attribute_name, pointcloud->totpoint);
+ return true;
+}
+
+static bool custom_data_has_layer_with_name(const CustomData &custom_data, const StringRef name)
+{
+ for (const CustomDataLayer &layer : blender::Span(custom_data.layers, custom_data.totlayer)) {
+ if (layer.name == name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PointCloudComponent::attribute_try_create(const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type)
+{
+ if (this->attribute_is_builtin(attribute_name)) {
+ return false;
+ }
+ if (!this->attribute_domain_with_type_supported(domain, data_type)) {
+ return false;
+ }
+ PointCloud *pointcloud = this->get_for_write();
+ if (pointcloud == nullptr) {
+ return false;
+ }
+ if (custom_data_has_layer_with_name(pointcloud->pdata, attribute_name)) {
+ return false;
+ }
+
+ char attribute_name_c[MAX_NAME];
+ attribute_name.copy(attribute_name_c);
+ CustomData_add_layer_named(
+ &pointcloud->pdata, data_type, CD_DEFAULT, nullptr, pointcloud_->totpoint, attribute_name_c);
+ return true;
+}
+
+Set<std::string> PointCloudComponent::attribute_names() const
+{
+ if (pointcloud_ == nullptr) {
+ return {};
+ }
+
+ Set<std::string> names;
+ get_custom_data_layer_attribute_names(pointcloud_->pdata, *this, ATTR_DOMAIN_POINT, names);
+ return names;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mesh Component
+ * \{ */
+
+bool MeshComponent::attribute_domain_supported(const AttributeDomain domain) const
+{
+ return ELEM(
+ domain, ATTR_DOMAIN_CORNER, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_POLYGON);
+}
+
+bool MeshComponent::attribute_domain_with_type_supported(const AttributeDomain domain,
+ const CustomDataType data_type) const
+{
+ if (!this->attribute_domain_supported(domain)) {
+ return false;
+ }
+ return ELEM(
+ data_type, CD_PROP_FLOAT, CD_PROP_FLOAT2, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR);
+}
+
+int MeshComponent::attribute_domain_size(const AttributeDomain domain) const
+{
+ BLI_assert(this->attribute_domain_supported(domain));
+ if (mesh_ == nullptr) {
+ return 0;
+ }
+ switch (domain) {
+ case ATTR_DOMAIN_CORNER:
+ return mesh_->totloop;
+ case ATTR_DOMAIN_POINT:
+ return mesh_->totvert;
+ case ATTR_DOMAIN_EDGE:
+ return mesh_->totedge;
+ case ATTR_DOMAIN_POLYGON:
+ return mesh_->totpoly;
+ default:
+ BLI_assert(false);
+ break;
+ }
+ return 0;
+}
+
+bool MeshComponent::attribute_is_builtin(const StringRef attribute_name) const
+{
+ return attribute_name == "position";
+}
+
+ReadAttributePtr MeshComponent::attribute_try_get_for_read(const StringRef attribute_name) const
+{
+ if (mesh_ == nullptr) {
+ return {};
+ }
+
+ if (attribute_name == "position") {
+ auto get_vertex_position = [](const MVert &vert) { return float3(vert.co); };
+ return std::make_unique<
+ blender::bke::DerivedArrayReadAttribute<MVert, float3, decltype(get_vertex_position)>>(
+ ATTR_DOMAIN_POINT, blender::Span(mesh_->mvert, mesh_->totvert), get_vertex_position);
+ }
+
+ ReadAttributePtr corner_attribute = read_attribute_from_custom_data(
+ mesh_->ldata, mesh_->totloop, attribute_name, ATTR_DOMAIN_CORNER);
+ if (corner_attribute) {
+ return corner_attribute;
+ }
+
+ const int vertex_group_index = vertex_group_names_.lookup_default(attribute_name, -1);
+ if (vertex_group_index >= 0) {
+ return std::make_unique<blender::bke::VertexWeightReadAttribute>(
+ mesh_->dvert, mesh_->totvert, vertex_group_index);
+ }
+
+ ReadAttributePtr vertex_attribute = read_attribute_from_custom_data(
+ mesh_->vdata, mesh_->totvert, attribute_name, ATTR_DOMAIN_POINT);
+ if (vertex_attribute) {
+ return vertex_attribute;
+ }
+
+ ReadAttributePtr edge_attribute = read_attribute_from_custom_data(
+ mesh_->edata, mesh_->totedge, attribute_name, ATTR_DOMAIN_EDGE);
+ if (edge_attribute) {
+ return edge_attribute;
+ }
+
+ ReadAttributePtr polygon_attribute = read_attribute_from_custom_data(
+ mesh_->pdata, mesh_->totpoly, attribute_name, ATTR_DOMAIN_POLYGON);
+ if (polygon_attribute) {
+ return polygon_attribute;
+ }
+
+ return {};
+}
+
+WriteAttributePtr MeshComponent::attribute_try_get_for_write(const StringRef attribute_name)
+{
+ Mesh *mesh = this->get_for_write();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ const std::function<void()> update_mesh_pointers = [&]() {
+ BKE_mesh_update_customdata_pointers(mesh, false);
+ };
+
+ if (attribute_name == "position") {
+ CustomData_duplicate_referenced_layer(&mesh->vdata, CD_MVERT, mesh->totvert);
+ update_mesh_pointers();
+
+ auto get_vertex_position = [](const MVert &vert) { return float3(vert.co); };
+ auto set_vertex_position = [](MVert &vert, const float3 &co) { copy_v3_v3(vert.co, co); };
+ return std::make_unique<
+ blender::bke::DerivedArrayWriteAttribute<MVert,
+ float3,
+ decltype(get_vertex_position),
+ decltype(set_vertex_position)>>(
+ ATTR_DOMAIN_POINT,
+ blender::MutableSpan(mesh_->mvert, mesh_->totvert),
+ get_vertex_position,
+ set_vertex_position);
+ }
+
+ WriteAttributePtr corner_attribute = write_attribute_from_custom_data(
+ mesh_->ldata, mesh_->totloop, attribute_name, ATTR_DOMAIN_CORNER, update_mesh_pointers);
+ if (corner_attribute) {
+ return corner_attribute;
+ }
+
+ const int vertex_group_index = vertex_group_names_.lookup_default_as(attribute_name, -1);
+ if (vertex_group_index >= 0) {
+ if (mesh_->dvert == nullptr) {
+ BKE_object_defgroup_data_create(&mesh_->id);
+ }
+ return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
+ mesh_->dvert, mesh_->totvert, vertex_group_index);
+ }
+
+ WriteAttributePtr vertex_attribute = write_attribute_from_custom_data(
+ mesh_->vdata, mesh_->totvert, attribute_name, ATTR_DOMAIN_POINT, update_mesh_pointers);
+ if (vertex_attribute) {
+ return vertex_attribute;
+ }
+
+ WriteAttributePtr edge_attribute = write_attribute_from_custom_data(
+ mesh_->edata, mesh_->totedge, attribute_name, ATTR_DOMAIN_EDGE, update_mesh_pointers);
+ if (edge_attribute) {
+ return edge_attribute;
+ }
+
+ WriteAttributePtr polygon_attribute = write_attribute_from_custom_data(
+ mesh_->pdata, mesh_->totpoly, attribute_name, ATTR_DOMAIN_POLYGON, update_mesh_pointers);
+ if (polygon_attribute) {
+ return polygon_attribute;
+ }
+
+ return {};
+}
+
+bool MeshComponent::attribute_try_delete(const StringRef attribute_name)
+{
+ if (this->attribute_is_builtin(attribute_name)) {
+ return false;
+ }
+ Mesh *mesh = this->get_for_write();
+ if (mesh == nullptr) {
+ return false;
+ }
+
+ delete_named_custom_data_layer(mesh_->ldata, attribute_name, mesh_->totloop);
+ delete_named_custom_data_layer(mesh_->vdata, attribute_name, mesh_->totvert);
+ delete_named_custom_data_layer(mesh_->edata, attribute_name, mesh_->totedge);
+ delete_named_custom_data_layer(mesh_->pdata, attribute_name, mesh_->totpoly);
+
+ const int vertex_group_index = vertex_group_names_.lookup_default_as(attribute_name, -1);
+ if (vertex_group_index != -1) {
+ for (MDeformVert &dvert : blender::MutableSpan(mesh_->dvert, mesh_->totvert)) {
+ MDeformWeight *weight = BKE_defvert_find_index(&dvert, vertex_group_index);
+ BKE_defvert_remove_group(&dvert, weight);
+ }
+ vertex_group_names_.remove_as(attribute_name);
+ }
+
+ return true;
+}
+
+bool MeshComponent::attribute_try_create(const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type)
+{
+ if (this->attribute_is_builtin(attribute_name)) {
+ return false;
+ }
+ if (!this->attribute_domain_with_type_supported(domain, data_type)) {
+ return false;
+ }
+ Mesh *mesh = this->get_for_write();
+ if (mesh == nullptr) {
+ return false;
+ }
+
+ char attribute_name_c[MAX_NAME];
+ attribute_name.copy(attribute_name_c);
+
+ switch (domain) {
+ case ATTR_DOMAIN_CORNER: {
+ if (custom_data_has_layer_with_name(mesh->ldata, attribute_name)) {
+ return false;
+ }
+ CustomData_add_layer_named(
+ &mesh->ldata, data_type, CD_DEFAULT, nullptr, mesh->totloop, attribute_name_c);
+ return true;
+ }
+ case ATTR_DOMAIN_POINT: {
+ if (custom_data_has_layer_with_name(mesh->vdata, attribute_name)) {
+ return false;
+ }
+ if (vertex_group_names_.contains_as(attribute_name)) {
+ return false;
+ }
+ CustomData_add_layer_named(
+ &mesh->vdata, data_type, CD_DEFAULT, nullptr, mesh->totvert, attribute_name_c);
+ return true;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ if (custom_data_has_layer_with_name(mesh->edata, attribute_name)) {
+ return false;
+ }
+ CustomData_add_layer_named(
+ &mesh->edata, data_type, CD_DEFAULT, nullptr, mesh->totedge, attribute_name_c);
+ return true;
+ }
+ case ATTR_DOMAIN_POLYGON: {
+ if (custom_data_has_layer_with_name(mesh->pdata, attribute_name)) {
+ return false;
+ }
+ CustomData_add_layer_named(
+ &mesh->pdata, data_type, CD_DEFAULT, nullptr, mesh->totpoly, attribute_name_c);
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+Set<std::string> MeshComponent::attribute_names() const
+{
+ if (mesh_ == nullptr) {
+ return {};
+ }
+
+ Set<std::string> names;
+ names.add("position");
+ for (StringRef name : vertex_group_names_.keys()) {
+ names.add(name);
+ }
+ get_custom_data_layer_attribute_names(mesh_->pdata, *this, ATTR_DOMAIN_CORNER, names);
+ get_custom_data_layer_attribute_names(mesh_->vdata, *this, ATTR_DOMAIN_POINT, names);
+ get_custom_data_layer_attribute_names(mesh_->edata, *this, ATTR_DOMAIN_EDGE, names);
+ get_custom_data_layer_attribute_names(mesh_->pdata, *this, ATTR_DOMAIN_POLYGON, names);
+ return names;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index 434456a922e..73ca490c395 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -4559,7 +4559,7 @@ void BKE_fluid_particle_system_destroy(struct Object *ob, const int particle_typ
if (psys->part->type == particle_type) {
/* clear modifier */
pfmd = psys_get_modifier(ob, psys);
- BLI_remlink(&ob->modifiers, pfmd);
+ BLI_remlink(&ob->modifiers, (ModifierData *)pfmd);
BKE_modifier_free((ModifierData *)pfmd);
/* clear particle system */
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
new file mode 100644
index 00000000000..28695d769d3
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -0,0 +1,554 @@
+/*
+ * 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_geometry_set.hh"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_wrapper.h"
+#include "BKE_pointcloud.h"
+
+#include "DNA_object_types.h"
+
+#include "MEM_guardedalloc.h"
+
+using blender::float3;
+using blender::MutableSpan;
+using blender::Span;
+using blender::StringRef;
+using blender::Vector;
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Component
+ * \{ */
+
+GeometryComponent::GeometryComponent(GeometryComponentType type) : type_(type)
+{
+}
+
+GeometryComponent ::~GeometryComponent()
+{
+}
+
+GeometryComponent *GeometryComponent::create(GeometryComponentType component_type)
+{
+ switch (component_type) {
+ case GeometryComponentType::Mesh:
+ return new MeshComponent();
+ case GeometryComponentType::PointCloud:
+ return new PointCloudComponent();
+ case GeometryComponentType::Instances:
+ return new InstancesComponent();
+ }
+ BLI_assert(false);
+ return nullptr;
+}
+
+void GeometryComponent::user_add() const
+{
+ users_.fetch_add(1);
+}
+
+void GeometryComponent::user_remove() const
+{
+ const int new_users = users_.fetch_sub(1) - 1;
+ if (new_users == 0) {
+ delete this;
+ }
+}
+
+bool GeometryComponent::is_mutable() const
+{
+ /* If the item is shared, it is read-only. */
+ /* The user count can be 0, when this is called from the destructor. */
+ return users_ <= 1;
+}
+
+GeometryComponentType GeometryComponent::type() const
+{
+ return type_;
+}
+
+bool GeometryComponent::is_empty() const
+{
+ return false;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Set
+ * \{ */
+
+/* This method can only be used when the geometry set is mutable. It returns a mutable geometry
+ * component of the given type.
+ */
+GeometryComponent &GeometrySet::get_component_for_write(GeometryComponentType component_type)
+{
+ return components_.add_or_modify(
+ component_type,
+ [&](GeometryComponentPtr *value_ptr) -> GeometryComponent & {
+ /* If the component did not exist before, create a new one. */
+ new (value_ptr) GeometryComponentPtr(GeometryComponent::create(component_type));
+ return **value_ptr;
+ },
+ [&](GeometryComponentPtr *value_ptr) -> GeometryComponent & {
+ GeometryComponentPtr &value = *value_ptr;
+ if (value->is_mutable()) {
+ /* If the referenced component is already mutable, return it directly. */
+ return *value;
+ }
+ /* If the referenced component is shared, make a copy. The copy is not shared and is
+ * therefore mutable. */
+ GeometryComponent *copied_component = value->copy();
+ value = GeometryComponentPtr{copied_component};
+ return *copied_component;
+ });
+}
+
+/* Get the component of the given type. Might return null if the component does not exist yet. */
+const GeometryComponent *GeometrySet::get_component_for_read(
+ GeometryComponentType component_type) const
+{
+ const GeometryComponentPtr *component = components_.lookup_ptr(component_type);
+ if (component != nullptr) {
+ return component->get();
+ }
+ return nullptr;
+}
+
+bool GeometrySet::has(const GeometryComponentType component_type) const
+{
+ return components_.contains(component_type);
+}
+
+void GeometrySet::remove(const GeometryComponentType component_type)
+{
+ components_.remove(component_type);
+}
+
+void GeometrySet::add(const GeometryComponent &component)
+{
+ BLI_assert(!components_.contains(component.type()));
+ component.user_add();
+ GeometryComponentPtr component_ptr{const_cast<GeometryComponent *>(&component)};
+ components_.add_new(component.type(), std::move(component_ptr));
+}
+
+void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_max) const
+{
+ const PointCloud *pointcloud = this->get_pointcloud_for_read();
+ if (pointcloud != nullptr) {
+ BKE_pointcloud_minmax(pointcloud, *r_min, *r_max);
+ }
+ const Mesh *mesh = this->get_mesh_for_read();
+ if (mesh != nullptr) {
+ BKE_mesh_wrapper_minmax(mesh, *r_min, *r_max);
+ }
+}
+
+std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
+{
+ stream << "<GeometrySet at " << &geometry_set << ", " << geometry_set.components_.size()
+ << " components>";
+ return stream;
+}
+
+/* This generally should not be used. It is necessary currently, so that GeometrySet can by used by
+ * the CPPType system. */
+bool operator==(const GeometrySet &UNUSED(a), const GeometrySet &UNUSED(b))
+{
+ return false;
+}
+
+/* This generally should not be used. It is necessary currently, so that GeometrySet can by used by
+ * the CPPType system. */
+uint64_t GeometrySet::hash() const
+{
+ return reinterpret_cast<uint64_t>(this);
+}
+
+/* Returns a read-only mesh or null. */
+const Mesh *GeometrySet::get_mesh_for_read() const
+{
+ const MeshComponent *component = this->get_component_for_read<MeshComponent>();
+ return (component == nullptr) ? nullptr : component->get_for_read();
+}
+
+/* Returns true when the geometry set has a mesh component that has a mesh. */
+bool GeometrySet::has_mesh() const
+{
+ const MeshComponent *component = this->get_component_for_read<MeshComponent>();
+ return component != nullptr && component->has_mesh();
+}
+
+/* Returns a read-only point cloud of null. */
+const PointCloud *GeometrySet::get_pointcloud_for_read() const
+{
+ const PointCloudComponent *component = this->get_component_for_read<PointCloudComponent>();
+ return (component == nullptr) ? nullptr : component->get_for_read();
+}
+
+/* Returns true when the geometry set has a point cloud component that has a point cloud. */
+bool GeometrySet::has_pointcloud() const
+{
+ const PointCloudComponent *component = this->get_component_for_read<PointCloudComponent>();
+ return component != nullptr && component->has_pointcloud();
+}
+
+/* Returns true when the geometry set has an instances component that has at least one instance. */
+bool GeometrySet::has_instances() const
+{
+ const InstancesComponent *component = this->get_component_for_read<InstancesComponent>();
+ return component != nullptr && component->instances_amount() >= 1;
+}
+
+/* Create a new geometry set that only contains the given mesh. */
+GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
+{
+ GeometrySet geometry_set;
+ MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>();
+ component.replace(mesh, ownership);
+ return geometry_set;
+}
+
+/* Create a new geometry set that only contains the given point cloud. */
+GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
+ GeometryOwnershipType ownership)
+{
+ GeometrySet geometry_set;
+ PointCloudComponent &component = geometry_set.get_component_for_write<PointCloudComponent>();
+ component.replace(pointcloud, ownership);
+ return geometry_set;
+}
+
+/* Clear the existing mesh and replace it with the given one. */
+void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
+{
+ MeshComponent &component = this->get_component_for_write<MeshComponent>();
+ component.replace(mesh, ownership);
+}
+
+/* Clear the existing point cloud and replace with the given one. */
+void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
+{
+ PointCloudComponent &pointcloud_component = this->get_component_for_write<PointCloudComponent>();
+ pointcloud_component.replace(pointcloud, ownership);
+}
+
+/* Returns a mutable mesh or null. No ownership is transferred. */
+Mesh *GeometrySet::get_mesh_for_write()
+{
+ MeshComponent &component = this->get_component_for_write<MeshComponent>();
+ return component.get_for_write();
+}
+
+/* Returns a mutable point cloud or null. No ownership is transferred. */
+PointCloud *GeometrySet::get_pointcloud_for_write()
+{
+ PointCloudComponent &component = this->get_component_for_write<PointCloudComponent>();
+ return component.get_for_write();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mesh Component
+ * \{ */
+
+MeshComponent::MeshComponent() : GeometryComponent(GeometryComponentType::Mesh)
+{
+}
+
+MeshComponent::~MeshComponent()
+{
+ this->clear();
+}
+
+GeometryComponent *MeshComponent::copy() const
+{
+ MeshComponent *new_component = new MeshComponent();
+ if (mesh_ != nullptr) {
+ new_component->mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
+ new_component->ownership_ = GeometryOwnershipType::Owned;
+ }
+ return new_component;
+}
+
+void MeshComponent::clear()
+{
+ BLI_assert(this->is_mutable());
+ if (mesh_ != nullptr) {
+ if (ownership_ == GeometryOwnershipType::Owned) {
+ BKE_id_free(nullptr, mesh_);
+ }
+ mesh_ = nullptr;
+ }
+ vertex_group_names_.clear();
+}
+
+bool MeshComponent::has_mesh() const
+{
+ return mesh_ != nullptr;
+}
+
+/* Clear the component and replace it with the new mesh. */
+void MeshComponent::replace(Mesh *mesh, GeometryOwnershipType ownership)
+{
+ BLI_assert(this->is_mutable());
+ this->clear();
+ mesh_ = mesh;
+ ownership_ = ownership;
+}
+
+/* Return the mesh and clear the component. The caller takes over responsibility for freeing the
+ * mesh (if the component was responsible before). */
+Mesh *MeshComponent::release()
+{
+ BLI_assert(this->is_mutable());
+ Mesh *mesh = mesh_;
+ mesh_ = nullptr;
+ return mesh;
+}
+
+void MeshComponent::copy_vertex_group_names_from_object(const Object &object)
+{
+ BLI_assert(this->is_mutable());
+ vertex_group_names_.clear();
+ int index = 0;
+ LISTBASE_FOREACH (const bDeformGroup *, group, &object.defbase) {
+ vertex_group_names_.add(group->name, index);
+ index++;
+ }
+}
+
+/* Get the mesh from this component. This method can be used by multiple threads at the same
+ * time. Therefore, the returned mesh should not be modified. No ownership is transferred. */
+const Mesh *MeshComponent::get_for_read() const
+{
+ return mesh_;
+}
+
+/* Get the mesh from this component. This method can only be used when the component is mutable,
+ * i.e. it is not shared. The returned mesh can be modified. No ownership is transferred. */
+Mesh *MeshComponent::get_for_write()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ == GeometryOwnershipType::ReadOnly) {
+ mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+ return mesh_;
+}
+
+bool MeshComponent::is_empty() const
+{
+ return mesh_ == nullptr;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Pointcloud Component
+ * \{ */
+
+PointCloudComponent::PointCloudComponent() : GeometryComponent(GeometryComponentType::PointCloud)
+{
+}
+
+PointCloudComponent::~PointCloudComponent()
+{
+ this->clear();
+}
+
+GeometryComponent *PointCloudComponent::copy() const
+{
+ PointCloudComponent *new_component = new PointCloudComponent();
+ if (pointcloud_ != nullptr) {
+ new_component->pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
+ new_component->ownership_ = GeometryOwnershipType::Owned;
+ }
+ return new_component;
+}
+
+void PointCloudComponent::clear()
+{
+ BLI_assert(this->is_mutable());
+ if (pointcloud_ != nullptr) {
+ if (ownership_ == GeometryOwnershipType::Owned) {
+ BKE_id_free(nullptr, pointcloud_);
+ }
+ pointcloud_ = nullptr;
+ }
+}
+
+bool PointCloudComponent::has_pointcloud() const
+{
+ return pointcloud_ != nullptr;
+}
+
+/* Clear the component and replace it with the new point cloud. */
+void PointCloudComponent::replace(PointCloud *pointcloud, GeometryOwnershipType ownership)
+{
+ BLI_assert(this->is_mutable());
+ this->clear();
+ pointcloud_ = pointcloud;
+ ownership_ = ownership;
+}
+
+/* Return the point cloud and clear the component. The caller takes over responsibility for freeing
+ * the point cloud (if the component was responsible before). */
+PointCloud *PointCloudComponent::release()
+{
+ BLI_assert(this->is_mutable());
+ PointCloud *pointcloud = pointcloud_;
+ pointcloud_ = nullptr;
+ return pointcloud;
+}
+
+/* Get the point cloud from this component. This method can be used by multiple threads at the same
+ * time. Therefore, the returned point cloud should not be modified. No ownership is transferred.
+ */
+const PointCloud *PointCloudComponent::get_for_read() const
+{
+ return pointcloud_;
+}
+
+/* Get the point cloud from this component. This method can only be used when the component is
+ * mutable, i.e. it is not shared. The returned point cloud can be modified. No ownership is
+ * transferred. */
+PointCloud *PointCloudComponent::get_for_write()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ == GeometryOwnershipType::ReadOnly) {
+ pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+ return pointcloud_;
+}
+
+bool PointCloudComponent::is_empty() const
+{
+ return pointcloud_ == nullptr;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Instances Component
+ * \{ */
+
+InstancesComponent::InstancesComponent() : GeometryComponent(GeometryComponentType::Instances)
+{
+}
+
+GeometryComponent *InstancesComponent::copy() const
+{
+ InstancesComponent *new_component = new InstancesComponent();
+ new_component->positions_ = positions_;
+ new_component->rotations_ = rotations_;
+ new_component->scales_ = scales_;
+ new_component->objects_ = objects_;
+ return new_component;
+}
+
+void InstancesComponent::clear()
+{
+ objects_.clear();
+ positions_.clear();
+ rotations_.clear();
+ scales_.clear();
+}
+void InstancesComponent::add_instance(const Object *object,
+ blender::float3 position,
+ blender::float3 rotation,
+ blender::float3 scale)
+{
+ objects_.append(object);
+ positions_.append(position);
+ rotations_.append(rotation);
+ scales_.append(scale);
+}
+
+Span<const Object *> InstancesComponent::objects() const
+{
+ return objects_;
+}
+
+Span<float3> InstancesComponent::positions() const
+{
+ return positions_;
+}
+
+blender::Span<blender::float3> InstancesComponent::rotations() const
+{
+ return rotations_;
+}
+
+blender::Span<blender::float3> InstancesComponent::scales() const
+{
+ return scales_;
+}
+
+MutableSpan<float3> InstancesComponent::positions()
+{
+ return positions_;
+}
+
+int InstancesComponent::instances_amount() const
+{
+ BLI_assert(positions_.size() == objects_.size());
+ return objects_.size();
+}
+
+bool InstancesComponent::is_empty() const
+{
+ return positions_.size() == 0;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name C API
+ * \{ */
+
+void BKE_geometry_set_free(GeometrySet *geometry_set)
+{
+ delete geometry_set;
+}
+
+bool BKE_geometry_set_has_instances(const GeometrySet *geometry_set)
+{
+ return geometry_set->get_component_for_read<InstancesComponent>() != nullptr;
+}
+
+int BKE_geometry_set_instances(const GeometrySet *geometry_set,
+ float (**r_positions)[3],
+ float (**r_rotations)[3],
+ float (**r_scales)[3],
+ Object ***r_objects)
+{
+ const InstancesComponent *component = geometry_set->get_component_for_read<InstancesComponent>();
+ if (component == nullptr) {
+ return 0;
+ }
+ *r_positions = (float(*)[3])component->positions().data();
+ *r_rotations = (float(*)[3])component->rotations().data();
+ *r_scales = (float(*)[3])component->scales().data();
+ *r_objects = (Object **)component->objects().data();
+ return component->instances_amount();
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c
index 554919ad1a0..a44b054e366 100644
--- a/source/blender/blenkernel/intern/hair.c
+++ b/source/blender/blenkernel/intern/hair.c
@@ -49,8 +49,8 @@
#include "BLO_read_write.h"
-static const char *HAIR_ATTR_POSITION = "Position";
-static const char *HAIR_ATTR_RADIUS = "Radius";
+static const char *HAIR_ATTR_POSITION = "position";
+static const char *HAIR_ATTR_RADIUS = "radius";
/* Hair datablock */
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index f0031d4191d..9e3189afee9 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -342,7 +342,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o
static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id)
{
/* Update all group nodes using a node group. */
- ntreeUpdateAllUsers(bmain, new_id);
+ ntreeUpdateAllUsers(bmain, (bNodeTree *)new_id);
}
/**
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index e639bb319a3..b564a4c468b 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -39,6 +39,7 @@
#include "DNA_light_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_material_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "DNA_simulation_types.h"
@@ -65,7 +66,6 @@
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_node.h"
-#include "BKE_simulation.h"
#include "BLI_ghash.h"
#include "BLI_threads.h"
@@ -75,8 +75,8 @@
#include "NOD_common.h"
#include "NOD_composite.h"
#include "NOD_function.h"
+#include "NOD_geometry.h"
#include "NOD_shader.h"
-#include "NOD_simulation.h"
#include "NOD_socket.h"
#include "NOD_texture.h"
@@ -85,6 +85,8 @@
#include "BLO_read_write.h"
+#include "MOD_nodes.h"
+
#define NODE_DEFAULT_MAX_WIDTH 700
/* Fallback types for undefined tree, nodes, sockets */
@@ -3958,14 +3960,18 @@ void ntreeUpdateAllNew(Main *main)
FOREACH_NODETREE_END;
}
-void ntreeUpdateAllUsers(Main *main, ID *ngroup)
+void ntreeUpdateAllUsers(Main *main, bNodeTree *ngroup)
{
+ if (ngroup == NULL) {
+ return;
+ }
+
/* Update all users of ngroup, to add/remove sockets as needed. */
FOREACH_NODETREE_BEGIN (main, ntree, owner_id) {
bool need_update = false;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
- if (node->id == ngroup) {
+ if (node->id == &ngroup->id) {
if (node->typeinfo->group_update_func) {
node->typeinfo->group_update_func(ntree, node);
}
@@ -3979,6 +3985,19 @@ void ntreeUpdateAllUsers(Main *main, ID *ngroup)
}
}
FOREACH_NODETREE_END;
+
+ if (ngroup->type == NTREE_GEOMETRY) {
+ LISTBASE_FOREACH (Object *, object, &main->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
+ if (md->type == eModifierType_Nodes) {
+ NodesModifierData *nmd = (NodesModifierData *)md;
+ if (nmd->node_group == ngroup) {
+ MOD_nodes_update_interface(object, nmd);
+ }
+ }
+ }
+ }
+ }
}
void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
@@ -4022,7 +4041,7 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
}
if (bmain) {
- ntreeUpdateAllUsers(bmain, &ntree->id);
+ ntreeUpdateAllUsers(bmain, ntree);
}
if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) {
@@ -4659,9 +4678,21 @@ static void registerTextureNodes(void)
register_node_type_tex_proc_distnoise();
}
-static void registerSimulationNodes(void)
+static void registerGeometryNodes(void)
{
- register_node_type_sim_group();
+ register_node_type_geo_group();
+
+ register_node_type_geo_triangulate();
+ register_node_type_geo_edge_split();
+ register_node_type_geo_transform();
+ register_node_type_geo_subdivision_surface();
+ register_node_type_geo_boolean();
+ register_node_type_geo_point_distribute();
+ register_node_type_geo_point_instance();
+ register_node_type_geo_object_info();
+ register_node_type_geo_random_attribute();
+ register_node_type_geo_attribute_math();
+ register_node_type_geo_join_geometry();
}
static void registerFunctionNodes(void)
@@ -4688,7 +4719,7 @@ void BKE_node_system_init(void)
register_node_tree_type_cmp();
register_node_tree_type_sh();
register_node_tree_type_tex();
- register_node_tree_type_sim();
+ register_node_tree_type_geo();
register_node_type_frame();
register_node_type_reroute();
@@ -4698,7 +4729,7 @@ void BKE_node_system_init(void)
registerCompositNodes();
registerShaderNodes();
registerTextureNodes();
- registerSimulationNodes();
+ registerGeometryNodes();
registerFunctionNodes();
}
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 242c0edd5a4..31d2e3738f4 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -94,6 +94,7 @@
#include "BKE_fcurve.h"
#include "BKE_fcurve_driver.h"
#include "BKE_font.h"
+#include "BKE_geometry_set.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
@@ -1284,8 +1285,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
}
if (ob->type == OB_POINTCLOUD) {
- return (mti->modifyPointCloud != NULL) ||
- (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
+ return (mti->modifyPointCloud != NULL);
}
if (ob->type == OB_VOLUME) {
return (mti->modifyVolume != NULL);
@@ -1507,6 +1507,9 @@ void BKE_object_eval_assign_data(Object *object_eval, ID *data_eval, bool is_own
object_eval->data = data_eval;
}
}
+
+ /* Is set separately currently. */
+ object_eval->runtime.geometry_set_eval = NULL;
}
/**
@@ -1551,6 +1554,11 @@ void BKE_object_free_derived_caches(Object *ob)
BKE_gpencil_eval_delete(ob->runtime.gpd_eval);
ob->runtime.gpd_eval = NULL;
}
+
+ if (ob->runtime.geometry_set_eval != NULL) {
+ BKE_geometry_set_free(ob->runtime.geometry_set_eval);
+ ob->runtime.geometry_set_eval = NULL;
+ }
}
void BKE_object_free_caches(Object *object)
@@ -1771,6 +1779,10 @@ int BKE_object_visibility(const Object *ob, const int dag_eval_mode)
visibility |= OB_VISIBLE_INSTANCES;
}
+ if (ob->runtime.geometry_set_eval != NULL) {
+ visibility |= OB_VISIBLE_INSTANCES;
+ }
+
/* Optional hiding of self if there are particles or instancers. */
if (visibility & (OB_VISIBLE_PARTICLES | OB_VISIBLE_INSTANCES)) {
switch ((eEvaluationMode)dag_eval_mode) {
@@ -4880,6 +4892,7 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag))
runtime->mesh_deform_eval = NULL;
runtime->curve_cache = NULL;
runtime->object_as_temp_mesh = NULL;
+ runtime->geometry_set_eval = NULL;
}
/**
diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c
index a1b01bce6c9..9a8c560f116 100644
--- a/source/blender/blenkernel/intern/object_dupli.c
+++ b/source/blender/blenkernel/intern/object_dupli.c
@@ -47,6 +47,7 @@
#include "BKE_editmesh.h"
#include "BKE_editmesh_cache.h"
#include "BKE_font.h"
+#include "BKE_geometry_set.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_lattice.h"
@@ -807,6 +808,45 @@ static const DupliGenerator gen_dupli_verts_pointcloud = {
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Instances Geometry Component Implementation
+ * \{ */
+
+static void make_duplis_instances_component(const DupliContext *ctx)
+{
+ float(*positions)[3];
+ float(*rotations)[3];
+ float(*scales)[3];
+ Object **objects;
+ const int amount = BKE_geometry_set_instances(
+ ctx->object->runtime.geometry_set_eval, &positions, &rotations, &scales, &objects);
+
+ for (int i = 0; i < amount; i++) {
+ Object *object = objects[i];
+ if (object == NULL) {
+ continue;
+ }
+ float scale_matrix[4][4];
+ size_to_mat4(scale_matrix, scales[i]);
+ float rotation_matrix[4][4];
+ eul_to_mat4(rotation_matrix, rotations[i]);
+ float matrix[4][4];
+ mul_m4_m4m4(matrix, rotation_matrix, scale_matrix);
+ copy_v3_v3(matrix[3], positions[i]);
+ mul_m4_m4_pre(matrix, ctx->object->obmat);
+
+ make_dupli(ctx, object, matrix, i);
+ make_recursive_duplis(ctx, object, matrix, i);
+ }
+}
+
+static const DupliGenerator gen_dupli_instances_component = {
+ 0,
+ make_duplis_instances_component,
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Dupli-Faces Implementation (#OB_DUPLIFACES)
* \{ */
@@ -1473,7 +1513,7 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
int transflag = ctx->object->transflag;
int restrictflag = ctx->object->restrictflag;
- if ((transflag & OB_DUPLI) == 0) {
+ if ((transflag & OB_DUPLI) == 0 && ctx->object->runtime.geometry_set_eval == NULL) {
return NULL;
}
@@ -1483,6 +1523,12 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
return NULL;
}
+ if (ctx->object->runtime.geometry_set_eval != NULL) {
+ if (BKE_geometry_set_has_instances(ctx->object->runtime.geometry_set_eval)) {
+ return &gen_dupli_instances_component;
+ }
+ }
+
if (transflag & OB_DUPLIPARTS) {
return &gen_dupli_particles;
}
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 72115db4fb8..14e049384ef 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -1198,9 +1198,6 @@ static bool foreach_object_modifier_ptcache(Object *object,
}
}
}
- else if (md->type == eModifierType_Simulation) {
- /* TODO(jacques): */
- }
}
return true;
}
diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc
index cbfb1086e8d..5f6685817b9 100644
--- a/source/blender/blenkernel/intern/pointcloud.cc
+++ b/source/blender/blenkernel/intern/pointcloud.cc
@@ -33,12 +33,14 @@
#include "BKE_anim_data.h"
#include "BKE_customdata.h"
+#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
+#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_pointcloud.h"
@@ -53,8 +55,8 @@
static void pointcloud_random(PointCloud *pointcloud);
-const char *POINTCLOUD_ATTR_POSITION = "Position";
-const char *POINTCLOUD_ATTR_RADIUS = "Radius";
+const char *POINTCLOUD_ATTR_POSITION = "position";
+const char *POINTCLOUD_ATTR_RADIUS = "radius";
static void pointcloud_init_data(ID *id)
{
@@ -86,6 +88,8 @@ static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_s
alloc_type,
pointcloud_dst->totpoint);
BKE_pointcloud_update_customdata_pointers(pointcloud_dst);
+
+ pointcloud_dst->batch_cache = nullptr;
}
static void pointcloud_free_data(ID *id)
@@ -230,10 +234,46 @@ void *BKE_pointcloud_add_default(Main *bmain, const char *name)
return pointcloud;
}
+PointCloud *BKE_pointcloud_new_nomain(const int totpoint)
+{
+ PointCloud *pointcloud = static_cast<PointCloud *>(BKE_libblock_alloc(
+ nullptr, ID_PT, BKE_idtype_idcode_to_name(ID_PT), LIB_ID_CREATE_LOCALIZE));
+
+ pointcloud_init_data(&pointcloud->id);
+
+ pointcloud->totpoint = totpoint;
+
+ CustomData_add_layer_named(&pointcloud->pdata,
+ CD_PROP_FLOAT,
+ CD_CALLOC,
+ nullptr,
+ pointcloud->totpoint,
+ POINTCLOUD_ATTR_RADIUS);
+
+ pointcloud->totpoint = totpoint;
+ CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint);
+ BKE_pointcloud_update_customdata_pointers(pointcloud);
+
+ return pointcloud;
+}
+
+void BKE_pointcloud_minmax(const struct PointCloud *pointcloud, float r_min[3], float r_max[3])
+{
+ float(*pointcloud_co)[3] = pointcloud->co;
+ float *pointcloud_radius = pointcloud->radius;
+ for (int a = 0; a < pointcloud->totpoint; a++) {
+ float *co = pointcloud_co[a];
+ float radius = (pointcloud_radius) ? pointcloud_radius[a] : 0.0f;
+ const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius};
+ const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius};
+ DO_MIN(co_min, r_min);
+ DO_MAX(co_max, r_max);
+ }
+}
+
BoundBox *BKE_pointcloud_boundbox_get(Object *ob)
{
BLI_assert(ob->type == OB_POINTCLOUD);
- PointCloud *pointcloud = static_cast<PointCloud *>(ob->data);
if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
return ob->runtime.bb;
@@ -241,23 +281,18 @@ BoundBox *BKE_pointcloud_boundbox_get(Object *ob)
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = static_cast<BoundBox *>(MEM_callocN(sizeof(BoundBox), "pointcloud boundbox"));
+ }
- float min[3], max[3];
- INIT_MINMAX(min, max);
-
- float(*pointcloud_co)[3] = pointcloud->co;
- float *pointcloud_radius = pointcloud->radius;
- for (int a = 0; a < pointcloud->totpoint; a++) {
- float *co = pointcloud_co[a];
- float radius = (pointcloud_radius) ? pointcloud_radius[a] : 0.0f;
- const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius};
- const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius};
- DO_MIN(co_min, min);
- DO_MAX(co_max, max);
- }
-
- BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+ blender::float3 min, max;
+ INIT_MINMAX(min, max);
+ if (ob->runtime.geometry_set_eval != nullptr) {
+ ob->runtime.geometry_set_eval->compute_boundbox_without_instances(&min, &max);
+ }
+ else {
+ const PointCloud *pointcloud = static_cast<PointCloud *>(ob->data);
+ BKE_pointcloud_minmax(pointcloud, min, max);
}
+ BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
return ob->runtime.bb;
}
@@ -306,13 +341,11 @@ PointCloud *BKE_pointcloud_copy_for_eval(struct PointCloud *pointcloud_src, bool
return result;
}
-static PointCloud *pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
- struct Scene *scene,
- Object *object,
- PointCloud *pointcloud_input)
+static void pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ Object *object,
+ GeometrySet &geometry_set)
{
- PointCloud *pointcloud = pointcloud_input;
-
/* Modifier evaluation modes. */
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
@@ -332,40 +365,10 @@ static PointCloud *pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
continue;
}
- if ((mti->type == eModifierTypeType_OnlyDeform) &&
- (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) {
- /* Ensure we are not modifying the input. */
- if (pointcloud == pointcloud_input) {
- pointcloud = BKE_pointcloud_copy_for_eval(pointcloud, true);
- }
-
- /* Ensure we are not overwriting referenced data. */
- CustomData_duplicate_referenced_layer_named(
- &pointcloud->pdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION, pointcloud->totpoint);
- BKE_pointcloud_update_customdata_pointers(pointcloud);
-
- /* Created deformed coordinates array on demand. */
- mti->deformVerts(md, &mectx, nullptr, pointcloud->co, pointcloud->totpoint);
- }
- else if (mti->modifyPointCloud) {
- /* Ensure we are not modifying the input. */
- if (pointcloud == pointcloud_input) {
- pointcloud = BKE_pointcloud_copy_for_eval(pointcloud, true);
- }
-
- PointCloud *pointcloud_next = mti->modifyPointCloud(md, &mectx, pointcloud);
-
- if (pointcloud_next && pointcloud_next != pointcloud) {
- /* If the modifier returned a new pointcloud, release the old one. */
- if (pointcloud != pointcloud_input) {
- BKE_id_free(nullptr, pointcloud);
- }
- pointcloud = pointcloud_next;
- }
+ if (mti->modifyPointCloud) {
+ mti->modifyPointCloud(md, &mectx, &geometry_set);
}
}
-
- return pointcloud;
}
void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
@@ -375,12 +378,14 @@ void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene
/* Evaluate modifiers. */
PointCloud *pointcloud = static_cast<PointCloud *>(object->data);
- PointCloud *pointcloud_eval = pointcloud_evaluate_modifiers(
- depsgraph, scene, object, pointcloud);
+ GeometrySet geometry_set = GeometrySet::create_with_pointcloud(pointcloud,
+ GeometryOwnershipType::ReadOnly);
+ pointcloud_evaluate_modifiers(depsgraph, scene, object, geometry_set);
/* Assign evaluated object. */
- const bool is_owned = (pointcloud != pointcloud_eval);
- BKE_object_eval_assign_data(object, &pointcloud_eval->id, is_owned);
+ PointCloud *dummy_pointcloud = BKE_pointcloud_new_nomain(0);
+ BKE_object_eval_assign_data(object, &dummy_pointcloud->id, true);
+ object->runtime.geometry_set_eval = new GeometrySet(geometry_set);
}
/* Draw Cache */
diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc
index 861779ec700..14e6ce63023 100644
--- a/source/blender/blenkernel/intern/simulation.cc
+++ b/source/blender/blenkernel/intern/simulation.cc
@@ -48,8 +48,8 @@
#include "BKE_pointcache.h"
#include "BKE_simulation.h"
+#include "NOD_geometry.h"
#include "NOD_node_tree_multi_function.hh"
-#include "NOD_simulation.h"
#include "BLI_map.hh"
#include "BLT_translation.h"
@@ -70,7 +70,7 @@ static void simulation_init_data(ID *id)
MEMCPY_STRUCT_AFTER(simulation, DNA_struct_default_get(Simulation), id);
- bNodeTree *ntree = ntreeAddTree(nullptr, "Simulation Nodetree", ntreeType_Simulation->idname);
+ bNodeTree *ntree = ntreeAddTree(nullptr, "Geometry Nodetree", ntreeType_Geometry->idname);
simulation->nodetree = ntree;
}
diff --git a/source/blender/blenlib/BLI_rand.h b/source/blender/blenlib/BLI_rand.h
index 78a323c2431..ec74cef9311 100644
--- a/source/blender/blenlib/BLI_rand.h
+++ b/source/blender/blenlib/BLI_rand.h
@@ -60,6 +60,12 @@ void BLI_rng_get_tri_sample_float_v2(struct RNG *rng,
const float v2[2],
const float v3[2],
float r_pt[2]) ATTR_NONNULL();
+void BLI_rng_get_tri_sample_float_v3(RNG *rng,
+ const float v1[3],
+ const float v2[3],
+ const float v3[3],
+ float r_pt[3]) ATTR_NONNULL();
+
void BLI_rng_shuffle_array(struct RNG *rng,
void *data,
unsigned int elem_size_i,
diff --git a/source/blender/blenlib/BLI_rand.hh b/source/blender/blenlib/BLI_rand.hh
index 1b321bd7bcd..0c0dd464d4d 100644
--- a/source/blender/blenlib/BLI_rand.hh
+++ b/source/blender/blenlib/BLI_rand.hh
@@ -118,6 +118,7 @@ class RandomNumberGenerator {
float2 get_unit_float2();
float3 get_unit_float3();
float2 get_triangle_sample(float2 v1, float2 v2, float2 v3);
+ float3 get_triangle_sample_3d(float3 v1, float3 v2, float3 v3);
void get_bytes(MutableSpan<char> r_bytes);
/**
diff --git a/source/blender/blenlib/BLI_user_counter.hh b/source/blender/blenlib/BLI_user_counter.hh
new file mode 100644
index 00000000000..ef276814981
--- /dev/null
+++ b/source/blender/blenlib/BLI_user_counter.hh
@@ -0,0 +1,158 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+#include <atomic>
+
+namespace blender {
+
+/**
+ * A simple automatic reference counter. It is similar to std::shared_ptr, but expects that the
+ * reference count is inside the object.
+ */
+template<typename T> class UserCounter {
+ private:
+ T *data_ = nullptr;
+
+ public:
+ UserCounter() = default;
+
+ UserCounter(T *data) : data_(data)
+ {
+ }
+
+ UserCounter(const UserCounter &other) : data_(other.data_)
+ {
+ this->user_add(data_);
+ }
+
+ UserCounter(UserCounter &&other) : data_(other.data_)
+ {
+ other.data_ = nullptr;
+ }
+
+ ~UserCounter()
+ {
+ this->user_remove(data_);
+ }
+
+ UserCounter &operator=(const UserCounter &other)
+ {
+ if (this == &other) {
+ return *this;
+ }
+
+ this->user_remove(data_);
+ data_ = other.data_;
+ this->user_add(data_);
+ return *this;
+ }
+
+ UserCounter &operator=(UserCounter &&other)
+ {
+ if (this == &other) {
+ return *this;
+ }
+
+ this->user_remove(data_);
+ data_ = other.data_;
+ other.data_ = nullptr;
+ return *this;
+ }
+
+ T *operator->()
+ {
+ BLI_assert(data_ != nullptr);
+ return data_;
+ }
+
+ T &operator*()
+ {
+ BLI_assert(data_ != nullptr);
+ return *data_;
+ }
+
+ operator bool() const
+ {
+ return data_ != nullptr;
+ }
+
+ T *get()
+ {
+ return data_;
+ }
+
+ const T *get() const
+ {
+ return data_;
+ }
+
+ T *release()
+ {
+ T *data = data_;
+ data_ = nullptr;
+ return data;
+ }
+
+ void reset()
+ {
+ this->user_remove(data_);
+ data_ = nullptr;
+ }
+
+ bool has_value() const
+ {
+ return data_ != nullptr;
+ }
+
+ uint64_t hash() const
+ {
+ return DefaultHash<T *>{}(data_);
+ }
+
+ friend bool operator==(const UserCounter &a, const UserCounter &b)
+ {
+ return a.data_ == b.data_;
+ }
+
+ friend std::ostream &operator<<(std::ostream &stream, const UserCounter &value)
+ {
+ stream << value.data_;
+ return stream;
+ }
+
+ private:
+ static void user_add(T *data)
+ {
+ if (data != nullptr) {
+ data->user_add();
+ }
+ }
+
+ static void user_remove(T *data)
+ {
+ if (data != nullptr) {
+ data->user_remove();
+ }
+ }
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 9736d8f9b3b..46a3ad87dfe 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -279,6 +279,7 @@ set(SRC
BLI_timecode.h
BLI_timeit.hh
BLI_timer.h
+ BLI_user_counter.hh
BLI_utildefines.h
BLI_utildefines_iter.h
BLI_utildefines_stack.h
diff --git a/source/blender/blenlib/intern/rand.cc b/source/blender/blenlib/intern/rand.cc
index 224db31471d..c61e17e6627 100644
--- a/source/blender/blenlib/intern/rand.cc
+++ b/source/blender/blenlib/intern/rand.cc
@@ -141,6 +141,12 @@ void BLI_rng_get_tri_sample_float_v2(
copy_v2_v2(r_pt, rng->rng.get_triangle_sample(v1, v2, v3));
}
+void BLI_rng_get_tri_sample_float_v3(
+ RNG *rng, const float v1[3], const float v2[3], const float v3[3], float r_pt[3])
+{
+ copy_v3_v3(r_pt, rng->rng.get_triangle_sample_3d(v1, v2, v3));
+}
+
void BLI_rng_shuffle_array(RNG *rng, void *data, unsigned int elem_size_i, unsigned int elem_tot)
{
const uint elem_size = elem_size_i;
@@ -425,6 +431,25 @@ float2 RandomNumberGenerator::get_triangle_sample(float2 v1, float2 v2, float2 v
return sample;
}
+float3 RandomNumberGenerator::get_triangle_sample_3d(float3 v1, float3 v2, float3 v3)
+{
+ float u = this->get_float();
+ float v = this->get_float();
+
+ if (u + v > 1.0f) {
+ u = 1.0f - u;
+ v = 1.0f - v;
+ }
+
+ float3 side_u = v2 - v1;
+ float3 side_v = v3 - v1;
+
+ float3 sample = v1;
+ sample += side_u * u;
+ sample += side_v * v;
+ return sample;
+}
+
void RandomNumberGenerator::get_bytes(MutableSpan<char> r_bytes)
{
constexpr int64_t mask_bytes = 2;
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index aca8d2f3439..9278ff51b8d 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -519,6 +519,20 @@ static void do_versions_point_attributes(CustomData *pdata)
}
}
+static void do_versions_point_attribute_names(CustomData *pdata)
+{
+ /* Change from capital initial letter to lower case (T82693). */
+ for (int i = 0; i < pdata->totlayer; i++) {
+ CustomDataLayer *layer = &pdata->layers[i];
+ if (layer->type == CD_PROP_FLOAT3 && STREQ(layer->name, "Position")) {
+ STRNCPY(layer->name, "position");
+ }
+ else if (layer->type == CD_PROP_FLOAT && STREQ(layer->name, "Radius")) {
+ STRNCPY(layer->name, "radius");
+ }
+ }
+}
+
/* Move FCurve handles towards the control point in such a way that the curve itself doesn't
* change. Since 2.91 FCurves are computed slightly differently, which requires this update to keep
* the same animation result. Previous versions scaled down overlapping handles during evaluation.
@@ -1201,5 +1215,13 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ /* Hair and PointCloud attributes names. */
+ LISTBASE_FOREACH (Hair *, hair, &bmain->hairs) {
+ do_versions_point_attribute_names(&hair->pdata);
+ }
+ LISTBASE_FOREACH (PointCloud *, pointcloud, &bmain->pointclouds) {
+ do_versions_point_attribute_names(&pointcloud->pdata);
+ }
}
}
diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt
index 2f613a0d416..95ae587f4ce 100644
--- a/source/blender/depsgraph/CMakeLists.txt
+++ b/source/blender/depsgraph/CMakeLists.txt
@@ -24,6 +24,7 @@ set(INC
../blenlib
../bmesh
../draw
+ ../functions
../makesdna
../makesrna
../modifiers
diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h
index 2147a584765..f894bdabba4 100644
--- a/source/blender/depsgraph/DEG_depsgraph_build.h
+++ b/source/blender/depsgraph/DEG_depsgraph_build.h
@@ -141,6 +141,9 @@ void DEG_add_object_relation(struct DepsNodeHandle *node_handle,
void DEG_add_simulation_relation(struct DepsNodeHandle *node_handle,
struct Simulation *simulation,
const char *description);
+void DEG_add_node_tree_relation(struct DepsNodeHandle *node_handle,
+ struct bNodeTree *node_tree,
+ const char *description);
void DEG_add_bone_relation(struct DepsNodeHandle *handle,
struct Object *object,
const char *bone_name,
diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc
index 96c17ae4dc5..6717ba521f6 100644
--- a/source/blender/depsgraph/intern/depsgraph_build.cc
+++ b/source/blender/depsgraph/intern/depsgraph_build.cc
@@ -32,6 +32,7 @@
#include "PIL_time_utildefines.h"
#include "DNA_cachefile_types.h"
+#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_simulation_types.h"
@@ -116,6 +117,17 @@ void DEG_add_simulation_relation(DepsNodeHandle *node_handle,
deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description);
}
+void DEG_add_node_tree_relation(DepsNodeHandle *node_handle,
+ bNodeTree *node_tree,
+ const char *description)
+{
+ /* Using shading key, because that's the one that exists right now. Should use something else in
+ * the future. */
+ deg::ComponentKey shading_key(&node_tree->id, deg::NodeType::SHADING);
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg_node_handle->builder->add_node_handle_relation(shading_key, deg_node_handle, description);
+}
+
void DEG_add_object_cache_relation(DepsNodeHandle *node_handle,
CacheFile *cache_file,
eDepsObjectComponentType component,
diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h
index ecb98a46f99..417cae800ea 100644
--- a/source/blender/editors/include/ED_node.h
+++ b/source/blender/editors/include/ED_node.h
@@ -96,7 +96,7 @@ void ED_node_set_tree_type(struct SpaceNode *snode, struct bNodeTreeType *typein
bool ED_node_is_compositor(struct SpaceNode *snode);
bool ED_node_is_shader(struct SpaceNode *snode);
bool ED_node_is_texture(struct SpaceNode *snode);
-bool ED_node_is_simulation(struct SpaceNode *snode);
+bool ED_node_is_geometry(struct SpaceNode *snode);
void ED_node_shader_default(const struct bContext *C, struct ID *id);
void ED_node_composit_default(const struct bContext *C, struct Scene *scene);
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 0b22afb7e66..6bd95cd8e51 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -90,6 +90,8 @@
#include "ED_screen.h"
#include "ED_sculpt.h"
+#include "MOD_nodes.h"
+
#include "UI_interface.h"
#include "WM_api.h"
@@ -232,6 +234,9 @@ ModifierData *ED_object_modifier_add(
/* ensure skin-node customdata exists */
BKE_mesh_ensure_skin_customdata(ob->data);
}
+ else if (type == eModifierType_Nodes) {
+ MOD_nodes_init(bmain, (NodesModifierData *)new_md);
+ }
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index 0757f840375..d8f31161c20 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -73,5 +73,8 @@ if(WITH_OPENIMAGEDENOISE)
add_definitions(-DWITH_OPENIMAGEDENOISE)
endif()
+if(WITH_OPENSUBDIV)
+ add_definitions(-DWITH_OPENSUBDIV)
+endif()
blender_add_lib(bf_editor_space_node "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index a0e03490fa3..8fdc86f8599 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -71,8 +71,8 @@
#include "IMB_imbuf_types.h"
#include "NOD_composite.h"
+#include "NOD_geometry.h"
#include "NOD_shader.h"
-#include "NOD_simulation.h"
#include "NOD_texture.h"
#include "node_intern.h" /* own include */
@@ -3142,10 +3142,65 @@ static void node_texture_set_butfunc(bNodeType *ntype)
}
}
-/* ****************** BUTTON CALLBACKS FOR SIMULATION NODES ***************** */
+/* ****************** BUTTON CALLBACKS FOR GEOMETRY NODES ***************** */
-static void node_simulation_set_butfunc(bNodeType *UNUSED(ntype))
+static void node_geometry_buts_boolean_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
+ uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
+}
+
+static void node_geometry_buts_subdivision_surface(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr))
+{
+#ifndef WITH_OPENSUBDIV
+ uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR);
+#else
+ UNUSED_VARS(layout);
+#endif
+}
+
+static void node_geometry_buts_triangulate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "quad_method", DEFAULT_FLAGS, "", ICON_NONE);
+ uiItemR(layout, ptr, "ngon_method", DEFAULT_FLAGS, "", ICON_NONE);
+}
+
+static void node_geometry_buts_random_attribute(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE);
+}
+
+static void node_geometry_buts_attribute_math(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
+ uiItemR(layout, ptr, "input_type_a", DEFAULT_FLAGS, IFACE_("Type A"), ICON_NONE);
+ uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
+}
+
+static void node_geometry_set_butfunc(bNodeType *ntype)
+{
+ switch (ntype->type) {
+ case GEO_NODE_BOOLEAN:
+ ntype->draw_buttons = node_geometry_buts_boolean_math;
+ break;
+ case GEO_NODE_SUBDIVISION_SURFACE:
+ ntype->draw_buttons = node_geometry_buts_subdivision_surface;
+ break;
+ case GEO_NODE_TRIANGULATE:
+ ntype->draw_buttons = node_geometry_buts_triangulate;
+ break;
+ case GEO_NODE_RANDOM_ATTRIBUTE:
+ ntype->draw_buttons = node_geometry_buts_random_attribute;
+ break;
+ case GEO_NODE_ATTRIBUTE_MATH:
+ ntype->draw_buttons = node_geometry_buts_attribute_math;
+ break;
+ }
}
/* ****************** BUTTON CALLBACKS FOR FUNCTION NODES ***************** */
@@ -3290,7 +3345,7 @@ void ED_node_init_butfuncs(void)
node_composit_set_butfunc(ntype);
node_shader_set_butfunc(ntype);
node_texture_set_butfunc(ntype);
- node_simulation_set_butfunc(ntype);
+ node_geometry_set_butfunc(ntype);
node_function_set_butfunc(ntype);
/* define update callbacks for socket properties */
@@ -3302,7 +3357,7 @@ void ED_node_init_butfuncs(void)
ntreeType_Composite->ui_icon = ICON_NODE_COMPOSITING;
ntreeType_Shader->ui_icon = ICON_NODE_MATERIAL;
ntreeType_Texture->ui_icon = ICON_NODE_TEXTURE;
- ntreeType_Simulation->ui_icon = ICON_PHYSICS; /* TODO: Use correct icon. */
+ ntreeType_Geometry->ui_icon = ICON_PHYSICS; /* TODO: Use correct icon. */
}
void ED_init_custom_node_type(bNodeType *ntype)
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index a567ae41a54..fb83414e435 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -138,6 +138,9 @@ void ED_node_tag_update_id(ID *id)
DEG_id_tag_update(id, 0);
WM_main_add_notifier(NC_TEXTURE | ND_NODES, id);
}
+ else if (ntree->type == NTREE_GEOMETRY) {
+ WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id);
+ }
else if (id == &ntree->id) {
/* node groups */
DEG_id_tag_update(id, 0);
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index a39e84419d0..039ddad71ef 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -68,8 +68,8 @@
#include "IMB_imbuf_types.h"
#include "NOD_composite.h"
+#include "NOD_geometry.h"
#include "NOD_shader.h"
-#include "NOD_simulation.h"
#include "NOD_texture.h"
#include "node_intern.h" /* own include */
@@ -391,6 +391,7 @@ void snode_dag_update(bContext *C, SpaceNode *snode)
}
DEG_id_tag_update(snode->id, 0);
+ DEG_id_tag_update(&snode->nodetree->id, 0);
}
void snode_notify(bContext *C, SpaceNode *snode)
@@ -416,6 +417,9 @@ void snode_notify(bContext *C, SpaceNode *snode)
else if (ED_node_is_texture(snode)) {
WM_event_add_notifier(C, NC_TEXTURE | ND_NODES, id);
}
+ else if (ED_node_is_geometry(snode)) {
+ WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id);
+ }
}
void ED_node_set_tree_type(SpaceNode *snode, bNodeTreeType *typeinfo)
@@ -443,9 +447,9 @@ bool ED_node_is_texture(struct SpaceNode *snode)
return STREQ(snode->tree_idname, ntreeType_Texture->idname);
}
-bool ED_node_is_simulation(struct SpaceNode *snode)
+bool ED_node_is_geometry(struct SpaceNode *snode)
{
- return STREQ(snode->tree_idname, ntreeType_Simulation->idname);
+ return STREQ(snode->tree_idname, ntreeType_Geometry->idname);
}
/* assumes nothing being done in ntree yet, sets the default in/out node */
@@ -1696,7 +1700,7 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- do_tag_update |= ED_node_is_simulation(snode);
+ do_tag_update |= ED_node_is_geometry(snode);
snode_notify(C, snode);
if (do_tag_update) {
@@ -1740,7 +1744,7 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- do_tag_update |= ED_node_is_simulation(snode);
+ do_tag_update |= ED_node_is_geometry(snode);
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c
index 654b95858bb..f2abe272f48 100644
--- a/source/blender/editors/space_node/node_group.c
+++ b/source/blender/editors/space_node/node_group.c
@@ -77,7 +77,7 @@ static bool node_group_operator_active_poll(bContext *C)
"ShaderNodeTree",
"CompositorNodeTree",
"TextureNodeTree",
- "SimulationNodeTree")) {
+ "GeometryNodeTree")) {
return true;
}
}
@@ -94,7 +94,7 @@ static bool node_group_operator_editable(bContext *C)
* with same keymap.
*/
if (ED_node_is_shader(snode) || ED_node_is_compositor(snode) || ED_node_is_texture(snode) ||
- ED_node_is_simulation(snode)) {
+ ED_node_is_geometry(snode)) {
return true;
}
}
@@ -120,8 +120,8 @@ static const char *group_node_idname(bContext *C)
if (ED_node_is_texture(snode)) {
return "TextureNodeGroup";
}
- if (ED_node_is_simulation(snode)) {
- return "SimulationNodeGroup";
+ if (ED_node_is_geometry(snode)) {
+ return "GeometryNodeGroup";
}
return "";
diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c
index b6d9cdc729d..ba1e752e276 100644
--- a/source/blender/editors/space_node/node_relationships.c
+++ b/source/blender/editors/space_node/node_relationships.c
@@ -655,7 +655,7 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
}
ntree->is_updating = false;
- do_tag_update |= ED_node_is_simulation(snode);
+ do_tag_update |= ED_node_is_geometry(snode);
ntreeUpdateTree(bmain, ntree);
snode_notify(C, snode);
@@ -1052,7 +1052,7 @@ static int cut_links_exec(bContext *C, wmOperator *op)
}
}
- do_tag_update |= ED_node_is_simulation(snode);
+ do_tag_update |= ED_node_is_geometry(snode);
if (found) {
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
@@ -1932,6 +1932,7 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
ntreeUpdateTree(bmain, snode->edittree); /* needed for pointers */
snode_update(snode, select);
+ ED_node_tag_update_id((ID *)snode->edittree);
ED_node_tag_update_id(snode->id);
}
}
diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c
index 76c25799e2c..609fc43b435 100644
--- a/source/blender/editors/space_node/space_node.c
+++ b/source/blender/editors/space_node/space_node.c
@@ -932,7 +932,7 @@ static void node_space_subtype_item_extend(bContext *C, EnumPropertyItem **item,
const EnumPropertyItem *item_src = RNA_enum_node_tree_types_itemf_impl(C, &free);
for (const EnumPropertyItem *item_iter = item_src; item_iter->identifier; item_iter++) {
if (!U.experimental.use_new_geometry_nodes &&
- STREQ(item_iter->identifier, "SimulationNodeTree")) {
+ STREQ(item_iter->identifier, "GeometryNodeTree")) {
continue;
}
RNA_enum_item_add(item, totitem, item_iter);
diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt
index e005753e228..429959f9c33 100644
--- a/source/blender/functions/CMakeLists.txt
+++ b/source/blender/functions/CMakeLists.txt
@@ -39,6 +39,7 @@ set(SRC
FN_attributes_ref.hh
FN_cpp_type.hh
FN_generic_pointer.hh
+ FN_generic_value_map.hh
FN_generic_vector_array.hh
FN_multi_function.hh
FN_multi_function_builder.hh
diff --git a/source/blender/functions/FN_generic_value_map.hh b/source/blender/functions/FN_generic_value_map.hh
new file mode 100644
index 00000000000..2c1b37c0461
--- /dev/null
+++ b/source/blender/functions/FN_generic_value_map.hh
@@ -0,0 +1,123 @@
+/*
+ * 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 "BLI_linear_allocator.hh"
+#include "BLI_map.hh"
+
+#include "FN_generic_pointer.hh"
+
+namespace blender::fn {
+
+/**
+ * This is a map that stores key-value-pairs. What makes it special is that the type of values does
+ * not have to be known at compile time. There just has to be a corresponding CPPType.
+ */
+template<typename Key> class GValueMap {
+ private:
+ /* Used to allocate values owned by this container. */
+ LinearAllocator<> &allocator_;
+ Map<Key, GMutablePointer> values_;
+
+ public:
+ GValueMap(LinearAllocator<> &allocator) : allocator_(allocator)
+ {
+ }
+
+ ~GValueMap()
+ {
+ /* Destruct all values that are still in the map. */
+ for (GMutablePointer value : values_.values()) {
+ value.destruct();
+ }
+ }
+
+ /* Add a value to the container. The container becomes responsible for destructing the value that
+ * is passed in. The caller remains responsible for freeing the value after it has been
+ * destructed. */
+ template<typename ForwardKey> void add_new_direct(ForwardKey &&key, GMutablePointer value)
+ {
+ values_.add_new_as(std::forward<ForwardKey>(key), value);
+ }
+
+ /* Add a value to the container that is move constructed from the given value. The caller remains
+ * responsible for destructing and freeing the given value. */
+ template<typename ForwardKey> void add_new_by_move(ForwardKey &&key, GMutablePointer value)
+ {
+ const CPPType &type = *value.type();
+ void *buffer = allocator_.allocate(type.size(), type.alignment());
+ type.move_to_uninitialized(value.get(), buffer);
+ values_.add_new_as(std::forward<ForwardKey>(key), GMutablePointer{type, buffer});
+ }
+
+ /* Add a value to the container that is copy constructed from the given value. The caller remains
+ * responsible for destructing and freeing the given value. */
+ template<typename ForwardKey> void add_new_by_copy(ForwardKey &&key, GMutablePointer value)
+ {
+ const CPPType &type = *value.type();
+ void *buffer = allocator_.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(value.get(), buffer);
+ values_.add_new_as(std::forward<ForwardKey>(key), GMutablePointer{type, buffer});
+ }
+
+ /* Add a value to the container. */
+ template<typename ForwardKey, typename T> void add_new(ForwardKey &&key, T &&value)
+ {
+ if constexpr (std::is_rvalue_reference_v<T>) {
+ this->add_new_by_move(std::forward<ForwardKey>(key), &value);
+ }
+ else {
+ this->add_new_by_copy(std::forward<ForwardKey>(key), &value);
+ }
+ }
+
+ /* Remove the value for the given name from the container and remove it. The caller is
+ * responsible for freeing it. The lifetime of the referenced memory might be bound to lifetime
+ * of the container. */
+ template<typename ForwardKey> GMutablePointer extract(const ForwardKey &key)
+ {
+ return values_.pop_as(key);
+ }
+
+ /* Remove the value for the given name from the container and remove it. */
+ template<typename T, typename ForwardKey> T extract(const ForwardKey &key)
+ {
+ GMutablePointer value = values_.pop_as(key);
+ const CPPType &type = *value.type();
+ BLI_assert(type.is<T>());
+ T return_value;
+ type.relocate_to_initialized(value.get(), &return_value);
+ return return_value;
+ }
+
+ template<typename T, typename ForwardKey> T lookup(const ForwardKey &key) const
+ {
+ GMutablePointer value = values_.lookup_as(key);
+ const CPPType &type = *value.type();
+ BLI_assert(type.is<T>());
+ T return_value;
+ type.copy_to_initialized(value.get(), &return_value);
+ return return_value;
+ }
+
+ template<typename ForwardKey> bool contains(const ForwardKey &key) const
+ {
+ return values_.contains_as(key);
+ }
+};
+
+} // namespace blender::fn
diff --git a/source/blender/functions/FN_multi_function.hh b/source/blender/functions/FN_multi_function.hh
index bf431984946..d8924b3cf23 100644
--- a/source/blender/functions/FN_multi_function.hh
+++ b/source/blender/functions/FN_multi_function.hh
@@ -122,4 +122,17 @@ inline MFParamsBuilder::MFParamsBuilder(const class MultiFunction &fn, int64_t m
extern const MultiFunction &dummy_multi_function;
+namespace multi_function_types {
+using fn::CPPType;
+using fn::GMutableSpan;
+using fn::GSpan;
+using fn::MFContext;
+using fn::MFContextBuilder;
+using fn::MFDataType;
+using fn::MFParams;
+using fn::MFParamsBuilder;
+using fn::MFParamType;
+using fn::MultiFunction;
+} // namespace multi_function_types
+
} // namespace blender::fn
diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h
index b5bcfa4d157..f73f43ddade 100644
--- a/source/blender/makesdna/DNA_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_modifier_defaults.h
@@ -573,7 +573,7 @@
.flag = 0, \
}
-#define _DNA_DEFAULT_SimulationModifierData \
+#define _DNA_DEFAULT_NodesModifierData \
{ 0 }
#define _DNA_DEFAULT_SkinModifierData \
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 7b5bdac47f5..43ed532a65b 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -94,7 +94,7 @@ typedef enum ModifierType {
eModifierType_WeightedNormal = 54,
eModifierType_Weld = 55,
eModifierType_Fluid = 56,
- eModifierType_Simulation = 57,
+ eModifierType_Nodes = 57,
eModifierType_MeshToVolume = 58,
eModifierType_VolumeDisplace = 59,
eModifierType_VolumeToMesh = 60,
@@ -2224,9 +2224,16 @@ enum {
#define MOD_MESHSEQ_READ_ALL \
(MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)
-typedef struct SimulationModifierData {
+typedef struct NodesModifierSettings {
+ /* This stores data that is passed into the node group. */
+ struct IDProperty *properties;
+} NodesModifierSettings;
+
+typedef struct NodesModifierData {
ModifierData modifier;
-} SimulationModifierData;
+ struct bNodeTree *node_group;
+ struct NodesModifierSettings settings;
+} NodesModifierData;
typedef struct MeshToVolumeModifierData {
ModifierData modifier;
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index fdae3559599..29c83d2d4ed 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -504,7 +504,7 @@ typedef struct bNodeTree {
#define NTREE_SHADER 0
#define NTREE_COMPOSIT 1
#define NTREE_TEXTURE 2
-#define NTREE_SIMULATION 3
+#define NTREE_GEOMETRY 3
/* ntree->init, flag */
#define NTREE_TYPE_INIT 1
@@ -1437,17 +1437,32 @@ typedef enum NodeShaderOutputTarget {
SHD_OUTPUT_CYCLES = 2,
} NodeShaderOutputTarget;
-/* Particle Time Step Event node */
-typedef enum NodeSimParticleTimeStepEventType {
- NODE_PARTICLE_TIME_STEP_EVENT_BEGIN = 0,
- NODE_PARTICLE_TIME_STEP_EVENT_END = 1,
-} NodeSimParticleTimeStepEventType;
-
-/* Simulation Time node */
-typedef enum NodeSimInputTimeType {
- NODE_SIM_INPUT_SIMULATION_TIME = 0,
- NODE_SIM_INPUT_SCENE_TIME = 1,
-} NodeSimInputTimeType;
+/* Geometry Nodes */
+
+/* Boolean Node */
+typedef enum GeometryNodeBooleanOperation {
+ GEO_NODE_BOOLEAN_INTERSECT = 0,
+ GEO_NODE_BOOLEAN_UNION = 1,
+ GEO_NODE_BOOLEAN_DIFFERENCE = 2,
+} GeometryNodeBooleanOperation;
+
+/* Triangulate Node */
+typedef enum GeometryNodeTriangulateNGons {
+ GEO_NODE_TRIANGULATE_NGON_BEAUTY = 0,
+ GEO_NODE_TRIANGULATE_NGON_EARCLIP = 1,
+} GeometryNodeTriangulateNGons;
+
+typedef enum GeometryNodeTriangulateQuads {
+ GEO_NODE_TRIANGULATE_QUAD_BEAUTY = 0,
+ GEO_NODE_TRIANGULATE_QUAD_FIXED = 1,
+ GEO_NODE_TRIANGULATE_QUAD_ALTERNATE = 2,
+ GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE = 3,
+} GeometryNodeTriangulateQuads;
+
+typedef enum GeometryNodeUseAttributeFlag {
+ GEO_NODE_USE_ATTRIBUTE_A = (1 << 0),
+ GEO_NODE_USE_ATTRIBUTE_B = (1 << 1),
+} GeometryNodeUseAttributeFlag;
#ifdef __cplusplus
}
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 5a00b6479d5..328e4d2ba22 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -51,6 +51,7 @@ struct RigidBodyOb;
struct SculptSession;
struct SoftBody;
struct bGPdata;
+struct GeometrySet;
/* Vertex Groups - Name Info */
typedef struct bDeformGroup {
@@ -153,6 +154,13 @@ typedef struct Object_Runtime {
* It has all modifiers applied.
*/
struct ID *data_eval;
+
+ /**
+ * Some objects support evaluating to a geometry set instead of a single ID. In those cases the
+ * evaluated geometry will be stored here instead of in #data_eval.
+ */
+ struct GeometrySet *geometry_set_eval;
+
/**
* Mesh structure created during object evaluation.
* It has deformation only modifiers applied on it.
diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c
index 8c95a6d2a31..1a8bd25215f 100644
--- a/source/blender/makesdna/intern/dna_defaults.c
+++ b/source/blender/makesdna/intern/dna_defaults.c
@@ -271,7 +271,7 @@ SDNA_DEFAULT_DECL_STRUCT(ScrewModifierData);
/* Shape key modifier has no items. */
SDNA_DEFAULT_DECL_STRUCT(ShrinkwrapModifierData);
SDNA_DEFAULT_DECL_STRUCT(SimpleDeformModifierData);
-SDNA_DEFAULT_DECL_STRUCT(SimulationModifierData);
+SDNA_DEFAULT_DECL_STRUCT(NodesModifierData);
SDNA_DEFAULT_DECL_STRUCT(SkinModifierData);
SDNA_DEFAULT_DECL_STRUCT(SmoothModifierData);
/* Softbody modifier skipped for now. */
@@ -491,7 +491,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
/* Shape key modifier has no items. */
SDNA_DEFAULT_DECL(ShrinkwrapModifierData),
SDNA_DEFAULT_DECL(SimpleDeformModifierData),
- SDNA_DEFAULT_DECL(SimulationModifierData),
+ SDNA_DEFAULT_DECL(NodesModifierData),
SDNA_DEFAULT_DECL(SkinModifierData),
SDNA_DEFAULT_DECL(SmoothModifierData),
/* Softbody modifier skipped for now. */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 6dfd7b0fdf5..bc25a404de8 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -566,10 +566,11 @@ extern StructRNA RNA_SimpleDeformModifier;
extern StructRNA RNA_SimplifyGpencilModifier;
extern StructRNA RNA_Simulation;
#ifdef WITH_GEOMETRY_NODES
-extern StructRNA RNA_SimulationModifier;
+extern StructRNA RNA_NodesModifier;
+extern StructRNA RNA_NodesModifierSettings;
#endif
-extern StructRNA RNA_SimulationNode;
-extern StructRNA RNA_SimulationNodeTree;
+extern StructRNA RNA_GeometryNode;
+extern StructRNA RNA_GeometryNodeTree;
extern StructRNA RNA_SkinModifier;
extern StructRNA RNA_SmoothGpencilModifier;
extern StructRNA RNA_SmoothModifier;
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index d638d6f66cc..3ebbb98934e 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -373,6 +373,7 @@ blender_include_dirs(
../../ikplugin
../../imbuf
../../makesdna
+ ../../modifiers
../../nodes/
../../sequencer
../../simulation
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index ceccccc3291..4991f34c3f6 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -1160,8 +1160,8 @@ PropertySubType RNA_property_subtype(PropertyRNA *prop)
if (prop->magic != RNA_MAGIC) {
IDProperty *idprop = (IDProperty *)prop;
- /* Restrict to arrays only for now for performance reasons. */
- if (idprop->type == IDP_ARRAY && ELEM(idprop->subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)) {
+ if (ELEM(idprop->type, IDP_INT, IDP_FLOAT, IDP_DOUBLE) ||
+ ((idprop->type == IDP_ARRAY) && ELEM(idprop->subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE))) {
const IDProperty *idp_ui = rna_idproperty_ui(prop);
if (idp_ui) {
diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c
index 7f0ab2df70d..ad615026343 100644
--- a/source/blender/makesrna/intern/rna_attribute.c
+++ b/source/blender/makesrna/intern/rna_attribute.c
@@ -50,13 +50,12 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
const EnumPropertyItem rna_enum_attribute_domain_items[] = {
/* Not implement yet */
// {ATTR_DOMAIN_GEOMETRY, "GEOMETRY", 0, "Geometry", "Attribute on (whole) geometry"},
- {ATTR_DOMAIN_VERTEX, "VERTEX", 0, "Vertex", "Attribute on mesh vertex"},
+ {ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"},
{ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"},
{ATTR_DOMAIN_CORNER, "CORNER", 0, "Corner", "Attribute on mesh polygon corner"},
{ATTR_DOMAIN_POLYGON, "POLYGON", 0, "Polygon", "Attribute on mesh polygons"},
/* Not implement yet */
// {ATTR_DOMAIN_GRIDS, "GRIDS", 0, "Grids", "Attribute on mesh multires grids"},
- {ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"},
{ATTR_DOMAIN_CURVE, "CURVE", 0, "Curve", "Attribute on hair curve"},
{0, NULL, 0, NULL, NULL},
};
@@ -117,7 +116,7 @@ const EnumPropertyItem *rna_enum_attribute_domain_itemf(ID *id, bool *r_free)
if (id_type == ID_HA && !ELEM(domain_item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
continue;
}
- if (id_type == ID_ME && ELEM(domain_item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
+ if (id_type == ID_ME && ELEM(domain_item->value, ATTR_DOMAIN_CURVE)) {
continue;
}
@@ -619,7 +618,7 @@ static void rna_def_attribute_group(BlenderRNA *brna)
parm = RNA_def_enum(func,
"domain",
rna_enum_attribute_domain_items,
- ATTR_DOMAIN_VERTEX,
+ ATTR_DOMAIN_POINT,
"Domain",
"Type of element that attribute is stored on");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index c0f3197b2d1..09c1869b84b 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -29,7 +29,6 @@
#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
-#include "DNA_simulation_types.h"
#include "MEM_guardedalloc.h"
@@ -43,6 +42,7 @@
#include "BKE_dynamicpaint.h"
#include "BKE_effect.h"
#include "BKE_fluid.h" /* For BKE_fluid_modifier_free & BKE_fluid_modifier_create_type_data */
+#include "BKE_idprop.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_remap.h"
#include "BKE_multires.h"
@@ -57,6 +57,8 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "MOD_nodes.h"
+
const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
{0, "", 0, N_("Modify"), ""},
{eModifierType_DataTransfer,
@@ -141,6 +143,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_MOD_EDGESPLIT,
"Edge Split",
"Split away joined faces at the edges"},
+ {eModifierType_Nodes, "NODES", ICON_NODETREE, "Geometry Nodes", ""},
{eModifierType_Mask,
"MASK",
ICON_MOD_MASK,
@@ -305,11 +308,6 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
"Spawn particles from the shape"},
{eModifierType_Softbody, "SOFT_BODY", ICON_MOD_SOFT, "Soft Body", ""},
{eModifierType_Surface, "SURFACE", ICON_MODIFIER, "Surface", ""},
- {eModifierType_Simulation,
- "SIMULATION",
- ICON_PHYSICS,
- "Simulation",
- ""}, /* TODO: Use correct icon. */
{0, NULL, 0, NULL, NULL},
};
@@ -1588,6 +1586,37 @@ static int rna_MeshSequenceCacheModifier_read_velocity_get(PointerRNA *ptr)
# endif
}
+static bool rna_NodesModifier_node_group_poll(PointerRNA *ptr, PointerRNA value)
+{
+ NodesModifierData *nmd = ptr->data;
+ bNodeTree *ntree = value.data;
+ UNUSED_VARS(nmd, ntree);
+ return true;
+}
+
+static void rna_NodesModifier_node_group_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ Object *object = (Object *)ptr->owner_id;
+ NodesModifierData *nmd = ptr->data;
+ rna_Modifier_dependency_update(bmain, scene, ptr);
+ MOD_nodes_update_interface(object, nmd);
+}
+
+static IDProperty *rna_NodesModifierSettings_properties(PointerRNA *ptr, bool create)
+{
+ NodesModifierSettings *settings = ptr->data;
+ if (create && settings->properties == NULL) {
+ IDPropertyTemplate val = {0};
+ settings->properties = IDP_New(IDP_GROUP, &val, "Nodes Modifier Settings");
+ }
+ return settings->properties;
+}
+
+static char *rna_NodesModifierSettings_path(PointerRNA *UNUSED(ptr))
+{
+ return BLI_strdup("settings");
+}
+
#else
static void rna_def_property_subdivision_common(StructRNA *srna)
@@ -6907,18 +6936,43 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna)
}
# ifdef WITH_GEOMETRY_NODES
-static void rna_def_modifier_simulation(BlenderRNA *brna)
+static void rna_def_modifier_nodes_settings(BlenderRNA *brna)
{
StructRNA *srna;
- srna = RNA_def_struct(brna, "SimulationModifier", "Modifier");
- RNA_def_struct_ui_text(srna, "Simulation Modifier", "");
- RNA_def_struct_sdna(srna, "SimulationModifierData");
- RNA_def_struct_ui_icon(srna, ICON_PHYSICS); /* TODO: Use correct icon. */
+ srna = RNA_def_struct(brna, "NodesModifierSettings", NULL);
+ RNA_def_struct_nested(brna, srna, "NodesModifier");
+ RNA_def_struct_path_func(srna, "rna_NodesModifierSettings_path");
+ RNA_def_struct_ui_text(
+ srna, "Nodes Modifier Settings", "Settings that are passed into the node group");
+ RNA_def_struct_idprops_func(srna, "rna_NodesModifierSettings_properties");
+}
+
+static void rna_def_modifier_nodes(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "NodesModifier", "Modifier");
+ RNA_def_struct_ui_text(srna, "Nodes Modifier", "");
+ RNA_def_struct_sdna(srna, "NodesModifierData");
+ RNA_def_struct_ui_icon(srna, ICON_MESH_DATA); /* TODO: Use correct icon. */
RNA_define_lib_overridable(true);
+ prop = RNA_def_property(srna, "node_group", PROP_POINTER, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Node Group", "Node group that controls what this modifier does");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_NodesModifier_node_group_poll");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, 0, "rna_NodesModifier_node_group_update");
+
+ prop = RNA_def_property(srna, "settings", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_ui_text(prop, "Settings", "Settings that are passed into the node group");
+
RNA_define_lib_overridable(false);
+
+ rna_def_modifier_nodes_settings(brna);
}
# endif
@@ -7277,7 +7331,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_surfacedeform(brna);
rna_def_modifier_weightednormal(brna);
# ifdef WITH_GEOMETRY_NODES
- rna_def_modifier_simulation(brna);
+ rna_def_modifier_nodes(brna);
# endif
rna_def_modifier_mesh_to_volume(brna);
rna_def_modifier_volume_displace(brna);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 67bb5e89254..11d57919fb1 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -36,9 +36,9 @@
#include "DNA_texture_types.h"
#include "BKE_animsys.h"
+#include "BKE_attribute.h"
#include "BKE_image.h"
#include "BKE_node.h"
-#include "BKE_simulation.h"
#include "BKE_texture.h"
#include "RNA_access.h"
@@ -369,6 +369,72 @@ static const EnumPropertyItem prop_shader_output_target_items[] = {
{SHD_OUTPUT_CYCLES, "CYCLES", 0, "Cycles", "Use shaders for Cycles renderer"},
{0, NULL, 0, NULL, NULL},
};
+
+static const EnumPropertyItem rna_node_geometry_boolean_method_items[] = {
+ {GEO_NODE_BOOLEAN_INTERSECT,
+ "INTERSECT",
+ 0,
+ "Intersect",
+ "Keep the part of the mesh that is common between all operands"},
+ {GEO_NODE_BOOLEAN_UNION, "UNION", 0, "Union", "Combine meshes in an additive way"},
+ {GEO_NODE_BOOLEAN_DIFFERENCE,
+ "DIFFERENCE",
+ 0,
+ "Difference",
+ "Combine meshes in a subtractive way"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static const EnumPropertyItem rna_node_geometry_triangulate_quad_method_items[] = {
+ {GEO_NODE_TRIANGULATE_QUAD_BEAUTY,
+ "BEAUTY",
+ 0,
+ "Beauty",
+ "Split the quads in nice triangles, slower method"},
+ {GEO_NODE_TRIANGULATE_QUAD_FIXED,
+ "FIXED",
+ 0,
+ "Fixed",
+ "Split the quads on the first and third vertices"},
+ {GEO_NODE_TRIANGULATE_QUAD_ALTERNATE,
+ "FIXED_ALTERNATE",
+ 0,
+ "Fixed Alternate",
+ "Split the quads on the 2nd and 4th vertices"},
+ {GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE,
+ "SHORTEST_DIAGONAL",
+ 0,
+ "Shortest Diagonal",
+ "Split the quads based on the distance between the vertices"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static const EnumPropertyItem rna_node_geometry_triangulate_ngon_method_items[] = {
+ {GEO_NODE_TRIANGULATE_NGON_BEAUTY,
+ "BEAUTY",
+ 0,
+ "Beauty",
+ "Arrange the new triangles evenly (slow)"},
+ {GEO_NODE_TRIANGULATE_NGON_EARCLIP,
+ "CLIP",
+ 0,
+ "Clip",
+ "Split the polygons with an ear clipping algorithm"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static const EnumPropertyItem rna_node_geometry_attribute_input_a_items[] = {
+ {0, "FLOAT", 0, "Float", ""},
+ {GEO_NODE_USE_ATTRIBUTE_A, "ATTRIBUTE", 0, "Attribute", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static const EnumPropertyItem rna_node_geometry_attribute_input_b_items[] = {
+ {0, "FLOAT", 0, "Float", ""},
+ {GEO_NODE_USE_ATTRIBUTE_B, "ATTRIBUTE", 0, "Attribute", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
#endif
#ifdef RNA_RUNTIME
@@ -727,9 +793,9 @@ static const EnumPropertyItem *rna_node_static_type_itemf(bContext *UNUSED(C),
# undef DefNode
}
- if (RNA_struct_is_a(ptr->type, &RNA_SimulationNode)) {
+ if (RNA_struct_is_a(ptr->type, &RNA_GeometryNode)) {
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
- if (STREQ(#Category, "SimulationNode")) { \
+ if (STREQ(#Category, "GeometryNode")) { \
tmp.value = ID; \
tmp.identifier = EnumName; \
tmp.name = UIName; \
@@ -1774,6 +1840,61 @@ static StructRNA *rna_Node_register(Main *bmain,
return nt->rna_ext.srna;
}
+static const EnumPropertyItem *itemf_function_check(
+ const EnumPropertyItem *original_item_array,
+ bool (*value_supported)(const EnumPropertyItem *item))
+{
+ EnumPropertyItem *item_array = NULL;
+ int items_len = 0;
+
+ for (const EnumPropertyItem *item = original_item_array; item->identifier != NULL; item++) {
+ if (value_supported(item)) {
+ RNA_enum_item_add(&item_array, &items_len, item);
+ }
+ }
+
+ RNA_enum_item_end(&item_array, &items_len);
+ return item_array;
+}
+
+static bool attribute_random_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3);
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_type_itemf(
+ bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(rna_enum_attribute_type_items, attribute_random_type_supported);
+}
+
+static bool attribute_random_domain_supported(const EnumPropertyItem *item)
+{
+ return item->value == ATTR_DOMAIN_POINT;
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_domain_itemf(
+ bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(rna_enum_attribute_domain_items, attribute_random_domain_supported);
+}
+
+static bool attribute_math_operation_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value,
+ NODE_MATH_ADD,
+ NODE_MATH_SUBTRACT,
+ NODE_MATH_MULTIPLY,
+ NODE_MATH_DIVIDE) &&
+ (item->identifier[0] != '\0');
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeMath_operation_itemf(
+ bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(rna_enum_node_math_items, attribute_math_operation_supported);
+}
+
static StructRNA *rna_ShaderNode_register(Main *bmain,
ReportList *reports,
void *data,
@@ -1840,16 +1961,16 @@ static StructRNA *rna_TextureNode_register(Main *bmain,
return nt->rna_ext.srna;
}
-static StructRNA *rna_SimulationNode_register(Main *bmain,
- ReportList *reports,
- void *data,
- const char *identifier,
- StructValidateFunc validate,
- StructCallbackFunc call,
- StructFreeFunc free)
+static StructRNA *rna_GeometryNode_register(Main *bmain,
+ ReportList *reports,
+ void *data,
+ const char *identifier,
+ StructValidateFunc validate,
+ StructCallbackFunc call,
+ StructFreeFunc free)
{
bNodeType *nt = rna_Node_register_base(
- bmain, reports, &RNA_SimulationNode, data, identifier, validate, call, free);
+ bmain, reports, &RNA_GeometryNode, data, identifier, validate, call, free);
if (!nt) {
return NULL;
}
@@ -2812,6 +2933,7 @@ static void rna_NodeSocketStandard_value_and_relation_update(struct bContext *C,
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
Main *bmain = CTX_data_main(C);
ntreeUpdateTree(bmain, ntree);
+ DEG_relations_tag_update(bmain);
}
/* ******** Node Types ******** */
@@ -3697,7 +3819,16 @@ static void rna_ShaderNode_socket_update(Main *bmain, Scene *scene, PointerRNA *
rna_Node_update(bmain, scene, ptr);
}
-static void rna_FunctionNode_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+static void rna_Node_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
+ bNode *node = (bNode *)ptr->data;
+
+ nodeUpdate(ntree, node);
+ rna_Node_update(bmain, scene, ptr);
+}
+
+static void rna_GeometryNode_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNode *node = (bNode *)ptr->data;
@@ -4149,7 +4280,7 @@ static void def_boolean_math(StructRNA *srna)
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_enum_node_boolean_math_items);
RNA_def_property_ui_text(prop, "Operation", "");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_FunctionNode_socket_update");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_float_compare(StructRNA *srna)
@@ -4160,7 +4291,7 @@ static void def_float_compare(StructRNA *srna)
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_enum_node_float_compare_items);
RNA_def_property_ui_text(prop, "Operation", "");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_FunctionNode_socket_update");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_fn_switch(StructRNA *srna)
@@ -4171,7 +4302,7 @@ static void def_fn_switch(StructRNA *srna)
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, node_socket_data_type_items);
RNA_def_property_ui_text(prop, "Data Type", "Data type for inputs and outputs");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_FunctionNode_socket_update");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_vector_math(StructRNA *srna)
@@ -8140,6 +8271,103 @@ static void def_tex_bricks(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+/* -- Geometry Nodes --------------------------------------------------------- */
+
+static void def_geo_boolean(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, rna_node_geometry_boolean_method_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_BOOLEAN_INTERSECT);
+ RNA_def_property_ui_text(prop, "Operation", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
+static void def_geo_triangulate(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "quad_method", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, rna_node_geometry_triangulate_quad_method_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE);
+ RNA_def_property_ui_text(prop, "Quad Method", "Method for splitting the quads into triangles");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "ngon_method", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom2");
+ RNA_def_property_enum_items(prop, rna_node_geometry_triangulate_ngon_method_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_TRIANGULATE_NGON_BEAUTY);
+ RNA_def_property_ui_text(
+ prop, "Polygon Method", "Method for splitting the polygons into triangles");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
+/**
+ * \note Passing the item functions as arguments here allows reusing the same
+ * original list of items from Attribute RNA.
+ */
+static void def_geo_attribute_create_common(StructRNA *srna,
+ const char *type_items_func,
+ const char *domain_items_func)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ if (type_items_func != NULL) {
+ RNA_def_property_enum_funcs(prop, NULL, NULL, type_items_func);
+ }
+ RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
+ RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
+
+ prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom2");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
+ if (domain_items_func != NULL) {
+ RNA_def_property_enum_funcs(prop, NULL, NULL, domain_items_func);
+ }
+ RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
+ RNA_def_property_ui_text(prop, "Domain", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
+static void def_geo_random_attribute(StructRNA *srna)
+{
+ def_geo_attribute_create_common(srna,
+ "rna_GeometryNodeAttributeRandom_type_itemf",
+ "rna_GeometryNodeAttributeRandom_domain_itemf");
+}
+
+static void def_geo_attribute_math(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, rna_enum_node_math_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeMath_operation_itemf");
+ RNA_def_property_enum_default(prop, NODE_MATH_ADD);
+ RNA_def_property_ui_text(prop, "Operation", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "custom2");
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_a_items);
+ RNA_def_property_ui_text(prop, "Input Type A", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "custom2");
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_b_items);
+ RNA_def_property_ui_text(prop, "Input Type B", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
@@ -8177,14 +8405,14 @@ static void rna_def_texture_node(BlenderRNA *brna)
RNA_def_struct_register_funcs(srna, "rna_TextureNode_register", "rna_Node_unregister", NULL);
}
-static void rna_def_simulation_node(BlenderRNA *brna)
+static void rna_def_geometry_node(BlenderRNA *brna)
{
StructRNA *srna;
- srna = RNA_def_struct(brna, "SimulationNode", "NodeInternal");
- RNA_def_struct_ui_text(srna, "Simulation Node", "");
+ srna = RNA_def_struct(brna, "GeometryNode", "NodeInternal");
+ RNA_def_struct_ui_text(srna, "Geometry Node", "");
RNA_def_struct_sdna(srna, "bNode");
- RNA_def_struct_register_funcs(srna, "rna_SimulationNode_register", "rna_Node_unregister", NULL);
+ RNA_def_struct_register_funcs(srna, "rna_GeometryNode_register", "rna_Node_unregister", NULL);
}
static void rna_def_function_node(BlenderRNA *brna)
@@ -9623,7 +9851,7 @@ static void rna_def_nodetree(BlenderRNA *brna)
{NTREE_SHADER, "SHADER", ICON_MATERIAL, "Shader", "Shader nodes"},
{NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"},
{NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"},
- {NTREE_SIMULATION, "SIMULATION", ICON_PHYSICS, "Simulation", "Simulation nodes"},
+ {NTREE_GEOMETRY, "GEOMETRY", ICON_MESH_DATA, "Geometry", "Geometry nodes"},
{0, NULL, 0, NULL, NULL},
};
@@ -9847,15 +10075,15 @@ static void rna_def_texture_nodetree(BlenderRNA *brna)
RNA_def_struct_ui_icon(srna, ICON_TEXTURE);
}
-static void rna_def_simulation_nodetree(BlenderRNA *brna)
+static void rna_def_geometry_nodetree(BlenderRNA *brna)
{
StructRNA *srna;
- srna = RNA_def_struct(brna, "SimulationNodeTree", "NodeTree");
+ srna = RNA_def_struct(brna, "GeometryNodeTree", "NodeTree");
RNA_def_struct_ui_text(
- srna, "Simulation Node Tree", "Node tree consisting of linked nodes used for simulations");
+ srna, "Geometry Node Tree", "Node tree consisting of linked nodes used for geometries");
RNA_def_struct_sdna(srna, "bNodeTree");
- RNA_def_struct_ui_icon(srna, ICON_PHYSICS); /* TODO: Use correct icon. */
+ RNA_def_struct_ui_icon(srna, ICON_MESH_DATA); /* TODO: Use correct icon. */
}
static StructRNA *define_specific_node(BlenderRNA *brna,
@@ -9944,7 +10172,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
rna_def_shader_node(brna);
rna_def_compositor_node(brna);
rna_def_texture_node(brna);
- rna_def_simulation_node(brna);
+ rna_def_geometry_node(brna);
rna_def_function_node(brna);
rna_def_nodetree(brna);
@@ -9954,7 +10182,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
rna_def_composite_nodetree(brna);
rna_def_shader_nodetree(brna);
rna_def_texture_nodetree(brna);
- rna_def_simulation_nodetree(brna);
+ rna_def_geometry_nodetree(brna);
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
{ \
@@ -9971,13 +10199,13 @@ void RNA_def_nodetree(BlenderRNA *brna)
*/
# include "../../nodes/NOD_static_types.h"
- /* Node group types need to be defined for shader, compositor, texture, simulation nodes
+ /* Node group types need to be defined for shader, compositor, texture, geometry nodes
* individually. Cannot use the static types header for this, since they share the same int id.
*/
define_specific_node(brna, "ShaderNodeGroup", "ShaderNode", "Group", "", def_group);
define_specific_node(brna, "CompositorNodeGroup", "CompositorNode", "Group", "", def_group);
define_specific_node(brna, "TextureNodeGroup", "TextureNode", "Group", "", def_group);
- define_specific_node(brna, "SimulationNodeGroup", "SimulationNode", "Group", "", def_group);
+ define_specific_node(brna, "GeometryNodeGroup", "GeometryNode", "Group", "", def_group);
def_custom_group(brna,
"ShaderNodeCustomGroup",
"ShaderNode",
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index e6e54abebd4..dc3e48b9c70 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -44,7 +44,6 @@
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_sequence_types.h"
-#include "DNA_simulation_types.h"
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "DNA_workspace_types.h"
@@ -2179,40 +2178,6 @@ static void rna_SpaceNodeEditor_node_tree_update(const bContext *C, PointerRNA *
ED_node_tree_update(C);
}
-# ifdef WITH_GEOMETRY_NODES
-static PointerRNA rna_SpaceNodeEditor_simulation_get(PointerRNA *ptr)
-{
- SpaceNode *snode = (SpaceNode *)ptr->data;
- ID *id = snode->id;
- if (id && GS(id->name) == ID_SIM) {
- return rna_pointer_inherit_refine(ptr, &RNA_Simulation, snode->id);
- }
- else {
- return PointerRNA_NULL;
- }
-}
-
-static void rna_SpaceNodeEditor_simulation_set(PointerRNA *ptr,
- const PointerRNA value,
- struct ReportList *UNUSED(reports))
-{
- SpaceNode *snode = (SpaceNode *)ptr->data;
- if (!STREQ(snode->tree_idname, "SimulationNodeTree")) {
- return;
- }
-
- Simulation *sim = (Simulation *)value.data;
- if (sim != NULL) {
- bNodeTree *ntree = sim->nodetree;
- ED_node_tree_start(snode, ntree, NULL, NULL);
- }
- else {
- ED_node_tree_start(snode, NULL, NULL, NULL);
- }
- snode->id = &sim->id;
-}
-# endif
-
static int rna_SpaceNodeEditor_tree_type_get(PointerRNA *ptr)
{
SpaceNode *snode = (SpaceNode *)ptr->data;
@@ -6350,19 +6315,6 @@ static void rna_def_space_node(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "ID From", "Data-block from which the edited data-block is linked");
-# ifdef WITH_GEOMETRY_NODES
- prop = RNA_def_property(srna, "simulation", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE);
- RNA_def_property_struct_type(prop, "Simulation");
- RNA_def_property_ui_text(prop, "Simulation", "Simulation that is being edited");
- RNA_def_property_pointer_funcs(prop,
- "rna_SpaceNodeEditor_simulation_get",
- "rna_SpaceNodeEditor_simulation_set",
- NULL,
- NULL);
- RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL);
-# endif
-
prop = RNA_def_property(srna, "path", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "treepath", NULL);
RNA_def_property_struct_type(prop, "NodeTreePath");
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index 549751ce267..34713392afb 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -29,8 +29,10 @@ set(INC
../bmesh
../depsgraph
../editors/include
+ ../functions
../makesdna
../makesrna
+ ../nodes
../render
../windowmanager
../../../intern/eigen
@@ -86,7 +88,7 @@ set(SRC
intern/MOD_shapekey.c
intern/MOD_shrinkwrap.c
intern/MOD_simpledeform.c
- intern/MOD_simulation.cc
+ intern/MOD_nodes.cc
intern/MOD_skin.c
intern/MOD_smooth.c
intern/MOD_softbody.c
@@ -114,6 +116,7 @@ set(SRC
intern/MOD_wireframe.c
MOD_modifiertypes.h
+ MOD_nodes.h
intern/MOD_meshcache_util.h
intern/MOD_solidify_util.h
intern/MOD_ui_common.h
diff --git a/source/blender/modifiers/MOD_modifiertypes.h b/source/blender/modifiers/MOD_modifiertypes.h
index 9c488780366..f36cb7ded9c 100644
--- a/source/blender/modifiers/MOD_modifiertypes.h
+++ b/source/blender/modifiers/MOD_modifiertypes.h
@@ -85,7 +85,7 @@ extern ModifierTypeInfo modifierType_CorrectiveSmooth;
extern ModifierTypeInfo modifierType_MeshSequenceCache;
extern ModifierTypeInfo modifierType_SurfaceDeform;
extern ModifierTypeInfo modifierType_WeightedNormal;
-extern ModifierTypeInfo modifierType_Simulation;
+extern ModifierTypeInfo modifierType_Nodes;
extern ModifierTypeInfo modifierType_MeshToVolume;
extern ModifierTypeInfo modifierType_VolumeDisplace;
extern ModifierTypeInfo modifierType_VolumeToMesh;
diff --git a/source/blender/nodes/NOD_simulation.h b/source/blender/modifiers/MOD_nodes.h
index 6b3d51b46a9..9c75e7e3416 100644
--- a/source/blender/nodes/NOD_simulation.h
+++ b/source/blender/modifiers/MOD_nodes.h
@@ -16,15 +16,17 @@
#pragma once
+struct Main;
+struct Object;
+struct NodesModifierData;
+
#ifdef __cplusplus
extern "C" {
#endif
-extern struct bNodeTreeType *ntreeType_Simulation;
-
-void register_node_tree_type_sim(void);
+void MOD_nodes_update_interface(struct Object *object, struct NodesModifierData *nmd);
-void register_node_type_sim_group(void);
+void MOD_nodes_init(struct Main *bmain, struct NodesModifierData *nmd);
#ifdef __cplusplus
}
diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c
index 882d080c08d..30c38623f68 100644
--- a/source/blender/modifiers/intern/MOD_edgesplit.c
+++ b/source/blender/modifiers/intern/MOD_edgesplit.c
@@ -53,7 +53,10 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
-static Mesh *doEdgeSplit(Mesh *mesh, EdgeSplitModifierData *emd)
+/* For edge split modifier node. */
+Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd);
+
+Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd)
{
Mesh *result;
BMesh *bm;
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
new file mode 100644
index 00000000000..b1849b3dbf2
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -0,0 +1,1088 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2005 by the Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include <cstring>
+#include <iostream>
+#include <string>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_float3.hh"
+#include "BLI_listbase.h"
+#include "BLI_set.hh"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_defaults.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_pointcloud_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_idprop.h"
+#include "BKE_lib_query.h"
+#include "BKE_mesh.h"
+#include "BKE_modifier.h"
+#include "BKE_pointcloud.h"
+#include "BKE_screen.h"
+#include "BKE_simulation.h"
+
+#include "BLO_read_write.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+#include "RNA_enum_types.h"
+
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
+
+#include "MOD_modifiertypes.h"
+#include "MOD_nodes.h"
+#include "MOD_ui_common.h"
+
+#include "NOD_derived_node_tree.hh"
+#include "NOD_geometry.h"
+#include "NOD_geometry_exec.hh"
+#include "NOD_node_tree_multi_function.hh"
+#include "NOD_type_callbacks.hh"
+
+using blender::float3;
+using blender::IndexRange;
+using blender::Map;
+using blender::Set;
+using blender::Span;
+using blender::StringRef;
+using blender::Vector;
+using blender::fn::GMutablePointer;
+using blender::fn::GValueMap;
+using blender::nodes::GeoNodeExecParams;
+using namespace blender::nodes::derived_node_tree_types;
+using namespace blender::fn::multi_function_types;
+
+static void initData(ModifierData *md)
+{
+ NodesModifierData *nmd = (NodesModifierData *)md;
+
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(nmd, modifier));
+
+ MEMCPY_STRUCT_AFTER(nmd, DNA_struct_default_get(NodesModifierData), modifier);
+}
+
+static void addIdsUsedBySocket(const ListBase *sockets, Set<ID *> &ids)
+{
+ LISTBASE_FOREACH (const bNodeSocket *, socket, sockets) {
+ if (socket->type == SOCK_OBJECT) {
+ Object *object = ((bNodeSocketValueObject *)socket->default_value)->value;
+ if (object != nullptr) {
+ ids.add(&object->id);
+ }
+ }
+ }
+}
+
+static void findUsedIds(const bNodeTree &tree, Set<ID *> &ids)
+{
+ Set<const bNodeTree *> handled_groups;
+
+ LISTBASE_FOREACH (const bNode *, node, &tree.nodes) {
+ addIdsUsedBySocket(&node->inputs, ids);
+ addIdsUsedBySocket(&node->outputs, ids);
+
+ if (node->type == NODE_GROUP) {
+ const bNodeTree *group = (bNodeTree *)node->id;
+ if (group != nullptr && handled_groups.add(group)) {
+ findUsedIds(*group, ids);
+ }
+ }
+ }
+}
+
+static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
+{
+ NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
+ if (nmd->node_group != nullptr) {
+ DEG_add_node_tree_relation(ctx->node, nmd->node_group, "Nodes Modifier");
+
+ Set<ID *> used_ids;
+ findUsedIds(*nmd->node_group, used_ids);
+ for (ID *id : used_ids) {
+ if (GS(id->name) == ID_OB) {
+ Object *object = reinterpret_cast<Object *>(id);
+ DEG_add_object_relation(ctx->node, object, DEG_OB_COMP_TRANSFORM, "Nodes Modifier");
+ if (id != &ctx->object->id) {
+ if (object->type != OB_EMPTY) {
+ DEG_add_object_relation(
+ ctx->node, (Object *)id, DEG_OB_COMP_GEOMETRY, "Nodes Modifier");
+ }
+ }
+ }
+ }
+ }
+
+ /* TODO: Add relations for IDs in settings. */
+}
+
+static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
+{
+ NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
+ walk(userData, ob, (ID **)&nmd->node_group, IDWALK_CB_USER);
+
+ struct ForeachSettingData {
+ IDWalkFunc walk;
+ void *userData;
+ Object *ob;
+ } settings = {walk, userData, ob};
+
+ IDP_foreach_property(
+ nmd->settings.properties,
+ IDP_TYPE_FILTER_ID,
+ [](IDProperty *id_prop, void *user_data) {
+ ForeachSettingData *settings = (ForeachSettingData *)user_data;
+ settings->walk(
+ settings->userData, settings->ob, (ID **)&id_prop->data.pointer, IDWALK_CB_USER);
+ },
+ &settings);
+}
+
+static bool isDisabled(const struct Scene *UNUSED(scene),
+ ModifierData *md,
+ bool UNUSED(useRenderParams))
+{
+ NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
+
+ if (nmd->node_group == nullptr) {
+ return true;
+ }
+
+ return false;
+}
+
+class GeometryNodesEvaluator {
+ private:
+ blender::LinearAllocator<> allocator_;
+ Map<const DInputSocket *, GMutablePointer> value_by_input_;
+ Vector<const DInputSocket *> group_outputs_;
+ blender::nodes::MultiFunctionByNode &mf_by_node_;
+ const blender::nodes::DataTypeConversions &conversions_;
+ const blender::bke::PersistentDataHandleMap &handle_map_;
+ const Object *self_object_;
+
+ public:
+ GeometryNodesEvaluator(const Map<const DOutputSocket *, GMutablePointer> &group_input_data,
+ Vector<const DInputSocket *> group_outputs,
+ blender::nodes::MultiFunctionByNode &mf_by_node,
+ const blender::bke::PersistentDataHandleMap &handle_map,
+ const Object *self_object)
+ : group_outputs_(std::move(group_outputs)),
+ mf_by_node_(mf_by_node),
+ conversions_(blender::nodes::get_implicit_type_conversions()),
+ handle_map_(handle_map),
+ self_object_(self_object)
+ {
+ for (auto item : group_input_data.items()) {
+ this->forward_to_inputs(*item.key, item.value);
+ }
+ }
+
+ Vector<GMutablePointer> execute()
+ {
+ Vector<GMutablePointer> results;
+ for (const DInputSocket *group_output : group_outputs_) {
+ GMutablePointer result = this->get_input_value(*group_output);
+ results.append(result);
+ }
+ for (GMutablePointer value : value_by_input_.values()) {
+ value.destruct();
+ }
+ return results;
+ }
+
+ private:
+ GMutablePointer get_input_value(const DInputSocket &socket_to_compute)
+ {
+ std::optional<GMutablePointer> value = value_by_input_.pop_try(&socket_to_compute);
+ if (value.has_value()) {
+ /* This input has been computed before, return it directly. */
+ return *value;
+ }
+
+ Span<const DOutputSocket *> from_sockets = socket_to_compute.linked_sockets();
+ Span<const DGroupInput *> from_group_inputs = socket_to_compute.linked_group_inputs();
+ const int total_inputs = from_sockets.size() + from_group_inputs.size();
+ BLI_assert(total_inputs <= 1);
+
+ if (total_inputs == 0) {
+ /* The input is not connected, use the value from the socket itself. */
+ return get_unlinked_input_value(socket_to_compute);
+ }
+ if (from_group_inputs.size() == 1) {
+ /* The input gets its value from the input of a group that is not further connected. */
+ return get_unlinked_input_value(socket_to_compute);
+ }
+
+ /* Compute the socket now. */
+ const DOutputSocket &from_socket = *from_sockets[0];
+ this->compute_output_and_forward(from_socket);
+ return value_by_input_.pop(&socket_to_compute);
+ }
+
+ void compute_output_and_forward(const DOutputSocket &socket_to_compute)
+ {
+ const DNode &node = socket_to_compute.node();
+ const bNode &bnode = *node.bnode();
+
+ if (!socket_to_compute.is_available()) {
+ /* If the output is not available, use a default value. */
+ const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute.typeinfo());
+ void *buffer = allocator_.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(type.default_value(), buffer);
+ this->forward_to_inputs(socket_to_compute, {type, buffer});
+ return;
+ }
+
+ /* Prepare inputs required to execute the node. */
+ GValueMap<StringRef> node_inputs_map{allocator_};
+ for (const DInputSocket *input_socket : node.inputs()) {
+ if (input_socket->is_available()) {
+ GMutablePointer value = this->get_input_value(*input_socket);
+ node_inputs_map.add_new_direct(input_socket->identifier(), value);
+ }
+ }
+
+ /* Execute the node. */
+ GValueMap<StringRef> node_outputs_map{allocator_};
+ GeoNodeExecParams params{bnode, node_inputs_map, node_outputs_map, handle_map_, self_object_};
+ this->execute_node(node, params);
+
+ /* Forward computed outputs to linked input sockets. */
+ for (const DOutputSocket *output_socket : node.outputs()) {
+ if (output_socket->is_available()) {
+ GMutablePointer value = node_outputs_map.extract(output_socket->identifier());
+ this->forward_to_inputs(*output_socket, value);
+ }
+ }
+ }
+
+ void execute_node(const DNode &node, GeoNodeExecParams params)
+ {
+ const bNode &bnode = params.node();
+ if (bnode.typeinfo->geometry_node_execute != nullptr) {
+ bnode.typeinfo->geometry_node_execute(params);
+ return;
+ }
+
+ /* Use the multi-function implementation of the node. */
+ const MultiFunction &fn = *mf_by_node_.lookup(&node);
+ MFContextBuilder fn_context;
+ MFParamsBuilder fn_params{fn, 1};
+ Vector<GMutablePointer> input_data;
+ for (const DInputSocket *dsocket : node.inputs()) {
+ if (dsocket->is_available()) {
+ GMutablePointer data = params.extract_input(dsocket->identifier());
+ fn_params.add_readonly_single_input(GSpan(*data.type(), data.get(), 1));
+ input_data.append(data);
+ }
+ }
+ Vector<GMutablePointer> output_data;
+ for (const DOutputSocket *dsocket : node.outputs()) {
+ if (dsocket->is_available()) {
+ const CPPType &type = *blender::nodes::socket_cpp_type_get(*dsocket->typeinfo());
+ void *buffer = allocator_.allocate(type.size(), type.alignment());
+ fn_params.add_uninitialized_single_output(GMutableSpan(type, buffer, 1));
+ output_data.append(GMutablePointer(type, buffer));
+ }
+ }
+ fn.call(IndexRange(1), fn_params, fn_context);
+ for (GMutablePointer value : input_data) {
+ value.destruct();
+ }
+ int output_index = 0;
+ for (const int i : node.outputs().index_range()) {
+ if (node.output(i).is_available()) {
+ GMutablePointer value = output_data[output_index];
+ params.set_output_by_move(node.output(i).identifier(), value);
+ value.destruct();
+ output_index++;
+ }
+ }
+ }
+
+ void forward_to_inputs(const DOutputSocket &from_socket, GMutablePointer value_to_forward)
+ {
+ Span<const DInputSocket *> to_sockets_all = from_socket.linked_sockets();
+
+ const CPPType &from_type = *value_to_forward.type();
+
+ Vector<const DInputSocket *> to_sockets_same_type;
+ for (const DInputSocket *to_socket : to_sockets_all) {
+ const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo());
+ if (from_type == to_type) {
+ to_sockets_same_type.append(to_socket);
+ }
+ else {
+ void *buffer = allocator_.allocate(to_type.size(), to_type.alignment());
+ if (conversions_.is_convertible(from_type, to_type)) {
+ conversions_.convert(from_type, to_type, value_to_forward.get(), buffer);
+ }
+ else {
+ to_type.copy_to_uninitialized(to_type.default_value(), buffer);
+ }
+ value_by_input_.add_new(to_socket, GMutablePointer{to_type, buffer});
+ }
+ }
+
+ if (to_sockets_same_type.size() == 0) {
+ /* This value is not further used, so destruct it. */
+ value_to_forward.destruct();
+ }
+ else if (to_sockets_same_type.size() == 1) {
+ /* This value is only used on one input socket, no need to copy it. */
+ const DInputSocket *to_socket = to_sockets_same_type[0];
+ value_by_input_.add_new(to_socket, value_to_forward);
+ }
+ else {
+ /* Multiple inputs use the value, make a copy for every input except for one. */
+ const DInputSocket *first_to_socket = to_sockets_same_type[0];
+ Span<const DInputSocket *> other_to_sockets = to_sockets_same_type.as_span().drop_front(1);
+ const CPPType &type = *value_to_forward.type();
+
+ value_by_input_.add_new(first_to_socket, value_to_forward);
+ for (const DInputSocket *to_socket : other_to_sockets) {
+ void *buffer = allocator_.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(value_to_forward.get(), buffer);
+ value_by_input_.add_new(to_socket, GMutablePointer{type, buffer});
+ }
+ }
+ }
+
+ GMutablePointer get_unlinked_input_value(const DInputSocket &socket)
+ {
+ bNodeSocket *bsocket;
+ if (socket.linked_group_inputs().size() == 0) {
+ bsocket = socket.bsocket();
+ }
+ else {
+ bsocket = socket.linked_group_inputs()[0]->bsocket();
+ }
+ const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket.typeinfo());
+ void *buffer = allocator_.allocate(type.size(), type.alignment());
+
+ if (bsocket->type == SOCK_OBJECT) {
+ Object *object = ((bNodeSocketValueObject *)bsocket->default_value)->value;
+ blender::bke::PersistentObjectHandle object_handle = handle_map_.lookup(object);
+ new (buffer) blender::bke::PersistentObjectHandle(object_handle);
+ }
+ else {
+ blender::nodes::socket_cpp_value_get(*bsocket, buffer);
+ }
+
+ return {type, buffer};
+ }
+};
+
+/**
+ * This code is responsible for creating the new property and also creating the group of
+ * properties in the prop_ui_container group for the UI info, the mapping for which is
+ * scattered about in RNA_access.c.
+ *
+ * TODO(Hans): Codify this with some sort of table or refactor IDProperty use in RNA_access.c.
+ */
+struct SocketPropertyType {
+ /* Create the actual propery used to store the data for the modifier. */
+ IDProperty *(*create_prop)(const bNodeSocket &socket, const char *name);
+ /* Reused to build the "soft_min" property too. */
+ IDProperty *(*create_min_ui_prop)(const bNodeSocket &socket, const char *name);
+ /* Reused to build the "soft_max" property too. */
+ IDProperty *(*create_max_ui_prop)(const bNodeSocket &socket, const char *name);
+ /* This uses the same values as #create_prop, but sometimes the type is different, so it can't
+ * be the same function. */
+ IDProperty *(*create_default_ui_prop)(const bNodeSocket &socket, const char *name);
+ PropertyType (*rna_subtype_get)(const bNodeSocket &socket);
+ bool (*is_correct_type)(const IDProperty &property);
+ void (*init_cpp_value)(const IDProperty &property, void *r_value);
+};
+
+static IDProperty *socket_add_property(IDProperty *settings_prop_group,
+ IDProperty *ui_container,
+ const SocketPropertyType &property_type,
+ const bNodeSocket &socket)
+{
+ const char *new_prop_name = socket.identifier;
+ /* Add the property actually storing the data to the modifier's group. */
+ IDProperty *prop = property_type.create_prop(socket, new_prop_name);
+ IDP_AddToGroup(settings_prop_group, prop);
+
+ /* Make the group in the ui container group to hold the property's UI settings. */
+ IDProperty *prop_ui_group;
+ {
+ IDPropertyTemplate idprop = {0};
+ prop_ui_group = IDP_New(IDP_GROUP, &idprop, new_prop_name);
+ IDP_AddToGroup(ui_container, prop_ui_group);
+ }
+
+ /* Create the properties for the socket's UI settings. */
+ if (property_type.create_min_ui_prop != nullptr) {
+ IDP_AddToGroup(prop_ui_group, property_type.create_min_ui_prop(socket, "min"));
+ IDP_AddToGroup(prop_ui_group, property_type.create_min_ui_prop(socket, "soft_min"));
+ }
+ if (property_type.create_max_ui_prop != nullptr) {
+ IDP_AddToGroup(prop_ui_group, property_type.create_max_ui_prop(socket, "max"));
+ IDP_AddToGroup(prop_ui_group, property_type.create_max_ui_prop(socket, "soft_max"));
+ }
+ if (property_type.create_default_ui_prop != nullptr) {
+ IDP_AddToGroup(prop_ui_group, property_type.create_default_ui_prop(socket, "default"));
+ }
+ if (property_type.rna_subtype_get != nullptr) {
+ const char *subtype_identifier = nullptr;
+ RNA_enum_identifier(rna_enum_property_subtype_items,
+ property_type.rna_subtype_get(socket),
+ &subtype_identifier);
+
+ if (subtype_identifier != nullptr) {
+ IDPropertyTemplate idprop = {0};
+ idprop.string.str = subtype_identifier;
+ idprop.string.len = BLI_strnlen(subtype_identifier, MAX_NAME) + 1;
+ IDP_AddToGroup(prop_ui_group, IDP_New(IDP_STRING, &idprop, "subtype"));
+ }
+ }
+
+ return prop;
+}
+
+static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bsocket)
+{
+ switch (bsocket.type) {
+ case SOCK_FLOAT: {
+ static const SocketPropertyType float_type = {
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.f = value->value;
+ return IDP_New(IDP_FLOAT, &idprop, name);
+ },
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.d = value->min;
+ return IDP_New(IDP_DOUBLE, &idprop, name);
+ },
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.d = value->max;
+ return IDP_New(IDP_DOUBLE, &idprop, name);
+ },
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.d = value->value;
+ return IDP_New(IDP_DOUBLE, &idprop, name);
+ },
+ [](const bNodeSocket &socket) {
+ return (PropertyType)((bNodeSocketValueFloat *)socket.default_value)->subtype;
+ },
+ [](const IDProperty &property) { return property.type == IDP_FLOAT; },
+ [](const IDProperty &property, void *r_value) {
+ *(float *)r_value = IDP_Float(&property);
+ },
+ };
+ return &float_type;
+ }
+ case SOCK_INT: {
+ static const SocketPropertyType int_type = {
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.i = value->value;
+ return IDP_New(IDP_INT, &idprop, name);
+ },
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.i = value->min;
+ return IDP_New(IDP_INT, &idprop, name);
+ },
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.i = value->max;
+ return IDP_New(IDP_INT, &idprop, name);
+ },
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.i = value->value;
+ return IDP_New(IDP_INT, &idprop, name);
+ },
+ [](const bNodeSocket &socket) {
+ return (PropertyType)((bNodeSocketValueInt *)socket.default_value)->subtype;
+ },
+ [](const IDProperty &property) { return property.type == IDP_INT; },
+ [](const IDProperty &property, void *r_value) { *(int *)r_value = IDP_Int(&property); },
+ };
+ return &int_type;
+ }
+ case SOCK_VECTOR: {
+ static const SocketPropertyType vector_type = {
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.array.len = 3;
+ idprop.array.type = IDP_FLOAT;
+ IDProperty *property = IDP_New(IDP_ARRAY, &idprop, name);
+ copy_v3_v3((float *)IDP_Array(property), value->value);
+ return property;
+ },
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.d = value->min;
+ return IDP_New(IDP_DOUBLE, &idprop, name);
+ },
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.d = value->max;
+ return IDP_New(IDP_DOUBLE, &idprop, name);
+ },
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.array.len = 3;
+ idprop.array.type = IDP_FLOAT;
+ IDProperty *property = IDP_New(IDP_ARRAY, &idprop, name);
+ copy_v3_v3((float *)IDP_Array(property), value->value);
+ return property;
+ },
+ [](const bNodeSocket &socket) {
+ return (PropertyType)((bNodeSocketValueVector *)socket.default_value)->subtype;
+ },
+ [](const IDProperty &property) {
+ return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT &&
+ property.len == 3;
+ },
+ [](const IDProperty &property, void *r_value) {
+ copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
+ },
+ };
+ return &vector_type;
+ }
+ case SOCK_BOOLEAN: {
+ static const SocketPropertyType boolean_type = {
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.i = value->value != 0;
+ return IDP_New(IDP_INT, &idprop, name);
+ },
+ [](const bNodeSocket &UNUSED(socket), const char *name) {
+ IDPropertyTemplate idprop = {0};
+ idprop.i = 0;
+ return IDP_New(IDP_INT, &idprop, name);
+ },
+ [](const bNodeSocket &UNUSED(socket), const char *name) {
+ IDPropertyTemplate idprop = {0};
+ idprop.i = 1;
+ return IDP_New(IDP_INT, &idprop, name);
+ },
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.i = value->value != 0;
+ return IDP_New(IDP_INT, &idprop, name);
+ },
+ nullptr,
+ [](const IDProperty &property) { return property.type == IDP_INT; },
+ [](const IDProperty &property, void *r_value) {
+ *(bool *)r_value = IDP_Int(&property) != 0;
+ },
+ };
+ return &boolean_type;
+ }
+ case SOCK_STRING: {
+ static const SocketPropertyType string_type = {
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value;
+ return IDP_NewString(
+ value->value, name, BLI_strnlen(value->value, sizeof(value->value)) + 1);
+ },
+ nullptr,
+ nullptr,
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value;
+ return IDP_NewString(
+ value->value, name, BLI_strnlen(value->value, sizeof(value->value)) + 1);
+ },
+ nullptr,
+ [](const IDProperty &property) { return property.type == IDP_STRING; },
+ [](const IDProperty &property, void *r_value) {
+ new (r_value) std::string(IDP_String(&property));
+ },
+ };
+ return &string_type;
+ }
+ default: {
+ return nullptr;
+ }
+ }
+}
+
+/**
+ * Rebuild the list of properties based on the sockets exposed as the modifier's node group
+ * inputs. If any properties correspond to the old properties by name and type, carry over
+ * the values.
+ */
+void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
+{
+ if (nmd->node_group == nullptr) {
+ return;
+ }
+
+ IDProperty *old_properties = nmd->settings.properties;
+
+ {
+ IDPropertyTemplate idprop = {0};
+ nmd->settings.properties = IDP_New(IDP_GROUP, &idprop, "Nodes Modifier Settings");
+ }
+
+ IDProperty *ui_container_group;
+ {
+ IDPropertyTemplate idprop = {0};
+ ui_container_group = IDP_New(IDP_GROUP, &idprop, "_RNA_UI");
+ IDP_AddToGroup(nmd->settings.properties, ui_container_group);
+ }
+
+ LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) {
+ const SocketPropertyType *property_type = get_socket_property_type(*socket);
+ if (property_type == nullptr) {
+ continue;
+ }
+
+ IDProperty *new_prop = socket_add_property(
+ nmd->settings.properties, ui_container_group, *property_type, *socket);
+
+ if (old_properties != nullptr) {
+ IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, socket->identifier);
+ if (old_prop != nullptr && property_type->is_correct_type(*old_prop)) {
+ IDP_CopyPropertyContent(new_prop, old_prop);
+ }
+ }
+ }
+
+ if (old_properties != nullptr) {
+ IDP_FreeProperty(old_properties);
+ }
+
+ DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
+}
+
+void MOD_nodes_init(Main *bmain, NodesModifierData *nmd)
+{
+ bNodeTree *ntree = ntreeAddTree(bmain, "Geometry Nodes", ntreeType_Geometry->idname);
+ nmd->node_group = ntree;
+
+ ntreeAddSocketInterface(ntree, SOCK_IN, "NodeSocketGeometry", "Geometry");
+ ntreeAddSocketInterface(ntree, SOCK_OUT, "NodeSocketGeometry", "Geometry");
+
+ bNode *group_input_node = nodeAddStaticNode(nullptr, ntree, NODE_GROUP_INPUT);
+ bNode *group_output_node = nodeAddStaticNode(nullptr, ntree, NODE_GROUP_OUTPUT);
+
+ group_input_node->locx = -200 - group_input_node->width;
+ group_output_node->locx = 200;
+ group_output_node->flag |= NODE_DO_OUTPUT;
+
+ nodeAddLink(ntree,
+ group_output_node,
+ (bNodeSocket *)group_output_node->inputs.first,
+ group_input_node,
+ (bNodeSocket *)group_input_node->outputs.first);
+
+ ntreeUpdateTree(bmain, ntree);
+}
+
+static void initialize_group_input(NodesModifierData &nmd,
+ const bNodeSocket &socket,
+ const CPPType &cpp_type,
+ void *r_value)
+{
+ const SocketPropertyType *property_type = get_socket_property_type(socket);
+ if (property_type == nullptr) {
+ cpp_type.copy_to_uninitialized(cpp_type.default_value(), r_value);
+ return;
+ }
+ if (nmd.settings.properties == nullptr) {
+ blender::nodes::socket_cpp_value_get(socket, r_value);
+ return;
+ }
+ const IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties,
+ socket.identifier);
+ if (property == nullptr) {
+ blender::nodes::socket_cpp_value_get(socket, r_value);
+ return;
+ }
+ if (!property_type->is_correct_type(*property)) {
+ blender::nodes::socket_cpp_value_get(socket, r_value);
+ }
+ property_type->init_cpp_value(*property, r_value);
+}
+
+static void fill_data_handle_map(const DerivedNodeTree &tree,
+ blender::bke::PersistentDataHandleMap &handle_map)
+{
+ Set<ID *> used_ids;
+ findUsedIds(*tree.btree(), used_ids);
+
+ int current_handle = 0;
+ for (ID *id : used_ids) {
+ handle_map.add(current_handle, *id);
+ current_handle++;
+ }
+}
+
+/**
+ * Evaluate a node group to compute the output geometry.
+ * Currently, this uses a fairly basic and inefficient algorithm that might compute things more
+ * often than necessary. It's going to be replaced soon.
+ */
+static GeometrySet compute_geometry(const DerivedNodeTree &tree,
+ Span<const DOutputSocket *> group_input_sockets,
+ const DInputSocket &socket_to_compute,
+ GeometrySet input_geometry_set,
+ NodesModifierData *nmd,
+ const ModifierEvalContext *ctx)
+{
+ blender::ResourceCollector resources;
+ blender::LinearAllocator<> &allocator = resources.linear_allocator();
+ blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, resources);
+
+ Map<const DOutputSocket *, GMutablePointer> group_inputs;
+
+ if (group_input_sockets.size() > 0) {
+ Span<const DOutputSocket *> remaining_input_sockets = group_input_sockets;
+
+ /* If the group expects a geometry as first input, use the geometry that has been passed to
+ * modifier. */
+ const DOutputSocket *first_input_socket = group_input_sockets[0];
+ if (first_input_socket->bsocket()->type == SOCK_GEOMETRY) {
+ GeometrySet *geometry_set_in = allocator.construct<GeometrySet>(
+ std::move(input_geometry_set));
+ group_inputs.add_new(first_input_socket, geometry_set_in);
+ remaining_input_sockets = remaining_input_sockets.drop_front(1);
+ }
+
+ /* Initialize remaining group inputs. */
+ for (const DOutputSocket *socket : remaining_input_sockets) {
+ const CPPType &cpp_type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
+ void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment());
+ initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in);
+ group_inputs.add_new(socket, {cpp_type, value_in});
+ }
+ }
+
+ Vector<const DInputSocket *> group_outputs;
+ group_outputs.append(&socket_to_compute);
+
+ blender::bke::PersistentDataHandleMap handle_map;
+ fill_data_handle_map(tree, handle_map);
+
+ GeometryNodesEvaluator evaluator{
+ group_inputs, group_outputs, mf_by_node, handle_map, ctx->object};
+ Vector<GMutablePointer> results = evaluator.execute();
+ BLI_assert(results.size() == 1);
+ GMutablePointer result = results[0];
+
+ GeometrySet output_geometry = std::move(*(GeometrySet *)result.get());
+ return output_geometry;
+}
+
+/**
+ * \note This could be done in #initialize_group_input, though that would require adding the
+ * the object as a parameter, so it's likely better to this check as a separate step.
+ */
+static void check_property_socket_sync(const Object *ob, ModifierData *md)
+{
+ NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
+
+ int i = 0;
+ LISTBASE_FOREACH_INDEX (const bNodeSocket *, socket, &nmd->node_group->inputs, i) {
+ /* The first socket is the special geometry socket for the modifier object. */
+ if (i == 0 && socket->type == SOCK_GEOMETRY) {
+ continue;
+ }
+
+ IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket->identifier);
+ if (property == nullptr) {
+ if (socket->type == SOCK_STRING) {
+ BKE_modifier_set_error(ob, md, "String socket can not be exposed in the modifier");
+ }
+ else if (socket->type == SOCK_OBJECT) {
+ BKE_modifier_set_error(ob, md, "Object socket can not be exposed in the modifier");
+ }
+ else if (socket->type == SOCK_GEOMETRY) {
+ BKE_modifier_set_error(ob, md, "Node group can only have one geometry input");
+ }
+ else {
+ BKE_modifier_set_error(ob, md, "Missing property for input socket \"%s\"", socket->name);
+ }
+ continue;
+ }
+
+ const SocketPropertyType *property_type = get_socket_property_type(*socket);
+ if (!property_type->is_correct_type(*property)) {
+ BKE_modifier_set_error(
+ ob, md, "Property type does not match input socket \"(%s)\"", socket->name);
+ continue;
+ }
+ }
+
+ bool has_geometry_output = false;
+ LISTBASE_FOREACH (const bNodeSocket *, socket, &nmd->node_group->outputs) {
+ if (socket->type == SOCK_GEOMETRY) {
+ has_geometry_output = true;
+ }
+ }
+
+ if (!has_geometry_output) {
+ BKE_modifier_set_error(ob, md, "Node group must have a geometry output");
+ }
+}
+
+static void modifyGeometry(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ GeometrySet &geometry_set)
+{
+ NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
+ if (nmd->node_group == nullptr) {
+ return;
+ }
+
+ check_property_socket_sync(ctx->object, md);
+
+ blender::nodes::NodeTreeRefMap tree_refs;
+ DerivedNodeTree tree{nmd->node_group, tree_refs};
+
+ if (tree.has_link_cycles()) {
+ BKE_modifier_set_error(ctx->object, md, "Node group has cycles");
+ return;
+ }
+
+ Span<const DNode *> input_nodes = tree.nodes_by_type("NodeGroupInput");
+ Span<const DNode *> output_nodes = tree.nodes_by_type("NodeGroupOutput");
+
+ if (input_nodes.size() > 1) {
+ return;
+ }
+ if (output_nodes.size() != 1) {
+ return;
+ }
+
+ Span<const DOutputSocket *> group_inputs = (input_nodes.size() == 1) ?
+ input_nodes[0]->outputs().drop_back(1) :
+ Span<const DOutputSocket *>{};
+ Span<const DInputSocket *> group_outputs = output_nodes[0]->inputs().drop_back(1);
+
+ if (group_outputs.size() == 0) {
+ return;
+ }
+
+ const DInputSocket *group_output = group_outputs[0];
+ if (group_output->idname() != "NodeSocketGeometry") {
+ return;
+ }
+
+ geometry_set = compute_geometry(
+ tree, group_inputs, *group_outputs[0], std::move(geometry_set), nmd, ctx);
+}
+
+static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
+{
+ GeometrySet geometry_set = GeometrySet::create_with_mesh(mesh, GeometryOwnershipType::Editable);
+ geometry_set.get_component_for_write<MeshComponent>().copy_vertex_group_names_from_object(
+ *ctx->object);
+ modifyGeometry(md, ctx, geometry_set);
+ Mesh *new_mesh = geometry_set.get_component_for_write<MeshComponent>().release();
+ if (new_mesh == nullptr) {
+ return BKE_mesh_new_nomain(0, 0, 0, 0, 0);
+ }
+ return new_mesh;
+}
+
+static void modifyPointCloud(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ GeometrySet *geometry_set)
+{
+ modifyGeometry(md, ctx, *geometry_set);
+}
+
+/* Drawing the properties manually with #uiItemR instead of #uiDefAutoButsRNA allows using
+ * the node socket identifier for the property names, since they are unique, but also having
+ * the correct label displayed in the UI. */
+static void draw_property_for_socket(uiLayout *layout,
+ PointerRNA *settings_ptr,
+ const IDProperty *modifier_props,
+ const bNodeSocket &socket)
+{
+ const SocketPropertyType *property_type = get_socket_property_type(socket);
+ if (property_type == nullptr) {
+ return;
+ }
+
+ /* The property should be created in #MOD_nodes_update_interface with the correct type. */
+ IDProperty *property = IDP_GetPropertyFromGroup(modifier_props, socket.identifier);
+
+ /* IDProperties can be removed with python, so there could be a situation where
+ * there isn't a property for a socket or it doesn't have the correct type. */
+ if (property != nullptr && property_type->is_correct_type(*property)) {
+ char rna_path[128];
+ BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket.identifier);
+ uiItemR(layout, settings_ptr, rna_path, 0, socket.name, ICON_NONE);
+ }
+}
+
+static void panel_draw(const bContext *C, Panel *panel)
+{
+#ifdef WITH_GEOMETRY_NODES
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
+ NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data);
+
+ uiLayoutSetPropSep(layout, true);
+ /* This should be removed, but animation currently doesn't work with the IDProperties. */
+ uiLayoutSetPropDecorate(layout, false);
+
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "node_group",
+ "node.new_geometry_node_group_assign",
+ nullptr,
+ nullptr,
+ 0,
+ false,
+ nullptr);
+
+ if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) {
+ PointerRNA settings_ptr;
+ RNA_pointer_create(ptr->owner_id, &RNA_NodesModifierSettings, &nmd->settings, &settings_ptr);
+
+ LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) {
+ draw_property_for_socket(layout, &settings_ptr, nmd->settings.properties, *socket);
+ }
+ }
+
+ modifier_panel_end(layout, ptr);
+#endif
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ modifier_panel_register(region_type, eModifierType_Nodes, panel_draw);
+}
+
+static void blendWrite(BlendWriter *writer, const ModifierData *md)
+{
+ const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
+ if (nmd->settings.properties != nullptr) {
+ /* Note that the property settings are based on the socket type info
+ * and don't necessarily need to be written, but we can't just free them. */
+ IDP_BlendWrite(writer, nmd->settings.properties);
+ }
+}
+
+static void blendRead(BlendDataReader *reader, ModifierData *md)
+{
+ NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
+ BLO_read_data_address(reader, &nmd->settings.properties);
+ IDP_BlendDataRead(reader, &nmd->settings.properties);
+}
+
+static void copyData(const ModifierData *md, ModifierData *target, const int flag)
+{
+ const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
+ NodesModifierData *tnmd = reinterpret_cast<NodesModifierData *>(target);
+
+ BKE_modifier_copydata_generic(md, target, flag);
+
+ if (nmd->settings.properties != nullptr) {
+ tnmd->settings.properties = IDP_CopyProperty_ex(nmd->settings.properties, flag);
+ }
+}
+
+static void freeData(ModifierData *md)
+{
+ NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
+ if (nmd->settings.properties != nullptr) {
+ IDP_FreeProperty_ex(nmd->settings.properties, false);
+ nmd->settings.properties = nullptr;
+ }
+}
+
+ModifierTypeInfo modifierType_Nodes = {
+ /* name */ "GeometryNodes",
+ /* structName */ "NodesModifierData",
+ /* structSize */ sizeof(NodesModifierData),
+#ifdef WITH_GEOMETRY_NODES
+ /* srna */ &RNA_NodesModifier,
+#else
+ /* srna */ &RNA_Modifier,
+#endif
+ /* type */ eModifierTypeType_Constructive,
+ /* flags */
+ static_cast<ModifierTypeFlag>(eModifierTypeFlag_AcceptsMesh |
+ eModifierTypeFlag_SupportsEditmode |
+ eModifierTypeFlag_EnableInEditmode),
+ /* icon */ ICON_MESH_DATA, /* TODO: Use correct icon. */
+
+ /* copyData */ copyData,
+
+ /* deformVerts */ nullptr,
+ /* deformMatrices */ nullptr,
+ /* deformVertsEM */ nullptr,
+ /* deformMatricesEM */ nullptr,
+ /* modifyMesh */ modifyMesh,
+ /* modifyHair */ nullptr,
+ /* modifyPointCloud */ modifyPointCloud,
+ /* modifyVolume */ nullptr,
+
+ /* initData */ initData,
+ /* requiredDataMask */ nullptr,
+ /* freeData */ freeData,
+ /* isDisabled */ isDisabled,
+ /* updateDepsgraph */ updateDepsgraph,
+ /* dependsOnTime */ nullptr,
+ /* dependsOnNormals */ nullptr,
+ /* foreachIDLink */ foreachIDLink,
+ /* foreachTexLink */ nullptr,
+ /* freeRuntimeData */ nullptr,
+ /* panelRegister */ panelRegister,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
+};
diff --git a/source/blender/modifiers/intern/MOD_simulation.cc b/source/blender/modifiers/intern/MOD_simulation.cc
deleted file mode 100644
index 0766c59cda6..00000000000
--- a/source/blender/modifiers/intern/MOD_simulation.cc
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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.
- *
- * The Original Code is Copyright (C) 2005 by the Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup modifiers
- */
-
-#include <cstring>
-#include <iostream>
-#include <string>
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_float3.hh"
-#include "BLI_listbase.h"
-#include "BLI_string.h"
-#include "BLI_utildefines.h"
-
-#include "DNA_defaults.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_modifier_types.h"
-#include "DNA_object_types.h"
-#include "DNA_pointcloud_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_screen_types.h"
-#include "DNA_simulation_types.h"
-
-#include "BKE_customdata.h"
-#include "BKE_lib_query.h"
-#include "BKE_mesh.h"
-#include "BKE_modifier.h"
-#include "BKE_pointcloud.h"
-#include "BKE_screen.h"
-#include "BKE_simulation.h"
-
-#include "BLO_read_write.h"
-
-#include "UI_interface.h"
-#include "UI_resources.h"
-
-#include "RNA_access.h"
-
-#include "DEG_depsgraph_build.h"
-#include "DEG_depsgraph_query.h"
-
-#include "MOD_modifiertypes.h"
-#include "MOD_ui_common.h"
-
-using blender::float3;
-
-static void initData(ModifierData *md)
-{
- SimulationModifierData *smd = (SimulationModifierData *)md;
-
- BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(smd, modifier));
-
- MEMCPY_STRUCT_AFTER(smd, DNA_struct_default_get(SimulationModifierData), modifier);
-}
-
-static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *UNUSED(ctx))
-{
- SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
- UNUSED_VARS(smd);
-}
-
-static void foreachIDLink(ModifierData *md,
- Object *UNUSED(ob),
- IDWalkFunc UNUSED(walk),
- void *UNUSED(userData))
-{
- SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
- UNUSED_VARS(smd);
-}
-
-static bool isDisabled(const struct Scene *UNUSED(scene),
- ModifierData *md,
- bool UNUSED(useRenderParams))
-{
- SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
- UNUSED_VARS(smd);
- return false;
-}
-
-static PointCloud *modifyPointCloud(ModifierData *md,
- const ModifierEvalContext *UNUSED(ctx),
- PointCloud *pointcloud)
-{
- SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
- UNUSED_VARS(smd);
- return pointcloud;
-}
-
-static void panel_draw(const bContext *UNUSED(C), Panel *panel)
-{
- uiLayout *layout = panel->layout;
-
- PointerRNA ob_ptr;
- PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
-
- uiLayoutSetPropSep(layout, true);
- uiLayoutSetPropDecorate(layout, false);
-
- uiItemL(layout, "This modifier does nothing currently", ICON_INFO);
-
- modifier_panel_end(layout, ptr);
-}
-
-static void panelRegister(ARegionType *region_type)
-{
- modifier_panel_register(region_type, eModifierType_Simulation, panel_draw);
-}
-
-static void blendWrite(BlendWriter *writer, const ModifierData *md)
-{
- const SimulationModifierData *smd = reinterpret_cast<const SimulationModifierData *>(md);
- UNUSED_VARS(smd, writer);
-}
-
-static void blendRead(BlendDataReader *reader, ModifierData *md)
-{
- SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
- UNUSED_VARS(smd, reader);
-}
-
-static void copyData(const ModifierData *md, ModifierData *target, const int flag)
-{
- const SimulationModifierData *smd = reinterpret_cast<const SimulationModifierData *>(md);
- SimulationModifierData *tsmd = reinterpret_cast<SimulationModifierData *>(target);
- UNUSED_VARS(smd, tsmd);
-
- BKE_modifier_copydata_generic(md, target, flag);
-}
-
-static void freeData(ModifierData *md)
-{
- SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
- UNUSED_VARS(smd);
-}
-
-ModifierTypeInfo modifierType_Simulation = {
- /* name */ "Simulation",
- /* structName */ "SimulationModifierData",
- /* structSize */ sizeof(SimulationModifierData),
-#ifdef WITH_GEOMETRY_NODES
- /* srna */ &RNA_SimulationModifier,
-#else
- /* srna */ &RNA_Modifier,
-#endif
- /* type */ eModifierTypeType_None,
- /* flags */ (ModifierTypeFlag)0,
- /* icon */ ICON_PHYSICS, /* TODO: Use correct icon. */
-
- /* copyData */ copyData,
-
- /* deformVerts */ nullptr,
- /* deformMatrices */ nullptr,
- /* deformVertsEM */ nullptr,
- /* deformMatricesEM */ nullptr,
- /* modifyMesh */ nullptr,
- /* modifyHair */ nullptr,
- /* modifyPointCloud */ modifyPointCloud,
- /* modifyVolume */ nullptr,
-
- /* initData */ initData,
- /* requiredDataMask */ nullptr,
- /* freeData */ freeData,
- /* isDisabled */ isDisabled,
- /* updateDepsgraph */ updateDepsgraph,
- /* dependsOnTime */ nullptr,
- /* dependsOnNormals */ nullptr,
- /* foreachIDLink */ foreachIDLink,
- /* foreachTexLink */ nullptr,
- /* freeRuntimeData */ nullptr,
- /* panelRegister */ panelRegister,
- /* blendWrite */ blendWrite,
- /* blendRead */ blendRead,
-};
diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c
index 5a07c4e5e64..1930a38b825 100644
--- a/source/blender/modifiers/intern/MOD_triangulate.c
+++ b/source/blender/modifiers/intern/MOD_triangulate.c
@@ -48,11 +48,17 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
-static Mesh *triangulate_mesh(Mesh *mesh,
- const int quad_method,
- const int ngon_method,
- const int min_vertices,
- const int flag)
+Mesh *triangulate_mesh(Mesh *mesh,
+ const int quad_method,
+ const int ngon_method,
+ const int min_vertices,
+ const int flag);
+
+Mesh *triangulate_mesh(Mesh *mesh,
+ const int quad_method,
+ const int ngon_method,
+ const int min_vertices,
+ const int flag)
{
Mesh *result;
BMesh *bm;
diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c
index e0802dc5fb4..55409cba114 100644
--- a/source/blender/modifiers/intern/MOD_util.c
+++ b/source/blender/modifiers/intern/MOD_util.c
@@ -342,9 +342,9 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(MeshSequenceCache);
INIT_TYPE(SurfaceDeform);
INIT_TYPE(WeightedNormal);
- INIT_TYPE(Simulation);
INIT_TYPE(MeshToVolume);
INIT_TYPE(VolumeDisplace);
INIT_TYPE(VolumeToMesh);
+ INIT_TYPE(Nodes);
#undef INIT_TYPE
}
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/geometry/node_geometry_exec.cc b/source/blender/nodes/geometry/node_geometry_exec.cc
new file mode 100644
index 00000000000..64a04d79076
--- /dev/null
+++ b/source/blender/nodes/geometry/node_geometry_exec.cc
@@ -0,0 +1,23 @@
+/*
+ * 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"
+
+MAKE_CPP_TYPE(GeometrySet, GeometrySet);
+
+namespace blender::nodes {
+
+}
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)