Compare commits
11 Commits
geometry-n
...
temp-geome
Author | SHA1 | Date | |
---|---|---|---|
2d190d0480 | |||
51f6fa351d | |||
db384d4df6 | |||
20ddb64207 | |||
cc39200bfc | |||
36ea32a3f0 | |||
dc0c71d3ae | |||
d7a74a2ad6 | |||
4580340321 | |||
a101f6d974 | |||
3efc50dfde |
@@ -504,6 +504,7 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeCurveSubdivide"),
|
||||
NodeItem("GeometryNodeCurveToMesh"),
|
||||
NodeItem("GeometryNodeCurveResample"),
|
||||
NodeItem("GeometryNodeCurveSample"),
|
||||
NodeItem("GeometryNodeMeshToCurve"),
|
||||
NodeItem("GeometryNodeCurveToPoints"),
|
||||
NodeItem("GeometryNodeCurveEndpoints"),
|
||||
|
@@ -1465,6 +1465,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
||||
#define GEO_NODE_CURVE_ENDPOINTS 1069
|
||||
#define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070
|
||||
#define GEO_NODE_CURVE_TRIM 1071
|
||||
#define GEO_NODE_CURVE_SAMPLE 1072
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -56,6 +56,8 @@ using SplinePtr = std::unique_ptr<Spline>;
|
||||
* along the length of a curve.
|
||||
* 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.
|
||||
* 4. #sample_lengths_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
|
||||
* splines don't need to worry about taking ownership of evaluated data when they don't need to.
|
||||
@@ -170,11 +172,16 @@ class Spline {
|
||||
LookupResult lookup_evaluated_length(const float length) const;
|
||||
|
||||
blender::Array<float> sample_uniform_index_factors(const int samples_size) const;
|
||||
blender::Array<float> sample_lengths_to_index_factors(blender::Span<float> parameters) const;
|
||||
|
||||
LookupResult lookup_data_from_index_factor(const float index_factor) const;
|
||||
|
||||
void sample_with_index_factors(const blender::fn::GVArray &src,
|
||||
blender::Span<float> index_factors,
|
||||
blender::fn::GMutableSpan dst) const;
|
||||
void sample_with_index_factors(blender::fn::GSpan src,
|
||||
blender::Span<float> index_factors,
|
||||
blender::fn::GMutableSpan dst) const;
|
||||
template<typename T>
|
||||
void sample_with_index_factors(const blender::VArray<T> &src,
|
||||
blender::Span<float> index_factors,
|
||||
|
@@ -5115,6 +5115,7 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_curve_primitive_quadrilateral();
|
||||
register_node_type_geo_curve_primitive_spiral();
|
||||
register_node_type_geo_curve_primitive_star();
|
||||
register_node_type_geo_curve_sample();
|
||||
register_node_type_geo_curve_resample();
|
||||
register_node_type_geo_curve_reverse();
|
||||
register_node_type_geo_curve_subdivide();
|
||||
|
@@ -463,6 +463,56 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const
|
||||
return samples;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* \note This could have a result data argument instead of returning a span in the future.
|
||||
*
|
||||
* \todo Investigate running the single threaded loop over evaluated points in chunks.
|
||||
*/
|
||||
Array<float> Spline::sample_lengths_to_index_factors(const Span<float> parameters) const
|
||||
{
|
||||
Array<int> original_indices(parameters.size());
|
||||
for (const int i : original_indices.index_range()) {
|
||||
original_indices[i] = i;
|
||||
}
|
||||
|
||||
std::sort(original_indices.begin(), original_indices.end(), [parameters](int a, int b) {
|
||||
return parameters[a] > parameters[b];
|
||||
});
|
||||
|
||||
const Span<float> lengths = this->evaluated_lengths();
|
||||
const float total_length = this->length();
|
||||
Array<float> index_factors(parameters.size());
|
||||
|
||||
/* 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 = std::clamp(parameters[i_sample], 0.0f, total_length);
|
||||
|
||||
/* 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);
|
||||
index_factors[original_indices[i_sample]] = i_evaluated + factor;
|
||||
}
|
||||
|
||||
return index_factors;
|
||||
}
|
||||
|
||||
Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const
|
||||
{
|
||||
const int eval_size = this->evaluated_points_size();
|
||||
@@ -497,6 +547,13 @@ GVArrayPtr Spline::interpolate_to_evaluated(GSpan data) const
|
||||
return this->interpolate_to_evaluated(GVArray_For_GSpan(data));
|
||||
}
|
||||
|
||||
void Spline::sample_with_index_factors(GSpan src,
|
||||
Span<float> index_factors,
|
||||
GMutableSpan dst) const
|
||||
{
|
||||
return this->sample_with_index_factors(GVArray_For_GSpan(src), index_factors, dst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample any input data with a value for each evaluated point (already interpolated to evaluated
|
||||
* points) to arbitrary parameters in between the evaluated points. The interpolation is quite
|
||||
|
@@ -1376,10 +1376,15 @@ typedef struct NodeGeometryCurvePrimitiveQuad {
|
||||
} NodeGeometryCurvePrimitiveQuad;
|
||||
|
||||
typedef struct NodeGeometryCurveResample {
|
||||
/* GeometryNodeCurveSampleMode. */
|
||||
/* GeometryNodeCurveResampleMode. */
|
||||
uint8_t mode;
|
||||
} NodeGeometryCurveResample;
|
||||
|
||||
typedef struct NodeGeometryCurveSample {
|
||||
/* GeometryNodeCurveInterpolateMode. */
|
||||
uint8_t mode;
|
||||
} NodeGeometryCurveSample;
|
||||
|
||||
typedef struct NodeGeometryCurveSubdivide {
|
||||
/* GeometryNodeAttributeInputMode (integer or attribute). */
|
||||
uint8_t cuts_type;
|
||||
@@ -1391,7 +1396,7 @@ typedef struct NodeGeometryCurveTrim {
|
||||
} NodeGeometryCurveTrim;
|
||||
|
||||
typedef struct NodeGeometryCurveToPoints {
|
||||
/* GeometryNodeCurveSampleMode. */
|
||||
/* GeometryNodeCurveResampleMode. */
|
||||
uint8_t mode;
|
||||
} NodeGeometryCurveToPoints;
|
||||
|
||||
@@ -1943,11 +1948,11 @@ typedef enum GeometryNodeCurvePrimitiveBezierSegmentMode {
|
||||
GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_OFFSET = 1,
|
||||
} GeometryNodeCurvePrimitiveBezierSegmentMode;
|
||||
|
||||
typedef enum GeometryNodeCurveSampleMode {
|
||||
GEO_NODE_CURVE_SAMPLE_COUNT = 0,
|
||||
GEO_NODE_CURVE_SAMPLE_LENGTH = 1,
|
||||
GEO_NODE_CURVE_SAMPLE_EVALUATED = 2,
|
||||
} GeometryNodeCurveSampleMode;
|
||||
typedef enum GeometryNodeCurveResampleMode {
|
||||
GEO_NODE_CURVE_RESAMPLE_COUNT = 0,
|
||||
GEO_NODE_CURVE_RESAMPLE_LENGTH = 1,
|
||||
GEO_NODE_CURVE_RESAMPLE_EVALUATED = 2,
|
||||
} GeometryNodeCurveResampleMode;
|
||||
|
||||
typedef enum GeometryNodeCurveInterpolateMode {
|
||||
GEO_NODE_CURVE_INTERPOLATE_FACTOR = 0,
|
||||
|
@@ -9973,12 +9973,12 @@ static void def_geo_curve_resample(StructRNA *srna)
|
||||
PropertyRNA *prop;
|
||||
|
||||
static EnumPropertyItem mode_items[] = {
|
||||
{GEO_NODE_CURVE_SAMPLE_COUNT,
|
||||
{GEO_NODE_CURVE_RESAMPLE_COUNT,
|
||||
"COUNT",
|
||||
0,
|
||||
"Count",
|
||||
"Sample the specified number of points along each spline"},
|
||||
{GEO_NODE_CURVE_SAMPLE_LENGTH,
|
||||
{GEO_NODE_CURVE_RESAMPLE_LENGTH,
|
||||
"LENGTH",
|
||||
0,
|
||||
"Length",
|
||||
@@ -9995,6 +9995,32 @@ static void def_geo_curve_resample(StructRNA *srna)
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_curve_sample(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
static EnumPropertyItem mode_items[] = {
|
||||
{GEO_NODE_CURVE_INTERPOLATE_FACTOR,
|
||||
"FACTOR",
|
||||
0,
|
||||
"Factor",
|
||||
"Choose sample points on the curve based on the portion of the total length"},
|
||||
{GEO_NODE_CURVE_INTERPOLATE_LENGTH,
|
||||
"LENGTH",
|
||||
0,
|
||||
"Length",
|
||||
"Choose sample points on the curve based on the accumulated length at that point"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeGeometryCurveResample", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, mode_items);
|
||||
RNA_def_property_ui_text(prop, "Mode", "How to specify the sample positions on the curve");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_curve_subdivide(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
@@ -10012,18 +10038,18 @@ static void def_geo_curve_to_points(StructRNA *srna)
|
||||
PropertyRNA *prop;
|
||||
|
||||
static EnumPropertyItem mode_items[] = {
|
||||
{GEO_NODE_CURVE_SAMPLE_EVALUATED,
|
||||
{GEO_NODE_CURVE_RESAMPLE_EVALUATED,
|
||||
"EVALUATED",
|
||||
0,
|
||||
"Evaluated",
|
||||
"Create points from the curve's evaluated points, based on the resolution attribute for "
|
||||
"NURBS and Bezier splines"},
|
||||
{GEO_NODE_CURVE_SAMPLE_COUNT,
|
||||
{GEO_NODE_CURVE_RESAMPLE_COUNT,
|
||||
"COUNT",
|
||||
0,
|
||||
"Count",
|
||||
"Sample each spline by evenly distributing the specified number of points"},
|
||||
{GEO_NODE_CURVE_SAMPLE_LENGTH,
|
||||
{GEO_NODE_CURVE_RESAMPLE_LENGTH,
|
||||
"LENGTH",
|
||||
0,
|
||||
"Length",
|
||||
|
@@ -173,6 +173,7 @@ set(SRC
|
||||
geometry/nodes/node_geo_curve_primitive_quadrilateral.cc
|
||||
geometry/nodes/node_geo_curve_primitive_spiral.cc
|
||||
geometry/nodes/node_geo_curve_primitive_star.cc
|
||||
geometry/nodes/node_geo_curve_sample.cc
|
||||
geometry/nodes/node_geo_curve_resample.cc
|
||||
geometry/nodes/node_geo_curve_reverse.cc
|
||||
geometry/nodes/node_geo_curve_subdivide.cc
|
||||
|
@@ -60,6 +60,7 @@ void register_node_type_geo_curve_primitive_quadratic_bezier(void);
|
||||
void register_node_type_geo_curve_primitive_quadrilateral(void);
|
||||
void register_node_type_geo_curve_primitive_spiral(void);
|
||||
void register_node_type_geo_curve_primitive_star(void);
|
||||
void register_node_type_geo_curve_sample(void);
|
||||
void register_node_type_geo_curve_resample(void);
|
||||
void register_node_type_geo_curve_reverse(void);
|
||||
void register_node_type_geo_curve_subdivide(void);
|
||||
|
@@ -300,6 +300,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMI
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "")
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "")
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "")
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "")
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "")
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "")
|
||||
|
@@ -52,26 +52,26 @@ static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node)
|
||||
NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN(
|
||||
sizeof(NodeGeometryCurveResample), __func__);
|
||||
|
||||
data->mode = GEO_NODE_CURVE_SAMPLE_COUNT;
|
||||
data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT;
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage;
|
||||
const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
|
||||
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
|
||||
|
||||
bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next;
|
||||
bNodeSocket *length_socket = count_socket->next;
|
||||
|
||||
nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT);
|
||||
nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
|
||||
nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT);
|
||||
nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH);
|
||||
}
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
struct SampleModeParam {
|
||||
GeometryNodeCurveSampleMode mode;
|
||||
GeometryNodeCurveResampleMode mode;
|
||||
std::optional<float> length;
|
||||
std::optional<int> count;
|
||||
};
|
||||
@@ -161,7 +161,7 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
|
||||
output_curve->resize(input_splines.size());
|
||||
MutableSpan<SplinePtr> output_splines = output_curve->splines();
|
||||
|
||||
if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
|
||||
if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) {
|
||||
threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
BLI_assert(mode_param.count);
|
||||
@@ -169,7 +169,7 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
|
||||
else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) {
|
||||
threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
const float length = input_splines[i]->length();
|
||||
@@ -197,10 +197,10 @@ static void geo_node_resample_exec(GeoNodeExecParams params)
|
||||
|
||||
const CurveEval &input_curve = *geometry_set.get_curve_for_read();
|
||||
NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage;
|
||||
const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
|
||||
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
|
||||
SampleModeParam mode_param;
|
||||
mode_param.mode = mode;
|
||||
if (mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
|
||||
if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) {
|
||||
const int count = params.extract_input<int>("Count");
|
||||
if (count < 1) {
|
||||
params.set_output("Geometry", GeometrySet());
|
||||
@@ -208,7 +208,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params)
|
||||
}
|
||||
mode_param.count.emplace(count);
|
||||
}
|
||||
else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
|
||||
else if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) {
|
||||
/* Don't allow asymptotic count increase for low resolution values. */
|
||||
const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f);
|
||||
mode_param.length.emplace(resolution);
|
||||
|
272
source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
Normal file
272
source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* 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_task.hh"
|
||||
#include "BLI_timeit.hh"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
using blender::fn::GVArray_For_GSpan;
|
||||
using blender::fn::GVArray_For_Span;
|
||||
using blender::fn::GVArray_Typed;
|
||||
|
||||
static bNodeSocketTemplate geo_node_curve_sample_in[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_STRING, N_("Parameter")},
|
||||
{SOCK_GEOMETRY, N_("Curve")},
|
||||
{SOCK_STRING, N_("Position")},
|
||||
{SOCK_STRING, N_("Tangent")},
|
||||
{SOCK_STRING, N_("Normal")},
|
||||
{SOCK_STRING, N_("Attribute")},
|
||||
{SOCK_STRING, N_("Result")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate geo_node_curve_sample_out[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||
{
|
||||
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
|
||||
}
|
||||
|
||||
static void geo_node_curve_sample_init(bNodeTree *UNUSED(tree), bNode *node)
|
||||
{
|
||||
NodeGeometryCurveSample *data = (NodeGeometryCurveSample *)MEM_callocN(
|
||||
sizeof(NodeGeometryCurveSample), __func__);
|
||||
|
||||
data->mode = GEO_NODE_CURVE_INTERPOLATE_FACTOR;
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
static void geo_node_curve_sample_update(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
const NodeGeometryCurveSample &node_storage = *(NodeGeometryCurveSample *)node->storage;
|
||||
const GeometryNodeCurveInterpolateMode mode = (GeometryNodeCurveInterpolateMode)
|
||||
node_storage.mode;
|
||||
|
||||
bNodeSocket *parameter_socket = ((bNodeSocket *)node->inputs.first)->next;
|
||||
|
||||
if (mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR) {
|
||||
node_sock_label(parameter_socket, "Factor");
|
||||
}
|
||||
else {
|
||||
BLI_assert(mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH);
|
||||
node_sock_label(parameter_socket, "Length");
|
||||
}
|
||||
}
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
const StringRef parameter_name,
|
||||
const StringRef result_name)
|
||||
{
|
||||
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
|
||||
if (result_info) {
|
||||
return result_info->domain;
|
||||
}
|
||||
std::optional<AttributeMetaData> parameter_info = component.attribute_get_meta_data(
|
||||
parameter_name);
|
||||
if (parameter_info) {
|
||||
return parameter_info->domain;
|
||||
}
|
||||
return ATTR_DOMAIN_POINT;
|
||||
}
|
||||
|
||||
static CustomDataType get_result_type(const CurveComponent &curve_component,
|
||||
const StringRef attribute_name)
|
||||
{
|
||||
std::optional<AttributeMetaData> curve_meta_data = curve_component.attribute_get_meta_data(
|
||||
attribute_name);
|
||||
return curve_meta_data->data_type;
|
||||
}
|
||||
|
||||
struct SamplePair {
|
||||
GSpan src;
|
||||
GMutableSpan dst;
|
||||
};
|
||||
|
||||
static void sample_with_lookups(const SamplePair &sample, Span<Spline::LookupResult> lookups)
|
||||
{
|
||||
blender::attribute_math::convert_to_static_type(sample.src.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
Span<T> src = sample.src.typed<T>();
|
||||
MutableSpan<T> dst = sample.dst.typed<T>();
|
||||
|
||||
threading::parallel_for(lookups.index_range(), 1024, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
dst[i] = attribute_math::mix2<T>(lookups[i].factor,
|
||||
src[lookups[i].evaluated_index],
|
||||
src[lookups[i].next_evaluated_index]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static void execute_on_component(GeometryComponent &component,
|
||||
const CurveComponent &curve_component,
|
||||
const GeometryNodeCurveInterpolateMode mode,
|
||||
const StringRef pararameter_name,
|
||||
const StringRef position_name,
|
||||
const StringRef tangent_name,
|
||||
const StringRef normal_name,
|
||||
const StringRef attribute_name,
|
||||
const StringRef result_name)
|
||||
{
|
||||
const CurveEval &curve = *curve_component.get_for_read();
|
||||
const Spline &spline = *curve.splines().first();
|
||||
const float length = spline.length();
|
||||
|
||||
const AttributeDomain domain = get_result_domain(component, pararameter_name, result_name);
|
||||
|
||||
GVArray_Typed<float> parameters = component.attribute_get_for_read<float>(
|
||||
pararameter_name, domain, 0.0f);
|
||||
|
||||
Array<Spline::LookupResult> lookups(parameters.size());
|
||||
if (mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH) {
|
||||
threading::parallel_for(lookups.index_range(), 1024, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
lookups[i] = spline.lookup_evaluated_length(std::clamp(parameters[i], 0.0f, length));
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
threading::parallel_for(lookups.index_range(), 1024, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
lookups[i] = spline.lookup_evaluated_factor(std::clamp(parameters[i], 0.0f, 1.0f));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!position_name.is_empty()) {
|
||||
OutputAttribute positions = component.attribute_try_get_for_output_only(
|
||||
position_name, domain, CD_PROP_FLOAT3);
|
||||
if (positions) {
|
||||
sample_with_lookups({spline.evaluated_positions(), positions.as_span()}, lookups);
|
||||
positions.save();
|
||||
}
|
||||
}
|
||||
if (!tangent_name.is_empty()) {
|
||||
OutputAttribute tangents = component.attribute_try_get_for_output_only(
|
||||
tangent_name, domain, CD_PROP_FLOAT3);
|
||||
if (tangents) {
|
||||
sample_with_lookups({spline.evaluated_tangents(), tangents.as_span()}, lookups);
|
||||
tangents.save();
|
||||
}
|
||||
}
|
||||
if (!normal_name.is_empty()) {
|
||||
OutputAttribute normals = component.attribute_try_get_for_output_only(
|
||||
normal_name, domain, CD_PROP_FLOAT3);
|
||||
if (normals) {
|
||||
sample_with_lookups({spline.evaluated_normals(), normals.as_span()}, lookups);
|
||||
normals.save();
|
||||
}
|
||||
}
|
||||
if (!attribute_name.is_empty() && !result_name.is_empty()) {
|
||||
OutputAttribute dst = component.attribute_try_get_for_output_only(
|
||||
result_name, domain, get_result_type(curve_component, attribute_name));
|
||||
if (dst) {
|
||||
std::optional<GSpan> src = spline.attributes.get_for_read(attribute_name);
|
||||
if (src) {
|
||||
GVArrayPtr attribute_interpolated = spline.interpolate_to_evaluated(*src);
|
||||
sample_with_lookups({attribute_interpolated->get_internal_span(), dst.as_span()}, lookups);
|
||||
dst.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void geo_node_sample_exec(GeoNodeExecParams params)
|
||||
{
|
||||
const NodeGeometryCurveSample &node_storage = *(NodeGeometryCurveSample *)params.node().storage;
|
||||
const GeometryNodeCurveInterpolateMode mode = (GeometryNodeCurveInterpolateMode)
|
||||
node_storage.mode;
|
||||
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
|
||||
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
curve_set = bke::geometry_set_realize_instances(curve_set);
|
||||
|
||||
if (!curve_set.has_curve()) {
|
||||
params.set_output("Geometry", geometry_set);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string pararameter_name = params.extract_input<std::string>("Parameter");
|
||||
const std::string position_name = params.extract_input<std::string>("Position");
|
||||
const std::string tangent_name = params.extract_input<std::string>("Tangent");
|
||||
const std::string normal_name = params.extract_input<std::string>("Normal");
|
||||
const std::string attribute_name = params.extract_input<std::string>("Attribute");
|
||||
const std::string result_name = params.extract_input<std::string>("Result");
|
||||
|
||||
const CurveComponent &curve_component = *curve_set.get_component_for_read<CurveComponent>();
|
||||
|
||||
if (attribute_name.empty()) {
|
||||
if (position_name.empty() && tangent_name.empty() && normal_name.empty()) {
|
||||
params.set_output("Geometry", geometry_set);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (!curve_component.attribute_exists(attribute_name)) {
|
||||
params.error_message_add(NodeWarningType::Error,
|
||||
TIP_("No attribute with name \"") + attribute_name + "\"");
|
||||
}
|
||||
|
||||
for (const GeometryComponentType type :
|
||||
{GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) {
|
||||
if (geometry_set.has(type)) {
|
||||
execute_on_component(geometry_set.get_component_for_write(type),
|
||||
curve_component,
|
||||
mode,
|
||||
pararameter_name,
|
||||
position_name,
|
||||
tangent_name,
|
||||
normal_name,
|
||||
attribute_name,
|
||||
result_name);
|
||||
}
|
||||
}
|
||||
|
||||
params.set_output("Geometry", std::move(geometry_set));
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
void register_node_type_geo_curve_sample()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
geo_node_type_base(&ntype, GEO_NODE_CURVE_SAMPLE, "Sample Curve", NODE_CLASS_GEOMETRY, 0);
|
||||
node_type_socket_templates(&ntype, geo_node_curve_sample_in, geo_node_curve_sample_out);
|
||||
ntype.draw_buttons = geo_node_curve_sample_layout;
|
||||
node_type_storage(
|
||||
&ntype, "NodeGeometryCurveSample", node_free_standard_storage, node_copy_standard_storage);
|
||||
node_type_init(&ntype, geo_node_curve_sample_init);
|
||||
node_type_update(&ntype, geo_node_curve_sample_update);
|
||||
ntype.geometry_node_execute = blender::nodes::geo_node_sample_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -48,20 +48,20 @@ static void geo_node_curve_to_points_init(bNodeTree *UNUSED(tree), bNode *node)
|
||||
NodeGeometryCurveToPoints *data = (NodeGeometryCurveToPoints *)MEM_callocN(
|
||||
sizeof(NodeGeometryCurveToPoints), __func__);
|
||||
|
||||
data->mode = GEO_NODE_CURVE_SAMPLE_COUNT;
|
||||
data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT;
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)node->storage;
|
||||
const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
|
||||
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
|
||||
|
||||
bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next;
|
||||
bNodeSocket *length_socket = count_socket->next;
|
||||
|
||||
nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT);
|
||||
nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
|
||||
nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT);
|
||||
nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH);
|
||||
}
|
||||
|
||||
namespace blender::nodes {
|
||||
@@ -81,13 +81,13 @@ static void evaluate_splines(Span<SplinePtr> splines)
|
||||
}
|
||||
|
||||
static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms,
|
||||
const GeometryNodeCurveSampleMode mode,
|
||||
const GeometryNodeCurveResampleMode mode,
|
||||
const CurveEval &curve,
|
||||
const Span<SplinePtr> splines)
|
||||
{
|
||||
const int size = curve.splines().size();
|
||||
switch (mode) {
|
||||
case GEO_NODE_CURVE_SAMPLE_COUNT: {
|
||||
case GEO_NODE_CURVE_RESAMPLE_COUNT: {
|
||||
const int count = params.extract_input<int>("Count");
|
||||
if (count < 1) {
|
||||
return {0};
|
||||
@@ -98,7 +98,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms,
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
case GEO_NODE_CURVE_SAMPLE_LENGTH: {
|
||||
case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
|
||||
/* Don't allow asymptotic count increase for low resolution values. */
|
||||
const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f);
|
||||
Array<int> offsets(size + 1);
|
||||
@@ -110,7 +110,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms,
|
||||
offsets.last() = offset;
|
||||
return offsets;
|
||||
}
|
||||
case GEO_NODE_CURVE_SAMPLE_EVALUATED: {
|
||||
case GEO_NODE_CURVE_RESAMPLE_EVALUATED: {
|
||||
return curve.evaluated_point_offsets();
|
||||
}
|
||||
}
|
||||
@@ -303,7 +303,7 @@ void curve_create_default_rotation_attribute(Span<float3> tangents,
|
||||
static void geo_node_curve_to_points_exec(GeoNodeExecParams params)
|
||||
{
|
||||
NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)params.node().storage;
|
||||
const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
|
||||
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
@@ -333,11 +333,11 @@ static void geo_node_curve_to_points_exec(GeoNodeExecParams params)
|
||||
CurveToPointsResults new_attributes = curve_to_points_create_result_attributes(point_component,
|
||||
curve);
|
||||
switch (mode) {
|
||||
case GEO_NODE_CURVE_SAMPLE_COUNT:
|
||||
case GEO_NODE_CURVE_SAMPLE_LENGTH:
|
||||
case GEO_NODE_CURVE_RESAMPLE_COUNT:
|
||||
case GEO_NODE_CURVE_RESAMPLE_LENGTH:
|
||||
copy_uniform_sample_point_attributes(splines, offsets, new_attributes);
|
||||
break;
|
||||
case GEO_NODE_CURVE_SAMPLE_EVALUATED:
|
||||
case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
|
||||
copy_evaluated_point_attributes(splines, offsets, new_attributes);
|
||||
break;
|
||||
}
|
||||
|
Reference in New Issue
Block a user