Blender V4.3
node_geo_subdivision_surface.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_task.hh"
6
8
9#include "BKE_attribute.hh"
10#include "BKE_lib_id.hh"
11#include "BKE_mesh.hh"
12#include "BKE_subdiv.hh"
13#include "BKE_subdiv_mesh.hh"
14
15#include "UI_interface.hh"
16#include "UI_resources.hh"
17
18#include "RNA_enum_types.hh"
19
20#include "NOD_rna_define.hh"
21
22#include "GEO_randomize.hh"
23
24#include "node_geometry_util.hh"
25
27
29
31{
32 b.add_input<decl::Geometry>("Mesh").supported_type(GeometryComponent::Type::Mesh);
33 b.add_input<decl::Int>("Level").default_value(1).min(0).max(6);
34 b.add_input<decl::Float>("Edge Crease")
35 .default_value(0.0f)
36 .min(0.0f)
37 .max(1.0f)
39 .field_on_all();
40 b.add_input<decl::Float>("Vertex Crease")
41 .default_value(0.0f)
42 .min(0.0f)
43 .max(1.0f)
45 .field_on_all();
46 b.add_output<decl::Geometry>("Mesh").propagate_all();
47}
48
49static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
50{
51 uiItemR(layout, ptr, "uv_smooth", UI_ITEM_NONE, "", ICON_NONE);
52 uiItemR(layout, ptr, "boundary_smooth", UI_ITEM_NONE, "", ICON_NONE);
53}
54
55static void node_init(bNodeTree * /*tree*/, bNode *node)
56{
57 NodeGeometrySubdivisionSurface *data = MEM_cnew<NodeGeometrySubdivisionSurface>(__func__);
59 data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL;
60 node->storage = data;
61}
62
63#ifdef WITH_OPENSUBDIV
64
65static void write_vert_creases(Mesh &mesh, const VArray<float> &creases)
66{
67 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
68 attributes.remove("crease_vert");
69 attributes.add<float>("crease_vert", AttrDomain::Point, bke::AttributeInitVArray(creases));
70}
71
72static void write_edge_creases(Mesh &mesh, const VArray<float> &creases)
73{
74 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
75 attributes.remove("crease_edge");
76 attributes.add<float>("crease_edge", AttrDomain::Edge, bke::AttributeInitVArray(creases));
77}
78
79static bool varray_is_single_zero(const VArray<float> &varray)
80{
81 if (const std::optional<float> value = varray.get_if_single()) {
82 return *value == 0.0f;
83 }
84 return false;
85}
86
87static fn::Field<float> clamp_crease(fn::Field<float> crease_field)
88{
89 static auto clamp_fn = mf::build::SI1_SO<float, float>(
90 "Clamp",
91 [](float value) { return std::clamp(value, 0.0f, 1.0f); },
92 mf::build::exec_presets::AllSpanOrSingle());
93 return fn::Field<float>(fn::FieldOperation::Create(clamp_fn, {std::move(crease_field)}));
94}
95
96static Mesh *mesh_subsurf_calc(const Mesh *mesh,
97 const int level,
98 const Field<float> &vert_crease_field,
99 const Field<float> &edge_crease_field,
100 const int boundary_smooth,
101 const int uv_smooth)
102{
103 const bke::MeshFieldContext point_context{*mesh, AttrDomain::Point};
104 FieldEvaluator point_evaluator(point_context, mesh->verts_num);
105 point_evaluator.add(clamp_crease(vert_crease_field));
106 point_evaluator.evaluate();
107
108 const bke::MeshFieldContext edge_context{*mesh, AttrDomain::Edge};
109 FieldEvaluator edge_evaluator(edge_context, mesh->edges_num);
110 edge_evaluator.add(clamp_crease(edge_crease_field));
111 edge_evaluator.evaluate();
112
113 const VArray<float> vert_creases = point_evaluator.get_evaluated<float>(0);
114 const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0);
115 const bool use_creases = !varray_is_single_zero(vert_creases) ||
116 !varray_is_single_zero(edge_creases);
117
118 Mesh *mesh_copy = nullptr;
119 if (use_creases) {
120 /* Due to the "BKE_subdiv" API, the crease layers must be on the input mesh. But in this node
121 * they are provided as separate inputs, not as custom data layers. When needed, retrieve the
122 * mesh with write access and store the new crease values there. */
123 mesh_copy = BKE_mesh_copy_for_eval(*mesh);
124 write_vert_creases(*mesh_copy, vert_creases);
125 write_edge_creases(*mesh_copy, edge_creases);
126 mesh = mesh_copy;
127 }
128
129 bke::subdiv::ToMeshSettings mesh_settings;
130 mesh_settings.resolution = (1 << level) + 1;
131 mesh_settings.use_optimal_display = false;
132
133 bke::subdiv::Settings subdiv_settings;
134 subdiv_settings.is_simple = false;
135 subdiv_settings.is_adaptive = false;
136 subdiv_settings.use_creases = use_creases;
137 subdiv_settings.level = level;
138 subdiv_settings.vtx_boundary_interpolation =
140 subdiv_settings.fvar_linear_interpolation = bke::subdiv::fvar_interpolation_from_uv_smooth(
141 uv_smooth);
142
143 bke::subdiv::Subdiv *subdiv = bke::subdiv::new_from_mesh(&subdiv_settings, mesh);
144 if (!subdiv) {
145 return nullptr;
146 }
147
148 Mesh *result = bke::subdiv::subdiv_to_mesh(subdiv, &mesh_settings, mesh);
149 bke::subdiv::free(subdiv);
150
151 if (use_creases) {
152 /* Remove the layer in case it was created by the node from the field input. The fact
153 * that this node uses attributes to input creases to the subdivision code is meant to be
154 * an implementation detail ideally. */
155 result->attributes_for_write().remove("crease_vert");
156 result->attributes_for_write().remove("crease_edge");
157 }
158
159 if (mesh_copy) {
160 BKE_id_free(nullptr, mesh_copy);
161 }
162
164
165 return result;
166}
167
168#endif
169
171{
172 GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
173#ifdef WITH_OPENSUBDIV
174 const Field<float> vert_crease = params.extract_input<Field<float>>("Vertex Crease");
175 const Field<float> edge_crease = params.extract_input<Field<float>>("Edge Crease");
176
177 const NodeGeometrySubdivisionSurface &storage = node_storage(params.node());
178 const int uv_smooth = storage.uv_smooth;
179 const int boundary_smooth = storage.boundary_smooth;
180 const int level = std::clamp(params.extract_input<int>("Level"), 0, 11);
181 if (level == 0) {
182 params.set_output("Mesh", std::move(geometry_set));
183 return;
184 }
185
186 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
187 if (const Mesh *mesh = geometry_set.get_mesh()) {
188 geometry_set.replace_mesh(
189 mesh_subsurf_calc(mesh, level, vert_crease, edge_crease, boundary_smooth, uv_smooth));
190 }
191 });
192#else
193 params.error_message_add(NodeWarningType::Error,
194 TIP_("Disabled, Blender was compiled without OpenSubdiv"));
195
196#endif
197 params.set_output("Mesh", std::move(geometry_set));
198}
199
200static void node_rna(StructRNA *srna)
201{
203 "uv_smooth",
204 "UV Smooth",
205 "Controls how smoothing is applied to UVs",
209 nullptr,
210 true);
211
213 "boundary_smooth",
214 "Boundary Smooth",
215 "Controls how open boundaries are smoothed",
217 NOD_storage_enum_accessors(boundary_smooth),
219 nullptr,
220 true);
221}
222
223static void node_register()
224{
225 static blender::bke::bNodeType ntype;
226
228 &ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY);
229 ntype.declare = node_declare;
232 ntype.initfunc = node_init;
235 "NodeGeometrySubdivisionSurface",
239
240 node_rna(ntype.rna_ext.srna);
241}
243
244} // namespace blender::nodes::node_geo_subdivision_surface_cc
void BKE_id_free(Main *bmain, void *idv)
Mesh * BKE_mesh_copy_for_eval(const Mesh &source)
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
#define TIP_(msgid)
@ SUBSURF_BOUNDARY_SMOOTH_ALL
@ SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
@ PROP_FACTOR
Definition RNA_types.hh:154
#define UI_ITEM_NONE
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
bool remove(const StringRef attribute_id)
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:244
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void free(Subdiv *subdiv)
Definition subdiv.cc:192
Subdiv * new_from_mesh(const Settings *settings, const Mesh *mesh)
Definition subdiv.cc:133
FVarLinearInterpolation fvar_interpolation_from_uv_smooth(int uv_smooth)
Definition subdiv.cc:49
VtxBoundaryInterpolation vtx_boundary_interpolation_from_subsurf(int boundary_smooth)
Definition subdiv.cc:69
Mesh * subdiv_to_mesh(Subdiv *subdiv, const ToMeshSettings *settings, const Mesh *coarse_mesh)
void node_type_size_preset(bNodeType *ntype, eNodeSizePreset size)
Definition node.cc:4614
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
void debug_randomize_mesh_order(Mesh *mesh)
Definition randomize.cc:220
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:46
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:58
const EnumPropertyItem rna_enum_subdivision_uv_smooth_items[]
const EnumPropertyItem rna_enum_subdivision_boundary_smooth_items[]
StructRNA * srna
Definition RNA_types.hh:780
const Mesh * get_mesh() const
void modify_geometry_sets(ForeachSubGeometryCallback callback)
Defines a node type.
Definition BKE_node.hh:218
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeDeclareFunction declare
Definition BKE_node.hh:347
PointerRNA * ptr
Definition wm_files.cc:4126