Compare commits
27 Commits
devirtuali
...
temp-geome
Author | SHA1 | Date | |
---|---|---|---|
c8698e6657 | |||
b517b9f646 | |||
379f116a2a | |||
2b343c74d5 | |||
1e468f57b1 | |||
2ee7d5282d | |||
d9c3a223cc | |||
2f9dbe9e7c | |||
45f32b43fc | |||
12bf0af064 | |||
9c393bab47 | |||
3ec74ee230 | |||
539095ca40 | |||
21ac9d9cff | |||
4aae988163 | |||
bd75d9f44c | |||
4602874a04 | |||
3a8c57afd5 | |||
5b8cd24863 | |||
91ecc53994 | |||
64fc0e34e6 | |||
83dab8bf98 | |||
4cc8f09843 | |||
3e2e8f437c | |||
7e5df11cac | |||
0d756bd4be | |||
dedf834884 |
@@ -504,6 +504,7 @@ geometry_node_categories = [
|
|||||||
NodeItem("GeometryNodeCurveSubdivide"),
|
NodeItem("GeometryNodeCurveSubdivide"),
|
||||||
NodeItem("GeometryNodeCurveToMesh"),
|
NodeItem("GeometryNodeCurveToMesh"),
|
||||||
NodeItem("GeometryNodeCurveResample"),
|
NodeItem("GeometryNodeCurveResample"),
|
||||||
|
NodeItem("GeometryNodeCurveDeform"),
|
||||||
NodeItem("GeometryNodeMeshToCurve"),
|
NodeItem("GeometryNodeMeshToCurve"),
|
||||||
NodeItem("GeometryNodeCurveToPoints"),
|
NodeItem("GeometryNodeCurveToPoints"),
|
||||||
NodeItem("GeometryNodeCurveLength"),
|
NodeItem("GeometryNodeCurveLength"),
|
||||||
|
@@ -1439,6 +1439,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
|||||||
#define GEO_NODE_SEPARATE_COMPONENTS 1059
|
#define GEO_NODE_SEPARATE_COMPONENTS 1059
|
||||||
#define GEO_NODE_CURVE_SUBDIVIDE 1060
|
#define GEO_NODE_CURVE_SUBDIVIDE 1060
|
||||||
#define GEO_NODE_RAYCAST 1061
|
#define GEO_NODE_RAYCAST 1061
|
||||||
|
#define GEO_NODE_CURVE_DEFORM 1062
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
|
@@ -55,6 +55,8 @@ using SplinePtr = std::unique_ptr<Spline>;
|
|||||||
* along the length of a curve.
|
* along the length of a curve.
|
||||||
* 3. #sample_uniform_index_factors returns an array that stores uniform-length samples
|
* 3. #sample_uniform_index_factors returns an array that stores uniform-length samples
|
||||||
* along the spline which can be used to interpolate data from method 1.
|
* along the spline which can be used to interpolate data from method 1.
|
||||||
|
* 4. #sample_length_parameters_to_index_factors does the same, but uses arbitrary parameter
|
||||||
|
* inputs, instead of sampling uniformly.
|
||||||
*
|
*
|
||||||
* Commonly used evaluated data is stored in caches on the spline itself so that operations on
|
* Commonly used evaluated data is stored in caches on the spline itself so that operations on
|
||||||
* splines don't need to worry about taking ownership of evaluated data when they don't need to.
|
* splines don't need to worry about taking ownership of evaluated data when they don't need to.
|
||||||
@@ -167,8 +169,11 @@ class Spline {
|
|||||||
};
|
};
|
||||||
LookupResult lookup_evaluated_factor(const float factor) const;
|
LookupResult lookup_evaluated_factor(const float factor) const;
|
||||||
LookupResult lookup_evaluated_length(const float length) const;
|
LookupResult lookup_evaluated_length(const float length) const;
|
||||||
|
LookupResult lookup_evaluated_length_cyclic(const float length) const;
|
||||||
|
|
||||||
blender::Array<float> sample_uniform_index_factors(const int samples_size) const;
|
blender::Array<float> sample_uniform_index_factors(const int samples_size) const;
|
||||||
|
void sample_length_parameters_to_index_factors(blender::MutableSpan<float> parameters) const;
|
||||||
|
|
||||||
LookupResult lookup_data_from_index_factor(const float index_factor) const;
|
LookupResult lookup_data_from_index_factor(const float index_factor) const;
|
||||||
|
|
||||||
void sample_with_index_factors(const blender::fn::GVArray &src,
|
void sample_with_index_factors(const blender::fn::GVArray &src,
|
||||||
|
@@ -5053,6 +5053,7 @@ static void registerGeometryNodes()
|
|||||||
register_node_type_geo_bounding_box();
|
register_node_type_geo_bounding_box();
|
||||||
register_node_type_geo_collection_info();
|
register_node_type_geo_collection_info();
|
||||||
register_node_type_geo_convex_hull();
|
register_node_type_geo_convex_hull();
|
||||||
|
register_node_type_geo_curve_deform();
|
||||||
register_node_type_geo_curve_length();
|
register_node_type_geo_curve_length();
|
||||||
register_node_type_geo_curve_to_mesh();
|
register_node_type_geo_curve_to_mesh();
|
||||||
register_node_type_geo_curve_to_points();
|
register_node_type_geo_curve_to_points();
|
||||||
|
@@ -399,21 +399,33 @@ Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const
|
|||||||
return this->lookup_evaluated_length(this->length() * factor);
|
return this->lookup_evaluated_length(this->length() * factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* \note This does not support extrapolation currently.
|
|
||||||
*/
|
|
||||||
Spline::LookupResult Spline::lookup_evaluated_length(const float length) const
|
Spline::LookupResult Spline::lookup_evaluated_length(const float length) const
|
||||||
{
|
{
|
||||||
BLI_assert(length >= 0.0f && length <= this->length());
|
const int64_t evaluated_size = this->evaluated_points_size();
|
||||||
|
|
||||||
Span<float> lengths = this->evaluated_lengths();
|
Span<float> lengths = this->evaluated_lengths();
|
||||||
|
|
||||||
|
if (is_cyclic_) {
|
||||||
|
const float *offset = std::lower_bound(
|
||||||
|
lengths.begin(), lengths.end(), std::remainder(length, this->length()));
|
||||||
|
const int index = offset - lengths.begin();
|
||||||
|
const int next_index = (index == evaluated_size - 1) ? 0 : index + 1;
|
||||||
|
|
||||||
|
const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
|
||||||
|
const float factor = (lengths[index] == previous_length) ?
|
||||||
|
0.0f :
|
||||||
|
(length - previous_length) / (lengths[index] - previous_length);
|
||||||
|
|
||||||
|
return LookupResult{index, next_index, factor};
|
||||||
|
}
|
||||||
|
|
||||||
const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length);
|
const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length);
|
||||||
const int index = offset - lengths.begin();
|
const int index = std::min(offset - lengths.begin(), evaluated_size - 2);
|
||||||
const int next_index = (index == this->size() - 1) ? 0 : index + 1;
|
const int next_index = index + 1;
|
||||||
|
|
||||||
const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
|
const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
|
||||||
const float factor = (length - previous_length) / (lengths[index] - previous_length);
|
const float factor = (lengths[index] == previous_length) ?
|
||||||
|
0.0f :
|
||||||
|
(length - previous_length) / (lengths[index] - previous_length);
|
||||||
|
|
||||||
return LookupResult{index, next_index, factor};
|
return LookupResult{index, next_index, factor};
|
||||||
}
|
}
|
||||||
@@ -463,6 +475,53 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const
|
|||||||
return samples;
|
return samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
static void assert_sorted_array_in_range(Span<float> data, const float min, const float max)
|
||||||
|
{
|
||||||
|
BLI_assert(data.first() >= min);
|
||||||
|
for (const int i : IndexRange(1, data.size() - 1)) {
|
||||||
|
BLI_assert(data[i] >= data[i - 1]);
|
||||||
|
}
|
||||||
|
BLI_assert(data.last() <= max);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform an array of sorted length parameters into index factors. The result is indices
|
||||||
|
* and factors to the next index, encoded in floats. The logic for converting from the float
|
||||||
|
* values to interpolation data is in #lookup_data_from_index_factor.
|
||||||
|
*
|
||||||
|
* \param parameters: Lengths along the spline to be transformed into index factors
|
||||||
|
* (to save another allocation). Must be between zero and the total length of the spline.
|
||||||
|
*
|
||||||
|
* \note The implementation is similar to #sample_uniform_index_factors(), though
|
||||||
|
* the two loops are inverted, and obviously custom parameters are provided.
|
||||||
|
*/
|
||||||
|
void Spline::sample_length_parameters_to_index_factors(MutableSpan<float> parameters) const
|
||||||
|
{
|
||||||
|
const Span<float> lengths = this->evaluated_lengths();
|
||||||
|
#ifdef DEBUG
|
||||||
|
assert_sorted_array_in_range(parameters, 0.0f, this->length());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Store the length at the previous evaluated point in a variable so it can
|
||||||
|
* start out at zero (the lengths array doesn't contain 0 for the first point). */
|
||||||
|
float prev_length = 0.0f;
|
||||||
|
int i_evaluated = 0;
|
||||||
|
for (const int i_sample : parameters.index_range()) {
|
||||||
|
const float sample_length = parameters[i_sample];
|
||||||
|
|
||||||
|
/* Skip over every evaluated point that fits before this sample. */
|
||||||
|
while (lengths[i_evaluated] < sample_length) {
|
||||||
|
prev_length = lengths[i_evaluated];
|
||||||
|
i_evaluated++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float factor = (sample_length - prev_length) / (lengths[i_evaluated] - prev_length);
|
||||||
|
parameters[i_sample] = i_evaluated + factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const
|
Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const
|
||||||
{
|
{
|
||||||
const int eval_size = this->evaluated_points_size();
|
const int eval_size = this->evaluated_points_size();
|
||||||
|
@@ -1362,6 +1362,11 @@ typedef struct NodeGeometryCurveResample {
|
|||||||
uint8_t mode;
|
uint8_t mode;
|
||||||
} NodeGeometryCurveResample;
|
} NodeGeometryCurveResample;
|
||||||
|
|
||||||
|
typedef struct NodeGeometryCurveDeform {
|
||||||
|
/* GeometryNodeCurveDeformAxis. */
|
||||||
|
uint8_t axis;
|
||||||
|
} NodeGeometryCurveDeform;
|
||||||
|
|
||||||
typedef struct NodeGeometryCurveSubdivide {
|
typedef struct NodeGeometryCurveSubdivide {
|
||||||
/* GeometryNodeAttributeInputMode (integer or attribute). */
|
/* GeometryNodeAttributeInputMode (integer or attribute). */
|
||||||
uint8_t cuts_type;
|
uint8_t cuts_type;
|
||||||
@@ -1900,6 +1905,15 @@ typedef enum GeometryNodeAttributeTransferMapMode {
|
|||||||
GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1,
|
GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1,
|
||||||
} GeometryNodeAttributeTransferMapMode;
|
} GeometryNodeAttributeTransferMapMode;
|
||||||
|
|
||||||
|
typedef enum GeometryNodeCurveDeformAxis {
|
||||||
|
GEO_NODE_CURVE_DEFORM_POSX = 0,
|
||||||
|
GEO_NODE_CURVE_DEFORM_POSY = 1,
|
||||||
|
GEO_NODE_CURVE_DEFORM_POSZ = 2,
|
||||||
|
GEO_NODE_CURVE_DEFORM_NEGX = 3,
|
||||||
|
GEO_NODE_CURVE_DEFORM_NEGY = 4,
|
||||||
|
GEO_NODE_CURVE_DEFORM_NEGZ = 5,
|
||||||
|
} GeometryNodeCurveDeformAxis;
|
||||||
|
|
||||||
typedef enum GeometryNodeRaycastMapMode {
|
typedef enum GeometryNodeRaycastMapMode {
|
||||||
GEO_NODE_RAYCAST_INTERPOLATED = 0,
|
GEO_NODE_RAYCAST_INTERPOLATED = 0,
|
||||||
GEO_NODE_RAYCAST_NEAREST = 1,
|
GEO_NODE_RAYCAST_NEAREST = 1,
|
||||||
|
@@ -9859,6 +9859,28 @@ static void def_geo_curve_resample(StructRNA *srna)
|
|||||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void def_geo_curve_deform(StructRNA *srna)
|
||||||
|
{
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
static const EnumPropertyItem axis_items[] = {
|
||||||
|
{GEO_NODE_CURVE_DEFORM_POSX, "POS_X", 0, "X", ""},
|
||||||
|
{GEO_NODE_CURVE_DEFORM_POSY, "POS_Y", 0, "Y", ""},
|
||||||
|
{GEO_NODE_CURVE_DEFORM_POSZ, "POS_Z", 0, "Z", ""},
|
||||||
|
{GEO_NODE_CURVE_DEFORM_NEGX, "NEG_X", 0, "-X", ""},
|
||||||
|
{GEO_NODE_CURVE_DEFORM_NEGY, "NEG_Y", 0, "-Y", ""},
|
||||||
|
{GEO_NODE_CURVE_DEFORM_NEGZ, "NEG_Z", 0, "-Z", ""},
|
||||||
|
{0, NULL, 0, NULL, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
RNA_def_struct_sdna_from(srna, "NodeGeometryCurveDeform", "storage");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "axis", PROP_ENUM, PROP_NONE);
|
||||||
|
RNA_def_property_enum_items(prop, axis_items);
|
||||||
|
RNA_def_property_ui_text(prop, "Position Axis", "");
|
||||||
|
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||||
|
}
|
||||||
|
|
||||||
static void def_geo_curve_subdivide(StructRNA *srna)
|
static void def_geo_curve_subdivide(StructRNA *srna)
|
||||||
{
|
{
|
||||||
PropertyRNA *prop;
|
PropertyRNA *prop;
|
||||||
|
@@ -163,6 +163,7 @@ set(SRC
|
|||||||
geometry/nodes/node_geo_collection_info.cc
|
geometry/nodes/node_geo_collection_info.cc
|
||||||
geometry/nodes/node_geo_common.cc
|
geometry/nodes/node_geo_common.cc
|
||||||
geometry/nodes/node_geo_convex_hull.cc
|
geometry/nodes/node_geo_convex_hull.cc
|
||||||
|
geometry/nodes/node_geo_curve_deform.cc
|
||||||
geometry/nodes/node_geo_curve_length.cc
|
geometry/nodes/node_geo_curve_length.cc
|
||||||
geometry/nodes/node_geo_curve_to_mesh.cc
|
geometry/nodes/node_geo_curve_to_mesh.cc
|
||||||
geometry/nodes/node_geo_curve_to_points.cc
|
geometry/nodes/node_geo_curve_to_points.cc
|
||||||
|
@@ -51,6 +51,7 @@ void register_node_type_geo_boolean(void);
|
|||||||
void register_node_type_geo_bounding_box(void);
|
void register_node_type_geo_bounding_box(void);
|
||||||
void register_node_type_geo_collection_info(void);
|
void register_node_type_geo_collection_info(void);
|
||||||
void register_node_type_geo_convex_hull(void);
|
void register_node_type_geo_convex_hull(void);
|
||||||
|
void register_node_type_geo_curve_deform(void);
|
||||||
void register_node_type_geo_curve_length(void);
|
void register_node_type_geo_curve_length(void);
|
||||||
void register_node_type_geo_curve_to_mesh(void);
|
void register_node_type_geo_curve_to_mesh(void);
|
||||||
void register_node_type_geo_curve_to_points(void);
|
void register_node_type_geo_curve_to_points(void);
|
||||||
|
@@ -290,6 +290,7 @@ DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Bo
|
|||||||
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
|
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
|
||||||
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
|
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
|
||||||
DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "")
|
DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "")
|
||||||
|
DefNode(GeometryNode, GEO_NODE_CURVE_DEFORM, def_geo_curve_deform, "CURVE_DEFORM", CurveDeform, "Curve Deform", "")
|
||||||
DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
|
DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
|
||||||
DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "")
|
DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "")
|
||||||
DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "")
|
DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "")
|
||||||
|
320
source/blender/nodes/geometry/nodes/node_geo_curve_deform.cc
Normal file
320
source/blender/nodes/geometry/nodes/node_geo_curve_deform.cc
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
/*
|
||||||
|
* 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_float4x4.hh"
|
||||||
|
#include "BLI_resource_scope.hh"
|
||||||
|
#include "BLI_task.hh"
|
||||||
|
#include "BLI_timeit.hh"
|
||||||
|
|
||||||
|
#include "BKE_attribute_math.hh"
|
||||||
|
#include "BKE_geometry_set_instances.hh"
|
||||||
|
#include "BKE_spline.hh"
|
||||||
|
|
||||||
|
#include "UI_interface.h"
|
||||||
|
#include "UI_resources.h"
|
||||||
|
|
||||||
|
#include "node_geometry_util.hh"
|
||||||
|
|
||||||
|
using blender::float4x4;
|
||||||
|
using blender::bke::GeometryInstanceGroup;
|
||||||
|
|
||||||
|
static bNodeSocketTemplate geo_node_curve_deform_in[] = {
|
||||||
|
{SOCK_GEOMETRY, N_("Geometry")},
|
||||||
|
{SOCK_GEOMETRY, N_("Curve")},
|
||||||
|
{SOCK_BOOLEAN, N_("Use Bounds")},
|
||||||
|
{SOCK_BOOLEAN, N_("Stretch")},
|
||||||
|
{SOCK_BOOLEAN, N_("Temp Only Bounds")},
|
||||||
|
{SOCK_VECTOR, N_("Weird Hack"), 0.5f, 0.5f, 0.5f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
|
||||||
|
{-1, ""},
|
||||||
|
};
|
||||||
|
|
||||||
|
static bNodeSocketTemplate geo_node_curve_deform_out[] = {
|
||||||
|
{SOCK_GEOMETRY, N_("Geometry")},
|
||||||
|
{-1, ""},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void geo_node_curve_deform_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||||
|
{
|
||||||
|
uiItemR(layout, ptr, "axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void geo_node_curve_deform_init(bNodeTree *UNUSED(tree), bNode *node)
|
||||||
|
{
|
||||||
|
NodeGeometryCurveDeform *data = (NodeGeometryCurveDeform *)MEM_callocN(
|
||||||
|
sizeof(NodeGeometryCurveDeform), __func__);
|
||||||
|
|
||||||
|
data->axis = GEO_NODE_CURVE_DEFORM_POSX;
|
||||||
|
node->storage = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace blender::nodes {
|
||||||
|
|
||||||
|
static constexpr int deform_axis_index(const GeometryNodeCurveDeformAxis axis)
|
||||||
|
{
|
||||||
|
switch (axis) {
|
||||||
|
case GEO_NODE_CURVE_DEFORM_POSX:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_NEGX:
|
||||||
|
return 0;
|
||||||
|
case GEO_NODE_CURVE_DEFORM_POSY:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_NEGY:
|
||||||
|
return 1;
|
||||||
|
case GEO_NODE_CURVE_DEFORM_POSZ:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_NEGZ:
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
BLI_assert_unreachable();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr int deform_next_axis_index(const GeometryNodeCurveDeformAxis axis)
|
||||||
|
{
|
||||||
|
switch (axis) {
|
||||||
|
case GEO_NODE_CURVE_DEFORM_POSX:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_NEGX:
|
||||||
|
return 1;
|
||||||
|
case GEO_NODE_CURVE_DEFORM_POSY:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_NEGY:
|
||||||
|
return 2;
|
||||||
|
case GEO_NODE_CURVE_DEFORM_POSZ:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_NEGZ:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
BLI_assert_unreachable();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr int deform_other_axis_index(const GeometryNodeCurveDeformAxis axis)
|
||||||
|
{
|
||||||
|
switch (axis) {
|
||||||
|
case GEO_NODE_CURVE_DEFORM_POSX:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_NEGX:
|
||||||
|
return 2;
|
||||||
|
case GEO_NODE_CURVE_DEFORM_POSY:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_NEGY:
|
||||||
|
return 0;
|
||||||
|
case GEO_NODE_CURVE_DEFORM_POSZ:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_NEGZ:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
BLI_assert_unreachable();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr bool axis_is_negative(const GeometryNodeCurveDeformAxis axis)
|
||||||
|
{
|
||||||
|
switch (axis) {
|
||||||
|
case GEO_NODE_CURVE_DEFORM_POSX:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_POSY:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_POSZ:
|
||||||
|
return false;
|
||||||
|
case GEO_NODE_CURVE_DEFORM_NEGX:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_NEGY:
|
||||||
|
case GEO_NODE_CURVE_DEFORM_NEGZ:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
BLI_assert_unreachable();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SplineDeformInput {
|
||||||
|
const Spline &spline;
|
||||||
|
Span<float3> positions;
|
||||||
|
Span<float3> tangents;
|
||||||
|
Span<float3> normals;
|
||||||
|
GVArray_Typed<float> radii;
|
||||||
|
float total_length;
|
||||||
|
bool use_stretch;
|
||||||
|
bool use_bounds;
|
||||||
|
};
|
||||||
|
|
||||||
|
static float3 deform_position(const SplineDeformInput &in,
|
||||||
|
const Spline::LookupResult &lookup,
|
||||||
|
const float cotangent_factor,
|
||||||
|
const float normal_factor,
|
||||||
|
const bool is_negative)
|
||||||
|
{
|
||||||
|
const int index = lookup.evaluated_index;
|
||||||
|
const int next = lookup.next_evaluated_index;
|
||||||
|
const float factor = lookup.factor;
|
||||||
|
const float clamped = std::clamp(lookup.factor, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
const float3 position = float3::interpolate(in.positions[index], in.positions[next], factor);
|
||||||
|
const float3 tangent = float3::interpolate(in.tangents[index], in.tangents[next], clamped);
|
||||||
|
const float3 normal = float3::interpolate(in.normals[index], in.normals[next], clamped);
|
||||||
|
const float3 cotangent = float3::cross(tangent, normal).normalized();
|
||||||
|
const float radius = interpf(in.radii[next], in.radii[index], clamped);
|
||||||
|
|
||||||
|
if (is_negative) {
|
||||||
|
return position + (cotangent * cotangent_factor + normal * normal_factor) * radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
return position - (cotangent * cotangent_factor + normal * normal_factor) * radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bounds {
|
||||||
|
float3 min;
|
||||||
|
float3 max;
|
||||||
|
float3 size;
|
||||||
|
float3 inv_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static Bounds position_bounds(const Span<float3> positions)
|
||||||
|
{
|
||||||
|
float3 min = float3(FLT_MAX);
|
||||||
|
float3 max = float3(-FLT_MAX);
|
||||||
|
for (const float3 &position : positions) {
|
||||||
|
minmax_v3v3_v3(min, max, position);
|
||||||
|
}
|
||||||
|
const float3 size = max - min;
|
||||||
|
return {min, max, size, float3::safe_divide(float3(1), size)};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bounds dummy_parameter_bounds(const GeometryNodeCurveDeformAxis deform_axis)
|
||||||
|
{
|
||||||
|
if (axis_is_negative(deform_axis)) {
|
||||||
|
return {float3(-1), float3(0), float3(-1), float3(-1)};
|
||||||
|
}
|
||||||
|
return {float3(0), float3(1), float3(1), float3(1)};
|
||||||
|
}
|
||||||
|
|
||||||
|
static float process_parameter(const float3 position,
|
||||||
|
const int axis_index,
|
||||||
|
const bool is_negative,
|
||||||
|
const SplineDeformInput &input,
|
||||||
|
const Bounds &bounds)
|
||||||
|
{
|
||||||
|
const float parameter = is_negative ? -(position[axis_index] - bounds.max[axis_index]) :
|
||||||
|
position[axis_index] - bounds.min[axis_index];
|
||||||
|
if (input.use_stretch) {
|
||||||
|
return parameter * bounds.inv_size[axis_index] * input.total_length;
|
||||||
|
}
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void execute_on_component(const GeoNodeExecParams ¶ms,
|
||||||
|
const SplineDeformInput &input,
|
||||||
|
GeometryComponent &component)
|
||||||
|
{
|
||||||
|
const NodeGeometryCurveDeform &node_storage = *(NodeGeometryCurveDeform *)params.node().storage;
|
||||||
|
const GeometryNodeCurveDeformAxis deform_axis = (GeometryNodeCurveDeformAxis)node_storage.axis;
|
||||||
|
const int axis_index = deform_axis_index(deform_axis);
|
||||||
|
const int next_axis = deform_next_axis_index(deform_axis);
|
||||||
|
const int other_axis = deform_other_axis_index(deform_axis);
|
||||||
|
const bool is_negative = axis_is_negative(deform_axis);
|
||||||
|
|
||||||
|
OutputAttribute_Typed<float3> position_attribute =
|
||||||
|
component.attribute_try_get_for_output<float3>("position", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||||
|
MutableSpan<float3> positions = position_attribute.as_span();
|
||||||
|
const Bounds bounds = position_bounds(positions);
|
||||||
|
const Bounds parameter_bounds = input.use_bounds ? bounds : dummy_parameter_bounds(deform_axis);
|
||||||
|
|
||||||
|
float3 center_offset(0);
|
||||||
|
if (bounds.size[next_axis] != 0.0f) {
|
||||||
|
center_offset[next_axis] = 0.5f;
|
||||||
|
}
|
||||||
|
float3 input_center_offset = params.get_input<float3>("Weird Hack");
|
||||||
|
if (!input_center_offset.is_zero()) {
|
||||||
|
center_offset = input_center_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
threading::parallel_for(positions.index_range(), 1024, [&](IndexRange range) {
|
||||||
|
for (const int i : range) {
|
||||||
|
const float parameter = process_parameter(
|
||||||
|
positions[i], axis_index, is_negative, input, parameter_bounds);
|
||||||
|
|
||||||
|
const Spline::LookupResult lookup = input.spline.lookup_evaluated_length(parameter);
|
||||||
|
|
||||||
|
const float3 co = ((positions[i] - bounds.min) * bounds.inv_size - center_offset) *
|
||||||
|
bounds.size;
|
||||||
|
if (is_negative) {
|
||||||
|
positions[i] = deform_position(input, lookup, co[next_axis], co[other_axis], is_negative);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
positions[i] = deform_position(input, lookup, co[other_axis], co[next_axis], is_negative);
|
||||||
|
}
|
||||||
|
if (params.get_input<bool>("Temp Only Bounds")) {
|
||||||
|
positions[i] = co;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
position_attribute.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void geo_node_curve_deform_exec(GeoNodeExecParams params)
|
||||||
|
{
|
||||||
|
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||||
|
GeometrySet curve_geometry_set = params.extract_input<GeometrySet>("Curve");
|
||||||
|
|
||||||
|
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||||
|
|
||||||
|
/* TODO: Theoretically this could be easily avoided. */
|
||||||
|
curve_geometry_set = bke::geometry_set_realize_instances(curve_geometry_set);
|
||||||
|
|
||||||
|
const CurveEval *curve = curve_geometry_set.get_curve_for_read();
|
||||||
|
if (curve == nullptr || curve->splines().size() == 0) {
|
||||||
|
params.set_output("Geometry", geometry_set);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Spline &spline = *curve->splines().first();
|
||||||
|
const float total_length = spline.length();
|
||||||
|
if (total_length == 0.0f) {
|
||||||
|
params.set_output("Geometry", geometry_set);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SplineDeformInput spline_data{spline,
|
||||||
|
spline.evaluated_positions(),
|
||||||
|
spline.evaluated_tangents(),
|
||||||
|
spline.evaluated_normals(),
|
||||||
|
spline.interpolate_to_evaluated(spline.radii()),
|
||||||
|
total_length,
|
||||||
|
params.extract_input<bool>("Stretch"),
|
||||||
|
params.extract_input<bool>("Use Bounds")};
|
||||||
|
|
||||||
|
if (geometry_set.has<MeshComponent>()) {
|
||||||
|
execute_on_component(
|
||||||
|
params, spline_data, geometry_set.get_component_for_write<MeshComponent>());
|
||||||
|
}
|
||||||
|
if (geometry_set.has<PointCloudComponent>()) {
|
||||||
|
execute_on_component(
|
||||||
|
params, spline_data, geometry_set.get_component_for_write<PointCloudComponent>());
|
||||||
|
}
|
||||||
|
if (geometry_set.has<CurveComponent>()) {
|
||||||
|
execute_on_component(
|
||||||
|
params, spline_data, geometry_set.get_component_for_write<CurveComponent>());
|
||||||
|
}
|
||||||
|
|
||||||
|
params.set_output("Geometry", geometry_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace blender::nodes
|
||||||
|
|
||||||
|
void register_node_type_geo_curve_deform()
|
||||||
|
{
|
||||||
|
static bNodeType ntype;
|
||||||
|
|
||||||
|
geo_node_type_base(&ntype, GEO_NODE_CURVE_DEFORM, "Curve Deform", NODE_CLASS_GEOMETRY, 0);
|
||||||
|
node_type_socket_templates(&ntype, geo_node_curve_deform_in, geo_node_curve_deform_out);
|
||||||
|
ntype.draw_buttons = geo_node_curve_deform_layout;
|
||||||
|
node_type_storage(
|
||||||
|
&ntype, "NodeGeometryCurveDeform", node_free_standard_storage, node_copy_standard_storage);
|
||||||
|
node_type_init(&ntype, geo_node_curve_deform_init);
|
||||||
|
ntype.geometry_node_execute = blender::nodes::geo_node_curve_deform_exec;
|
||||||
|
nodeRegisterType(&ntype);
|
||||||
|
}
|
Reference in New Issue
Block a user