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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2020-12-02 15:25:25 +0300
committerJacques Lucke <jacques@blender.org>2020-12-02 17:38:47 +0300
commit6be56c13e96048cbc494ba5473a8deaf2cf5a6f8 (patch)
tree83435d439b3d106eb1405b2b815bf1d5aa1fdd43
parentddbe3274eff68523547bc8eb70dd95c3d411b89b (diff)
Geometry Nodes: initial scattering and geometry nodes
This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
-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)