Changeset View
Changeset View
Standalone View
Standalone View
source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
- This file was copied from source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc.
| /* | /* | ||||
| * This program is free software; you can redistribute it and/or | * This program is free software; you can redistribute it and/or | ||||
| * modify it under the terms of the GNU General Public License | * modify it under the terms of the GNU General Public License | ||||
| * as published by the Free Software Foundation; either version 2 | * as published by the Free Software Foundation; either version 2 | ||||
| * of the License, or (at your option) any later version. | * of the License, or (at your option) any later version. | ||||
| * | * | ||||
| * This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | ||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
| */ | */ | ||||
| #include "DNA_mesh_types.h" | |||||
| #include "DNA_meshdata_types.h" | |||||
| #include "DNA_modifier_types.h" | |||||
| #include "BKE_mesh.h" | #include "BKE_mesh.h" | ||||
| #include "BKE_subdiv.h" | #include "BKE_subdiv.h" | ||||
| #include "BKE_subdiv_mesh.h" | #include "BKE_subdiv_mesh.h" | ||||
| #include "DNA_modifier_types.h" | |||||
| #include "UI_interface.h" | #include "UI_interface.h" | ||||
| #include "UI_resources.h" | #include "UI_resources.h" | ||||
| #include "node_geometry_util.hh" | #include "node_geometry_util.hh" | ||||
| namespace blender::nodes { | namespace blender::nodes { | ||||
| static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) | static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) | ||||
| { | { | ||||
| b.add_input<decl::Geometry>("Geometry"); | b.add_input<decl::Geometry>("Geometry"); | ||||
| b.add_input<decl::Int>("Level").default_value(1).min(0).max(6); | b.add_input<decl::Int>("Level").default_value(1).min(0).max(6); | ||||
| b.add_input<decl::Bool>("Use Creases"); | b.add_input<decl::Float>("Crease") | ||||
| .default_value(0.0f) | |||||
| .min(0.0f) | |||||
| .max(1.0f) | |||||
| .supports_field() | |||||
| .subtype(PROP_FACTOR); | |||||
| b.add_output<decl::Geometry>("Geometry"); | b.add_output<decl::Geometry>("Geometry"); | ||||
| } | } | ||||
| static void geo_node_subdivision_surface_layout(uiLayout *layout, | static void geo_node_subdivision_surface_layout(uiLayout *layout, | ||||
| bContext *UNUSED(C), | bContext *UNUSED(C), | ||||
| PointerRNA *ptr) | PointerRNA *ptr) | ||||
| { | { | ||||
| #ifdef WITH_OPENSUBDIV | #ifdef WITH_OPENSUBDIV | ||||
| Show All 14 Lines | static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *node) | ||||
| data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; | data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; | ||||
| node->storage = data; | node->storage = data; | ||||
| } | } | ||||
| static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) | static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) | ||||
| { | { | ||||
| GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); | GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); | ||||
| geometry_set = geometry_set_realize_instances(geometry_set); | Field<float> crease_field = params.extract_input<Field<float>>("Crease"); | ||||
| if (!geometry_set.has_mesh()) { | |||||
| params.set_output("Geometry", geometry_set); | |||||
| return; | |||||
| } | |||||
| #ifndef WITH_OPENSUBDIV | |||||
| params.error_message_add(NodeWarningType::Error, | |||||
| TIP_("Disabled, Blender was compiled without OpenSubdiv")); | |||||
| #else | |||||
| const NodeGeometrySubdivisionSurface &storage = | const NodeGeometrySubdivisionSurface &storage = | ||||
| *(const NodeGeometrySubdivisionSurface *)params.node().storage; | *(const NodeGeometrySubdivisionSurface *)params.node().storage; | ||||
| const int uv_smooth = storage.uv_smooth; | const int uv_smooth = storage.uv_smooth; | ||||
| const int boundary_smooth = storage.boundary_smooth; | const int boundary_smooth = storage.boundary_smooth; | ||||
| const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30); | const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30); | ||||
| /* Only process subdivision if level is greater than 0. */ | /* Only process subdivision if level is greater than 0. */ | ||||
| if (subdiv_level == 0) { | if (subdiv_level == 0) { | ||||
| params.set_output("Geometry", std::move(geometry_set)); | params.set_output("Geometry", std::move(geometry_set)); | ||||
| return; | return; | ||||
| } | } | ||||
| const bool use_crease = params.extract_input<bool>("Use Creases"); | #ifndef WITH_OPENSUBDIV | ||||
| const Mesh *mesh_in = geometry_set.get_mesh_for_read(); | params.error_message_add(NodeWarningType::Error, | ||||
| TIP_("Disabled, Blender was compiled without OpenSubdiv")); | |||||
| #else | |||||
| geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { | |||||
| if (!geometry_set.has_mesh()) { | |||||
| return; | |||||
| } | |||||
| MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); | |||||
| AttributeDomain domain = ATTR_DOMAIN_EDGE; | |||||
| GeometryComponentFieldContext field_context{mesh_component, domain}; | |||||
| const int domain_size = mesh_component.attribute_domain_size(domain); | |||||
| FieldEvaluator evaluator(field_context, domain_size); | |||||
| evaluator.add(crease_field); | |||||
JacquesLucke: And an empty line before here and after `const VArray<float> &creases = evaluator. | |||||
| evaluator.evaluate(); | |||||
| const VArray<float> &creases = evaluator.get_evaluated<float>(0); | |||||
| OutputAttribute_Typed<float> crease = mesh_component.attribute_try_get_for_output_only<float>( | |||||
Done Inline Actions[1/3] Building CXX object source/blender/nodes/CMakeFiles/bf_nodes.dir/geometry/nodes/node_geo_subdivision_surface.cc.o
/home/jacques/blender-git/blender/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc:102:5: warning: 'auto &creases' can be declared as 'const auto &creases' [readability-qualified-auto]
auto &creases = evaluator.get_evaluated<float>(0);
^
constUse the type name instead of auto. JacquesLucke: ```
[1/3] Building CXX object source/blender/nodes/CMakeFiles/bf_nodes. | |||||
| "crease", domain); | |||||
| MutableSpan<float> crease_span = crease.as_span(); | |||||
| for (auto i : creases.index_range()) { | |||||
Done Inline Actions
To see the bug, change the second input in the Add node. crease_reference_bug.blend783 KBDownload JacquesLucke: * This has to clamp the crease, otherwise the behavior is weird when the value is out of range. | |||||
| crease_span[i] = std::clamp(creases[i], 0.0f, 1.0f); | |||||
| } | |||||
| crease.save(); | |||||
| /* Initialize mesh settings. */ | /* Initialize mesh settings. */ | ||||
| SubdivToMeshSettings mesh_settings; | SubdivToMeshSettings mesh_settings; | ||||
| mesh_settings.resolution = (1 << subdiv_level) + 1; | mesh_settings.resolution = (1 << subdiv_level) + 1; | ||||
| mesh_settings.use_optimal_display = false; | mesh_settings.use_optimal_display = false; | ||||
Done Inline ActionsI guess I wasn't totally clear before, sorry about that, but the crease should be evaluated as a float and stored in the MEdge.crease (or passed to the subdivision process in some other way) if it's necessary (the use_creases condition hear evaluates to true). Otherwise it don't be possible to use different crease values for every vertex. HooglyBoogly: I guess I wasn't totally clear before, sorry about that, but the crease should be evaluated as… | |||||
Done Inline ActionsIt makes much more sense now. Thanks. jarrett.johnson: It makes much more sense now. Thanks. | |||||
| /* Initialize subdivision settings. */ | /* Initialize subdivision settings. */ | ||||
| SubdivSettings subdiv_settings; | SubdivSettings subdiv_settings; | ||||
| subdiv_settings.is_simple = false; | subdiv_settings.is_simple = false; | ||||
| subdiv_settings.is_adaptive = false; | subdiv_settings.is_adaptive = false; | ||||
| subdiv_settings.use_creases = use_crease; | subdiv_settings.use_creases = !(creases.is_single() && creases.get_internal_single() == 0.0f); | ||||
| subdiv_settings.level = subdiv_level; | subdiv_settings.level = subdiv_level; | ||||
| subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf( | subdiv_settings.vtx_boundary_interpolation = | ||||
| boundary_smooth); | BKE_subdiv_vtx_boundary_interpolation_from_subsurf(boundary_smooth); | ||||
| subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( | subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( | ||||
| uv_smooth); | uv_smooth); | ||||
| Mesh *mesh_in = mesh_component.get_for_write(); | |||||
| /* Apply subdivision to mesh. */ | /* Apply subdivision to mesh. */ | ||||
| Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); | Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); | ||||
| /* In case of bad topology, skip to input mesh. */ | /* In case of bad topology, skip to input mesh. */ | ||||
| if (subdiv == nullptr) { | if (subdiv == nullptr) { | ||||
| params.set_output("Geometry", std::move(geometry_set)); | |||||
| return; | return; | ||||
| } | } | ||||
| Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); | Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); | ||||
| BKE_mesh_normals_tag_dirty(mesh_out); | BKE_mesh_normals_tag_dirty(mesh_out); | ||||
| MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); | |||||
| mesh_component.replace(mesh_out); | mesh_component.replace(mesh_out); | ||||
| // BKE_subdiv_stats_print(&subdiv->stats); | |||||
| BKE_subdiv_free(subdiv); | BKE_subdiv_free(subdiv); | ||||
| }); | |||||
| #endif | #endif | ||||
| params.set_output("Geometry", std::move(geometry_set)); | params.set_output("Geometry", std::move(geometry_set)); | ||||
| } | } | ||||
| } // namespace blender::nodes | } // namespace blender::nodes | ||||
| void register_node_type_geo_subdivision_surface() | void register_node_type_geo_subdivision_surface() | ||||
| { | { | ||||
| static bNodeType ntype; | static bNodeType ntype; | ||||
| geo_node_type_base( | geo_node_type_base( | ||||
| &ntype, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0); | &ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0); | ||||
| ntype.declare = blender::nodes::geo_node_subdivision_surface_declare; | ntype.declare = blender::nodes::geo_node_subdivision_surface_declare; | ||||
| ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec; | ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec; | ||||
| ntype.draw_buttons = blender::nodes::geo_node_subdivision_surface_layout; | ntype.draw_buttons = blender::nodes::geo_node_subdivision_surface_layout; | ||||
| node_type_init(&ntype, blender::nodes::geo_node_subdivision_surface_init); | node_type_init(&ntype, blender::nodes::geo_node_subdivision_surface_init); | ||||
| node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); | node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); | ||||
| node_type_storage(&ntype, | node_type_storage(&ntype, | ||||
| "NodeGeometrySubdivisionSurface", | "NodeGeometrySubdivisionSurface", | ||||
| node_free_standard_storage, | node_free_standard_storage, | ||||
| node_copy_standard_storage); | node_copy_standard_storage); | ||||
| nodeRegisterType(&ntype); | nodeRegisterType(&ntype); | ||||
| } | } | ||||
And an empty line before here and after const VArray<float> &creases = evaluator.get_evaluated<float>(0);.
This just makes the code a bit easier to scan.