Geometry Nodes: Edge Angle Node

Calculates the angle in radians between two faces that meet at an edge.
0 to PI in either direction with flat being 0 and folded over on itself PI.
If there are not 2 faces on the edge, the angle will be 0.

For valid edges, the angle is the same as the 'edge angle' overlay.

For the Face and Point domain, the node uses simple interpolation to calculate a value.

Differential Revision: https://developer.blender.org/D13366
This commit is contained in:
Johnny Matthews 2022-01-03 11:16:50 -06:00
parent ca143fafa6
commit b7ad58b945
7 changed files with 134 additions and 0 deletions

View File

@ -150,6 +150,7 @@ def mesh_node_items(context):
yield NodeItem("GeometryNodeSubdivisionSurface")
yield NodeItem("GeometryNodeTriangulate")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeInputMeshEdgeAngle")
yield NodeItem("GeometryNodeInputMeshEdgeNeighbors")
yield NodeItem("GeometryNodeInputMeshEdgeVertices")
yield NodeItem("GeometryNodeInputMeshFaceArea")

View File

@ -1707,6 +1707,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_INPUT_MESH_ISLAND 1144
#define GEO_NODE_INPUT_SCENE_TIME 1145
#define GEO_NODE_ACCUMULATE_FIELD 1146
#define GEO_NODE_INPUT_MESH_EDGE_ANGLE 1147
/** \} */

View File

@ -4927,6 +4927,7 @@ static void registerGeometryNodes()
register_node_type_geo_input_index();
register_node_type_geo_input_material_index();
register_node_type_geo_input_material();
register_node_type_geo_input_mesh_edge_angle();
register_node_type_geo_input_mesh_edge_neighbors();
register_node_type_geo_input_mesh_edge_vertices();
register_node_type_geo_input_mesh_face_area();

View File

@ -106,6 +106,7 @@ void register_node_type_geo_input_id(void);
void register_node_type_geo_input_index(void);
void register_node_type_geo_input_material_index(void);
void register_node_type_geo_input_material(void);
void register_node_type_geo_input_mesh_edge_angle(void);
void register_node_type_geo_input_mesh_edge_neighbors(void);
void register_node_type_geo_input_mesh_edge_vertices(void);
void register_node_type_geo_input_mesh_face_area(void);

View File

@ -359,6 +359,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "")
DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", InputMeshEdgeAngle, "Edge Angle", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS", InputMeshEdgeNeighbors, "Edge Neighbors", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "")

View File

@ -124,6 +124,7 @@ set(SRC
nodes/node_geo_input_index.cc
nodes/node_geo_input_material_index.cc
nodes/node_geo_input_material.cc
nodes/node_geo_input_mesh_edge_angle.cc
nodes/node_geo_input_mesh_edge_neighbors.cc
nodes/node_geo_input_mesh_edge_vertices.cc
nodes/node_geo_input_mesh_face_area.cc

View File

@ -0,0 +1,128 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_mesh.h"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_input_mesh_edge_angle_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_output<decl::Float>(N_("Angle"))
.field_source()
.description(
"The angle in radians between two faces where they meet at an edge. Flat edges and "
"Non-manifold edges have an angle of zero");
}
struct EdgeMapEntry {
int face_count;
int face_index_1;
int face_index_2;
};
class AngleFieldInput final : public GeometryFieldInput {
public:
AngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Angle Field")
{
category_ = Category::Generated;
}
GVArray get_varray_for_context(const GeometryComponent &component,
const AttributeDomain domain,
IndexMask UNUSED(mask)) const final
{
if (component.type() != GEO_COMPONENT_TYPE_MESH) {
return {};
}
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
const Mesh *mesh = mesh_component.get_for_read();
if (mesh == nullptr) {
return {};
}
Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
Span<MLoop> loops{mesh->mloop, mesh->totloop};
Array<EdgeMapEntry> edge_map(mesh->totedge, {0, 0, 0});
for (const int i_poly : polys.index_range()) {
const MPoly &mpoly = polys[i_poly];
for (const MLoop &loop : loops.slice(mpoly.loopstart, mpoly.totloop)) {
EdgeMapEntry &entry = edge_map[loop.e];
if (entry.face_count == 0) {
entry.face_index_1 = i_poly;
}
else if (entry.face_count == 1) {
entry.face_index_2 = i_poly;
}
entry.face_count++;
}
}
auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float {
if (edge_map[i].face_count == 2) {
const MPoly &mpoly_1 = polys[edge_map[i].face_index_1];
const MPoly &mpoly_2 = polys[edge_map[i].face_index_2];
float3 normal_1, normal_2;
BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, normal_1);
BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, normal_2);
return angle_normalized_v3v3(normal_1, normal_2);
}
else {
return 0.0f;
}
};
VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
return component.attribute_try_adapt_domain<float>(
std::move(angles), ATTR_DOMAIN_EDGE, domain);
}
uint64_t hash() const override
{
/* Some random constant hash. */
return 32426725235;
}
bool is_equal_to(const fn::FieldNode &other) const override
{
return dynamic_cast<const AngleFieldInput *>(&other) != nullptr;
}
};
static void node_geo_exec(GeoNodeExecParams params)
{
Field<float> angle_field{std::make_shared<AngleFieldInput>()};
params.set_output("Angle", std::move(angle_field));
}
} // namespace blender::nodes::node_geo_input_mesh_edge_angle_cc
void register_node_type_geo_input_mesh_edge_angle()
{
namespace file_ns = blender::nodes::node_geo_input_mesh_edge_angle_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_INPUT_MESH_EDGE_ANGLE, "Edge Angle", NODE_CLASS_INPUT, 0);
ntype.declare = file_ns::node_declare;
ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}