Blender V5.0
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
6
7#include "BKE_attribute.hh"
8#include "BKE_lib_id.hh"
9#include "BKE_mesh.hh"
10#include "BKE_subdiv.hh"
11#include "BKE_subdiv_mesh.hh"
12
13#include "RNA_enum_types.hh"
14
16#include "GEO_randomize.hh"
17
19
20#include "node_geometry_util.hh"
21
23
25
27{
28 b.use_custom_socket_order();
29 b.allow_any_socket_order();
30 b.add_input<decl::Geometry>("Mesh")
31 .supported_type(GeometryComponent::Type::Mesh)
32 .description("Mesh to subdivide");
33 b.add_output<decl::Geometry>("Mesh").propagate_all().align_with_previous();
34 b.add_input<decl::Int>("Level").default_value(1).min(0).max(6);
35 b.add_input<decl::Float>("Edge Crease")
36 .default_value(0.0f)
37 .min(0.0f)
38 .max(1.0f)
39 .subtype(PROP_FACTOR)
40 .field_on_all();
41 b.add_input<decl::Float>("Vertex Crease")
42 .default_value(0.0f)
43 .min(0.0f)
44 .max(1.0f)
45 .subtype(PROP_FACTOR)
46 .field_on_all();
47 b.add_input<decl::Bool>("Limit Surface")
48 .default_value(true)
49 .description(
50 "Place vertices at the surface that would be produced with infinite "
51 "levels of subdivision (smoothest possible shape)");
52 b.add_input<decl::Menu>("UV Smooth")
55 .optional_label()
56 .description("Controls how smoothing is applied to UVs");
57 b.add_input<decl::Menu>("Boundary Smooth")
59 .default_value(SUBSURF_BOUNDARY_SMOOTH_ALL)
60 .optional_label()
61 .description("Controls how open boundaries are smoothed");
62}
63
64static void node_init(bNodeTree * /*tree*/, bNode *node)
65{
66 /* Still used for forward compatibility. */
68}
69
70#ifdef WITH_OPENSUBDIV
71
72static void write_vert_creases(Mesh &mesh, const VArray<float> &creases)
73{
74 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
75 attributes.remove("crease_vert");
76 attributes.add<float>("crease_vert", AttrDomain::Point, bke::AttributeInitVArray(creases));
77}
78
79static void write_edge_creases(Mesh &mesh, const VArray<float> &creases)
80{
81 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
82 attributes.remove("crease_edge");
83 attributes.add<float>("crease_edge", AttrDomain::Edge, bke::AttributeInitVArray(creases));
84}
85
86static bool varray_is_single_zero(const VArray<float> &varray)
87{
88 if (const std::optional<float> value = varray.get_if_single()) {
89 return *value == 0.0f;
90 }
91 return false;
92}
93
94static fn::Field<float> clamp_crease(fn::Field<float> crease_field)
95{
96 static auto clamp_fn = mf::build::SI1_SO<float, float>(
97 "Clamp",
98 [](float value) { return std::clamp(value, 0.0f, 1.0f); },
99 mf::build::exec_presets::AllSpanOrSingle());
100 return fn::Field<float>(fn::FieldOperation::from(clamp_fn, {std::move(crease_field)}));
101}
102
103static Mesh *mesh_subsurf_calc(const Mesh *mesh,
104 const int level,
105 const Field<float> &vert_crease_field,
106 const Field<float> &edge_crease_field,
107 const int boundary_smooth,
108 const int uv_smooth,
109 const bool use_limit_surface)
110{
111 const bke::MeshFieldContext point_context{*mesh, AttrDomain::Point};
112 FieldEvaluator point_evaluator(point_context, mesh->verts_num);
113 point_evaluator.add(clamp_crease(vert_crease_field));
114 point_evaluator.evaluate();
115
116 const bke::MeshFieldContext edge_context{*mesh, AttrDomain::Edge};
117 FieldEvaluator edge_evaluator(edge_context, mesh->edges_num);
118 edge_evaluator.add(clamp_crease(edge_crease_field));
119 edge_evaluator.evaluate();
120
121 const VArray<float> vert_creases = point_evaluator.get_evaluated<float>(0);
122 const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0);
123 const bool use_creases = !varray_is_single_zero(vert_creases) ||
124 !varray_is_single_zero(edge_creases);
125
126 Mesh *mesh_copy = nullptr;
127 if (use_creases) {
128 /* Due to the "BKE_subdiv" API, the crease layers must be on the input mesh. But in this node
129 * they are provided as separate inputs, not as custom data layers. When needed, retrieve the
130 * mesh with write access and store the new crease values there. */
131 mesh_copy = BKE_mesh_copy_for_eval(*mesh);
132 write_vert_creases(*mesh_copy, vert_creases);
133 write_edge_creases(*mesh_copy, edge_creases);
134 mesh = mesh_copy;
135 }
136
137 bke::subdiv::ToMeshSettings mesh_settings;
138 mesh_settings.resolution = (1 << level) + 1;
139 mesh_settings.use_optimal_display = false;
140
141 bke::subdiv::Settings subdiv_settings;
142 subdiv_settings.is_simple = false;
143 subdiv_settings.is_adaptive = use_limit_surface;
144 subdiv_settings.use_creases = use_creases;
145 subdiv_settings.level = level;
146 subdiv_settings.vtx_boundary_interpolation =
148 subdiv_settings.fvar_linear_interpolation = bke::subdiv::fvar_interpolation_from_uv_smooth(
149 uv_smooth);
150
151 bke::subdiv::Subdiv *subdiv = bke::subdiv::new_from_mesh(&subdiv_settings, mesh);
152 if (!subdiv) {
153 return nullptr;
154 }
155
156 Mesh *result = bke::subdiv::subdiv_to_mesh(subdiv, &mesh_settings, mesh);
157 bke::subdiv::free(subdiv);
158
159 if (use_creases) {
160 /* Remove the layer in case it was created by the node from the field input. The fact
161 * that this node uses attributes to input creases to the subdivision code is meant to be
162 * an implementation detail ideally. */
163 result->attributes_for_write().remove("crease_vert");
164 result->attributes_for_write().remove("crease_edge");
165 }
166
167 if (mesh_copy) {
168 BKE_id_free(nullptr, mesh_copy);
169 }
170
172
173 return result;
174}
175
176#endif
177
179{
180 GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
181#ifdef WITH_OPENSUBDIV
182 const Field<float> vert_crease = params.extract_input<Field<float>>("Vertex Crease");
183 const Field<float> edge_crease = params.extract_input<Field<float>>("Edge Crease");
184
185 const int uv_smooth = params.get_input<eSubsurfUVSmooth>("UV Smooth");
186 const int boundary_smooth = params.get_input<eSubsurfBoundarySmooth>("Boundary Smooth");
187 const int level = std::max(params.extract_input<int>("Level"), 0);
188 const bool use_limit_surface = params.extract_input<bool>("Limit Surface");
189 if (level == 0) {
190 params.set_output("Mesh", std::move(geometry_set));
191 return;
192 }
193 /* At this limit, a subdivided single triangle would be too large to be stored in #Mesh. */
194 if (level >= 16) {
195 params.error_message_add(NodeWarningType::Error, TIP_("The subdivision level is too large"));
196 params.set_default_remaining_outputs();
197 return;
198 }
199
200 geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
201 if (const Mesh *mesh = geometry_set.get_mesh()) {
202 geometry_set.replace_mesh(mesh_subsurf_calc(
203 mesh, level, vert_crease, edge_crease, boundary_smooth, uv_smooth, use_limit_surface));
204 }
205 });
206#else
207 params.error_message_add(NodeWarningType::Error,
208 TIP_("Disabled, Blender was compiled without OpenSubdiv"));
209
210#endif
211 params.set_output("Mesh", std::move(geometry_set));
212}
213
214static void node_register()
215{
216 static blender::bke::bNodeType ntype;
217
218 geo_node_type_base(&ntype, "GeometryNodeSubdivisionSurface", GEO_NODE_SUBDIVISION_SURFACE);
219 ntype.ui_name = "Subdivision Surface";
220 ntype.ui_description =
221 "Divide mesh faces to form a smooth surface, using the Catmull-Clark subdivision method";
222 ntype.enum_name_legacy = "SUBDIVISION_SURFACE";
224 ntype.declare = node_declare;
226 ntype.initfunc = node_init;
229 "NodeGeometrySubdivisionSurface",
233}
235
236} // 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:1240
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_SUBDIVISION_SURFACE
#define TIP_(msgid)
struct Mesh Mesh
eSubsurfBoundarySmooth
@ SUBSURF_BOUNDARY_SMOOTH_ALL
eSubsurfUVSmooth
@ SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_FACTOR
Definition RNA_types.hh:251
std::optional< T > get_if_single() const
bool add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
bool remove(const StringRef attribute_id)
static std::shared_ptr< FieldOperation > from(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:242
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void free(Subdiv *subdiv)
Definition subdiv.cc:190
Subdiv * new_from_mesh(const Settings *settings, const Mesh *mesh)
Definition subdiv.cc:131
FVarLinearInterpolation fvar_interpolation_from_uv_smooth(int uv_smooth)
Definition subdiv.cc:47
VtxBoundaryInterpolation vtx_boundary_interpolation_from_subsurf(int boundary_smooth)
Definition subdiv.cc:67
Mesh * subdiv_to_mesh(Subdiv *subdiv, const ToMeshSettings *settings, const Mesh *coarse_mesh)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:5414
void node_type_size_preset(bNodeType &ntype, eNodeSizePreset size)
Definition node.cc:5396
void foreach_real_geometry(bke::GeometrySet &geometry, FunctionRef< void(bke::GeometrySet &geometry_set)> fn)
void debug_randomize_mesh_order(Mesh *mesh)
Definition randomize.cc:288
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:42
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:54
const EnumPropertyItem rna_enum_subdivision_uv_smooth_items[]
const EnumPropertyItem rna_enum_subdivision_boundary_smooth_items[]
#define min(a, b)
Definition sort.cc:36
int edges_num
int verts_num
void * storage
const Mesh * get_mesh() const
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:362
max
Definition text_draw.cc:251