Geometry Nodes: Add initial version of mesh primitives

This commit includes nodes to build the following primitives:
 - Cone
 - Cylinder
 - Circle
 - Cube
 - UV Sphere
 - Ico Sphere
 - Line
 - Plane/Grid

In general the inputs are the same as the corresponding operators
in the 3D view.

**Line Primitive**
The line primitive has two modes-- adding vertices between two end
points, or adding vertices each at an offset from the start point.
For the former mode, there is a choice between a vertex count
and a distance between each point.

**Plane Primitive**
This commit includes the "Plane" and "Grid" primitives as one node.
Generally primitives are named after the simpler form of the shape they
create (i.e. "Cone" can make some more complex shapes). Also, generally
you want to tweak the number of subdivisions anyway, so defaulting to
plane is not an inconvenience. And generally having fewer redundant
base primitives is better.

**Future Improvements**
A following patch proposes to improve the speed of the cylinder, cone,
and sphere primitives: D10730. Additional possible future improvements
would be adding subdivisions to the cube node and rings to the cone
and cylinder nodes.

Differential Revision: https://developer.blender.org/D10715
This commit is contained in:
Hans Goudey 2021-03-16 17:35:12 -04:00
parent 3e87d8a431
commit 9a56a3865c
Notes: blender-bot 2023-02-14 06:21:59 +01:00
Referenced by issue #86391, Mesh Primitive Nodes
18 changed files with 1502 additions and 6 deletions

View File

@ -520,6 +520,16 @@ geometry_node_categories = [
NodeItem("GeometryNodeEdgeSplit"),
NodeItem("GeometryNodeSubdivideSmooth"),
NodeItem("GeometryNodeSubdivide"),
# These should be in a sub-menu, but that requires a refactor to build the add menu manually.
NodeItem("GeometryNodeMeshCube"),
NodeItem("GeometryNodeMeshCircle"),
NodeItem("GeometryNodeMeshUVSphere"),
NodeItem("GeometryNodeMeshIcoSphere"),
NodeItem("GeometryNodeMeshCylinder"),
NodeItem("GeometryNodeMeshCone"),
NodeItem("GeometryNodeMeshLine"),
NodeItem("GeometryNodeMeshPlane"),
]),
GeometryNodeCategory("GEO_POINT", "Point", items=[
NodeItem("GeometryNodePointDistribute"),

View File

@ -1384,6 +1384,14 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_SUBDIVIDE 1029
#define GEO_NODE_ATTRIBUTE_REMOVE 1030
#define GEO_NODE_ATTRIBUTE_CONVERT 1031
#define GEO_NODE_MESH_PRIMITIVE_CUBE 1032
#define GEO_NODE_MESH_PRIMITIVE_CIRCLE 1033
#define GEO_NODE_MESH_PRIMITIVE_UV_SPHERE 1034
#define GEO_NODE_MESH_PRIMITIVE_CYLINDER 1035
#define GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE 1036
#define GEO_NODE_MESH_PRIMITIVE_CONE 1037
#define GEO_NODE_MESH_PRIMITIVE_LINE 1038
#define GEO_NODE_MESH_PRIMITIVE_PLANE 1039
/** \} */

View File

@ -4811,6 +4811,14 @@ 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_ico_sphere();
register_node_type_geo_mesh_primitive_line();
register_node_type_geo_mesh_primitive_plane();
register_node_type_geo_mesh_primitive_uv_sphere();
register_node_type_geo_object_info();
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();

View File

@ -1258,6 +1258,28 @@ typedef struct NodeAttributeConvert {
int16_t domain;
} NodeAttributeConvert;
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;
typedef struct NodeGeometryMeshLine {
/* GeometryNodeMeshLineMode. */
uint8_t mode;
/* GeometryNodeMeshLineCountMode. */
uint8_t count_mode;
} NodeGeometryMeshLine;
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@ -1734,6 +1756,22 @@ 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;
typedef enum GeometryNodeMeshLineMode {
GEO_NODE_MESH_LINE_MODE_END_POINTS = 0,
GEO_NODE_MESH_LINE_MODE_OFFSET = 1,
} GeometryNodeMeshLineMode;
typedef enum GeometryNodeMeshLineCountMode {
GEO_NODE_MESH_LINE_COUNT_TOTAL = 0,
GEO_NODE_MESH_LINE_COUNT_RESOLUTION = 1,
} GeometryNodeMeshLineCountMode;
#ifdef __cplusplus
}
#endif

View File

@ -402,8 +402,15 @@ static const EnumPropertyItem node_cryptomatte_layer_name_items[] = {
{0, "CryptoObject", 0, "Object", "Use Object layer"},
{1, "CryptoMaterial", 0, "Material", "Use Material layer"},
{2, "CryptoAsset", 0, "Asset", "Use Asset layer"},
{0, NULL, 0, NULL, NULL}};
{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 \
@ -9437,6 +9444,87 @@ 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 def_geo_mesh_line(StructRNA *srna)
{
PropertyRNA *prop;
static EnumPropertyItem mode_items[] = {
{GEO_NODE_MESH_LINE_MODE_OFFSET,
"OFFSET",
0,
"Offset",
"Specify the offset from one vertex to the next"},
{GEO_NODE_MESH_LINE_MODE_END_POINTS,
"END_POINTS",
0,
"End Points",
"Specify the line's start and end points"},
{0, NULL, 0, NULL, NULL},
};
static EnumPropertyItem count_mode_items[] = {
{GEO_NODE_MESH_LINE_COUNT_TOTAL,
"TOTAL",
0,
"Count",
"Specify the total number of vertices"},
{GEO_NODE_MESH_LINE_COUNT_RESOLUTION,
"RESOLUTION",
0,
"Resolution",
"Specify the distance between vertices"},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryMeshLine", "storage");
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, mode_items);
RNA_def_property_ui_text(prop, "Mode", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "count_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, count_mode_items);
RNA_def_property_ui_text(prop, "Count Mode", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)

View File

@ -162,6 +162,14 @@ 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_line.cc
geometry/nodes/node_geo_mesh_primitive_plane.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

View File

@ -44,6 +44,14 @@ 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_plane(void);
void register_node_type_geo_mesh_primitive_cylinder(void);
void register_node_type_geo_mesh_primitive_ico_sphere(void);
void register_node_type_geo_mesh_primitive_line(void);
void register_node_type_geo_mesh_primitive_uv_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);

View File

@ -302,6 +302,14 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separat
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_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "")
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", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_PLANE, 0, "MESH_PRIMITIVE_PLANE", MeshPlane, "Plane", "")
/* undefine macros */
#undef DefNode

View File

@ -47,4 +47,17 @@ 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 float3 location,
const float3 rotation,
const float radius_top,
const float radius_bottom,
const float depth,
const int verts_num,
const GeometryNodeMeshCircleFillType fill_type);
} // namespace blender::nodes

View File

@ -0,0 +1,244 @@
/*
* 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 "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, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.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{mesh->mvert, mesh->totvert};
MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
MutableSpan<MPoly> polys{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. */
const 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. */
if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
MPoly &poly = polys[0];
poly.loopstart = 0;
poly.totloop = loops.size();
for (const int i : IndexRange(verts_num)) {
MLoop &loop = loops[i];
loop.e = i;
loop.v = i;
}
}
else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
for (const int i : IndexRange(verts_num)) {
MPoly &poly = polys[i];
poly.loopstart = 3 * i;
poly.totloop = 3;
MLoop &loop_a = loops[3 * i];
loop_a.e = i;
loop_a.v = i;
MLoop &loop_b = loops[3 * i + 1];
loop_b.e = verts_num + ((i + 1) % verts_num);
loop_b.v = (i + 1) % verts_num;
MLoop &loop_c = loops[3 * i + 2];
loop_c.e = verts_num + i;
loop_c.v = verts_num;
}
}
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));
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);
}

View File

@ -0,0 +1,279 @@
/*
* 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 "DNA_meshdata_types.h"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "bmesh.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, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.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 int vert_total(const GeometryNodeMeshCircleFillType fill_type,
const int verts_num,
const bool top_is_point,
const bool bottom_is_point)
{
int vert_total = 0;
if (!top_is_point) {
vert_total += verts_num;
if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
vert_total++;
}
}
else {
vert_total++;
}
if (!bottom_is_point) {
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 top_is_point,
const bool bottom_is_point)
{
if (top_is_point && bottom_is_point) {
return 1;
}
int edge_total = 0;
if (!top_is_point) {
edge_total += verts_num;
if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
edge_total += verts_num;
}
}
edge_total += verts_num;
if (!bottom_is_point) {
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 top_is_point,
const bool bottom_is_point)
{
if (top_is_point && bottom_is_point) {
return 0;
}
int corner_total = 0;
if (!top_is_point) {
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 (!top_is_point && !bottom_is_point) {
corner_total += verts_num * 4;
}
else {
corner_total += verts_num * 3;
}
if (!bottom_is_point) {
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 top_is_point,
const bool bottom_is_point)
{
if (top_is_point && bottom_is_point) {
return 0;
}
int face_total = 0;
if (!top_is_point) {
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 (!bottom_is_point) {
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 float3 location,
const float3 rotation,
const float radius_top,
const float radius_bottom,
const float depth,
const int verts_num,
const GeometryNodeMeshCircleFillType fill_type)
{
float4x4 transform;
loc_eul_size_to_mat4(transform.values, location, rotation, float3(1));
const bool top_is_point = radius_top == 0.0f;
const bool bottom_is_point = radius_bottom == 0.0f;
const BMeshCreateParams bmcp = {true};
const BMAllocTemplate allocsize = {
vert_total(fill_type, verts_num, top_is_point, bottom_is_point),
edge_total(fill_type, verts_num, top_is_point, bottom_is_point),
corner_total(fill_type, verts_num, top_is_point, bottom_is_point),
face_total(fill_type, verts_num, top_is_point, bottom_is_point)};
BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
const bool cap_end = (fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE);
const bool cap_tri = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN);
BMO_op_callf(bm,
BMO_FLAG_DEFAULTS,
"create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b "
"cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
verts_num,
radius_bottom,
radius_top,
cap_end,
cap_tri,
depth,
transform.values,
true);
Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr);
BM_mesh_free(bm);
return mesh;
}
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(
location, rotation, radius_top, radius_bottom, depth, verts_num, fill_type);
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);
}

View File

@ -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_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.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, nullptr);
BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr);
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>("Location");
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);
}

View File

@ -0,0 +1,105 @@
/*
* 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 "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, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.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 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");
/* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */
Mesh *mesh = create_cylinder_or_cone_mesh(
location, rotation, radius, radius, depth, verts_num, fill_type);
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_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);
}

View File

@ -0,0 +1,91 @@
/*
* 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_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.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,
std::abs(radius),
transform.values,
true);
Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr);
BM_mesh_free(bm);
return mesh;
}
static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params)
{
const int subdivisions = std::min(params.extract_input<int>("Subdivisions"), 10);
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_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);
}

View File

@ -0,0 +1,188 @@
/*
* 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_line_in[] = {
{SOCK_INT, N_("Count"), 10, 0.0f, 0.0f, 0.0f, 1, 10000},
{SOCK_FLOAT, N_("Resolution"), 1.0f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE},
{SOCK_VECTOR,
N_("Start Location"),
0.0f,
0.0f,
0.0f,
1.0f,
-FLT_MAX,
FLT_MAX,
PROP_TRANSLATION},
{SOCK_VECTOR, N_("Offset"), 0.0f, 0.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{-1, ""},
};
static bNodeSocketTemplate geo_node_mesh_primitive_line_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_node_mesh_primitive_line_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
if (RNA_enum_get(ptr, "mode") == GEO_NODE_MESH_LINE_MODE_END_POINTS) {
uiItemR(layout, ptr, "count_mode", 0, "", ICON_NONE);
}
}
static void geo_node_mesh_primitive_line_init(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryMeshLine *node_storage = (NodeGeometryMeshLine *)MEM_callocN(
sizeof(NodeGeometryMeshLine), __func__);
node_storage->mode = GEO_NODE_MESH_LINE_MODE_OFFSET;
node_storage->count_mode = GEO_NODE_MESH_LINE_COUNT_TOTAL;
node->storage = node_storage;
}
static void geo_node_mesh_primitive_line_update(bNodeTree *UNUSED(tree), bNode *node)
{
bNodeSocket *count_socket = (bNodeSocket *)node->inputs.first;
bNodeSocket *resolution_socket = count_socket->next;
bNodeSocket *start_socket = resolution_socket->next;
bNodeSocket *end_and_offset_socket = start_socket->next;
const NodeGeometryMeshLine &storage = *(const NodeGeometryMeshLine *)node->storage;
const GeometryNodeMeshLineMode mode = (const GeometryNodeMeshLineMode)storage.mode;
const GeometryNodeMeshLineCountMode count_mode = (const GeometryNodeMeshLineCountMode)
storage.count_mode;
node_sock_label(end_and_offset_socket,
(mode == GEO_NODE_MESH_LINE_MODE_END_POINTS) ? N_("End Location") :
N_("Offset"));
nodeSetSocketAvailability(resolution_socket,
mode == GEO_NODE_MESH_LINE_MODE_END_POINTS &&
count_mode == GEO_NODE_MESH_LINE_COUNT_RESOLUTION);
nodeSetSocketAvailability(count_socket,
mode == GEO_NODE_MESH_LINE_MODE_OFFSET ||
count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL);
}
namespace blender::nodes {
static void fill_edge_data(MutableSpan<MEdge> edges)
{
for (const int i : edges.index_range()) {
edges[i].v1 = i;
edges[i].v2 = i + 1;
edges[i].flag |= ME_LOOSEEDGE;
}
}
static Mesh *create_line_mesh(const float3 start, const float3 delta, const int count)
{
if (count < 1) {
return nullptr;
}
Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0);
MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
short normal[3];
normal_float_to_short_v3(normal, delta.normalized());
float3 co = start;
for (const int i : verts.index_range()) {
copy_v3_v3(verts[i].co, co);
copy_v3_v3_short(verts[i].no, normal);
co += delta;
}
fill_edge_data(edges);
return mesh;
}
static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params)
{
const NodeGeometryMeshLine &storage = *(const NodeGeometryMeshLine *)params.node().storage;
const GeometryNodeMeshLineMode mode = (const GeometryNodeMeshLineMode)storage.mode;
const GeometryNodeMeshLineCountMode count_mode = (const GeometryNodeMeshLineCountMode)
storage.count_mode;
Mesh *mesh = nullptr;
const float3 start = params.extract_input<float3>("Start Location");
if (mode == GEO_NODE_MESH_LINE_MODE_END_POINTS) {
/* The label switches to "End Location", but the same socket is used. */
const float3 end = params.extract_input<float3>("Offset");
const float3 total_delta = end - start;
if (count_mode == GEO_NODE_MESH_LINE_COUNT_RESOLUTION) {
/* Don't allow asymptotic count increase for low resolution values. */
const float resolution = std::max(params.extract_input<float>("Resolution"), 0.0001f);
const int count = total_delta.length() / resolution + 1;
const float3 delta = total_delta.normalized() * resolution;
mesh = create_line_mesh(start, delta, count);
}
else if (count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL) {
const int count = params.extract_input<int>("Count");
if (count > 1) {
const float3 delta = total_delta / (float)(count - 1);
mesh = create_line_mesh(start, delta, count);
}
}
}
else if (mode == GEO_NODE_MESH_LINE_MODE_OFFSET) {
const float3 delta = params.extract_input<float3>("Offset");
const int count = params.extract_input<int>("Count");
mesh = create_line_mesh(start, delta, count);
}
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
void register_node_type_geo_mesh_primitive_line()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_LINE, "Line", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(
&ntype, geo_node_mesh_primitive_line_in, geo_node_mesh_primitive_line_out);
node_type_init(&ntype, geo_node_mesh_primitive_line_init);
node_type_update(&ntype, geo_node_mesh_primitive_line_update);
node_type_storage(
&ntype, "NodeGeometryMeshLine", node_free_standard_storage, node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_line_exec;
ntype.draw_buttons = geo_node_mesh_primitive_line_layout;
nodeRegisterType(&ntype);
}

View File

@ -0,0 +1,183 @@
/*
* 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_plane_in[] = {
{SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
{SOCK_INT, N_("Vertices X"), 10, 0.0f, 0.0f, 0.0f, 2, 1000},
{SOCK_INT, N_("Vertices Y"), 10, 0.0f, 0.0f, 0.0f, 2, 1000},
{SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
{-1, ""},
};
static bNodeSocketTemplate geo_node_mesh_primitive_plane_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void calculate_uvs(Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size)
{
MeshComponent mesh_component;
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
"uv", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
for (const int i : loops.index_range()) {
const float3 &co = verts[loops[i].v].co;
uvs[i].x = (co.x + size) / (size * 2.0f);
uvs[i].y = (co.y + size) / (size * 2.0f);
}
uv_attribute.apply_span_and_save();
}
static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float size)
{
const int edges_x = verts_x - 1;
const int edges_y = verts_y - 1;
Mesh *mesh = BKE_mesh_new_nomain(verts_x * verts_y,
edges_x * verts_y + edges_y * verts_x,
0,
edges_x * edges_y * 4,
edges_x * edges_y);
MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
{
const float dx = size * 2.0f / edges_x;
const float dy = size * 2.0f / edges_y;
float x = -size;
for (const int x_index : IndexRange(verts_x)) {
float y = -size;
for (const int y_index : IndexRange(verts_y)) {
const int vert_index = x_index * verts_y + y_index;
verts[vert_index].co[0] = x;
verts[vert_index].co[1] = y;
verts[vert_index].co[2] = 0.0f;
y += dy;
}
x += dx;
}
}
/* Point all vertex normals in the up direction. */
const short up_normal[3] = {0, 0, SHRT_MAX};
for (MVert &vert : verts) {
copy_v3_v3_short(vert.no, up_normal);
}
/* Build the horizontal edges in the X direction. */
const int y_edges_start = 0;
int edge_index = 0;
for (const int x : IndexRange(verts_x)) {
for (const int y : IndexRange(edges_y)) {
const int vert_index = x * verts_y + y;
MEdge &edge = edges[edge_index++];
edge.v1 = vert_index;
edge.v2 = vert_index + 1;
}
}
/* Build the vertical edges in the Y direction. */
const int x_edges_start = edge_index;
for (const int y : IndexRange(verts_y)) {
for (const int x : IndexRange(edges_x)) {
const int vert_index = x * verts_y + y;
MEdge &edge = edges[edge_index++];
edge.v1 = vert_index;
edge.v2 = vert_index + verts_y;
}
}
int loop_index = 0;
int poly_index = 0;
for (const int x : IndexRange(edges_x)) {
for (const int y : IndexRange(edges_y)) {
MPoly &poly = polys[poly_index++];
poly.loopstart = loop_index;
poly.totloop = 4;
const int vert_index = x * verts_y + y;
MLoop &loop_a = loops[loop_index++];
loop_a.v = vert_index;
loop_a.e = x_edges_start + edges_x * y + x;
MLoop &loop_b = loops[loop_index++];
loop_b.v = vert_index + verts_y;
loop_b.e = y_edges_start + edges_y * (x + 1) + y;
MLoop &loop_c = loops[loop_index++];
loop_c.v = vert_index + verts_y + 1;
loop_c.e = x_edges_start + edges_x * (y + 1) + x;
MLoop &loop_d = loops[loop_index++];
loop_d.v = vert_index + 1;
loop_d.e = y_edges_start + edges_y * x + y;
}
}
calculate_uvs(mesh, verts, loops, size);
return mesh;
}
static void geo_node_mesh_primitive_plane_exec(GeoNodeExecParams params)
{
const float size = params.extract_input<float>("Size");
const int verts_x = params.extract_input<int>("Vertices X");
const int verts_y = params.extract_input<int>("Vertices Y");
const float3 location = params.extract_input<float3>("Location");
const float3 rotation = params.extract_input<float3>("Rotation");
if (verts_x < 2 || verts_y < 2) {
params.set_output("Geometry", GeometrySet());
return;
}
Mesh *mesh = create_plane_mesh(verts_x, verts_y, size);
BLI_assert(BKE_mesh_is_valid(mesh));
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_plane()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_PLANE, "Plane", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(
&ntype, geo_node_mesh_primitive_plane_in, geo_node_mesh_primitive_plane_out);
ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_plane_exec;
nodeRegisterType(&ntype);
}

View File

@ -0,0 +1,131 @@
/*
* 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 "DNA_meshdata_types.h"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "bmesh.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, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.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 Mesh *create_uv_sphere_mesh_bmesh(const float3 location,
const float3 rotation,
const float radius,
const int segments,
const int rings)
{
float4x4 transform;
loc_eul_size_to_mat4(transform.values, location, rotation, float3(radius));
const BMeshCreateParams bmcp = {true};
const BMAllocTemplate allocsize = {sphere_vert_total(segments, rings),
sphere_edge_total(segments, rings),
sphere_corner_total(segments, rings),
sphere_face_total(segments, rings)};
BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
BMO_op_callf(bm,
BMO_FLAG_DEFAULTS,
"create_uvsphere u_segments=%i v_segments=%i diameter=%f matrix=%m4 calc_uvs=%b",
segments,
rings,
radius,
transform.values,
true);
Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr);
BM_mesh_free(bm);
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_bmesh(location, rotation, radius, segments_num, rings_num);
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);
}

View File

@ -57,14 +57,16 @@ 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);
if (!translation.is_zero()) {
BKE_mesh_translate(mesh, translation, true);
}
}
else {
float mat[4][4];