Compare commits
23 Commits
temp-ui-cp
...
temp-geome
Author | SHA1 | Date | |
---|---|---|---|
efcaac7ea6 | |||
c9df2017dc | |||
101a298775 | |||
d86becbf67 | |||
b39b552ded | |||
7bd29bc0ea | |||
409bcdc102 | |||
1438398488 | |||
1385f3af9e | |||
901237f8de | |||
becd9d7f99 | |||
e15c20fa3e | |||
0a9a9190a1 | |||
3cd76cc27b | |||
38f5fcc94d | |||
32e8381e0e | |||
c31a346ec5 | |||
00c5fa3bf1 | |||
2f6a84bee9 | |||
f9e03a5e16 | |||
abaed315d5 | |||
05a7f7c1fd | |||
7e3459ff78 |
@@ -142,6 +142,7 @@ def mesh_node_items(context):
|
||||
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
|
||||
|
||||
yield NodeItem("GeometryNodeDualMesh")
|
||||
yield NodeItem("GeometryNodeExtrudeMesh")
|
||||
yield NodeItem("GeometryNodeMeshBoolean")
|
||||
yield NodeItem("GeometryNodeMeshToCurve")
|
||||
yield NodeItem("GeometryNodeMeshToPoints")
|
||||
@@ -149,6 +150,7 @@ def mesh_node_items(context):
|
||||
yield NodeItem("GeometryNodeSubdivideMesh")
|
||||
yield NodeItem("GeometryNodeSubdivisionSurface")
|
||||
yield NodeItem("GeometryNodeTriangulate")
|
||||
yield NodeItem("GeometryNodeScaleElements")
|
||||
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
|
||||
yield NodeItem("GeometryNodeInputMeshEdgeAngle")
|
||||
yield NodeItem("GeometryNodeInputMeshEdgeNeighbors")
|
||||
|
@@ -25,6 +25,12 @@
|
||||
#include "BKE_mesh_types.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include "BLI_span.hh"
|
||||
# include "DNA_mesh_types.h"
|
||||
# include "DNA_meshdata_types.h"
|
||||
#endif
|
||||
|
||||
struct BLI_Stack;
|
||||
struct BMesh;
|
||||
struct BMeshCreateParams;
|
||||
@@ -979,3 +985,47 @@ BLI_INLINE int BKE_mesh_origindex_mface_mpoly(const int *index_mf_to_mpoly,
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
inline Span<MVert> mesh_verts(const Mesh &mesh)
|
||||
{
|
||||
return {mesh.mvert, mesh.totvert};
|
||||
}
|
||||
inline MutableSpan<MVert> mesh_verts(Mesh &mesh)
|
||||
{
|
||||
return {mesh.mvert, mesh.totvert};
|
||||
}
|
||||
|
||||
inline Span<MEdge> mesh_edges(const Mesh &mesh)
|
||||
{
|
||||
return {mesh.medge, mesh.totedge};
|
||||
}
|
||||
inline MutableSpan<MEdge> mesh_edges(Mesh &mesh)
|
||||
{
|
||||
return {mesh.medge, mesh.totedge};
|
||||
}
|
||||
|
||||
inline Span<MPoly> mesh_polys(const Mesh &mesh)
|
||||
{
|
||||
return {mesh.mpoly, mesh.totpoly};
|
||||
}
|
||||
inline MutableSpan<MPoly> mesh_polys(Mesh &mesh)
|
||||
{
|
||||
return {mesh.mpoly, mesh.totpoly};
|
||||
}
|
||||
|
||||
inline Span<MLoop> mesh_loops(const Mesh &mesh)
|
||||
{
|
||||
return {mesh.mloop, mesh.totloop};
|
||||
}
|
||||
inline MutableSpan<MLoop> mesh_loops(Mesh &mesh)
|
||||
{
|
||||
return {mesh.mloop, mesh.totloop};
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
#endif
|
||||
|
@@ -1633,6 +1633,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
||||
#define GEO_NODE_INPUT_SCENE_TIME 1145
|
||||
#define GEO_NODE_ACCUMULATE_FIELD 1146
|
||||
#define GEO_NODE_INPUT_MESH_EDGE_ANGLE 1147
|
||||
#define GEO_NODE_SCALE_ELEMENTS 1148
|
||||
#define GEO_NODE_EXTRUDE_MESH 1149
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -2227,7 +2227,7 @@ void CustomData_realloc(CustomData *data, int totelem)
|
||||
continue;
|
||||
}
|
||||
typeInfo = layerType_getInfo(layer->type);
|
||||
layer->data = MEM_reallocN(layer->data, (size_t)totelem * typeInfo->size);
|
||||
layer->data = MEM_recallocN(layer->data, (size_t)totelem * typeInfo->size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -30,6 +30,8 @@
|
||||
#include "BKE_spline.hh"
|
||||
#include "BKE_volume.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
@@ -611,6 +613,8 @@ bool NormalFieldInput::is_equal_to(const fn::FieldNode &other) const
|
||||
|
||||
/** \} */
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name C API
|
||||
* \{ */
|
||||
|
@@ -4766,6 +4766,7 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_distribute_points_on_faces();
|
||||
register_node_type_geo_dual_mesh();
|
||||
register_node_type_geo_edge_split();
|
||||
register_node_type_geo_extrude_mesh();
|
||||
register_node_type_geo_geometry_to_instance();
|
||||
register_node_type_geo_image_texture();
|
||||
register_node_type_geo_input_curve_handles();
|
||||
@@ -4821,6 +4822,7 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_realize_instances();
|
||||
register_node_type_geo_rotate_instances();
|
||||
register_node_type_geo_sample_texture();
|
||||
register_node_type_geo_scale_elements();
|
||||
register_node_type_geo_scale_instances();
|
||||
register_node_type_geo_separate_components();
|
||||
register_node_type_geo_separate_geometry();
|
||||
|
@@ -96,7 +96,7 @@ std::ostream &operator<<(std::ostream &stream, const eSpace &space);
|
||||
/** Template class to store RGBA values with different precision, space and alpha association. */
|
||||
template<typename ChannelStorageType, eSpace Space, eAlpha Alpha> class ColorRGBA {
|
||||
public:
|
||||
ChannelStorageType r, g, b, a;
|
||||
ChannelStorageType r = 0, g = 0, b = 0, a = 1;
|
||||
constexpr ColorRGBA() = default;
|
||||
|
||||
constexpr ColorRGBA(const ChannelStorageType rgba[4])
|
||||
|
@@ -1385,6 +1385,11 @@ typedef struct NodeGeometryPointTranslate {
|
||||
uint8_t input_type;
|
||||
} NodeGeometryPointTranslate;
|
||||
|
||||
typedef struct NodeGeometryExtrudeMesh {
|
||||
/* GeometryNodeExtrudeMeshMode */
|
||||
uint8_t mode;
|
||||
} NodeGeometryExtrudeMesh;
|
||||
|
||||
typedef struct NodeGeometryObjectInfo {
|
||||
/* GeometryNodeTransformSpace. */
|
||||
uint8_t transform_space;
|
||||
@@ -2149,6 +2154,12 @@ typedef enum GeometryNodeDistributePointsOnFacesMode {
|
||||
GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON = 1,
|
||||
} GeometryNodeDistributePointsOnFacesMode;
|
||||
|
||||
typedef enum GeometryNodeExtrudeMeshMode {
|
||||
GEO_NODE_EXTRUDE_MESH_VERTICES = 0,
|
||||
GEO_NODE_EXTRUDE_MESH_EDGES = 1,
|
||||
GEO_NODE_EXTRUDE_MESH_FACES = 2,
|
||||
} GeometryNodeExtrudeMeshMode;
|
||||
|
||||
typedef enum GeometryNodeRotatePointsType {
|
||||
GEO_NODE_POINT_ROTATE_TYPE_EULER = 0,
|
||||
GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1,
|
||||
@@ -2330,6 +2341,13 @@ typedef enum GeometryNodeRealizeInstancesFlag {
|
||||
GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR = (1 << 0),
|
||||
} GeometryNodeRealizeInstancesFlag;
|
||||
|
||||
typedef enum GeometryNodeScaleElementsMode {
|
||||
GEO_NODE_SCALE_ELEMENTS_MODE_FACE = 0,
|
||||
GEO_NODE_SCALE_ELEMENTS_MODE_EDGE = 1,
|
||||
} GeometryNodeScaleElementsMode;
|
||||
|
||||
#define GEO_NODE_SCALE_ELEMENTS_UNIFORM 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -9889,6 +9889,27 @@ static void def_geo_point_distribute(StructRNA *srna)
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_extrude_mesh(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
static const EnumPropertyItem mode_items[] = {
|
||||
{GEO_NODE_EXTRUDE_MESH_VERTICES, "VERTICES", 0, "Vertices", ""},
|
||||
{GEO_NODE_EXTRUDE_MESH_EDGES, "EDGES", 0, "Edges", ""},
|
||||
{GEO_NODE_EXTRUDE_MESH_FACES, "FACES", 0, "Faces", ""},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeGeometryExtrudeMesh", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "mode");
|
||||
RNA_def_property_enum_items(prop, mode_items);
|
||||
RNA_def_property_enum_default(prop, GEO_NODE_EXTRUDE_MESH_FACES);
|
||||
RNA_def_property_ui_text(prop, "Mode", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
}
|
||||
|
||||
static void def_geo_distribute_points_on_faces(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
@@ -11372,6 +11393,37 @@ static void def_geo_realize_instances(StructRNA *srna)
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_scale_elements(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
static const EnumPropertyItem scale_elements_mode_items[] = {
|
||||
{GEO_NODE_SCALE_ELEMENTS_MODE_FACE,
|
||||
"FACE",
|
||||
ICON_NONE,
|
||||
"Face",
|
||||
"Scale individual faces or neighbouring face islands"},
|
||||
{GEO_NODE_SCALE_ELEMENTS_MODE_EDGE,
|
||||
"EDGE",
|
||||
ICON_NONE,
|
||||
"Edge",
|
||||
"Scale individual edges or neighbouring edge islands"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "custom1");
|
||||
RNA_def_property_enum_items(prop, scale_elements_mode_items);
|
||||
RNA_def_property_enum_default(prop, GEO_NODE_SCALE_ELEMENTS_MODE_FACE);
|
||||
RNA_def_property_ui_text(prop, "Mode", "Element type to transform");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
|
||||
|
||||
prop = RNA_def_property(srna, "uniform", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "custom2", GEO_NODE_SCALE_ELEMENTS_UNIFORM);
|
||||
RNA_def_property_ui_text(prop, "Uniform", "Scale element by the same factor in every direction");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static void rna_def_shader_node(BlenderRNA *brna)
|
||||
|
@@ -16,6 +16,9 @@
|
||||
|
||||
#include "MOD_nodes_evaluator.hh"
|
||||
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_type_conversions.hh"
|
||||
|
||||
#include "NOD_geometry_exec.hh"
|
||||
@@ -381,6 +384,11 @@ static bool get_implicit_socket_input(const SocketRef &socket, void *r_value)
|
||||
new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side));
|
||||
return true;
|
||||
}
|
||||
if (bnode.type == GEO_NODE_EXTRUDE_MESH) {
|
||||
new (r_value)
|
||||
ValueOrField<float3>(Field<float3>(std::make_shared<bke::NormalFieldInput>()));
|
||||
return true;
|
||||
}
|
||||
new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position"));
|
||||
return true;
|
||||
}
|
||||
|
@@ -98,6 +98,7 @@ void register_node_type_geo_delete_geometry(void);
|
||||
void register_node_type_geo_distribute_points_on_faces(void);
|
||||
void register_node_type_geo_dual_mesh(void);
|
||||
void register_node_type_geo_edge_split(void);
|
||||
void register_node_type_geo_extrude_mesh(void);
|
||||
void register_node_type_geo_geometry_to_instance(void);
|
||||
void register_node_type_geo_image_texture(void);
|
||||
void register_node_type_geo_input_curve_handles(void);
|
||||
@@ -153,6 +154,7 @@ void register_node_type_geo_raycast(void);
|
||||
void register_node_type_geo_realize_instances(void);
|
||||
void register_node_type_geo_rotate_instances(void);
|
||||
void register_node_type_geo_sample_texture(void);
|
||||
void register_node_type_geo_scale_elements(void);
|
||||
void register_node_type_geo_scale_instances(void);
|
||||
void register_node_type_geo_select_by_handle_type(void);
|
||||
void register_node_type_geo_separate_components(void);
|
||||
|
@@ -350,6 +350,7 @@ DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE
|
||||
DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "")
|
||||
DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "")
|
||||
DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, def_geo_extrude_mesh, "EXTRUDE_MESH", ExtrudeMesh, "Extrude Mesh", "")
|
||||
DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "")
|
||||
DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "")
|
||||
DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "")
|
||||
@@ -403,6 +404,7 @@ DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE
|
||||
DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "")
|
||||
DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "")
|
||||
DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "")
|
||||
DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "")
|
||||
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
|
||||
DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "")
|
||||
|
@@ -116,6 +116,7 @@ set(SRC
|
||||
nodes/node_geo_distribute_points_on_faces.cc
|
||||
nodes/node_geo_dual_mesh.cc
|
||||
nodes/node_geo_edge_split.cc
|
||||
nodes/node_geo_extrude_mesh.cc
|
||||
nodes/node_geo_geometry_to_instance.cc
|
||||
nodes/node_geo_image_texture.cc
|
||||
nodes/node_geo_input_curve_handles.cc
|
||||
@@ -164,6 +165,7 @@ set(SRC
|
||||
nodes/node_geo_raycast.cc
|
||||
nodes/node_geo_realize_instances.cc
|
||||
nodes/node_geo_rotate_instances.cc
|
||||
nodes/node_geo_scale_elements.cc
|
||||
nodes/node_geo_scale_instances.cc
|
||||
nodes/node_geo_separate_components.cc
|
||||
nodes/node_geo_separate_geometry.cc
|
||||
|
1281
source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
Normal file
1281
source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
Normal file
File diff suppressed because it is too large
Load Diff
396
source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
Normal file
396
source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
Normal file
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* 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 "BLI_array.hh"
|
||||
#include "BLI_disjoint_set.hh"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "BKE_mesh.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_scale_elements_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH);
|
||||
b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
|
||||
b.add_input<decl::Float>(N_("Scale"), "Scale_Float").default_value(1.0f).supports_field();
|
||||
b.add_input<decl::Vector>(N_("Scale"), "Scale_Vector")
|
||||
.default_value({1.0f, 1.0f, 1.0f})
|
||||
.supports_field();
|
||||
b.add_input<decl::Vector>(N_("Pivot"))
|
||||
.subtype(PROP_TRANSLATION)
|
||||
.implicit_field()
|
||||
.description(
|
||||
N_("Origin of the scaling for each element. If multiple elements are scaled together, "
|
||||
"their pivot is averaged"));
|
||||
b.add_input<decl::Vector>(N_("X Axis"))
|
||||
.default_value({1.0f, 0.0f, 0.0f})
|
||||
.supports_field()
|
||||
.description(N_("Direction which the x component of the scale input changes the element"));
|
||||
b.add_input<decl::Vector>(N_("Up"))
|
||||
.default_value({0.0f, 0.0f, 1.0f})
|
||||
.supports_field()
|
||||
.description(N_("Direction which the z component of the scale input changes the element (or "
|
||||
"approximate direction when not orthogonal to x axis)"));
|
||||
b.add_output<decl::Geometry>(N_("Geometry"));
|
||||
};
|
||||
|
||||
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||
{
|
||||
uiItemR(layout, ptr, "mode", 0, nullptr, ICON_NONE);
|
||||
uiItemR(layout, ptr, "uniform", 0, nullptr, ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
|
||||
{
|
||||
node->custom1 = GEO_NODE_SCALE_ELEMENTS_MODE_FACE;
|
||||
node->custom2 = GEO_NODE_SCALE_ELEMENTS_UNIFORM;
|
||||
}
|
||||
|
||||
static void node_update(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
bNodeSocket *geometry_socket = static_cast<bNodeSocket *>(node->inputs.first);
|
||||
bNodeSocket *selection_socket = geometry_socket->next;
|
||||
bNodeSocket *scale_float_socket = selection_socket->next;
|
||||
bNodeSocket *scale_vector_socket = scale_float_socket->next;
|
||||
bNodeSocket *pivot_socket = scale_vector_socket->next;
|
||||
bNodeSocket *x_axis_socket = pivot_socket->next;
|
||||
bNodeSocket *up_socket = x_axis_socket->next;
|
||||
|
||||
const bool use_uniform_scale = node->custom2 & GEO_NODE_SCALE_ELEMENTS_UNIFORM;
|
||||
|
||||
nodeSetSocketAvailability(ntree, scale_float_socket, use_uniform_scale);
|
||||
nodeSetSocketAvailability(ntree, scale_vector_socket, !use_uniform_scale);
|
||||
nodeSetSocketAvailability(ntree, x_axis_socket, !use_uniform_scale);
|
||||
nodeSetSocketAvailability(ntree, up_socket, !use_uniform_scale);
|
||||
}
|
||||
|
||||
struct InputFields {
|
||||
bool use_uniform_scale;
|
||||
Field<bool> selection;
|
||||
Field<float> uniform_scale;
|
||||
Field<float3> vector_scale;
|
||||
Field<float3> pivot;
|
||||
Field<float3> x_axis;
|
||||
Field<float3> up;
|
||||
};
|
||||
|
||||
struct EvaluatedFields {
|
||||
IndexMask selection;
|
||||
VArray<float> uniform_scales;
|
||||
VArray<float3> vector_scales;
|
||||
VArray<float3> pivots;
|
||||
VArray<float3> x_axis_vectors;
|
||||
VArray<float3> up_vectors;
|
||||
};
|
||||
|
||||
/**
|
||||
* When multiple elements share the same vertices, they are scaled together.
|
||||
*/
|
||||
struct ElementIsland {
|
||||
/* Either face or edge indices. */
|
||||
Vector<int> element_indices;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepares a matrix that can be used to transform every point in an island.
|
||||
*/
|
||||
static float4x4 create_transform(const float3 &pivot,
|
||||
float3 x_axis,
|
||||
const float3 &up,
|
||||
const float3 &scale)
|
||||
{
|
||||
/* Compute main scale axis. */
|
||||
x_axis = x_axis.normalized();
|
||||
const float3 y_axis = -float3::cross(x_axis, up).normalized();
|
||||
const float3 z_axis = float3::cross(x_axis, y_axis);
|
||||
|
||||
float4x4 transform;
|
||||
unit_m4(transform.values);
|
||||
|
||||
/* Move pivot to origin. */
|
||||
sub_v3_v3(transform.values[3], pivot);
|
||||
|
||||
float4x4 axis_transform;
|
||||
unit_m4(axis_transform.values);
|
||||
copy_v3_v3(axis_transform.values[0], x_axis);
|
||||
copy_v3_v3(axis_transform.values[1], y_axis);
|
||||
copy_v3_v3(axis_transform.values[2], z_axis);
|
||||
|
||||
/* Can invert by transposing, because the matrix is orthonormal. */
|
||||
float4x4 axis_transform_inv = axis_transform.transposed();
|
||||
|
||||
/* Prepare scale matrix. */
|
||||
float4x4 scale_transform;
|
||||
unit_m4(scale_transform.values);
|
||||
scale_transform.values[0][0] = scale.x;
|
||||
scale_transform.values[1][1] = scale.y;
|
||||
scale_transform.values[2][2] = scale.z;
|
||||
|
||||
/* Do the scaling in the right space. */
|
||||
transform = axis_transform * scale_transform * axis_transform_inv * transform;
|
||||
|
||||
/* Move pivot back to where it was. */
|
||||
add_v3_v3(transform.values[3], pivot);
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
static EvaluatedFields evaluate_fields(FieldEvaluator &evaluator, const InputFields &input_fields)
|
||||
{
|
||||
EvaluatedFields evaluated;
|
||||
evaluator.set_selection(input_fields.selection);
|
||||
if (input_fields.use_uniform_scale) {
|
||||
evaluator.add(input_fields.uniform_scale, &evaluated.uniform_scales);
|
||||
}
|
||||
else {
|
||||
evaluator.add(input_fields.vector_scale, &evaluated.vector_scales);
|
||||
evaluator.add(input_fields.x_axis, &evaluated.x_axis_vectors);
|
||||
evaluator.add(input_fields.up, &evaluated.up_vectors);
|
||||
}
|
||||
evaluator.add(input_fields.pivot, &evaluated.pivots);
|
||||
evaluator.evaluate();
|
||||
evaluated.selection = evaluator.get_evaluated_selection_as_mask();
|
||||
return evaluated;
|
||||
}
|
||||
|
||||
static void scale_vertex_islands(
|
||||
Mesh &mesh,
|
||||
const Span<ElementIsland> islands,
|
||||
const EvaluatedFields &evaluated,
|
||||
const FunctionRef<void(int element_index, Vector<int> &r_vertex_indices)> get_vertex_indices)
|
||||
{
|
||||
threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
|
||||
Set<int> handled_vertices;
|
||||
for (const int island_index : range) {
|
||||
const ElementIsland &island = islands[island_index];
|
||||
|
||||
float3 scale = {0.0f, 0.0f, 0.0f};
|
||||
float3 pivot = {0.0f, 0.0f, 0.0f};
|
||||
float3 x_axis = {0.0f, 0.0f, 0.0f};
|
||||
float3 up = {0.0f, 0.0f, 0.0f};
|
||||
|
||||
Vector<int> vertex_indices;
|
||||
for (const int poly_index : island.element_indices) {
|
||||
get_vertex_indices(poly_index, vertex_indices);
|
||||
pivot += evaluated.pivots[poly_index];
|
||||
if (evaluated.uniform_scales) {
|
||||
scale += float3(evaluated.uniform_scales[poly_index]);
|
||||
x_axis += float3(1, 0, 0);
|
||||
up += float3(0, 0, 1);
|
||||
}
|
||||
else {
|
||||
scale += evaluated.vector_scales[poly_index];
|
||||
x_axis += evaluated.x_axis_vectors[poly_index];
|
||||
up += evaluated.up_vectors[poly_index];
|
||||
}
|
||||
}
|
||||
|
||||
/* Divide by number of elements to get the average. */
|
||||
const float f = 1.0f / island.element_indices.size();
|
||||
scale *= f;
|
||||
pivot *= f;
|
||||
x_axis *= f;
|
||||
up *= f;
|
||||
|
||||
const float4x4 transform = create_transform(pivot, x_axis, up, scale);
|
||||
handled_vertices.clear();
|
||||
for (const int vert_index : vertex_indices) {
|
||||
if (!handled_vertices.add(vert_index)) {
|
||||
/* This vertex has been transformed already. */
|
||||
continue;
|
||||
}
|
||||
MVert &vert = mesh.mvert[vert_index];
|
||||
const float3 old_position = vert.co;
|
||||
const float3 new_position = transform * old_position;
|
||||
copy_v3_v3(vert.co, new_position);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Positions have changed, so the normals will have to be recomputed. */
|
||||
BKE_mesh_normals_tag_dirty(&mesh);
|
||||
}
|
||||
|
||||
static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexMask face_selection)
|
||||
{
|
||||
/* Use the disjoing set data structure to determine which vertices have to be scaled together. */
|
||||
DisjointSet disjoint_set(mesh.totvert);
|
||||
for (const int poly_index : face_selection) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
|
||||
for (const int loop_index : IndexRange(poly.totloop - 1)) {
|
||||
const int v1 = poly_loops[loop_index].v;
|
||||
const int v2 = poly_loops[loop_index + 1].v;
|
||||
disjoint_set.join(v1, v2);
|
||||
}
|
||||
disjoint_set.join(poly_loops.first().v, poly_loops.last().v);
|
||||
}
|
||||
|
||||
VectorSet<int> island_ids;
|
||||
Vector<ElementIsland> islands;
|
||||
/* There are at most as many islands as there are selected faces. */
|
||||
islands.reserve(face_selection.size());
|
||||
for (const int poly_index : face_selection) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
|
||||
const int island_id = disjoint_set.find_root(poly_loops[0].v);
|
||||
const int island_index = island_ids.index_of_or_add(island_id);
|
||||
if (island_index == islands.size()) {
|
||||
islands.append_as();
|
||||
}
|
||||
ElementIsland &island = islands[island_index];
|
||||
island.element_indices.append(poly_index);
|
||||
}
|
||||
|
||||
return islands;
|
||||
}
|
||||
|
||||
static void scale_faces(MeshComponent &mesh_component, const InputFields &input_fields)
|
||||
{
|
||||
Mesh &mesh = *mesh_component.get_for_write();
|
||||
mesh.mvert = static_cast<MVert *>(
|
||||
CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
|
||||
|
||||
GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
|
||||
FieldEvaluator evaluator{field_context, mesh.totpoly};
|
||||
EvaluatedFields evaluated = evaluate_fields(evaluator, input_fields);
|
||||
|
||||
Vector<ElementIsland> island = prepare_face_islands(mesh, evaluated.selection);
|
||||
scale_vertex_islands(
|
||||
mesh, island, evaluated, [&](int face_index, Vector<int> &r_vertex_indices) {
|
||||
const MPoly &poly = mesh.mpoly[face_index];
|
||||
const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
|
||||
for (const MLoop &loop : poly_loops) {
|
||||
r_vertex_indices.append(loop.v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexMask edge_selection)
|
||||
{
|
||||
/* Use the disjoing set data structure to determine which vertices have to be scaled together. */
|
||||
DisjointSet disjoint_set(mesh.totvert);
|
||||
for (const int edge_index : edge_selection) {
|
||||
const MEdge &edge = mesh.medge[edge_index];
|
||||
disjoint_set.join(edge.v1, edge.v2);
|
||||
}
|
||||
|
||||
VectorSet<int> island_ids;
|
||||
Vector<ElementIsland> islands;
|
||||
/* There are at most as many islands as there are selected edges. */
|
||||
islands.reserve(edge_selection.size());
|
||||
for (const int edge_index : edge_selection) {
|
||||
const MEdge &edge = mesh.medge[edge_index];
|
||||
const int island_id = disjoint_set.find_root(edge.v1);
|
||||
const int island_index = island_ids.index_of_or_add(island_id);
|
||||
if (island_index == islands.size()) {
|
||||
islands.append_as();
|
||||
}
|
||||
ElementIsland &island = islands[island_index];
|
||||
island.element_indices.append(edge_index);
|
||||
}
|
||||
|
||||
return islands;
|
||||
}
|
||||
|
||||
static void scale_edges(MeshComponent &mesh_component, const InputFields &input_fields)
|
||||
{
|
||||
Mesh &mesh = *mesh_component.get_for_write();
|
||||
mesh.mvert = static_cast<MVert *>(
|
||||
CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
|
||||
|
||||
GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
|
||||
FieldEvaluator evaluator{field_context, mesh.totedge};
|
||||
EvaluatedFields evaluated = evaluate_fields(evaluator, input_fields);
|
||||
|
||||
Vector<ElementIsland> island = prepare_edge_islands(mesh, evaluated.selection);
|
||||
scale_vertex_islands(
|
||||
mesh, island, evaluated, [&](const int edge_index, Vector<int> &r_vertex_indices) {
|
||||
const MEdge &edge = mesh.medge[edge_index];
|
||||
r_vertex_indices.append(edge.v1);
|
||||
r_vertex_indices.append(edge.v2);
|
||||
});
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
const bNode &node = params.node();
|
||||
const GeometryNodeScaleElementsMode mode = static_cast<GeometryNodeScaleElementsMode>(
|
||||
node.custom1);
|
||||
|
||||
GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
|
||||
InputFields input_fields;
|
||||
input_fields.use_uniform_scale = node.custom2 & GEO_NODE_SCALE_ELEMENTS_UNIFORM;
|
||||
input_fields.selection = params.get_input<Field<bool>>("Selection");
|
||||
if (input_fields.use_uniform_scale) {
|
||||
input_fields.uniform_scale = params.get_input<Field<float>>("Scale_Float");
|
||||
}
|
||||
else {
|
||||
input_fields.vector_scale = params.get_input<Field<float3>>("Scale_Vector");
|
||||
input_fields.x_axis = params.get_input<Field<float3>>("X Axis");
|
||||
input_fields.up = params.get_input<Field<float3>>("Up");
|
||||
}
|
||||
input_fields.pivot = params.get_input<Field<float3>>("Pivot");
|
||||
|
||||
geometry.modify_geometry_sets([&](GeometrySet &geometry) {
|
||||
switch (mode) {
|
||||
case GEO_NODE_SCALE_ELEMENTS_MODE_FACE: {
|
||||
if (geometry.has_mesh()) {
|
||||
MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>();
|
||||
scale_faces(mesh_component, input_fields);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_SCALE_ELEMENTS_MODE_EDGE: {
|
||||
if (geometry.has_mesh()) {
|
||||
MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>();
|
||||
scale_edges(mesh_component, input_fields);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
params.set_output("Geometry", std::move(geometry));
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_scale_elements_cc
|
||||
|
||||
void register_node_type_geo_scale_elements()
|
||||
{
|
||||
namespace file_ns = blender::nodes::node_geo_scale_elements_cc;
|
||||
|
||||
static bNodeType ntype;
|
||||
|
||||
geo_node_type_base(&ntype, GEO_NODE_SCALE_ELEMENTS, "Scale Elements", NODE_CLASS_GEOMETRY);
|
||||
ntype.geometry_node_execute = file_ns::node_geo_exec;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.draw_buttons = file_ns::node_layout;
|
||||
ntype.initfunc = file_ns::node_init;
|
||||
ntype.updatefunc = file_ns::node_update;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
Reference in New Issue
Block a user