Design task: #93551 This PR replaces the auto smooth option with a geometry nodes modifier that sets the sharp edge attribute. This solves a fair number of long- standing problems related to auto smooth, simplifies the process of normal computation, and allows Blender to automatically choose between face, vertex, and face corner normals based on the sharp edge and face attributes. Versioning adds a geometry node group to objects with meshes that had auto-smooth enabled. The modifier can be applied, which also improves performance. Auto smooth is now unnecessary to get a combination of sharp and smooth edges. In general workflows are changed a bit. Separate procedural and destructive workflows are available. Custom normals can be used immediately without turning on the removed auto smooth option. **Procedural** The node group asset "Smooth by Angle" is the main way to set sharp normals based on the edge angle. It can be accessed directly in the add modifier menu. Of course the modifier can be reordered, muted, or applied like any other, or changed internally like any geometry nodes modifier. **Destructive** Often the sharp edges don't need to be dynamic. This can give better performance since edge angles don't need to be recalculated. In edit mode the two operators "Select Sharp Edges" and "Mark Sharp" can be used. In other modes, the "Shade Smooth by Angle" controls the edge sharpness directly. ### Breaking API Changes - `use_auto_smooth` is removed. Face corner normals are now used automatically if there are mixed smooth vs. not smooth tags. Meshes now always use custom normals if they exist. - In Cycles, the lack of the separate auto smooth state makes normals look triangulated when all faces are shaded smooth. - `auto_smooth_angle` is removed. Replaced by a modifier (or operator) controlling the sharp edge attribute. This means the mesh itself (without an object) doesn't know anything about automatically smoothing by angle anymore. - `create_normals_split`, `calc_normals_split`, and `free_normals_split` are removed, and are replaced by the simpler `Mesh.corner_normals` collection property. Since it gives access to the normals cache, it is automatically updated when relevant data changes. Addons are updated here: https://projects.blender.org/blender/blender-addons/pulls/104609 ### Tests - `geo_node_curves_test_deform_curves_on_surface` has slightly different results because face corner normals are used instead of interpolated vertex normals. - `bf_wavefront_obj_tests` has different export results for one file which mixed sharp and smooth faces without turning on auto smooth. - `cycles_mesh_cpu` has one object which is completely flat shaded. Previously every edge was split before rendering, now it looks triangulated. Pull Request: https://projects.blender.org/blender/blender/pulls/108014
380 lines
14 KiB
C++
380 lines
14 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#pragma once
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*/
|
|
|
|
#include "BLI_index_mask.hh"
|
|
|
|
#include "BKE_mesh.h"
|
|
#include "BKE_mesh_types.hh"
|
|
|
|
namespace blender::bke {
|
|
namespace mesh {
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Polygon Data Evaluation
|
|
* \{ */
|
|
|
|
/** Calculate the up direction for the face, depending on its winding direction. */
|
|
float3 face_normal_calc(Span<float3> vert_positions, Span<int> face_verts);
|
|
|
|
/**
|
|
* Calculate tessellation into #MLoopTri which exist only for this purpose.
|
|
*/
|
|
void looptris_calc(Span<float3> vert_positions,
|
|
OffsetIndices<int> faces,
|
|
Span<int> corner_verts,
|
|
MutableSpan<MLoopTri> looptris);
|
|
/**
|
|
* A version of #looptris_calc which takes pre-calculated face normals
|
|
* (used to avoid having to calculate the face normal for NGON tessellation).
|
|
*
|
|
* \note Only use this function if normals have already been calculated, there is no need
|
|
* to calculate normals just to use this function.
|
|
*/
|
|
void looptris_calc_with_normals(Span<float3> vert_positions,
|
|
OffsetIndices<int> faces,
|
|
Span<int> corner_verts,
|
|
Span<float3> face_normals,
|
|
MutableSpan<MLoopTri> looptris);
|
|
|
|
void looptris_calc_face_indices(OffsetIndices<int> faces, MutableSpan<int> looptri_faces);
|
|
|
|
/** Calculate the average position of the vertices in the face. */
|
|
float3 face_center_calc(Span<float3> vert_positions, Span<int> face_verts);
|
|
|
|
/** Calculate the surface area of the face described by the indexed vertices. */
|
|
float face_area_calc(Span<float3> vert_positions, Span<int> face_verts);
|
|
|
|
/** Calculate the angles at each of the faces corners. */
|
|
void face_angles_calc(Span<float3> vert_positions,
|
|
Span<int> face_verts,
|
|
MutableSpan<float> angles);
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Medium-Level Normals Calculation
|
|
* \{ */
|
|
|
|
/**
|
|
* Calculate face normals directly into a result array.
|
|
*
|
|
* \note Usually #Mesh::face_normals() is the preferred way to access face normals,
|
|
* since they may already be calculated and cached on the mesh.
|
|
*/
|
|
void normals_calc_faces(Span<float3> vert_positions,
|
|
OffsetIndices<int> faces,
|
|
Span<int> corner_verts,
|
|
MutableSpan<float3> face_normals);
|
|
|
|
/**
|
|
* Calculate vertex normals directly into the result array.
|
|
*
|
|
* \note Vertex and face normals can be calculated at the same time with
|
|
* #normals_calc_faces_and_verts, which can have performance benefits in some cases.
|
|
*
|
|
* \note Usually #Mesh::vert_normals() is the preferred way to access vertex normals,
|
|
* since they may already be calculated and cached on the mesh.
|
|
*/
|
|
void normals_calc_verts(Span<float3> vert_positions,
|
|
OffsetIndices<int> faces,
|
|
Span<int> corner_verts,
|
|
Span<float3> face_normals,
|
|
MutableSpan<float3> vert_normals);
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Face Corner Normal Calculation
|
|
* \{ */
|
|
|
|
/**
|
|
* Combined with the automatically calculated face corner normal, this gives a dimensional
|
|
* coordinate space used to convert normals between the "custom normal" #short2 representation and
|
|
* a regular #float3 format.
|
|
*/
|
|
struct CornerNormalSpace {
|
|
/** The automatically computed face corner normal, not including influence of custom normals. */
|
|
float3 vec_lnor;
|
|
/** Reference vector, orthogonal to #vec_lnor. */
|
|
float3 vec_ref;
|
|
/** Third vector, orthogonal to #vec_lnor and #vec_ref. */
|
|
float3 vec_ortho;
|
|
/** Reference angle around #vec_ortho, in [0, pi] range (0.0 marks space as invalid). */
|
|
float ref_alpha;
|
|
/** Reference angle around #vec_lnor, in [0, 2pi] range (0.0 marks space as invalid). */
|
|
float ref_beta;
|
|
};
|
|
|
|
/**
|
|
* Storage for corner fan coordinate spaces for an entire mesh.
|
|
*/
|
|
struct CornerNormalSpaceArray {
|
|
/**
|
|
* The normal coordinate spaces, potentially shared between multiple face corners in a smooth fan
|
|
* connected to a vertex (and not per face corner). Depending on the mesh (the amount of sharing
|
|
* / number of sharp edges / size of each fan), there may be many fewer spaces than face corners,
|
|
* so they are stored in a separate array.
|
|
*/
|
|
Array<CornerNormalSpace> spaces;
|
|
|
|
/**
|
|
* The index of the data in the #spaces array for each face corner (the array size is the
|
|
* same as #Mesh::totloop). Rare -1 values define face corners without a coordinate space.
|
|
*/
|
|
Array<int> corner_space_indices;
|
|
|
|
/**
|
|
* A map containing the face corners that make up each space,
|
|
* in the order that they were processed (winding around a vertex).
|
|
*/
|
|
Array<Array<int>> corners_by_space;
|
|
/** Whether to create the above map when calculating normals. */
|
|
bool create_corners_by_space = false;
|
|
};
|
|
|
|
short2 lnor_space_custom_normal_to_data(const CornerNormalSpace &lnor_space,
|
|
const float3 &custom_lnor);
|
|
|
|
/**
|
|
* Compute split normals, i.e. vertex normals associated with each face (hence 'loop normals').
|
|
* Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry
|
|
* (splitting edges).
|
|
*
|
|
* \param sharp_edges: Optional array of sharp edge tags, used to split the evaluated normals on
|
|
* each side of the edge.
|
|
* \param r_lnors_spacearr: Optional return data filled with information about the custom
|
|
* normals spaces for each grouped fan of face corners.
|
|
*/
|
|
void normals_calc_loop(Span<float3> vert_positions,
|
|
Span<int2> edges,
|
|
OffsetIndices<int> faces,
|
|
Span<int> corner_verts,
|
|
Span<int> corner_edges,
|
|
Span<int> loop_to_face_map,
|
|
Span<float3> vert_normals,
|
|
Span<float3> face_normals,
|
|
const bool *sharp_edges,
|
|
const bool *sharp_faces,
|
|
const short2 *clnors_data,
|
|
CornerNormalSpaceArray *r_lnors_spacearr,
|
|
MutableSpan<float3> r_loop_normals);
|
|
|
|
void normals_loop_custom_set(Span<float3> vert_positions,
|
|
Span<int2> edges,
|
|
OffsetIndices<int> faces,
|
|
Span<int> corner_verts,
|
|
Span<int> corner_edges,
|
|
Span<float3> vert_normals,
|
|
Span<float3> face_normals,
|
|
const bool *sharp_faces,
|
|
MutableSpan<bool> sharp_edges,
|
|
MutableSpan<float3> r_custom_loop_normals,
|
|
MutableSpan<short2> r_clnors_data);
|
|
|
|
void normals_loop_custom_set_from_verts(Span<float3> vert_positions,
|
|
Span<int2> edges,
|
|
OffsetIndices<int> faces,
|
|
Span<int> corner_verts,
|
|
Span<int> corner_edges,
|
|
Span<float3> vert_normals,
|
|
Span<float3> face_normals,
|
|
const bool *sharp_faces,
|
|
MutableSpan<bool> sharp_edges,
|
|
MutableSpan<float3> r_custom_vert_normals,
|
|
MutableSpan<short2> r_clnors_data);
|
|
|
|
/**
|
|
* Define sharp edges as needed to mimic 'autosmooth' from angle threshold.
|
|
*
|
|
* Used when defining an empty custom loop normals data layer,
|
|
* to keep same shading as with auto-smooth!
|
|
*
|
|
* \param sharp_faces: Optional array used to mark specific faces for sharp shading.
|
|
*/
|
|
void edges_sharp_from_angle_set(OffsetIndices<int> faces,
|
|
Span<int> corner_verts,
|
|
Span<int> corner_edges,
|
|
Span<float3> face_normals,
|
|
Span<int> loop_to_face,
|
|
const bool *sharp_faces,
|
|
const float split_angle,
|
|
MutableSpan<bool> sharp_edges);
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Topology Queries
|
|
* \{ */
|
|
|
|
/**
|
|
* Find the index of the next corner in the face, looping to the start if necessary.
|
|
* The indices are into the entire corners array, not just the face's corners.
|
|
*/
|
|
inline int face_corner_prev(const IndexRange face, const int corner)
|
|
{
|
|
return corner - 1 + (corner == face.start()) * face.size();
|
|
}
|
|
|
|
/**
|
|
* Find the index of the previous corner in the face, looping to the end if necessary.
|
|
* The indices are into the entire corners array, not just the face's corners.
|
|
*/
|
|
inline int face_corner_next(const IndexRange face, const int corner)
|
|
{
|
|
if (corner == face.last()) {
|
|
return face.start();
|
|
}
|
|
return corner + 1;
|
|
}
|
|
|
|
/**
|
|
* Find the index of the corner in the face that uses the given vertex.
|
|
* The index is into the entire corners array, not just the face's corners.
|
|
*/
|
|
inline int face_find_corner_from_vert(const IndexRange face,
|
|
const Span<int> corner_verts,
|
|
const int vert)
|
|
{
|
|
return face[corner_verts.slice(face).first_index(vert)];
|
|
}
|
|
|
|
/**
|
|
* Return the vertex indices on either side of the given vertex, ordered based on the winding
|
|
* direction of the face. The vertex must be in the face.
|
|
*/
|
|
inline int2 face_find_adjecent_verts(const IndexRange face,
|
|
const Span<int> corner_verts,
|
|
const int vert)
|
|
{
|
|
const int corner = face_find_corner_from_vert(face, corner_verts, vert);
|
|
return {corner_verts[face_corner_prev(face, corner)],
|
|
corner_verts[face_corner_next(face, corner)]};
|
|
}
|
|
|
|
/**
|
|
* Return the number of triangles needed to tessellate a face with \a face_size corners.
|
|
*/
|
|
inline int face_triangles_num(const int face_size)
|
|
{
|
|
BLI_assert(face_size > 2);
|
|
return face_size - 2;
|
|
}
|
|
|
|
/**
|
|
* Return the index of the edge's vertex that is not the \a vert.
|
|
* If neither edge vertex is equal to \a v, returns -1.
|
|
*/
|
|
inline int edge_other_vert(const int2 &edge, const int vert)
|
|
{
|
|
if (edge[0] == vert) {
|
|
return edge[1];
|
|
}
|
|
if (edge[1] == vert) {
|
|
return edge[0];
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
} // namespace mesh
|
|
|
|
void mesh_flip_faces(Mesh &mesh, const IndexMask &selection);
|
|
|
|
/** Set mesh vertex normals to known-correct values, avoiding future lazy computation. */
|
|
void mesh_vert_normals_assign(Mesh &mesh, Span<float3> vert_normals);
|
|
|
|
/** Set mesh vertex normals to known-correct values, avoiding future lazy computation. */
|
|
void mesh_vert_normals_assign(Mesh &mesh, Vector<float3> vert_normals);
|
|
|
|
} // namespace blender::bke
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Inline Mesh Data Access
|
|
* \{ */
|
|
|
|
inline blender::Span<blender::float3> Mesh::vert_positions() const
|
|
{
|
|
return {static_cast<const blender::float3 *>(
|
|
CustomData_get_layer_named(&this->vert_data, CD_PROP_FLOAT3, "position")),
|
|
this->totvert};
|
|
}
|
|
inline blender::MutableSpan<blender::float3> Mesh::vert_positions_for_write()
|
|
{
|
|
return {static_cast<blender::float3 *>(CustomData_get_layer_named_for_write(
|
|
&this->vert_data, CD_PROP_FLOAT3, "position", this->totvert)),
|
|
this->totvert};
|
|
}
|
|
|
|
inline blender::Span<blender::int2> Mesh::edges() const
|
|
{
|
|
return {static_cast<const blender::int2 *>(
|
|
CustomData_get_layer_named(&this->edge_data, CD_PROP_INT32_2D, ".edge_verts")),
|
|
this->totedge};
|
|
}
|
|
inline blender::MutableSpan<blender::int2> Mesh::edges_for_write()
|
|
{
|
|
return {static_cast<blender::int2 *>(CustomData_get_layer_named_for_write(
|
|
&this->edge_data, CD_PROP_INT32_2D, ".edge_verts", this->totedge)),
|
|
this->totedge};
|
|
}
|
|
|
|
inline blender::OffsetIndices<int> Mesh::faces() const
|
|
{
|
|
return blender::Span(this->face_offset_indices, this->faces_num + 1);
|
|
}
|
|
inline blender::Span<int> Mesh::face_offsets() const
|
|
{
|
|
if (this->faces_num == 0) {
|
|
return {};
|
|
}
|
|
return {this->face_offset_indices, this->faces_num + 1};
|
|
}
|
|
|
|
inline blender::Span<int> Mesh::corner_verts() const
|
|
{
|
|
return {static_cast<const int *>(
|
|
CustomData_get_layer_named(&this->loop_data, CD_PROP_INT32, ".corner_vert")),
|
|
this->totloop};
|
|
}
|
|
inline blender::MutableSpan<int> Mesh::corner_verts_for_write()
|
|
{
|
|
return {static_cast<int *>(CustomData_get_layer_named_for_write(
|
|
&this->loop_data, CD_PROP_INT32, ".corner_vert", this->totloop)),
|
|
this->totloop};
|
|
}
|
|
|
|
inline blender::Span<int> Mesh::corner_edges() const
|
|
{
|
|
return {static_cast<const int *>(
|
|
CustomData_get_layer_named(&this->loop_data, CD_PROP_INT32, ".corner_edge")),
|
|
this->totloop};
|
|
}
|
|
inline blender::MutableSpan<int> Mesh::corner_edges_for_write()
|
|
{
|
|
return {static_cast<int *>(CustomData_get_layer_named_for_write(
|
|
&this->loop_data, CD_PROP_INT32, ".corner_edge", this->totloop)),
|
|
this->totloop};
|
|
}
|
|
|
|
inline blender::Span<MDeformVert> Mesh::deform_verts() const
|
|
{
|
|
const MDeformVert *dverts = BKE_mesh_deform_verts(this);
|
|
if (!dverts) {
|
|
return {};
|
|
}
|
|
return {dverts, this->totvert};
|
|
}
|
|
inline blender::MutableSpan<MDeformVert> Mesh::deform_verts_for_write()
|
|
{
|
|
return {BKE_mesh_deform_verts_for_write(this), this->totvert};
|
|
}
|
|
|
|
/** \} */
|