Blender V4.5
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 "UI_interface.hh"
14#include "UI_resources.hh"
15
16#include "RNA_enum_types.hh"
17
18#include "NOD_rna_define.hh"
19
20#include "GEO_randomize.hh"
21
23
24#include "node_geometry_util.hh"
25
27
29
31{
32 b.use_custom_socket_order();
33 b.allow_any_socket_order();
34 b.add_default_layout();
35 b.add_input<decl::Geometry>("Mesh").supported_type(GeometryComponent::Type::Mesh);
36 b.add_output<decl::Geometry>("Mesh").propagate_all().align_with_previous();
37 b.add_input<decl::Int>("Level").default_value(1).min(0).max(6);
38 b.add_input<decl::Float>("Edge Crease")
39 .default_value(0.0f)
40 .min(0.0f)
41 .max(1.0f)
42 .subtype(PROP_FACTOR)
43 .field_on_all();
44 b.add_input<decl::Float>("Vertex Crease")
45 .default_value(0.0f)
46 .min(0.0f)
47 .max(1.0f)
48 .subtype(PROP_FACTOR)
49 .field_on_all();
50 b.add_input<decl::Bool>("Limit Surface")
51 .default_value(true)
52 .description(
53 "Place vertices at the surface that would be produced with infinite "
54 "levels of subdivision (smoothest possible shape)");
55}
56
57static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
58{
59 layout->prop(ptr, "uv_smooth", UI_ITEM_NONE, "", ICON_NONE);
60 layout->prop(ptr, "boundary_smooth", UI_ITEM_NONE, "", ICON_NONE);
61}
62
70
71#ifdef WITH_OPENSUBDIV
72
73static void write_vert_creases(Mesh &mesh, const VArray<float> &creases)
74{
75 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
76 attributes.remove("crease_vert");
77 attributes.add<float>("crease_vert", AttrDomain::Point, bke::AttributeInitVArray(creases));
78}
79
80static void write_edge_creases(Mesh &mesh, const VArray<float> &creases)
81{
82 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
83 attributes.remove("crease_edge");
84 attributes.add<float>("crease_edge", AttrDomain::Edge, bke::AttributeInitVArray(creases));
85}
86
87static bool varray_is_single_zero(const VArray<float> &varray)
88{
89 if (const std::optional<float> value = varray.get_if_single()) {
90 return *value == 0.0f;
91 }
92 return false;
93}
94
95static fn::Field<float> clamp_crease(fn::Field<float> crease_field)
96{
97 static auto clamp_fn = mf::build::SI1_SO<float, float>(
98 "Clamp",
99 [](float value) { return std::clamp(value, 0.0f, 1.0f); },
100 mf::build::exec_presets::AllSpanOrSingle());
101 return fn::Field<float>(fn::FieldOperation::Create(clamp_fn, {std::move(crease_field)}));
102}
103
104static Mesh *mesh_subsurf_calc(const Mesh *mesh,
105 const int level,
106 const Field<float> &vert_crease_field,
107 const Field<float> &edge_crease_field,
108 const int boundary_smooth,
109 const int uv_smooth,
110 const bool use_limit_surface)
111{
112 const bke::MeshFieldContext point_context{*mesh, AttrDomain::Point};
113 FieldEvaluator point_evaluator(point_context, mesh->verts_num);
114 point_evaluator.add(clamp_crease(vert_crease_field));
115 point_evaluator.evaluate();
116
117 const bke::MeshFieldContext edge_context{*mesh, AttrDomain::Edge};
118 FieldEvaluator edge_evaluator(edge_context, mesh->edges_num);
119 edge_evaluator.add(clamp_crease(edge_crease_field));
120 edge_evaluator.evaluate();
121
122 const VArray<float> vert_creases = point_evaluator.get_evaluated<float>(0);
123 const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0);
124 const bool use_creases = !varray_is_single_zero(vert_creases) ||
125 !varray_is_single_zero(edge_creases);
126
127 Mesh *mesh_copy = nullptr;
128 if (use_creases) {
129 /* Due to the "BKE_subdiv" API, the crease layers must be on the input mesh. But in this node
130 * they are provided as separate inputs, not as custom data layers. When needed, retrieve the
131 * mesh with write access and store the new crease values there. */
132 mesh_copy = BKE_mesh_copy_for_eval(*mesh);
133 write_vert_creases(*mesh_copy, vert_creases);
134 write_edge_creases(*mesh_copy, edge_creases);
135 mesh = mesh_copy;
136 }
137
138 bke::subdiv::ToMeshSettings mesh_settings;
139 mesh_settings.resolution = (1 << level) + 1;
140 mesh_settings.use_optimal_display = false;
141
142 bke::subdiv::Settings subdiv_settings;
143 subdiv_settings.is_simple = false;
144 subdiv_settings.is_adaptive = use_limit_surface;
145 subdiv_settings.use_creases = use_creases;
146 subdiv_settings.level = level;
147 subdiv_settings.vtx_boundary_interpolation =
149 subdiv_settings.fvar_linear_interpolation = bke::subdiv::fvar_interpolation_from_uv_smooth(
150 uv_smooth);
151
152 bke::subdiv::Subdiv *subdiv = bke::subdiv::new_from_mesh(&subdiv_settings, mesh);
153 if (!subdiv) {
154 return nullptr;
155 }
156
157 Mesh *result = bke::subdiv::subdiv_to_mesh(subdiv, &mesh_settings, mesh);
158 bke::subdiv::free(subdiv);
159
160 if (use_creases) {
161 /* Remove the layer in case it was created by the node from the field input. The fact
162 * that this node uses attributes to input creases to the subdivision code is meant to be
163 * an implementation detail ideally. */
164 result->attributes_for_write().remove("crease_vert");
165 result->attributes_for_write().remove("crease_edge");
166 }
167
168 if (mesh_copy) {
169 BKE_id_free(nullptr, mesh_copy);
170 }
171
173
174 return result;
175}
176
177#endif
178
180{
181 GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
182#ifdef WITH_OPENSUBDIV
183 const Field<float> vert_crease = params.extract_input<Field<float>>("Vertex Crease");
184 const Field<float> edge_crease = params.extract_input<Field<float>>("Edge Crease");
185
186 const NodeGeometrySubdivisionSurface &storage = node_storage(params.node());
187 const int uv_smooth = storage.uv_smooth;
188 const int boundary_smooth = storage.boundary_smooth;
189 const int level = std::max(params.extract_input<int>("Level"), 0);
190 const bool use_limit_surface = params.extract_input<bool>("Limit Surface");
191 if (level == 0) {
192 params.set_output("Mesh", std::move(geometry_set));
193 return;
194 }
195 /* At this limit, a subdivided single triangle would be too large to be stored in #Mesh. */
196 if (level >= 16) {
197 params.error_message_add(NodeWarningType::Error, TIP_("The subdivision level is too large"));
198 params.set_default_remaining_outputs();
199 return;
200 }
201
202 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
203 if (const Mesh *mesh = geometry_set.get_mesh()) {
204 geometry_set.replace_mesh(mesh_subsurf_calc(
205 mesh, level, vert_crease, edge_crease, boundary_smooth, uv_smooth, use_limit_surface));
206 }
207 });
208#else
209 params.error_message_add(NodeWarningType::Error,
210 TIP_("Disabled, Blender was compiled without OpenSubdiv"));
211
212#endif
213 params.set_output("Mesh", std::move(geometry_set));
214}
215
216static void node_rna(StructRNA *srna)
217{
219 "uv_smooth",
220 "UV Smooth",
221 "Controls how smoothing is applied to UVs",
225 nullptr,
226 true);
227
229 "boundary_smooth",
230 "Boundary Smooth",
231 "Controls how open boundaries are smoothed",
233 NOD_storage_enum_accessors(boundary_smooth),
235 nullptr,
236 true);
237}
238
239static void node_register()
240{
241 static blender::bke::bNodeType ntype;
242
243 geo_node_type_base(&ntype, "GeometryNodeSubdivisionSurface", GEO_NODE_SUBDIVISION_SURFACE);
244 ntype.ui_name = "Subdivision Surface";
245 ntype.ui_description =
246 "Divide mesh faces to form a smooth surface, using the Catmull-Clark subdivision method";
247 ntype.enum_name_legacy = "SUBDIVISION_SURFACE";
249 ntype.declare = node_declare;
252 ntype.initfunc = node_init;
255 "NodeGeometrySubdivisionSurface",
259
260 node_rna(ntype.rna_ext.srna);
261}
263
264} // 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:1215
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:447
#define GEO_NODE_SUBDIVISION_SURFACE
#define TIP_(msgid)
struct Mesh Mesh
@ 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:239
#define UI_ITEM_NONE
BMesh const char void * data
std::optional< T > get_if_single() const
bool remove(const StringRef attribute_id)
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
static std::shared_ptr< FieldOperation > Create(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:2748
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:5603
void node_type_size_preset(bNodeType &ntype, eNodeSizePreset size)
Definition node.cc:5585
void debug_randomize_mesh_order(Mesh *mesh)
Definition randomize.cc:217
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, 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
StructRNA * srna
Definition RNA_types.hh:909
int edges_num
int verts_num
void * storage
const Mesh * get_mesh() const
void modify_geometry_sets(ForeachSubGeometryCallback callback)
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Defines a node type.
Definition BKE_node.hh:226
std::string ui_description
Definition BKE_node.hh:232
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:277
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:347
const char * enum_name_legacy
Definition BKE_node.hh:235
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:355
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
max
Definition text_draw.cc:251
PointerRNA * ptr
Definition wm_files.cc:4226