diff --git a/intern/cycles/blender/mesh.cpp b/intern/cycles/blender/mesh.cpp index 00586b6cf00..cdfeadb806a 100644 --- a/intern/cycles/blender/mesh.cpp +++ b/intern/cycles/blender/mesh.cpp @@ -816,9 +816,10 @@ static void create_mesh(Scene *scene, const blender::OffsetIndices faces = b_mesh.faces(); const blender::Span corner_verts = b_mesh.corner_verts(); const blender::bke::AttributeAccessor b_attributes = b_mesh.attributes(); + const blender::bke::MeshNormalDomain normals_domain = b_mesh.normals_domain(); int numfaces = (!subdivision) ? b_mesh.looptris().size() : faces.size(); - bool use_loop_normals = (b_mesh.flag & ME_AUTOSMOOTH) && + bool use_loop_normals = normals_domain == blender::bke::MeshNormalDomain::Corner && (mesh->get_subdivision_type() != Mesh::SUBDIVISION_CATMULL_CLARK); /* If no faces, create empty mesh. */ @@ -832,9 +833,7 @@ static void create_mesh(Scene *scene, ATTR_DOMAIN_FACE); blender::Span corner_normals; if (use_loop_normals) { - corner_normals = { - static_cast(CustomData_get_layer(&b_mesh.loop_data, CD_NORMAL)), - corner_verts.size()}; + corner_normals = b_mesh.corner_normals(); } int numngons = 0; @@ -939,7 +938,8 @@ static void create_mesh(Scene *scene, } } else { - std::fill(smooth, smooth + numtris, true); + /* If only face normals are needed, all faces are sharp. */ + std::fill(smooth, smooth + numtris, normals_domain != blender::bke::MeshNormalDomain::Face); } if (use_loop_normals && !corner_normals.is_empty()) { diff --git a/intern/cycles/blender/util.h b/intern/cycles/blender/util.h index 7583e87f4e3..b685fa75c33 100644 --- a/intern/cycles/blender/util.h +++ b/intern/cycles/blender/util.h @@ -95,7 +95,9 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/, * Also in edit mode do we need to make a copy, to ensure data layers like * UV are not empty. */ if (mesh.is_editmode() || - (mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE)) { + (mesh.normals_domain() == BL::Mesh::normals_domain_CORNER && + subdivision_type == Mesh::SUBDIVISION_NONE)) + { BL::Depsgraph depsgraph(PointerRNA_NULL); mesh = b_ob_info.real_object.to_mesh(false, depsgraph); } @@ -119,8 +121,7 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/, #endif if ((bool)mesh && subdivision_type == Mesh::SUBDIVISION_NONE) { - if (mesh.use_auto_smooth()) { - mesh.calc_normals_split(); + if (mesh.normals_domain() == BL::Mesh::normals_domain_CORNER) { mesh.split_faces(); } diff --git a/scripts/startup/bl_ui/properties_data_mesh.py b/scripts/startup/bl_ui/properties_data_mesh.py index 3eacf3ced3d..f510c37f9bb 100644 --- a/scripts/startup/bl_ui/properties_data_mesh.py +++ b/scripts/startup/bl_ui/properties_data_mesh.py @@ -183,33 +183,6 @@ class DATA_PT_context_mesh(MeshButtonsPanel, Panel): layout.template_ID(space, "pin_id") -class DATA_PT_normals(MeshButtonsPanel, Panel): - bl_label = "Normals" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = { - 'BLENDER_RENDER', - 'BLENDER_EEVEE', - 'BLENDER_EEVEE_NEXT', - 'BLENDER_WORKBENCH', - } - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - mesh = context.mesh - - col = layout.column(align=False, heading="Auto Smooth") - col.use_property_decorate = False - row = col.row(align=True) - sub = row.row(align=True) - sub.prop(mesh, "use_auto_smooth", text="") - sub = sub.row(align=True) - sub.active = mesh.use_auto_smooth and not mesh.has_custom_normals - sub.prop(mesh, "auto_smooth_angle", text="") - row.prop_decorator(mesh, "auto_smooth_angle") - - class DATA_PT_texture_space(MeshButtonsPanel, Panel): bl_label = "Texture Space" bl_options = {'DEFAULT_CLOSED'} @@ -728,7 +701,6 @@ classes = ( DATA_PT_uv_texture, DATA_PT_vertex_colors, DATA_PT_mesh_attributes, - DATA_PT_normals, DATA_PT_texture_space, DATA_PT_remesh, DATA_PT_customdata, diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 41d8ae10d57..ae9a78fb750 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -2723,7 +2723,8 @@ class VIEW3D_MT_object(Menu): layout.separator() layout.operator("object.shade_smooth") - layout.operator("object.shade_smooth", text="Shade Auto Smooth").use_auto_smooth = True + if context.object and context.object.type == 'MESH': + layout.operator("object.shade_smooth_by_angle") layout.operator("object.shade_flat") layout.separator() @@ -2967,8 +2968,9 @@ class VIEW3D_MT_object_context_menu(Menu): if obj is not None: if obj.type in {'MESH', 'CURVE', 'SURFACE'}: layout.operator("object.shade_smooth") - layout.operator("object.shade_smooth", text="Shade Auto Smooth").use_auto_smooth = True - layout.operator("object.shade_flat", text="Shade Flat") + if obj.type == 'MESH': + layout.operator("object.shade_smooth_by_angle") + layout.operator("object.shade_flat") layout.separator() diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 8c75a57ff66..eabb647dfd6 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 1 +#define BLENDER_FILE_SUBVERSION 2 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index 8f903d9cb08..a9a6c4d52e6 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -120,11 +120,7 @@ const float (*BKE_editmesh_vert_coords_when_deformed(struct Depsgraph *depsgraph int *r_vert_len, bool *r_is_alloc))[3]; -void BKE_editmesh_lnorspace_update(BMEditMesh *em, struct Mesh *me); -/** - * If auto-smooth not already set, set it. - */ -void BKE_editmesh_ensure_autosmooth(BMEditMesh *em, struct Mesh *me); +void BKE_editmesh_lnorspace_update(BMEditMesh *em); struct BoundBox *BKE_editmesh_cage_boundbox_get(struct Object *object, BMEditMesh *em); #ifdef __cplusplus diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 67d4a9f84a5..70efdc0c6e6 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -77,6 +77,9 @@ void BKE_mesh_tag_topology_changed(struct Mesh *mesh); */ void BKE_mesh_tag_edges_split(struct Mesh *mesh); +/** Call when changing "sharp_face" or "sharp_edge" data. */ +void BKE_mesh_tag_sharpness_changed(struct Mesh *mesh); + /** * Call when face vertex order has changed but positions and faces haven't changed */ @@ -224,7 +227,7 @@ bool BKE_mesh_material_index_used(struct Mesh *me, short index); void BKE_mesh_material_index_clear(struct Mesh *me); void BKE_mesh_material_remap(struct Mesh *me, const unsigned int *remap, unsigned int remap_len); void BKE_mesh_smooth_flag_set(struct Mesh *me, bool use_smooth); -void BKE_mesh_auto_smooth_flag_set(struct Mesh *me, bool use_auto_smooth, float auto_smooth_angle); +void BKE_mesh_sharp_edges_set_from_angle(struct Mesh *me, float angle); /** * Used for unit testing; compares two meshes, checking only @@ -328,11 +331,6 @@ bool BKE_mesh_vert_normals_are_dirty(const struct Mesh *mesh); /** Return true if the mesh face normals either are not stored or are dirty. */ bool BKE_mesh_face_normals_are_dirty(const struct Mesh *mesh); -/** - * Called after calculating all modifiers. - */ -void BKE_mesh_ensure_normals_for_display(struct Mesh *mesh); - /** * References a contiguous loop-fan with normal offset vars. */ @@ -456,18 +454,6 @@ void BKE_mesh_normals_loop_to_vertex(int numVerts, */ bool BKE_mesh_has_custom_loop_normals(struct Mesh *me); -void BKE_mesh_calc_normals_split(struct Mesh *mesh); -/** - * Compute 'split' (aka loop, or per face corner's) normals. - * - * \param r_lnors_spacearr: Allows to get computed loop normal space array. - * That data, among other things, contains 'smooth fan' info, useful e.g. - * to split geometry along sharp edges. - */ -void BKE_mesh_calc_normals_split_ex(const struct Mesh *mesh, - struct MLoopNorSpaceArray *r_lnors_spacearr, - float (*r_corner_normals)[3]); - /** * Higher level functions hiding most of the code needed around call to * #normals_loop_custom_set(). @@ -650,8 +636,7 @@ void BKE_mesh_calc_edges(struct Mesh *mesh, bool keep_existing_edges, bool selec void BKE_mesh_calc_edges_tessface(struct Mesh *mesh); /* In DerivedMesh.cc */ -void BKE_mesh_wrapper_deferred_finalize_mdata(struct Mesh *me_eval, - const struct CustomData_MeshMasks *cd_mask_finalize); +void BKE_mesh_wrapper_deferred_finalize_mdata(struct Mesh *me_eval); /* **** Depsgraph evaluation **** */ diff --git a/source/blender/blenkernel/BKE_mesh.hh b/source/blender/blenkernel/BKE_mesh.hh index fb7e3d9549d..e7a9605844d 100644 --- a/source/blender/blenkernel/BKE_mesh.hh +++ b/source/blender/blenkernel/BKE_mesh.hh @@ -162,8 +162,6 @@ void normals_calc_loop(Span vert_positions, const bool *sharp_edges, const bool *sharp_faces, const short2 *clnors_data, - bool use_split_normals, - float split_angle, CornerNormalSpaceArray *r_lnors_spacearr, MutableSpan r_loop_normals); diff --git a/source/blender/blenkernel/BKE_mesh_legacy_convert.hh b/source/blender/blenkernel/BKE_mesh_legacy_convert.hh index d5d9be43abe..b10870e5ad4 100644 --- a/source/blender/blenkernel/BKE_mesh_legacy_convert.hh +++ b/source/blender/blenkernel/BKE_mesh_legacy_convert.hh @@ -13,6 +13,7 @@ #include "BLI_utildefines.h" struct CustomData; +struct Main; struct Mesh; struct MFace; @@ -108,6 +109,8 @@ void BKE_mesh_calc_edges_legacy(Mesh *me); void BKE_mesh_do_versions_cd_flag_init(Mesh *mesh); +void BKE_main_mesh_legacy_convert_auto_smooth(Main &bmain); + /* Inlines */ /* NOTE(@sybren): Instead of -1 that function uses ORIGINDEX_NONE as defined in BKE_customdata.h, diff --git a/source/blender/blenkernel/BKE_mesh_remap.hh b/source/blender/blenkernel/BKE_mesh_remap.hh index 73058279014..13c877339be 100644 --- a/source/blender/blenkernel/BKE_mesh_remap.hh +++ b/source/blender/blenkernel/BKE_mesh_remap.hh @@ -144,9 +144,6 @@ enum { MREMAP_MODE_TOPOLOGY = MREMAP_MODE_VERT | MREMAP_MODE_EDGE | MREMAP_MODE_LOOP | MREMAP_MODE_POLY, }; -void BKE_mesh_remap_calc_source_cddata_masks_from_map_modes( - int vert_mode, int edge_mode, int loop_mode, int face_mode, CustomData_MeshMasks *cddata_mask); - /** * Compute a value of the difference between both given meshes. * The smaller the result, the better the match. @@ -176,7 +173,6 @@ void BKE_mesh_remap_calc_verts_from_mesh(int mode, float ray_radius, const float (*vert_positions_dst)[3], int numverts_dst, - bool dirty_nors_dst, const Mesh *me_src, Mesh *me_dst, MeshPairRemap *r_map); @@ -189,7 +185,6 @@ void BKE_mesh_remap_calc_edges_from_mesh(int mode, int numverts_dst, const blender::int2 *edges_dst, int numedges_dst, - bool dirty_nors_dst, const Mesh *me_src, Mesh *me_dst, MeshPairRemap *r_map); @@ -198,19 +193,12 @@ void BKE_mesh_remap_calc_loops_from_mesh(int mode, const SpaceTransform *space_transform, float max_dist, float ray_radius, - Mesh *mesh_dst, + const Mesh *mesh_dst, const float (*vert_positions_dst)[3], int numverts_dst, - const blender::int2 *edges_dst, - int numedges_dst, const int *corner_verts_dst, - const int *corner_edges_dst, int numloops_dst, const blender::OffsetIndices faces_dst, - CustomData *ldata_dst, - bool use_split_nors_dst, - float split_angle_dst, - bool dirty_nors_dst, const Mesh *me_src, MeshRemapIslandsCalc gen_islands_src, float islands_precision_src, diff --git a/source/blender/blenkernel/BKE_mesh_runtime.hh b/source/blender/blenkernel/BKE_mesh_runtime.hh index 62e8eba3171..e2da362b739 100644 --- a/source/blender/blenkernel/BKE_mesh_runtime.hh +++ b/source/blender/blenkernel/BKE_mesh_runtime.hh @@ -33,8 +33,7 @@ bool BKE_mesh_runtime_ensure_edit_data(Mesh *mesh); * For "smaller" changes to meshes like updating positions, consider calling a more specific update * function like #BKE_mesh_tag_positions_changed. * - * Also note that some derived caches like #CD_NORMAL and #CD_TANGENT are stored directly in - * #CustomData. + * Also note that some derived caches like #CD_TANGENT are stored directly in #CustomData. */ void BKE_mesh_runtime_clear_geometry(Mesh *mesh); diff --git a/source/blender/blenkernel/BKE_mesh_types.hh b/source/blender/blenkernel/BKE_mesh_types.hh index 416a4500a67..d5799b96c9d 100644 --- a/source/blender/blenkernel/BKE_mesh_types.hh +++ b/source/blender/blenkernel/BKE_mesh_types.hh @@ -43,6 +43,31 @@ enum eMeshWrapperType { namespace blender::bke { +/** + * The complexity requirement of attribute domains needed to process normals. + * See #Mesh::normals_domain(). + */ +enum class MeshNormalDomain : int8_t { + /** + * The mesh is completely smooth shaded; either all faces or edges are sharp. + * Only #Mesh::face_normals() is necessary. This case is generally the best + * for performance, since no mixing is necessary and multithreading is simple. + */ + Face, + /** + * The mesh is completely smooth shaded; there are no sharp face or edges. Only + * #Mesh::vert_normals() is necessary. Calculating face normals is still necessary though, + * since they have to be mixed to become vertex normals. + */ + Point, + /** + * The mesh has mixed smooth and sharp shading. In order to split the normals on each side of + * sharp edges, they need to be processed per-face-corner. Normals can be retrieved with + * #Mesh::corner_normals(). + */ + Corner, +}; + /** * Cache of a mesh's loose edges, accessed with #Mesh::loose_edges(). * */ @@ -136,9 +161,10 @@ struct MeshRuntime { */ SubsurfRuntimeData *subsurf_runtime_data = nullptr; - /** Caches for lazily computed vertex and face normals. */ + /** Caches for lazily computed normals. */ SharedCache> vert_normals_cache; SharedCache> face_normals_cache; + SharedCache> corner_normals_cache; /** * Cache of offsets for vert to face/corner maps. The same offsets array is used to group diff --git a/source/blender/blenkernel/BKE_subdiv_modifier.hh b/source/blender/blenkernel/BKE_subdiv_modifier.hh index a6416be3f4e..e6a269be308 100644 --- a/source/blender/blenkernel/BKE_subdiv_modifier.hh +++ b/source/blender/blenkernel/BKE_subdiv_modifier.hh @@ -45,7 +45,6 @@ struct SubsurfRuntimeData { bool has_gpu_subdiv; int resolution; bool use_optimal_display; - bool calc_loop_normals; bool use_loop_normals; /* Cached from the draw code for stats display. */ diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 9353ed5e717..a9c8b9868e9 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -89,10 +89,8 @@ using blender::bke::MeshComponent; #endif static void mesh_init_origspace(Mesh *mesh); -static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, - const CustomData_MeshMasks *final_datamask); -static void editbmesh_calc_modifier_final_normals_or_defer( - Mesh *mesh_final, const CustomData_MeshMasks *final_datamask); +static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final); +static void editbmesh_calc_modifier_final_normals_or_defer(Mesh *mesh_final); /* -------------------------------------------------------------------- */ @@ -448,60 +446,6 @@ static void add_orco_mesh( } } -static bool mesh_has_modifier_final_normals(const Mesh *mesh_input, - const CustomData_MeshMasks *final_datamask, - Mesh *mesh_final) -{ - /* Test if mesh has the required loop normals, in case an additional modifier - * evaluation from another instance or from an operator requests it but the - * initial normals were not loop normals. */ - const bool calc_loop_normals = ((mesh_input->flag & ME_AUTOSMOOTH) != 0 || - (final_datamask->lmask & CD_MASK_NORMAL) != 0); - - return (!calc_loop_normals || CustomData_has_layer(&mesh_final->loop_data, CD_NORMAL)); -} - -static void mesh_calc_modifier_final_normals(const Mesh *mesh_input, - const CustomData_MeshMasks *final_datamask, - const bool sculpt_dyntopo, - Mesh *mesh_final) -{ - /* Compute normals. */ - const bool calc_loop_normals = ((mesh_input->flag & ME_AUTOSMOOTH) != 0 || - (final_datamask->lmask & CD_MASK_NORMAL) != 0); - - /* Needed as `final_datamask` is not preserved outside modifier stack evaluation. */ - SubsurfRuntimeData *subsurf_runtime_data = mesh_final->runtime->subsurf_runtime_data; - if (subsurf_runtime_data) { - subsurf_runtime_data->calc_loop_normals = calc_loop_normals; - } - - if (calc_loop_normals) { - /* Compute loop normals (NOTE: will compute face and vert normals as well, if needed!). In case - * of deferred CPU subdivision, this will be computed when the wrapper is generated. */ - if (!subsurf_runtime_data || subsurf_runtime_data->resolution == 0) { - BKE_mesh_calc_normals_split(mesh_final); - } - } - else { - if (sculpt_dyntopo == false) { - /* without this, drawing ngon tri's faces will show ugly tessellated face - * normals and will also have to calculate normals on the fly, try avoid - * this where possible since calculating face normals isn't fast, - * note that this isn't a problem for subsurf (only quads) or edit-mode - * which deals with drawing differently. */ - BKE_mesh_ensure_normals_for_display(mesh_final); - } - - /* Some modifiers, like data-transfer, may generate those data as temp layer, - * we do not want to keep them, as they are used by display code when available - * (i.e. even if auto-smooth is disabled). */ - if (CustomData_has_layer(&mesh_final->loop_data, CD_NORMAL)) { - CustomData_free_layers(&mesh_final->loop_data, CD_NORMAL, mesh_final->totloop); - } - } -} - /* Does final touches to the final evaluated mesh, making sure it is perfectly usable. * * This is needed because certain information is not passed along intermediate meshes allocated @@ -516,11 +460,10 @@ static void mesh_calc_finalize(const Mesh *mesh_input, Mesh *mesh_eval) mesh_eval->edit_mesh = mesh_input->edit_mesh; } -void BKE_mesh_wrapper_deferred_finalize_mdata(Mesh *me_eval, - const CustomData_MeshMasks *cd_mask_finalize) +void BKE_mesh_wrapper_deferred_finalize_mdata(Mesh *me_eval) { if (me_eval->runtime->wrapper_type_finalize & (1 << ME_WRAPPER_TYPE_BMESH)) { - editbmesh_calc_modifier_final_normals(me_eval, cd_mask_finalize); + editbmesh_calc_modifier_final_normals(me_eval); me_eval->runtime->wrapper_type_finalize = eMeshWrapperType( me_eval->runtime->wrapper_type_finalize & ~(1 << ME_WRAPPER_TYPE_BMESH)); } @@ -1024,7 +967,6 @@ static void mesh_calc_modifiers(Depsgraph *depsgraph, /* Compute normals. */ if (is_own_mesh) { - mesh_calc_modifier_final_normals(mesh_input, &final_datamask, sculpt_dyntopo, mesh_final); mesh_calc_finalize(mesh_input, mesh_final); } else { @@ -1036,8 +978,6 @@ static void mesh_calc_modifiers(Depsgraph *depsgraph, * Isolate since computing normals is multithreaded and we are holding a lock. */ blender::threading::isolate_task([&] { mesh_final = BKE_mesh_copy_for_eval(mesh_input); - mesh_calc_modifier_final_normals( - mesh_input, &final_datamask, sculpt_dyntopo, mesh_final); mesh_calc_finalize(mesh_input, mesh_final); runtime->mesh_eval = mesh_final; }); @@ -1047,13 +987,6 @@ static void mesh_calc_modifiers(Depsgraph *depsgraph, mesh_final = runtime->mesh_eval; } } - else if (!mesh_has_modifier_final_normals(mesh_input, &final_datamask, runtime->mesh_eval)) { - /* Modifier stack was (re-)evaluated with a request for additional normals - * different than the instanced mesh, can't instance anymore now. */ - mesh_final = BKE_mesh_copy_for_eval(mesh_input); - mesh_calc_modifier_final_normals(mesh_input, &final_datamask, sculpt_dyntopo, mesh_final); - mesh_calc_finalize(mesh_input, mesh_final); - } else { /* Already finalized by another instance, reuse. */ mesh_final = runtime->mesh_eval; @@ -1102,39 +1035,25 @@ bool editbmesh_modifier_is_enabled(const Scene *scene, return true; } -static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, - const CustomData_MeshMasks *final_datamask) +static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final) { - const bool calc_loop_normals = ((mesh_final->flag & ME_AUTOSMOOTH) != 0 || - (final_datamask->lmask & CD_MASK_NORMAL) != 0); - - SubsurfRuntimeData *subsurf_runtime_data = mesh_final->runtime->subsurf_runtime_data; - if (subsurf_runtime_data) { - subsurf_runtime_data->calc_loop_normals = calc_loop_normals; - } - - if (calc_loop_normals) { - /* Compute loop normals. In case of deferred CPU subdivision, this will be computed when the - * wrapper is generated. */ - if (!subsurf_runtime_data || subsurf_runtime_data->resolution == 0) { - BKE_mesh_calc_normals_split(mesh_final); - } - } - else { - /* Same as #mesh_calc_modifiers. - * If using loop normals, face normals have already been computed. */ - BKE_mesh_ensure_normals_for_display(mesh_final); - - /* Some modifiers, like data-transfer, may generate those data, we do not want to keep them, - * as they are used by display code when available (i.e. even if auto-smooth is disabled). */ - if (CustomData_has_layer(&mesh_final->loop_data, CD_NORMAL)) { - CustomData_free_layers(&mesh_final->loop_data, CD_NORMAL, mesh_final->totloop); + switch (mesh_final->runtime->wrapper_type) { + case ME_WRAPPER_TYPE_SUBD: + case ME_WRAPPER_TYPE_MDATA: + break; + case ME_WRAPPER_TYPE_BMESH: { + BMEditMesh *em = mesh_final->edit_mesh; + blender::bke::EditMeshData *emd = mesh_final->runtime->edit_data; + if (!emd->vertexCos.is_empty()) { + BKE_editmesh_cache_ensure_vert_normals(em, emd); + BKE_editmesh_cache_ensure_face_normals(em, emd); + } + return; } } } -static void editbmesh_calc_modifier_final_normals_or_defer( - Mesh *mesh_final, const CustomData_MeshMasks *final_datamask) +static void editbmesh_calc_modifier_final_normals_or_defer(Mesh *mesh_final) { if (mesh_final->runtime->wrapper_type != ME_WRAPPER_TYPE_MDATA) { /* Generated at draw time. */ @@ -1143,7 +1062,7 @@ static void editbmesh_calc_modifier_final_normals_or_defer( return; } - editbmesh_calc_modifier_final_normals(mesh_final, final_datamask); + editbmesh_calc_modifier_final_normals(mesh_final); } static MutableSpan mesh_wrapper_vert_coords_ensure_for_write(Mesh *mesh) @@ -1361,9 +1280,9 @@ static void editbmesh_calc_modifiers(Depsgraph *depsgraph, } /* Compute normals. */ - editbmesh_calc_modifier_final_normals_or_defer(mesh_final, &final_datamask); + editbmesh_calc_modifier_final_normals_or_defer(mesh_final); if (mesh_cage && (mesh_cage != mesh_final)) { - editbmesh_calc_modifier_final_normals_or_defer(mesh_cage, &final_datamask); + editbmesh_calc_modifier_final_normals_or_defer(mesh_cage); } /* Return final mesh. */ diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 00bd97d51bc..8dceca58acb 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -796,8 +796,6 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, CustomData_free_layer_named(&mesh->vert_data, "position", 0); mesh->totvert = offsets.vert.last(); - mesh->flag |= ME_AUTOSMOOTH; - mesh->smoothresh = DEG2RADF(180.0f); MutableSpan edges = mesh->edges_for_write(); MutableSpan face_offsets = mesh->face_offsets_for_write(); MutableSpan corner_verts = mesh->corner_verts_for_write(); diff --git a/source/blender/blenkernel/intern/data_transfer.cc b/source/blender/blenkernel/intern/data_transfer.cc index bbc2ccc0526..7912f3844b6 100644 --- a/source/blender/blenkernel/intern/data_transfer.cc +++ b/source/blender/blenkernel/intern/data_transfer.cc @@ -70,7 +70,7 @@ void BKE_object_data_transfer_dttypes_to_cdmask(const int dtdata_types, r_data_masks->lmask |= CD_MASK_PROP_FLOAT2; } else if (cddata_type == CD_FAKE_LNOR) { - r_data_masks->lmask |= CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL; + r_data_masks->lmask |= CD_MASK_CUSTOMLOOPNORMAL; } } } @@ -353,60 +353,6 @@ static void data_transfer_mesh_attributes_transfer_default_color_string( /* ********** */ -/* Generic pre/post processing, only used by custom loop normals currently. */ - -static void data_transfer_dtdata_type_preprocess(const Mesh *me_src, - Mesh *me_dst, - const int dtdata_type, - const bool dirty_nors_dst) -{ - if (dtdata_type == DT_TYPE_LNOR) { - /* Compute custom normals into regular loop normals, which will be used for the transfer. */ - CustomData *ldata_dst = &me_dst->loop_data; - - const bool use_split_nors_dst = (me_dst->flag & ME_AUTOSMOOTH) != 0; - const float split_angle_dst = me_dst->smoothresh; - - /* This should be ensured by cddata_masks we pass to code generating/giving us me_src now. */ - BLI_assert(CustomData_get_layer(&me_src->loop_data, CD_NORMAL) != nullptr); - (void)me_src; - - const blender::short2 *custom_nors_dst = static_cast( - CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL)); - - /* Cache loop nors into a temp CDLayer. */ - blender::float3 *loop_nors_dst = static_cast( - CustomData_get_layer_for_write(ldata_dst, CD_NORMAL, me_dst->totloop)); - const bool do_loop_nors_dst = (loop_nors_dst == nullptr); - if (do_loop_nors_dst) { - loop_nors_dst = static_cast( - CustomData_add_layer(ldata_dst, CD_NORMAL, CD_SET_DEFAULT, me_dst->totloop)); - CustomData_set_layer_flag(ldata_dst, CD_NORMAL, CD_FLAG_TEMPORARY); - } - if (dirty_nors_dst || do_loop_nors_dst) { - const bool *sharp_edges = static_cast( - CustomData_get_layer_named(&me_dst->edge_data, CD_PROP_BOOL, "sharp_edge")); - const bool *sharp_faces = static_cast( - CustomData_get_layer_named(&me_dst->face_data, CD_PROP_BOOL, "sharp_face")); - blender::bke::mesh::normals_calc_loop(me_dst->vert_positions(), - me_dst->edges(), - me_dst->faces(), - me_dst->corner_verts(), - me_dst->corner_edges(), - me_dst->corner_to_face_map(), - me_dst->vert_normals(), - me_dst->face_normals(), - sharp_edges, - sharp_faces, - custom_nors_dst, - use_split_nors_dst, - split_angle_dst, - nullptr, - {loop_nors_dst, me_dst->totloop}); - } - } -} - static void data_transfer_dtdata_type_postprocess(Mesh *me_dst, const int dtdata_type, const bool changed) @@ -1352,8 +1298,6 @@ bool BKE_object_data_transfer_ex(Depsgraph *depsgraph, SpaceTransform auto_space_transform; const Mesh *me_src; - /* Assumed always true if not using an evaluated mesh as destination. */ - bool dirty_nors_dst = true; const MDeformVert *mdef = nullptr; int vg_idx = -1; @@ -1370,7 +1314,6 @@ bool BKE_object_data_transfer_ex(Depsgraph *depsgraph, BLI_assert((ob_src != ob_dst) && (ob_src->type == OB_MESH) && (ob_dst->type == OB_MESH)); if (me_dst) { - dirty_nors_dst = BKE_mesh_vert_normals_are_dirty(me_dst); /* Never create needed custom layers on passed destination mesh * (assumed to *not* be ob_dst->data, aka modifier case). */ use_create = false; @@ -1424,8 +1367,6 @@ bool BKE_object_data_transfer_ex(Depsgraph *depsgraph, continue; } - data_transfer_dtdata_type_preprocess(me_src, me_dst, dtdata_type, dirty_nors_dst); - cddata_type = BKE_object_data_transfer_dttype_to_cdtype(dtdata_type); fromto_idx = BKE_object_data_transfer_dttype_to_srcdst_index(dtdata_type); @@ -1480,7 +1421,6 @@ bool BKE_object_data_transfer_ex(Depsgraph *depsgraph, ray_radius, reinterpret_cast(positions_dst.data()), num_verts_dst, - dirty_nors_dst, me_src, me_dst, &geom_map[VDATA]); @@ -1560,7 +1500,6 @@ bool BKE_object_data_transfer_ex(Depsgraph *depsgraph, num_verts_dst, edges_dst.data(), edges_dst.size(), - dirty_nors_dst, me_src, me_dst, &geom_map[EDATA]); @@ -1608,11 +1547,8 @@ bool BKE_object_data_transfer_ex(Depsgraph *depsgraph, if (DT_DATATYPE_IS_LOOP(dtdata_type)) { const blender::Span positions_dst = me_dst->vert_positions(); const int num_verts_dst = me_dst->totvert; - const blender::Span edges_dst = me_dst->edges(); const blender::OffsetIndices faces_dst = me_dst->faces(); const blender::Span corner_verts_dst = me_dst->corner_verts(); - const blender::Span corner_edges_dst = me_dst->corner_edges(); - CustomData *ldata_dst = &me_dst->loop_data; MeshRemapIslandsCalc island_callback = data_transfer_get_loop_islands_generator(cddata_type); @@ -1650,16 +1586,9 @@ bool BKE_object_data_transfer_ex(Depsgraph *depsgraph, me_dst, reinterpret_cast(positions_dst.data()), num_verts_dst, - edges_dst.data(), - edges_dst.size(), corner_verts_dst.data(), - corner_edges_dst.data(), corner_verts_dst.size(), faces_dst, - ldata_dst, - (me_dst->flag & ME_AUTOSMOOTH) != 0, - me_dst->smoothresh, - dirty_nors_dst, me_src, island_callback, islands_handling_precision, diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index f018bb97a9c..4583b73c63a 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -758,9 +758,6 @@ static blender::bke::GeometrySet curve_calc_modifiers_post(Depsgraph *depsgraph, if (geometry_set.has_mesh()) { Mesh *final_mesh = geometry_set.get_mesh_for_write(); - - BKE_mesh_ensure_normals_for_display(final_mesh); - STRNCPY(final_mesh->id.name, cu->id.name); *((short *)final_mesh->id.name) = ID_ME; } diff --git a/source/blender/blenkernel/intern/editmesh.cc b/source/blender/blenkernel/intern/editmesh.cc index f34fc1b3db9..fef5ab7d5d3 100644 --- a/source/blender/blenkernel/intern/editmesh.cc +++ b/source/blender/blenkernel/intern/editmesh.cc @@ -252,33 +252,9 @@ float (*BKE_editmesh_vert_coords_alloc_orco(BMEditMesh *em, int *r_vert_len))[3] return BM_mesh_vert_coords_alloc(em->bm, r_vert_len); } -void BKE_editmesh_lnorspace_update(BMEditMesh *em, Mesh *me) +void BKE_editmesh_lnorspace_update(BMEditMesh *em) { - BMesh *bm = em->bm; - - /* We need to create custom-loop-normals (CLNORS) data if none exist yet, - * otherwise there is no way to edit them. - * Similar code to #MESH_OT_customdata_custom_splitnormals_add operator, - * we want to keep same shading in case we were using auto-smooth so far. - * NOTE: there is a problem here, which is that if someone starts a normal editing operation on - * previously auto-smooth-ed mesh, and cancel that operation, generated CLNORS data remain, - * with related sharp edges (and hence auto-smooth is 'lost'). - * Not sure how critical this is, and how to fix that issue? */ - if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) { - if (me->flag & ME_AUTOSMOOTH) { - BM_edges_sharp_from_angle_set(bm, me->smoothresh); - } - } - - BM_lnorspace_update(bm); -} - -void BKE_editmesh_ensure_autosmooth(BMEditMesh *em, Mesh *me) -{ - if (!(me->flag & ME_AUTOSMOOTH)) { - me->flag |= ME_AUTOSMOOTH; - BKE_editmesh_lnorspace_update(em, me); - } + BM_lnorspace_update(em->bm); } BoundBox *BKE_editmesh_cage_boundbox_get(Object *object, BMEditMesh * /*em*/) diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index ace3d1ccd79..5b2744abdab 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -858,6 +858,13 @@ static void tag_component_positions_changed(void *owner) } } +static void tag_component_sharpness_changed(void *owner) +{ + if (Mesh *mesh = static_cast(owner)) { + BKE_mesh_tag_sharpness_changed(mesh); + } +} + /** * This provider makes vertex groups available as float attributes. */ @@ -1075,7 +1082,7 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Deletable, face_access, - nullptr); + tag_component_sharpness_changed); static BuiltinCustomDataLayerProvider sharp_edge("sharp_edge", ATTR_DOMAIN_EDGE, @@ -1084,7 +1091,7 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Deletable, edge_access, - nullptr); + tag_component_sharpness_changed); static MeshVertexGroupsAttributeProvider vertex_groups; static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access); diff --git a/source/blender/blenkernel/intern/key.cc b/source/blender/blenkernel/intern/key.cc index e02804a00be..93e4e5c6324 100644 --- a/source/blender/blenkernel/intern/key.cc +++ b/source/blender/blenkernel/intern/key.cc @@ -2287,8 +2287,6 @@ void BKE_keyblock_mesh_calc_normals(const KeyBlock *kb, sharp_edges, sharp_faces, clnors, - (mesh->flag & ME_AUTOSMOOTH) != 0, - mesh->smoothresh, nullptr, {reinterpret_cast(r_loop_normals), corner_verts.size()}); } diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index a874b9ec7e5..7638788febe 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -972,7 +972,6 @@ void BKE_mesh_copy_parameters(Mesh *me_dst, const Mesh *me_src) /* Copy general settings. */ me_dst->editflag = me_src->editflag; me_dst->flag = me_src->flag; - me_dst->smoothresh = me_src->smoothresh; me_dst->remesh_voxel_size = me_src->remesh_voxel_size; me_dst->remesh_voxel_adaptivity = me_src->remesh_voxel_adaptivity; me_dst->remesh_mode = me_src->remesh_mode; @@ -1429,9 +1428,11 @@ void BKE_mesh_smooth_flag_set(Mesh *me, const bool use_smooth) using namespace blender::bke; MutableAttributeAccessor attributes = me->attributes_for_write(); if (use_smooth) { + attributes.remove("sharp_edge"); attributes.remove("sharp_face"); } else { + attributes.remove("sharp_edge"); SpanAttributeWriter sharp_faces = attributes.lookup_or_add_for_write_only_span( "sharp_face", ATTR_DOMAIN_FACE); sharp_faces.span.fill(true); @@ -1439,17 +1440,33 @@ void BKE_mesh_smooth_flag_set(Mesh *me, const bool use_smooth) } } -void BKE_mesh_auto_smooth_flag_set(Mesh *me, - const bool use_auto_smooth, - const float auto_smooth_angle) +void BKE_mesh_sharp_edges_set_from_angle(Mesh *me, const float angle) { - if (use_auto_smooth) { - me->flag |= ME_AUTOSMOOTH; - me->smoothresh = auto_smooth_angle; + using namespace blender; + using namespace blender::bke; + bke::MutableAttributeAccessor attributes = me->attributes_for_write(); + if (angle >= M_PI) { + attributes.remove("sharp_edge"); + attributes.remove("sharp_face"); + return; } - else { - me->flag &= ~ME_AUTOSMOOTH; + if (angle == 0.0f) { + BKE_mesh_smooth_flag_set(me, false); + return; } + bke::SpanAttributeWriter sharp_edges = attributes.lookup_or_add_for_write_span( + "sharp_edge", ATTR_DOMAIN_EDGE); + const bool *sharp_faces = static_cast( + CustomData_get_layer_named(&me->face_data, CD_PROP_BOOL, "sharp_face")); + bke::mesh::edges_sharp_from_angle_set(me->faces(), + me->corner_verts(), + me->corner_edges(), + me->face_normals(), + me->corner_to_face_map(), + sharp_faces, + angle, + sharp_edges.span); + sharp_edges.finish(); } void BKE_mesh_looptri_get_real_edges(const blender::int2 *edges, @@ -1506,20 +1523,6 @@ void BKE_mesh_transform(Mesh *me, const float mat[4][4], bool do_keys) } } - /* don't update normals, caller can do this explicitly. - * We do update loop normals though, those may not be auto-generated - * (see e.g. STL import script)! */ - float(*lnors)[3] = (float(*)[3])CustomData_get_layer_for_write( - &me->loop_data, CD_NORMAL, me->totloop); - if (lnors) { - float m3[3][3]; - - copy_m3_m4(m3, mat); - normalize_m3(m3); - for (int i = 0; i < me->totloop; i++, lnors++) { - mul_m3_v3(m3, *lnors); - } - } BKE_mesh_tag_positions_changed(me); } @@ -1731,63 +1734,6 @@ void BKE_mesh_vert_coords_apply_with_mat4(Mesh *mesh, BKE_mesh_tag_positions_changed(mesh); } -static float (*ensure_corner_normal_layer(Mesh &mesh))[3] -{ - float(*r_loop_normals)[3]; - if (CustomData_has_layer(&mesh.loop_data, CD_NORMAL)) { - r_loop_normals = (float(*)[3])CustomData_get_layer_for_write( - &mesh.loop_data, CD_NORMAL, mesh.totloop); - memset(r_loop_normals, 0, sizeof(float[3]) * mesh.totloop); - } - else { - r_loop_normals = (float(*)[3])CustomData_add_layer( - &mesh.loop_data, CD_NORMAL, CD_SET_DEFAULT, mesh.totloop); - CustomData_set_layer_flag(&mesh.loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); - } - return r_loop_normals; -} - -void BKE_mesh_calc_normals_split_ex(const Mesh *mesh, - MLoopNorSpaceArray *r_lnors_spacearr, - float (*r_corner_normals)[3]) -{ - /* Note that we enforce computing clnors when the clnor space array is requested by caller here. - * However, we obviously only use the auto-smooth angle threshold - * only in case auto-smooth is enabled. */ - const bool use_split_normals = (r_lnors_spacearr != nullptr) || - ((mesh->flag & ME_AUTOSMOOTH) != 0); - const float split_angle = (mesh->flag & ME_AUTOSMOOTH) != 0 ? mesh->smoothresh : float(M_PI); - - const blender::short2 *clnors = static_cast( - CustomData_get_layer(&mesh->loop_data, CD_CUSTOMLOOPNORMAL)); - const bool *sharp_edges = static_cast( - CustomData_get_layer_named(&mesh->edge_data, CD_PROP_BOOL, "sharp_edge")); - const bool *sharp_faces = static_cast( - CustomData_get_layer_named(&mesh->face_data, CD_PROP_BOOL, "sharp_face")); - - blender::bke::mesh::normals_calc_loop( - mesh->vert_positions(), - mesh->edges(), - mesh->faces(), - mesh->corner_verts(), - mesh->corner_edges(), - mesh->corner_to_face_map(), - mesh->vert_normals(), - mesh->face_normals(), - sharp_edges, - sharp_faces, - clnors, - use_split_normals, - split_angle, - nullptr, - {reinterpret_cast(r_corner_normals), mesh->totloop}); -} - -void BKE_mesh_calc_normals_split(Mesh *mesh) -{ - BKE_mesh_calc_normals_split_ex(mesh, nullptr, ensure_corner_normal_layer(*mesh)); -} - /* **** Depsgraph evaluation **** */ void BKE_mesh_eval_geometry(Depsgraph *depsgraph, Mesh *mesh) diff --git a/source/blender/blenkernel/intern/mesh_iterators.cc b/source/blender/blenkernel/intern/mesh_iterators.cc index 052cd3d7316..ff16d6cdf72 100644 --- a/source/blender/blenkernel/intern/mesh_iterators.cc +++ b/source/blender/blenkernel/intern/mesh_iterators.cc @@ -167,9 +167,7 @@ void BKE_mesh_foreach_mapped_loop(Mesh *mesh, /* XXX: investigate using EditMesh data. */ blender::Span corner_normals; if (flag & MESH_FOREACH_USE_NORMAL) { - corner_normals = { - static_cast(CustomData_get_layer(&mesh->loop_data, CD_NORMAL)), - mesh->totloop}; + corner_normals = mesh->corner_normals(); } int f_idx; @@ -194,9 +192,7 @@ void BKE_mesh_foreach_mapped_loop(Mesh *mesh, else { blender::Span corner_normals; if (flag & MESH_FOREACH_USE_NORMAL) { - corner_normals = { - static_cast(CustomData_get_layer(&mesh->loop_data, CD_NORMAL)), - mesh->totloop}; + corner_normals = mesh->corner_normals(); } const blender::Span positions = mesh->vert_positions(); diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc index 1af63be2b68..ecd50d0098b 100644 --- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc +++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc @@ -33,10 +33,17 @@ #include "BKE_attribute.hh" #include "BKE_customdata.h" #include "BKE_global.h" +#include "BKE_idprop.hh" #include "BKE_main.h" #include "BKE_mesh.hh" #include "BKE_mesh_legacy_convert.hh" +#include "BKE_modifier.h" #include "BKE_multires.hh" +#include "BKE_node.hh" +#include "BKE_node_runtime.hh" +#include "BKE_node_tree_update.h" + +#include "BLT_translation.h" using blender::MutableSpan; using blender::Span; @@ -2116,3 +2123,168 @@ void BKE_mesh_legacy_convert_polys_to_offsets(Mesh *mesh) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Auto Smooth Conversion + * \{ */ + +static bNodeTree *add_auto_smooth_node_tree(Main &bmain) +{ + bNodeTree *group = ntreeAddTree(&bmain, DATA_("Auto Smooth"), "GeometryNodeTree"); + + group->tree_interface.add_socket(DATA_("Geometry"), + "", + "NodeSocketGeometry", + NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT, + nullptr); + group->tree_interface.add_socket( + DATA_("Angle"), "", "NodeSocketFloatAngle", NODE_INTERFACE_SOCKET_INPUT, nullptr); + + bNode *group_output = nodeAddNode(nullptr, group, "NodeGroupOutput"); + group_output->locx = 480.0f; + group_output->locy = -100.0f; + bNode *group_input_angle = nodeAddNode(nullptr, group, "NodeGroupInput"); + group_input_angle->locx = -420.0f; + group_input_angle->locy = -300.0f; + LISTBASE_FOREACH (bNodeSocket *, socket, &group_input_angle->outputs) { + if (!STREQ(socket->identifier, "Socket_1")) { + socket->flag |= SOCK_HIDDEN; + } + } + bNode *group_input_mesh = nodeAddNode(nullptr, group, "NodeGroupInput"); + group_input_mesh->locx = -60.0f; + group_input_mesh->locy = -100.0f; + LISTBASE_FOREACH (bNodeSocket *, socket, &group_input_mesh->outputs) { + if (!STREQ(socket->identifier, "Socket_0")) { + socket->flag |= SOCK_HIDDEN; + } + } + bNode *shade_smooth_edge = nodeAddNode(nullptr, group, "GeometryNodeSetShadeSmooth"); + shade_smooth_edge->custom1 = ATTR_DOMAIN_EDGE; + shade_smooth_edge->locx = 120.0f; + shade_smooth_edge->locy = -100.0f; + bNode *shade_smooth_face = nodeAddNode(nullptr, group, "GeometryNodeSetShadeSmooth"); + shade_smooth_face->custom1 = ATTR_DOMAIN_FACE; + shade_smooth_face->locx = 300.0f; + shade_smooth_face->locy = -100.0f; + bNode *edge_angle = nodeAddNode(nullptr, group, "GeometryNodeInputMeshEdgeAngle"); + edge_angle->locx = -420.0f; + edge_angle->locy = -220.0f; + bNode *edge_smooth = nodeAddNode(nullptr, group, "GeometryNodeInputEdgeSmooth"); + edge_smooth->locx = -60.0f; + edge_smooth->locy = -160.0f; + bNode *face_smooth = nodeAddNode(nullptr, group, "GeometryNodeInputShadeSmooth"); + face_smooth->locx = -240.0f; + face_smooth->locy = -340.0f; + bNode *boolean_and = nodeAddNode(nullptr, group, "FunctionNodeBooleanMath"); + boolean_and->custom1 = NODE_BOOLEAN_MATH_AND; + boolean_and->locx = -60.0f; + boolean_and->locy = -220.0f; + bNode *less_than_or_equal = nodeAddNode(nullptr, group, "FunctionNodeCompare"); + static_cast(less_than_or_equal->storage)->operation = + NODE_COMPARE_LESS_EQUAL; + less_than_or_equal->locx = -240.0f; + less_than_or_equal->locy = -180.0f; + + nodeAddLink(group, + edge_angle, + nodeFindSocket(edge_angle, SOCK_OUT, "Unsigned Angle"), + less_than_or_equal, + nodeFindSocket(less_than_or_equal, SOCK_IN, "A")); + nodeAddLink(group, + shade_smooth_face, + nodeFindSocket(shade_smooth_face, SOCK_OUT, "Geometry"), + group_output, + nodeFindSocket(group_output, SOCK_IN, "Socket_0")); + nodeAddLink(group, + group_input_angle, + nodeFindSocket(group_input_angle, SOCK_OUT, "Socket_1"), + less_than_or_equal, + nodeFindSocket(less_than_or_equal, SOCK_IN, "B")); + nodeAddLink(group, + less_than_or_equal, + nodeFindSocket(less_than_or_equal, SOCK_OUT, "Result"), + boolean_and, + nodeFindSocket(boolean_and, SOCK_IN, "Boolean")); + nodeAddLink(group, + face_smooth, + nodeFindSocket(face_smooth, SOCK_OUT, "Smooth"), + boolean_and, + nodeFindSocket(boolean_and, SOCK_IN, "Boolean_001")); + nodeAddLink(group, + group_input_mesh, + nodeFindSocket(group_input_mesh, SOCK_OUT, "Socket_0"), + shade_smooth_edge, + nodeFindSocket(shade_smooth_edge, SOCK_IN, "Geometry")); + nodeAddLink(group, + edge_smooth, + nodeFindSocket(edge_smooth, SOCK_OUT, "Smooth"), + shade_smooth_edge, + nodeFindSocket(shade_smooth_edge, SOCK_IN, "Selection")); + nodeAddLink(group, + shade_smooth_edge, + nodeFindSocket(shade_smooth_edge, SOCK_OUT, "Geometry"), + shade_smooth_face, + nodeFindSocket(shade_smooth_face, SOCK_IN, "Geometry")); + nodeAddLink(group, + boolean_and, + nodeFindSocket(boolean_and, SOCK_OUT, "Boolean"), + shade_smooth_edge, + nodeFindSocket(shade_smooth_edge, SOCK_IN, "Shade Smooth")); + + LISTBASE_FOREACH (bNode *, node, &group->nodes) { + nodeSetSelected(node, false); + } + + return group; +} + +void BKE_main_mesh_legacy_convert_auto_smooth(Main &bmain) +{ + using namespace blender; + bNodeTree *auto_smooth_node_tree = nullptr; + LISTBASE_FOREACH (Object *, object, &bmain.objects) { + if (object->type != OB_MESH) { + continue; + } + Mesh *mesh = static_cast(object->data); + if (!(mesh->flag & ME_AUTOSMOOTH_LEGACY)) { + continue; + } + if (CustomData_has_layer(&mesh->loop_data, CD_CUSTOMLOOPNORMAL)) { + continue; + } + if (!auto_smooth_node_tree) { + auto_smooth_node_tree = add_auto_smooth_node_tree(bmain); + BKE_ntree_update_main_tree(&bmain, auto_smooth_node_tree, nullptr); + } + auto *md = reinterpret_cast(BKE_modifier_new(eModifierType_Nodes)); + STRNCPY(md->modifier.name, DATA_("Auto Smooth")); + BKE_modifier_unique_name(&object->modifiers, &md->modifier); + md->node_group = auto_smooth_node_tree; + if (!BLI_listbase_is_empty(&object->modifiers) && + static_cast(object->modifiers.last)->type == eModifierType_Subsurf) + { + /* Add the auto smooth node group before the last subdivision surface modifier if possible. + * Subdivision surface modifiers have special handling for interpolating face corner normals, + * and recalculating them afterwards isn't usually helpful and can be much slower. */ + BLI_insertlinkbefore(&object->modifiers, object->modifiers.last, md); + } + else { + BLI_addtail(&object->modifiers, md); + } + + md->settings.properties = bke::idprop::create_group("Nodes Modifier Settings").release(); + IDProperty *angle_prop = + bke::idprop::create(DATA_("Socket_1"), mesh->smoothresh_legacy).release(); + auto *ui_data = reinterpret_cast(IDP_ui_data_ensure(angle_prop)); + ui_data->base.rna_subtype = PROP_ANGLE; + IDP_AddToGroup(md->settings.properties, angle_prop); + IDP_AddToGroup(md->settings.properties, + bke::idprop::create(DATA_("Input_1_use_attribute"), 0).release()); + IDP_AddToGroup(md->settings.properties, + bke::idprop::create(DATA_("Input_1_attribute_name"), "").release()); + } +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/mesh_mirror.cc b/source/blender/blenkernel/intern/mesh_mirror.cc index 61e66515697..9a415c16163 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.cc +++ b/source/blender/blenkernel/intern/mesh_mirror.cc @@ -15,6 +15,7 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "BKE_attribute.hh" #include "BKE_deform.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" @@ -389,8 +390,8 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, } /* handle custom split normals */ - if (ob->type == OB_MESH && (((Mesh *)ob->data)->flag & ME_AUTOSMOOTH) && - CustomData_has_layer(&result->loop_data, CD_CUSTOMLOOPNORMAL) && result->faces_num > 0) + if (ob->type == OB_MESH && CustomData_has_layer(&result->loop_data, CD_CUSTOMLOOPNORMAL) && + result->faces_num > 0) { blender::Array loop_normals(result_corner_verts.size()); blender::short2 *clnors = static_cast( @@ -420,8 +421,6 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, sharp_edges, sharp_faces, clnors, - true, - result->smoothresh, &lnors_spacearr, loop_normals); diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 599802bba33..640075689f3 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -299,6 +299,42 @@ static void normals_calc_faces_and_verts(const Span positions, /** \name Mesh Normal Calculation * \{ */ +blender::bke::MeshNormalDomain Mesh::normals_domain() const +{ + using namespace blender; + using namespace blender::bke; + if (this->faces_num == 0) { + return MeshNormalDomain::Point; + } + + if (CustomData_has_layer(&this->loop_data, CD_CUSTOMLOOPNORMAL)) { + return MeshNormalDomain::Corner; + } + + const AttributeAccessor attributes = this->attributes(); + const VArray sharp_faces = *attributes.lookup_or_default( + "sharp_face", ATTR_DOMAIN_FACE, false); + + const array_utils::BooleanMix face_mix = array_utils::booleans_mix_calc(sharp_faces); + if (face_mix == array_utils::BooleanMix::AllTrue) { + return MeshNormalDomain::Face; + } + + const VArray sharp_edges = *attributes.lookup_or_default( + "sharp_edge", ATTR_DOMAIN_EDGE, false); + const array_utils::BooleanMix edge_mix = array_utils::booleans_mix_calc(sharp_edges); + if (edge_mix == array_utils::BooleanMix::AllTrue) { + return MeshNormalDomain::Face; + } + + if (edge_mix == array_utils::BooleanMix::AllFalse && + face_mix == array_utils::BooleanMix::AllFalse) { + return MeshNormalDomain::Point; + } + + return MeshNormalDomain::Corner; +} + blender::Span Mesh::vert_normals() const { using namespace blender; @@ -348,25 +384,52 @@ blender::Span Mesh::face_normals() const return this->runtime->face_normals_cache.data(); } -void BKE_mesh_ensure_normals_for_display(Mesh *mesh) +blender::Span Mesh::corner_normals() const { - switch (mesh->runtime->wrapper_type) { - case ME_WRAPPER_TYPE_SUBD: - case ME_WRAPPER_TYPE_MDATA: - mesh->vert_normals(); - mesh->face_normals(); - break; - case ME_WRAPPER_TYPE_BMESH: { - BMEditMesh *em = mesh->edit_mesh; - if (blender::bke::EditMeshData *emd = mesh->runtime->edit_data) { - if (!emd->vertexCos.is_empty()) { - BKE_editmesh_cache_ensure_vert_normals(em, emd); - BKE_editmesh_cache_ensure_face_normals(em, emd); - } + using namespace blender; + using namespace blender::bke; + this->runtime->corner_normals_cache.ensure([&](Vector &r_data) { + r_data.reinitialize(this->totloop); + const OffsetIndices faces = this->faces(); + switch (this->normals_domain()) { + case MeshNormalDomain::Point: { + array_utils::gather(this->vert_normals(), this->corner_verts(), r_data.as_mutable_span()); + break; + } + case MeshNormalDomain::Face: { + const Span face_normals = this->face_normals(); + threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + r_data.as_mutable_span().slice(faces[i]).fill(face_normals[i]); + } + }); + break; + } + case MeshNormalDomain::Corner: { + const bool *sharp_edges = static_cast( + CustomData_get_layer_named(&this->edge_data, CD_PROP_BOOL, "sharp_edge")); + const bool *sharp_faces = static_cast( + CustomData_get_layer_named(&this->face_data, CD_PROP_BOOL, "sharp_face")); + const short2 *custom_normals = static_cast( + CustomData_get_layer(&this->loop_data, CD_CUSTOMLOOPNORMAL)); + mesh::normals_calc_loop(this->vert_positions(), + this->edges(), + this->faces(), + this->corner_verts(), + this->corner_edges(), + this->corner_to_face_map(), + this->vert_normals(), + this->face_normals(), + sharp_edges, + sharp_faces, + custom_normals, + nullptr, + r_data); + break; } - return; } - } + }); + return this->runtime->corner_normals_cache.data(); } void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, @@ -764,7 +827,7 @@ static void mesh_edges_sharp_tag(const OffsetIndices faces, /* We want to avoid tagging edges as sharp when it is already defined as such by * other causes than angle threshold. */ - if (!r_sharp_edges.is_empty() && is_angle_sharp) { + if (is_angle_sharp) { r_sharp_edges[edge_i] = true; } } @@ -778,9 +841,60 @@ static void mesh_edges_sharp_tag(const OffsetIndices faces, /* We want to avoid tagging edges as sharp when it is already defined as such by * other causes than angle threshold. */ - if (!r_sharp_edges.is_empty()) { - r_sharp_edges[edge_i] = false; + r_sharp_edges[edge_i] = false; + } + /* Else, edge is already 'disqualified' (i.e. sharp)! */ + } + } +} + +/** + * Builds a simplified map from edges to face corners, marking special values when + * it encounters sharp edges or borders between faces with flipped winding orders. + */ +static void build_edge_to_loop_map_with_flip_and_sharp(const OffsetIndices faces, + const Span corner_verts, + const Span corner_edges, + const Span sharp_faces, + const Span sharp_edges, + MutableSpan edge_to_loops) +{ + auto face_is_smooth = [&](const int face_i) { + return sharp_faces.is_empty() || !sharp_faces[face_i]; + }; + + for (const int face_i : faces.index_range()) { + for (const int loop_index : faces[face_i]) { + const int vert_i = corner_verts[loop_index]; + const int edge_i = corner_edges[loop_index]; + + int2 &e2l = edge_to_loops[edge_i]; + + /* Check whether current edge might be smooth or sharp */ + if ((e2l[0] | e2l[1]) == 0) { + /* 'Empty' edge until now, set e2l[0] (and e2l[1] to INDEX_UNSET to tag it as unset). */ + e2l[0] = loop_index; + /* We have to check this here too, else we might miss some flat faces!!! */ + e2l[1] = !face_is_smooth(face_i) ? INDEX_INVALID : INDEX_UNSET; + } + else if (e2l[1] == INDEX_UNSET) { + /* Second loop using this edge, time to test its sharpness. + * An edge is sharp if it is tagged as such, or its face is not smooth, + * or both face have opposed (flipped) normals, i.e. both loops on the same edge share the + * same vertex. */ + if (!face_is_smooth(face_i) || (!sharp_edges.is_empty() && sharp_edges[edge_i]) || + vert_i == corner_verts[e2l[0]]) + { + /* NOTE: we are sure that loop != 0 here ;). */ + e2l[1] = INDEX_INVALID; } + else { + e2l[1] = loop_index; + } + } + else if (!IS_EDGE_SHARP(e2l)) { + /* More than two loops using this edge, tag as sharp if not yet done. */ + e2l[1] = INDEX_INVALID; } /* Else, edge is already 'disqualified' (i.e. sharp)! */ } @@ -1209,36 +1323,9 @@ void normals_calc_loop(const Span vert_positions, const bool *sharp_edges, const bool *sharp_faces, const short2 *clnors_data, - bool use_split_normals, - float split_angle, CornerNormalSpaceArray *r_lnors_spacearr, MutableSpan r_loop_normals) { - /* For now this is not supported. - * If we do not use split normals, we do not generate anything fancy! */ - BLI_assert(use_split_normals || !(r_lnors_spacearr)); - - if (!use_split_normals) { - /* In this case, simply fill `r_loop_normals` with `vert_normals` - * (or `face_normals` for flat faces), quite simple! - * Note this is done here to keep some logic and consistency in this quite complex code, - * since we may want to use loop_normals even when mesh's 'autosmooth' is disabled - * (see e.g. mesh mapping code). As usual, we could handle that on case-by-case basis, - * but simpler to keep it well confined here. */ - for (const int face_index : faces.index_range()) { - const bool is_face_flat = sharp_faces && sharp_faces[face_index]; - for (const int corner : faces[face_index]) { - if (is_face_flat) { - copy_v3_v3(r_loop_normals[corner], face_normals[face_index]); - } - else { - copy_v3_v3(r_loop_normals[corner], vert_normals[corner_verts[corner]]); - } - } - } - return; - } - /** * Mapping edge -> loops. * If that edge is used by more than two loops (faces), @@ -1255,9 +1342,6 @@ void normals_calc_loop(const Span vert_positions, * Note also that loose edges always have both values set to 0! */ Array edge_to_loops(edges.size(), int2(0)); - /* When using custom loop normals, disable the angle feature! */ - const bool check_angle = (split_angle < float(M_PI)) && (clnors_data == nullptr); - CornerNormalSpaceArray _lnors_spacearr; #ifdef DEBUG_TIME @@ -1289,17 +1373,13 @@ void normals_calc_loop(const Span vert_positions, array_utils::gather(vert_normals, corner_verts, r_loop_normals, 1024); /* This first loop check which edges are actually smooth, and compute edge vectors. */ - mesh_edges_sharp_tag(faces, - corner_verts, - corner_edges, - loop_to_face_map, - face_normals, - Span(sharp_faces, sharp_faces ? faces.size() : 0), - Span(sharp_edges, sharp_edges ? edges.size() : 0), - check_angle, - split_angle, - edge_to_loops, - {}); + build_edge_to_loop_map_with_flip_and_sharp( + faces, + corner_verts, + corner_edges, + Span(sharp_faces, sharp_faces ? faces.size() : 0), + Span(sharp_edges, sharp_edges ? edges.size() : 0), + edge_to_loops); Vector single_corners; Vector fan_corners; @@ -1367,10 +1447,6 @@ static void mesh_normals_loop_custom_set(Span positions, BitVector<> done_loops(corner_verts.size(), false); Array loop_normals(corner_verts.size()); const Array loop_to_face = build_loop_to_face_map(faces); - /* In this case we always consider split nors as ON, - * and do not want to use angle to define smooth fans! */ - const bool use_split_normals = true; - const float split_angle = float(M_PI); /* Compute current lnor spacearr. */ normals_calc_loop(positions, @@ -1384,8 +1460,6 @@ static void mesh_normals_loop_custom_set(Span positions, sharp_edges.data(), sharp_faces, r_clnors_data.data(), - use_split_normals, - split_angle, &lnors_spacearr, loop_normals); @@ -1507,8 +1581,6 @@ static void mesh_normals_loop_custom_set(Span positions, sharp_edges.data(), sharp_faces, r_clnors_data.data(), - use_split_normals, - split_angle, &lnors_spacearr, loop_normals); } diff --git a/source/blender/blenkernel/intern/mesh_remap.cc b/source/blender/blenkernel/intern/mesh_remap.cc index 8ddf18ba34d..1c98b9e3870 100644 --- a/source/blender/blenkernel/intern/mesh_remap.cc +++ b/source/blender/blenkernel/intern/mesh_remap.cc @@ -303,20 +303,6 @@ void BKE_mesh_remap_find_best_match_from_mesh(const float (*vert_positions_dst)[ /** \name Mesh to Mesh Mapping * \{ */ -void BKE_mesh_remap_calc_source_cddata_masks_from_map_modes(const int /*vert_mode*/, - const int /*edge_mode*/, - const int loop_mode, - const int /*face_mode*/, - CustomData_MeshMasks *r_cddata_mask) -{ - /* vert, edge and face mapping modes never need extra cddata from source object. */ - const bool need_lnors_src = (loop_mode & MREMAP_USE_LOOP) && (loop_mode & MREMAP_USE_NORMAL); - - if (need_lnors_src) { - r_cddata_mask->lmask |= CD_MASK_NORMAL; - } -} - void BKE_mesh_remap_init(MeshPairRemap *map, const int items_num) { MemArena *mem = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); @@ -469,7 +455,6 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode, const float ray_radius, const float (*vert_positions_dst)[3], const int numverts_dst, - const bool /*dirty_nors_dst*/, const Mesh *me_src, Mesh *me_dst, MeshPairRemap *r_map) @@ -695,7 +680,6 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode, const int numverts_dst, const blender::int2 *edges_dst, const int numedges_dst, - const bool /*dirty_nors_dst*/, const Mesh *me_src, Mesh *me_dst, MeshPairRemap *r_map) @@ -1228,19 +1212,12 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, const SpaceTransform *space_transform, const float max_dist, const float ray_radius, - Mesh *mesh_dst, + const Mesh *mesh_dst, const float (*vert_positions_dst)[3], const int numverts_dst, - const blender::int2 *edges_dst, - const int numedges_dst, const int *corner_verts_dst, - const int *corner_edges_dst, const int numloops_dst, const blender::OffsetIndices faces_dst, - CustomData *ldata_dst, - const bool use_split_nors_dst, - const float split_angle_dst, - const bool dirty_nors_dst, const Mesh *me_src, MeshRemapIslandsCalc gen_islands_src, const float islands_precision_src, @@ -1286,7 +1263,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, blender::Span loop_normals_src; blender::Span face_normals_dst; - blender::float3 *loop_normals_dst; + blender::Span loop_normals_dst; blender::Array face_cents_src; @@ -1342,52 +1319,13 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, face_normals_dst = mesh_dst->face_normals(); } if (need_lnors_dst) { - const blender::short2 *custom_nors_dst = static_cast( - CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL)); - - /* Cache loop normals into a temporary custom data layer. */ - loop_normals_dst = static_cast( - CustomData_get_layer_for_write(ldata_dst, CD_NORMAL, numloops_dst)); - - const bool do_loop_normals_dst = (loop_normals_dst == nullptr); - if (!loop_normals_dst) { - loop_normals_dst = static_cast( - CustomData_add_layer(ldata_dst, CD_NORMAL, CD_SET_DEFAULT, numloops_dst)); - CustomData_set_layer_flag(ldata_dst, CD_NORMAL, CD_FLAG_TEMPORARY); - } - if (dirty_nors_dst || do_loop_normals_dst) { - const bool *sharp_edges = static_cast( - CustomData_get_layer_named(&mesh_dst->edge_data, CD_PROP_BOOL, "sharp_edge")); - const bool *sharp_faces = static_cast( - CustomData_get_layer_named(&mesh_dst->face_data, CD_PROP_BOOL, "sharp_face")); - blender::bke::mesh::normals_calc_loop( - {reinterpret_cast(vert_positions_dst), numverts_dst}, - {edges_dst, numedges_dst}, - faces_dst, - {corner_verts_dst, numloops_dst}, - {corner_edges_dst, numloops_dst}, - mesh_dst->corner_to_face_map(), - mesh_dst->vert_normals(), - mesh_dst->face_normals(), - sharp_edges, - sharp_faces, - custom_nors_dst, - use_split_nors_dst, - split_angle_dst, - nullptr, - {loop_normals_dst, numloops_dst}); - } + loop_normals_dst = mesh_dst->corner_normals(); } - if (need_pnors_src || need_lnors_src) { - if (need_pnors_src) { - face_normals_src = me_src->face_normals(); - } - if (need_lnors_src) { - loop_normals_src = {static_cast( - CustomData_get_layer(&me_src->loop_data, CD_NORMAL)), - me_src->totloop}; - BLI_assert(loop_normals_src.data() != nullptr); - } + if (need_pnors_src) { + face_normals_src = me_src->face_normals(); + } + if (need_lnors_src) { + loop_normals_src = me_src->corner_normals(); } } diff --git a/source/blender/blenkernel/intern/mesh_runtime.cc b/source/blender/blenkernel/intern/mesh_runtime.cc index b8ef5358c36..d93336fca62 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.cc +++ b/source/blender/blenkernel/intern/mesh_runtime.cc @@ -355,10 +355,16 @@ void BKE_mesh_tag_edges_split(Mesh *mesh) } } +void BKE_mesh_tag_sharpness_changed(Mesh *mesh) +{ + mesh->runtime->corner_normals_cache.tag_dirty(); +} + void BKE_mesh_tag_face_winding_changed(Mesh *mesh) { mesh->runtime->vert_normals_cache.tag_dirty(); mesh->runtime->face_normals_cache.tag_dirty(); + mesh->runtime->corner_normals_cache.tag_dirty(); mesh->runtime->vert_to_corner_map_cache.tag_dirty(); } @@ -366,6 +372,7 @@ void BKE_mesh_tag_positions_changed(Mesh *mesh) { mesh->runtime->vert_normals_cache.tag_dirty(); mesh->runtime->face_normals_cache.tag_dirty(); + mesh->runtime->corner_normals_cache.tag_dirty(); BKE_mesh_tag_positions_changed_no_normals(mesh); } diff --git a/source/blender/blenkernel/intern/mesh_tangent.cc b/source/blender/blenkernel/intern/mesh_tangent.cc index 4863201c78d..07bfbaf0bf5 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.cc +++ b/source/blender/blenkernel/intern/mesh_tangent.cc @@ -138,20 +138,12 @@ void BKE_mesh_calc_loop_tangent_single(Mesh *mesh, return; } - const float(*loop_normals)[3] = static_cast( - CustomData_get_layer(&mesh->loop_data, CD_NORMAL)); - if (!loop_normals) { - BKE_report( - reports, RPT_ERROR, "Tangent space computation needs loop normals, none found, aborting"); - return; - } - BKE_mesh_calc_loop_tangent_single_ex( reinterpret_cast(mesh->vert_positions().data()), mesh->totvert, mesh->corner_verts().data(), r_looptangents, - loop_normals, + reinterpret_cast(mesh->corner_normals().data()), reinterpret_cast(uv_map.data()), mesh->totloop, mesh->faces(), @@ -606,7 +598,7 @@ void BKE_mesh_calc_loop_tangents(Mesh *me_eval, tangent_names_len, reinterpret_cast(me_eval->vert_normals().data()), reinterpret_cast(me_eval->face_normals().data()), - static_cast(CustomData_get_layer(&me_eval->loop_data, CD_NORMAL)), + reinterpret_cast(me_eval->corner_normals().data()), /* may be nullptr */ static_cast(CustomData_get_layer(&me_eval->vert_data, CD_ORCO)), /* result */ diff --git a/source/blender/blenkernel/intern/mesh_wrapper.cc b/source/blender/blenkernel/intern/mesh_wrapper.cc index 10256a69a2c..e2283ed5cf9 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.cc +++ b/source/blender/blenkernel/intern/mesh_wrapper.cc @@ -130,16 +130,17 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) me->vert_positions_for_write().copy_from(edit_data->vertexCos); me->runtime->is_original_bmesh = false; } + + if (me->runtime->wrapper_type_finalize) { + BKE_mesh_wrapper_deferred_finalize_mdata(me); + } + MEM_delete(me->runtime->edit_data); me->runtime->edit_data = nullptr; break; } } - if (me->runtime->wrapper_type_finalize) { - BKE_mesh_wrapper_deferred_finalize_mdata(me, &me->runtime->cd_mask_extra); - } - /* Keep type assignment last, so that read-only access only uses the mdata code paths after all * the underlying data has been initialized. */ me->runtime->wrapper_type = ME_WRAPPER_TYPE_MDATA; @@ -384,22 +385,17 @@ static Mesh *mesh_wrapper_ensure_subdivision(Mesh *me) if (use_clnors) { /* If custom normals are present and the option is turned on calculate the split * normals and clear flag so the normals get interpolated to the result mesh. */ - BKE_mesh_calc_normals_split(me); - CustomData_clear_layer_flag(&me->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); + void *data = CustomData_add_layer(&me->loop_data, CD_NORMAL, CD_CONSTRUCT, me->totloop); + memcpy(data, me->corner_normals().data(), me->corner_normals().size_in_bytes()); } Mesh *subdiv_mesh = BKE_subdiv_to_mesh(subdiv, &mesh_settings, me); if (use_clnors) { - float(*lnors)[3] = static_cast( - CustomData_get_layer_for_write(&subdiv_mesh->loop_data, CD_NORMAL, subdiv_mesh->totloop)); - BLI_assert(lnors != nullptr); - BKE_mesh_set_custom_normals(subdiv_mesh, lnors); - CustomData_set_layer_flag(&me->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); - CustomData_set_layer_flag(&subdiv_mesh->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); - } - else if (runtime_data->calc_loop_normals) { - BKE_mesh_calc_normals_split(subdiv_mesh); + BKE_mesh_set_custom_normals(subdiv_mesh, + static_cast(CustomData_get_layer_for_write( + &subdiv_mesh->loop_data, CD_NORMAL, me->totloop))); + CustomData_free_layers(&subdiv_mesh->loop_data, CD_NORMAL, me->totloop); } if (!ELEM(subdiv, runtime_data->subdiv_cpu, runtime_data->subdiv_gpu)) { diff --git a/source/blender/blenkernel/intern/shrinkwrap.cc b/source/blender/blenkernel/intern/shrinkwrap.cc index 059874b10a4..42f429cb26c 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.cc +++ b/source/blender/blenkernel/intern/shrinkwrap.cc @@ -27,6 +27,7 @@ #include "BLI_utildefines.h" #include "BKE_DerivedMesh.h" +#include "BKE_attribute.h" #include "BKE_cdderivedmesh.h" #include "BKE_context.h" #include "BKE_lattice.h" @@ -141,9 +142,8 @@ bool BKE_shrinkwrap_init_tree( if (force_normals || BKE_shrinkwrap_needs_normals(shrinkType, shrinkMode)) { data->face_normals = reinterpret_cast(mesh->face_normals().data()); - if ((mesh->flag & ME_AUTOSMOOTH) != 0) { - data->clnors = static_cast( - CustomData_get_layer(&mesh->loop_data, CD_NORMAL)); + if (mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner) { + data->clnors = reinterpret_cast(mesh->corner_normals().data()); } } diff --git a/source/blender/blenkernel/intern/subdiv_modifier.cc b/source/blender/blenkernel/intern/subdiv_modifier.cc index 9261088b9df..b1c61e80486 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.cc +++ b/source/blender/blenkernel/intern/subdiv_modifier.cc @@ -2,6 +2,7 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_attribute.h" #include "BKE_subdiv_modifier.hh" #include "MEM_guardedalloc.h" @@ -85,14 +86,8 @@ static ModifierData *modifier_get_last_enabled_for_mode(const Scene *scene, bool BKE_subsurf_modifier_use_custom_loop_normals(const SubsurfModifierData *smd, const Mesh *mesh) { - return (smd->flags & eSubsurfModifierFlag_UseCustomNormals) && (mesh->flag & ME_AUTOSMOOTH) && - CustomData_has_layer(&mesh->loop_data, CD_CUSTOMLOOPNORMAL); -} - -static bool subsurf_modifier_use_autosmooth_or_split_normals(const SubsurfModifierData *smd, - const Mesh *mesh) -{ - return (mesh->flag & ME_AUTOSMOOTH) || BKE_subsurf_modifier_use_custom_loop_normals(smd, mesh); + return smd->flags & eSubsurfModifierFlag_UseCustomNormals && + mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner; } static bool is_subdivision_evaluation_possible_on_gpu() @@ -126,7 +121,7 @@ bool BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(const SubsurfMod return false; } - return subsurf_modifier_use_autosmooth_or_split_normals(smd, mesh); + return BKE_subsurf_modifier_use_custom_loop_normals(smd, mesh); } bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene, @@ -141,7 +136,7 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene, /* Deactivate GPU subdivision if autosmooth or custom split normals are used as those are * complicated to support on GPU, and should really be separate workflows. */ - if (subsurf_modifier_use_autosmooth_or_split_normals(smd, mesh)) { + if (BKE_subsurf_modifier_use_custom_loop_normals(smd, mesh)) { return false; } diff --git a/source/blender/blenloader/intern/versioning_270.cc b/source/blender/blenloader/intern/versioning_270.cc index 2aca6000c64..24e58b003e7 100644 --- a/source/blender/blenloader/intern/versioning_270.cc +++ b/source/blender/blenloader/intern/versioning_270.cc @@ -529,9 +529,9 @@ void blo_do_versions_270(FileData *fd, Library * /*lib*/, Main *bmain) } if (!MAIN_VERSION_FILE_ATLEAST(bmain, 270, 2)) { - /* Mesh smoothresh deg->rad. */ + /* Mesh smoothresh_legacy deg->rad. */ LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) { - me->smoothresh = DEG2RADF(me->smoothresh); + me->smoothresh_legacy = DEG2RADF(me->smoothresh_legacy); } } diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 6a45b329e94..d68be18250e 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -25,6 +25,7 @@ #include "BKE_lib_override.hh" #include "BKE_main.h" #include "BKE_main_namemap.h" +#include "BKE_mesh_legacy_convert.hh" #include "BKE_node.hh" #include "BKE_node_runtime.hh" @@ -526,4 +527,8 @@ void do_versions_after_setup(Main *new_bmain, BlendFileReadReport *reports) /* Does not add any new IDs, but needs the full Main data-base. */ BKE_lib_override_library_main_hierarchy_root_ensure(new_bmain); } + + if (!blendfile_or_libraries_versions_atleast(new_bmain, 401, 2)) { + BKE_main_mesh_legacy_convert_auto_smooth(*new_bmain); + } } diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index d8d234c0aa6..7517b2ff58b 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -13,6 +13,8 @@ * To update preference defaults see `userdef_default.c`. */ +#define DNA_DEPRECATED_ALLOW + #include "MEM_guardedalloc.h" #include "BLI_listbase.h" @@ -591,7 +593,7 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) { /* Match default for new meshes. */ - mesh->smoothresh = DEG2RADF(30); + mesh->smoothresh_legacy = DEG2RADF(30); /* Match voxel remesher options for all existing meshes in templates. */ mesh->flag |= ME_REMESH_REPROJECT_VOLUME | ME_REMESH_REPROJECT_PAINT_MASK | ME_REMESH_REPROJECT_SCULPT_FACE_SETS | ME_REMESH_REPROJECT_VERTEX_COLORS; diff --git a/source/blender/blenloader/intern/versioning_legacy.cc b/source/blender/blenloader/intern/versioning_legacy.cc index 7370d3c9ea0..71a86b672c8 100644 --- a/source/blender/blenloader/intern/versioning_legacy.cc +++ b/source/blender/blenloader/intern/versioning_legacy.cc @@ -635,7 +635,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) if (bmain->versionfile <= 164) { Mesh *me = static_cast(bmain->meshes.first); while (me) { - me->smoothresh = 30; + me->smoothresh_legacy = 30; me = static_cast(me->id.next); } } diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.cc b/source/blender/bmesh/intern/bmesh_mesh_normals.cc index 59316dcb17c..bd7d8fc90e5 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.cc @@ -1697,14 +1697,12 @@ void BM_loops_calc_normal_vcos(BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], const bool use_split_normals, - const float split_angle, float (*r_lnos)[3], MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset, const bool do_rebuild) { - const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); if (use_split_normals) { bm_mesh_loops_calc_normals(bm, @@ -1715,7 +1713,7 @@ void BM_loops_calc_normal_vcos(BMesh *bm, clnors_data, cd_loop_clnors_offset, do_rebuild, - has_clnors ? -1.0f : cosf(split_angle)); + -1.0f); } else { BLI_assert(!r_lnors_spacearr); @@ -1744,7 +1742,6 @@ void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3]) nullptr, nullptr, true, - M_PI, r_lnors, bm->lnor_spacearr, nullptr, @@ -1871,7 +1868,6 @@ void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor) nullptr, nullptr, true, - M_PI, r_lnors, bm->lnor_spacearr, nullptr, @@ -1947,17 +1943,8 @@ void BM_lnorspace_err(BMesh *bm) int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); float(*lnors)[3] = static_cast(MEM_callocN(sizeof(*lnors) * bm->totloop, __func__)); - BM_loops_calc_normal_vcos(bm, - nullptr, - nullptr, - nullptr, - true, - M_PI, - lnors, - temp, - nullptr, - cd_loop_clnors_offset, - true); + BM_loops_calc_normal_vcos( + bm, nullptr, nullptr, nullptr, true, lnors, temp, nullptr, cd_loop_clnors_offset, true); for (int i = 0; i < bm->totloop; i++) { int j = 0; diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.h b/source/blender/bmesh/intern/bmesh_mesh_normals.h index b2b94d3591e..d56eddd83e5 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.h +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.h @@ -62,7 +62,6 @@ void BM_loops_calc_normal_vcos(BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], bool use_split_normals, - float split_angle, float (*r_lnos)[3], struct MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], diff --git a/source/blender/bmesh/intern/bmesh_opdefines.cc b/source/blender/bmesh/intern/bmesh_opdefines.cc index 545cec9eb54..573119449c2 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.cc +++ b/source/blender/bmesh/intern/bmesh_opdefines.cc @@ -1816,7 +1816,6 @@ static BMOpDefine bmo_bevel_def = { {"miter_inner", BMO_OP_SLOT_INT, to_subtype_union(BMO_OP_SLOT_SUBTYPE_INT_ENUM), bmo_enum_bevel_miter_type}, /* outer miter kind */ {"spread", BMO_OP_SLOT_FLT}, /* amount to offset beveled edge */ - {"smoothresh", BMO_OP_SLOT_FLT}, /* for passing mesh's smoothresh, used in hardening */ {"custom_profile", BMO_OP_SLOT_PTR, to_subtype_union(BMO_OP_SLOT_SUBTYPE_PTR_STRUCT)}, /* CurveProfile, if None ignored */ {"vmesh_method", BMO_OP_SLOT_INT, to_subtype_union(BMO_OP_SLOT_SUBTYPE_INT_ENUM), bmo_enum_bevel_vmesh_method}, /* The method to use to create meshes at intersections. */ diff --git a/source/blender/bmesh/operators/bmo_bevel.cc b/source/blender/bmesh/operators/bmo_bevel.cc index 1532459dd58..58c839f1d20 100644 --- a/source/blender/bmesh/operators/bmo_bevel.cc +++ b/source/blender/bmesh/operators/bmo_bevel.cc @@ -35,7 +35,6 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op) const int miter_outer = BMO_slot_int_get(op->slots_in, "miter_outer"); const int miter_inner = BMO_slot_int_get(op->slots_in, "miter_inner"); const float spread = BMO_slot_float_get(op->slots_in, "spread"); - const float smoothresh = BMO_slot_float_get(op->slots_in, "smoothresh"); const CurveProfile *custom_profile = static_cast( BMO_slot_ptr_get(op->slots_in, "custom_profile")); const int vmesh_method = BMO_slot_int_get(op->slots_in, "vmesh_method"); @@ -82,7 +81,6 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op) miter_outer, miter_inner, spread, - smoothresh, custom_profile, vmesh_method); diff --git a/source/blender/bmesh/tools/bmesh_bevel.cc b/source/blender/bmesh/tools/bmesh_bevel.cc index 3e4ec78f1fa..fbfe8874eb4 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.cc +++ b/source/blender/bmesh/tools/bmesh_bevel.cc @@ -378,8 +378,6 @@ struct BevelParams { int vmesh_method; /** Amount to spread when doing inside miter. */ float spread; - /** Mesh's smoothresh, used if hardening. */ - float smoothresh; }; // #pragma GCC diagnostic ignored "-Wpadded" @@ -2497,7 +2495,6 @@ static void bevel_harden_normals(BevelParams *bp, BMesh *bm) * To get that to happen, we have to mark the sharpen the edges that are only sharp because * of the angle test -- otherwise would be smooth. */ if (cd_clnors_offset == -1) { - BM_edges_sharp_from_angle_set(bm, bp->smoothresh); bevel_edges_sharp_boundary(bm, bp); } @@ -7725,7 +7722,6 @@ void BM_mesh_bevel(BMesh *bm, const int miter_outer, const int miter_inner, const float spread, - const float smoothresh, const CurveProfile *custom_profile, const int vmesh_method) { @@ -7762,7 +7758,6 @@ void BM_mesh_bevel(BMesh *bm, bp.miter_outer = miter_outer; bp.miter_inner = miter_inner; bp.spread = spread; - bp.smoothresh = smoothresh; bp.face_hash = nullptr; bp.profile_type = profile_type; bp.custom_profile = custom_profile; diff --git a/source/blender/bmesh/tools/bmesh_bevel.h b/source/blender/bmesh/tools/bmesh_bevel.h index fa6c497f750..2f9f15e3bc5 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.h +++ b/source/blender/bmesh/tools/bmesh_bevel.h @@ -47,7 +47,6 @@ void BM_mesh_bevel(BMesh *bm, int miter_outer, int miter_inner, float spread, - float smoothresh, const struct CurveProfile *custom_profile, int vmesh_method); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc index e5594396362..c3b5ee31834 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc @@ -11,9 +11,12 @@ #include "MEM_guardedalloc.h" #include "BLI_array.hh" +#include "BLI_array_utils.hh" #include "BLI_enumerable_thread_specific.hh" +#include "BLI_index_mask.hh" #include "BLI_math_matrix.h" #include "BLI_task.hh" +#include "BLI_virtual_array.hh" #include "BKE_attribute.hh" #include "BKE_editmesh.h" @@ -352,39 +355,70 @@ void mesh_render_data_update_looptris(MeshRenderData &mr, } } +static bool bm_edge_is_sharp(const BMEdge *const &edge) +{ + return !BM_elem_flag_test(edge, BM_ELEM_SMOOTH); +} + +static bool bm_face_is_sharp(const BMFace *const &face) +{ + return !BM_elem_flag_test(face, BM_ELEM_SMOOTH); +} + +/** + * Returns whether loop normals are required because of mixed sharp and smooth flags. + * Similar to #Mesh::normals_domain(). + */ +static bool bm_loop_normals_required(BMesh *bm) +{ + using namespace blender; + using namespace blender::bke; + if (bm->totface == 0) { + return false; + } + + if (CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) { + return true; + } + + BM_mesh_elem_table_ensure(bm, BM_FACE); + const VArray sharp_faces = VArray::ForDerivedSpan( + Span(bm->ftable, bm->totface)); + const array_utils::BooleanMix face_mix = array_utils::booleans_mix_calc(sharp_faces); + if (face_mix == array_utils::BooleanMix::AllTrue) { + return false; + } + + BM_mesh_elem_table_ensure(bm, BM_EDGE); + const VArray sharp_edges = VArray::ForDerivedSpan( + Span(bm->etable, bm->totedge)); + const array_utils::BooleanMix edge_mix = array_utils::booleans_mix_calc(sharp_edges); + if (edge_mix == array_utils::BooleanMix::AllTrue) { + return false; + } + + if (edge_mix == array_utils::BooleanMix::AllFalse && + face_mix == array_utils::BooleanMix::AllFalse) { + return false; + } + + return true; +} + void mesh_render_data_update_normals(MeshRenderData &mr, const eMRDataType data_flag) { - Mesh *me = mr.me; - const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; - const float split_angle = is_auto_smooth ? me->smoothresh : float(M_PI); - if (mr.extract_type != MR_EXTRACT_BMESH) { /* Mesh */ mr.vert_normals = mr.me->vert_normals(); if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) { mr.face_normals = mr.me->face_normals(); } - if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { - mr.loop_normals.reinitialize(mr.corner_verts.size()); - const blender::short2 *clnors = static_cast( - CustomData_get_layer(&mr.me->loop_data, CD_CUSTOMLOOPNORMAL)); - const bool *sharp_edges = static_cast( - CustomData_get_layer_named(&mr.me->edge_data, CD_PROP_BOOL, "sharp_edge")); - blender::bke::mesh::normals_calc_loop(mr.vert_positions, - mr.edges, - mr.faces, - mr.corner_verts, - mr.corner_edges, - mr.me->corner_to_face_map(), - mr.vert_normals, - mr.face_normals, - sharp_edges, - mr.sharp_faces, - clnors, - is_auto_smooth, - split_angle, - nullptr, - mr.loop_normals); + if (((data_flag & MR_DATA_LOOP_NOR) && ELEM(mr.me->normals_domain(), + blender::bke::MeshNormalDomain::Corner, + blender::bke::MeshNormalDomain::Face)) || + (data_flag & MR_DATA_TAN_LOOP_NOR)) + { + mr.loop_normals = mr.me->corner_normals(); } } else { @@ -392,7 +426,9 @@ void mesh_render_data_update_normals(MeshRenderData &mr, const eMRDataType data_ if (data_flag & MR_DATA_POLY_NOR) { /* Use #BMFace.no instead. */ } - if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { + if (((data_flag & MR_DATA_LOOP_NOR) && bm_loop_normals_required(mr.bm)) || + (data_flag & MR_DATA_TAN_LOOP_NOR)) + { const float(*vert_coords)[3] = nullptr; const float(*vert_normals)[3] = nullptr; @@ -404,19 +440,19 @@ void mesh_render_data_update_normals(MeshRenderData &mr, const eMRDataType data_ face_normals = reinterpret_cast(mr.bm_face_normals.data()); } - mr.loop_normals.reinitialize(mr.loop_len); + mr.bm_loop_normals.reinitialize(mr.loop_len); const int clnors_offset = CustomData_get_offset(&mr.bm->ldata, CD_CUSTOMLOOPNORMAL); BM_loops_calc_normal_vcos(mr.bm, vert_coords, vert_normals, face_normals, - is_auto_smooth, - split_angle, - reinterpret_cast(mr.loop_normals.data()), + true, + reinterpret_cast(mr.bm_loop_normals.data()), nullptr, nullptr, clnors_offset, false); + mr.loop_normals = mr.bm_loop_normals; } } } diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index b880a83c1ca..5aacb8aa5a6 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -2152,9 +2152,8 @@ static bool draw_subdiv_create_requested_buffers(Object *ob, runtime_data->stats_totloop = draw_cache.num_subdiv_loops; draw_cache.use_custom_loop_normals = (runtime_data->use_loop_normals) && - (mesh_eval->flag & ME_AUTOSMOOTH) && - CustomData_has_layer(&mesh_eval->loop_data, - CD_CUSTOMLOOPNORMAL); + mesh_eval->normals_domain() == + blender::bke::MeshNormalDomain::Corner; if (DRW_ibo_requested(mbc.buff.ibo.tris)) { draw_subdiv_cache_ensure_mat_offsets(draw_cache, mesh_eval, batch_cache.mat_len); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh index 92adf795b86..4aebdde050f 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh @@ -67,6 +67,7 @@ struct MeshRenderData { blender::Span bm_vert_normals; blender::Span bm_face_normals; blender::Span bm_face_centers; + blender::Array bm_loop_normals; const int *v_origindex, *e_origindex, *p_origindex; int edge_crease_ofs; @@ -91,6 +92,7 @@ struct MeshRenderData { const int *material_indices; blender::Span vert_normals; blender::Span face_normals; + blender::Span loop_normals; const bool *hide_vert; const bool *hide_edge; const bool *hide_poly; @@ -98,7 +100,6 @@ struct MeshRenderData { const bool *select_edge; const bool *select_poly; const bool *sharp_faces; - blender::Array loop_normals; blender::Span loose_verts; blender::Span loose_edges; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc index 831688e8d23..5185a0a8a16 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc @@ -258,18 +258,16 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache &subdiv_cache, draw_subdiv_extract_pos_nor(subdiv_cache, flags_buffer, vbo, orco_vbo); if (subdiv_cache.use_custom_loop_normals) { - Mesh *coarse_mesh = subdiv_cache.mesh; - const float(*loop_normals)[3] = static_cast( - CustomData_get_layer(&coarse_mesh->loop_data, CD_NORMAL)); - BLI_assert(loop_normals != nullptr); + const Mesh *coarse_mesh = subdiv_cache.mesh; + const Span corner_normals = coarse_mesh->corner_normals(); GPUVertBuf *src_custom_normals = GPU_vertbuf_calloc(); GPU_vertbuf_init_with_format(src_custom_normals, get_custom_normals_format()); GPU_vertbuf_data_alloc(src_custom_normals, coarse_mesh->totloop); memcpy(GPU_vertbuf_get_data(src_custom_normals), - loop_normals, - sizeof(float[3]) * coarse_mesh->totloop); + corner_normals.data(), + corner_normals.size_in_bytes()); GPUVertBuf *dst_custom_normals = GPU_vertbuf_calloc(); GPU_vertbuf_init_build_on_device( diff --git a/source/blender/editors/include/ED_screen.hh b/source/blender/editors/include/ED_screen.hh index e05111fea6d..a67d4ab8b93 100644 --- a/source/blender/editors/include/ED_screen.hh +++ b/source/blender/editors/include/ED_screen.hh @@ -543,7 +543,6 @@ bool ED_operator_editable_mesh(bContext *C); bool ED_operator_editmesh(bContext *C); bool ED_operator_editmesh_view3d(bContext *C); bool ED_operator_editmesh_region_view3d(bContext *C); -bool ED_operator_editmesh_auto_smooth(bContext *C); bool ED_operator_editarmature(bContext *C); bool ED_operator_editcurve(bContext *C); bool ED_operator_editcurve_3d(bContext *C); diff --git a/source/blender/editors/mesh/editmesh_bevel.cc b/source/blender/editors/mesh/editmesh_bevel.cc index 1669483eb66..4d79dd0abfe 100644 --- a/source/blender/editors/mesh/editmesh_bevel.cc +++ b/source/blender/editors/mesh/editmesh_bevel.cc @@ -336,20 +336,13 @@ static bool edbm_bevel_calc(wmOperator *op) const int material = CLAMPIS(material_init, -1, obedit->totcol - 1); - Mesh *me = static_cast(obedit->data); - - if (harden_normals && !(me->flag & ME_AUTOSMOOTH)) { - /* `harden_normals` only has a visible effect if auto-smooth is on, so turn it on. */ - me->flag |= ME_AUTOSMOOTH; - } - EDBM_op_init(em, &bmop, op, "bevel geom=%hev offset=%f segments=%i affect=%i offset_type=%i " "profile_type=%i profile=%f clamp_overlap=%b material=%i loop_slide=%b " "mark_seam=%b mark_sharp=%b harden_normals=%b face_strength_mode=%i " - "miter_outer=%i miter_inner=%i spread=%f smoothresh=%f custom_profile=%p " + "miter_outer=%i miter_inner=%i spread=%f custom_profile=%p " "vmesh_method=%i", BM_ELEM_SELECT, offset, @@ -368,7 +361,6 @@ static bool edbm_bevel_calc(wmOperator *op) miter_outer, miter_inner, spread, - me->smoothresh, opdata->custom_profile, vmesh_method); diff --git a/source/blender/editors/mesh/editmesh_tools.cc b/source/blender/editors/mesh/editmesh_tools.cc index 0b0809b0cd7..a9a88eba6c1 100644 --- a/source/blender/editors/mesh/editmesh_tools.cc +++ b/source/blender/editors/mesh/editmesh_tools.cc @@ -8449,8 +8449,7 @@ static bool point_normals_init(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; - BKE_editmesh_ensure_autosmooth(em, static_cast(obedit->data)); - BKE_editmesh_lnorspace_update(em, static_cast(obedit->data)); + BKE_editmesh_lnorspace_update(em); BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, false); op->customdata = lnors_ed_arr; @@ -9075,8 +9074,7 @@ static int normals_split_merge(bContext *C, const bool do_merge) BMEdge *e; BMIter eiter; - BKE_editmesh_ensure_autosmooth(em, static_cast(obedit->data)); - BKE_editmesh_lnorspace_update(em, static_cast(obedit->data)); + BKE_editmesh_lnorspace_update(em); /* Note that we need temp lnor editing data for all loops of all affected vertices, since by * setting some faces/edges as smooth we are going to change clnors spaces... See also #65809. @@ -9094,7 +9092,7 @@ static int normals_split_merge(bContext *C, const bool do_merge) } bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; - BKE_editmesh_lnorspace_update(em, static_cast(obedit->data)); + BKE_editmesh_lnorspace_update(em); if (do_merge) { normals_merge(bm, lnors_ed_arr); @@ -9214,9 +9212,8 @@ static int edbm_average_normals_exec(bContext *C, wmOperator *op) BMLoop *l, *l_curr, *l_first; BMIter fiter; - BKE_editmesh_ensure_autosmooth(em, static_cast(obedit->data)); bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; - BKE_editmesh_lnorspace_update(em, static_cast(obedit->data)); + BKE_editmesh_lnorspace_update(em); const int cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); @@ -9466,8 +9463,7 @@ static int edbm_normals_tools_exec(bContext *C, wmOperator *op) continue; } - BKE_editmesh_ensure_autosmooth(em, static_cast(obedit->data)); - BKE_editmesh_lnorspace_update(em, static_cast(obedit->data)); + BKE_editmesh_lnorspace_update(em); BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, false); BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; @@ -9690,8 +9686,7 @@ static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op) const bool keep_sharp = RNA_boolean_get(op->ptr, "keep_sharp"); - BKE_editmesh_ensure_autosmooth(em, static_cast(obedit->data)); - BKE_editmesh_lnorspace_update(em, static_cast(obedit->data)); + BKE_editmesh_lnorspace_update(em); float(*vert_normals)[3] = static_cast( MEM_mallocN(sizeof(*vert_normals) * bm->totvert, __func__)); @@ -9799,8 +9794,7 @@ static int edbm_smooth_normals_exec(bContext *C, wmOperator *op) BMLoop *l; BMIter fiter, liter; - BKE_editmesh_ensure_autosmooth(em, static_cast(obedit->data)); - BKE_editmesh_lnorspace_update(em, static_cast(obedit->data)); + BKE_editmesh_lnorspace_update(em); BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, false); float(*smooth_normal)[3] = static_cast( diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index b3157cd7d19..e2a915248e8 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -723,34 +723,9 @@ static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator if (me->edit_mesh) { BMesh &bm = *me->edit_mesh->bm; - /* Tag edges as sharp according to smooth threshold if needed, - * to preserve auto-smooth shading. */ - if (me->flag & ME_AUTOSMOOTH) { - BM_edges_sharp_from_angle_set(&bm, me->smoothresh); - } - BM_data_layer_add(&bm, &bm.ldata, CD_CUSTOMLOOPNORMAL); } else { - /* Tag edges as sharp according to smooth threshold if needed, - * to preserve auto-smooth shading. */ - if (me->flag & ME_AUTOSMOOTH) { - bke::MutableAttributeAccessor attributes = me->attributes_for_write(); - bke::SpanAttributeWriter sharp_edges = attributes.lookup_or_add_for_write_span( - "sharp_edge", ATTR_DOMAIN_EDGE); - const bool *sharp_faces = static_cast( - CustomData_get_layer_named(&me->face_data, CD_PROP_BOOL, "sharp_face")); - bke::mesh::edges_sharp_from_angle_set(me->faces(), - me->corner_verts(), - me->corner_edges(), - me->face_normals(), - me->corner_to_face_map(), - sharp_faces, - me->smoothresh, - sharp_edges.span); - sharp_edges.finish(); - } - CustomData_add_layer(&me->loop_data, CD_CUSTOMLOOPNORMAL, CD_SET_DEFAULT, me->totloop); } @@ -1154,9 +1129,7 @@ void ED_mesh_split_faces(Mesh *mesh) { using namespace blender; const OffsetIndices polys = mesh->faces(); - const Span corner_verts = mesh->corner_verts(); const Span corner_edges = mesh->corner_edges(); - const float split_angle = (mesh->flag & ME_AUTOSMOOTH) != 0 ? mesh->smoothresh : float(M_PI); const bke::AttributeAccessor attributes = mesh->attributes(); const VArray mesh_sharp_edges = *attributes.lookup_or_default( "sharp_edge", ATTR_DOMAIN_EDGE, false); @@ -1166,15 +1139,6 @@ void ED_mesh_split_faces(Mesh *mesh) Array sharp_edges(mesh->totedge); mesh_sharp_edges.materialize(sharp_edges); - bke::mesh::edges_sharp_from_angle_set(polys, - corner_verts, - corner_edges, - mesh->face_normals(), - mesh->corner_to_face_map(), - sharp_faces, - split_angle, - sharp_edges); - threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) { for (const int face_i : range) { if (sharp_faces && sharp_faces[face_i]) { diff --git a/source/blender/editors/object/object_bake_api.cc b/source/blender/editors/object/object_bake_api.cc index b15a4741109..3503f9785c2 100644 --- a/source/blender/editors/object/object_bake_api.cc +++ b/source/blender/editors/object/object_bake_api.cc @@ -712,9 +712,8 @@ static Mesh *bake_mesh_new_from_object(Depsgraph *depsgraph, { Mesh *me = BKE_mesh_new_from_object(depsgraph, object, false, preserve_origindex); - if (me->flag & ME_AUTOSMOOTH) { + if (me->normals_domain() == blender::bke::MeshNormalDomain::Corner) { ED_mesh_split_faces(me); - CustomData_free_layers(&me->loop_data, CD_NORMAL, me->totloop); } return me; diff --git a/source/blender/editors/object/object_data_transfer.cc b/source/blender/editors/object/object_data_transfer.cc index 034a52983cd..6b0b28d72eb 100644 --- a/source/blender/editors/object/object_data_transfer.cc +++ b/source/blender/editors/object/object_data_transfer.cc @@ -522,11 +522,6 @@ static int data_transfer_exec(bContext *C, wmOperator *op) false, op->reports)) { - - if (data_type == DT_TYPE_LNOR && use_create) { - ((Mesh *)ob_dst->data)->flag |= ME_AUTOSMOOTH; - } - DEG_id_tag_update(&ob_dst->id, ID_RECALC_GEOMETRY); changed = true; } diff --git a/source/blender/editors/object/object_edit.cc b/source/blender/editors/object/object_edit.cc index 1e50bbe0c56..6f4879a1772 100644 --- a/source/blender/editors/object/object_edit.cc +++ b/source/blender/editors/object/object_edit.cc @@ -1567,6 +1567,7 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot) static int shade_smooth_exec(bContext *C, wmOperator *op) { const bool use_smooth = STREQ(op->idname, "OBJECT_OT_shade_smooth"); + const bool use_smooth_by_angle = STREQ(op->idname, "OBJECT_OT_shade_smooth_by_angle"); bool changed_multi = false; bool has_linked_data = false; @@ -1615,12 +1616,12 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) bool changed = false; if (ob->type == OB_MESH) { - BKE_mesh_smooth_flag_set(static_cast(ob->data), use_smooth); - if (use_smooth) { - const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth"); - const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle"); - BKE_mesh_auto_smooth_flag_set( - static_cast(ob->data), use_auto_smooth, auto_smooth_angle); + BKE_mesh_smooth_flag_set(static_cast(ob->data), use_smooth || use_smooth_by_angle); + if (use_smooth || use_smooth_by_angle) { + if (use_smooth_by_angle) { + const float angle = RNA_float_get(op->ptr, "angle"); + BKE_mesh_sharp_edges_set_from_angle(static_cast(ob->data), angle); + } } BKE_mesh_batch_cache_dirty_tag(static_cast(ob->data), BKE_MESH_BATCH_DIRTY_ALL); changed = true; @@ -1694,25 +1695,25 @@ void OBJECT_OT_shade_smooth(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} - /* properties */ - PropertyRNA *prop; +void OBJECT_OT_shade_smooth_by_angle(wmOperatorType *ot) +{ + ot->name = "Shade Smooth by Angle"; + ot->description = + "Set the sharpness of mesh edges based on the angle between the neighboring faces"; + ot->idname = "OBJECT_OT_shade_smooth_by_angle"; - prop = RNA_def_boolean( - ot->srna, - "use_auto_smooth", - false, - "Auto Smooth", - "Enable automatic smooth based on smooth/sharp faces/edges and angle between faces"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + ot->poll = shade_poll; + ot->exec = shade_smooth_exec; - prop = RNA_def_property(ot->srna, "auto_smooth_angle", PROP_FLOAT, PROP_ANGLE); + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + PropertyRNA *prop = RNA_def_property(ot->srna, "angle", PROP_FLOAT, PROP_ANGLE); RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f)); RNA_def_property_float_default(prop, DEG2RADF(30.0f)); - RNA_def_property_ui_text(prop, - "Angle", - "Maximum angle between face normals that will be considered as smooth " - "(unused if custom split normals data are available)"); + RNA_def_property_ui_text( + prop, "Angle", "Maximum angle between face normals that will be considered as smooth"); } /** \} */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 5d5c0c056f7..d63676d8f8c 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -83,6 +83,7 @@ void OBJECT_OT_mode_set_with_submode(struct wmOperatorType *ot); void OBJECT_OT_editmode_toggle(struct wmOperatorType *ot); void OBJECT_OT_posemode_toggle(struct wmOperatorType *ot); void OBJECT_OT_shade_smooth(struct wmOperatorType *ot); +void OBJECT_OT_shade_smooth_by_angle(struct wmOperatorType *ot); void OBJECT_OT_shade_flat(struct wmOperatorType *ot); void OBJECT_OT_paths_calculate(struct wmOperatorType *ot); void OBJECT_OT_paths_update(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.cc b/source/blender/editors/object/object_ops.cc index cbb57578737..7eb1585577c 100644 --- a/source/blender/editors/object/object_ops.cc +++ b/source/blender/editors/object/object_ops.cc @@ -45,6 +45,7 @@ void ED_operatortypes_object() WM_operatortype_append(OBJECT_OT_editmode_toggle); WM_operatortype_append(OBJECT_OT_posemode_toggle); WM_operatortype_append(OBJECT_OT_shade_smooth); + WM_operatortype_append(OBJECT_OT_shade_smooth_by_angle); WM_operatortype_append(OBJECT_OT_shade_flat); WM_operatortype_append(OBJECT_OT_paths_calculate); WM_operatortype_append(OBJECT_OT_paths_update); diff --git a/source/blender/editors/screen/screen_ops.cc b/source/blender/editors/screen/screen_ops.cc index 7a11b874b96..59b70ecbd6d 100644 --- a/source/blender/editors/screen/screen_ops.cc +++ b/source/blender/editors/screen/screen_ops.cc @@ -482,15 +482,6 @@ bool ED_operator_editmesh_region_view3d(bContext *C) return false; } -bool ED_operator_editmesh_auto_smooth(bContext *C) -{ - Object *obedit = CTX_data_edit_object(C); - if (obedit && obedit->type == OB_MESH && (((Mesh *)(obedit->data))->flag & ME_AUTOSMOOTH)) { - return nullptr != BKE_editmesh_from_object(obedit); - } - return false; -} - bool ED_operator_editarmature(bContext *C) { Object *obedit = CTX_data_edit_object(C); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 95c5eef50cc..fb092bf01ed 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -128,7 +128,7 @@ struct AddOperationExecutor { transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); Object &surface_ob_orig = *curves_id_orig_->surface; - Mesh &surface_orig = *static_cast(surface_ob_orig.data); + const Mesh &surface_orig = *static_cast(surface_ob_orig.data); if (surface_orig.faces_num == 0) { report_empty_original_surface(stroke_extension.reports); return; @@ -207,15 +207,7 @@ struct AddOperationExecutor { } const Span surface_looptris_orig = surface_orig.looptris(); - - /* Find normals. */ - if (!CustomData_has_layer(&surface_orig.loop_data, CD_NORMAL)) { - BKE_mesh_calc_normals_split(&surface_orig); - } - const Span corner_normals_su = { - reinterpret_cast(CustomData_get_layer(&surface_orig.loop_data, CD_NORMAL)), - surface_orig.totloop}; - + const Span corner_normals_su = surface_orig.corner_normals(); const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig}; geometry::AddCurvesOnMeshInputs add_inputs; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc index f96de0b55ce..3d7f13d9ace 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc @@ -76,7 +76,7 @@ struct DensityAddOperationExecutor { CurvesGeometry *curves_orig_ = nullptr; Object *surface_ob_orig_ = nullptr; - Mesh *surface_orig_ = nullptr; + const Mesh *surface_orig_ = nullptr; Object *surface_ob_eval_ = nullptr; Mesh *surface_eval_ = nullptr; @@ -115,7 +115,7 @@ struct DensityAddOperationExecutor { } surface_ob_orig_ = curves_id_orig_->surface; - surface_orig_ = static_cast(surface_ob_orig_->data); + surface_orig_ = static_cast(surface_ob_orig_->data); if (surface_orig_->faces_num == 0) { report_empty_original_surface(stroke_extension.reports); return; @@ -257,14 +257,7 @@ struct DensityAddOperationExecutor { } self_->new_deformed_root_positions_.extend(new_positions_cu); - /* Find normals. */ - if (!CustomData_has_layer(&surface_orig_->loop_data, CD_NORMAL)) { - BKE_mesh_calc_normals_split(surface_orig_); - } - const Span corner_normals_su = {reinterpret_cast(CustomData_get_layer( - &surface_orig_->loop_data, CD_NORMAL)), - surface_orig_->totloop}; - + const Span corner_normals_su = surface_orig_->corner_normals(); const Span surface_looptris_orig = surface_orig_->looptris(); const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig}; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc index 91627a8cf84..9f7aaff99de 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc @@ -73,8 +73,8 @@ struct PuffOperationExecutor { CurvesSurfaceTransforms transforms_; - Object *surface_ob_ = nullptr; - Mesh *surface_ = nullptr; + const Object *surface_ob_ = nullptr; + const Mesh *surface_ = nullptr; Span surface_positions_; Span surface_corner_verts_; Span surface_looptris_; @@ -113,20 +113,14 @@ struct PuffOperationExecutor { falloff_shape_ = static_cast(brush_->falloff_shape); surface_ob_ = curves_id_->surface; - surface_ = static_cast(surface_ob_->data); + surface_ = static_cast(surface_ob_->data); transforms_ = CurvesSurfaceTransforms(*object_, surface_ob_); - if (!CustomData_has_layer(&surface_->loop_data, CD_NORMAL)) { - BKE_mesh_calc_normals_split(surface_); - } - corner_normals_su_ = { - reinterpret_cast(CustomData_get_layer(&surface_->loop_data, CD_NORMAL)), - surface_->totloop}; - surface_positions_ = surface_->vert_positions(); surface_corner_verts_ = surface_->corner_verts(); surface_looptris_ = surface_->looptris(); + corner_normals_su_ = surface_->corner_normals(); BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc index 2e1189a8621..91c1d9c489e 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc @@ -99,7 +99,7 @@ struct SlideOperationExecutor { CurvesGeometry *curves_orig_ = nullptr; Object *surface_ob_orig_ = nullptr; - Mesh *surface_orig_ = nullptr; + const Mesh *surface_orig_ = nullptr; Span surface_looptris_orig_; VArraySpan surface_uv_map_orig_; Span corner_normals_orig_su_; @@ -166,25 +166,19 @@ struct SlideOperationExecutor { transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); surface_ob_orig_ = curves_id_orig_->surface; - surface_orig_ = static_cast(surface_ob_orig_->data); + surface_orig_ = static_cast(surface_ob_orig_->data); if (surface_orig_->faces_num == 0) { report_empty_original_surface(stroke_extension.reports); return; } surface_looptris_orig_ = surface_orig_->looptris(); + corner_normals_orig_su_ = surface_orig_->corner_normals(); surface_uv_map_orig_ = *surface_orig_->attributes().lookup(uv_map_name, ATTR_DOMAIN_CORNER); if (surface_uv_map_orig_.is_empty()) { report_missing_uv_map_on_original_surface(stroke_extension.reports); return; } - if (!CustomData_has_layer(&surface_orig_->loop_data, CD_NORMAL)) { - BKE_mesh_calc_normals_split(surface_orig_); - } - corner_normals_orig_su_ = {reinterpret_cast( - CustomData_get_layer(&surface_orig_->loop_data, CD_NORMAL)), - surface_orig_->totloop}; - surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_); if (surface_ob_eval_ == nullptr) { return; diff --git a/source/blender/editors/transform/transform.cc b/source/blender/editors/transform/transform.cc index 509b2fe66fa..f5c7e6fdd39 100644 --- a/source/blender/editors/transform/transform.cc +++ b/source/blender/editors/transform/transform.cc @@ -2094,32 +2094,30 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (((Mesh *)(tc->obedit->data))->flag & ME_AUTOSMOOTH) { - BMEditMesh *em = nullptr; /* BKE_editmesh_from_object(t->obedit); */ - bool do_skip = false; + BMEditMesh *em = nullptr; /* BKE_editmesh_from_object(t->obedit); */ + bool do_skip = false; - /* Currently only used for two of three most frequent transform ops, - * can include more ops. - * Note that scaling cannot be included here, - * non-uniform scaling will affect normals. */ - if (ELEM(t->mode, TFM_TRANSLATION, TFM_ROTATION)) { - if (em->bm->totvertsel == em->bm->totvert) { - /* No need to invalidate if whole mesh is selected. */ - do_skip = true; - } + /* Currently only used for two of three most frequent transform ops, + * can include more ops. + * Note that scaling cannot be included here, + * non-uniform scaling will affect normals. */ + if (ELEM(t->mode, TFM_TRANSLATION, TFM_ROTATION)) { + if (em->bm->totvertsel == em->bm->totvert) { + /* No need to invalidate if whole mesh is selected. */ + do_skip = true; } + } - if (t->flag & T_MODAL) { - RNA_property_boolean_set(op->ptr, prop, false); - } - else if (!do_skip) { - const bool preserve_clnor = RNA_property_boolean_get(op->ptr, prop); - if (preserve_clnor) { - BKE_editmesh_lnorspace_update(em, static_cast(tc->obedit->data)); - t->flag |= T_CLNOR_REBUILD; - } - BM_lnorspace_invalidate(em->bm, true); + if (t->flag & T_MODAL) { + RNA_property_boolean_set(op->ptr, prop, false); + } + else if (!do_skip) { + const bool preserve_clnor = RNA_property_boolean_get(op->ptr, prop); + if (preserve_clnor) { + BKE_editmesh_lnorspace_update(em); + t->flag |= T_CLNOR_REBUILD; } + BM_lnorspace_invalidate(em->bm, true); } } } diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.cc b/source/blender/editors/transform/transform_mode_edge_rotate_normal.cc index ce341d57d70..f1484ed41c4 100644 --- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.cc +++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.cc @@ -131,8 +131,7 @@ static void initNormalRotation(TransInfo *t, wmOperator * /*op*/) BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; - BKE_editmesh_ensure_autosmooth(em, static_cast(tc->obedit->data)); - BKE_editmesh_lnorspace_update(em, static_cast(tc->obedit->data)); + BKE_editmesh_lnorspace_update(em); storeCustomLNorValue(tc, bm); } diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp index d38f424ce14..2d0544e3bd2 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp @@ -422,14 +422,7 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id) MLoopTri *mlooptri = (MLoopTri *)MEM_malloc_arrayN(tottri, sizeof(*mlooptri), __func__); blender::bke::mesh::looptris_calc(vert_positions, mesh_polys, corner_verts, {mlooptri, tottri}); const blender::Span looptri_faces = me->looptri_faces(); - - // Compute loop normals - BKE_mesh_calc_normals_split(me); - const float(*lnors)[3] = nullptr; - - if (CustomData_has_layer(&me->loop_data, CD_NORMAL)) { - lnors = (const float(*)[3])CustomData_get_layer(&me->loop_data, CD_NORMAL); - } + const blender::Span lnors = me->corner_normals(); // Get other mesh data const FreestyleEdge *fed = (const FreestyleEdge *)CustomData_get_layer(&me->edge_data, @@ -546,7 +539,7 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id) v2[2] += _z_offset; v3[2] += _z_offset; - if (_smooth && (!sharp_faces[poly_i]) && lnors) { + if (_smooth && (!sharp_faces[poly_i])) { copy_v3_v3(n1, lnors[lt->tri[0]]); copy_v3_v3(n2, lnors[lt->tri[1]]); copy_v3_v3(n3, lnors[lt->tri[2]]); diff --git a/source/blender/gpencil_modifiers_legacy/intern/MOD_gpencil_legacy_shrinkwrap.cc b/source/blender/gpencil_modifiers_legacy/intern/MOD_gpencil_legacy_shrinkwrap.cc index 4660da2afe7..7331ab8ad8a 100644 --- a/source/blender/gpencil_modifiers_legacy/intern/MOD_gpencil_legacy_shrinkwrap.cc +++ b/source/blender/gpencil_modifiers_legacy/intern/MOD_gpencil_legacy_shrinkwrap.cc @@ -206,7 +206,7 @@ static void update_depsgraph(GpencilModifierData *md, CustomData_MeshMasks mask = {0}; if (BKE_shrinkwrap_needs_normals(mmd->shrink_type, mmd->shrink_mode)) { - mask.lmask |= CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL; + mask.lmask |= CD_MASK_CUSTOMLOOPNORMAL; } if (mmd->target != nullptr) { diff --git a/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_cpu.cc b/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_cpu.cc index 750edc765c6..37423c2e60d 100644 --- a/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_cpu.cc +++ b/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_cpu.cc @@ -2006,10 +2006,6 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) { crease_angle = cosf(M_PI - orig_ob->lineart.crease_threshold); } - else if (ob_info->original_me->flag & ME_AUTOSMOOTH) { - crease_angle = cosf(ob_info->original_me->smoothresh); - use_auto_smooth = true; - } else { crease_angle = la_data->conf.crease_threshold; } diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index 16f2c52bf3b..58e6ac0d358 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -10,6 +10,7 @@ #include "abc_hierarchy_iterator.h" #include "intern/abc_axis_conversion.h" +#include "BLI_array_utils.hh" #include "BLI_assert.h" #include "BLI_math_vector.h" @@ -63,8 +64,7 @@ namespace blender::io::alembic { static void get_vertices(Mesh *mesh, std::vector &points); static void get_topology(Mesh *mesh, std::vector &face_verts, - std::vector &loop_counts, - bool &r_has_flat_shaded_face); + std::vector &loop_counts); static void get_edge_creases(Mesh *mesh, std::vector &indices, std::vector &lengths, @@ -72,9 +72,7 @@ static void get_edge_creases(Mesh *mesh, static void get_vert_creases(Mesh *mesh, std::vector &indices, std::vector &sharpnesses); -static void get_loop_normals(Mesh *mesh, - std::vector &normals, - bool has_flat_shaded_poly); +static void get_loop_normals(const Mesh *mesh, std::vector &normals); ABCGenericMeshWriter::ABCGenericMeshWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args), is_subd_(false) @@ -215,10 +213,9 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) std::vector points, normals; std::vector face_verts, loop_counts; std::vector velocities; - bool has_flat_shaded_poly = false; get_vertices(mesh, points); - get_topology(mesh, face_verts, loop_counts, has_flat_shaded_poly); + get_topology(mesh, face_verts, loop_counts); if (!frame_has_been_written_ && args_.export_params->face_sets) { write_face_sets(context.object, mesh, abc_poly_mesh_schema_); @@ -249,7 +246,7 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) } if (args_.export_params->normals) { - get_loop_normals(mesh, normals, has_flat_shaded_poly); + get_loop_normals(mesh, normals); ON3fGeomParam::Sample normals_sample; if (!normals.empty()) { @@ -282,10 +279,9 @@ void ABCGenericMeshWriter::write_subd(HierarchyContext &context, Mesh *mesh) std::vector points; std::vector face_verts, loop_counts; std::vector edge_crease_indices, edge_crease_lengths, vert_crease_indices; - bool has_flat_poly = false; get_vertices(mesh, points); - get_topology(mesh, face_verts, loop_counts, has_flat_poly); + get_topology(mesh, face_verts, loop_counts); get_edge_creases(mesh, edge_crease_indices, edge_crease_lengths, edge_crease_sharpness); get_vert_creases(mesh, vert_crease_indices, vert_crease_sharpness); @@ -450,20 +446,10 @@ static void get_vertices(Mesh *mesh, std::vector &points) static void get_topology(Mesh *mesh, std::vector &face_verts, - std::vector &loop_counts, - bool &r_has_flat_shaded_face) + std::vector &loop_counts) { const OffsetIndices faces = mesh->faces(); const Span corner_verts = mesh->corner_verts(); - const bke::AttributeAccessor attributes = mesh->attributes(); - const VArray sharp_faces = *attributes.lookup_or_default( - "sharp_face", ATTR_DOMAIN_FACE, false); - for (const int i : sharp_faces.index_range()) { - if (sharp_faces[i]) { - r_has_flat_shaded_face = true; - break; - } - } face_verts.clear(); loop_counts.clear(); @@ -535,36 +521,47 @@ static void get_vert_creases(Mesh *mesh, } } -static void get_loop_normals(Mesh *mesh, - std::vector &normals, - bool has_flat_shaded_poly) +static void get_loop_normals(const Mesh *mesh, std::vector &normals) { normals.clear(); - /* If all polygons are smooth shaded, and there are no custom normals, we don't need to export - * normals at all. This is also done by other software, see #71246. */ - if (!has_flat_shaded_poly && !CustomData_has_layer(&mesh->loop_data, CD_CUSTOMLOOPNORMAL) && - (mesh->flag & ME_AUTOSMOOTH) == 0) - { - return; - } + switch (mesh->normals_domain()) { + case blender::bke::MeshNormalDomain::Point: { + /* If all faces are smooth shaded, and there are no custom normals, we don't need to + * export normals at all. This is also done by other software, see #71246. */ + break; + } + case blender::bke::MeshNormalDomain::Face: { + normals.resize(mesh->totloop); + MutableSpan dst_normals(reinterpret_cast(normals.data()), normals.size()); - BKE_mesh_calc_normals_split(mesh); - const float(*lnors)[3] = static_cast( - CustomData_get_layer(&mesh->loop_data, CD_NORMAL)); - BLI_assert_msg(lnors != nullptr, "BKE_mesh_calc_normals_split() should have computed CD_NORMAL"); + const OffsetIndices faces = mesh->faces(); + const Span face_normals = mesh->face_normals(); + threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + float3 y_up; + copy_yup_from_zup(y_up, face_normals[i]); + dst_normals.slice(faces[i]).fill(y_up); + } + }); + break; + } + case blender::bke::MeshNormalDomain::Corner: { + normals.resize(mesh->totloop); + MutableSpan dst_normals(reinterpret_cast(normals.data()), normals.size()); - normals.resize(mesh->totloop); - - /* NOTE: data needs to be written in the reverse order. */ - int abc_index = 0; - const OffsetIndices faces = mesh->faces(); - - for (const int i : faces.index_range()) { - const IndexRange face = faces[i]; - for (int j = face.size() - 1; j >= 0; j--, abc_index++) { - int blender_index = face[j]; - copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]); + /* NOTE: data needs to be written in the reverse order. */ + const OffsetIndices faces = mesh->faces(); + const Span corner_normals = mesh->corner_normals(); + threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + const IndexRange face = faces[i]; + for (const int i : face.index_range()) { + copy_yup_from_zup(dst_normals[face.last(i)], corner_normals[face[i]]); + } + } + }); + break; } } } diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 085315d9411..9826c1dd3dd 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -289,7 +289,6 @@ static void process_loop_normals(CDStreamConfig &config, const N3fArraySamplePtr } } - mesh->flag |= ME_AUTOSMOOTH; BKE_mesh_set_custom_normals(mesh, lnors); MEM_freeN(lnors); @@ -312,7 +311,6 @@ static void process_vertex_normals(CDStreamConfig &config, copy_zup_from_yup(vert_normals[index], vertex_normals[index].getValue()); } - config.mesh->flag |= ME_AUTOSMOOTH; BKE_mesh_set_custom_normals_from_verts(config.mesh, vert_normals); MEM_freeN(vert_normals); } diff --git a/source/blender/io/collada/GeometryExporter.cpp b/source/blender/io/collada/GeometryExporter.cpp index e72b414b83c..1b85a0f8c5c 100644 --- a/source/blender/io/collada/GeometryExporter.cpp +++ b/source/blender/io/collada/GeometryExporter.cpp @@ -620,22 +620,19 @@ void GeometryExporter::create_normals(std::vector &normals, const Span vert_normals = me->vert_normals(); const blender::OffsetIndices faces = me->faces(); const Span corner_verts = me->corner_verts(); - const float(*lnors)[3] = nullptr; - bool use_custom_normals = false; const bke::AttributeAccessor attributes = me->attributes(); const VArray sharp_faces = *attributes.lookup_or_default( "sharp_face", ATTR_DOMAIN_FACE, false); - BKE_mesh_calc_normals_split(me); - if (CustomData_has_layer(&me->loop_data, CD_NORMAL)) { - lnors = (float(*)[3])CustomData_get_layer(&me->loop_data, CD_NORMAL); - use_custom_normals = true; + blender::Span corner_normals; + if (me->normals_domain() == blender::bke::MeshNormalDomain::Corner) { + corner_normals = me->corner_normals(); } for (const int face_index : faces.index_range()) { const IndexRange face = faces[face_index]; - bool use_vert_normals = use_custom_normals || !sharp_faces[face_index]; + bool use_vert_normals = !corner_normals.is_empty() || !sharp_faces[face_index]; if (!use_vert_normals) { /* For flat faces use face normal as vertex normal: */ @@ -653,8 +650,8 @@ void GeometryExporter::create_normals(std::vector &normals, if (use_vert_normals) { float normalized[3]; - if (use_custom_normals) { - normalize_v3_v3(normalized, lnors[corner]); + if (!corner_normals.is_empty()) { + normalize_v3_v3(normalized, corner_normals[corner]); } else { copy_v3_v3(normalized, vert_normals[corner_verts[corner]]); diff --git a/source/blender/io/collada/MeshImporter.cpp b/source/blender/io/collada/MeshImporter.cpp index 8b41b4bf72f..6486e4ee789 100644 --- a/source/blender/io/collada/MeshImporter.cpp +++ b/source/blender/io/collada/MeshImporter.cpp @@ -1173,7 +1173,6 @@ bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom) } else { BKE_mesh_set_custom_normals(me, reinterpret_cast(loop_normals.data())); - me->flag |= ME_AUTOSMOOTH; } } diff --git a/source/blender/io/stl/importer/stl_import_mesh.cc b/source/blender/io/stl/importer/stl_import_mesh.cc index ad035501011..ec7d8af1869 100644 --- a/source/blender/io/stl/importer/stl_import_mesh.cc +++ b/source/blender/io/stl/importer/stl_import_mesh.cc @@ -92,7 +92,6 @@ Mesh *STLMeshHelper::to_mesh() if (use_custom_normals_ && loop_normals_.size() == mesh->totloop) { BKE_mesh_set_custom_normals(mesh, reinterpret_cast(loop_normals_.data())); - mesh->flag |= ME_AUTOSMOOTH; } return mesh; diff --git a/source/blender/io/usd/hydra/mesh.cc b/source/blender/io/usd/hydra/mesh.cc index 581fc46e8cb..7dac3b15bc9 100644 --- a/source/blender/io/usd/hydra/mesh.cc +++ b/source/blender/io/usd/hydra/mesh.cc @@ -8,6 +8,7 @@ #include "BLI_string.h" +#include "BKE_attribute.h" #include "BKE_material.h" #include "BKE_mesh.hh" #include "BKE_mesh_runtime.hh" @@ -231,9 +232,10 @@ void MeshData::write_submeshes(const Mesh *mesh) const Span corner_verts = mesh->corner_verts(); const Span looptris = mesh->looptris(); - Array corner_normals(mesh->totloop); - BKE_mesh_calc_normals_split_ex( - mesh, nullptr, reinterpret_cast(corner_normals.data())); + Span corner_normals; + if (mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner) { + corner_normals = mesh->corner_normals(); + } const float2 *uv_map = static_cast( CustomData_get_layer(&mesh->loop_data, CD_PROP_FLOAT2)); diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 3e91c36235a..039548dfa0c 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -773,8 +773,6 @@ void USDMeshReader::process_normals_face_varying(Mesh *mesh) return; } - mesh->flag |= ME_AUTOSMOOTH; - long int loop_count = normals_.size(); float(*lnors)[3] = static_cast( @@ -828,7 +826,6 @@ void USDMeshReader::process_normals_uniform(Mesh *mesh) } } - mesh->flag |= ME_AUTOSMOOTH; BKE_mesh_set_custom_normals(mesh, lnors); MEM_freeN(lnors); diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc index 53d98944246..62f1530f3db 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.cc +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -10,6 +10,7 @@ #include #include +#include "BLI_array_utils.hh" #include "BLI_assert.h" #include "BLI_math_color.hh" #include "BLI_math_quaternion_types.hh" @@ -648,42 +649,28 @@ void USDGenericMeshWriter::assign_materials(const HierarchyContext &context, void USDGenericMeshWriter::write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh) { pxr::UsdTimeCode timecode = get_export_time_code(); - const float(*lnors)[3] = static_cast( - CustomData_get_layer(&mesh->loop_data, CD_NORMAL)); - const OffsetIndices faces = mesh->faces(); - const Span corner_verts = mesh->corner_verts(); pxr::VtVec3fArray loop_normals; - loop_normals.reserve(mesh->totloop); + loop_normals.resize(mesh->totloop); - if (lnors != nullptr) { - /* Export custom loop normals. */ - for (int loop_idx = 0, totloop = mesh->totloop; loop_idx < totloop; ++loop_idx) { - loop_normals.push_back(pxr::GfVec3f(lnors[loop_idx])); + MutableSpan dst_normals(reinterpret_cast(loop_normals.data()), loop_normals.size()); + + switch (mesh->normals_domain()) { + case bke::MeshNormalDomain::Point: { + array_utils::gather(mesh->vert_normals(), mesh->corner_verts(), dst_normals); + break; } - } - else { - /* Compute the loop normals based on the 'smooth' flag. */ - bke::AttributeAccessor attributes = mesh->attributes(); - const Span vert_normals = mesh->vert_normals(); - const Span face_normals = mesh->face_normals(); - const VArray sharp_faces = *attributes.lookup_or_default( - "sharp_face", ATTR_DOMAIN_FACE, false); - for (const int i : faces.index_range()) { - const IndexRange face = faces[i]; - if (sharp_faces[i]) { - /* Flat shaded, use common normal for all verts. */ - pxr::GfVec3f pxr_normal(&face_normals[i].x); - for (int loop_idx = 0; loop_idx < face.size(); ++loop_idx) { - loop_normals.push_back(pxr_normal); - } - } - else { - /* Smooth shaded, use individual vert normals. */ - for (const int vert : corner_verts.slice(face)) { - loop_normals.push_back(pxr::GfVec3f(&vert_normals[vert].x)); - } + case bke::MeshNormalDomain::Face: { + const OffsetIndices faces = mesh->faces(); + const Span face_normals = mesh->face_normals(); + for (const int i : faces.index_range()) { + dst_normals.slice(faces[i]).fill(face_normals[i]); } + break; + } + case bke::MeshNormalDomain::Corner: { + array_utils::copy(mesh->corner_normals(), dst_normals); + break; } } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc index c4cd13beec2..8a0b3a97139 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc @@ -192,12 +192,6 @@ int OBJMesh::ith_smooth_group(const int face_index) const return poly_smooth_groups_[face_index]; } -void OBJMesh::ensure_mesh_normals() const -{ - /* Constant cast can be removed when calculating face corner normals lazily is possible. */ - BKE_mesh_calc_normals_split(const_cast(export_mesh_)); -} - void OBJMesh::calc_smooth_groups(const bool use_bitflags) { const bool *sharp_edges = static_cast( @@ -372,16 +366,23 @@ void OBJMesh::store_normal_coords_and_indices() normal_to_index.reserve(export_mesh_->faces_num); loop_to_normal_index_.resize(export_mesh_->totloop); loop_to_normal_index_.fill(-1); - const float(*lnors)[3] = static_cast( - CustomData_get_layer(&export_mesh_->loop_data, CD_NORMAL)); + + Span corner_normals; + if (ELEM(export_mesh_->normals_domain(), + bke::MeshNormalDomain::Point, + bke::MeshNormalDomain::Corner)) + { + corner_normals = export_mesh_->corner_normals(); + } + for (int face_index = 0; face_index < export_mesh_->faces_num; ++face_index) { const IndexRange face = mesh_faces_[face_index]; - bool need_per_loop_normals = lnors != nullptr || !(sharp_faces_[face_index]); + bool need_per_loop_normals = !corner_normals.is_empty() || !(sharp_faces_[face_index]); if (need_per_loop_normals) { for (const int corner : face) { float3 loop_normal; BLI_assert(corner < export_mesh_->totloop); - copy_v3_v3(loop_normal, lnors[corner]); + copy_v3_v3(loop_normal, corner_normals[corner]); mul_m3_v3(world_and_axes_normal_transform_, loop_normal); normalize_v3(loop_normal); float3 rounded_loop_normal = round_float3_to_n_digits(loop_normal, round_digits); diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh index a47b2679956..919ecce61b3 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh @@ -119,8 +119,6 @@ class OBJMesh : NonCopyable { */ const Material *get_object_material(int16_t mat_nr) const; - void ensure_mesh_normals() const; - /** * Calculate smooth groups of a smooth-shaded object. * \return A polygon aligned array of smooth group numbers. diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index b049addbf04..35701c0b452 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -160,9 +160,6 @@ static void write_mesh_objects(Vector> exportable_as_me if (mtl_writer) { mtlindices.append(mtl_writer->add_materials(obj)); } - if (export_params.export_normals) { - obj.ensure_mesh_normals(); - } } /* Parallel over meshes: store normal coords & indices, uv coords and indices. */ diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 5a602cd3521..e31ce580e39 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -384,7 +384,6 @@ void MeshFromGeometry::create_normals(Mesh *mesh) tot_loop_idx++; } } - mesh->flag |= ME_AUTOSMOOTH; BKE_mesh_set_custom_normals(mesh, loop_normals); MEM_freeN(loop_normals); } diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc index 164dd4f22da..869a23ca3a3 100644 --- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc @@ -137,7 +137,9 @@ class obj_importer_test : public BlendfileLoadingBaseTest { const Span positions = mesh->vert_positions(); EXPECT_V3_NEAR(positions.first(), exp.vert_first, 0.0001f); EXPECT_V3_NEAR(positions.last(), exp.vert_last, 0.0001f); - const float3 *lnors = (const float3 *)CustomData_get_layer(&mesh->loop_data, CD_NORMAL); + const float3 *lnors = mesh->normals_domain() == bke::MeshNormalDomain::Corner ? + mesh->corner_normals().data() : + nullptr; float3 normal_first = lnors != nullptr ? lnors[0] : float3(0, 0, 0); EXPECT_V3_NEAR(normal_first, exp.normal_first, 0.0001f); const float2 *mloopuv = static_cast( diff --git a/source/blender/makesdna/DNA_mesh_defaults.h b/source/blender/makesdna/DNA_mesh_defaults.h index 50db3c88c4c..15bf7cb4a83 100644 --- a/source/blender/makesdna/DNA_mesh_defaults.h +++ b/source/blender/makesdna/DNA_mesh_defaults.h @@ -18,7 +18,6 @@ #define _DNA_DEFAULT_Mesh \ { \ .texspace_size = {1.0f, 1.0f, 1.0f}, \ - .smoothresh = DEG2RADF(30), \ .texspace_flag = ME_TEXSPACE_FLAG_AUTO, \ .remesh_voxel_size = 0.1f, \ .remesh_voxel_adaptivity = 0.0f, \ diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 4032138c025..6d08fafcf44 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -32,6 +32,7 @@ class AttributeAccessor; class MutableAttributeAccessor; struct LooseVertCache; struct LooseEdgeCache; +enum class MeshNormalDomain : int8_t; } // namespace bke } // namespace blender using MeshRuntimeHandle = blender::bke::MeshRuntime; @@ -149,10 +150,7 @@ typedef struct Mesh { /** Mostly more flags used when editing or displaying the mesh. */ uint16_t flag; - /** - * The angle for auto smooth in radians. `M_PI` (180 degrees) causes all edges to be smooth. - */ - float smoothresh; + float smoothresh_legacy DNA_DEPRECATED; /** Per-mesh settings for voxel remesh. */ float remesh_voxel_size; @@ -359,6 +357,17 @@ typedef struct Mesh { */ void tag_loose_verts_none() const; + /** + * Returns the least complex attribute domain needed to store normals encoding all relevant mesh + * data. When all edges or faces are sharp, face normals are enough. When all are smooth, vertex + * normals are enough. With a combination of sharp and smooth, normals may be "split", + * requiring face corner storage. + * + * When possible, it's preferred to use face normals over vertex normals and vertex normals over + * face corner normals, since there is a 2-4x performance cost increase for each more complex + * domain. + */ + blender::bke::MeshNormalDomain normals_domain() const; /** * Normal direction of polygons, defined by positions and the winding direction of face corners. */ @@ -368,6 +377,14 @@ typedef struct Mesh { * surrounding each vertex and the normalized position for loose vertices. */ blender::Span vert_normals() const; + /** + * Normal direction at each face corner. Defined by a combination of face normals, vertex + * normals, the `sharp_edge` and `sharp_face` attributes, and potentially by custom normals. + * + * \note Because of the large memory requirements of storing normals per face corner, prefer + * using #face_normals() or #vert_normals() when possible (see #normals_domain()). + */ + blender::Span corner_normals() const; #endif } Mesh; @@ -422,9 +439,9 @@ enum { ME_FLAG_DEPRECATED_2 = 1 << 2, /* deprecated */ ME_FLAG_UNUSED_3 = 1 << 3, /* cleared */ ME_FLAG_UNUSED_4 = 1 << 4, /* cleared */ - ME_AUTOSMOOTH = 1 << 5, - ME_FLAG_UNUSED_6 = 1 << 6, /* cleared */ - ME_FLAG_UNUSED_7 = 1 << 7, /* cleared */ + ME_AUTOSMOOTH_LEGACY = 1 << 5, /* deprecated */ + ME_FLAG_UNUSED_6 = 1 << 6, /* cleared */ + ME_FLAG_UNUSED_7 = 1 << 7, /* cleared */ ME_REMESH_REPROJECT_VERTEX_COLORS = 1 << 8, ME_DS_EXPAND = 1 << 9, ME_SCULPT_DYNAMIC_TOPOLOGY = 1 << 10, diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 7535aff9ed6..b214e986760 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -116,6 +116,7 @@ DNA_STRUCT_RENAME_ELEM(Mesh, loc, texspace_location) DNA_STRUCT_RENAME_ELEM(Mesh, pdata, face_data) DNA_STRUCT_RENAME_ELEM(Mesh, poly_offset_indices, face_offset_indices) DNA_STRUCT_RENAME_ELEM(Mesh, size, texspace_size) +DNA_STRUCT_RENAME_ELEM(Mesh, smoothresh, smoothresh_legacy) DNA_STRUCT_RENAME_ELEM(Mesh, texflag, texspace_flag) DNA_STRUCT_RENAME_ELEM(Mesh, totface, totface_legacy) DNA_STRUCT_RENAME_ELEM(Mesh, totpoly, faces_num) diff --git a/source/blender/makesrna/RNA_enum_items.hh b/source/blender/makesrna/RNA_enum_items.hh index 5cc1fefc547..243bda78e56 100644 --- a/source/blender/makesrna/RNA_enum_items.hh +++ b/source/blender/makesrna/RNA_enum_items.hh @@ -225,6 +225,8 @@ DEF_ENUM(rna_enum_attribute_type_with_auto_items) DEF_ENUM(rna_enum_attribute_domain_items) DEF_ENUM(rna_enum_attribute_domain_edge_face_items) DEF_ENUM(rna_enum_attribute_domain_only_mesh_items) +DEF_ENUM(rna_enum_attribute_domain_only_mesh_no_edge_items) +DEF_ENUM(rna_enum_attribute_domain_point_face_curve_items) DEF_ENUM(rna_enum_attribute_domain_point_edge_face_curve_items) DEF_ENUM(rna_enum_attribute_curves_domain_items) DEF_ENUM(rna_enum_color_attribute_domain_items) diff --git a/source/blender/makesrna/intern/rna_attribute.cc b/source/blender/makesrna/intern/rna_attribute.cc index b1307abc744..62304d2937f 100644 --- a/source/blender/makesrna/intern/rna_attribute.cc +++ b/source/blender/makesrna/intern/rna_attribute.cc @@ -100,6 +100,20 @@ const EnumPropertyItem rna_enum_attribute_domain_only_mesh_items[] = { {0, nullptr, 0, nullptr, nullptr}, }; +const EnumPropertyItem rna_enum_attribute_domain_only_mesh_no_edge_items[] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"}, + {ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"}, + {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +const EnumPropertyItem rna_enum_attribute_domain_point_face_curve_items[] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"}, + {ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"}, + {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + const EnumPropertyItem rna_enum_attribute_domain_point_edge_face_curve_items[] = { {ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"}, {ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"}, diff --git a/source/blender/makesrna/intern/rna_mesh.cc b/source/blender/makesrna/intern/rna_mesh.cc index d7c338a024c..18a2abbe690 100644 --- a/source/blender/makesrna/intern/rna_mesh.cc +++ b/source/blender/makesrna/intern/rna_mesh.cc @@ -23,6 +23,7 @@ #include "BKE_attribute.h" #include "BKE_editmesh.h" +#include "BKE_mesh_types.hh" #include "RNA_access.hh" #include "RNA_define.hh" @@ -478,27 +479,7 @@ static void rna_MeshLoop_normal_get(PointerRNA *ptr, float *values) { Mesh *me = rna_mesh(ptr); const int index = rna_MeshLoop_index_get(ptr); - const float(*layer)[3] = static_cast( - CustomData_get_layer(&me->loop_data, CD_NORMAL)); - - if (!layer) { - zero_v3(values); - } - else { - copy_v3_v3(values, layer[index]); - } -} - -static void rna_MeshLoop_normal_set(PointerRNA *ptr, const float *values) -{ - Mesh *me = rna_mesh(ptr); - const int index = rna_MeshLoop_index_get(ptr); - float(*layer)[3] = static_cast( - CustomData_get_layer_for_write(&me->loop_data, CD_NORMAL, me->totloop)); - - if (layer) { - normalize_v3_v3(layer[index], values); - } + copy_v3_v3(values, me->corner_normals()[index]); } static void rna_MeshLoop_tangent_get(PointerRNA *ptr, float *values) @@ -530,13 +511,11 @@ static void rna_MeshLoop_bitangent_get(PointerRNA *ptr, float *values) { Mesh *me = rna_mesh(ptr); const int index = rna_MeshLoop_index_get(ptr); - const float(*nor)[3] = static_cast( - CustomData_get_layer(&me->loop_data, CD_NORMAL)); const float(*vec)[4] = static_cast( CustomData_get_layer(&me->loop_data, CD_MLOOPTANGENT)); - if (nor && vec) { - cross_v3_v3v3(values, nor[index], vec[index]); + if (vec) { + cross_v3_v3v3(values, me->corner_normals()[index], vec[index]); mul_v3_fl(values, vec[index][3]); } else { @@ -604,7 +583,10 @@ static void rna_MeshPolygon_use_smooth_set(PointerRNA *ptr, bool value) &mesh->face_data, CD_PROP_BOOL, CD_SET_DEFAULT, mesh->faces_num, "sharp_face")); } const int index = rna_MeshPolygon_index_get(ptr); - sharp_faces[index] = !value; + if (value != sharp_faces[index]) { + sharp_faces[index] = value; + BKE_mesh_tag_sharpness_changed(mesh); + } } static bool rna_MeshPolygon_select_get(PointerRNA *ptr) @@ -706,20 +688,11 @@ static void rna_MeshLoopTriangle_normal_get(PointerRNA *ptr, float *values) static void rna_MeshLoopTriangle_split_normals_get(PointerRNA *ptr, float *values) { Mesh *me = rna_mesh(ptr); - const float(*lnors)[3] = static_cast( - CustomData_get_layer(&me->loop_data, CD_NORMAL)); - - if (!lnors) { - zero_v3(values + 0); - zero_v3(values + 3); - zero_v3(values + 6); - } - else { - MLoopTri *lt = (MLoopTri *)ptr->data; - copy_v3_v3(values + 0, lnors[lt->tri[0]]); - copy_v3_v3(values + 3, lnors[lt->tri[1]]); - copy_v3_v3(values + 6, lnors[lt->tri[2]]); - } + const blender::Span loop_normals = me->corner_normals(); + const MLoopTri *lt = (const MLoopTri *)ptr->data; + copy_v3_v3(values + 0, loop_normals[lt->tri[0]]); + copy_v3_v3(values + 3, loop_normals[lt->tri[1]]); + copy_v3_v3(values + 6, loop_normals[lt->tri[2]]); } static float rna_MeshLoopTriangle_area_get(PointerRNA *ptr) @@ -1399,7 +1372,10 @@ static void rna_MeshEdge_use_edge_sharp_set(PointerRNA *ptr, bool value) &mesh->edge_data, CD_PROP_BOOL, CD_SET_DEFAULT, mesh->totedge, "sharp_edge")); } const int index = rna_MeshEdge_index_get(ptr); - sharp_edge[index] = value; + if (value != sharp_edge[index]) { + sharp_edge[index] = value; + BKE_mesh_tag_sharpness_changed(mesh); + } } static bool rna_MeshEdge_use_seam_get(PointerRNA *ptr) @@ -1642,6 +1618,11 @@ int rna_Mesh_loops_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) return true; } +static int rna_Mesh_normals_domain_get(PointerRNA *ptr) +{ + return int(rna_mesh(ptr)->normals_domain()); +} + static void rna_Mesh_vertex_normals_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { const Mesh *mesh = rna_mesh(ptr); @@ -1707,41 +1688,32 @@ int rna_Mesh_poly_normals_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_p static void rna_Mesh_corner_normals_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { const Mesh *mesh = rna_mesh(ptr); - const blender::float3 *normals = static_cast( - CustomData_get_layer(&mesh->loop_data, CD_NORMAL)); - if (!normals) { + const blender::Span normals = mesh->corner_normals(); + if (normals.is_empty()) { iter->valid = false; return; } - rna_iterator_array_begin(iter, - const_cast(normals), - sizeof(float[3]), - mesh->totloop, - false, - nullptr); + rna_iterator_array_begin( + iter, (void *)normals.data(), sizeof(float[3]), mesh->totloop, false, nullptr); } static int rna_Mesh_corner_normals_length(PointerRNA *ptr) { const Mesh *mesh = rna_mesh(ptr); - if (!CustomData_has_layer(&mesh->loop_data, CD_NORMAL)) { - return 0; - } return mesh->totloop; } int rna_Mesh_corner_normals_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) { const Mesh *mesh = rna_mesh(ptr); - const blender::float3 *normals = static_cast( - CustomData_get_layer(&mesh->loop_data, CD_NORMAL)); - if (index < 0 || index >= mesh->totloop || !normals) { + const blender::Span normals = mesh->corner_normals(); + if (index < 0 || index >= mesh->totloop || normals.is_empty()) { return false; } /* Casting away const is okay because this RNA type doesn't allow changing the value. */ r_ptr->owner_id = (ID *)&mesh->id; r_ptr->type = &RNA_MeshNormalValue; - r_ptr->data = const_cast(&normals[index]); + r_ptr->data = (float *)&normals[index]; return true; } @@ -2193,8 +2165,7 @@ static void rna_def_mlooptri(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Split Normals", - "Local space unit length split normals vectors of the vertices of this triangle " - "(must be computed beforehand using calc_normals_split or calc_tangents)"); + "Local space unit length split normal vectors of the face corners of this triangle"); prop = RNA_def_property(srna, "area", PROP_FLOAT, PROP_UNSIGNED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -2243,15 +2214,14 @@ static void rna_def_mloop(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Index", "Index of this loop"); prop = RNA_def_property(srna, "normal", PROP_FLOAT, PROP_DIRECTION); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_array(prop, 3); RNA_def_property_range(prop, -1.0f, 1.0f); - RNA_def_property_float_funcs( - prop, "rna_MeshLoop_normal_get", "rna_MeshLoop_normal_set", nullptr); - RNA_def_property_ui_text( - prop, - "Normal", - "Local space unit length split normal vector of this vertex for this face " - "(must be computed beforehand using calc_normals_split or calc_tangents)"); + RNA_def_property_float_funcs(prop, "rna_MeshLoop_normal_get", nullptr, nullptr); + RNA_def_property_ui_text(prop, + "Normal", + "The normal direction of the face corner, taking into account sharp " + "faces, sharp edges, and custom normal data"); prop = RNA_def_property(srna, "tangent", PROP_FLOAT, PROP_DIRECTION); RNA_def_property_array(prop, 3); @@ -3030,6 +3000,22 @@ static void rna_def_mesh(BlenderRNA *brna) rna_def_normal_layer_value(brna); + static const EnumPropertyItem normal_domain_items[] = { + {int(blender::bke::MeshNormalDomain::Point), "POINT", 0, "Point", ""}, + {int(blender::bke::MeshNormalDomain::Face), "FACE", 0, "Face", ""}, + {int(blender::bke::MeshNormalDomain::Corner), "CORNER", 0, "Corner", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + prop = RNA_def_property(srna, "normals_domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, normal_domain_items); + RNA_def_property_ui_text( + prop, + "Normal Domain", + "The attribute domain that gives enough information to represent the mesh's normals"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_enum_funcs(prop, "rna_Mesh_normals_domain_get", NULL, NULL); + prop = RNA_def_property(srna, "vertex_normals", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshNormalValue"); RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); @@ -3325,24 +3311,6 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); /* End Symmetry */ - prop = RNA_def_property(srna, "use_auto_smooth", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flag", ME_AUTOSMOOTH); - RNA_def_property_ui_text( - prop, - "Auto Smooth", - "Auto smooth (based on smooth/sharp faces/edges and angle between faces), " - "or use custom split normals data if available"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_geom_and_params"); - - prop = RNA_def_property(srna, "auto_smooth_angle", PROP_FLOAT, PROP_ANGLE); - RNA_def_property_float_sdna(prop, nullptr, "smoothresh"); - RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f)); - RNA_def_property_ui_text(prop, - "Auto Smooth Angle", - "Maximum angle between face normals that will be considered as smooth " - "(unused if custom split normals data are available)"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_geom_and_params"); - RNA_define_verify_sdna(false); prop = RNA_def_property(srna, "has_custom_normals", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "", 0); diff --git a/source/blender/makesrna/intern/rna_mesh_api.cc b/source/blender/makesrna/intern/rna_mesh_api.cc index 41123183024..e1436bda500 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.cc +++ b/source/blender/makesrna/intern/rna_mesh_api.cc @@ -13,6 +13,7 @@ #include "DNA_customdata_types.h" +#include "BLI_math_base.h" #include "BLI_sys_types.h" #include "BLI_utildefines.h" @@ -23,6 +24,8 @@ # include "DNA_mesh_types.h" # include "BKE_anim_data.h" +# include "BKE_attribute.hh" +# include "BKE_mesh.h" # include "BKE_mesh.hh" # include "BKE_mesh_mapping.hh" # include "BKE_mesh_runtime.hh" @@ -46,17 +49,12 @@ static const char *rna_Mesh_unit_test_compare(Mesh *mesh, Mesh *mesh2, float thr return ret; } -static void rna_Mesh_create_normals_split(Mesh *mesh) +static void rna_Mesh_sharp_from_angle_set(Mesh *mesh, const float angle) { - if (!CustomData_has_layer(&mesh->loop_data, CD_NORMAL)) { - CustomData_add_layer(&mesh->loop_data, CD_NORMAL, CD_SET_DEFAULT, mesh->totloop); - CustomData_set_layer_flag(&mesh->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); - } -} - -static void rna_Mesh_free_normals_split(Mesh *mesh) -{ - CustomData_free_layers(&mesh->loop_data, CD_NORMAL, mesh->totloop); + mesh->attributes_for_write().remove("sharp_edge"); + mesh->attributes_for_write().remove("sharp_face"); + BKE_mesh_sharp_edges_set_from_angle(mesh, angle); + DEG_id_tag_update(&mesh->id, ID_RECALC_GEOMETRY); } static void rna_Mesh_calc_tangents(Mesh *mesh, ReportList *reports, const char *uvmap) @@ -74,11 +72,6 @@ static void rna_Mesh_calc_tangents(Mesh *mesh, ReportList *reports, const char * CustomData_set_layer_flag(&mesh->loop_data, CD_MLOOPTANGENT, CD_FLAG_TEMPORARY); } - /* Compute loop normals if needed. */ - if (!CustomData_has_layer(&mesh->loop_data, CD_NORMAL)) { - BKE_mesh_calc_normals_split(mesh); - } - BKE_mesh_calc_loop_tangent_single(mesh, uvmap, r_looptangents, reports); } @@ -185,6 +178,7 @@ static void rna_Mesh_update(Mesh *mesh, mesh->runtime->vert_normals_cache.tag_dirty(); mesh->runtime->face_normals_cache.tag_dirty(); + mesh->runtime->corner_normals_cache.tag_dirty(); DEG_id_tag_update(&mesh->id, 0); WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh); @@ -230,15 +224,19 @@ void RNA_api_mesh(StructRNA *srna) "Invert winding of all polygons " "(clears tessellation, does not handle custom normals)"); - func = RNA_def_function(srna, "create_normals_split", "rna_Mesh_create_normals_split"); - RNA_def_function_ui_description(func, "Empty split vertex normals"); - - func = RNA_def_function(srna, "calc_normals_split", "BKE_mesh_calc_normals_split"); + func = RNA_def_function(srna, "set_sharp_from_angle", "rna_Mesh_sharp_from_angle_set"); RNA_def_function_ui_description(func, - "Calculate split vertex normals, which preserve sharp edges"); - - func = RNA_def_function(srna, "free_normals_split", "rna_Mesh_free_normals_split"); - RNA_def_function_ui_description(func, "Free split vertex normals"); + "Reset and fill the \"sharp_edge\" attribute based on the angle " + "of faces neighboring manifold edges"); + RNA_def_float(func, + "angle", + M_PI, + 0.0f, + M_PI, + "Angle", + "Angle between faces beyond which edges are marked sharp", + 0.0f, + M_PI); func = RNA_def_function(srna, "split_faces", "ED_mesh_split_faces"); RNA_def_function_ui_description(func, "Split faces based on the edge angle"); diff --git a/source/blender/modifiers/intern/MOD_bevel.cc b/source/blender/modifiers/intern/MOD_bevel.cc index a777c10b44e..624fd7b79d0 100644 --- a/source/blender/modifiers/intern/MOD_bevel.cc +++ b/source/blender/modifiers/intern/MOD_bevel.cc @@ -193,13 +193,6 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh } } - Object *ob = ctx->object; - - if (harden_normals && (ob->type == OB_MESH) && !(((Mesh *)ob->data)->flag & ME_AUTOSMOOTH)) { - BKE_modifier_set_error(ob, md, "Enable 'Auto Smooth' in Object Data Properties"); - harden_normals = false; - } - BM_mesh_bevel(bm, value, offset_type, @@ -220,7 +213,6 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh miter_outer, miter_inner, spread, - mesh->smoothresh, bmd->custom_profile, bmd->vmesh_method); diff --git a/source/blender/modifiers/intern/MOD_datatransfer.cc b/source/blender/modifiers/intern/MOD_datatransfer.cc index c1e66ee94b8..269af31f03a 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.cc +++ b/source/blender/modifiers/intern/MOD_datatransfer.cc @@ -117,8 +117,6 @@ static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphCont if (dtmd->ob_source != nullptr) { CustomData_MeshMasks cddata_masks = {0}; BKE_object_data_transfer_dttypes_to_cdmask(dtmd->data_types, &cddata_masks); - BKE_mesh_remap_calc_source_cddata_masks_from_map_modes( - dtmd->vmap_mode, dtmd->emap_mode, dtmd->lmap_mode, dtmd->pmap_mode, &cddata_masks); DEG_add_object_relation( ctx->node, dtmd->ob_source, DEG_OB_COMP_GEOMETRY, "DataTransfer Modifier"); @@ -222,10 +220,6 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh BKE_modifier_set_error(ctx->object, md, "%s", report_str); MEM_freeN((void *)report_str); } - else if ((dtmd->data_types & DT_TYPE_LNOR) && !(me->flag & ME_AUTOSMOOTH)) { - BKE_modifier_set_error( - ctx->object, (ModifierData *)dtmd, "Enable 'Auto Smooth' in Object Data Properties"); - } BKE_reports_free(&reports); diff --git a/source/blender/modifiers/intern/MOD_displace.cc b/source/blender/modifiers/intern/MOD_displace.cc index 2fda0e8fc7a..23f34695547 100644 --- a/source/blender/modifiers/intern/MOD_displace.cc +++ b/source/blender/modifiers/intern/MOD_displace.cc @@ -302,22 +302,15 @@ static void displaceModifier_do(DisplaceModifierData *dmd, } if (direction == MOD_DISP_DIR_CLNOR) { - CustomData *ldata = &mesh->loop_data; - - if (CustomData_has_layer(ldata, CD_CUSTOMLOOPNORMAL)) { - if (!CustomData_has_layer(ldata, CD_NORMAL)) { - BKE_mesh_calc_normals_split(mesh); - } - - float(*clnors)[3] = static_cast( - CustomData_get_layer_for_write(ldata, CD_NORMAL, mesh->totloop)); + if (CustomData_has_layer(&mesh->loop_data, CD_CUSTOMLOOPNORMAL)) { vert_clnors = static_cast( MEM_malloc_arrayN(verts_num, sizeof(*vert_clnors), __func__)); - BKE_mesh_normals_loop_to_vertex(verts_num, - mesh->corner_verts().data(), - mesh->totloop, - (const float(*)[3])clnors, - vert_clnors); + BKE_mesh_normals_loop_to_vertex( + verts_num, + mesh->corner_verts().data(), + mesh->totloop, + reinterpret_cast(mesh->corner_normals().data()), + vert_clnors); } else { direction = MOD_DISP_DIR_NOR; diff --git a/source/blender/modifiers/intern/MOD_multires.cc b/source/blender/modifiers/intern/MOD_multires.cc index 88cb63f1b12..1280c34abfc 100644 --- a/source/blender/modifiers/intern/MOD_multires.cc +++ b/source/blender/modifiers/intern/MOD_multires.cc @@ -67,7 +67,6 @@ static void required_data_mask(ModifierData *md, CustomData_MeshMasks *r_cddata_ { MultiresModifierData *mmd = (MultiresModifierData *)md; if (mmd->flags & eMultiresModifierFlag_UseCustomNormals) { - r_cddata_masks->lmask |= CD_MASK_NORMAL; r_cddata_masks->lmask |= CD_MASK_CUSTOMLOOPNORMAL; } } @@ -217,8 +216,7 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh return result; } const bool use_clnors = mmd->flags & eMultiresModifierFlag_UseCustomNormals && - mesh->flag & ME_AUTOSMOOTH && - CustomData_has_layer(&mesh->loop_data, CD_CUSTOMLOOPNORMAL); + mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner; /* NOTE: Orco needs final coordinates on CPU side, which are expected to be * accessible via mesh vertices. For this reason we do not evaluate multires to * grids when orco is requested. */ @@ -253,10 +251,8 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh } else { if (use_clnors) { - /* If custom normals are present and the option is turned on calculate the split - * normals and clear flag so the normals get interpolated to the result mesh. */ - BKE_mesh_calc_normals_split(mesh); - CustomData_clear_layer_flag(&mesh->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); + void *data = CustomData_add_layer(&mesh->loop_data, CD_NORMAL, CD_CONSTRUCT, mesh->totloop); + memcpy(data, mesh->corner_normals().data(), mesh->corner_normals().size_in_bytes()); } result = multires_as_mesh(mmd, ctx, mesh, subdiv); @@ -264,10 +260,8 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh if (use_clnors) { float(*lnors)[3] = static_cast( CustomData_get_layer_for_write(&result->loop_data, CD_NORMAL, result->totloop)); - BLI_assert(lnors != nullptr); BKE_mesh_set_custom_normals(result, lnors); - CustomData_set_layer_flag(&mesh->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); - CustomData_set_layer_flag(&result->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); + CustomData_free_layers(&result->loop_data, CD_NORMAL, result->totloop); } // BKE_subdiv_stats_print(&subdiv->stats); if (subdiv != runtime_data->subdiv) { diff --git a/source/blender/modifiers/intern/MOD_normal_edit.cc b/source/blender/modifiers/intern/MOD_normal_edit.cc index 2960b35c7f1..bd996c3d250 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.cc +++ b/source/blender/modifiers/intern/MOD_normal_edit.cc @@ -477,22 +477,6 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd, return mesh; } - /* XXX TODO(Rohan Rathi): - * Once we fully switch to Mesh evaluation of modifiers, - * we can expect to get that flag from the COW copy. - * But for now, it is lost in the DM intermediate step, - * so we need to directly check orig object's data. */ -#if 0 - if (!(mesh->flag & ME_AUTOSMOOTH)) -#else - if (!(((Mesh *)ob->data)->flag & ME_AUTOSMOOTH)) -#endif - { - BKE_modifier_set_error( - ob, (ModifierData *)enmd, "Enable 'Auto Smooth' in Object Data Properties"); - return mesh; - } - Mesh *result; if (mesh->edges().data() == ((Mesh *)ob->data)->edges().data()) { /* We need to duplicate data here, otherwise setting custom normals @@ -540,8 +524,6 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd, sharp_edges.span.data(), sharp_faces, clnors, - true, - result->smoothresh, nullptr, loop_normals); } diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.cc b/source/blender/modifiers/intern/MOD_shrinkwrap.cc index f6e39f4a6d4..2a30b96843c 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.cc +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.cc @@ -108,7 +108,7 @@ static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphCont CustomData_MeshMasks mask = {0}; if (BKE_shrinkwrap_needs_normals(smd->shrinkType, smd->shrinkMode)) { - mask.lmask |= CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL; + mask.lmask |= CD_MASK_CUSTOMLOOPNORMAL; } if (smd->target != nullptr) { diff --git a/source/blender/modifiers/intern/MOD_subsurf.cc b/source/blender/modifiers/intern/MOD_subsurf.cc index 16b3212b082..0e99ea09560 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.cc +++ b/source/blender/modifiers/intern/MOD_subsurf.cc @@ -66,7 +66,6 @@ static void required_data_mask(ModifierData *md, CustomData_MeshMasks *r_cddata_ { SubsurfModifierData *smd = (SubsurfModifierData *)md; if (smd->flags & eSubsurfModifierFlag_UseCustomNormals) { - r_cddata_masks->lmask |= CD_MASK_NORMAL; r_cddata_masks->lmask |= CD_MASK_CUSTOMLOOPNORMAL; } } @@ -206,7 +205,6 @@ static void subdiv_cache_mesh_wrapper_settings(const ModifierEvalContext *ctx, runtime_data->has_gpu_subdiv = true; runtime_data->resolution = mesh_settings.resolution; runtime_data->use_optimal_display = mesh_settings.use_optimal_display; - runtime_data->calc_loop_normals = false; /* Set at the end of modifier stack evaluation. */ runtime_data->use_loop_normals = (smd->flags & eSubsurfModifierFlag_UseCustomNormals); mesh->runtime->subsurf_runtime_data = runtime_data; @@ -260,10 +258,8 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh } const bool use_clnors = BKE_subsurf_modifier_use_custom_loop_normals(smd, mesh); if (use_clnors) { - /* If custom normals are present and the option is turned on calculate the split - * normals and clear flag so the normals get interpolated to the result mesh. */ - BKE_mesh_calc_normals_split(mesh); - CustomData_clear_layer_flag(&mesh->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); + void *data = CustomData_add_layer(&mesh->loop_data, CD_NORMAL, CD_CONSTRUCT, mesh->totloop); + memcpy(data, mesh->corner_normals().data(), mesh->corner_normals().size_in_bytes()); } /* TODO(sergey): Decide whether we ever want to use CCG for subsurf, * maybe when it is a last modifier in the stack? */ @@ -275,12 +271,10 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh } if (use_clnors) { - float(*lnors)[3] = static_cast( - CustomData_get_layer_for_write(&result->loop_data, CD_NORMAL, result->totloop)); - BLI_assert(lnors != nullptr); - BKE_mesh_set_custom_normals(result, lnors); - CustomData_set_layer_flag(&mesh->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); - CustomData_set_layer_flag(&result->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); + BKE_mesh_set_custom_normals(result, + static_cast(CustomData_get_layer_for_write( + &result->loop_data, CD_NORMAL, result->totloop))); + CustomData_free_layers(&result->loop_data, CD_NORMAL, result->totloop); } // BKE_subdiv_stats_print(&subdiv->stats); if (!ELEM(subdiv, runtime_data->subdiv_cpu, runtime_data->subdiv_gpu)) { diff --git a/source/blender/modifiers/intern/MOD_triangulate.cc b/source/blender/modifiers/intern/MOD_triangulate.cc index a9d29d02ebb..5f6ee851e9b 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.cc +++ b/source/blender/modifiers/intern/MOD_triangulate.cc @@ -53,9 +53,8 @@ static Mesh *triangulate_mesh(Mesh *mesh, bool keep_clnors = (flag & MOD_TRIANGULATE_KEEP_CUSTOMLOOP_NORMALS) != 0; if (keep_clnors) { - BKE_mesh_calc_normals_split(mesh); - /* We need that one to 'survive' to/from BMesh conversions. */ - CustomData_clear_layer_flag(&mesh->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); + void *data = CustomData_add_layer(&mesh->loop_data, CD_NORMAL, CD_CONSTRUCT, mesh->totloop); + memcpy(data, mesh->corner_normals().data(), mesh->corner_normals().size_in_bytes()); cd_mask_extra.lmask |= CD_MASK_NORMAL; } @@ -76,13 +75,8 @@ static Mesh *triangulate_mesh(Mesh *mesh, if (keep_clnors) { float(*lnors)[3] = static_cast( CustomData_get_layer_for_write(&result->loop_data, CD_NORMAL, result->totloop)); - BLI_assert(lnors != nullptr); - BKE_mesh_set_custom_normals(result, lnors); - - /* Do some cleanup, we do not want those temp data to stay around. */ - CustomData_set_layer_flag(&mesh->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); - CustomData_set_layer_flag(&result->loop_data, CD_NORMAL, CD_FLAG_TEMPORARY); + CustomData_free_layers(&result->loop_data, CD_NORMAL, result->totloop); } return result; diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.cc b/source/blender/modifiers/intern/MOD_weighted_normal.cc index 7a878c4d6da..f376b1f4577 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.cc +++ b/source/blender/modifiers/intern/MOD_weighted_normal.cc @@ -83,7 +83,6 @@ struct WeightedNormalData { blender::Span loop_to_face; blender::MutableSpan clnors; bool has_clnors; /* True if clnors already existed, false if we had to create them. */ - float split_angle; blender::OffsetIndices faces; blender::Span face_normals; @@ -207,7 +206,6 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, ModePair *mode_pair = wn_data->mode_pair; const bool has_clnors = wn_data->has_clnors; - const float split_angle = wn_data->split_angle; bke::mesh::CornerNormalSpaceArray lnors_spacearr; const bool keep_sharp = (wnmd->flag & MOD_WEIGHTEDNORMAL_KEEP_SHARP) != 0; @@ -233,8 +231,6 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, wn_data->sharp_edges.data(), wn_data->sharp_faces, has_clnors ? clnors.data() : nullptr, - true, - split_angle, &lnors_spacearr, loop_normals); @@ -363,8 +359,6 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, wn_data->sharp_edges.data(), wn_data->sharp_faces, has_clnors ? clnors.data() : nullptr, - true, - split_angle, nullptr, loop_normals); @@ -482,23 +476,6 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh { using namespace blender; WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md; - Object *ob = ctx->object; - - /* XXX TODO(Rohan Rathi): - * Once we fully switch to Mesh evaluation of modifiers, - * we can expect to get that flag from the COW copy. - * But for now, it is lost in the DM intermediate step, - * so we need to directly check orig object's data. */ -#if 0 - if (!(mesh->flag & ME_AUTOSMOOTH)) -#else - if (!(((Mesh *)ob->data)->flag & ME_AUTOSMOOTH)) -#endif - { - BKE_modifier_set_error( - ctx->object, (ModifierData *)wnmd, "Enable 'Auto Smooth' in Object Data Properties"); - return mesh; - } Mesh *result; result = (Mesh *)BKE_id_copy_ex(nullptr, &mesh->id, nullptr, LIB_ID_COPY_LOCALIZE); @@ -527,7 +504,6 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh weight = (weight - 1) * 25; } - const float split_angle = mesh->smoothresh; blender::short2 *clnors = static_cast( CustomData_get_layer_for_write(&result->loop_data, CD_CUSTOMLOOPNORMAL, mesh->totloop)); @@ -562,7 +538,6 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh wn_data.loop_to_face = loop_to_face_map; wn_data.clnors = {clnors, mesh->totloop}; wn_data.has_clnors = has_clnors; - wn_data.split_angle = split_angle; wn_data.faces = faces; wn_data.face_normals = mesh->face_normals(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc index ed652506869..53ebce559e6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc @@ -316,14 +316,9 @@ static void node_geo_exec(GeoNodeExecParams params) /* Retrieve face corner normals from each mesh. It's necessary to use face corner normals * because face normals or vertex normals may lose information (custom normals, auto smooth) in - * some cases. It isn't yet possible to retrieve lazily calculated face corner normals from a - * const mesh, so they are calculated here every time. */ - Array corner_normals_orig(surface_mesh_orig->totloop); - Array corner_normals_eval(surface_mesh_eval->totloop); - BKE_mesh_calc_normals_split_ex( - surface_mesh_orig, nullptr, reinterpret_cast(corner_normals_orig.data())); - BKE_mesh_calc_normals_split_ex( - surface_mesh_eval, nullptr, reinterpret_cast(corner_normals_eval.data())); + * some cases. */ + const Span corner_normals_orig = surface_mesh_orig->corner_normals(); + const Span corner_normals_eval = surface_mesh_eval->corner_normals(); std::atomic invalid_uv_count = 0; diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 61a099a7afe..9ceb6b9dd5f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -338,15 +338,36 @@ static void compute_normal_outputs(const Mesh &mesh, const Span looptri_indices, MutableSpan r_normals) { - Array corner_normals(mesh.totloop); - BKE_mesh_calc_normals_split_ex( - &mesh, nullptr, reinterpret_cast(corner_normals.data())); - - const Span looptris = mesh.looptris(); - threading::parallel_for(bary_coords.index_range(), 512, [&](const IndexRange range) { - bke::mesh_surface_sample::sample_corner_normals( - looptris, looptri_indices, bary_coords, corner_normals, range, r_normals); - }); + switch (mesh.normals_domain()) { + case bke::MeshNormalDomain::Point: { + const Span corner_verts = mesh.corner_verts(); + const Span looptris = mesh.looptris(); + const Span vert_normals = mesh.vert_normals(); + threading::parallel_for(bary_coords.index_range(), 512, [&](const IndexRange range) { + bke::mesh_surface_sample::sample_point_normals( + corner_verts, looptris, looptri_indices, bary_coords, vert_normals, range, r_normals); + }); + break; + } + case bke::MeshNormalDomain::Face: { + const Span looptri_faces = mesh.looptri_faces(); + VArray face_normals = VArray::ForSpan(mesh.face_normals()); + threading::parallel_for(bary_coords.index_range(), 512, [&](const IndexRange range) { + bke::mesh_surface_sample::sample_face_attribute( + looptri_faces, looptri_indices, face_normals, range, r_normals); + }); + break; + } + case bke::MeshNormalDomain::Corner: { + const Span looptris = mesh.looptris(); + const Span corner_normals = mesh.corner_normals(); + threading::parallel_for(bary_coords.index_range(), 512, [&](const IndexRange range) { + bke::mesh_surface_sample::sample_corner_normals( + looptris, looptri_indices, bary_coords, corner_normals, range, r_normals); + }); + break; + } + } } static void compute_legacy_normal_outputs(const Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index ca861f09e4e..955607f5ae7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -148,7 +148,6 @@ static void expand_mesh(Mesh &mesh, mesh.face_offset_indices[mesh.faces_num] = mesh.totloop + loop_expand; } if (loop_expand != 0) { - CustomData_free_layers(&mesh.loop_data, CD_NORMAL, mesh.totloop); CustomData_free_layers(&mesh.loop_data, CD_MDISPS, mesh.totloop); CustomData_free_layers(&mesh.loop_data, CD_TANGENT, mesh.totloop); CustomData_free_layers(&mesh.loop_data, CD_PAINT_MASK, mesh.totloop); diff --git a/source/blender/render/intern/bake.cc b/source/blender/render/intern/bake.cc index 9c6d8d07824..a5921a0ae2b 100644 --- a/source/blender/render/intern/bake.cc +++ b/source/blender/render/intern/bake.cc @@ -491,17 +491,14 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval } const TSpace *tspace = nullptr; - const float(*loop_normals)[3] = nullptr; + blender::Span corner_normals; if (tangent) { - BKE_mesh_ensure_normals_for_display(me_eval); - BKE_mesh_calc_normals_split(me_eval); BKE_mesh_calc_loop_tangents(me_eval, true, nullptr, 0); tspace = static_cast(CustomData_get_layer(&me_eval->loop_data, CD_TANGENT)); BLI_assert(tspace); - loop_normals = static_cast( - CustomData_get_layer(&me_eval->loop_data, CD_NORMAL)); + corner_normals = me_eval->corner_normals(); } const blender::Span vert_normals = me->vert_normals(); @@ -524,10 +521,10 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval triangles[i].tspace[2] = &tspace[lt->tri[2]]; } - if (loop_normals) { - triangles[i].loop_normal[0] = loop_normals[lt->tri[0]]; - triangles[i].loop_normal[1] = loop_normals[lt->tri[1]]; - triangles[i].loop_normal[2] = loop_normals[lt->tri[2]]; + if (!corner_normals.is_empty()) { + triangles[i].loop_normal[0] = corner_normals[lt->tri[0]]; + triangles[i].loop_normal[1] = corner_normals[lt->tri[1]]; + triangles[i].loop_normal[2] = corner_normals[lt->tri[2]]; } if (calculate_normal) { diff --git a/source/blender/render/intern/multires_bake.cc b/source/blender/render/intern/multires_bake.cc index c90f34d7dce..6026379c28a 100644 --- a/source/blender/render/intern/multires_bake.cc +++ b/source/blender/render/intern/multires_bake.cc @@ -519,6 +519,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, if (require_tangent) { if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { + const blender::Span corner_normals = temp_mesh->corner_normals(); BKE_mesh_calc_loop_tangent_ex( reinterpret_cast(positions.data()), faces, @@ -534,7 +535,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, 0, reinterpret_cast(vert_normals.data()), reinterpret_cast(face_normals.data()), - (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), + reinterpret_cast(corner_normals.data()), (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* May be nullptr. */ /* result */ &dm->loopData,