1
1

Compare commits

...

4 Commits

Author SHA1 Message Date
7ff375b212 Simplified runtime node type definition. 2021-08-26 09:59:10 +01:00
df05fae126 Merge branch 'master' into temp-runtime-node-def 2021-08-25 07:55:50 +01:00
4793154d0d Revert cleanup changes to node DNA.
This was used a some point to get a clear type for the node flags enum,
but isn't a necessary change right now. Node DNA cleanup should be done
in a separate patch.
2021-08-23 09:10:06 +01:00
4af555a031 Utility classes for compact node definitions in C++.
Node definitions in C++ are currently spread out over a large number of
files all over the code base (nodes, DNA, RNA, UI). By contrast, python
nodes are very compact and can be added much more easily. To make node
definitions in C++ more convenient, this patch adds some utility
functions that allow defining type properties and callbacks of a node
in a single file.

The NodeDefinition template takes a struct (which should usually also be
a child class of the template) and finds static fields and functions of
the template argument to use for the node type. The "Mesh Primitive
Ellipse" node has been added as an example case, it may not end up being
used. Other existing node types are unaffected, this patch only provides
alternative ways to define a node.

Known limitations:
- Only ID properties can be added in the node source files without
  additional DNA structs. This should be sufficient for the vast
  majority of nodes, but can be augmented with conventional DNA structs
  in node->storage if necessary.
- Runtime node definitions are identified only by their idname, they do
  not have a fixed integer type. This has to be taken into account for
  versioning.
- Sockets are currently added in the init function. The "template"
  system is not supported and a better alternative should be added
  eventually.
2021-08-22 17:05:30 +01:00
10 changed files with 483 additions and 0 deletions

View File

@@ -560,6 +560,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeMeshCone"),
NodeItem("GeometryNodeMeshCube"),
NodeItem("GeometryNodeMeshCylinder"),
NodeItem("GeometryNodeMeshEllipse"),
NodeItem("GeometryNodeMeshGrid"),
NodeItem("GeometryNodeMeshIcoSphere"),
NodeItem("GeometryNodeMeshLine"),

View File

@@ -5165,6 +5165,7 @@ static void registerGeometryNodes()
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_ellipse();
register_node_type_geo_mesh_primitive_grid();
register_node_type_geo_mesh_primitive_ico_sphere();
register_node_type_geo_mesh_primitive_line();

View File

@@ -193,6 +193,7 @@ set(SRC
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_ellipse.cc
geometry/nodes/node_geo_mesh_primitive_grid.cc
geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
geometry/nodes/node_geo_mesh_primitive_line.cc
@@ -346,6 +347,7 @@ set(SRC
intern/node_exec.cc
intern/node_geometry_exec.cc
intern/node_multi_function.cc
intern/node_runtime_types.cc
intern/node_socket.cc
intern/node_tree_ref.cc
intern/node_util.c
@@ -367,6 +369,7 @@ set(SRC
NOD_math_functions.hh
NOD_multi_function.hh
NOD_node_tree_ref.hh
NOD_runtime_types.hh
NOD_shader.h
NOD_socket.h
NOD_static_types.h

View File

@@ -79,6 +79,7 @@ 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_ellipse(void);
void register_node_type_geo_mesh_primitive_grid(void);
void register_node_type_geo_mesh_primitive_ico_sphere(void);
void register_node_type_geo_mesh_primitive_line(void);

View File

@@ -239,6 +239,14 @@ class GeoNodeExecParams {
return *provider_->dnode->bnode();
}
/**
* Get the node tree containing the executed node.
*/
const bNodeTree &node_tree() const
{
return *provider_->dnode->btree();
}
const Object *self_object() const
{
return provider_->self_object;

View File

@@ -0,0 +1,70 @@
/*
* 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.
*/
/** \file
* \ingroup nodes
*/
#pragma once
#include <type_traits>
#include "BKE_node.h"
#include "RNA_access.h"
#include "RNA_types.h"
#include "UI_resources.h"
/** \file
* \ingroup fn
*
* Utility functions for registering node types at runtime.
*
* Defining nodes here does not require compile-time DNA (makesdna) or RNA (makesrna).
* Nodes can use ID properties and runtime RNA definition.
*
* Node types can be registered using a C++ class with static fields and functions.
* Functions are plain C callbacks, not actual class methods.
* The register function detects missing optional fields and falls back on default values.
*/
#ifdef __cplusplus
extern "C" {
#endif
/**
* Define a basic runtime node type.
* A custom RNA struct is declared for the type.
* The node type is not registered here.
*/
void node_make_runtime_type(struct bNodeType *ntype,
const char *idname,
const char *ui_name,
const char *ui_description,
int ui_icon,
short node_class,
const StructRNA *rna_base);
/**
* Free runtime type information of the node type.
* The node type is not unregistered here.
*/
void node_free_runtime_type(struct bNodeType *ntype);
#ifdef __cplusplus
}
#endif

View File

@@ -28,6 +28,20 @@ namespace blender::nodes {
using bke::GeometryInstanceGroup;
void geometry_node_make_runtime_type(bNodeType *ntype,
const char *idname,
const char *ui_name,
const char *ui_description,
int ui_icon,
short node_class,
const StructRNA *rna_base)
{
node_make_runtime_type(ntype, idname, ui_name, ui_description, ui_icon, node_class, rna_base);
/* Default poll function for geometry nodes. */
ntype->poll = geo_node_poll_default;
}
/**
* Update the availability of a group of input sockets with the same name,
* used for switching between attribute inputs or single values.

View File

@@ -31,6 +31,9 @@
#include "NOD_geometry.h"
#include "NOD_geometry_exec.hh"
#include "NOD_runtime_types.hh"
#include "UI_resources.h"
#include "node_util.h"
@@ -41,6 +44,15 @@ bool geo_node_poll_default(struct bNodeType *ntype,
const char **r_disabled_hint);
namespace blender::nodes {
void geometry_node_make_runtime_type(struct bNodeType *ntype,
const char *idname,
const char *ui_name,
const char *ui_description,
int ui_icon,
short node_class,
const StructRNA *rna_base);
void update_attribute_input_socket_availabilities(bNode &node,
const StringRef name,
const GeometryNodeAttributeInputMode mode,

View File

@@ -0,0 +1,286 @@
/*
* 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_material.h"
#include "BKE_mesh.h"
#include "RNA_define.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_runtime_types.hh"
#include "node_geometry_util.hh"
namespace blender::nodes {
enum FillType {
FILL_NONE = 0,
FILL_NGON = 1,
FILL_TRIANGLE_FAN = 2,
};
static void node_init(bNodeTree *ntree, bNode *node)
{
PointerRNA ptr;
RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
RNA_enum_set(&ptr, "fill_type", FILL_NONE);
{
bNodeSocketValueInt *dval = (bNodeSocketValueInt *)nodeAddSocket(
ntree, node, SOCK_IN, "NodeSocketInt", "Vertices", "Vertices")
->default_value;
dval->value = 32;
dval->min = 3;
dval->max = 4096;
}
{
bNodeSocketValueFloat *dval =
(bNodeSocketValueFloat *)nodeAddSocket(
ntree, node, SOCK_IN, "NodeSocketFloat", "Radius A", "Radius A")
->default_value;
dval->value = 1.0f;
dval->min = 0.0f;
dval->max = FLT_MAX;
dval->subtype = PROP_DISTANCE;
}
{
bNodeSocketValueFloat *dval =
(bNodeSocketValueFloat *)nodeAddSocket(
ntree, node, SOCK_IN, "NodeSocketFloat", "Radius B", "Radius B")
->default_value;
dval->value = 1.0f;
dval->min = 0.0f;
dval->max = FLT_MAX;
dval->subtype = PROP_DISTANCE;
}
nodeAddSocket(ntree, node, SOCK_OUT, "NodeSocketGeometry", "Geometry", "Geometry");
}
static void node_draw_buttons(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE);
}
static int circle_vert_total(const FillType 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_unreachable();
return 0;
}
static int circle_edge_total(const FillType 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_unreachable();
return 0;
}
static int circle_corner_total(const FillType 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_unreachable();
return 0;
}
static int circle_face_total(const FillType 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_unreachable();
return 0;
}
static Mesh *create_ellipse_mesh(const float radius_a,
const float radius_b,
const int verts_num,
const FillType 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));
BKE_id_material_eval_ensure_default_slot(&mesh->id);
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};
/* Assign vertex coordinates. */
const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num));
for (const int i : IndexRange(verts_num)) {
const float angle = i * angle_delta;
copy_v3_v3(verts[i].co, float3(std::cos(angle) * radius_a, std::sin(angle) * radius_b, 0.0f));
}
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;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
/* 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 geometry_node_execute(GeoNodeExecParams params)
{
PointerRNA ptr;
RNA_pointer_create((ID *)&params.node_tree(), &RNA_Node, (void *)&params.node(), &ptr);
const FillType fill_type = (FillType)RNA_enum_get(&ptr, "fill_type");
const float radius_a = params.extract_input<float>("Radius A");
const float radius_b = params.extract_input<float>("Radius B");
const int verts_num = params.extract_input<int>("Vertices");
if (verts_num < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
params.set_output("Geometry", GeometrySet());
return;
}
Mesh *mesh = create_ellipse_mesh(radius_a, radius_b, verts_num, fill_type);
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_ellipse()
{
static bNodeType ntype;
node_make_runtime_type(&ntype,
"GeometryNodeMeshEllipse",
"Mesh Ellipse",
"Create an elliptical shape",
ICON_NONE,
NODE_CLASS_GEOMETRY,
&RNA_GeometryNode);
node_type_init(&ntype, blender::nodes::node_init);
ntype.geometry_node_execute = blender::nodes::geometry_node_execute;
ntype.draw_buttons = blender::nodes::node_draw_buttons;
static EnumPropertyItem fill_type_items[] = {
{blender::nodes::FILL_NONE, "NONE", 0, "None", ""},
{blender::nodes::FILL_NGON, "NGON", 0, "N-Gon", ""},
{blender::nodes::FILL_TRIANGLE_FAN, "TRIANGLE_FAN", 0, "Triangles", ""},
{0, NULL, 0, NULL, NULL},
};
StructRNA *srna = ntype.rna_ext.srna;
RNA_def_enum(srna,
"fill_type",
fill_type_items,
blender::nodes::FILL_NONE,
"Fill Type",
"");
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,87 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup nodes
*/
#include "NOD_runtime_types.hh"
#include "BLI_math.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "RNA_define.h"
#include "ED_node.h"
#include "node_util.h"
static bool custom_node_poll_default(bNodeType *UNUSED(ntype),
bNodeTree *UNUSED(ntree),
const char **UNUSED(disabled_hint))
{
return true;
}
static bool custom_node_poll_instance(bNode *node,
bNodeTree *nodetree,
const char **r_disabled_hint)
{
return node->typeinfo->poll(node->typeinfo, nodetree, r_disabled_hint);
}
void node_make_runtime_type(bNodeType *ntype,
const char *idname,
const char *ui_name,
const char *ui_description,
int ui_icon,
short node_class,
const StructRNA *rna_base)
{
const short node_flags = 0;
/* Basic type setup. */
node_type_base_custom(ntype, idname, ui_name, node_class, node_flags);
BLI_strncpy(ntype->ui_description, ui_description, sizeof(ntype->ui_description));
ntype->ui_icon = ui_icon;
/* RNA runtime type declaration. */
ntype->rna_ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, idname, (StructRNA *)rna_base);
RNA_struct_blender_type_set(ntype->rna_ext.srna, ntype);
RNA_def_struct_ui_text(ntype->rna_ext.srna, ntype->ui_name, ntype->ui_description);
RNA_def_struct_ui_icon(ntype->rna_ext.srna, ntype->ui_icon);
/* Default BKE callbacks. */
ntype->poll = custom_node_poll_default;
ntype->poll_instance = custom_node_poll_instance;
ntype->insert_link = node_insert_link_default;
ntype->update_internal_links = node_update_internal_links_default;
/* Default UI callbacks. */
ED_init_custom_node_type(ntype);
}
void node_free_runtime_type(bNodeType *ntype)
{
if (!ntype) {
return;
}
RNA_struct_free_extension(ntype->rna_ext.srna, &ntype->rna_ext);
RNA_struct_free(&BLENDER_RNA, ntype->rna_ext.srna);
}