Mesh: Replace auto smooth with node group #108014

Merged
Hans Goudey merged 149 commits from HooglyBoogly/blender:refactor-mesh-corner-normals-lazy into main 2023-10-20 16:54:20 +02:00
98 changed files with 883 additions and 1160 deletions

View File

@ -816,9 +816,10 @@ static void create_mesh(Scene *scene,
const blender::OffsetIndices faces = b_mesh.faces();
const blender::Span<int> 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<blender::float3> corner_normals;
if (use_loop_normals) {
corner_normals = {
static_cast<const blender::float3 *>(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()) {

View File

@ -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();
}

View File

@ -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,

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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 **** */

View File

@ -162,8 +162,6 @@ void normals_calc_loop(Span<float3> 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<float3> r_loop_normals);

View File

@ -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,

View File

@ -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<int> 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,

View File

@ -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);

View File

@ -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<Vector<float3>> vert_normals_cache;
SharedCache<Vector<float3>> face_normals_cache;
SharedCache<Vector<float3>> corner_normals_cache;
/**
* Cache of offsets for vert to face/corner maps. The same offsets array is used to group

View File

@ -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. */

View File

@ -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));
}
HooglyBoogly marked this conversation as resolved Outdated

missing word after the

missing word after `the`
@ -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<float3> 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. */

View File

@ -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<int2> edges = mesh->edges_for_write();
MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
MutableSpan<int> corner_verts = mesh->corner_verts_for_write();

View File

@ -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<const blender::short2 *>(
CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL));
/* Cache loop nors into a temp CDLayer. */
blender::float3 *loop_nors_dst = static_cast<blender::float3 *>(
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<blender::float3 *>(
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<const bool *>(
CustomData_get_layer_named(&me_dst->edge_data, CD_PROP_BOOL, "sharp_edge"));
const bool *sharp_faces = static_cast<const bool *>(
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<const float(*)[3]>(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<blender::float3> positions_dst = me_dst->vert_positions();
const int num_verts_dst = me_dst->totvert;
const blender::Span<blender::int2> edges_dst = me_dst->edges();
const blender::OffsetIndices faces_dst = me_dst->faces();
const blender::Span<int> corner_verts_dst = me_dst->corner_verts();
const blender::Span<int> 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<const float(*)[3]>(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,

View File

@ -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;
}

View File

@ -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*/)

View File

@ -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<Mesh *>(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);

View File

@ -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<blender::float3 *>(r_loop_normals), corner_verts.size()});
}

View File

@ -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<bool> sharp_faces = attributes.lookup_or_add_for_write_only_span<bool>(
"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<bool> sharp_edges = attributes.lookup_or_add_for_write_span<bool>(
"sharp_edge", ATTR_DOMAIN_EDGE);
const bool *sharp_faces = static_cast<const bool *>(
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<const blender::short2 *>(
CustomData_get_layer(&mesh->loop_data, CD_CUSTOMLOOPNORMAL));
const bool *sharp_edges = static_cast<const bool *>(
CustomData_get_layer_named(&mesh->edge_data, CD_PROP_BOOL, "sharp_edge"));
const bool *sharp_faces = static_cast<const bool *>(
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<float3 *>(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)

View File

@ -167,9 +167,7 @@ void BKE_mesh_foreach_mapped_loop(Mesh *mesh,
/* XXX: investigate using EditMesh data. */
blender::Span<blender::float3> corner_normals;
if (flag & MESH_FOREACH_USE_NORMAL) {
corner_normals = {
static_cast<const blender::float3 *>(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<blender::float3> corner_normals;
if (flag & MESH_FOREACH_USE_NORMAL) {
corner_normals = {
static_cast<const blender::float3 *>(CustomData_get_layer(&mesh->loop_data, CD_NORMAL)),
mesh->totloop};
corner_normals = mesh->corner_normals();
}
const blender::Span<blender::float3> positions = mesh->vert_positions();

View File

@ -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<NodeFunctionCompare *>(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,

MOD_nodes_update_interface(object, md);?

`MOD_nodes_update_interface(object, md);`?

Maybe. I'd rather not bring in another "MOD" include to the blenkernel though, theoretically the dependency is supposed to be in the other direction. I'm investigating just importing the asset here anyway, that might simplify things.

Maybe. I'd rather not bring in another "MOD" include to the blenkernel though, theoretically the dependency is supposed to be in the other direction. I'm investigating just importing the asset here anyway, that might simplify things.
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<Mesh *>(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<NodesModifierData *>(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<ModifierData *>(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<IDPropertyUIDataFloat *>(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());
}
}
/** \} */

View File

@ -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<blender::float3> loop_normals(result_corner_verts.size());
blender::short2 *clnors = static_cast<blender::short2 *>(
@ -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);

View File

@ -299,6 +299,42 @@ static void normals_calc_faces_and_verts(const Span<float3> 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<bool> sharp_faces = *attributes.lookup_or_default<bool>(
"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<bool> sharp_edges = *attributes.lookup_or_default<bool>(
"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<blender::float3> Mesh::vert_normals() const
{
using namespace blender;
@ -348,25 +384,52 @@ blender::Span<blender::float3> Mesh::face_normals() const
return this->runtime->face_normals_cache.data();
}
void BKE_mesh_ensure_normals_for_display(Mesh *mesh)
blender::Span<blender::float3> 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<float3> &r_data) {
r_data.reinitialize(this->totloop);
const OffsetIndices<int> 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<float3> 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<const bool *>(
CustomData_get_layer_named(&this->edge_data, CD_PROP_BOOL, "sharp_edge"));
const bool *sharp_faces = static_cast<const bool *>(
CustomData_get_layer_named(&this->face_data, CD_PROP_BOOL, "sharp_face"));
const short2 *custom_normals = static_cast<const short2 *>(
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<int> 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<int> 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
HooglyBoogly marked this conversation as resolved Outdated

suggestion: this reads like a generic map generation function, it could include that this is for calculating sharp edges.

*suggestion*: this reads like a generic map generation function, it could include that this is for calculating sharp edges.
* 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<int> faces,
const Span<int> corner_verts,
const Span<int> corner_edges,
const Span<bool> sharp_faces,
const Span<bool> sharp_edges,
MutableSpan<int2> 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<float3> 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<float3> 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<float3> vert_positions,
* Note also that loose edges always have both values set to 0! */
Array<int2> 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<float3> 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<bool>(sharp_faces, sharp_faces ? faces.size() : 0),
Span<bool>(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<bool>(sharp_faces, sharp_faces ? faces.size() : 0),
Span<bool>(sharp_edges, sharp_edges ? edges.size() : 0),
edge_to_loops);
Vector<int> single_corners;
Vector<int> fan_corners;
@ -1367,10 +1447,6 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
BitVector<> done_loops(corner_verts.size(), false);
Array<float3> loop_normals(corner_verts.size());
const Array<int> 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<float3> 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<float3> positions,
sharp_edges.data(),
sharp_faces,
r_clnors_data.data(),
use_split_normals,
split_angle,
&lnors_spacearr,
loop_normals);
}

View File

@ -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<int> 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<blender::float3> loop_normals_src;
blender::Span<blender::float3> face_normals_dst;
blender::float3 *loop_normals_dst;
blender::Span<blender::float3> loop_normals_dst;
blender::Array<blender::float3> 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<const blender::short2 *>(
CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL));
/* Cache loop normals into a temporary custom data layer. */
loop_normals_dst = static_cast<blender::float3 *>(
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<blender::float3 *>(
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<const bool *>(
CustomData_get_layer_named(&mesh_dst->edge_data, CD_PROP_BOOL, "sharp_edge"));
const bool *sharp_faces = static_cast<const bool *>(
CustomData_get_layer_named(&mesh_dst->face_data, CD_PROP_BOOL, "sharp_face"));
blender::bke::mesh::normals_calc_loop(
{reinterpret_cast<const blender::float3 *>(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<const blender::float3 *>(
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();
}
}

View File

@ -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);
}

View File

@ -138,20 +138,12 @@ void BKE_mesh_calc_loop_tangent_single(Mesh *mesh,
return;
}
const float(*loop_normals)[3] = static_cast<const float(*)[3]>(
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<const float(*)[3]>(mesh->vert_positions().data()),
mesh->totvert,
mesh->corner_verts().data(),
r_looptangents,
loop_normals,
reinterpret_cast<const float(*)[3]>(mesh->corner_normals().data()),
reinterpret_cast<const float(*)[2]>(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<const float(*)[3]>(me_eval->vert_normals().data()),
reinterpret_cast<const float(*)[3]>(me_eval->face_normals().data()),
static_cast<const float(*)[3]>(CustomData_get_layer(&me_eval->loop_data, CD_NORMAL)),
reinterpret_cast<const float(*)[3]>(me_eval->corner_normals().data()),
/* may be nullptr */
static_cast<const float(*)[3]>(CustomData_get_layer(&me_eval->vert_data, CD_ORCO)),
/* result */

View File

@ -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<float(*)[3]>(
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<float(*)[3]>(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)) {

View File

@ -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<const float(*)[3]>(mesh->face_normals().data());
if ((mesh->flag & ME_AUTOSMOOTH) != 0) {
data->clnors = static_cast<const float(*)[3]>(
CustomData_get_layer(&mesh->loop_data, CD_NORMAL));
if (mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner) {
data->clnors = reinterpret_cast<const float(*)[3]>(mesh->corner_normals().data());
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -635,7 +635,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
if (bmain->versionfile <= 164) {
Mesh *me = static_cast<Mesh *>(bmain->meshes.first);
while (me) {
me->smoothresh = 30;
me->smoothresh_legacy = 30;
me = static_cast<Mesh *>(me->id.next);
}
}

View File

@ -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<float(*)[3]>(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;

View File

@ -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],

View File

@ -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. */

View File

@ -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<const CurveProfile *>(
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);

View File

@ -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;

View File

@ -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);

View File

@ -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<bool> sharp_faces = VArray<bool>::ForDerivedSpan<const BMFace *, bm_face_is_sharp>(
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<bool> sharp_edges = VArray<bool>::ForDerivedSpan<const BMEdge *, bm_edge_is_sharp>(
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<const blender::short2 *>(
CustomData_get_layer(&mr.me->loop_data, CD_CUSTOMLOOPNORMAL));
const bool *sharp_edges = static_cast<const bool *>(
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))
{
HooglyBoogly marked this conversation as resolved Outdated

It seems like you could potentially use VArray::ForDerivedSpan here.

It seems like you could potentially use `VArray::ForDerivedSpan` here.
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<const float(*)[3]>(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<float(*)[3]>(mr.loop_normals.data()),
true,
reinterpret_cast<float(*)[3]>(mr.bm_loop_normals.data()),
nullptr,
nullptr,
clnors_offset,
false);
mr.loop_normals = mr.bm_loop_normals;
}
}
}

View File

@ -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);

View File

@ -67,6 +67,7 @@ struct MeshRenderData {
blender::Span<blender::float3> bm_vert_normals;
blender::Span<blender::float3> bm_face_normals;
blender::Span<blender::float3> bm_face_centers;
blender::Array<blender::float3> 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<blender::float3> vert_normals;
blender::Span<blender::float3> face_normals;
blender::Span<blender::float3> 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<blender::float3> loop_normals;
blender::Span<int> loose_verts;
blender::Span<int> loose_edges;

View File

@ -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<const float(*)[3]>(
CustomData_get_layer(&coarse_mesh->loop_data, CD_NORMAL));
BLI_assert(loop_normals != nullptr);
const Mesh *coarse_mesh = subdiv_cache.mesh;
const Span<float3> 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(

View File

@ -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);

View File

@ -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<Mesh *>(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);

View File

@ -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<Mesh *>(obedit->data));
BKE_editmesh_lnorspace_update(em, static_cast<Mesh *>(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<Mesh *>(obedit->data));
BKE_editmesh_lnorspace_update(em, static_cast<Mesh *>(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<Mesh *>(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<Mesh *>(obedit->data));
bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
BKE_editmesh_lnorspace_update(em, static_cast<Mesh *>(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<Mesh *>(obedit->data));
BKE_editmesh_lnorspace_update(em, static_cast<Mesh *>(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<Mesh *>(obedit->data));
BKE_editmesh_lnorspace_update(em, static_cast<Mesh *>(obedit->data));
BKE_editmesh_lnorspace_update(em);
float(*vert_normals)[3] = static_cast<float(*)[3]>(
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<Mesh *>(obedit->data));
BKE_editmesh_lnorspace_update(em, static_cast<Mesh *>(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<float(*)[3]>(

View File

@ -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<bool> sharp_edges = attributes.lookup_or_add_for_write_span<bool>(
"sharp_edge", ATTR_DOMAIN_EDGE);
const bool *sharp_faces = static_cast<const bool *>(
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<int> corner_verts = mesh->corner_verts();
const Span<int> 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<bool> mesh_sharp_edges = *attributes.lookup_or_default<bool>(
"sharp_edge", ATTR_DOMAIN_EDGE, false);
@ -1166,15 +1139,6 @@ void ED_mesh_split_faces(Mesh *mesh)
Array<bool> 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]) {

View File

@ -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;

View File

@ -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;
}

View File

@ -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<Mesh *>(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<Mesh *>(ob->data), use_auto_smooth, auto_smooth_angle);
BKE_mesh_smooth_flag_set(static_cast<Mesh *>(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<Mesh *>(ob->data), angle);
}
}
BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(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");
}
/** \} */

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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<Mesh *>(surface_ob_orig.data);
const Mesh &surface_orig = *static_cast<const Mesh *>(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<MLoopTri> 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<float3> corner_normals_su = {
reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig.loop_data, CD_NORMAL)),
surface_orig.totloop};
const Span<float3> corner_normals_su = surface_orig.corner_normals();
const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig};
geometry::AddCurvesOnMeshInputs add_inputs;

View File

@ -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<Mesh *>(surface_ob_orig_->data);
surface_orig_ = static_cast<const Mesh *>(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<float3> corner_normals_su = {reinterpret_cast<const float3 *>(CustomData_get_layer(
&surface_orig_->loop_data, CD_NORMAL)),
surface_orig_->totloop};
const Span<float3> corner_normals_su = surface_orig_->corner_normals();
const Span<MLoopTri> surface_looptris_orig = surface_orig_->looptris();
const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig};

View File

@ -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<float3> surface_positions_;
Span<int> surface_corner_verts_;
Span<MLoopTri> surface_looptris_;
@ -113,20 +113,14 @@ struct PuffOperationExecutor {
falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape);
surface_ob_ = curves_id_->surface;
surface_ = static_cast<Mesh *>(surface_ob_->data);
surface_ = static_cast<const Mesh *>(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<const float3 *>(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_); });

View File

@ -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<MLoopTri> surface_looptris_orig_;
VArraySpan<float2> surface_uv_map_orig_;
Span<float3> 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<Mesh *>(surface_ob_orig_->data);
surface_orig_ = static_cast<const Mesh *>(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<float2>(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<const float3 *>(
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;

View File

@ -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<Mesh *>(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);
}
}
}

View File

@ -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<Mesh *>(tc->obedit->data));
BKE_editmesh_lnorspace_update(em, static_cast<Mesh *>(tc->obedit->data));
BKE_editmesh_lnorspace_update(em);
storeCustomLNorValue(tc, bm);
}

View File

@ -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<int> 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<blender::float3> 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]]);

View File

@ -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) {

View File

@ -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;
}

View File

@ -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<Imath::V3f> &points);
static void get_topology(Mesh *mesh,
std::vector<int32_t> &face_verts,
std::vector<int32_t> &loop_counts,
bool &r_has_flat_shaded_face);
std::vector<int32_t> &loop_counts);
static void get_edge_creases(Mesh *mesh,
std::vector<int32_t> &indices,
std::vector<int32_t> &lengths,
@ -72,9 +72,7 @@ static void get_edge_creases(Mesh *mesh,
static void get_vert_creases(Mesh *mesh,
std::vector<int32_t> &indices,
std::vector<float> &sharpnesses);
static void get_loop_normals(Mesh *mesh,
std::vector<Imath::V3f> &normals,
bool has_flat_shaded_poly);
static void get_loop_normals(const Mesh *mesh, std::vector<Imath::V3f> &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<Imath::V3f> points, normals;
std::vector<int32_t> face_verts, loop_counts;
std::vector<Imath::V3f> 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<Imath::V3f> points;
std::vector<int32_t> face_verts, loop_counts;
std::vector<int32_t> 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<Imath::V3f> &points)
static void get_topology(Mesh *mesh,
std::vector<int32_t> &face_verts,
std::vector<int32_t> &loop_counts,
bool &r_has_flat_shaded_face)
std::vector<int32_t> &loop_counts)
{
const OffsetIndices faces = mesh->faces();
const Span<int> corner_verts = mesh->corner_verts();
const bke::AttributeAccessor attributes = mesh->attributes();
const VArray<bool> sharp_faces = *attributes.lookup_or_default<bool>(
"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<Imath::V3f> &normals,
bool has_flat_shaded_poly)
static void get_loop_normals(const Mesh *mesh, std::vector<Imath::V3f> &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<float3 *>(normals.data()), normals.size());
BKE_mesh_calc_normals_split(mesh);
const float(*lnors)[3] = static_cast<const float(*)[3]>(
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<float3> 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<float3 *>(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<float3> 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;
}
}
}

View File

@ -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);
}

View File

@ -620,22 +620,19 @@ void GeometryExporter::create_normals(std::vector<Normal> &normals,
const Span<float3> vert_normals = me->vert_normals();
const blender::OffsetIndices faces = me->faces();
const Span<int> corner_verts = me->corner_verts();
const float(*lnors)[3] = nullptr;
bool use_custom_normals = false;
const bke::AttributeAccessor attributes = me->attributes();
const VArray<bool> sharp_faces = *attributes.lookup_or_default<bool>(
"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<blender::float3> 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<Normal> &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]]);

View File

@ -1173,7 +1173,6 @@ bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom)
}
else {
BKE_mesh_set_custom_normals(me, reinterpret_cast<float(*)[3]>(loop_normals.data()));
me->flag |= ME_AUTOSMOOTH;
}
}

View File

@ -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<float(*)[3]>(loop_normals_.data()));
mesh->flag |= ME_AUTOSMOOTH;
}
return mesh;

View File

@ -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<int> corner_verts = mesh->corner_verts();
const Span<MLoopTri> looptris = mesh->looptris();
Array<float3> corner_normals(mesh->totloop);
BKE_mesh_calc_normals_split_ex(
mesh, nullptr, reinterpret_cast<float(*)[3]>(corner_normals.data()));
Span<float3> corner_normals;
if (mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner) {
corner_normals = mesh->corner_normals();
}
const float2 *uv_map = static_cast<const float2 *>(
CustomData_get_layer(&mesh->loop_data, CD_PROP_FLOAT2));

View File

@ -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<float(*)[3]>(
@ -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);

View File

@ -10,6 +10,7 @@
#include <pxr/usd/usdShade/material.h>
#include <pxr/usd/usdShade/materialBindingAPI.h>
#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<const float(*)[3]>(
CustomData_get_layer(&mesh->loop_data, CD_NORMAL));
const OffsetIndices faces = mesh->faces();
const Span<int> 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<float3 *>(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<float3> vert_normals = mesh->vert_normals();
const Span<float3> face_normals = mesh->face_normals();
const VArray<bool> sharp_faces = *attributes.lookup_or_default<bool>(
"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<float3> 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;
}
}

View File

@ -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<Mesh *>(export_mesh_));
}
void OBJMesh::calc_smooth_groups(const bool use_bitflags)
{
const bool *sharp_edges = static_cast<const bool *>(
@ -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<const float(*)[3]>(
CustomData_get_layer(&export_mesh_->loop_data, CD_NORMAL));
Span<float3> 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);

View File

@ -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.

View File

@ -160,9 +160,6 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> 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. */

View File

@ -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);
}

View File

@ -137,7 +137,9 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
const Span<float3> 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<const float2 *>(

View File

@ -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, \

View File

@ -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.
*/
HooglyBoogly marked this conversation as resolved Outdated

I wonder if we could call this normals_domain. I found "all info" more confusing than useful at first.

I wonder if we could call this `normals_domain`. I found "all info" more confusing than useful at first.
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<blender::float3> 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<blender::float3> 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 */
HooglyBoogly marked this conversation as resolved Outdated

Could call this: ME_AUTOSMOOTH_LEGACY

Could also rename Mesh::smoothresh -> Mesh::smoothresh_legacy.

Could call this: `ME_AUTOSMOOTH_LEGACY` Could also rename `Mesh::smoothresh` -> `Mesh::smoothresh_legacy`.
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,

View File

@ -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)

View File

@ -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)

View File

@ -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"},

View File

@ -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<const float(*)[3]>(
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<float(*)[3]>(
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<const float(*)[3]>(
CustomData_get_layer(&me->loop_data, CD_NORMAL));
const float(*vec)[4] = static_cast<const float(*)[4]>(
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<const float(*)[3]>(
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<blender::float3> 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<const blender::float3 *>(
CustomData_get_layer(&mesh->loop_data, CD_NORMAL));
if (!normals) {
const blender::Span<blender::float3> normals = mesh->corner_normals();
if (normals.is_empty()) {
iter->valid = false;
return;
}
rna_iterator_array_begin(iter,
const_cast<blender::float3 *>(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<const blender::float3 *>(
CustomData_get_layer(&mesh->loop_data, CD_NORMAL));
if (index < 0 || index >= mesh->totloop || !normals) {
const blender::Span<blender::float3> 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<blender::float3 *>(&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");
HooglyBoogly marked this conversation as resolved Outdated

Think the description below has to be updated.

Think the description below has to be updated.
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);

View File

@ -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");

View File

@ -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);

View File

@ -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);

View File

@ -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<float(*)[3]>(
CustomData_get_layer_for_write(ldata, CD_NORMAL, mesh->totloop));
if (CustomData_has_layer(&mesh->loop_data, CD_CUSTOMLOOPNORMAL)) {
vert_clnors = static_cast<float(*)[3]>(
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<const float(*)[3]>(mesh->corner_normals().data()),
vert_clnors);
}
else {
direction = MOD_DISP_DIR_NOR;

View File

@ -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<float(*)[3]>(
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) {

View File

@ -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);
}

View File

@ -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) {

View File

@ -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<float(*)[3]>(
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<float(*)[3]>(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)) {

View File

@ -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<float(*)[3]>(
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;

View File

@ -83,7 +83,6 @@ struct WeightedNormalData {
blender::Span<int> loop_to_face;
blender::MutableSpan<blender::short2> clnors;
bool has_clnors; /* True if clnors already existed, false if we had to create them. */
float split_angle;
blender::OffsetIndices<int> faces;
blender::Span<blender::float3> 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<blender::short2 *>(
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();

View File

@ -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<float3> corner_normals_orig(surface_mesh_orig->totloop);
Array<float3> corner_normals_eval(surface_mesh_eval->totloop);
BKE_mesh_calc_normals_split_ex(
surface_mesh_orig, nullptr, reinterpret_cast<float(*)[3]>(corner_normals_orig.data()));
BKE_mesh_calc_normals_split_ex(
surface_mesh_eval, nullptr, reinterpret_cast<float(*)[3]>(corner_normals_eval.data()));
* some cases. */
const Span<float3> corner_normals_orig = surface_mesh_orig->corner_normals();
const Span<float3> corner_normals_eval = surface_mesh_eval->corner_normals();
std::atomic<int> invalid_uv_count = 0;

View File

@ -338,15 +338,36 @@ static void compute_normal_outputs(const Mesh &mesh,
const Span<int> looptri_indices,
MutableSpan<float3> r_normals)
{
Array<float3> corner_normals(mesh.totloop);
BKE_mesh_calc_normals_split_ex(
&mesh, nullptr, reinterpret_cast<float(*)[3]>(corner_normals.data()));
const Span<MLoopTri> 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<int> corner_verts = mesh.corner_verts();
const Span<MLoopTri> looptris = mesh.looptris();
const Span<float3> 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<int> looptri_faces = mesh.looptri_faces();
VArray<float3> face_normals = VArray<float3>::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<MLoopTri> looptris = mesh.looptris();
const Span<float3> 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,

View File

@ -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);

View File

@ -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<blender::float3> 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<const TSpace *>(CustomData_get_layer(&me_eval->loop_data, CD_TANGENT));
BLI_assert(tspace);
loop_normals = static_cast<const float(*)[3]>(
CustomData_get_layer(&me_eval->loop_data, CD_NORMAL));
corner_normals = me_eval->corner_normals();
}
const blender::Span<blender::float3> 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) {

View File

@ -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<blender::float3> corner_normals = temp_mesh->corner_normals();
BKE_mesh_calc_loop_tangent_ex(
reinterpret_cast<const float(*)[3]>(positions.data()),
faces,
@ -534,7 +535,7 @@ static void do_multires_bake(MultiresBakeRender *bkr,
0,
reinterpret_cast<const float(*)[3]>(vert_normals.data()),
reinterpret_cast<const float(*)[3]>(face_normals.data()),
(const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL),
reinterpret_cast<const float(*)[3]>(corner_normals.data()),
(const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* May be nullptr. */
/* result */
&dm->loopData,