Geometry Nodes: initial scattering and geometry nodes
authorJacques Lucke <jacques@blender.org>
Wed, 2 Dec 2020 12:25:25 +0000 (13:25 +0100)
committerJacques Lucke <jacques@blender.org>
Wed, 2 Dec 2020 14:38:47 +0000 (15:38 +0100)
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

103 files changed:
release/scripts/startup/bl_operators/__init__.py
release/scripts/startup/bl_operators/geometry_nodes.py [new file with mode: 0644]
release/scripts/startup/bl_operators/simulation.py [deleted file]
release/scripts/startup/bl_ui/space_node.py
release/scripts/startup/nodeitems_builtins.py
source/blender/blenkernel/BKE_attribute.h
source/blender/blenkernel/BKE_attribute_access.hh [new file with mode: 0644]
source/blender/blenkernel/BKE_geometry_set.h [new file with mode: 0644]
source/blender/blenkernel/BKE_geometry_set.hh [new file with mode: 0644]
source/blender/blenkernel/BKE_modifier.h
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/BKE_pointcloud.h
source/blender/blenkernel/CMakeLists.txt
source/blender/blenkernel/intern/attribute.c
source/blender/blenkernel/intern/attribute_access.cc [new file with mode: 0644]
source/blender/blenkernel/intern/fluid.c
source/blender/blenkernel/intern/geometry_set.cc [new file with mode: 0644]
source/blender/blenkernel/intern/hair.c
source/blender/blenkernel/intern/lib_remap.c
source/blender/blenkernel/intern/node.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/object_dupli.c
source/blender/blenkernel/intern/pointcache.c
source/blender/blenkernel/intern/pointcloud.cc
source/blender/blenkernel/intern/simulation.cc
source/blender/blenlib/BLI_rand.h
source/blender/blenlib/BLI_rand.hh
source/blender/blenlib/BLI_user_counter.hh [new file with mode: 0644]
source/blender/blenlib/CMakeLists.txt
source/blender/blenlib/intern/rand.cc
source/blender/blenloader/intern/versioning_290.c
source/blender/depsgraph/CMakeLists.txt
source/blender/depsgraph/DEG_depsgraph_build.h
source/blender/depsgraph/intern/depsgraph_build.cc
source/blender/editors/include/ED_node.h
source/blender/editors/object/object_modifier.c
source/blender/editors/space_node/CMakeLists.txt
source/blender/editors/space_node/drawnode.c
source/blender/editors/space_node/node_draw.c
source/blender/editors/space_node/node_edit.c
source/blender/editors/space_node/node_group.c
source/blender/editors/space_node/node_relationships.c
source/blender/editors/space_node/space_node.c
source/blender/functions/CMakeLists.txt
source/blender/functions/FN_generic_value_map.hh [new file with mode: 0644]
source/blender/functions/FN_multi_function.hh
source/blender/makesdna/DNA_modifier_defaults.h
source/blender/makesdna/DNA_modifier_types.h
source/blender/makesdna/DNA_node_types.h
source/blender/makesdna/DNA_object_types.h
source/blender/makesdna/intern/dna_defaults.c
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/CMakeLists.txt
source/blender/makesrna/intern/rna_access.c
source/blender/makesrna/intern/rna_attribute.c
source/blender/makesrna/intern/rna_modifier.c
source/blender/makesrna/intern/rna_nodetree.c
source/blender/makesrna/intern/rna_space.c
source/blender/modifiers/CMakeLists.txt
source/blender/modifiers/MOD_modifiertypes.h
source/blender/modifiers/MOD_nodes.h [moved from source/blender/nodes/NOD_simulation.h with 79% similarity]
source/blender/modifiers/intern/MOD_edgesplit.c
source/blender/modifiers/intern/MOD_nodes.cc [new file with mode: 0644]
source/blender/modifiers/intern/MOD_simulation.cc [deleted file]
source/blender/modifiers/intern/MOD_triangulate.c
source/blender/modifiers/intern/MOD_util.c
source/blender/nodes/CMakeLists.txt
source/blender/nodes/NOD_derived_node_tree.hh
source/blender/nodes/NOD_geometry.h [new file with mode: 0644]
source/blender/nodes/NOD_geometry_exec.hh [new file with mode: 0644]
source/blender/nodes/NOD_math_functions.hh [new file with mode: 0644]
source/blender/nodes/NOD_node_tree_multi_function.hh
source/blender/nodes/NOD_node_tree_ref.hh
source/blender/nodes/NOD_static_types.h
source/blender/nodes/NOD_type_callbacks.hh [new file with mode: 0644]
source/blender/nodes/function/node_function_util.cc
source/blender/nodes/function/nodes/node_fn_float_compare.cc
source/blender/nodes/function/nodes/node_fn_random_float.cc
source/blender/nodes/geometry/node_geometry_exec.cc [new file with mode: 0644]
source/blender/nodes/geometry/node_geometry_tree.cc [moved from source/blender/nodes/simulation/node_simulation_tree.cc with 66% similarity]
source/blender/nodes/geometry/node_geometry_util.cc [moved from source/blender/nodes/simulation/node_simulation_util.cc with 76% similarity]
source/blender/nodes/geometry/node_geometry_util.hh [moved from source/blender/nodes/simulation/node_simulation_util.h with 85% similarity]
source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc [new file with mode: 0644]
source/blender/nodes/geometry/nodes/node_geo_boolean.cc [new file with mode: 0644]
source/blender/nodes/geometry/nodes/node_geo_common.cc [moved from source/blender/nodes/simulation/nodes/node_sim_common.cc with 83% similarity]
source/blender/nodes/geometry/nodes/node_geo_edge_split.cc [new file with mode: 0644]
source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc [new file with mode: 0644]
source/blender/nodes/geometry/nodes/node_geo_object_info.cc [new file with mode: 0644]
source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc [new file with mode: 0644]
source/blender/nodes/geometry/nodes/node_geo_point_instance.cc [new file with mode: 0644]
source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc [new file with mode: 0644]
source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc [new file with mode: 0644]
source/blender/nodes/geometry/nodes/node_geo_transform.cc [new file with mode: 0644]
source/blender/nodes/geometry/nodes/node_geo_triangulate.cc [new file with mode: 0644]
source/blender/nodes/intern/derived_node_tree.cc
source/blender/nodes/intern/math_functions.cc [new file with mode: 0644]
source/blender/nodes/intern/node_geometry_exec.cc [new file with mode: 0644]
source/blender/nodes/intern/node_socket.cc
source/blender/nodes/intern/node_tree_multi_function.cc
source/blender/nodes/intern/node_tree_ref.cc
source/blender/nodes/intern/type_callbacks.cc [new file with mode: 0644]
source/blender/nodes/shader/node_shader_util.c
source/blender/nodes/shader/nodes/node_shader_math.cc

index eff88c835e7d1468292734450063457871287922..71b2de41d9e12a508fb9928f31fee60a51d467ee 100644 (file)
@@ -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 (file)
index 0000000..374bd41
--- /dev/null
@@ -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 (file)
index 0981baa..0000000
+++ /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,
-)
index c0c38c02c63c6c07ce11320247ff1ec9ecdfa78d..4cd38831cdf9373f561eceaaba72c610bb8a1e5a 100644 (file)
@@ -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
index 8d2b6198fd5320673d1a40d35748908d19c57702..b1789776728ea8a92634d7f1f650ddf340247f5e 100644 (file)
@@ -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__":
index aab962d42a63eb2ed8a84b275cf51f46f7cdc38b..55a841d8fd1b1c61c0b0644af6a471b5f636968b 100644 (file)
@@ -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 (file)
index 0000000..e58fba3
--- /dev/null
@@ -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 (file)
index 0000000..026f4d3
--- /dev/null
@@ -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 (file)
index 0000000..ef3ae3c
--- /dev/null
@@ -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;
+};
index a2c3787bcd269993b7f3d2507ce4e4bf8b9ac559..2491f0953c0b1d123fda7d8be6327a0f9262590b 100644 (file)
@@ -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);
index e6fb9d86b0ae8d6c3e0ec8d402d097cafed17210..905316a937d027662a1836d029b6080c967d333f 100644 (file)
@@ -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);
 
@@ -1322,6 +1333,24 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
                      struct MTex *mtex);
 /** \} */
 
+/* -------------------------------------------------------------------- */
+/** \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
  * \{ */
index 985a8cc3ca759bccbc0cc208b629996cfe11e0b1..d2d390dc786ac60408dd7e5c9d252b4882cbee38 100644 (file)
@@ -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,
index c89a45b2a43cf2af1ba16cc111acbbba77a0177b..1a90d6fadd392e818dc6e14da095c8954d90ff05 100644 (file)
@@ -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
index 9ad73133f9eb7863bf5a991e268b030e2ae87706..d8fd3a19303840c231d21129b2aec3bc4f6b03ca 100644 (file)
@@ -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 (file)
index 0000000..2345c83
--- /dev/null
@@ -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;
+}
+
+/** \} */
index 434456a922edecb0cacbfee430a47a1c78a90987..73ca490c39556f741d9a12dca83932d1ae00e729 100644 (file)
@@ -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 (file)
index 0000000..28695d7
--- /dev/null
@@ -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();
+}
+
+/** \} */
index 554919ad1a03f292bbcc3abd015a9d44d25efd65..a44b054e366950751a009d4019dbf26c8a25f547 100644 (file)
@@ -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 */
 
index f0031d4191dc88a6aa1ffd384eb0773e1f0d92fc..9e3189afee9ae223994f6e402b0980a04b8c5eae 100644 (file)
@@ -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);
 }
 
 /**
index e639bb319a3993697a23369cea2a0dfc99ea1a77..b564a4c468bffa90aa8c9ddfeecbd71938af84a2 100644 (file)
@@ -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();
 }
 
index 242c0edd5a4779bcc52929b8d905db6822e8f1ed..31d2e3738f46a9fa46ea842ccdb41a1b00f29591 100644 (file)
@@ -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;
 }
 
 /**
index a1b01bce6c9b2f80f67d00ac25c1bb295314af51..9a8c560f1161f4a46ef59a83bd9e5a2b99636f4d 100644 (file)
@@ -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"
@@ -806,6 +807,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;
   }
index 72115db4fb85eb09289c58dde479e80c8bd8ca28..14e049384efa0980596875abebdc1161efd51420 100644 (file)
@@ -1198,9 +1198,6 @@ static bool foreach_object_modifier_ptcache(Object *object,
         }
       }
     }
-    else if (md->type == eModifierType_Simulation) {
-      /* TODO(jacques): */
-    }
   }
   return true;
 }
index cbfb1086e8dd2063c3799fd8afd7bb6f2b885d8c..5f6685817b9ed43a344a93a5a343fa009eea91d8 100644 (file)
 
 #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 */
index 861779ec700f2b7854b095d90b0aa71351717b88..14e6ce63023de36ffc24affd8fa8303269fc410a 100644 (file)
@@ -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;
 }
 
index 78a323c24319068e7c98e81a28a0198b26369922..ec74cef9311bcc42bc4df36ba4f144e52a59a593 100644 (file)
@@ -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,
index 1b321bd7bcd5a74f2f691f3ec8754fa662012797..0c0dd464d4dfd7eebf9ac3d401d940c476ed0477 100644 (file)
@@ -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 (file)
index 0000000..ef27681
--- /dev/null
@@ -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
index 9736d8f9b3bdc3eaf717f0c77b371ca075ceb0eb..46a3ad87dfe86b410b8ca4b6c4765d97bb80a3f7 100644 (file)
@@ -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
index 224db31471d669d4f825a4b8600a1bca6f58e86a..c61e17e662763ecaa5d2a4d837dff2cc425524e0 100644 (file)
@@ -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;
index aca8d2f34391ff6f1f8a1d680f0443b71813a316..9278ff51b8dfa87eb64897a59d932dd102c747bb 100644 (file)
@@ -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);
+    }
   }
 }
index 2f613a0d416908ef46897eb567f99b2c1ea66a02..95ae587f4ced5ee139bc86e5078d96db78e01d7d 100644 (file)
@@ -24,6 +24,7 @@ set(INC
   ../blenlib
   ../bmesh
   ../draw
+  ../functions
   ../makesdna
   ../makesrna
   ../modifiers
index 2147a5847650730a4a117621e2280e2c7a8c4744..f894bdabba497912b6b7d68693a4b032b92eb81b 100644 (file)
@@ -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,
index 96c17ae4dc5278f4d34af07a4a357dbe29eb7946..6717ba521f6b3d0474409f12aeb84e75c147ad56 100644 (file)
@@ -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,
index ecb98a46f99a35f1450c0eb7f88c307858ebc258..417cae800ea2200cabc59b9a3091a9747aaffbcb 100644 (file)
@@ -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);
index 0b22afb7e66093a5973b57c75b72ed59ceb1a9af..6bd95cd8e514e091a1001f5612e54274480878a4 100644 (file)
@@ -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);
index 0757f840375744d37a8036ef970bffceaa86f6e2..d8f31161c20d0da85bf3509c8f86a87b6889ddaa 100644 (file)
@@ -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}")
index a0e03490fa3a42eed2afb473a3644778581b6a98..8fdc86f8599523e7970c3de750cb9304bd2dd022 100644 (file)
@@ -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)
index a567ae41a54ce84b8e6d973b157ba4c01bead736..fb83414e4351125ddbfdbffc7282637cc51213ea 100644 (file)
@@ -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);
index a39e84419d05080d985fa36bfd18c84bc205ae42..039ddad71ef4ceb4b626be2cf087084c58727b12 100644 (file)
@@ -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);
 
index 654b95858bba7683fe81ab33def5c97a9ef9b1ac..f2abe272f48e17177f522d53895386fb1d4b621a 100644 (file)
@@ -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 "";
index b6d9cdc729d9cdcc25e50b482a619f0110953982..ba1e752e27658389994c467c2472fb62fc78656f 100644 (file)
@@ -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);
     }
   }
index 76c25799e2c4a7f1a0d651a7c900937ebcd1ea6e..609fc43b4356e0e105648d0d3487c3559dc2f215 100644 (file)
@@ -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);
index e005753e228bb22bb1172375b60dcafdeb9a122d..429959f9c33490637c57a39a71c2418a7981a5b9 100644 (file)
@@ -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 (file)
index 0000000..2c1b37c
--- /dev/null
@@ -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
index bf4319849460664dcec2996fb1ae93cf00e1fadc..d8924b3cf23fadc612022879a79d638ece81214c 100644 (file)
@@ -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
index b5bcfa4d157c1636cfb0fc475514b1339b4df766..f73f43ddade7c75079bd7228806e25fc517e6975 100644 (file)
     .flag = 0, \
   }
 
-#define _DNA_DEFAULT_SimulationModifierData \
+#define _DNA_DEFAULT_NodesModifierData \
   { 0 }
 
 #define _DNA_DEFAULT_SkinModifierData \
index 7b5bdac47f57a2f8e3ad37d48a916b7a81359620..43ed532a65b6bc112181ee7c2ff0f6f5e3939518 100644 (file)
@@ -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;
index fdae35595997fa42807bdd155150bd167b9c6de4..29c83d2d4edb1bfa38e3aeb4bb056d24d05d7fde 100644 (file)
@@ -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
 }
index 5a00b6479d5f1b4c859a44156e38cc8bf2afd51c..328e4d2ba22feffa15ee489e13f101a756ab207f 100644 (file)
@@ -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.
index 8c95a6d2a316c1f66dd57a06fab16b5ef5a20c3f..1a8bd25215fdfc5d717dd0ca5e841264f7064ad8 100644 (file)
@@ -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. */
index 6dfd7b0fdf5f1e11a6a542c0c446de062a8fcdb6..bc25a404de817a7e8fde90f894a59c5d7d97280f 100644 (file)
@@ -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;
index d638d6f66cca1ce8aabf904ac7ae7c487752e2f7..3ebbb98934ecbc56522cb4e2f31782a0ef368251 100644 (file)
@@ -373,6 +373,7 @@ blender_include_dirs(
   ../../ikplugin
   ../../imbuf
   ../../makesdna
+  ../../modifiers
   ../../nodes/
   ../../sequencer
   ../../simulation
index ceccccc32912284a8e07ca5fb489e15404679521..4991f34c3f6e93c863e89fc3bd69ce3b77827386 100644 (file)
@@ -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) {
index 7f0ab2df70df79482926088ca596c9d2413ea9a7..ad615026343e24fa94a14ff0c2e4adc02b91c0d1 100644 (file)
@@ -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);
index c0f3197b2d10e3bbb85610f9a81809e9ab344e76..09c1869b84bfc69822f3ce2b4b71db7f40d28832 100644 (file)
@@ -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);
index 67bb5e89254c8849b8d991befbc362f59e37d3fe..11d57919fb1e700ca504b6a8c7b7d75352667014 100644 (file)
@@ -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",
index e6e54abebd4714a24cc17b01c1d3a448a3ce8494..dc3e48b9c70de18ab4dda6afd246949616109476 100644 (file)
@@ -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");
index 549751ce267dd162a9b7d7238887309257e42378..34713392afb1415ee05cea1923687889533b8b0e 100644 (file)
@@ -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
index 9c4887803668cb458c22718a5ee3aa911dd81b31..f36cb7ded9c53790e498fe73de8bc266bc412639 100644 (file)
@@ -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;
similarity index 79%
rename from source/blender/nodes/NOD_simulation.h
rename to source/blender/modifiers/MOD_nodes.h
index 6b3d51b46a97a9029cf249ab23f90b2cc733b5c2..9c75e7e341675517c8846bb536a38ffde72282b2 100644 (file)
 
 #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
 }
index 882d080c08d88b6faac67afbc5cae185a73f3ab5..30c38623f681ce6c6d6acec19d5799c89adb2659 100644 (file)
 #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 (file)
index 0000000..b1849b3
--- /dev/null
@@ -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 (file)
index 0766c59..0000000
+++ /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,
-};
index 5a07c4e5e641def2a99560a25440db35405e2e2e..1930a38b825672dbfb5cec9454bc99b8ce1cecbc 100644 (file)
 #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;
index e0802dc5fb40b60dbc72a39b248f885a7911fed8..55409cba114b519e095328025e0bc79c764f0d5e 100644 (file)
@@ -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
 }
index f88d50167ace70cfb03c479330d26f3867f8aa80..a367f40dca799e68ad9ce925d644ac9fd76ba1db 100644 (file)
@@ -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}")
index 9c71ae79cf2980363db88345ed97891cdb3b599a..087bfac44422ae8e94ff6c3ecfed7733502fce9b 100644 (file)
@@ -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 (file)
index 0000000..0532547
--- /dev/null
@@ -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 (file)
index 0000000..2b95f76
--- /dev/null
@@ -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 (file)
index 0000000..70e4174
--- /dev/null
@@ -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
index 25787231afa2dd4452cd775bf6a703b47119a324..552ef5509fadeece9fdaef55453e395780938af9 100644 (file)
 #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
index 3b085248a3930a30f39987e3238ef6617c7111ad..ebdd8b7fe49f9519acf86bfcda132cc24bd23ae5 100644 (file)
@@ -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_;
index 0173706b570399f6ef582e055efedb3a2f28028e..09e0908140ce9d4e9ca4f87c8281b44e60fcf0a8 100644 (file)
@@ -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 (file)
index 0000000..d1a4bd3
--- /dev/null
@@ -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
index 342c330a8fae71c52e941960e7295a7b9c16e3bb..05b452e61a580710f6c253f2dc3ea554690cfb32 100644 (file)
@@ -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)
index f8bd9a3094096f0bee7643b389cf1bf0a4833c7f..3d4006b59534d2a61d8b5ca127841bde7d106ec2 100644 (file)
@@ -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);
index 584c544946ebbc2056fa884a59694da6d962038b..f3401a2c00d610d09128cbb326f0b765815665b9 100644 (file)
@@ -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 (file)
index 0000000..64a04d7
--- /dev/null
@@ -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 {
+
+}
similarity index 66%
rename from source/blender/nodes/simulation/node_simulation_tree.cc
rename to source/blender/nodes/geometry/node_geometry_tree.cc
index eb3257d7e668ffce0c2ca11ba02bba9200e16a91..d4a9805f311a93110c9c78882c28af528796cd86 100644 (file)
@@ -18,7 +18,7 @@
 
 #include "MEM_guardedalloc.h"
 
-#include "NOD_simulation.h"
+#include "NOD_geometry.h"
 
 #include "BKE_node.h"
 
 
 #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);
 }
similarity index 76%
rename from source/blender/nodes/simulation/node_simulation_util.cc
rename to source/blender/nodes/geometry/node_geometry_util.cc
index ae875335da84c9b5cbd77978c89e35878b79916d..e8d2494f91d76da1805919a84ab9a40d7342d507 100644 (file)
  * 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;
 }
similarity index 85%
rename from source/blender/nodes/simulation/node_simulation_util.h
rename to source/blender/nodes/geometry/node_geometry_util.hh
index 76a10715cffc2e14c27c14a7f5541e6d0ff01056..bb26763642b00f67b65ab32aec3a9c1e87f47c31 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <string.h>
 
+#include "BLI_float3.hh"
 #include "BLI_utildefines.h"
 
 #include "MEM_guardedalloc.h"
 
 #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 (file)
index 0000000..5e2830d
--- /dev/null
@@ -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 (file)
index 0000000..a0ba8e3
--- /dev/null
@@ -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);
+}
similarity index 83%
rename from source/blender/nodes/simulation/nodes/node_sim_common.cc
rename to source/blender/nodes/geometry/nodes/node_geo_common.cc
index fd26d4501299aa046890adb368c69662bd7b1ebe..8adc3962698919c6d39df9238da293f5a70e71f7 100644 (file)
 
 #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 (file)
index 0000000..22e75b3
--- /dev/null
@@ -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 (file)
index 0000000..3bf560f
--- /dev/null
@@ -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 (file)
index 0000000..8d80e1c
--- /dev/null
@@ -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 (file)
index 0000000..7f94ca3
--- /dev/null
@@ -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 (file)
index 0000000..bb8f1ff
--- /dev/null
@@ -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 (file)
index 0000000..68ea548
--- /dev/null
@@ -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 (file)
index 0000000..dccdf94
--- /dev/null
@@ -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_m