1
1

Compare commits

...

109 Commits

Author SHA1 Message Date
7030e3a016 Splines: Fix curve to mesh node issues
- The expected flat axis of profile objects is now the Z axis, like
    the existing curve objects.
 - Simplify/speed up the "sharp edge" marking logic.
2021-04-26 15:49:51 -05:00
936dfae3de Merge branch 'master' into geometry-nodes-curve-support 2021-04-26 15:14:32 -05:00
399aed9a81 Splines: Fix interpolation for bezier splines
Also parallelize some of the calculations, since I was reworking
how the mapping was calculated anyway. I still haven't tuned the
grain size.
2021-04-26 14:17:25 -05:00
e7c1702ccb Splines: Cleanup, add comments, formatting 2021-04-25 23:29:28 -05:00
149078d580 Merge branch 'master' into geometry-nodes-curve-support 2021-04-25 21:45:33 -05:00
47b1f9a4d3 Splines: Cleanup, add comment 2021-04-25 21:37:04 -05:00
8709d0cd5d Splines: Rename DCurve to SplineGroup 2021-04-25 21:26:00 -05:00
46bfc3ec46 Splines: Add bounding box node support 2021-04-25 15:50:46 -05:00
a29b9a73d2 Splines: Cleanup: Remove another unecessary change 2021-04-25 15:21:02 -05:00
39f21c2475 Splines: Cleanup, remove more unecessary changes 2021-04-25 15:10:17 -05:00
87036f5eeb Merge branch 'master' into geometry-nodes-curve-support 2021-04-25 15:07:48 -05:00
9636e79c5a Splines: Reset unecessary geometry nodes changes 2021-04-25 15:01:19 -05:00
e1c04da36f Merge branch 'master' into geometry-nodes-curve-support 2021-04-25 13:40:03 -05:00
45bac491eb Splines: Fix curve lookup (for trim node) 2021-04-25 12:43:03 -05:00
837d630466 Splines: Halve the size of the bezier interpolation cache
Use floats to store the control point indices and the interpolation factors.
2021-04-24 15:02:06 -05:00
23eb05eba5 Merge branch 'master' into geometry-nodes-curve-support 2021-04-24 14:17:28 -05:00
12951aecf6 Merge branch 'master' into geometry-nodes-curve-support 2021-04-23 13:51:57 -05:00
46a037707b Splines: Fix cache not recalculated error 2021-04-22 16:30:06 -05:00
f2948faf97 Splines: Reorganize class inheritence, evaluation 2021-04-22 16:14:23 -05:00
d7aa481231 Splines: Add comments 2021-04-22 12:24:52 -05:00
cd07fb6477 Splines: Continue cleanup 2021-04-22 12:15:18 -05:00
d0bdb2b48f Splines: Continue cleanup 2021-04-22 11:41:41 -05:00
35b0d177a2 Splines: Cleanup: Rename variables and types 2021-04-22 11:25:01 -05:00
441f8d98eb Splines: Cleanup 2021-04-22 11:15:37 -05:00
c21199a0ba Geometry Nodes Curves: Rename files 2021-04-22 09:37:53 -05:00
26d4864ee6 Merge branch 'master' into geometry-nodes-curve-support 2021-04-22 09:25:40 -05:00
da443d82ee Geometry Nodes Curves: Refactor NURBS sampling
This is basically a template for a similar thing I'll do to bezier
splines. The idea is that every spline type is responsible for the
mapping of values from orginal control points to the evaluated
points based on a given resolution, and then the base class will
handle the rest.

This commit leaves the branch in a half-refactored state.
2021-04-21 22:47:11 -05:00
330c14c26c Merge branch 'master' into geometry-nodes-curve-support 2021-04-21 19:20:47 -05:00
34542eede5 Merge branch 'master' into geometry-nodes-curve-support 2021-04-20 15:07:21 -05:00
2c73e16d1c Geometry Nodes Curves: Sample positions from NURBS curves 2021-04-20 14:38:46 -05:00
dd886fcc6d Cleanup: Split spline implementations into separate files 2021-04-19 17:07:17 -05:00
6b44605cec Merge branch 'master' into geometry-nodes-curve-support 2021-04-19 16:57:02 -05:00
2e76c3565d Geometry Nodes Curves: fix get_components_for_write 2021-04-19 13:16:59 +02:00
c3b5378978 Geometry Nodes Curves: Fix build after merging master 2021-04-18 18:50:48 -05:00
645fa86b7d Merge branch 'master' into geometry-nodes-curve-support 2021-04-18 18:25:16 -05:00
e43428eba3 Geometry Nodes Curves: Add more structure for the sample points node 2021-04-18 18:20:24 -05:00
52cb657e91 Geometry Nodes Curves: Fixes for curve trim node
The remaining issue is interpolation and recalculation of bezier handles.
Other than that the node should work fine.
2021-04-18 17:14:34 -05:00
a80edb8865 Geometry nodes curves: Mostly working version of a curve trim node
This still needs some more fixes, but the basics are there now.
2021-04-18 13:20:21 -05:00
b500099e5c Geometry Nodes Curves: Add generic interpolattion to evaluated points 2021-04-16 15:33:15 -05:00
c9c295ef25 Merge branch 'master' into geometry-nodes-curve-support 2021-04-16 13:30:18 -05:00
d64dea8a7e Merge branch 'master' into geometry-nodes-curve-support 2021-04-16 12:59:47 -05:00
476006ae64 Geometry Nodes Curves: Refactor curve data storage
Each piece of data is now stored separately, not bunched up in a
control point class. This should help improve performance and
it's more aligned with the attribute approach.
2021-04-15 23:53:57 -05:00
51e72e4703 Geometry Nodes Curves: Cleanup 2021-04-15 15:04:12 -05:00
ba12548f24 Cleanup: Fix typo in NURBSpline class name 2021-04-15 13:55:13 -05:00
a6f2da1caf Geometry nodes curves: Transform curve from object info node 2021-04-15 13:52:18 -05:00
91b44ebb52 Geometry Nodes Curves: Support curves in the join geometry node 2021-04-15 13:20:15 -05:00
0d203862ca Geometry Nodes Curves: Add initial support for poly splines 2021-04-15 12:13:20 -05:00
371720cb6d Geometry nodes curves: Support instancing on control points 2021-04-15 11:47:22 -05:00
be1a41878c Geometry Nodes Curves: Implement copying a curve
This makes the spreadsheet pinning work as well.
2021-04-15 10:31:27 -05:00
53b2a1a0e3 Merge branch 'master' into geometry-nodes-curve-support 2021-04-15 09:18:53 -05:00
d9cb1fa7dc Merge branch 'master' into geometry-nodes-curve-support 2021-04-15 07:54:30 -05:00
e7516174c7 Cleanup: Use std::unique_ptr for spline vector 2021-04-14 15:32:59 -05:00
98c908d6f6 Merge branch 'master' into geometry-nodes-curve-support 2021-04-14 14:33:16 -05:00
fcb7d2fcba Geometry Nodes Curves: Add UI for curve sample points node 2021-04-12 23:45:02 -05:00
d0b2ddefa3 Merge branch 'master' into geometry-nodes-curve-support 2021-04-12 23:19:54 -05:00
bb04d1c2c8 Geometry Nodes Curves: Add structure for curve trim node
The node doesn't do anything yet because the last level needs to
be implemented, but the majority of the necessary changes are done.
2021-04-11 23:28:40 -05:00
839c796321 Geometry Nodes Curves: Fix profile radius transform 2021-04-11 18:54:05 -05:00
db731b0beb Geometry Nodes Curves: Add temporary node to test transform 2021-04-11 18:53:42 -05:00
6cf3878765 Geometry Nodes Curves: Add Z-Up normal calculation mode 2021-04-11 17:37:21 -05:00
1975dc96d1 Geometry Nodes Curves: Further refactor curve evaluation
This removes the need for some virtual functions
2021-04-09 23:28:19 -05:00
fa83dbda06 Geometry Nodes Curves: Initial read-only point attributes
Radius and position are included for now. The implementation is not at
all ideal, and writing to point attributes will be a bit trickier.
2021-04-09 22:50:44 -05:00
d0b0cb5982 Geometry Nodes Curves: Add updates while in curve is in edit mode 2021-04-09 22:49:51 -05:00
da3004a0f9 Merge branch 'master' into geometry-nodes-curve-support 2021-04-09 22:02:04 -05:00
ec090a215b WIP point attributes 2021-04-09 10:53:19 -05:00
797331db86 Geometry Nodes Curves: Make edges sharp in curve to mesh node 2021-04-08 23:17:09 -05:00
652ee81662 Geometry Nodes Curves: Switch order of generated edges 2021-04-08 22:28:37 -05:00
b5bd676a51 Geometry Nodes Curves: Apply scale instead of set scale 2021-04-08 17:01:22 -05:00
b490e0838c Geometry Nodes Curves: Fix radius interpolation 2021-04-08 16:33:49 -05:00
7c77d769dd Geometry Nodes Curves: Add "owns direct data" functions 2021-04-08 16:00:14 -05:00
48316b7e08 Merge branch 'master' into geometry-nodes-curve-support 2021-04-08 15:46:35 -05:00
ff34a3a612 Geometry Nodes Curves: Refactor, mapping from evaluated to control points
Also include some incomplete handling of radius in the curve to mesh node.
I'm not sure how much of the changes will stick around, but the reshuffling
of virtual vs. final methods will stay, and the separated caches probably
makes sense too.
2021-04-08 00:39:46 -05:00
583bc7a05d Merge branch 'master' into geometry-nodes-curve-support 2021-04-07 21:45:55 -05:00
2a96dc80b0 Geometry Nodes Curves: Small cleanup, remove debugging code 2021-04-06 00:04:11 -05:00
2cdec6cbf4 Geometry Nodes Curves: Generate faces in Curve to Mesh node 2021-04-05 23:59:33 -05:00
f8604f18e8 Geometry Nodes Curves: Further clean up forward differencing function 2021-04-05 22:55:31 -05:00
4f0e6c3aa3 Cleanup: Vectorize bezier forward differencing
It's a bit strange no one had done this already. This also solves the
problem of `BKE_curve_forward_diff_bezier` always affecting one
more points than you asked for, which was causing another problem.
2021-04-05 22:47:42 -05:00
f3775ca2cf Geometry Nodes Curves: Add edges output to mesh node 2021-04-05 17:24:25 -05:00
5e013d47a1 Geometry Nodes Curves: Fix writing to cyclic attribute 2021-04-05 17:23:34 -05:00
2f2bedafa1 Geometry Nodes Curves: Optimize evaluated points size 2021-04-05 17:23:18 -05:00
b90aeb0a44 Geometry Nodes Curves: Fix non-cylic mesh size calculation 2021-04-05 13:54:50 -05:00
29e24974a6 Geometry Nodes Curves: Fix of by one resolution issue 2021-04-05 13:54:30 -05:00
c752884d0d Geometry Nodes Curves: Fix memory leak 2021-04-05 13:43:58 -05:00
797ff2f064 Geometry Nodes Curves: Pass spline instead of position array 2021-04-05 13:36:04 -05:00
24d32a796a Geometry Nodes Curves: Add incomplete quaternion header 2021-04-05 13:15:09 -05:00
d1519df6a8 Geometry Nodes Curves: Expose cyclic attribute, fix cyclic sampling 2021-04-05 13:14:56 -05:00
457e1f9d1f Geometry Nodes Curves: Cleanup 2021-04-05 13:14:19 -05:00
85165058ef Geometry Nodes Curves: Fix clang tidy warnings 2021-04-05 13:13:16 -05:00
54188678b4 Merge branch 'master' into geometry-nodes-curve-support 2021-04-05 07:59:50 -05:00
073bf63a9d Geometry Nodes Curves: Cleanup, progres on evaluation for bezier splines 2021-04-04 23:09:20 -05:00
f91df2e3e3 Merge branch 'master' into geometry-nodes-curve-support 2021-04-04 16:58:55 -05:00
c95b2cefab Geometry Nodes Curves: More progress on curve to mesh node 2021-04-04 13:22:53 -05:00
53f92279cb Geometry Nodes Curves: Keep resolution attribute above 0 2021-04-04 13:22:29 -05:00
b3fc6ac07a Geometry Nodes Curves: Allow attribute nodes to run on curves 2021-04-04 09:30:35 -05:00
968977d684 Geometry Nodes Curves: Retrieve edit mode nurbs as well 2021-04-04 09:29:52 -05:00
f49a499129 Geometry Nodes Curves: Expose attributes in the spreadsheet 2021-04-03 09:40:11 -05:00
140828037b Geometry Nodes Curves: Initial support for spline attributes
Also: Move the evaluation cache to each spline, store spline pointers
instead of a separate list per spline type in `DCurve`, and also curve
to mesh node doesn't work now.
2021-04-03 09:39:56 -05:00
188e4d302c Geometry Nodes Curves: Rename spline domain 2021-04-03 09:38:22 -05:00
cc06b059c2 Cleanup: Reorder functions 2021-04-02 13:19:58 -05:00
0c64850e43 Geometry Nodes Curves: Fix the curve to mesh node 2021-04-02 13:13:03 -05:00
d64e149a4f Geometry Nodes Curves: Store splines a bit differently 2021-04-02 13:04:07 -05:00
09846cdce8 Merge branch 'master' into geometry-nodes-curve-support 2021-04-02 12:35:20 -05:00
a4ad4bc8d3 Create curve component with object info node 2021-04-02 11:20:45 -05:00
2622882d83 Add method to create geometry set from curve 2021-04-02 10:49:01 -05:00
b4beba7f1c Merge branch 'master' into geometry-nodes-curve-support 2021-04-02 10:41:55 -05:00
58cdb78ea0 Disfunctional support for geometry nodes on curve
Very hacky, doesn't work, but it's a base, and some components work.
I have a feeling that the curve modifier stack is just fundamentally broken.
2021-03-31 17:49:51 -05:00
25e95ccb1a Merge branch 'master' into geometry-nodes-curve-support 2021-03-31 11:41:58 -05:00
9e91aea40a Merge branch 'displist-to-cpp' into geometry-nodes-curve-support 2021-03-31 11:41:23 -05:00
5f03931fc4 Move displist.c to C++ 2021-03-31 09:11:33 -05:00
e1cef1b85d A bit of progress 2021-03-30 06:54:33 -05:00
52 changed files with 3725 additions and 22 deletions

View File

@@ -504,6 +504,12 @@ geometry_node_categories = [
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
]),
GeometryNodeCategory("GEO_CURVE", "Curve", items=[
NodeItem("GeometryNodeCurveToMesh"),
NodeItem("GeometryNodeTransformTest"),
NodeItem("GeometryNodeCurveTrim"),
NodeItem("GeometryNodeCurveSamplePoints"),
]),
GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[
NodeItem("GeometryNodeBoundBox"),
NodeItem("GeometryNodeTransform"),

View File

@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "BLI_array.hh"
#include "BLI_color.hh"
#include "BLI_float2.hh"

View File

@@ -36,6 +36,7 @@ typedef enum GeometryComponentType {
GEO_COMPONENT_TYPE_POINT_CLOUD = 1,
GEO_COMPONENT_TYPE_INSTANCES = 2,
GEO_COMPONENT_TYPE_VOLUME = 3,
GEO_COMPONENT_TYPE_CURVE = 4,
} GeometryComponentType;
void BKE_geometry_set_free(struct GeometrySet *geometry_set);

View File

@@ -34,11 +34,13 @@
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.h"
struct SplineGroup;
struct Collection;
struct Mesh;
struct Object;
struct PointCloud;
struct Volume;
struct SplineGroup;
enum class GeometryOwnershipType {
/* The geometry is owned. This implies that it can be changed. */
@@ -363,18 +365,25 @@ struct GeometrySet {
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
static GeometrySet create_with_pointcloud(
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
static GeometrySet create_with_curve(
SplineGroup *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/* Utility methods for access. */
bool has_mesh() const;
bool has_pointcloud() const;
bool has_instances() const;
bool has_volume() const;
bool has_curve() const;
const Mesh *get_mesh_for_read() const;
const PointCloud *get_pointcloud_for_read() const;
const Volume *get_volume_for_read() const;
const SplineGroup *get_curve_for_read() const;
Mesh *get_mesh_for_write();
PointCloud *get_pointcloud_for_write();
Volume *get_volume_for_write();
SplineGroup *get_curve_for_write();
/* Utility methods for replacement. */
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
@@ -382,6 +391,8 @@ struct GeometrySet {
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
void replace_volume(Volume *volume,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
void replace_curve(SplineGroup *mesh,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
};
/** A geometry component that can store a mesh. */
@@ -463,6 +474,38 @@ class PointCloudComponent : public GeometryComponent {
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
};
/** A geometry component that stores curve data, in other words, a group of splines. */
class CurveComponent : public GeometryComponent {
private:
SplineGroup *curve_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
public:
CurveComponent();
~CurveComponent();
GeometryComponent *copy() const override;
void clear();
bool has_curve() const;
void replace(SplineGroup *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
SplineGroup *release();
const SplineGroup *get_for_read() const;
SplineGroup *get_for_write();
int attribute_domain_size(const AttributeDomain domain) const final;
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
private:
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
};
/** A geometry component that stores instances. */
class InstancesComponent : public GeometryComponent {
private:

View File

@@ -1414,6 +1414,10 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_ATTRIBUTE_CLAMP 1041
#define GEO_NODE_BOUNDING_BOX 1042
#define GEO_NODE_SWITCH 1043
#define GEO_NODE_CURVE_TO_MESH 1044
#define GEO_NODE_CURVE_TRANSFORM_TEST 1045
#define GEO_NODE_CURVE_TRIM 1046
#define GEO_NODE_CURVE_SAMPLE_POINTS 1047
/** \} */

View File

@@ -0,0 +1,434 @@
/*
* 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.
*/
#pragma once
/** \file
* \ingroup bke
*/
#include <mutex>
#include "FN_generic_virtual_array.hh"
#include "BLI_float3.hh"
#include "BLI_float4x4.hh"
#include "BLI_vector.hh"
#include "BKE_attribute_math.hh"
struct Curve;
class Spline;
using SplinePtr = std::unique_ptr<Spline>;
/**
* A spline is an abstraction of a single branchless curve section, its evaluation methods,
* and data. The spline data itself is just control points and a set of attributes.
*
* Any derived class of Spline has to manage two things:
* 1. Evaluating the positions based on the stored control point data.
* 2. Interpolating arbitrary attribute data from the control points to evaluated points.
*
* Beyond that, everything else is the bas class's responsibility, with minor exceptions. Futher
* evaluation happens in a layer on top of the evaluated points generated by the derived types.
* There are a few methods to evaluate a spline:
*
* Common evaluated data is stored in caches on the spline itself. That way operations on splines
* don't need to worry about taking ownership of evaluated data when they don't need to.
*/
class Spline {
public:
enum Type {
Bezier,
NURBS,
Poly,
};
private:
Type type_;
public:
bool is_cyclic = false;
enum NormalCalculationMode {
ZUp,
Minimum,
Tangent,
};
NormalCalculationMode normal_mode;
protected:
mutable bool tangent_cache_dirty_ = true;
mutable std::mutex tangent_cache_mutex_;
mutable blender::Vector<blender::float3> evaluated_tangents_cache_;
mutable bool normal_cache_dirty_ = true;
mutable std::mutex normal_cache_mutex_;
mutable blender::Vector<blender::float3> evaluated_normals_cache_;
mutable bool length_cache_dirty_ = true;
mutable std::mutex length_cache_mutex_;
mutable blender::Vector<float> evaluated_lengths_cache_;
public:
virtual ~Spline() = default;
Spline(const Type type) : type_(type){};
Spline(Spline &other)
: type_(other.type_), is_cyclic(other.is_cyclic), normal_mode(other.normal_mode)
{
}
virtual SplinePtr copy() const = 0;
Spline::Type type() const;
virtual int size() const = 0;
int segments_size() const;
virtual int resolution() const = 0;
virtual void set_resolution(const int value) = 0;
virtual void drop_front(const int count) = 0;
virtual void drop_back(const int count) = 0;
virtual blender::MutableSpan<blender::float3> positions() = 0;
virtual blender::Span<blender::float3> positions() const = 0;
virtual blender::MutableSpan<float> radii() = 0;
virtual blender::Span<float> radii() const = 0;
virtual blender::MutableSpan<float> tilts() = 0;
virtual blender::Span<float> tilts() const = 0;
/**
* Mark all caches for recomputation. This must be called after any operation that would
* change the generated positions, tangents, normals, mapping, etc. of the evaluated points.
*/
virtual void mark_cache_invalid() = 0;
virtual int evaluated_points_size() const = 0;
int evaluated_edges_size() const;
float length() const;
virtual blender::Span<blender::float3> evaluated_positions() const = 0;
blender::Span<float> evaluated_lengths() const;
blender::Span<blender::float3> evaluated_tangents() const;
blender::Span<blender::float3> evaluated_normals() const;
void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
struct LookupResult {
/*
* The index of the evaluated point before the result location.
* In other words, the index of the edge that the result lies on.
*/
int evaluated_index;
/*
* The index of the evaluated point after the result location,
* accounting for wrapping when the spline is cyclic.
*/
int next_evaluated_index;
/**
* The portion of the way from the evaluated point at #evaluated_index to the next point.
*/
float factor;
};
LookupResult lookup_evaluated_factor(const float factor) const;
LookupResult lookup_evaluated_length(const float length) const;
virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const = 0;
protected:
virtual void correct_end_tangents() const = 0;
};
/**
* A Bézier spline is made up of a many curve segments, possibly achieving continuity of curvature
* by constraining the alignment of curve handles. Evaluation stores the positions and a map of
* factors and indices in a list of floats, which is then used to interpolate any other data.
*/
class BezierSpline final : public Spline {
public:
enum HandleType {
Free,
Auto,
Vector,
Align,
};
private:
blender::Vector<HandleType> handle_types_start_;
blender::Vector<blender::float3> handle_positions_start_;
blender::Vector<blender::float3> positions_;
blender::Vector<HandleType> handle_types_end_;
blender::Vector<blender::float3> handle_positions_end_;
blender::Vector<float> radii_;
blender::Vector<float> tilts_;
int resolution_;
mutable bool offset_cache_dirty_ = true;
mutable std::mutex offset_cache_mutex_;
mutable blender::Vector<int> offset_cache_;
mutable bool position_cache_dirty_ = true;
mutable std::mutex position_cache_mutex_;
mutable blender::Vector<blender::float3> evaluated_position_cache_;
mutable bool mapping_cache_dirty_ = true;
mutable std::mutex mapping_cache_mutex_;
mutable blender::Vector<float> evaluated_mapping_cache_;
public:
virtual SplinePtr copy() const final;
BezierSpline() : Spline(Type::Bezier){};
BezierSpline(const BezierSpline &other)
: Spline((Spline &)other),
handle_types_start_(other.handle_types_start_),
handle_positions_start_(other.handle_positions_start_),
positions_(other.positions_),
handle_types_end_(other.handle_types_end_),
handle_positions_end_(other.handle_positions_end_),
radii_(other.radii_),
tilts_(other.tilts_),
resolution_(other.resolution_)
{
}
int size() const final;
int resolution() const final;
void set_resolution(const int value) final;
void add_point(const blender::float3 position,
const HandleType handle_type_start,
const blender::float3 handle_position_start,
const HandleType handle_type_end,
const blender::float3 handle_position_end,
const float radius,
const float tilt);
void drop_front(const int count) final;
void drop_back(const int count) final;
blender::MutableSpan<blender::float3> positions() final;
blender::Span<blender::float3> positions() const final;
blender::MutableSpan<float> radii() final;
blender::Span<float> radii() const final;
blender::MutableSpan<float> tilts() final;
blender::Span<float> tilts() const final;
blender::Span<HandleType> handle_types_start() const;
blender::MutableSpan<HandleType> handle_types_start();
blender::Span<blender::float3> handle_positions_start() const;
blender::MutableSpan<blender::float3> handle_positions_start();
blender::Span<HandleType> handle_types_end() const;
blender::MutableSpan<HandleType> handle_types_end();
blender::Span<blender::float3> handle_positions_end() const;
blender::MutableSpan<blender::float3> handle_positions_end();
bool point_is_sharp(const int index) const;
bool handle_start_is_automatic(const int index) const;
bool handle_end_is_automatic(const int index) const;
void move_control_point(const int index, const blender::float3 new_position);
void mark_cache_invalid() final;
int evaluated_points_size() const final;
blender::Span<int> control_point_offsets() const;
blender::Span<float> evaluated_mappings() const;
blender::Span<blender::float3> evaluated_positions() const final;
struct InterpolationData {
int control_point_index;
int next_control_point_index;
float factor;
};
InterpolationData interpolation_data_from_map(const float map) const;
virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const;
private:
void correct_end_tangents() const final;
bool segment_is_vector(const int start_index) const;
void evaluate_bezier_segment(const int index,
const int next_index,
blender::MutableSpan<blender::float3> positions) const;
blender::Array<int> evaluated_point_offsets() const;
};
/**
* Data for Non-Uniform Rational B-Splines. The mapping from control points to evaluated points is
* influenced by a vector of knots, weights for each point, and the order of the spline. Every
* mapping of data to evaluated points is handled the same way, but the posistions are cached in
* the spline.
*/
class NURBSpline final : public Spline {
public:
enum KnotsMode {
Normal,
EndPoint,
Bezier,
};
KnotsMode knots_mode;
struct BasisCache {
blender::Vector<float> weights;
int start_index;
};
private:
blender::Vector<blender::float3> positions_;
blender::Vector<float> radii_;
blender::Vector<float> tilts_;
blender::Vector<float> weights_;
int resolution_;
uint8_t order_;
mutable bool knots_dirty_ = true;
mutable std::mutex knots_mutex_;
mutable blender::Vector<float> knots_;
mutable bool position_cache_dirty_ = true;
mutable std::mutex position_cache_mutex_;
mutable blender::Vector<blender::float3> evaluated_position_cache_;
mutable bool basis_cache_dirty_ = true;
mutable std::mutex basis_cache_mutex_;
mutable blender::Vector<BasisCache> basis_cache_;
public:
SplinePtr copy() const final;
NURBSpline() : Spline(Type::NURBS){};
NURBSpline(const NURBSpline &other)
: Spline((Spline &)other),
positions_(other.positions_),
radii_(other.radii_),
tilts_(other.tilts_),
weights_(other.weights_),
resolution_(other.resolution_),
order_(other.order_)
{
}
int size() const final;
int resolution() const final;
void set_resolution(const int value) final;
uint8_t order() const;
void set_order(const uint8_t value);
void add_point(const blender::float3 position,
const float radius,
const float tilt,
const float weight);
void drop_front(const int count) final;
void drop_back(const int count) final;
bool check_valid_size_and_order() const;
int knots_size() const;
blender::MutableSpan<blender::float3> positions() final;
blender::Span<blender::float3> positions() const final;
blender::MutableSpan<float> radii() final;
blender::Span<float> radii() const final;
blender::MutableSpan<float> tilts() final;
blender::Span<float> tilts() const final;
blender::Span<float> knots() const;
blender::MutableSpan<float> weights();
blender::Span<float> weights() const;
void mark_cache_invalid() final;
int evaluated_points_size() const final;
blender::Span<blender::float3> evaluated_positions() const final;
blender::fn::GVArrayPtr interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const final;
protected:
void correct_end_tangents() const final;
void calculate_knots() const;
void calculate_basis_cache() const;
};
/**
* A poly spline is like a bezier spline with a resolution of one. The main reason to distinguish
* the two is for reduced complexity and increased performance, since interpolating data to control
* points does not change it.
*/
class PolySpline final : public Spline {
public:
blender::Vector<blender::float3> positions_;
blender::Vector<float> radii_;
blender::Vector<float> tilts_;
private:
public:
SplinePtr copy() const final;
PolySpline() : Spline(Type::Bezier){};
PolySpline(const PolySpline &other)
: Spline((Spline &)other),
positions_(other.positions_),
radii_(other.radii_),
tilts_(other.tilts_)
{
}
int size() const final;
int resolution() const final;
void set_resolution(const int value) final;
void add_point(const blender::float3 position, const float radius, const float tilt);
void drop_front(const int count) final;
void drop_back(const int count) final;
blender::MutableSpan<blender::float3> positions() final;
blender::Span<blender::float3> positions() const final;
blender::MutableSpan<float> radii() final;
blender::Span<float> radii() const final;
blender::MutableSpan<float> tilts() final;
blender::Span<float> tilts() const final;
void mark_cache_invalid() final;
int evaluated_points_size() const final;
blender::Span<blender::float3> evaluated_positions() const final;
blender::fn::GVArrayPtr interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const final;
protected:
void correct_end_tangents() const final;
};
/**
* A #SplineGroup corresponds to the #Curve object data. The name is different for clarity, since
* more of the data is stored in the splines, but also just to be different than the name in DNA.
*/
class SplineGroup {
public:
blender::Vector<SplinePtr> splines;
SplineGroup *copy();
void translate(const blender::float3 translation);
void transform(const blender::float4x4 &matrix);
void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
};
SplineGroup *dcurve_from_dna_curve(const Curve &curve);

View File

@@ -133,6 +133,7 @@ set(SRC
intern/fmodifier.c
intern/font.c
intern/freestyle.c
intern/geometry_component_curve.cc
intern/geometry_component_instances.cc
intern/geometry_component_mesh.cc
intern/geometry_component_pointcloud.cc
@@ -241,6 +242,11 @@ set(SRC
intern/softbody.c
intern/sound.c
intern/speaker.c
intern/spline_base.cc
intern/spline_bezier.cc
intern/spline_group.cc
intern/spline_nurbs.cc
intern/spline_poly.cc
intern/studiolight.c
intern/subdiv.c
intern/subdiv_ccg.c
@@ -322,6 +328,7 @@ set(SRC
BKE_customdata_file.h
BKE_data_transfer.h
BKE_deform.h
BKE_spline.hh
BKE_displist.h
BKE_displist_tangent.h
BKE_duplilist.h

View File

@@ -143,10 +143,8 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_
static int attribute_domain_priority(const AttributeDomain domain)
{
switch (domain) {
#if 0
case ATTR_DOMAIN_CURVE:
return 0;
#endif
case ATTR_DOMAIN_FACE:
return 1;
case ATTR_DOMAIN_EDGE:

View File

@@ -46,6 +46,7 @@
#include "BKE_curve.h"
#include "BKE_displist.h"
#include "BKE_font.h"
#include "BKE_geometry_set.hh"
#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_lib_id.h"
@@ -54,6 +55,7 @@
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_spline.hh"
#include "BLI_sys_types.h" // for intptr_t support
@@ -893,14 +895,59 @@ static void displist_vert_coords_apply(ListBase *dispbase, const float (*allvert
}
}
static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md,
const ModifierEvalContext &mectx,
const Curve *curve,
Object *ob,
Mesh *input_mesh,
GeometrySet &geometry_set)
{
Mesh *mesh_output = nullptr;
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (mti->modifyGeometrySet == nullptr) {
mesh_output = BKE_modifier_modify_mesh(md, &mectx, input_mesh);
}
else {
/* Adds a new mesh component to the geometry set based on the #input_mesh. */
BLI_assert(!geometry_set.has<MeshComponent>());
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_component.replace(input_mesh, GeometryOwnershipType::Editable);
mesh_component.copy_vertex_group_names_from_object(*ob);
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
curve_component.replace(dcurve_from_dna_curve(*curve));
/* Let the modifier change the geometry set. */
mti->modifyGeometrySet(md, &mectx, &geometry_set);
/* Release the mesh from the geometry set again. */
if (geometry_set.has<MeshComponent>()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_output = mesh_component.release();
geometry_set.remove<MeshComponent>();
}
/* Return an empty mesh instead of null. */
if (mesh_output == nullptr) {
mesh_output = BKE_mesh_new_nomain(0, 0, 0, 0, 0);
BKE_mesh_copy_settings(mesh_output, input_mesh);
}
}
return mesh_output;
}
static void curve_calc_modifiers_post(Depsgraph *depsgraph,
const Scene *scene,
Object *ob,
ListBase *dispbase,
Mesh **r_final,
const bool for_render,
const bool force_mesh_conversion)
const bool force_mesh_conversion,
GeometrySet **r_geometry_set)
{
GeometrySet geometry_set_final;
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
ModifierData *pretessellatePoint;
@@ -1020,7 +1067,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (need_normal) {
BKE_mesh_ensure_normals(modified);
}
mesh_applied = mti->modifyMesh(md, &mectx_apply, modified);
mesh_applied = modifier_modify_mesh_and_geometry_set(
md, mectx_apply, cu, ob, modified, geometry_set_final);
if (mesh_applied) {
/* Modifier returned a new derived mesh */
@@ -1096,6 +1145,10 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
/* Pretty stupid to generate that whole mesh if it's unused, yet we have to free it. */
BKE_id_free(nullptr, modified);
}
if (r_geometry_set) {
*r_geometry_set = new GeometrySet(std::move(geometry_set_final));
}
}
static void displist_surf_indices(DispList *dl)
@@ -1235,7 +1288,7 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
if (!for_orco) {
BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);
curve_calc_modifiers_post(
depsgraph, scene, ob, dispbase, r_final, for_render, force_mesh_conversion);
depsgraph, scene, ob, dispbase, r_final, for_render, force_mesh_conversion, nullptr);
}
BKE_nurbList_free(&nubase);
@@ -1469,7 +1522,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
ListBase *dispbase,
const bool for_render,
const bool for_orco,
Mesh **r_final)
Mesh **r_final,
GeometrySet **r_geometry_set)
{
Curve *cu = (Curve *)ob->data;
@@ -1709,8 +1763,14 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);
curve_calc_modifiers_post(
depsgraph, scene, ob, dispbase, r_final, for_render, force_mesh_conversion);
curve_calc_modifiers_post(depsgraph,
scene,
ob,
dispbase,
r_final,
for_render,
force_mesh_conversion,
r_geometry_set);
}
if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) {
@@ -1746,10 +1806,18 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
dispbase = &(ob->runtime.curve_cache->disp);
Mesh *mesh_eval = nullptr;
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval);
GeometrySet *geometry_set_eval = nullptr;
do_makeDispListCurveTypes(
depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval, &geometry_set_eval);
if (mesh_eval != nullptr) {
BKE_object_eval_assign_data(ob, &mesh_eval->id, true);
/* Add the final mesh as read-only non-owning component to the geometry set. */
BLI_assert(!geometry_set_eval->has<MeshComponent>());
MeshComponent &mesh_component = geometry_set_eval->get_component_for_write<MeshComponent>();
mesh_component.replace(mesh_eval, GeometryOwnershipType::ReadOnly);
ob->runtime.geometry_set_eval = geometry_set_eval;
}
boundbox_displist_object(ob);
@@ -1767,7 +1835,7 @@ void BKE_displist_make_curveTypes_forRender(Depsgraph *depsgraph,
"CurveCache for Curve");
}
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, true, for_orco, r_final);
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, true, for_orco, r_final, nullptr);
}
void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3])

View File

@@ -0,0 +1,403 @@
/*
* 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 "BKE_spline.hh"
#include "BKE_attribute_access.hh"
#include "BKE_attribute_math.hh"
#include "BKE_geometry_set.hh"
#include "attribute_access_intern.hh"
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
* \{ */
CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
{
}
CurveComponent::~CurveComponent()
{
this->clear();
}
GeometryComponent *CurveComponent::copy() const
{
CurveComponent *new_component = new CurveComponent();
if (curve_ != nullptr) {
new_component->curve_ = curve_->copy();
new_component->ownership_ = GeometryOwnershipType::Owned;
}
return new_component;
}
void CurveComponent::clear()
{
BLI_assert(this->is_mutable());
if (curve_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
delete curve_;
}
curve_ = nullptr;
}
}
bool CurveComponent::has_curve() const
{
return curve_ != nullptr;
}
/* Clear the component and replace it with the new curve. */
void CurveComponent::replace(SplineGroup *curve, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
curve_ = curve;
ownership_ = ownership;
}
SplineGroup *CurveComponent::release()
{
BLI_assert(this->is_mutable());
SplineGroup *curve = curve_;
curve_ = nullptr;
return curve;
}
const SplineGroup *CurveComponent::get_for_read() const
{
return curve_;
}
SplineGroup *CurveComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
curve_ = curve_->copy();
ownership_ = GeometryOwnershipType::Owned;
}
return curve_;
}
bool CurveComponent::is_empty() const
{
return curve_ == nullptr;
}
bool CurveComponent::owns_direct_data() const
{
return ownership_ == GeometryOwnershipType::Owned;
}
void CurveComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
curve_ = curve_->copy();
ownership_ = GeometryOwnershipType::Owned;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Attribute Access
* \{ */
int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
{
if (curve_ == nullptr) {
return 0;
}
if (domain == ATTR_DOMAIN_POINT) {
int total = 0;
for (const SplinePtr &spline : curve_->splines) {
total += spline->size();
}
return total;
}
if (domain == ATTR_DOMAIN_CURVE) {
return curve_->splines.size();
}
return 0;
}
namespace blender::bke {
class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
using AsReadAttribute = GVArrayPtr (*)(const SplineGroup &data);
using AsWriteAttribute = GVMutableArrayPtr (*)(SplineGroup &data);
using UpdateOnWrite = void (*)(Spline &spline);
const AsReadAttribute as_read_attribute_;
const AsWriteAttribute as_write_attribute_;
public:
BuiltinSplineAttributeProvider(std::string attribute_name,
const CustomDataType attribute_type,
const WritableEnum writable,
const AsReadAttribute as_read_attribute,
const AsWriteAttribute as_write_attribute)
: BuiltinAttributeProvider(std::move(attribute_name),
ATTR_DOMAIN_CURVE,
attribute_type,
BuiltinAttributeProvider::NonCreatable,
writable,
BuiltinAttributeProvider::NonDeletable),
as_read_attribute_(as_read_attribute),
as_write_attribute_(as_write_attribute)
{
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
{
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
const SplineGroup *curve = curve_component.get_for_read();
if (curve == nullptr) {
return {};
}
return as_read_attribute_(*curve);
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
{
if (writable_ != Writable) {
return {};
}
CurveComponent &curve_component = static_cast<CurveComponent &>(component);
SplineGroup *curve = curve_component.get_for_write();
if (curve == nullptr) {
return {};
}
return as_write_attribute_(*curve);
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
{
return false;
}
bool try_create(GeometryComponent &UNUSED(component),
const AttributeInit &UNUSED(initializer)) const final
{
return false;
}
bool exists(const GeometryComponent &component) const final
{
return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0;
}
};
static int get_spline_resolution(const SplinePtr &spline)
{
return spline->resolution();
}
static void set_spline_resolution(SplinePtr &spline, const int resolution)
{
spline->set_resolution(std::max(resolution, 1));
}
static GVArrayPtr make_resolution_read_attribute(const SplineGroup &curve)
{
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, int, get_spline_resolution>>(
curve.splines.as_span());
}
static GVMutableArrayPtr make_resolution_write_attribute(SplineGroup &curve)
{
return std::make_unique<fn::GVMutableArray_For_DerivedSpan<SplinePtr,
int,
get_spline_resolution,
set_spline_resolution>>(
curve.splines.as_mutable_span());
}
static float get_spline_length(const SplinePtr &spline)
{
return spline->length();
}
static GVArrayPtr make_length_attribute(const SplineGroup &curve)
{
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, float, get_spline_length>>(
curve.splines.as_span());
}
static bool get_cyclic_value(const SplinePtr &spline)
{
return spline->is_cyclic;
}
static void set_cyclic_value(SplinePtr &spline, const bool value)
{
if (spline->is_cyclic != value) {
spline->is_cyclic = value;
spline->mark_cache_invalid();
}
}
static GVArrayPtr make_cyclic_read_attribute(const SplineGroup &curve)
{
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value>>(
curve.splines.as_span());
}
static GVMutableArrayPtr make_cyclic_write_attribute(SplineGroup &curve)
{
return std::make_unique<
fn::GVMutableArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value, set_cyclic_value>>(
curve.splines.as_mutable_span());
}
/**
* \note Currently this uses an inefficient method, copying data from each spline into a single
* array and then passing that as the attribute. Also, currently attributes are only read-only.
*/
class BuiltinPointAttributeProvider final : public BuiltinAttributeProvider {
using GetSplineData = void (*)(const Spline &spline, fn::GMutableSpan r_data);
using SetSplineData = void (*)(Spline &spline, fn::GSpan data);
const GetSplineData get_spline_data_;
const SetSplineData set_spline_data_;
public:
BuiltinPointAttributeProvider(std::string attribute_name,
const CustomDataType attribute_type,
const WritableEnum writable,
const GetSplineData get_spline_data,
const SetSplineData set_spline_data)
: BuiltinAttributeProvider(std::move(attribute_name),
ATTR_DOMAIN_POINT,
attribute_type,
BuiltinAttributeProvider::NonCreatable,
writable,
BuiltinAttributeProvider::NonDeletable),
get_spline_data_(get_spline_data),
set_spline_data_(set_spline_data)
{
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
{
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
const SplineGroup *curve = curve_component.get_for_read();
if (curve == nullptr) {
return {};
}
GVArrayPtr varray;
attribute_math::convert_to_static_type(data_type_, [&](auto dummy) {
using T = decltype(dummy);
Array<T> values(curve_component.attribute_domain_size(ATTR_DOMAIN_POINT));
int offset = 0;
for (const SplinePtr &spline : curve->splines) {
const int points_len = spline->size();
MutableSpan<T> spline_data = values.as_mutable_span().slice(offset, points_len);
fn::GMutableSpan generic_spline_data(spline_data);
get_spline_data_(*spline, generic_spline_data);
offset += points_len;
}
varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
});
return varray;
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
{
return {};
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
{
return false;
}
bool try_create(GeometryComponent &UNUSED(component),
const AttributeInit &UNUSED(initializer)) const final
{
return false;
}
bool exists(const GeometryComponent &component) const final
{
return component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0;
}
};
static void get_spline_radius_data(const Spline &spline, fn::GMutableSpan r_data)
{
MutableSpan<float> r_span = r_data.typed<float>();
r_span.copy_from(spline.radii());
}
static void get_spline_position_data(const Spline &spline, fn::GMutableSpan r_data)
{
MutableSpan<float3> r_span = r_data.typed<float3>();
r_span.copy_from(spline.positions());
}
/**
* In this function all the attribute providers for a curve component are created. Most data
* in this function is statically allocated, because it does not change over time.
*/
static ComponentAttributeProviders create_attribute_providers_for_curve()
{
static BuiltinSplineAttributeProvider resolution("resolution",
CD_PROP_INT32,
BuiltinAttributeProvider::Writable,
make_resolution_read_attribute,
make_resolution_write_attribute);
static BuiltinSplineAttributeProvider length(
"length", CD_PROP_FLOAT, BuiltinAttributeProvider::Readonly, make_length_attribute, nullptr);
static BuiltinSplineAttributeProvider cyclic("cyclic",
CD_PROP_BOOL,
BuiltinAttributeProvider::Writable,
make_cyclic_read_attribute,
make_cyclic_write_attribute);
static BuiltinPointAttributeProvider position("position",
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Readonly,
get_spline_position_data,
nullptr);
static BuiltinPointAttributeProvider radius("radius",
CD_PROP_FLOAT,
BuiltinAttributeProvider::Readonly,
get_spline_radius_data,
nullptr);
return ComponentAttributeProviders({&resolution, &length, &cyclic, &position, &radius}, {});
}
} // namespace blender::bke
const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const
{
static blender::bke::ComponentAttributeProviders providers =
blender::bke::create_attribute_providers_for_curve();
return &providers;
}
/** \} */

View File

@@ -24,6 +24,7 @@
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DNA_collection_types.h"
@@ -60,6 +61,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
return new InstancesComponent();
case GEO_COMPONENT_TYPE_VOLUME:
return new VolumeComponent();
case GEO_COMPONENT_TYPE_CURVE:
return new CurveComponent();
}
BLI_assert_unreachable();
return nullptr;
@@ -182,6 +185,13 @@ void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
if (volume != nullptr) {
BKE_volume_min_max(volume, *r_min, *r_max);
}
const SplineGroup *curve = this->get_curve_for_read();
if (curve != nullptr) {
/* Note the the choice of using the evaluated positions is somewhat arbitrary, and may counter
* the idea that the curve is the reduced set of control point information, but it may also be
* the expected result. */
curve->bounds_min_max(*r_min, *r_max, true);
}
}
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
@@ -252,6 +262,13 @@ const Volume *GeometrySet::get_volume_for_read() const
return (component == nullptr) ? nullptr : component->get_for_read();
}
/* Returns a read-only curve or null. */
const SplineGroup *GeometrySet::get_curve_for_read() const
{
const CurveComponent *component = this->get_component_for_read<CurveComponent>();
return (component == nullptr) ? nullptr : component->get_for_read();
}
/* Returns true when the geometry set has a point cloud component that has a point cloud. */
bool GeometrySet::has_pointcloud() const
{
@@ -273,6 +290,13 @@ bool GeometrySet::has_volume() const
return component != nullptr && component->has_volume();
}
/* Returns true when the geometry set has a curve component that has a curve. */
bool GeometrySet::has_curve() const
{
const CurveComponent *component = this->get_component_for_read<CurveComponent>();
return component != nullptr && component->has_curve();
}
/* Create a new geometry set that only contains the given mesh. */
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -292,6 +316,15 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
return geometry_set;
}
/* Create a new geometry set that only contains the given curve. */
GeometrySet GeometrySet::create_with_curve(SplineGroup *curve, GeometryOwnershipType ownership)
{
GeometrySet geometry_set;
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
component.replace(curve, ownership);
return geometry_set;
}
/* Clear the existing mesh and replace it with the given one. */
void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -299,6 +332,13 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
component.replace(mesh, ownership);
}
/* Clear the existing curve and replace it with the given one. */
void GeometrySet::replace_curve(SplineGroup *curve, GeometryOwnershipType ownership)
{
CurveComponent &component = this->get_component_for_write<CurveComponent>();
component.replace(curve, ownership);
}
/* Clear the existing point cloud and replace with the given one. */
void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
{
@@ -334,6 +374,13 @@ Volume *GeometrySet::get_volume_for_write()
return component.get_for_write();
}
/* Returns a mutable curve or null. No ownership is transferred. */
SplineGroup *GeometrySet::get_curve_for_write()
{
CurveComponent &component = this->get_component_for_write<CurveComponent>();
return component.get_for_write();
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -4946,6 +4946,10 @@ static void registerGeometryNodes()
register_node_type_geo_boolean();
register_node_type_geo_bounding_box();
register_node_type_geo_collection_info();
register_node_type_geo_curve_sample_points();
register_node_type_geo_curve_to_mesh();
register_node_type_geo_curve_transform_test();
register_node_type_geo_curve_trim();
register_node_type_geo_edge_split();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();

View File

@@ -0,0 +1,339 @@
/*
* 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_span.hh"
#include "BKE_spline.hh"
using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
Spline::Type Spline::type() const
{
return this->type_;
}
int Spline::evaluated_edges_size() const
{
const int points_len = this->evaluated_points_size();
return this->is_cyclic ? points_len : points_len - 1;
}
float Spline::length() const
{
return this->evaluated_lengths().last();
}
int Spline::segments_size() const
{
const int points_len = this->size();
return this->is_cyclic ? points_len : points_len - 1;
}
static void accumulate_lengths(Span<float3> positions,
const bool is_cyclic,
MutableSpan<float> lengths)
{
float length = 0.0f;
for (const int i : IndexRange(positions.size() - 1)) {
length += float3::distance(positions[i], positions[i + 1]);
lengths[i] = length;
}
if (is_cyclic) {
lengths.last() = length + float3::distance(positions.last(), positions.first());
}
}
/**
* Return non-owning access to the cache of accumulated lengths along the spline. Each item is the
* length of the subsequent segment, i.e. the first value is the length of the first segment rather
* than 0. This calculation is rather trivial, and only depends on the evaluated positions.
* However, the results are used often, so it makes sense to cache it.
*/
Span<float> Spline::evaluated_lengths() const
{
if (!this->length_cache_dirty_) {
return evaluated_lengths_cache_;
}
std::lock_guard lock{this->length_cache_mutex_};
if (!this->length_cache_dirty_) {
return evaluated_lengths_cache_;
}
const int total = this->evaluated_edges_size();
this->evaluated_lengths_cache_.resize(total);
Span<float3> positions = this->evaluated_positions();
accumulate_lengths(positions, this->is_cyclic, this->evaluated_lengths_cache_);
this->length_cache_dirty_ = false;
return evaluated_lengths_cache_;
}
static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next)
{
const float3 dir_prev = (middle - prev).normalized();
const float3 dir_next = (next - middle).normalized();
return (dir_prev + dir_next).normalized();
}
static void calculate_tangents(Span<float3> positions,
const bool is_cyclic,
MutableSpan<float3> tangents)
{
if (positions.size() == 1) {
return;
}
for (const int i : IndexRange(1, positions.size() - 2)) {
tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]);
}
if (is_cyclic) {
const float3 &second_to_last = positions[positions.size() - 2];
const float3 &last = positions.last();
const float3 &first = positions.first();
const float3 &second = positions[1];
tangents.first() = direction_bisect(last, first, second);
tangents.last() = direction_bisect(second_to_last, last, first);
}
else {
tangents.first() = (positions[1] - positions[0]).normalized();
tangents.last() = (positions.last() - positions[positions.size() - 1]).normalized();
}
}
/**
* Return non-owning access to the direction of the curve at each evaluated point.
*/
Span<float3> Spline::evaluated_tangents() const
{
if (!this->tangent_cache_dirty_) {
return evaluated_tangents_cache_;
}
std::lock_guard lock{this->tangent_cache_mutex_};
if (!this->tangent_cache_dirty_) {
return evaluated_tangents_cache_;
}
const int total = this->evaluated_points_size();
this->evaluated_tangents_cache_.resize(total);
Span<float3> positions = this->evaluated_positions();
calculate_tangents(positions, this->is_cyclic, this->evaluated_tangents_cache_);
this->correct_end_tangents();
this->tangent_cache_dirty_ = false;
return evaluated_tangents_cache_;
}
#if 0 /* Not supported yet, has errors. */
static float3 initial_normal(const float3 first_tangent)
{
/* TODO: Should be is "almost" zero. */
if (first_tangent.is_zero()) {
return float3(0.0f, 0.0f, 1.0f);
}
const float3 normal = float3::cross(first_tangent, float3(0.0f, 0.0f, 1.0f));
if (!normal.is_zero()) {
return normal.normalized();
}
return float3::cross(first_tangent, float3(0.0f, 1.0f, 0.0f)).normalized();
}
static float3 rotate_around_axis(const float3 dir, const float3 axis, const float angle)
{
BLI_ASSERT_UNIT_V3(axis);
const float3 scaled_axis = axis * float3::dot(dir, axis);
const float3 sub = dir - scaled_axis;
const float3 cross = float3::cross(sub, sub);
const float sin = std::sin(angle);
const float cos = std::cos(angle);
return (scaled_axis + sub * cos + cross * sin).normalized();
}
static float3 project_on_center_plane(const float3 vector, const float3 plane_normal)
{
BLI_ASSERT_UNIT_V3(plane_normal);
const float distance = float3::dot(vector, plane_normal);
const float3 projection_vector = plane_normal * -distance;
return vector + projection_vector;
}
static float3 propagate_normal(const float3 last_normal,
const float3 last_tangent,
const float3 current_tangent)
{
const float angle = angle_normalized_v3v3(last_tangent, current_tangent);
if (angle == 0.0f) {
return last_normal;
}
const float3 axis = float3::cross(last_tangent, current_tangent).normalized();
const float3 new_normal = rotate_around_axis(last_normal, axis, angle);
return project_on_center_plane(new_normal, current_tangent).normalized();
}
static void apply_rotation_gradient(Span<float3> tangents,
MutableSpan<float3> normals,
const float full_angle)
{
float remaining_rotation = full_angle;
float done_rotation = 0.0f;
for (const int i : IndexRange(1, normals.size() - 1)) {
if (angle_v3v3(tangents[i], tangents[i - 1]) < 0.001f) {
normals[i] = rotate_around_axis(normals[i], tangents[i], done_rotation);
}
else {
const float angle = remaining_rotation / (normals.size() - i);
normals[i] = rotate_around_axis(normals[i], tangents[i], angle + done_rotation);
remaining_rotation -= angle;
done_rotation += angle;
}
}
}
static void make_normals_cyclic(Span<float3> tangents, MutableSpan<float3> normals)
{
const float3 last_normal = propagate_normal(normals.last(), tangents.last(), tangents.first());
float angle = angle_normalized_v3v3(normals.first(), last_normal);
const float3 cross = float3::cross(normals.first(), last_normal);
if (float3::dot(cross, tangents.first()) <= 0.0f) {
angle = -angle;
}
apply_rotation_gradient(tangents, normals, -angle);
}
/* This algorithm is a copy from animation nodes bezier normal calculation.
* TODO: Explore different methods, this also doesn't work right now. */
static void calculate_normals_minimum_twist(Span<float3> tangents,
const bool is_cyclic,
MutableSpan<float3> normals)
{
if (normals.size() == 1) {
normals.first() = float3(1.0f, 0.0f, 0.0f);
return;
}
/* Start by calculating a simple normal for the first point. */
normals[0] = initial_normal(tangents[0]);
/* Then propogate that normal along the spline. */
for (const int i : IndexRange(1, normals.size() - 1)) {
normals[i] = propagate_normal(normals[i - 1], tangents[i - 1], tangents[i]);
}
if (is_cyclic) {
make_normals_cyclic(tangents, normals);
}
}
#endif
static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals)
{
for (const int i : normals.index_range()) {
normals[i] = float3::cross(tangents[i], float3(0.0f, 0.0f, 1.0f)).normalized();
}
}
/**
* Return non-owning access to the direction vectors perpendicular to the tangents at every
* evaluated point. The method used to generate the normal vectors depends on Spline.normal_mode.
*/
Span<float3> Spline::evaluated_normals() const
{
if (!this->normal_cache_dirty_) {
return evaluated_normals_cache_;
}
std::lock_guard lock{this->normal_cache_mutex_};
if (!this->normal_cache_dirty_) {
return evaluated_normals_cache_;
}
const int total = this->evaluated_points_size();
this->evaluated_normals_cache_.resize(total);
Span<float3> tangents = this->evaluated_tangents();
#if 0 /* Not supported yet, has errors. */
switch (this->normal_mode) {
case NormalCalculationMode::Minimum:
calculate_normals_minimum_twist(tangents, is_cyclic, this->evaluated_normals_cache_);
break;
case NormalCalculationMode::ZUp:
break;
case NormalCalculationMode::Tangent:
calculate_normals_tangent(tangents, this->evaluated_normals_cache_);
break;
}
#endif
calculate_normals_z_up(tangents, this->evaluated_normals_cache_);
this->normal_cache_dirty_ = false;
return evaluated_normals_cache_;
}
Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const
{
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
{
BLI_assert(length >= 0.0f && length <= this->length());
Span<float> lengths = this->evaluated_lengths();
const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length);
const int index = offset - lengths.begin();
const int next_index = (index == this->size() - 1) ? 0 : index + 1;
const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
const float factor = (length - previous_length) / (lengths[index] - previous_length);
return LookupResult{index, next_index, factor};
}
void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
{
Span<float3> positions = use_evaluated ? this->evaluated_positions() : this->positions();
for (const float3 &position : positions) {
minmax_v3v3_v3(min, max, position);
}
}

View File

@@ -0,0 +1,483 @@
/*
* 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_span.hh"
#include "BLI_task.hh"
#include "BKE_spline.hh"
using blender::Array;
using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
SplinePtr BezierSpline::copy() const
{
SplinePtr new_spline = std::make_unique<BezierSpline>(*this);
return new_spline;
}
int BezierSpline::size() const
{
const int size = this->positions_.size();
BLI_assert(this->handle_types_start_.size() == size);
BLI_assert(this->handle_positions_start_.size() == size);
BLI_assert(this->handle_types_end_.size() == size);
BLI_assert(this->handle_positions_end_.size() == size);
BLI_assert(this->radii_.size() == size);
BLI_assert(this->tilts_.size() == size);
return size;
}
int BezierSpline::resolution() const
{
return this->resolution_;
}
void BezierSpline::set_resolution(const int value)
{
this->resolution_ = value;
this->mark_cache_invalid();
}
MutableSpan<float3> BezierSpline::positions()
{
return this->positions_;
}
Span<float3> BezierSpline::positions() const
{
return this->positions_;
}
MutableSpan<float> BezierSpline::radii()
{
return this->radii_;
}
Span<float> BezierSpline::radii() const
{
return this->radii_;
}
MutableSpan<float> BezierSpline::tilts()
{
return this->tilts_;
}
Span<float> BezierSpline::tilts() const
{
return this->tilts_;
}
Span<BezierSpline::HandleType> BezierSpline::handle_types_start() const
{
return this->handle_types_start_;
}
MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_start()
{
return this->handle_types_start_;
}
Span<float3> BezierSpline::handle_positions_start() const
{
return this->handle_positions_start_;
}
MutableSpan<float3> BezierSpline::handle_positions_start()
{
return this->handle_positions_start_;
}
Span<BezierSpline::HandleType> BezierSpline::handle_types_end() const
{
return this->handle_types_end_;
}
MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_end()
{
return this->handle_types_end_;
}
Span<float3> BezierSpline::handle_positions_end() const
{
return this->handle_positions_end_;
}
MutableSpan<float3> BezierSpline::handle_positions_end()
{
return this->handle_positions_end_;
}
void BezierSpline::add_point(const float3 position,
const HandleType handle_type_start,
const float3 handle_position_start,
const HandleType handle_type_end,
const float3 handle_position_end,
const float radius,
const float tilt)
{
handle_types_start_.append(handle_type_start);
handle_positions_start_.append(handle_position_start);
positions_.append(position);
handle_types_end_.append(handle_type_end);
handle_positions_end_.append(handle_position_end);
radii_.append(radius);
tilts_.append(tilt);
}
void BezierSpline::drop_front(const int count)
{
BLI_assert(this->size() - count > 0);
this->handle_types_start_.remove(0, count);
this->handle_positions_start_.remove(0, count);
this->positions_.remove(0, count);
this->handle_types_end_.remove(0, count);
this->handle_positions_end_.remove(0, count);
this->radii_.remove(0, count);
this->tilts_.remove(0, count);
this->mark_cache_invalid();
}
void BezierSpline::drop_back(const int count)
{
const int new_size = this->size() - count;
BLI_assert(new_size > 0);
this->handle_types_start_.resize(new_size);
this->handle_positions_start_.resize(new_size);
this->positions_.resize(new_size);
this->handle_types_end_.resize(new_size);
this->handle_positions_end_.resize(new_size);
this->radii_.resize(new_size);
this->tilts_.resize(new_size);
this->mark_cache_invalid();
}
bool BezierSpline::point_is_sharp(const int index) const
{
return ELEM(handle_types_start_[index], HandleType::Vector, HandleType::Free) ||
ELEM(handle_types_end_[index], HandleType::Vector, HandleType::Free);
}
bool BezierSpline::handle_start_is_automatic(const int index) const
{
return ELEM(handle_types_start_[index], HandleType::Free, HandleType::Align);
}
bool BezierSpline::handle_end_is_automatic(const int index) const
{
return ELEM(handle_types_end_[index], HandleType::Free, HandleType::Align);
}
void BezierSpline::move_control_point(const int index, const float3 new_position)
{
const float3 position_delta = new_position - positions_[index];
if (!this->handle_start_is_automatic(index)) {
handle_positions_start_[index] += position_delta;
}
if (!this->handle_end_is_automatic(index)) {
handle_positions_end_[index] += position_delta;
}
positions_[index] = new_position;
}
bool BezierSpline::segment_is_vector(const int index) const
{
if (index == this->size() - 1) {
BLI_assert(this->is_cyclic);
return this->handle_types_end_.last() == HandleType::Vector &&
this->handle_types_start_.first() == HandleType::Vector;
}
return this->handle_types_end_[index] == HandleType::Vector &&
this->handle_types_start_[index + 1] == HandleType::Vector;
}
void BezierSpline::mark_cache_invalid()
{
this->offset_cache_dirty_ = true;
this->position_cache_dirty_ = true;
this->mapping_cache_dirty_ = true;
this->tangent_cache_dirty_ = true;
this->normal_cache_dirty_ = true;
this->length_cache_dirty_ = true;
}
int BezierSpline::evaluated_points_size() const
{
const int points_len = this->size();
BLI_assert(points_len > 0);
const int last_offset = this->control_point_offsets().last();
if (this->is_cyclic) {
return last_offset + (this->segment_is_vector(points_len - 1) ? 0 : this->resolution_);
}
return last_offset + 1;
}
/**
* If the spline is not cyclic, the direction for the first and last points is just the
* direction formed by the corresponding handles and control points. In the unlikely situation
* that the handles define a zero direction, fallback to using the direction defined by the
* first and last evaluated segments already calculated in #Spline::evaluated_tangents().
*/
void BezierSpline::correct_end_tangents() const
{
MutableSpan<float3> tangents(this->evaluated_tangents_cache_);
if (handle_positions_start_.first() != positions_.first()) {
tangents.first() = (positions_.first() - handle_positions_start_.first()).normalized();
}
if (handle_positions_end_.last() != positions_.last()) {
tangents.last() = (handle_positions_end_.last() - positions_.last()).normalized();
}
}
static void bezier_forward_difference_3d(const float3 &point_0,
const float3 &point_1,
const float3 &point_2,
const float3 &point_3,
MutableSpan<float3> result)
{
const float len = static_cast<float>(result.size());
const float len_squared = len * len;
const float len_cubed = len_squared * len;
BLI_assert(len > 0.0f);
const float3 rt1 = 3.0f * (point_1 - point_0) / len;
const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) / len_squared;
const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) / len_cubed;
float3 q0 = point_0;
float3 q1 = rt1 + rt2 + rt3;
float3 q2 = 2.0f * rt2 + 6.0f * rt3;
float3 q3 = 6.0f * rt3;
for (const int i : result.index_range()) {
result[i] = q0;
q0 += q1;
q1 += q2;
q2 += q3;
}
}
void BezierSpline::evaluate_bezier_segment(const int index,
const int next_index,
MutableSpan<float3> positions) const
{
if (this->segment_is_vector(index)) {
positions.first() = this->positions_[index];
}
else {
bezier_forward_difference_3d(this->positions_[index],
this->handle_positions_end_[index],
this->handle_positions_start_[next_index],
this->positions_[next_index],
positions);
}
}
/**
* Returns access to a cache of offsets into the evaluated point array for each control point.
* This is important because while most control point edges generate the number of edges specified
* by the resolution, vector segments only generate one edge.
*/
Span<int> BezierSpline::control_point_offsets() const
{
if (!this->offset_cache_dirty_) {
return this->offset_cache_;
}
std::lock_guard lock{this->offset_cache_mutex_};
if (!this->offset_cache_dirty_) {
return this->offset_cache_;
}
const int points_len = this->size();
this->offset_cache_.resize(points_len);
MutableSpan<int> offsets = this->offset_cache_;
int offset = 0;
for (const int i : IndexRange(points_len - 1)) {
offsets[i] = offset;
offset += this->segment_is_vector(i) ? 1 : this->resolution_;
}
offsets.last() = offset;
this->offset_cache_dirty_ = false;
return offsets;
}
/**
* Returns non-owning access to an array of values ontains the information necessary to
* interpolate values from the original control points to evaluated points. The control point
* index is the integer part of each value, and the factor used for interpolating to the next
* control point is the remaining factional part.
*/
Span<float> BezierSpline::evaluated_mappings() const
{
if (!this->mapping_cache_dirty_) {
return this->evaluated_mapping_cache_;
}
std::lock_guard lock{this->mapping_cache_mutex_};
if (!this->mapping_cache_dirty_) {
return this->evaluated_mapping_cache_;
}
const int size = this->size();
const int eval_size = this->evaluated_points_size();
this->evaluated_mapping_cache_.resize(eval_size);
MutableSpan<float> mappings = this->evaluated_mapping_cache_;
Span<int> offsets = this->control_point_offsets();
Span<float> lengths = this->evaluated_lengths();
/* Subtract one from the index into the lengths array to get the length
* at the start point rather than the length at the end of the edge. */
const float first_segment_len = lengths[offsets[1] - 1];
for (const int eval_index : IndexRange(0, offsets[1])) {
const float point_len = eval_index == 0 ? 0.0f : lengths[eval_index - 1];
const float length_factor = (first_segment_len == 0.0f) ? 0.0f : 1.0f / first_segment_len;
mappings[eval_index] = point_len * length_factor;
}
const int grain_size = std::max(512 / this->resolution_, 1);
blender::parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) {
for (const int i : range) {
const float segment_start_len = lengths[offsets[i] - 1];
const float segment_end_len = lengths[offsets[i + 1] - 1];
const float segment_len = segment_end_len - segment_start_len;
const float length_factor = (segment_len == 0.0f) ? 0.0f : 1.0f / segment_len;
for (const int eval_index : IndexRange(offsets[i], offsets[i + 1] - offsets[i])) {
const float factor = (lengths[eval_index - 1] - segment_start_len) * length_factor;
mappings[eval_index] = i + factor;
}
}
});
if (this->is_cyclic) {
const float segment_start_len = lengths[offsets.last() - 1];
const float segment_end_len = this->length();
const float segment_len = segment_end_len - segment_start_len;
const float length_factor = (segment_len == 0.0f) ? 0.0f : 1.0f / segment_len;
for (const int eval_index : IndexRange(offsets.last(), eval_size - offsets.last())) {
const float factor = (lengths[eval_index - 1] - segment_start_len) * length_factor;
mappings[eval_index] = size - 1 + factor;
}
mappings.last() = 0.0f;
}
else {
mappings.last() = size - 1;
}
this->mapping_cache_dirty_ = false;
return this->evaluated_mapping_cache_;
}
Span<float3> BezierSpline::evaluated_positions() const
{
if (!this->position_cache_dirty_) {
return this->evaluated_position_cache_;
}
std::lock_guard lock{this->position_cache_mutex_};
if (!this->position_cache_dirty_) {
return this->evaluated_position_cache_;
}
const int eval_total = this->evaluated_points_size();
this->evaluated_position_cache_.resize(eval_total);
MutableSpan<float3> positions = this->evaluated_position_cache_;
Span<int> offsets = this->control_point_offsets();
BLI_assert(offsets.last() <= eval_total);
const int grain_size = std::max(512 / this->resolution_, 1);
blender::parallel_for(IndexRange(this->size() - 1), grain_size, [&](IndexRange range) {
for (const int i : range) {
this->evaluate_bezier_segment(
i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i]));
}
});
const int i_last = this->size() - 1;
if (this->is_cyclic) {
this->evaluate_bezier_segment(i_last, 0, positions.slice(offsets.last(), this->resolution_));
}
else {
/* Since evaulating the bezier segment doesn't add the final point,
* it must be added manually in the non-cyclic case. */
positions.last() = this->positions_.last();
}
this->position_cache_dirty_ = false;
return this->evaluated_position_cache_;
}
BezierSpline::InterpolationData BezierSpline::interpolation_data_from_map(const float map) const
{
const int points_len = this->size();
const int index = std::floor(map);
if (index == points_len) {
BLI_assert(this->is_cyclic);
return InterpolationData{points_len - 1, 0, 1.0f};
}
if (index == points_len - 1) {
return InterpolationData{points_len - 2, points_len - 1, 1.0f};
}
return InterpolationData{index, index + 1, map - index};
}
template<typename T>
static void interpolate_to_evaluated_points_impl(Span<float> mappings,
const blender::VArray<T> &source_data,
MutableSpan<T> result_data)
{
const int points_len = source_data.size();
/* TODO: Use a set of functions mix2 in attribute_math instead of DefaultMixer. */
blender::attribute_math::DefaultMixer<T> mixer(result_data);
for (const int i : result_data.index_range()) {
const int index = std::floor(mappings[i]);
const int next_index = (index == points_len - 1) ? 0 : index + 1;
const float factor = mappings[i] - index;
const T &value = source_data[index];
const T &next_value = source_data[next_index];
mixer.mix_in(i, value, 1.0f - factor);
mixer.mix_in(i, next_value, factor);
}
mixer.finalize();
}
blender::fn::GVArrayPtr BezierSpline::interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const
{
BLI_assert(source_data.size() == this->size());
Span<float> mappings = this->evaluated_mappings();
blender::fn::GVArrayPtr new_varray;
blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
Array<T> values(this->evaluated_points_size());
interpolate_to_evaluated_points_impl<T>(mappings, source_data.typed<T>(), values);
new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
std::move(values));
}
});
return new_varray;
}

View File

@@ -0,0 +1,205 @@
/*
* 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_listbase.h"
#include "BLI_span.hh"
#include "DNA_curve_types.h"
#include "BKE_curve.h"
#include "BKE_spline.hh"
using blender::float3;
using blender::float4x4;
using blender::Span;
SplineGroup *SplineGroup::copy()
{
SplineGroup *new_curve = new SplineGroup();
for (SplinePtr &spline : this->splines) {
new_curve->splines.append(spline->copy());
}
return new_curve;
}
void SplineGroup::translate(const float3 translation)
{
for (SplinePtr &spline : this->splines) {
for (float3 &position : spline->positions()) {
position += translation;
}
if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) {
for (float3 &handle_position : bezier_spline->handle_positions_start()) {
handle_position += translation;
}
for (float3 &handle_position : bezier_spline->handle_positions_end()) {
handle_position += translation;
}
}
spline->mark_cache_invalid();
}
}
void SplineGroup::transform(const float4x4 &matrix)
{
for (SplinePtr &spline : this->splines) {
for (float3 &position : spline->positions()) {
position = matrix * position;
}
if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) {
for (float3 &handle_position : bezier_spline->handle_positions_start()) {
handle_position = matrix * handle_position;
}
for (float3 &handle_position : bezier_spline->handle_positions_end()) {
handle_position = matrix * handle_position;
}
}
spline->mark_cache_invalid();
}
}
void SplineGroup::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
{
for (const SplinePtr &spline : this->splines) {
spline->bounds_min_max(min, max, use_evaluated);
}
}
static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
{
switch (dna_handle_type) {
case HD_FREE:
return BezierSpline::Free;
case HD_AUTO:
return BezierSpline::Auto;
case HD_VECT:
return BezierSpline::Vector;
case HD_ALIGN:
return BezierSpline::Align;
case HD_AUTO_ANIM:
return BezierSpline::Auto;
case HD_ALIGN_DOUBLESIDE:
return BezierSpline::Align;
}
BLI_assert_unreachable();
return BezierSpline::Auto;
}
static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode)
{
switch (twist_mode) {
case CU_TWIST_Z_UP:
return Spline::NormalCalculationMode::ZUp;
case CU_TWIST_MINIMUM:
return Spline::NormalCalculationMode::Minimum;
case CU_TWIST_TANGENT:
return Spline::NormalCalculationMode::Tangent;
}
BLI_assert_unreachable();
return Spline::NormalCalculationMode::Minimum;
}
static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag)
{
switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) {
case CU_NURB_ENDPOINT:
return NURBSpline::KnotsMode::EndPoint;
case CU_NURB_BEZIER:
return NURBSpline::KnotsMode::Bezier;
default:
return NURBSpline::KnotsMode::Normal;
}
BLI_assert_unreachable();
return NURBSpline::KnotsMode::Normal;
}
SplineGroup *dcurve_from_dna_curve(const Curve &dna_curve)
{
SplineGroup *curve = new SplineGroup();
const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve));
curve->splines.reserve(BLI_listbase_count(nurbs));
LISTBASE_FOREACH (const Nurb *, nurb, nurbs) {
switch (nurb->type) {
case CU_BEZIER: {
std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
spline->set_resolution(nurb->resolu);
spline->is_cyclic = nurb->flagu & CU_NURB_CYCLIC;
/* TODO: Optimize by reserving the correct size. */
for (const BezTriple &bezt : Span(nurb->bezt, nurb->pntsu)) {
spline->add_point(bezt.vec[1],
handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1),
bezt.vec[0],
handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2),
bezt.vec[2],
bezt.radius,
bezt.tilt);
}
curve->splines.append(std::move(spline));
break;
}
case CU_NURBS: {
std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>();
spline->set_resolution(nurb->resolu);
spline->is_cyclic = nurb->flagu & CU_NURB_CYCLIC;
spline->set_order(nurb->orderu);
spline->knots_mode = knots_mode_from_dna_nurb(nurb->flagu);
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]);
}
curve->splines.append(std::move(spline));
break;
}
case CU_POLY: {
std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
spline->is_cyclic = nurb->flagu & CU_NURB_CYCLIC;
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
spline->add_point(bp.vec, bp.radius, bp.tilt);
}
curve->splines.append(std::move(spline));
break;
}
default: {
BLI_assert_unreachable();
break;
}
}
}
/* Note: Normal mode is stored separately in each spline to facilitate combining splines
* from multiple curve objects, where the value may be different. */
const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve(
dna_curve.twist_mode);
for (SplinePtr &spline : curve->splines) {
spline->normal_mode = normal_mode;
}
return curve;
}
/** \} */

View File

@@ -0,0 +1,444 @@
/*
* 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_span.hh"
#include "BLI_virtual_array.hh"
#include "BKE_attribute_math.hh"
#include "BKE_spline.hh"
using blender::Array;
using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
SplinePtr NURBSpline::copy() const
{
SplinePtr new_spline = std::make_unique<NURBSpline>(*this);
return new_spline;
}
int NURBSpline::size() const
{
const int size = this->positions_.size();
BLI_assert(this->radii_.size() == size);
BLI_assert(this->tilts_.size() == size);
BLI_assert(this->weights_.size() == size);
return size;
}
int NURBSpline::resolution() const
{
return this->resolution_;
}
void NURBSpline::set_resolution(const int value)
{
this->resolution_ = value;
this->mark_cache_invalid();
}
uint8_t NURBSpline::order() const
{
return this->order_;
}
void NURBSpline::set_order(const uint8_t value)
{
BLI_assert(value >= 2 && value <= 6);
this->order_ = value;
this->mark_cache_invalid();
}
void NURBSpline::add_point(const float3 position,
const float radius,
const float tilt,
const float weight)
{
this->positions_.append(position);
this->radii_.append(radius);
this->tilts_.append(tilt);
this->weights_.append(weight);
this->knots_dirty_ = true;
}
void NURBSpline::drop_front(const int count)
{
BLI_assert(this->size() - count > 0);
this->positions_.remove(0, count);
this->radii_.remove(0, count);
this->tilts_.remove(0, count);
this->weights_.remove(0, count);
this->mark_cache_invalid();
}
void NURBSpline::drop_back(const int count)
{
const int new_size = this->size() - count;
BLI_assert(new_size > 0);
this->positions_.resize(new_size);
this->radii_.resize(new_size);
this->tilts_.resize(new_size);
this->weights_.resize(new_size);
this->mark_cache_invalid();
}
MutableSpan<float3> NURBSpline::positions()
{
return this->positions_;
}
Span<float3> NURBSpline::positions() const
{
return this->positions_;
}
MutableSpan<float> NURBSpline::radii()
{
return this->radii_;
}
Span<float> NURBSpline::radii() const
{
return this->radii_;
}
MutableSpan<float> NURBSpline::tilts()
{
return this->tilts_;
}
Span<float> NURBSpline::tilts() const
{
return this->tilts_;
}
MutableSpan<float> NURBSpline::weights()
{
return this->weights_;
}
Span<float> NURBSpline::weights() const
{
return this->weights_;
}
void NURBSpline::mark_cache_invalid()
{
this->basis_cache_dirty_ = true;
this->position_cache_dirty_ = true;
this->tangent_cache_dirty_ = true;
this->normal_cache_dirty_ = true;
this->length_cache_dirty_ = true;
}
int NURBSpline::evaluated_points_size() const
{
return this->resolution_ * this->segments_size();
}
void NURBSpline::correct_end_tangents() const
{
}
bool NURBSpline::check_valid_size_and_order() const
{
if (this->size() < this->order_) {
return false;
}
if (!this->is_cyclic && this->knots_mode == KnotsMode::Bezier) {
if (this->order_ == 4) {
if (this->size() < 5) {
return false;
}
}
else if (this->order_ != 3) {
return false;
}
}
return true;
}
int NURBSpline::knots_size() const
{
const int size = this->size() + this->order_;
return this->is_cyclic ? size + this->order_ - 1 : size;
}
void NURBSpline::calculate_knots() const
{
const KnotsMode mode = this->knots_mode;
const int length = this->size();
const int order = this->order_;
this->knots_.resize(this->knots_size());
MutableSpan<float> knots = this->knots_;
if (mode == NURBSpline::KnotsMode::Normal || this->is_cyclic) {
for (const int i : knots.index_range()) {
knots[i] = static_cast<float>(i);
}
}
else if (mode == NURBSpline::KnotsMode::EndPoint) {
float k = 0.0f;
for (const int i : IndexRange(1, knots.size())) {
knots[i - 1] = k;
if (i >= order && i <= length) {
k += 1.0f;
}
}
}
else if (mode == NURBSpline::KnotsMode::Bezier) {
BLI_assert(ELEM(order, 3, 4));
if (order == 3) {
float k = 0.6f;
for (const int i : knots.index_range()) {
if (i >= order && i <= length) {
k += 0.5f;
}
knots[i] = std::floor(k);
}
}
else {
float k = 0.34f;
for (const int i : knots.index_range()) {
knots[i] = std::floor(k);
k += 1.0f / 3.0f;
}
}
}
if (this->is_cyclic) {
const int b = length + order - 1;
if (order > 2) {
for (const int i : IndexRange(1, order - 2)) {
if (knots[b] != knots[b - i]) {
if (i == order - 1) {
knots[length + order - 2] += 1.0f;
break;
}
}
}
}
int c = order;
for (int i = b; i < this->knots_size(); i++) {
knots[i] = knots[i - 1] + (knots[c] - knots[c - 1]);
c--;
}
}
}
Span<float> NURBSpline::knots() const
{
if (!this->knots_dirty_) {
BLI_assert(this->knots_.size() == this->size() + this->order_);
return this->knots_;
}
std::lock_guard lock{this->knots_mutex_};
if (!this->knots_dirty_) {
BLI_assert(this->knots_.size() == this->size() + this->order_);
return this->knots_;
}
this->calculate_knots();
this->knots_dirty_ = false;
return this->knots_;
}
static void calculate_basis_for_point(const float parameter,
const int points_len,
const int order,
Span<float> knots,
MutableSpan<float> basis_buffer,
NURBSpline::BasisCache &basis_cache)
{
/* Clamp parameter due to floating point inaccuracy. TODO: Look into using doubles. */
const float t = std::clamp(parameter, knots[0], knots[points_len + order - 1]);
int start = 0;
int end = 0;
for (const int i : IndexRange(points_len + order - 1)) {
const bool knots_equal = knots[i] == knots[i + 1];
if (knots_equal || t < knots[i] || t > knots[i + 1]) {
basis_buffer[i] = 0.0f;
continue;
}
basis_buffer[i] = 1.0f;
start = std::max(i - order - 1, 0);
end = i;
basis_buffer.slice(i + 1, points_len + order - 1 - i).fill(0.0f);
break;
}
basis_buffer[points_len + order - 1] = 0.0f;
for (const int i_order : IndexRange(2, order - 1)) {
if (end + i_order >= points_len + order) {
end = points_len + order - 1 - i_order;
}
for (const int i : IndexRange(start, end - start + 1)) {
float new_basis = 0.0f;
if (basis_buffer[i] != 0.0f) {
new_basis += ((t - knots[i]) * basis_buffer[i]) / (knots[i + i_order - 1] - knots[i]);
}
if (basis_buffer[i + 1] != 0.0f) {
new_basis += ((knots[i + i_order] - t) * basis_buffer[i + 1]) /
(knots[i + i_order] - knots[i + 1]);
}
basis_buffer[i] = new_basis;
}
}
/* Shrink the range of calculated values to avoid storing unecessary zeros. */
while (basis_buffer[start] == 0.0f && start < end) {
start++;
}
while (basis_buffer[end] == 0.0f && end > start) {
end--;
}
basis_cache.weights.clear();
basis_cache.weights.extend(basis_buffer.slice(start, end - start + 1));
basis_cache.start_index = start;
}
void NURBSpline::calculate_basis_cache() const
{
if (!this->basis_cache_dirty_) {
return;
}
std::lock_guard lock{this->basis_cache_mutex_};
if (!this->basis_cache_dirty_) {
return;
}
const int points_len = this->size();
const int evaluated_len = this->evaluated_points_size();
this->basis_cache_.resize(evaluated_len);
const int order = this->order();
Span<float> control_weights = this->weights();
Span<float> knots = this->knots();
MutableSpan<BasisCache> basis_cache(this->basis_cache_);
/* This buffer is reused by each basis calculation to store temporary values.
* Theoretically it could be optimized away in the future. */
Array<float> basis_buffer(this->knots_size());
const float start = knots[order - 1];
const float end = this->is_cyclic ? knots[points_len + order - 1] : knots[points_len];
const float step = (end - start) / (evaluated_len - (this->is_cyclic ? 0 : 1));
float parameter = start;
for (const int i : IndexRange(evaluated_len)) {
BasisCache &basis = basis_cache[i];
calculate_basis_for_point(parameter,
points_len + (this->is_cyclic ? order - 1 : 0),
order,
knots,
basis_buffer,
basis);
BLI_assert(basis.weights.size() <= order);
for (const int j : basis.weights.index_range()) {
const int point_index = (basis.start_index + j) % points_len;
basis.weights[j] *= control_weights[point_index];
}
parameter += step;
}
this->basis_cache_dirty_ = false;
}
template<typename T>
void interpolate_to_evaluated_points_impl(Span<NURBSpline::BasisCache> weights,
const blender::VArray<T> &source_data,
MutableSpan<T> result_data)
{
const int points_len = source_data.size();
BLI_assert(result_data.size() == weights.size());
blender::attribute_math::DefaultMixer<T> mixer(result_data);
for (const int i : result_data.index_range()) {
Span<float> point_weights = weights[i].weights;
const int start_index = weights[i].start_index;
for (const int j : point_weights.index_range()) {
const int point_index = (start_index + j) % points_len;
mixer.mix_in(i, source_data[point_index], point_weights[j]);
}
}
mixer.finalize();
}
blender::fn::GVArrayPtr NURBSpline::interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const
{
BLI_assert(source_data.size() == this->size());
this->calculate_basis_cache();
Span<BasisCache> weights(this->basis_cache_);
blender::fn::GVArrayPtr new_varray;
blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
Array<T> values(this->evaluated_points_size());
interpolate_to_evaluated_points_impl<T>(weights, source_data.typed<T>(), values);
new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
std::move(values));
}
});
return new_varray;
}
Span<float3> NURBSpline::evaluated_positions() const
{
if (!this->position_cache_dirty_) {
return this->evaluated_position_cache_;
}
std::lock_guard lock{this->position_cache_mutex_};
if (!this->position_cache_dirty_) {
return this->evaluated_position_cache_;
}
const int total = this->evaluated_points_size();
this->evaluated_position_cache_.resize(total);
blender::fn::GVArray_For_Span<float3> positions_varray(this->positions_.as_span());
blender::fn::GVArrayPtr evaluated_positions_varray = this->interpolate_to_evaluated_points(
positions_varray);
/* TODO: Avoid copying. */
Span<float3> evaluated_positions =
evaluated_positions_varray->typed<float3>()->get_internal_span();
for (const int i : IndexRange(total)) {
this->evaluated_position_cache_[i] = evaluated_positions[i];
}
this->position_cache_dirty_ = false;
return this->evaluated_position_cache_;
}

View File

@@ -0,0 +1,144 @@
/*
* 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_span.hh"
#include "BLI_virtual_array.hh"
#include "BKE_spline.hh"
using blender::float3;
using blender::MutableSpan;
using blender::Span;
SplinePtr PolySpline::copy() const
{
SplinePtr new_spline = std::make_unique<PolySpline>(*this);
return new_spline;
}
int PolySpline::size() const
{
const int size = this->positions_.size();
BLI_assert(this->radii_.size() == size);
BLI_assert(this->tilts_.size() == size);
return size;
}
int PolySpline::resolution() const
{
return 1;
}
void PolySpline::set_resolution(const int UNUSED(value))
{
/* Poly curve has no resolution, there is just one evaluated point per control point. */
}
void PolySpline::add_point(const float3 position, const float radius, const float tilt)
{
this->positions_.append(position);
this->radii_.append(radius);
this->tilts_.append(tilt);
}
void PolySpline::drop_front(const int count)
{
BLI_assert(this->size() - count > 0);
this->positions_.remove(0, count);
this->radii_.remove(0, count);
this->tilts_.remove(0, count);
this->mark_cache_invalid();
}
void PolySpline::drop_back(const int count)
{
const int new_size = this->size() - count;
BLI_assert(new_size > 0);
this->positions_.resize(new_size);
this->radii_.resize(new_size);
this->tilts_.resize(new_size);
this->mark_cache_invalid();
}
MutableSpan<float3> PolySpline::positions()
{
return this->positions_;
}
Span<float3> PolySpline::positions() const
{
return this->positions_;
}
MutableSpan<float> PolySpline::radii()
{
return this->radii_;
}
Span<float> PolySpline::radii() const
{
return this->radii_;
}
MutableSpan<float> PolySpline::tilts()
{
return this->tilts_;
}
Span<float> PolySpline::tilts() const
{
return this->tilts_;
}
void PolySpline::mark_cache_invalid()
{
this->tangent_cache_dirty_ = true;
this->normal_cache_dirty_ = true;
this->length_cache_dirty_ = true;
}
int PolySpline::evaluated_points_size() const
{
return this->size();
}
void PolySpline::correct_end_tangents() const
{
}
Span<float3> PolySpline::evaluated_positions() const
{
return this->positions();
}
/**
* Poly spline interpolation from control points to evaluated points is a special case, since the
* result data is the same as the input data.
*/
blender::fn::GVArrayPtr PolySpline::interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const
{
BLI_assert(source_data.size() == this->size());
/* TODO: Must make sure this ownership idea works. */
if (source_data.is_span()) {
return std::make_unique<blender::fn::GVArray_For_GSpan>(source_data.get_internal_span());
}
// if (source_data.is_single()) {
// BUFFER_FOR_CPP_TYPE_VALUE(source_data.type(), value);
// source_data.get_internal_single(value);
// return std::make_unique<blender::fn::GVArray_For_SingleValue>(
// source_data.type(), source_data.size(), value);
// }
return {};
}

View File

@@ -245,6 +245,13 @@ struct float3 {
return result;
}
static float3 cross(const float3 &a, const float3 &b)
{
float3 result;
cross_v3_v3v3(result, a, b);
return result;
}
static float3 project(const float3 &a, const float3 &b)
{
float3 result;

View File

@@ -45,6 +45,37 @@ struct float4x4 {
return mat;
}
static float4x4 from_normalized_axis_data(const float3 location,
const float3 forward,
const float3 up)
{
BLI_ASSERT_UNIT_V3(forward);
BLI_ASSERT_UNIT_V3(up);
float4x4 matrix;
const float3 cross = float3::cross(forward, up);
matrix.values[0][0] = forward.x;
matrix.values[1][0] = cross.x;
matrix.values[2][0] = up.x;
matrix.values[3][0] = location.x;
matrix.values[0][1] = forward.y;
matrix.values[1][1] = cross.y;
matrix.values[2][1] = up.y;
matrix.values[3][1] = location.y;
matrix.values[0][2] = forward.z;
matrix.values[1][2] = cross.z;
matrix.values[2][2] = up.z;
matrix.values[3][2] = location.z;
matrix.values[0][3] = 0.0f;
matrix.values[1][3] = 0.0f;
matrix.values[2][3] = 0.0f;
matrix.values[3][3] = 1.0f;
return matrix;
}
static float4x4 identity()
{
float4x4 mat;
@@ -116,6 +147,19 @@ struct float4x4 {
return scale;
}
void apply_scale(const float scale)
{
values[0][0] *= scale;
values[0][1] *= scale;
values[0][2] *= scale;
values[1][0] *= scale;
values[1][1] *= scale;
values[1][2] *= scale;
values[2][0] *= scale;
values[2][1] *= scale;
values[2][2] *= scale;
}
float4x4 inverted() const
{
float4x4 result;

View File

@@ -714,6 +714,10 @@ class GVArray_For_Span : public GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>
: GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>>(data.size(), data)
{
}
GVArray_For_Span(const MutableSpan<T> data)
: GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>>(data.size(), data.as_span())
{
}
};
/* Same as VMutableArray_For_MutableSpan, but for a generic virtual array. */

View File

@@ -1196,6 +1196,16 @@ typedef struct NodeInputString {
char *string;
} NodeInputString;
typedef struct NodeGeometryCurveTrim {
/* GeometryNodeCurveTrimMode. */
uint8_t mode;
} NodeGeometryCurveTrim;
typedef struct NodeGeometryCurveSamplePoints {
/* GeometryNodeCurveSamplePointsMode. */
uint8_t mode;
} NodeGeometryCurveSamplePoints;
typedef struct NodeGeometryRotatePoints {
/* GeometryNodeRotatePointsType */
uint8_t type;
@@ -1708,6 +1718,16 @@ typedef enum GeometryNodeAttributeProximityTargetType {
GEO_NODE_ATTRIBUTE_PROXIMITY_TARGET_GEOMETRY_ELEMENT_FACES = 2,
} GeometryNodeAttributeProximityTargetType;
typedef enum GeometryNodeCurveTrimMode {
GEO_NODE_CURVE_TRIM_FACTOR = 0,
GEO_NODE_CURVE_TRIM_LENGTH = 1,
} GeometryNodeCurveTrimMode;
typedef enum GeometryNodeCurveSamplePointsMode {
GEO_NODE_CURVE_SAMPLE_POINTS_COUNT = 0,
GEO_NODE_CURVE_SAMPLE_POINTS_LENGTH = 1,
} GeometryNodeCurveSamplePointsMode;
/* Boolean Node */
typedef enum GeometryNodeBooleanOperation {
GEO_NODE_BOOLEAN_INTERSECT = 0,

View File

@@ -58,7 +58,7 @@ const EnumPropertyItem rna_enum_attribute_domain_items[] = {
{ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"},
/* Not implement yet */
// {ATTR_DOMAIN_GRIDS, "GRIDS", 0, "Grids", "Attribute on mesh multires grids"},
{ATTR_DOMAIN_CURVE, "CURVE", 0, "Curve", "Attribute on hair curve"},
{ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"},
{0, NULL, 0, NULL, NULL},
};
@@ -68,6 +68,7 @@ const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = {
{ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"},
{ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"},
{ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"},
{ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"},
{0, NULL, 0, NULL, NULL},
};

View File

@@ -9586,6 +9586,58 @@ static void def_geo_attribute_separate_xyz(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_curve_trim(StructRNA *srna)
{
PropertyRNA *prop;
static EnumPropertyItem mode_items[] = {
{GEO_NODE_CURVE_TRIM_FACTOR,
"FACTOR",
0,
"Factor",
"Remove a portion of each spline's total length"},
{GEO_NODE_CURVE_TRIM_LENGTH,
"LENGTH",
0,
"Length",
"Remove the specified length from the beginning and end of the curve"},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryCurveTrim", "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 amount of each spline to remove");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_curve_sample_points(StructRNA *srna)
{
PropertyRNA *prop;
static EnumPropertyItem mode_items[] = {
{GEO_NODE_CURVE_SAMPLE_POINTS_COUNT,
"COUNT",
0,
"Count",
"Distribute the specified number of points along the curve"},
{GEO_NODE_CURVE_SAMPLE_POINTS_LENGTH,
"LENGTH",
0,
"Length",
"Add points to the curve at the specified distance from each other"},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryCurveTrim", "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 amount of points to sample");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_mesh_circle(StructRNA *srna)
{
PropertyRNA *prop;

View File

@@ -3046,6 +3046,10 @@ static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bma
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
}
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_CURVE &&
!ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
}
}
const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UNUSED(C),
@@ -3091,6 +3095,11 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UN
continue;
}
}
if (component_type == GEO_COMPONENT_TYPE_CURVE) {
if (!ELEM(item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
continue;
}
}
if (item->value == ATTR_DOMAIN_POINT && component_type == GEO_COMPONENT_TYPE_MESH) {
RNA_enum_item_add(&item_array, &items_len, &mesh_vertex_domain_item);
}
@@ -7485,6 +7494,11 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
ICON_POINTCLOUD_DATA,
"Point Cloud",
"Point cloud component containing only point data"},
{GEO_COMPONENT_TYPE_CURVE,
"CURVE",
ICON_CURVE_DATA,
"Curve",
"Curve component containing spline and control point data"},
{GEO_COMPONENT_TYPE_INSTANCES,
"INSTANCES",
ICON_EMPTY_AXIS,

View File

@@ -185,7 +185,7 @@ static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Objec
if (object.type == OB_EMPTY && object.instance_collection != nullptr) {
add_collection_relation(ctx, *object.instance_collection);
}
else if (ELEM(object.type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) {
else if (ELEM(object.type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVE)) {
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_GEOMETRY, "Nodes Modifier");
DEG_add_customdata_mask(ctx->node, &object, &dependency_data_mask);
}
@@ -1577,9 +1577,10 @@ ModifierTypeInfo modifierType_Nodes = {
/* srna */ &RNA_NodesModifier,
/* type */ eModifierTypeType_Constructive,
/* flags */
static_cast<ModifierTypeFlag>(
eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping),
static_cast<ModifierTypeFlag>(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs |
eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode |
eModifierTypeFlag_SupportsMapping),
/* icon */ ICON_NODETREE,
/* copyData */ copyData,

View File

@@ -159,6 +159,10 @@ set(SRC
geometry/nodes/node_geo_bounding_box.cc
geometry/nodes/node_geo_collection_info.cc
geometry/nodes/node_geo_common.cc
geometry/nodes/node_geo_curve_sample_points.cc
geometry/nodes/node_geo_curve_to_mesh.cc
geometry/nodes/node_geo_curve_transform_test.cc
geometry/nodes/node_geo_curve_trim.cc
geometry/nodes/node_geo_edge_split.cc
geometry/nodes/node_geo_is_viewport.cc
geometry/nodes/node_geo_join_geometry.cc

View File

@@ -47,6 +47,10 @@ void register_node_type_geo_attribute_remove(void);
void register_node_type_geo_boolean(void);
void register_node_type_geo_bounding_box(void);
void register_node_type_geo_collection_info(void);
void register_node_type_geo_curve_sample_points(void);
void register_node_type_geo_curve_to_mesh(void);
void register_node_type_geo_curve_transform_test(void);
void register_node_type_geo_curve_trim(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);

View File

@@ -311,6 +311,10 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range,
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "")
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TRANSFORM_TEST, 0, "TRANSFORM_TEST", TransformTest, "Transform Test", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "")
DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE_POINTS, def_geo_curve_sample_points, "CURVE_SAMPLE_POINTS", CurveSamplePoints, "Curve Sample Points", "")
/* undefine macros */
#undef DefNode

View File

@@ -183,6 +183,9 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
align_rotations_on_component(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
if (geometry_set.has<CurveComponent>()) {
align_rotations_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -255,6 +255,9 @@ static void geo_node_attribute_clamp_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
clamp_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
clamp_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -104,6 +104,9 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
if (geometry_set.has<CurveComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
}
params.set_output("Geometry", std::move(geometry_set));
}

View File

@@ -127,6 +127,9 @@ static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
combine_attributes(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
combine_attributes(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -334,6 +334,9 @@ static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_compare_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
attribute_compare_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -163,6 +163,14 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
data_type,
domain);
}
if (geometry_set.has<CurveComponent>()) {
attribute_convert_calc(geometry_set.get_component_for_write<CurveComponent>(),
params,
source_name,
result_name,
data_type,
domain);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -143,6 +143,9 @@ static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
fill_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -388,6 +388,9 @@ static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
map_range_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
map_range_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -271,6 +271,9 @@ static void geo_node_attribute_math_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -201,6 +201,9 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_mix_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
attribute_mix_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -253,6 +253,10 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
attribute_calc_proximity(
geometry_set.get_component_for_write<PointCloudComponent>(), geometry_set_target, params);
}
if (geometry_set.has<CurveComponent>()) {
attribute_calc_proximity(
geometry_set.get_component_for_write<CurveComponent>(), geometry_set_target, params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -302,6 +302,14 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
operation,
seed);
}
if (geometry_set.has<CurveComponent>()) {
randomize_attribute_on_component(geometry_set.get_component_for_write<CurveComponent>(),
params,
attribute_name,
data_type,
operation,
seed);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -70,6 +70,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
remove_attribute(
geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names);
}
if (geometry_set.has<CurveComponent>()) {
remove_attribute(
geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -118,6 +118,9 @@ static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
execute_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -148,6 +148,9 @@ static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
separate_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<PointCloudComponent>()) {
separate_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -510,6 +510,9 @@ static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
if (geometry_set.has<CurveComponent>()) {
attribute_vector_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -0,0 +1,161 @@
/*
* 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 "BKE_pointcloud.h"
#include "BKE_spline.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_curve_sample_points_in[] = {
{SOCK_GEOMETRY, N_("Curve")},
{SOCK_INT, N_("Count"), 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_FLOAT, N_("Length"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
{-1, ""},
};
static bNodeSocketTemplate geo_node_curve_sample_points_out[] = {
{SOCK_GEOMETRY, N_("Points")},
{-1, ""},
};
static void geo_node_curve_sample_points_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_points_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryCurveSamplePoints *data = (NodeGeometryCurveSamplePoints *)MEM_callocN(
sizeof(NodeGeometryCurveSamplePoints), __func__);
data->mode = GEO_NODE_CURVE_SAMPLE_POINTS_LENGTH;
node->storage = data;
}
static void geo_node_curve_sample_points_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryCurveSamplePoints &node_storage = *(NodeGeometryCurveSamplePoints *)node->storage;
const GeometryNodeCurveSamplePointsMode mode = (GeometryNodeCurveSamplePointsMode)
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_POINTS_COUNT);
nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_POINTS_LENGTH);
}
namespace blender::nodes {
static void sample_points_from_spline(const Spline &spline,
const int offset,
const float sample_length,
PointCloudComponent &point_component)
{
}
static Array<int> get_result_point_offsets(const SplineGroup &curve,
const GeometryNodeCurveSamplePointsMode mode,
const int count,
const float length)
{
Array<int> offsets(curve.splines.size() + 1);
if (mode == GEO_NODE_CURVE_SAMPLE_POINTS_COUNT) {
for (const int i : curve.splines.index_range()) {
offsets[i] = count * i;
}
offsets.last() = curve.splines.size() * count;
}
else {
int offset = 0;
for (const int i : curve.splines.index_range()) {
offsets[i] = offset;
offset += curve.splines[i]->length() / length;
}
offsets.last() = offset;
}
return offsets;
}
static void geo_node_curve_sample_points_exec(GeoNodeExecParams params)
{
const GeometrySet input_geometry_set = params.extract_input<GeometrySet>("Curve");
const bNode &node = params.node();
const NodeGeometryCurveSamplePoints &node_storage = *(const NodeGeometryCurveSamplePoints *)
node.storage;
const GeometryNodeCurveSamplePointsMode mode = (GeometryNodeCurveSamplePointsMode)
node_storage.mode;
if (!input_geometry_set.has_curve()) {
params.set_output("Points", GeometrySet());
}
const SplineGroup &curve = *input_geometry_set.get_curve_for_read();
const int count = (mode == GEO_NODE_CURVE_SAMPLE_POINTS_COUNT) ?
params.extract_input<int>("Count") :
0;
const float length = (mode == GEO_NODE_CURVE_SAMPLE_POINTS_LENGTH) ?
params.extract_input<float>("Length") :
0.0f;
Array<int> offsets = get_result_point_offsets(curve, mode, count, length);
PointCloud *pointcloud = BKE_pointcloud_new_nomain(offsets.last());
GeometrySet result_geometry_set = GeometrySet::create_with_pointcloud(pointcloud);
PointCloudComponent &point_component =
result_geometry_set.get_component_for_write<PointCloudComponent>();
if (mode == GEO_NODE_CURVE_SAMPLE_POINTS_COUNT) {
const int count = params.extract_input<int>("Count");
for (const int i : curve.splines.index_range()) {
Spline &spline = *curve.splines[i];
sample_points_from_spline(spline, offsets[i], spline.length() / count, point_component);
}
}
else {
}
params.set_output("Geometry", result_geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_curve_sample_points()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_CURVE_SAMPLE_POINTS, "Sample Curve Points", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(
&ntype, geo_node_curve_sample_points_in, geo_node_curve_sample_points_out);
node_type_init(&ntype, geo_node_curve_sample_points_init);
node_type_update(&ntype, geo_node_curve_sample_points_update);
node_type_storage(
&ntype, "NodeGeometryCurveTrim", node_free_standard_storage, node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_curve_sample_points_exec;
ntype.draw_buttons = geo_node_curve_sample_points_layout;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,309 @@
/*
* 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_timeit.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_mesh.h"
#include "BKE_spline.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_curve_to_mesh_in[] = {
{SOCK_GEOMETRY, N_("Curve")},
{SOCK_GEOMETRY, N_("Profile Curve")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_curve_to_mesh_out[] = {
{SOCK_GEOMETRY, N_("Mesh")},
{-1, ""},
};
using blender::Array;
namespace blender::nodes {
static void vert_extrude_to_mesh_data(const Spline &spline,
const float3 profile_vert,
MutableSpan<MVert> verts,
MutableSpan<MEdge> edges,
int &vert_offset,
int &edge_offset)
{
Span<float3> positions = spline.evaluated_positions();
for (const int i : IndexRange(positions.size() - 1)) {
MEdge &edge = edges[edge_offset++];
edge.v1 = vert_offset + i;
edge.v2 = vert_offset + i + 1;
edge.flag = ME_LOOSEEDGE;
}
if (spline.is_cyclic) {
MEdge &edge = edges[edge_offset++];
edge.v1 = vert_offset;
edge.v2 = vert_offset + positions.size() - 1;
edge.flag = ME_LOOSEEDGE;
}
for (const int i : positions.index_range()) {
MVert &vert = verts[vert_offset++];
copy_v3_v3(vert.co, positions[i] + profile_vert);
}
}
static void mark_edges_sharp(MutableSpan<MEdge> edges)
{
for (MEdge &edge : edges) {
edge.flag |= ME_SHARP;
}
}
static void spline_extrude_to_mesh_data(const Spline &spline,
const Spline &profile_spline,
MutableSpan<MVert> verts,
MutableSpan<MEdge> edges,
MutableSpan<MLoop> loops,
MutableSpan<MPoly> polys,
int &vert_offset,
int &edge_offset,
int &loop_offset,
int &poly_offset)
{
const int spline_vert_len = spline.evaluated_points_size();
const int spline_edge_len = spline.evaluated_edges_size();
const int profile_vert_len = profile_spline.evaluated_points_size();
const int profile_edge_len = profile_spline.evaluated_edges_size();
if (spline_vert_len == 0) {
return;
}
if (profile_vert_len == 1) {
vert_extrude_to_mesh_data(
spline, profile_spline.evaluated_positions()[0], verts, edges, vert_offset, edge_offset);
return;
}
/* Add the edges running along the length of the curve, starting at each profile vertex. */
const int spline_edges_start = edge_offset;
for (const int i_profile : IndexRange(profile_vert_len)) {
for (const int i_ring : IndexRange(spline_edge_len)) {
const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
MEdge &edge = edges[edge_offset++];
edge.v1 = ring_vert_offset + i_profile;
edge.v2 = next_ring_vert_offset + i_profile;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
/* Add the edges running along each profile ring. */
const int profile_edges_start = edge_offset;
for (const int i_ring : IndexRange(spline_vert_len)) {
const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
for (const int i_profile : IndexRange(profile_edge_len)) {
const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
MEdge &edge = edges[edge_offset++];
edge.v1 = ring_vert_offset + i_profile;
edge.v2 = ring_vert_offset + i_next_profile;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
/* Calculate poly and face indices. */
for (const int i_ring : IndexRange(spline_edge_len)) {
const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring;
const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring;
for (const int i_profile : IndexRange(profile_edge_len)) {
const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile;
const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile;
MPoly &poly = polys[poly_offset++];
poly.loopstart = loop_offset;
poly.totloop = 4;
poly.flag = ME_SMOOTH;
MLoop &loop_a = loops[loop_offset++];
loop_a.v = ring_vert_offset + i_profile;
loop_a.e = ring_edge_start + i_profile;
MLoop &loop_b = loops[loop_offset++];
loop_b.v = ring_vert_offset + i_next_profile;
loop_b.e = next_spline_edge_start + i_ring;
MLoop &loop_c = loops[loop_offset++];
loop_c.v = next_ring_vert_offset + i_next_profile;
loop_c.e = next_ring_edge_offset + i_profile;
MLoop &loop_d = loops[loop_offset++];
loop_d.v = next_ring_vert_offset + i_profile;
loop_d.e = spline_edge_start + i_ring;
}
}
/* Calculate the positions of each profile ring profile along the spline. */
Span<float3> positions = spline.evaluated_positions();
Span<float3> tangents = spline.evaluated_tangents();
Span<float3> normals = spline.evaluated_normals();
Span<float3> profile_positions = profile_spline.evaluated_positions();
GVArrayPtr radii_varray = spline.interpolate_to_evaluated_points(
blender::fn::GVArray_For_Span(spline.radii()));
GVArray_Typed<float> radii = radii_varray->typed<float>();
for (const int i_ring : IndexRange(spline_vert_len)) {
float4x4 point_matrix = float4x4::from_normalized_axis_data(
positions[i_ring], normals[i_ring], tangents[i_ring]);
point_matrix.apply_scale(radii[i_ring]);
for (const int i_profile : IndexRange(profile_vert_len)) {
MVert &vert = verts[vert_offset++];
copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]);
}
}
/* Mark edge loops from sharp vector control points sharp. */
if (profile_spline.type() == Spline::Bezier) {
const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile_spline);
Span<int> control_point_offsets = bezier_spline.control_point_offsets();
for (const int i : control_point_offsets.index_range()) {
if (bezier_spline.point_is_sharp(i)) {
mark_edges_sharp(edges.slice(
spline_edges_start + spline_edge_len * control_point_offsets[i], spline_edge_len));
}
}
}
}
static Mesh *curve_to_mesh_calculate(const SplineGroup &curve, const SplineGroup &profile_curve)
{
int profile_vert_total = 0;
int profile_edge_total = 0;
for (const SplinePtr &profile_spline : profile_curve.splines) {
profile_vert_total += profile_spline->evaluated_points_size();
profile_edge_total += profile_spline->evaluated_edges_size();
}
int vert_total = 0;
int edge_total = 0;
int poly_total = 0;
for (const SplinePtr &spline : curve.splines) {
const int spline_vert_len = spline->evaluated_points_size();
const int spline_edge_len = spline->evaluated_edges_size();
vert_total += spline_vert_len * profile_vert_total;
poly_total += spline_edge_len * profile_edge_total;
/* Add the ring edges, with one ring for every curve vertex, and the edge loops
* that run along the length of the curve, starting on the first profile. */
edge_total += profile_edge_total * spline_vert_len + profile_vert_total * spline_edge_len;
}
const int corner_total = poly_total * 4;
if (vert_total == 0) {
return nullptr;
}
Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total);
MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
mesh->flag |= ME_AUTOSMOOTH;
mesh->smoothresh = DEG2RADF(180.0f);
int vert_offset = 0;
int edge_offset = 0;
int loop_offset = 0;
int poly_offset = 0;
for (const SplinePtr &spline : curve.splines) {
for (const SplinePtr &profile_spline : profile_curve.splines) {
spline_extrude_to_mesh_data(*spline,
*profile_spline,
verts,
edges,
loops,
polys,
vert_offset,
edge_offset,
loop_offset,
poly_offset);
}
}
BKE_mesh_calc_normals(mesh);
BLI_assert(BKE_mesh_is_valid(mesh));
return mesh;
}
static SplineGroup get_curve_single_vert()
{
SplineGroup curve;
std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
spline->add_point(float3(0), 0, 0.0f);
curve.splines.append(std::move(spline));
return curve;
}
static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params)
{
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
if (!curve_set.has_curve()) {
params.set_output("Mesh", GeometrySet());
return;
}
const SplineGroup *profile_curve = profile_set.get_curve_for_read();
const SplineGroup vert_curve = get_curve_single_vert();
Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(),
(profile_curve == nullptr) ? vert_curve : *profile_curve);
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
void register_node_type_geo_curve_to_mesh()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_curve_to_mesh_in, geo_node_curve_to_mesh_out);
ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_mesh_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,73 @@
/*
* 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_float4x4.hh"
#include "BKE_mesh.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_transform_test_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{SOCK_VECTOR, N_("Forward"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX},
{SOCK_VECTOR, N_("Up"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{-1, ""},
};
static bNodeSocketTemplate geo_node_transform_test_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void geo_node_transform_test_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const float3 translation = params.extract_input<float3>("Translation");
const float3 forward = params.extract_input<float3>("Forward").normalized();
const float3 up = params.extract_input<float3>("Up").normalized();
const float radius = params.extract_input<float>("Radius");
float4x4 matrix = float4x4::from_normalized_axis_data(translation, forward, up);
// float4x4 scale_matrix;
// scale_m4_fl(scale_matrix.values, radius * 2.0f);
// const float4x4 final_matrix = matrix * scale_matrix;
matrix.apply_scale(radius);
// const float4x4 final_matrix = matrix;
if (geometry_set.has_mesh()) {
Mesh *mesh = geometry_set.get_mesh_for_write();
BKE_mesh_transform(mesh, matrix.values, false);
}
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_curve_transform_test()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_CURVE_TRANSFORM_TEST, "Transform Test", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_transform_test_in, geo_node_transform_test_out);
ntype.geometry_node_execute = blender::nodes::geo_node_transform_test_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,230 @@
/*
* 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 "BKE_spline.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_curve_trim_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_FLOAT, N_("Start"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
{SOCK_FLOAT, N_("Start"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
{SOCK_FLOAT, N_("End"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
{SOCK_FLOAT, N_("End"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
{-1, ""},
};
static bNodeSocketTemplate geo_node_curve_trim_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_node_curve_trim_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
static void geo_node_curve_trim_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryCurveTrim *data = (NodeGeometryCurveTrim *)MEM_callocN(sizeof(NodeGeometryCurveTrim),
__func__);
data->mode = GEO_NODE_CURVE_TRIM_FACTOR;
node->storage = data;
}
static void geo_node_curve_trim_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)node->storage;
const GeometryNodeCurveTrimMode mode = (GeometryNodeCurveTrimMode)node_storage.mode;
bNodeSocket *factor_start_socket = ((bNodeSocket *)node->inputs.first)->next;
bNodeSocket *length_start_socket = factor_start_socket->next;
bNodeSocket *factor_end_socket = length_start_socket->next;
bNodeSocket *length_end_socket = factor_end_socket->next;
nodeSetSocketAvailability(factor_start_socket, mode == GEO_NODE_CURVE_TRIM_FACTOR);
nodeSetSocketAvailability(length_start_socket, mode == GEO_NODE_CURVE_TRIM_LENGTH);
nodeSetSocketAvailability(factor_end_socket, mode == GEO_NODE_CURVE_TRIM_FACTOR);
nodeSetSocketAvailability(length_end_socket, mode == GEO_NODE_CURVE_TRIM_LENGTH);
}
namespace blender::nodes {
static void interpolate_control_point(BezierSpline &spline,
const bool adjust_next,
const Spline::LookupResult lookup)
{
using namespace ::blender::fn;
const int eval_index = lookup.evaluated_index;
const int next_eval_index = lookup.next_evaluated_index;
Span<float> mappings = spline.evaluated_mappings();
BezierSpline::InterpolationData interp_data = spline.interpolation_data_from_map(
mappings[eval_index]);
const int index = interp_data.control_point_index + (adjust_next ? 1 : 0);
Span<float3> evaluated_positions = spline.evaluated_positions();
spline.positions()[index] = float3::interpolate(
evaluated_positions[eval_index], evaluated_positions[next_eval_index], lookup.factor);
/* TODO: Do this interpolation with attributes instead. */
{
MutableSpan<float> radii = spline.radii();
GVArrayPtr radii_varray = spline.interpolate_to_evaluated_points(GVArray_For_Span(radii));
GVArray_Typed<float> radii_eval = radii_varray->typed<float>();
radii[index] = interpf(radii_eval[next_eval_index], radii_eval[eval_index], lookup.factor);
}
{
MutableSpan<float> tilts = spline.radii();
GVArrayPtr tilt_varray = spline.interpolate_to_evaluated_points(GVArray_For_Span(tilts));
GVArray_Typed<float> tilts_eval = tilt_varray->typed<float>();
tilts[index] = interpf(tilts_eval[next_eval_index], tilts_eval[eval_index], lookup.factor);
}
{
MutableSpan<float3> handle_positions_start = spline.handle_positions_start();
GVArrayPtr handle_positions_start_varray = spline.interpolate_to_evaluated_points(
GVArray_For_Span(handle_positions_start));
GVArray_Typed<float3> handle_positions_start_eval =
handle_positions_start_varray->typed<float3>();
handle_positions_start[index] = float3::interpolate(
handle_positions_start_eval[eval_index],
handle_positions_start_eval[next_eval_index],
lookup.factor);
}
{
MutableSpan<float3> handle_positions_end = spline.handle_positions_end();
GVArrayPtr handle_positions_end_varray = spline.interpolate_to_evaluated_points(
GVArray_For_Span(handle_positions_end));
GVArray_Typed<float3> handle_positions_end_eval = handle_positions_end_varray->typed<float3>();
handle_positions_end[index] = float3::interpolate(handle_positions_end_eval[eval_index],
handle_positions_end_eval[next_eval_index],
lookup.factor);
}
}
static void trim_spline(BezierSpline &spline,
const Spline::LookupResult start,
const Spline::LookupResult end)
{
BLI_assert(!spline.is_cyclic);
BLI_assert(start.evaluated_index <= end.evaluated_index);
Span<float> mappings = spline.evaluated_mappings();
const int points_len = spline.size();
const int start_index = std::floor(mappings[start.evaluated_index]);
const int end_index = std::min((int)std::floor(mappings[end.evaluated_index]) + 2, points_len);
if (!(start.evaluated_index == 0 && start.factor == 0.0f)) {
interpolate_control_point(spline, false, start);
}
if (end.evaluated_index != spline.evaluated_points_size() - 1) {
interpolate_control_point(spline, true, end);
}
spline.drop_back(std::min(points_len - end_index, points_len));
spline.drop_front(std::max(start_index, 0));
}
static void geo_node_curve_trim_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const bNode &node = params.node();
const NodeGeometryCurveTrim &node_storage = *(const NodeGeometryCurveTrim *)node.storage;
const GeometryNodeCurveTrimMode mode = (GeometryNodeCurveTrimMode)node_storage.mode;
if (!geometry_set.has_curve()) {
params.set_output("Geometry", geometry_set);
return;
}
SplineGroup &curve = *geometry_set.get_curve_for_write();
switch (mode) {
case GEO_NODE_CURVE_TRIM_FACTOR: {
const float factor_start = params.extract_input<float>("Start");
const float factor_end = params.extract_input<float>("End");
for (SplinePtr &spline : curve.splines) {
if (spline->type() != Spline::Type::Bezier) {
continue;
}
if (spline->is_cyclic) {
continue;
}
trim_spline(static_cast<BezierSpline &>(*spline),
spline->lookup_evaluated_factor(std::min(factor_start, factor_end)),
spline->lookup_evaluated_factor(std::max(factor_start, factor_end)));
}
break;
}
case GEO_NODE_CURVE_TRIM_LENGTH: {
const float length_start = params.extract_input<float>("Start_001");
const float length_from_end = params.extract_input<float>("End_001");
for (SplinePtr &spline : curve.splines) {
if (spline->type() != Spline::Type::Bezier) {
continue;
}
if (spline->is_cyclic) {
continue;
}
const float length_end = spline->length() - length_from_end;
trim_spline(static_cast<BezierSpline &>(*spline),
spline->lookup_evaluated_length(std::min(length_start, length_end)),
spline->lookup_evaluated_length(std::max(length_start, length_end)));
}
break;
}
default:
BLI_assert_unreachable();
break;
}
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_curve_trim()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_CURVE_TRIM, "Curve Trim", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_curve_trim_in, geo_node_curve_trim_out);
node_type_init(&ntype, geo_node_curve_trim_init);
node_type_update(&ntype, geo_node_curve_trim_update);
node_type_storage(
&ntype, "NodeGeometryCurveTrim", node_free_standard_storage, node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_curve_trim_exec;
ntype.draw_buttons = geo_node_curve_trim_layout;
nodeRegisterType(&ntype);
}

View File

@@ -17,6 +17,7 @@
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -260,6 +261,48 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet
UNUSED_VARS(src_components, dst_component);
}
/**
* Curve components are a special case. It's possibly to exploit the fact that they simply store
* splines by retrieved with write access is an optimization to avoid copying unecessarily when
* possible.
*/
static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result)
{
Vector<CurveComponent *> src_components;
for (GeometrySet &geometry_set : src_geometry_sets) {
if (geometry_set.has_curve()) {
/* Getting write access for write access seems counterintuitive, but it can actually allow
* avoiding a copy in the case where the input spline has no other users, because the splines
* can be moved from the source curve rather than copying them from a read-only source.
* Retrieving the curve for write will make a copy only when it has a user elsewhere. */
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
src_components.append(&component);
}
}
if (src_components.size() == 0) {
return;
}
if (src_components.size() == 1) {
result.add(*src_components[0]);
return;
}
CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
SplineGroup *dst_curve = new SplineGroup();
for (CurveComponent *component : src_components) {
SplineGroup *src_curve = component->get_for_write();
for (SplinePtr &spline : src_curve->splines) {
dst_curve->splines.append(std::move(spline));
}
}
dst_component.replace(dst_curve);
/* TODO: Make sure generic attributes in different splines have the same type. */
}
template<typename Component>
static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet &result)
{
@@ -290,6 +333,7 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params)
join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result);
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result);
join_curve_components(geometry_sets, geometry_set_result);
params.set_output("Geometry", std::move(geometry_set_result));
}

View File

@@ -14,6 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "DNA_curve_types.h"
#include "DNA_object_types.h"
#include "BKE_spline.hh"
#include "BLI_math_matrix.h"
#include "UI_interface.h"
@@ -72,15 +77,24 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
quat_to_eul(rotation, quaternion);
if (object != self_object) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
if (transform_space_relative) {
instances.add_instance(object, transform);
if (object->type == OB_CURVE) {
SplineGroup *curve = dcurve_from_dna_curve(*(Curve *)object->data);
if (transform_space_relative) {
curve->transform(float4x4(transform));
}
geometry_set = GeometrySet::create_with_curve(curve);
}
else {
float unit_transform[4][4];
unit_m4(unit_transform);
instances.add_instance(object, unit_transform);
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
if (transform_space_relative) {
instances.add_instance(object, transform);
}
else {
float unit_transform[4][4];
unit_m4(unit_transform);
instances.add_instance(object, unit_transform);
}
}
}
}

View File

@@ -208,6 +208,10 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params)
add_instances_from_geometry_component(
instances, *geometry_set.get_component_for_read<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
add_instances_from_geometry_component(
instances, *geometry_set.get_component_for_read<CurveComponent>(), params);
}
params.set_output("Geometry", std::move(geometry_set_out));
}

View File

@@ -24,6 +24,7 @@
#include "DNA_volume_types.h"
#include "BKE_mesh.h"
#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DEG_depsgraph_query.h"
@@ -152,6 +153,21 @@ static void transform_volume(Volume *volume,
#endif
}
static void transform_curve(SplineGroup &curve,
const float3 translation,
const float3 rotation,
const float3 scale)
{
if (use_translate(rotation, scale)) {
curve.translate(translation);
}
else {
const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
curve.transform(matrix);
}
}
static void geo_node_transform_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -179,6 +195,11 @@ static void geo_node_transform_exec(GeoNodeExecParams params)
transform_volume(volume, translation, rotation, scale, params);
}
if (geometry_set.has_curve()) {
SplineGroup *curve = geometry_set.get_curve_for_write();
transform_curve(*curve, translation, rotation, scale);
}
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes