diff options
18 files changed, 1478 insertions, 8 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index f9a789f800b..cac5a8f835c 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -519,6 +519,17 @@ geometry_node_categories = [ NodeItem("GeometryNodeEdgeSplit"), NodeItem("GeometryNodeSubdivideSmooth"), NodeItem("GeometryNodeSubdivide"), + + # These should be in a submenu, but that requires refactoring to remove NodeItem. + NodeItem("GeometryNodeMeshPlane"), + NodeItem("GeometryNodeMeshCube"), + NodeItem("GeometryNodeMeshCircle"), + NodeItem("GeometryNodeMeshUVSphere"), + NodeItem("GeometryNodeMeshIcoSphere"), + NodeItem("GeometryNodeMeshCylinder"), + NodeItem("GeometryNodeMeshCone"), + NodeItem("GeometryNodeMeshTorus"), + NodeItem("GeometryNodeMeshGrid"), ]), GeometryNodeCategory("GEO_POINT", "Point", items=[ NodeItem("GeometryNodePointDistribute"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 2c543a0a014..7da4c8c1f4e 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1373,6 +1373,12 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_ATTRIBUTE_SEPARATE_XYZ 1028 #define GEO_NODE_SUBDIVIDE 1029 #define GEO_NODE_ATTRIBUTE_REMOVE 1030 +#define GEO_NODE_MESH_PRIMITIVE_CUBE 1031 +#define GEO_NODE_MESH_PRIMITIVE_CIRCLE 1032 +#define GEO_NODE_MESH_PRIMITIVE_UV_SPHERE 1033 +#define GEO_NODE_MESH_PRIMITIVE_CYLINDER 1034 +#define GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE 1035 +#define GEO_NODE_MESH_PRIMITIVE_CONE 1036 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 528088b2ee7..17bdc08c995 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -1568,6 +1568,8 @@ const char *nodeStaticSocketType(int type, int subtype) return "NodeSocketFloatAngle"; case PROP_TIME: return "NodeSocketFloatTime"; + case PROP_DISTANCE: + return "NodeSocketFloatDistance"; case PROP_NONE: default: return "NodeSocketFloat"; @@ -1637,6 +1639,8 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype) return "NodeSocketInterfaceFloatAngle"; case PROP_TIME: return "NodeSocketInterfaceFloatTime"; + case PROP_DISTANCE: + return "NodeSocketInterfaceFloatDistance"; case PROP_NONE: default: return "NodeSocketInterfaceFloat"; @@ -4801,6 +4805,12 @@ static void registerGeometryNodes() register_node_type_geo_edge_split(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); + register_node_type_geo_mesh_primitive_circle(); + register_node_type_geo_mesh_primitive_cone(); + register_node_type_geo_mesh_primitive_cube(); + register_node_type_geo_mesh_primitive_cylinder(); + register_node_type_geo_mesh_primitive_uv_sphere(); + register_node_type_geo_mesh_primitive_ico_sphere(); register_node_type_geo_object_info(); register_node_type_geo_point_distribute(); register_node_type_geo_point_instance(); diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index 9a8870963bf..7e49cc89b52 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -174,6 +174,11 @@ struct float3 { return len_squared_v3(*this); } + bool is_zero() const + { + return this->x == 0.0f && this->y == 0.0f && this->z == 0.0f; + } + void reflect(const float3 &normal) { *this = this->reflected(normal); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index d19d270a7ac..e09a1ef38cf 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1226,6 +1226,21 @@ typedef struct NodeAttributeSeparateXYZ { uint8_t input_type; } NodeAttributeSeparateXYZ; +typedef struct NodeGeometryMeshCircle { + /* GeometryNodeMeshCircleFillType. */ + uint8_t fill_type; +} NodeGeometryMeshCircle; + +typedef struct NodeGeometryMeshCylinder { + /* GeometryNodeMeshCircleFillType. */ + uint8_t fill_type; +} NodeGeometryMeshCylinder; + +typedef struct NodeGeometryMeshCone { + /* GeometryNodeMeshCircleFillType. */ + uint8_t fill_type; +} NodeGeometryMeshCone; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1702,6 +1717,12 @@ typedef enum GeometryNodePointsToVolumeResolutionMode { GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE = 1, } GeometryNodePointsToVolumeResolutionMode; +typedef enum GeometryNodeMeshCircleFillType { + GEO_NODE_MESH_CIRCLE_FILL_NONE = 0, + GEO_NODE_MESH_CIRCLE_FILL_NGON = 1, + GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN = 2, +} GeometryNodeMeshCircleFillType; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index dfb882cde33..dd2924234ea 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -398,6 +398,13 @@ static const EnumPropertyItem prop_shader_output_target_items[] = { {0, NULL, 0, NULL, NULL}, }; +static EnumPropertyItem rna_node_geometry_mesh_circle_fill_type_items[] = { + {GEO_NODE_MESH_CIRCLE_FILL_NONE, "NONE", 0, "None", ""}, + {GEO_NODE_MESH_CIRCLE_FILL_NGON, "NGON", 0, "N-Gon", ""}, + {GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN, "TRIANGLE_FAN", 0, "Triangles", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #endif #define ITEM_ATTRIBUTE \ @@ -9188,6 +9195,42 @@ static void def_geo_attribute_separate_xyz(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_mesh_circle(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryMeshCircle", "storage"); + + prop = RNA_def_property(srna, "fill_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_mesh_circle_fill_type_items); + RNA_def_property_ui_text(prop, "Fill Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + +static void def_geo_mesh_cylinder(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryMeshCylinder", "storage"); + + prop = RNA_def_property(srna, "fill_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_mesh_circle_fill_type_items); + RNA_def_property_ui_text(prop, "Fill Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + +static void def_geo_mesh_cone(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryMeshCone", "storage"); + + prop = RNA_def_property(srna, "fill_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_mesh_circle_fill_type_items); + RNA_def_property_ui_text(prop, "Fill Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) @@ -10042,6 +10085,8 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna) brna, "NodeSocketFloatAngle", "NodeSocketInterfaceFloatAngle", PROP_ANGLE); rna_def_node_socket_float( brna, "NodeSocketFloatTime", "NodeSocketInterfaceFloatTime", PROP_TIME); + rna_def_node_socket_float( + brna, "NodeSocketFloatDistance", "NodeSocketInterfaceFloatDistance", PROP_DISTANCE); rna_def_node_socket_int(brna, "NodeSocketInt", "NodeSocketInterfaceInt", PROP_NONE); rna_def_node_socket_int( diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 9408e5348dd..cdf18f21f8a 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -161,6 +161,12 @@ set(SRC geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_is_viewport.cc geometry/nodes/node_geo_join_geometry.cc + geometry/nodes/node_geo_mesh_primitive_circle.cc + geometry/nodes/node_geo_mesh_primitive_cone.cc + geometry/nodes/node_geo_mesh_primitive_cube.cc + geometry/nodes/node_geo_mesh_primitive_cylinder.cc + geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc + geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc geometry/nodes/node_geo_object_info.cc geometry/nodes/node_geo_point_distribute.cc geometry/nodes/node_geo_point_instance.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d7f56464b36..2f3bd66859d 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -43,6 +43,12 @@ void register_node_type_geo_collection_info(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); +void register_node_type_geo_mesh_primitive_circle(void); +void register_node_type_geo_mesh_primitive_cone(void); +void register_node_type_geo_mesh_primitive_cube(void); +void register_node_type_geo_mesh_primitive_cylinder(void); +void register_node_type_geo_mesh_primitive_uv_sphere(void); +void register_node_type_geo_mesh_primitive_ico_sphere(void); void register_node_type_geo_object_info(void); void register_node_type_geo_point_distribute(void); void register_node_type_geo_point_instance(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 6669f47c6aa..cc05c221820 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -300,6 +300,12 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "") DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Circle", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 271e3771006..fb80bd08797 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -47,4 +47,15 @@ void update_attribute_input_socket_availabilities(bNode &node, Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, const AttributeDomain domain); +void transform_mesh(Mesh *mesh, + const float3 translation, + const float3 rotation, + const float3 scale); + +Mesh *create_cylinder_or_cone_mesh(const float radius_top, + const float radius_bottom, + const float depth, + const int verts_num, + const GeometryNodeMeshCircleFillType fill_type); + } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc new file mode 100644 index 00000000000..ebafb0c6aeb --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -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. + */ + +#include "BLI_map.hh" +#include "BLI_math_matrix.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_circle_in[] = { + {SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096}, + {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_circle_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_primitive_circle_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); +} + +static void geo_node_mesh_primitive_circle_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryMeshCircle *node_storage = (NodeGeometryMeshCircle *)MEM_callocN( + sizeof(NodeGeometryMeshCircle), __func__); + + node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NONE; + + node->storage = node_storage; +} + +namespace blender::nodes { + +static int circle_vert_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) +{ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + case GEO_NODE_MESH_CIRCLE_FILL_NGON: + return verts_num; + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: + return verts_num + 1; + } + BLI_assert(false); + return 0; +} + +static int circle_edge_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) +{ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + case GEO_NODE_MESH_CIRCLE_FILL_NGON: + return verts_num; + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: + return verts_num * 2; + } + BLI_assert(false); + return 0; +} + +static int circle_corner_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) +{ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + return 0; + case GEO_NODE_MESH_CIRCLE_FILL_NGON: + return verts_num; + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: + return verts_num * 3; + } + BLI_assert(false); + return 0; +} + +static int circle_face_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) +{ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + return 0; + case GEO_NODE_MESH_CIRCLE_FILL_NGON: + return 1; + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: + return verts_num; + } + BLI_assert(false); + return 0; +} + +static Mesh *create_circle_mesh(const float radius, + const int verts_num, + const GeometryNodeMeshCircleFillType fill_type) +{ + Mesh *mesh = BKE_mesh_new_nomain(circle_vert_total(fill_type, verts_num), + circle_edge_total(fill_type, verts_num), + 0, + circle_corner_total(fill_type, verts_num), + circle_face_total(fill_type, verts_num)); + MutableSpan<MVert> verts = MutableSpan<MVert>(mesh->mvert, mesh->totvert); + MutableSpan<MLoop> loops = MutableSpan<MLoop>(mesh->mloop, mesh->totloop); + MutableSpan<MEdge> edges = MutableSpan<MEdge>(mesh->medge, mesh->totedge); + MutableSpan<MPoly> polys = MutableSpan<MPoly>(mesh->mpoly, mesh->totpoly); + + float angle = 0.0f; + const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num); + for (MVert &vert : verts) { + copy_v3_v3(vert.co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f)); + angle += angle_delta; + } + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + copy_v3_v3(verts.last().co, float3(0)); + } + + /* Point all vertex normals in the up direction. */ + short up_normal[3] = {0, 0, SHRT_MAX}; + for (MVert &vert : verts) { + copy_v3_v3_short(vert.no, up_normal); + } + + /* Create outer edges. */ + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[i]; + edge.v1 = i; + edge.v2 = (i + 1) % verts_num; + } + + /* Set loose edge flags. */ + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) { + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[i]; + edge.flag |= ME_LOOSEEDGE; + } + } + + /* Create triangle fan edges. */ + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[verts_num + i]; + edge.v1 = verts_num; + edge.v2 = i; + } + } + + /* Create corners and faces. */ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + break; + case GEO_NODE_MESH_CIRCLE_FILL_NGON: { + for (const int i : IndexRange(verts_num)) { + MLoop &loop = loops[i]; + loop.e = i; + loop.v = i; + } + MPoly &poly = polys[0]; + poly.loopstart = 0; + poly.totloop = loops.size(); + break; + } + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: { + for (const int i : IndexRange(verts_num)) { + MLoop &loop = loops[3 * i]; + loop.e = i; + loop.v = i; + MLoop &loop2 = loops[3 * i + 1]; + loop2.e = verts_num + ((i + 1) % verts_num); + loop2.v = (i + 1) % verts_num; + MLoop &loop3 = loops[3 * i + 2]; + loop3.e = verts_num + i; + loop3.v = verts_num; + + MPoly &poly = polys[i]; + poly.loopstart = 3 * i; + poly.totloop = 3; + } + break; + } + } + + BLI_assert(BKE_mesh_is_valid(mesh)); + + return mesh; +} + +static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) +{ + const bNode &node = params.node(); + const NodeGeometryMeshCircle &storage = *(const NodeGeometryMeshCircle *)node.storage; + + const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) + storage.fill_type; + + const int verts_num = params.extract_input<int>("Vertices"); + if (verts_num < 3) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const float radius = params.extract_input<float>("Radius"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_circle_mesh(radius, verts_num, fill_type); + BLI_assert(BKE_mesh_is_valid(mesh)); + + if (!location.is_zero() || !rotation.is_zero()) { + transform_mesh(mesh, location, rotation, float3(1)); + } + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_circle() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Circle", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_circle_in, geo_node_mesh_primitive_circle_out); + node_type_init(&ntype, geo_node_mesh_primitive_circle_init); + node_type_storage( + &ntype, "NodeGeometryMeshCircle", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_circle_exec; + ntype.draw_buttons = geo_node_mesh_primitive_circle_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc new file mode 100644 index 00000000000..fcf1adcfee9 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -0,0 +1,112 @@ +/* + * 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_map.hh" +#include "BLI_math_matrix.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_cone_in[] = { + {SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096}, + {SOCK_FLOAT, N_("Radius Top"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Radius Bottom"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Depth"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_cone_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_primitive_cone_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); +} + +static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryMeshCone *node_storage = (NodeGeometryMeshCone *)MEM_callocN( + sizeof(NodeGeometryMeshCone), __func__); + + node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON; + + node->storage = node_storage; +} + +namespace blender::nodes { + +static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) +{ + const bNode &node = params.node(); + const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node.storage; + + const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) + storage.fill_type; + + const int verts_num = params.extract_input<int>("Vertices"); + if (verts_num < 3) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const float radius_top = params.extract_input<float>("Radius Top"); + const float radius_bottom = params.extract_input<float>("Radius Bottom"); + const float depth = params.extract_input<float>("Depth"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_cylinder_or_cone_mesh( + radius_top, radius_bottom, depth, verts_num, fill_type); + + BLI_assert(BKE_mesh_is_valid(mesh)); + + /* Transform the mesh so that the base of the cone is at the origin. */ + transform_mesh(mesh, location + float3(0.0f, 0.0f, depth), rotation, float3(1)); + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_cone() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_cone_in, geo_node_mesh_primitive_cone_out); + node_type_init(&ntype, geo_node_mesh_primitive_cone_init); + node_type_storage( + &ntype, "NodeGeometryMeshCone", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cone_exec; + ntype.draw_buttons = geo_node_mesh_primitive_cone_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc new file mode 100644 index 00000000000..ba19f0a70fe --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -0,0 +1,84 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" + +#include "BKE_lib_id.h" +#include "BKE_mesh.h" + +#include "bmesh.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_cube_in[] = { + {SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_cube_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static Mesh *create_cube_mesh(const float3 location, const float3 rotation, const float size) +{ + float4x4 transform; + loc_eul_size_to_mat4(transform.values, location, rotation, float3(size)); + + const BMeshCreateParams bmcp = {true}; + const BMAllocTemplate allocsize = {8, 12, 24, 6}; + BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + + BMO_op_callf(bm, + BMO_FLAG_DEFAULTS, + "create_cube matrix=%m4 size=%f calc_uvs=%b", + transform.values, + size, + true); + + Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, NULL); + BM_mesh_bm_to_me_for_eval(bm, mesh, NULL); + BM_mesh_free(bm); + + return mesh; +} + +static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) +{ + const float size = params.extract_input<float>("Size"); + const float3 location = params.extract_input<float3>("Translation"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_cube_mesh(location, rotation, size); + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_cube() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CUBE, "Cube", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_cube_in, geo_node_mesh_primitive_cube_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cube_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc new file mode 100644 index 00000000000..b2274a2c7c7 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -0,0 +1,494 @@ +/* + * 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_map.hh" +#include "BLI_math_matrix.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_cylinder_in[] = { + {SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096}, + {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Depth"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_cylinder_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_primitive_cylinder_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); +} + +static void geo_node_mesh_primitive_cylinder_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryMeshCylinder *node_storage = (NodeGeometryMeshCylinder *)MEM_callocN( + sizeof(NodeGeometryMeshCylinder), __func__); + + node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NONE; + + node->storage = node_storage; +} + +namespace blender::nodes { + +static int vert_total(const GeometryNodeMeshCircleFillType fill_type, + const int verts_num, + const bool use_top, + const bool use_bottom) +{ + int vert_total = 0; + if (use_top) { + vert_total += verts_num; + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + vert_total++; + } + } + else { + vert_total++; + } + if (use_bottom) { + vert_total += verts_num; + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + vert_total++; + } + } + else { + vert_total++; + } + + return vert_total; +} + +static int edge_total(const GeometryNodeMeshCircleFillType fill_type, + const int verts_num, + const bool use_top, + const bool use_bottom) +{ + if (!use_top && !use_bottom) { + return 1; + } + + int edge_total = 0; + if (use_top) { + edge_total += verts_num; + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + edge_total += verts_num; + } + } + + edge_total += verts_num; + + if (use_bottom) { + edge_total += verts_num; + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + edge_total += verts_num; + } + } + + return edge_total; +} + +static int corner_total(const GeometryNodeMeshCircleFillType fill_type, + const int verts_num, + const bool use_top, + const bool use_bottom) +{ + if (!use_top && !use_bottom) { + return 0; + } + + int corner_total = 0; + if (use_top) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + corner_total += verts_num; + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + corner_total += verts_num * 3; + } + } + + if (use_top && use_bottom) { + corner_total += verts_num * 4; + } + else { + corner_total += verts_num * 3; + } + + if (use_bottom) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + corner_total += verts_num; + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + corner_total += verts_num * 3; + } + } + + return corner_total; +} + +static int face_total(const GeometryNodeMeshCircleFillType fill_type, + const int verts_num, + const bool use_top, + const bool use_bottom) +{ + if (!use_top && !use_bottom) { + return 0; + } + + int face_total = 0; + if (use_top) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + face_total++; + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + face_total += verts_num; + } + } + + face_total += verts_num; + + if (use_bottom) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + face_total++; + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + face_total += verts_num; + } + } + + return face_total; +} + +Mesh *create_cylinder_or_cone_mesh(const float radius_top, + const float radius_bottom, + const float depth, + const int verts_num, + const GeometryNodeMeshCircleFillType fill_type) +{ + const bool use_top = radius_top != 0.0f; + const bool use_bottom = radius_bottom != 0.0f; + /* Handle the case of a line / single point before everything else to avoid + * the need to check for it later. */ + if (!use_top && !use_bottom) { + const bool single_vertex = depth == 0.0f; + Mesh *mesh = BKE_mesh_new_nomain(single_vertex ? 1 : 2, single_vertex ? 0 : 1, 0, 0, 0); + copy_v3_v3(mesh->mvert[0].co, float3(0.0f, 0.0f, depth)); + if (single_vertex) { + short up[3] = {0, 0, SHRT_MAX}; + copy_v3_v3_short(mesh->mvert[0].no, up); + return mesh; + } + copy_v3_v3(mesh->mvert[1].co, float3(0.0f, 0.0f, -depth)); + mesh->medge[0].v1 = 0; + mesh->medge[0].v2 = 1; + mesh->medge[0].flag |= ME_LOOSEEDGE; + BKE_mesh_calc_normals(mesh); + return mesh; + } + + Mesh *mesh = BKE_mesh_new_nomain(vert_total(fill_type, verts_num, use_top, use_bottom), + edge_total(fill_type, verts_num, use_top, use_bottom), + 0, + corner_total(fill_type, verts_num, use_top, use_bottom), + face_total(fill_type, verts_num, use_top, use_bottom)); + MutableSpan<MVert> verts = MutableSpan<MVert>(mesh->mvert, mesh->totvert); + MutableSpan<MEdge> edges = MutableSpan<MEdge>(mesh->medge, mesh->totedge); + MutableSpan<MLoop> loops = MutableSpan<MLoop>(mesh->mloop, mesh->totloop); + MutableSpan<MPoly> polys = MutableSpan<MPoly>(mesh->mpoly, mesh->totpoly); + + /* Calculate vertex positions. */ + const int top_verts_start = 0; + const int bottom_verts_start = top_verts_start + (use_top ? verts_num : 1); + float angle = 0.0f; + const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num); + for (const int i : IndexRange(verts_num)) { + const float x = std::cos(angle); + const float y = std::sin(angle); + if (use_top) { + copy_v3_v3(verts[top_verts_start + i].co, float3(x * radius_top, y * radius_top, depth)); + } + if (use_bottom) { + copy_v3_v3(verts[bottom_verts_start + i].co, + float3(x * radius_bottom, y * radius_bottom, -depth)); + } + angle += angle_delta; + } + if (!use_top) { + copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, depth)); + } + if (!use_bottom) { + copy_v3_v3(verts[bottom_verts_start].co, float3(0.0f, 0.0f, -depth)); + } + + /* Add center vertices for the triangle fans at the end. */ + const int top_center_vert_index = bottom_verts_start + (use_bottom ? verts_num : 1); + const int bottom_center_vert_index = top_center_vert_index + (use_top ? 1 : 0); + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + if (use_top) { + copy_v3_v3(verts[top_center_vert_index].co, float3(0.0f, 0.0f, depth)); + } + if (use_bottom) { + copy_v3_v3(verts[bottom_center_vert_index].co, float3(0.0f, 0.0f, -depth)); + } + } + + /* Create top edges. */ + const int top_edges_start = 0; + const int top_fan_edges_start = (use_top && + fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ? + top_edges_start + verts_num : + top_edges_start; + if (use_top) { + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[top_edges_start + i]; + edge.v1 = top_verts_start + i; + edge.v2 = top_verts_start + (i + 1) % verts_num; + } + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[top_fan_edges_start + i]; + edge.v1 = top_center_vert_index; + edge.v2 = top_verts_start + i; + } + } + } + + /* Create connecting edges. */ + const int connecting_edges_start = top_fan_edges_start + (use_top ? verts_num : 0); + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[connecting_edges_start + i]; + edge.v1 = top_verts_start + (use_top ? i : 0); + edge.v2 = bottom_verts_start + (use_bottom ? i : 0); + } + + /* Create bottom edges. */ + const int bottom_edges_start = connecting_edges_start + verts_num; + const int bottom_fan_edges_start = (use_bottom && + fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ? + bottom_edges_start + verts_num : + bottom_edges_start; + if (use_bottom) { + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[bottom_edges_start + i]; + edge.v1 = bottom_verts_start + i; + edge.v2 = bottom_verts_start + (i + 1) % verts_num; + } + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[bottom_fan_edges_start + i]; + edge.v1 = bottom_center_vert_index; + edge.v2 = bottom_verts_start + i; + } + } + } + + /* Create top corners and faces. */ + int loop_index = 0; + int poly_index = 0; + if (use_top) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = verts_num; + + for (const int i : IndexRange(verts_num)) { + MLoop &loop = loops[loop_index++]; + loop.v = top_verts_start + i; + loop.e = top_edges_start + i; + } + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + for (const int i : IndexRange(verts_num)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 3; + + MLoop &loop1 = loops[loop_index++]; + loop1.v = top_verts_start + i; + loop1.e = top_edges_start + i; + MLoop &loop2 = loops[loop_index++]; + loop2.v = top_verts_start + (i + 1) % verts_num; + loop2.e = top_fan_edges_start + (i + 1) % verts_num; + MLoop &loop3 = loops[loop_index++]; + loop3.v = top_center_vert_index; + loop3.e = top_fan_edges_start + i; + } + } + } + + /* Create side corners and faces. */ + if (use_top && use_bottom) { + /* Quads connect the top and bottom. */ + for (const int i : IndexRange(verts_num)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 4; + + MLoop &loop1 = loops[loop_index++]; + loop1.v = top_verts_start + i; + loop1.e = connecting_edges_start + i; + MLoop &loop2 = loops[loop_index++]; + loop2.v = bottom_verts_start + i; + loop2.e = bottom_edges_start + i; + MLoop &loop3 = loops[loop_index++]; + loop3.v = bottom_verts_start + (i + 1) % verts_num; + loop3.e = connecting_edges_start + (i + 1) % verts_num; + MLoop &loop4 = loops[loop_index++]; + loop4.v = top_verts_start + (i + 1) % verts_num; + loop4.e = top_edges_start + i; + } + } + else { + /* Triangles connect the top and bottom section. */ + if (use_top) { + for (const int i : IndexRange(verts_num)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 3; + + MLoop &loop1 = loops[loop_index++]; + loop1.v = top_verts_start + i; + loop1.e = connecting_edges_start + i; + MLoop &loop2 = loops[loop_index++]; + loop2.v = bottom_verts_start; + loop2.e = connecting_edges_start + (i + 1) % verts_num; + MLoop &loop3 = loops[loop_index++]; + loop3.v = top_verts_start + (i + 1) % verts_num; + loop3.e = top_edges_start + i; + } + } + else { + BLI_assert(use_bottom); + for (const int i : IndexRange(verts_num)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 3; + + MLoop &loop1 = loops[loop_index++]; + loop1.v = bottom_verts_start + i; + loop1.e = bottom_edges_start + i; + MLoop &loop2 = loops[loop_index++]; + loop2.v = bottom_verts_start + (i + 1) % verts_num; + loop2.e = connecting_edges_start + (i + 1) % verts_num; + MLoop &loop3 = loops[loop_index++]; + loop3.v = top_verts_start; + loop3.e = connecting_edges_start + i; + } + } + } + + /* Create bottom corners and faces. */ + if (use_bottom) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = verts_num; + + for (const int i : IndexRange(verts_num)) { + /* Go backwards to reverse surface normal. */ + MLoop &loop = loops[loop_index++]; + loop.v = bottom_verts_start + verts_num - 1 - i; + loop.e = bottom_edges_start + verts_num - 1 - (i + 1) % verts_num; + } + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + for (const int i : IndexRange(verts_num)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 3; + + MLoop &loop1 = loops[loop_index++]; + loop1.v = bottom_verts_start + i; + loop1.e = bottom_fan_edges_start + i; + MLoop &loop2 = loops[loop_index++]; + loop2.v = bottom_center_vert_index; + loop2.e = bottom_fan_edges_start + (i + 1) % verts_num; + MLoop &loop3 = loops[loop_index++]; + loop3.v = bottom_verts_start + (i + 1) % verts_num; + loop3.e = bottom_edges_start + i; + } + } + } + + BKE_mesh_calc_normals(mesh); + + return mesh; +} + +static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) +{ + const bNode &node = params.node(); + const NodeGeometryMeshCylinder &storage = *(const NodeGeometryMeshCylinder *)node.storage; + + const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) + storage.fill_type; + + const int verts_num = params.extract_input<int>("Vertices"); + if (verts_num < 3) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const float radius = params.extract_input<float>("Radius"); + const float depth = params.extract_input<float>("Depth"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, verts_num, fill_type); + + if (!location.is_zero() || !rotation.is_zero()) { + transform_mesh(mesh, location, rotation, float3(1)); + } + + BLI_assert(BKE_mesh_is_valid(mesh)); + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_cylinder() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_cylinder_in, geo_node_mesh_primitive_cylinder_out); + node_type_init(&ntype, geo_node_mesh_primitive_cylinder_init); + node_type_storage( + &ntype, "NodeGeometryMeshCylinder", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cylinder_exec; + ntype.draw_buttons = geo_node_mesh_primitive_cylinder_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc new file mode 100644 index 00000000000..048c8ee9069 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -0,0 +1,92 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" + +#include "BKE_lib_id.h" +#include "BKE_mesh.h" + +#include "bmesh.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_ico_sphere_in[] = { + {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_INT, N_("Subdivisions"), 1, 0, 0, 0, 0, 7}, + {SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_ico_sphere_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static Mesh *create_ico_sphere_mesh(const float3 location, + const float3 rotation, + const int subdivisions, + const float radius) +{ + float4x4 transform; + loc_eul_size_to_mat4(transform.values, location, rotation, float3(1.0f)); + + const BMeshCreateParams bmcp = {true}; + const BMAllocTemplate allocsize = {0, 0, 0, 0}; + BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + + BMO_op_callf(bm, + BMO_FLAG_DEFAULTS, + "create_icosphere subdivisions=%i diameter=%f matrix=%m4 calc_uvs=%b", + subdivisions, + radius, + transform.values, + true); + + Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, NULL); + BM_mesh_bm_to_me_for_eval(bm, mesh, NULL); + BM_mesh_free(bm); + + return mesh; +} + +static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params) +{ + /* Anything above 8 is not likely to be purposeful and will be very slow. */ + const int subdivisions = std::min(params.extract_input<int>("Subdivisions"), 8); + const float radius = params.extract_input<float>("Radius"); + const float3 location = params.extract_input<float3>("Translation"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_ico_sphere_mesh(location, rotation, subdivisions, radius); + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_ico_sphere() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Ico Sphere", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_ico_sphere_in, geo_node_mesh_primitive_ico_sphere_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_ico_sphere_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc new file mode 100644 index 00000000000..6486e310008 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -0,0 +1,289 @@ +/* + * 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_map.hh" +#include "BLI_math_matrix.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_in[] = { + {SOCK_INT, N_("Segments"), 32, 0.0f, 0.0f, 0.0f, 3, 1024}, + {SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 3, 1024}, + {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static int sphere_vert_total(const int segments, const int rings) +{ + return segments * (rings - 1) + 2; +} + +static int sphere_edge_total(const int segments, const int rings) +{ + return segments * (rings * 2 - 1); +} + +static int sphere_corner_total(const int segments, const int rings) +{ + const int quad_corners = 4 * segments * (rings - 2); + const int tri_corners = 3 * segments * 2; + return quad_corners + tri_corners; +} + +static int sphere_face_total(const int segments, const int rings) +{ + const int quads = segments * (rings - 2); + const int triangles = segments * 2; + return quads + triangles; +} + +static void calculate_sphere_vertex_data(MutableSpan<MVert> verts, + const float radius, + const int segments, + const int rings) +{ + const float delta_theta = M_PI / rings; + const float delta_phi = (2 * M_PI) / segments; + + copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius)); + normal_float_to_short_v3(verts[0].no, float3(0.0f, 0.0f, 1.0f)); + + int vert_index = 1; + float theta = delta_theta; + for (const int UNUSED(ring) : IndexRange(rings - 1)) { + float phi = 0.0f; + const float z = cosf(theta); + for (const int UNUSED(segment) : IndexRange(segments)) { + const float x = sinf(theta) * cosf(phi); + const float y = sinf(theta) * sinf(phi); + copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius); + normal_float_to_short_v3(verts[vert_index].no, float3(x, y, z)); + phi += delta_phi; + vert_index++; + } + theta += delta_theta; + } + + copy_v3_v3(verts.last().co, float3(0.0f, 0.0f, -radius)); + normal_float_to_short_v3(verts.last().no, float3(0.0f, 0.0f, -1.0f)); +} + +static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges, + const int segments, + const int rings) +{ + int edge_index = 0; + + /* Add the edges connecting the top vertex to the first ring. */ + const int first_vert_ring_index_start = 1; + for (const int segment : IndexRange(segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = 0; + edge.v2 = first_vert_ring_index_start + segment; + } + + int ring_vert_index_start = 1; + for (const int ring : IndexRange(rings - 1)) { + const int next_ring_vert_index_start = ring_vert_index_start + segments; + + /* Add the edges running along each ring. */ + for (const int segment : IndexRange(segments)) { + MEdge &edge_in_ring = edges[edge_index++]; + edge_in_ring.v1 = ring_vert_index_start + segment; + edge_in_ring.v2 = ring_vert_index_start + ((segment + 1) % segments); + } + + /* Add the edges connecting to the next ring. */ + if (ring < rings - 2) { + for (const int segment : IndexRange(segments)) { + MEdge &edge_to_next_ring = edges[edge_index++]; + edge_to_next_ring.v1 = ring_vert_index_start + segment; + edge_to_next_ring.v2 = next_ring_vert_index_start + segment; + } + } + ring_vert_index_start += segments; + } + + /* Add the edges connecting the last ring to the bottom vertex. */ + const int last_vert_index = sphere_vert_total(segments, rings) - 1; + const int last_vert_ring_start = last_vert_index - segments; + for (const int segment : IndexRange(segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = last_vert_index; + edge.v2 = last_vert_ring_start + segment; + } +} + +static void calculate_sphere_faces(MutableSpan<MLoop> loops, + MutableSpan<MPoly> polys, + const int segments, + const int rings) +{ + int loop_index = 0; + int poly_index = 0; + + /* Add the triangles conntected to the top vertex. */ + const int first_vert_ring_index_start = 1; + for (const int segment : IndexRange(segments)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 3; + + MLoop &loop_a = loops[loop_index++]; + loop_a.v = 0; + loop_a.e = segment; + + MLoop &loop_b = loops[loop_index++]; + loop_b.v = first_vert_ring_index_start + segment; + loop_b.e = segments + segment; + + MLoop &loop_c = loops[loop_index++]; + loop_c.v = first_vert_ring_index_start + (segment + 1) % segments; + loop_c.e = (segment + 1) % segments; + } + + int ring_vert_index_start = 1; + int ring_edge_index_start = segments; + for (const int UNUSED(ring) : IndexRange(rings - 2)) { + const int next_ring_vert_index_start = ring_vert_index_start + segments; + const int next_ring_edge_index_start = ring_edge_index_start + segments * 2; + const int ring_vertical_edge_index_start = ring_edge_index_start + segments; + + for (const int segment : IndexRange(segments)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 4; + + MLoop &loop_a = loops[loop_index++]; + loop_a.v = ring_vert_index_start + segment; + loop_a.e = ring_vertical_edge_index_start + segment; + + MLoop &loop_b = loops[loop_index++]; + loop_b.v = next_ring_vert_index_start + segment; + loop_b.e = next_ring_edge_index_start + segment; + + MLoop &loop_c = loops[loop_index++]; + loop_c.v = next_ring_vert_index_start + ((segment + 1) % segments); + loop_c.e = ring_vertical_edge_index_start + ((segment + 1) % segments); + + MLoop &loop_d = loops[loop_index++]; + loop_d.v = ring_vert_index_start + ((segment + 1) % segments); + loop_d.e = ring_edge_index_start + segment; + } + ring_vert_index_start += segments; + ring_edge_index_start += segments * 2; + } + + /* Add the triangles connected to the bottom vertex. */ + const int last_edge_ring_start = segments * (rings - 2) * 2 + segments; + const int bottom_edge_fan_start = last_edge_ring_start + segments; + const int last_vert_index = sphere_vert_total(segments, rings) - 1; + const int last_vert_ring_start = last_vert_index - segments; + for (const int segment : IndexRange(segments)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 3; + + MLoop &loop_a = loops[loop_index++]; + loop_a.v = last_vert_index; + loop_a.e = bottom_edge_fan_start + ((segment + 1) % segments); + + MLoop &loop_b = loops[loop_index++]; + loop_b.v = last_vert_ring_start + (segment + 1) % segments; + loop_b.e = last_edge_ring_start + segment; + + MLoop &loop_c = loops[loop_index++]; + loop_c.v = last_vert_ring_start + segment; + loop_c.e = bottom_edge_fan_start + segment; + } +} + +static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings) +{ + Mesh *mesh = BKE_mesh_new_nomain(sphere_vert_total(segments, rings), + sphere_edge_total(segments, rings), + 0, + sphere_corner_total(segments, rings), + sphere_face_total(segments, rings)); + MutableSpan<MVert> verts = MutableSpan<MVert>(mesh->mvert, mesh->totvert); + MutableSpan<MEdge> edges = MutableSpan<MEdge>(mesh->medge, mesh->totedge); + MutableSpan<MLoop> loops = MutableSpan<MLoop>(mesh->mloop, mesh->totloop); + MutableSpan<MPoly> polys = MutableSpan<MPoly>(mesh->mpoly, mesh->totpoly); + + calculate_sphere_vertex_data(verts, radius, segments, rings); + + calculate_sphere_edge_indices(edges, segments, rings); + + calculate_sphere_faces(loops, polys, segments, rings); + + BLI_assert(BKE_mesh_is_valid(mesh)); + + return mesh; +} + +static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) +{ + const int segments_num = params.extract_input<int>("Segments"); + const int rings_num = params.extract_input<int>("Rings"); + if (segments_num < 3 || rings_num < 3) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const float radius = params.extract_input<float>("Radius"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num); + BLI_assert(BKE_mesh_is_valid(mesh)); + + if (!location.is_zero() || !rotation.is_zero()) { + transform_mesh(mesh, location, rotation, float3(1)); + } + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_uv_sphere() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "UV Sphere", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_uv_sphere_in, geo_node_mesh_primitive_uv_sphere_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_uv_sphere_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 539a7551be9..341bb4c0472 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -20,6 +20,8 @@ #include "BLI_math_matrix.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" #include "DNA_volume_types.h" @@ -57,20 +59,34 @@ static bool use_translate(const float3 rotation, const float3 scale) return true; } -static void transform_mesh(Mesh *mesh, - const float3 translation, - const float3 rotation, - const float3 scale) +void transform_mesh(Mesh *mesh, + const float3 translation, + const float3 rotation, + const float3 scale) { /* Use only translation if rotation and scale are zero. */ if (use_translate(rotation, scale)) { BKE_mesh_translate(mesh, translation, true); } else { - float mat[4][4]; - loc_eul_size_to_mat4(mat, translation, rotation, scale); - BKE_mesh_transform(mesh, mat, true); - BKE_mesh_calc_normals(mesh); + float4x4 matrix; + loc_eul_size_to_mat4(matrix.values, translation, rotation, scale); + BKE_mesh_transform(mesh, matrix.values, true); + + /* https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/ + * geometry/transforming-normals */ + float rot[3][3]; + eul_to_mat3(rot, rotation); + if (!invert_m3(rot)) { + return; + } + transpose_m3(rot); + for (MVert &vert : MutableSpan(mesh->mvert, mesh->totvert)) { + float3 normal; + normal_short_to_float_v3(normal, vert.no); + mul_m3_v3(rot, normal); + normal_float_to_short_v3(vert.no, normal); + } } } diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 60c2d6c37e1..2d60e959c44 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -712,6 +712,7 @@ void register_standard_node_socket_types(void) nodeRegisterSocketType(make_socket_type_float(PROP_FACTOR)); nodeRegisterSocketType(make_socket_type_float(PROP_ANGLE)); nodeRegisterSocketType(make_socket_type_float(PROP_TIME)); + nodeRegisterSocketType(make_socket_type_float(PROP_DISTANCE)); nodeRegisterSocketType(make_socket_type_int(PROP_NONE)); nodeRegisterSocketType(make_socket_type_int(PROP_UNSIGNED)); |