Mesh: Replace auto smooth with node group #108014
|
@ -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(). *
|
||||
*/
|
||||
|
|
|
@ -449,15 +449,16 @@ static void add_orco_mesh(
|
|||
|
||||
static void mesh_calc_modifier_final_normals(const bool sculpt_dyntopo, Mesh *mesh_final)
|
||||
{
|
||||
const eAttrDomain domain = eAttrDomain(mesh_final->normals_domain());
|
||||
using namespace blender::bke;
|
||||
const MeshNormalDomain domain = mesh_final->normals_domain();
|
||||
|
||||
/* 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 = domain == ATTR_DOMAIN_CORNER;
|
||||
subsurf_runtime_data->calc_loop_normals = domain == MeshNormalDomain::Corner;
|
||||
}
|
||||
|
||||
if (domain == ATTR_DOMAIN_CORNER) {
|
||||
if (domain == MeshNormalDomain::Corner) {
|
||||
/* 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) {
|
||||
|
@ -467,10 +468,10 @@ static void mesh_calc_modifier_final_normals(const bool sculpt_dyntopo, Mesh *me
|
|||
else {
|
||||
if (sculpt_dyntopo == false) {
|
||||
HooglyBoogly marked this conversation as resolved
Outdated
|
||||
/* Eager normal calculation can potentially be faster than deferring to drawing code. */
|
||||
if (domain == ATTR_DOMAIN_FACE) {
|
||||
if (domain == MeshNormalDomain::Face) {
|
||||
mesh_final->face_normals();
|
||||
}
|
||||
HooglyBoogly marked this conversation as resolved
Outdated
Campbell Barton
commented
Shouldn't this be Shouldn't this be `ATTR_DOMAIN_VERT` ?
|
||||
else if (domain == ATTR_DOMAIN_POINT) {
|
||||
else if (domain == MeshNormalDomain::Point) {
|
||||
mesh_final->vert_normals();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -299,16 +299,16 @@ static void normals_calc_faces_and_verts(const Span<float3> positions,
|
|||
/** \name Mesh Normal Calculation
|
||||
* \{ */
|
||||
|
||||
int Mesh::normals_domain() const
|
||||
blender::bke::MeshNormalDomain Mesh::normals_domain() const
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::bke;
|
||||
if (this->faces_num == 0) {
|
||||
return ATTR_DOMAIN_POINT;
|
||||
return MeshNormalDomain::Point;
|
||||
}
|
||||
|
||||
if (CustomData_has_layer(&this->loop_data, CD_CUSTOMLOOPNORMAL)) {
|
||||
return ATTR_DOMAIN_CORNER;
|
||||
return MeshNormalDomain::Corner;
|
||||
}
|
||||
|
||||
const AttributeAccessor attributes = this->attributes();
|
||||
|
@ -317,22 +317,22 @@ int Mesh::normals_domain() const
|
|||
|
||||
const array_utils::BooleanMix face_mix = array_utils::booleans_mix_calc(sharp_faces);
|
||||
if (face_mix == array_utils::BooleanMix::AllTrue) {
|
||||
return ATTR_DOMAIN_FACE;
|
||||
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 ATTR_DOMAIN_FACE;
|
||||
return MeshNormalDomain::Face;
|
||||
}
|
||||
|
||||
if (edge_mix == array_utils::BooleanMix::AllFalse &&
|
||||
face_mix == array_utils::BooleanMix::AllFalse) {
|
||||
return ATTR_DOMAIN_POINT;
|
||||
return MeshNormalDomain::Point;
|
||||
}
|
||||
|
||||
return ATTR_DOMAIN_CORNER;
|
||||
return MeshNormalDomain::Corner;
|
||||
}
|
||||
|
||||
blender::Span<blender::float3> Mesh::vert_normals() const
|
||||
|
@ -387,15 +387,16 @@ blender::Span<blender::float3> Mesh::face_normals() const
|
|||
blender::Span<blender::float3> Mesh::corner_normals() const
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::bke;
|
||||
this->runtime->corner_normals_cache.ensure([&](Vector<float3> &r_data) {
|
||||
r_data.reinitialize(this->totloop);
|
||||
const OffsetIndices faces = this->faces();
|
||||
r_data.reinitialize(faces.total_size());
|
||||
switch (this->normals_domain()) {
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
case MeshNormalDomain::Point: {
|
||||
array_utils::gather(this->vert_normals(), this->corner_verts(), r_data.as_mutable_span());
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_FACE: {
|
||||
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) {
|
||||
|
@ -404,30 +405,28 @@ blender::Span<blender::float3> Mesh::corner_normals() const
|
|||
});
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_CORNER: {
|
||||
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));
|
||||
bke::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);
|
||||
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;
|
||||
}
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
});
|
||||
return this->runtime->corner_normals_cache.data();
|
||||
|
|
|
@ -142,7 +142,7 @@ 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->normals_domain() == ATTR_DOMAIN_CORNER) {
|
||||
if (mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner) {
|
||||
data->clnors = reinterpret_cast<const float(*)[3]>(mesh->corner_normals().data());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ 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->normals_domain() == ATTR_DOMAIN_CORNER;
|
||||
mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner;
|
||||
}
|
||||
|
||||
static bool is_subdivision_evaluation_possible_on_gpu()
|
||||
|
|
|
@ -413,8 +413,9 @@ void mesh_render_data_update_normals(MeshRenderData &mr, const eMRDataType data_
|
|||
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) &&
|
||||
ELEM(mr.me->normals_domain(), ATTR_DOMAIN_CORNER, ATTR_DOMAIN_FACE)) ||
|
||||
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
Jacques Lucke
commented
It seems like you could potentially use It seems like you could potentially use `VArray::ForDerivedSpan` here.
|
||||
mr.loop_normals = mr.me->corner_normals();
|
||||
|
|
|
@ -2152,7 +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->normals_domain() == ATTR_DOMAIN_CORNER;
|
||||
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);
|
||||
|
|
|
@ -712,7 +712,7 @@ static Mesh *bake_mesh_new_from_object(Depsgraph *depsgraph,
|
|||
{
|
||||
Mesh *me = BKE_mesh_new_from_object(depsgraph, object, false, preserve_origindex);
|
||||
|
||||
if (me->normals_domain() == ATTR_DOMAIN_CORNER) {
|
||||
if (me->normals_domain() == blender::bke::MeshNormalDomain::Corner) {
|
||||
ED_mesh_split_faces(me);
|
||||
}
|
||||
|
||||
|
|
|
@ -526,12 +526,12 @@ static void get_loop_normals(const Mesh *mesh, std::vector<Imath::V3f> &normals)
|
|||
normals.clear();
|
||||
|
||||
switch (mesh->normals_domain()) {
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
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 ATTR_DOMAIN_FACE: {
|
||||
case blender::bke::MeshNormalDomain::Face: {
|
||||
normals.resize(mesh->totloop);
|
||||
MutableSpan dst_normals(reinterpret_cast<float3 *>(normals.data()), normals.size());
|
||||
|
||||
|
@ -546,7 +546,7 @@ static void get_loop_normals(const Mesh *mesh, std::vector<Imath::V3f> &normals)
|
|||
});
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_CORNER: {
|
||||
case blender::bke::MeshNormalDomain::Corner: {
|
||||
normals.resize(mesh->totloop);
|
||||
MutableSpan dst_normals(reinterpret_cast<float3 *>(normals.data()), normals.size());
|
||||
|
||||
|
@ -563,8 +563,6 @@ static void get_loop_normals(const Mesh *mesh, std::vector<Imath::V3f> &normals)
|
|||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -626,7 +626,7 @@ void GeometryExporter::create_normals(std::vector<Normal> &normals,
|
|||
"sharp_face", ATTR_DOMAIN_FACE, false);
|
||||
|
||||
blender::Span<blender::float3> corner_normals;
|
||||
if (me->normals_domain() == ATTR_DOMAIN_CORNER) {
|
||||
if (me->normals_domain() == blender::bke::MeshNormalDomain::Corner) {
|
||||
corner_normals = me->corner_normals();
|
||||
}
|
||||
|
||||
|
|
|
@ -233,7 +233,7 @@ void MeshData::write_submeshes(const Mesh *mesh)
|
|||
const Span<MLoopTri> looptris = mesh->looptris();
|
||||
|
||||
Span<float3> corner_normals;
|
||||
if (mesh->normals_domain() == ATTR_DOMAIN_CORNER) {
|
||||
if (mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner) {
|
||||
corner_normals = mesh->corner_normals();
|
||||
}
|
||||
|
||||
|
|
|
@ -653,11 +653,11 @@ void USDGenericMeshWriter::write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_
|
|||
MutableSpan dst_normals(reinterpret_cast<float3 *>(loop_normals.data()), loop_normals.size());
|
||||
|
||||
switch (mesh->normals_domain()) {
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
case bke::MeshNormalDomain::Point: {
|
||||
array_utils::gather(mesh->vert_normals(), mesh->corner_verts(), dst_normals);
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_FACE: {
|
||||
case bke::MeshNormalDomain::Face: {
|
||||
const OffsetIndices faces = mesh->faces();
|
||||
const Span<float3> face_normals = mesh->face_normals();
|
||||
for (const int i : faces.index_range()) {
|
||||
|
@ -665,12 +665,10 @@ void USDGenericMeshWriter::write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_
|
|||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_CORNER: {
|
||||
case bke::MeshNormalDomain::Corner: {
|
||||
array_utils::copy(mesh->corner_normals(), dst_normals);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
pxr::UsdAttribute attr_normals = usd_mesh.CreateNormalsAttr(pxr::VtValue(), true);
|
||||
|
|
|
@ -368,7 +368,7 @@ void OBJMesh::store_normal_coords_and_indices()
|
|||
loop_to_normal_index_.fill(-1);
|
||||
|
||||
Span<float3> corner_normals;
|
||||
if (export_mesh_->normals_domain() == ATTR_DOMAIN_CORNER) {
|
||||
if (export_mesh_->normals_domain() == blender::bke::MeshNormalDomain::Corner) {
|
||||
corner_normals = export_mesh_->corner_normals();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -366,7 +367,7 @@ typedef struct Mesh {
|
|||
* face corner normals, since there is a 2-4x performance cost increase for each more complex
|
||||
* domain.
|
||||
*/
|
||||
HooglyBoogly marked this conversation as resolved
Outdated
Jacques Lucke
commented
I wonder if we could call this I wonder if we could call this `normals_domain`. I found "all info" more confusing than useful at first.
|
||||
int normals_domain() const;
|
||||
blender::bke::MeshNormalDomain normals_domain() const;
|
||||
/**
|
||||
* Normal direction of polygons, defined by positions and the winding direction of face corners.
|
||||
*/
|
||||
|
@ -438,7 +439,7 @@ 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
Campbell Barton
commented
Could call this: Could also rename Could call this: `ME_AUTOSMOOTH_LEGACY`
Could also rename `Mesh::smoothresh` -> `Mesh::smoothresh_legacy`.
|
||||
ME_AUTOSMOOTH_LEGACY = 1 << 5, /* deprecated */
|
||||
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,
|
||||
|
|
|
@ -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"
|
||||
|
@ -1619,7 +1620,7 @@ int rna_Mesh_loops_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
|
|||
|
||||
static int rna_Mesh_normals_domain_get(PointerRNA *ptr)
|
||||
{
|
||||
return rna_mesh(ptr)->normals_domain();
|
||||
return int(rna_mesh(ptr)->normals_domain());
|
||||
}
|
||||
|
||||
static void rna_Mesh_vertex_normals_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
||||
|
@ -2999,11 +3000,18 @@ 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, rna_enum_attribute_domain_only_mesh_no_edge_items);
|
||||
RNA_def_property_enum_items(prop, normal_domain_items);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Normal Domain All Info",
|
||||
"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);
|
||||
|
|
|
@ -216,7 +216,7 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh
|
|||
return result;
|
||||
}
|
||||
const bool use_clnors = mmd->flags & eMultiresModifierFlag_UseCustomNormals &&
|
||||
mesh->normals_domain() == ATTR_DOMAIN_CORNER;
|
||||
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. */
|
||||
|
|
|
@ -337,7 +337,7 @@ static void compute_normal_outputs(const Mesh &mesh,
|
|||
MutableSpan<float3> r_normals)
|
||||
{
|
||||
switch (mesh.normals_domain()) {
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
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();
|
||||
|
@ -347,7 +347,7 @@ static void compute_normal_outputs(const Mesh &mesh,
|
|||
});
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_FACE: {
|
||||
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) {
|
||||
|
@ -356,7 +356,7 @@ static void compute_normal_outputs(const Mesh &mesh,
|
|||
});
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_CORNER: {
|
||||
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) {
|
||||
|
@ -365,8 +365,6 @@ static void compute_normal_outputs(const Mesh &mesh,
|
|||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
missing word after
the