1
1

Compare commits

...

139 Commits

Author SHA1 Message Date
24d1c352e8 Make branch compile after recent multifunction network removal 2021-08-23 14:41:44 -05:00
b3639670ee Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-23 14:10:39 -05:00
ea3ee04fa8 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-20 12:42:11 +02:00
4838436387 fix extrude node 2021-08-20 12:42:07 +02:00
62d485e470 initial evaluate curve node 2021-08-18 14:53:34 +02:00
cd6eb65482 cleanup 2021-08-18 14:44:16 +02:00
d337c20cf9 fix use after free in socket inspection
For now I just evaluate constant fields immediately. The issue was that
parts of the fields was freed already when socket inspection runs.
2021-08-18 13:31:41 +02:00
a715aec6a3 initial sample mesh surface
This is a field based alternative for the attribute transfer node.
The main goal here is to experiment with how attribute transfer
should be done with fields.
2021-08-18 12:53:23 +02:00
eba32b5f4a cleanup 2021-08-18 10:29:40 +02:00
1d859c9183 fix after merge 2021-08-18 10:20:59 +02:00
a3d02965da fix after merge 2021-08-18 10:17:28 +02:00
45e58a7e92 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-18 10:17:15 +02:00
c920b83b84 Fix curve parameter node with multiple splines 2021-08-10 12:09:19 -05:00
34e0d8079f Add 1D noise to noise texture node 2021-08-10 11:58:11 -05:00
a376073f1e Fix curve length node (output a field, otherwise it crashes) 2021-08-10 11:57:42 -05:00
29ac510198 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-10 10:56:57 -05:00
d7cf8babf2 Merge branch 'temp-geometry-nodes-fields-prototype' of git.blender.org:blender into temp-geometry-nodes-fields-prototype 2021-08-10 10:56:23 -05:00
3becd4cb61 Fix curve parameter field with cyclic poly splines 2021-08-10 10:56:17 -05:00
52bb0d42b3 correct socket shape 2021-08-10 17:23:15 +02:00
acae8f430d simplify switching between value and attribute in modifier 2021-08-10 16:55:41 +02:00
bbb692ffb5 add Set Position node 2021-08-10 16:10:06 +02:00
df6a819982 rename to Attribute Freeze 2021-08-10 15:53:41 +02:00
48f2643556 remove dead code 2021-08-10 15:42:15 +02:00
d4b441851f new Position node 2021-08-10 15:33:19 +02:00
e0406d029d Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-10 15:11:56 +02:00
c5c82f801a Merge remote-tracking branch 'origin/temp-geometry-nodes-fields-prototype' into temp-geometry-nodes-fields-prototype 2021-08-10 10:45:30 +02:00
78c0e7c015 Fix: Extrude and Move Edge out selection 2021-08-10 10:45:23 +02:00
fd0ac1aec5 fix node id 2021-08-10 10:08:06 +02:00
0827cce9de Merge remote-tracking branch 'origin/temp-geometry-nodes-fields-prototype' into temp-geometry-nodes-fields-prototype
# Conflicts:
#	source/blender/blenkernel/BKE_node.h
2021-08-10 09:34:04 +02:00
2bba77045f Extrude and Move Node
This adds an additional version of the extrude node, which operates on
vertices, edges and faces and moves the extruded region by a given
offset vector.
2021-08-10 09:33:24 +02:00
00ecf29ec4 fix curve parameter type 2021-08-10 08:25:33 +02:00
172e713cc6 Add curve parameter node 2021-08-09 17:12:19 -05:00
5c32227025 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-09 15:35:06 -05:00
fa9d1cb2a9 properly adapt selection domain 2021-08-09 19:31:32 +02:00
577a12f840 add Normal node 2021-08-09 11:29:04 +02:00
8dad29d0ea new Attribute Extract node 2021-08-09 11:10:57 +02:00
4eda4fd49a support field in point separate node 2021-08-09 09:49:53 +02:00
1db53a8923 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-09 09:12:48 +02:00
20aba8a4c0 Merge remote-tracking branch 'origin/temp-geometry-nodes-fields-prototype' into temp-geometry-nodes-fields-prototype 2021-08-08 04:59:25 +02:00
1d53bf0412 Cleanup: Remove unused variable 2021-08-06 19:00:58 -05:00
1177debb01 Support selection in the point translate node 2021-08-06 17:56:41 -05:00
047daf5442 Don't draw special "constant" sockets in more cases 2021-08-06 17:33:58 -05:00
f2112199a4 Add utilities to enable and disable output sockets with a type 2021-08-06 17:08:22 -05:00
36de290807 Fix incorrect SOCK_FIELD cases 2021-08-06 16:58:24 -05:00
c09a535333 Cleanup: Rename flag
This tends to fit on one line more often
2021-08-06 16:24:22 -05:00
4e78bb3f0c Draw constant input sockets that don't support fields differently
This is a really incomplete way to do this (it doesn't propogate to
linked nodes, and I haven't though about outputs), but it still provides
a very nice benefit to readability.
2021-08-06 16:23:34 -05:00
49f33e9820 Merge remote-tracking branch 'origin/temp-geometry-nodes-fields-prototype' into temp-geometry-nodes-fields-prototype
# Conflicts:
#	source/blender/blenkernel/BKE_node.h
#	source/blender/bmesh/intern/bmesh_mesh.h
#	source/blender/nodes/geometry/nodes/node_geo_extrude.cc
2021-08-06 22:36:31 +02:00
6f525e0d98 Upgrade extrude node with field inputs and selection outputs 2021-08-06 14:55:32 -05:00
963448a2af Cleanup: Remove unused code 2021-08-06 14:53:35 -05:00
9506ef320e Merge branch 'soc-2021-porting-modifiers-to-nodes-extrude' into temp-geometry-nodes-fields-prototype
# Conflicts:
#	source/blender/blenkernel/BKE_node.h
#	source/blender/blenkernel/intern/node.cc
#	source/blender/bmesh/intern/bmesh_mesh.c
#	source/blender/bmesh/intern/bmesh_mesh.h
#	source/blender/nodes/CMakeLists.txt
#	source/blender/nodes/NOD_geometry.h
#	source/blender/nodes/NOD_static_types.h
#	source/blender/nodes/geometry/nodes/node_geo_extrude.cc
2021-08-06 20:25:44 +02:00
1700fde0dc Output anonymous attribute in select by handle node 2021-08-06 12:45:21 -05:00
d5a83a5a32 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-06 12:33:00 -05:00
a7dc3d1e90 Merge remote-tracking branch 'origin/soc-2021-porting-modifiers-to-nodes-extrude' into soc-2021-porting-modifiers-to-nodes-extrude 2021-08-06 07:53:56 +02:00
6fd836f53b Merged Master 2021-08-06 07:48:40 +02:00
40455c21fa Support switching between two fields in the switch node 2021-08-05 18:10:28 -05:00
2d0442e943 Use "true" defaults for selection fields 2021-08-05 17:50:33 -05:00
d55046b167 Support field for curve set spline type selection 2021-08-05 17:50:23 -05:00
4233128fa5 Add a node to store a field in a geometry and output an anonymous attribute 2021-08-05 17:43:15 -05:00
798496233a Rename the "Attribute Fill" node to "Store Persistent Attribute" 2021-08-05 17:42:57 -05:00
991f6b15f3 Merge branch 'master' into soc-2021-porting-modifiers-to-nodes-extrude
# Conflicts:
#	source/blender/blenkernel/BKE_node.h
2021-08-05 23:50:08 +02:00
aef45a4ef2 Merge remote-tracking branch 'origin/soc-2021-porting-modifiers-to-nodes-extrude' into soc-2021-porting-modifiers-to-nodes-extrude
# Conflicts:
#	source/blender/bmesh/intern/bmesh_mesh.c
#	source/blender/bmesh/intern/bmesh_mesh.h
#	source/blender/nodes/NOD_static_types.h
#	source/blender/nodes/geometry/nodes/node_geo_extrude.cc
2021-08-05 23:48:58 +02:00
504e3c563f added side selection. 2021-08-05 23:47:44 +02:00
8645ea16de Remove attribute nodes that have been made redundant 2021-08-05 14:48:18 -05:00
5e292d923f Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-05 14:13:16 -05:00
1ab1d6665c Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-04 18:13:45 -05:00
b1c98ad8ee Fix compile warning 2021-08-03 16:51:01 -04:00
e9d95fddf9 Add tweaked mesh extrude node from Fabian 2021-08-03 16:30:48 -04:00
782d240a1b Fix crash in the Is Viewport node 2021-08-03 16:12:18 -04:00
36055ba366 Remove density max socket from the point distribute node 2021-08-03 16:05:10 -04:00
236576fc91 Support anonymous attributes in the proximity node 2021-08-03 15:49:20 -04:00
e7d57e84bb Move "Align Rotation to Vector" to a function node without geometry
The node is still slightly finnicky, I may have done something wrong here.
But it generally works.
2021-08-03 15:07:55 -04:00
b5573bfbf4 Merge branch 'master' into soc-2021-porting-modifiers-to-nodes-extrude 2021-08-03 21:02:56 +02:00
3b2522650b Don't show face corner domain in the geometry delete node 2021-08-03 12:56:26 -04:00
eacc1f1ff4 Fix raycast and geometry delete node for multi-spline curves 2021-08-03 12:51:13 -04:00
9562a90632 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-03 09:01:23 -04:00
2ceeaf95a1 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-03 10:44:41 +02:00
c8de9dc2a4 Cleanup: Unused variable warning 2021-08-03 00:28:03 -04:00
f8f2873b44 Support fields in the curve set handles node 2021-08-03 00:11:20 -04:00
648fa6d89e Support fields in the curve subdivide node 2021-08-02 23:58:33 -04:00
d24419564e Fix unhandled switch cases 2021-08-02 23:58:19 -04:00
184be40aa1 Support field selection in the mesh to curve node 2021-08-02 23:49:33 -04:00
6131506689 Support anonymous attributse in the select by material node 2021-08-02 23:42:01 -04:00
e5f43cf4b7 Support fields in the material assign node 2021-08-02 23:33:36 -04:00
770d70127f Support fields and anonymous attributes in the raycast node 2021-08-02 23:29:32 -04:00
801034ba6a Support field selection in the geometry delete node 2021-08-02 22:42:34 -04:00
17442bcb7b Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-02 21:54:16 -04:00
ba9561ab0d Geometry Nodes: Extrude
Differential Revision: https://developer.blender.org/D12108
2021-08-02 19:04:04 +02:00
c659af0c13 Geometry Nodes: Extrude 2021-08-02 19:02:38 +02:00
dd8be48466 cleanup after merge 2021-08-02 13:07:31 +02:00
5914b8ed6b Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-02 12:56:18 +02:00
50da3cfbd6 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-02 10:39:37 +02:00
131e8c0d59 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-02 10:00:49 +02:00
aedd43b33f support field in Points to Volume node 2021-07-30 17:37:07 +02:00
adb437ad3f support socket inspection for fields 2021-07-30 17:16:00 +02:00
c106ed8fcd fix 2021-07-30 15:22:03 +02:00
11a6a56982 support fields in point translate node 2021-07-30 15:10:41 +02:00
0a7ab583a5 fix noise node 2021-07-30 15:04:14 +02:00
09009ce062 automatically change type in attribute node when selecting attribute 2021-07-30 13:56:40 +02:00
dc502fad43 support passing attribute field from modifier 2021-07-30 13:42:07 +02:00
3abce9e633 support modifier inputs again 2021-07-30 12:30:32 +02:00
dad5c3991c cleanup 2021-07-30 12:23:19 +02:00
dab3a07659 cleanup 2021-07-30 12:02:05 +02:00
53a724c804 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-07-30 11:39:59 +02:00
bacde38b67 refactor point distribute node 2021-07-29 21:41:01 +02:00
d9b128baf2 don't show anonymous attributes in attribute search 2021-07-29 21:40:50 +02:00
ae6c063b33 don't show anonymous attributes in spreadsheet 2021-07-29 21:03:52 +02:00
67e7aee853 fixes 2021-07-29 20:56:59 +02:00
302ab3ff7c cleanup 2021-07-29 19:56:54 +02:00
4352d1beb5 support anonymous attribute field input 2021-07-29 19:38:39 +02:00
32cb953b9b anonymous attribute fields 2021-07-29 19:28:58 +02:00
1d92a4d1a0 initial anonymous attributes implementation 2021-07-29 19:16:09 +02:00
2472b0f0ef Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-07-29 10:21:22 +02:00
626be25646 support attribute search in Attribute node 2021-07-28 15:41:00 +02:00
8132383662 new index node and fixes 2021-07-28 15:12:53 +02:00
f95214e9ae support implicit conversion of fields 2021-07-28 14:04:14 +02:00
dde42e19e3 support cpptype inheritance for templates 2021-07-28 13:37:29 +02:00
f90a83f816 support field in Point Instance node 2021-07-28 12:30:56 +02:00
433b4ae22a better field support in attribute fill node 2021-07-28 12:05:53 +02:00
23e217eafc support position input in noise node 2021-07-28 11:49:30 +02:00
87153cf019 progress 2021-07-28 11:33:00 +02:00
08122a7794 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-07-28 10:54:16 +02:00
58af171bc0 initial support for noise node 2021-07-27 17:15:13 +02:00
c98a535f5f fix 2021-07-27 17:06:57 +02:00
8262e6a0c1 progress 2021-07-27 17:00:37 +02:00
c00cc9fd60 improve 2021-07-27 16:29:34 +02:00
0a31b1f044 initial attribute field 2021-07-27 16:01:37 +02:00
5e3b33dfe8 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-07-27 15:49:46 +02:00
f716060408 improve 2021-07-27 14:33:35 +02:00
aa092f4e6f Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-07-27 13:54:17 +02:00
ad8dfb3823 cleanup 2021-07-26 18:18:11 +02:00
bb0e0fde32 cleanup 2021-07-26 17:17:23 +02:00
3472f83f00 cleanup 2021-07-26 17:07:45 +02:00
1b2b07fb18 progress 2021-07-26 16:34:28 +02:00
64a11ba6a2 progress 2021-07-26 15:21:15 +02:00
26d8d2884c Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-07-26 11:44:31 +02:00
12014ccfbc progress 2021-07-10 12:14:48 +02:00
34cebfbb78 progress 2021-07-10 11:22:48 +02:00
2cc4e1572a progress 2021-07-09 18:32:57 +02:00
ceb9e7d71e initial commit 2021-07-09 17:48:51 +02:00
87 changed files with 5782 additions and 1076 deletions

View File

@@ -475,24 +475,15 @@ texture_node_categories = [
geometry_node_categories = [
# Geometry Nodes
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
NodeItem("GeometryNodeAttributeRandomize"),
NodeItem("GeometryNodeAttributeMath"),
NodeItem("GeometryNodeAttributeClamp"),
NodeItem("GeometryNodeAttributeCompare"),
NodeItem("GeometryNodeAttributeConvert"),
NodeItem("GeometryNodeAttributeCurveMap"),
NodeItem("GeometryNodeAttributeFill"),
NodeItem("GeometryNodeAttributeMix"),
NodeItem("GeometryNodeAttributeProximity"),
NodeItem("GeometryNodeAttributeColorRamp"),
NodeItem("GeometryNodeAttributeVectorMath"),
NodeItem("GeometryNodeAttributeVectorRotate"),
NodeItem("GeometryNodeAttributeSampleTexture"),
NodeItem("GeometryNodeAttributeCombineXYZ"),
NodeItem("GeometryNodeAttributeSeparateXYZ"),
NodeItem("GeometryNodeAttributeRemove"),
NodeItem("GeometryNodeAttributeMapRange"),
NodeItem("GeometryNodeAttributeTransfer"),
NodeItem("GeometryNodeAttributeExtract"),
NodeItem("GeometryNodeAttributeFreeze"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeMixRGB"),
@@ -514,6 +505,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeCurveSplineType"),
NodeItem("GeometryNodeCurveSetHandles"),
NodeItem("GeometryNodeCurveSelectHandles"),
NodeItem("GeometryNodeEvaluateCurve"),
]),
GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[
NodeItem("GeometryNodeCurvePrimitiveLine"),
@@ -532,6 +524,8 @@ geometry_node_categories = [
NodeItem("GeometryNodeJoinGeometry"),
NodeItem("GeometryNodeSeparateComponents"),
NodeItem("GeometryNodeRaycast"),
NodeItem("GeometryNodeAttributeProximity"),
NodeItem("GeometryNodeSetPosition"),
]),
GeometryNodeCategory("GEO_INPUT", "Input", items=[
NodeItem("GeometryNodeObjectInfo"),
@@ -542,6 +536,11 @@ geometry_node_categories = [
NodeItem("FunctionNodeInputVector"),
NodeItem("GeometryNodeInputMaterial"),
NodeItem("GeometryNodeIsViewport"),
NodeItem("GeometryNodeAttribute"),
NodeItem("GeometryNodeIndex"),
NodeItem("GeometryNodeNormal"),
NodeItem("GeometryNodePosition"),
NodeItem("GeometryNodeCurveParameter"),
]),
GeometryNodeCategory("GEO_MATERIAL", "Material", items=[
NodeItem("GeometryNodeMaterialAssign"),
@@ -554,6 +553,9 @@ geometry_node_categories = [
NodeItem("GeometryNodeEdgeSplit"),
NodeItem("GeometryNodeSubdivisionSurface"),
NodeItem("GeometryNodeMeshSubdivide"),
NodeItem("GeometryNodeExtrude"),
NodeItem("GeometryNodeExtrudeAndMove"),
NodeItem("GeometryNodeSampleMeshSurface"),
]),
GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[
NodeItem("GeometryNodeMeshCircle"),
@@ -565,7 +567,6 @@ geometry_node_categories = [
NodeItem("GeometryNodeMeshLine"),
NodeItem("GeometryNodeMeshUVSphere"),
]),
GeometryNodeCategory("GEO_POINT", "Point", items=[
NodeItem("GeometryNodePointDistribute"),
NodeItem("GeometryNodePointInstance"),
@@ -573,7 +574,6 @@ geometry_node_categories = [
NodeItem("GeometryNodePointScale"),
NodeItem("GeometryNodePointTranslate"),
NodeItem("GeometryNodeRotatePoints"),
NodeItem("GeometryNodeAlignRotationToVector"),
]),
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
NodeItem("ShaderNodeMapRange"),
@@ -583,6 +583,7 @@ geometry_node_categories = [
NodeItem("FunctionNodeFloatCompare"),
NodeItem("FunctionNodeFloatToInt"),
NodeItem("GeometryNodeSwitch"),
NodeItem("ShaderNodeTexNoise"),
]),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
NodeItem("ShaderNodeVectorCurve"),
@@ -590,6 +591,7 @@ geometry_node_categories = [
NodeItem("ShaderNodeCombineXYZ"),
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeVectorRotate"),
NodeItem("FunctionNodeAlignRotationToVector"),
]),
GeometryNodeCategory("GEO_OUTPUT", "Output", items=[
NodeItem("GeometryNodeViewer"),

View File

@@ -37,10 +37,12 @@
struct AttributeMetaData {
AttributeDomain domain;
CustomDataType data_type;
const AnonymousCustomDataLayerID *anonymous_layer_id = nullptr;
constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
{
return (a.domain == b.domain) && (a.data_type == b.data_type);
return (a.domain == b.domain) && (a.data_type == b.data_type) &&
(a.anonymous_layer_id == b.anonymous_layer_id);
}
};
@@ -354,6 +356,12 @@ class CustomDataAttributes {
bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer);
bool remove(const blender::StringRef name);
bool create_anonymous(const AnonymousCustomDataLayerID &id, const CustomDataType data_type);
std::optional<blender::fn::GSpan> get_anonymous_for_read(
const AnonymousCustomDataLayerID &id) const;
std::optional<blender::fn::GMutableSpan> get_anonymous_for_write(
const AnonymousCustomDataLayerID &id);
bool foreach_attribute(const AttributeForeachCallback callback,
const AttributeDomain domain) const;
};

View File

@@ -482,6 +482,14 @@ void CustomData_external_reload(struct CustomData *data,
CustomDataMask mask,
int totelem);
/* Anonymous layers. */
struct AnonymousCustomDataLayerID *CustomData_anonymous_id_new(const char *debug_name);
void CustomData_anonymous_id_strong_decrement(const struct AnonymousCustomDataLayerID *layer_id);
void CustomData_anonymous_id_strong_increment(const struct AnonymousCustomDataLayerID *layer_id);
void CustomData_anonymous_id_weak_decrement(const struct AnonymousCustomDataLayerID *layer_id);
void CustomData_anonymous_id_weak_increment(const struct AnonymousCustomDataLayerID *layer_id);
bool CustomData_layer_is_unused_anonymous(const struct CustomDataLayer *layer);
/* Mesh-to-mesh transfer data. */
struct CustomDataTransferLayerMap;

View File

@@ -0,0 +1,553 @@
/*
* 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 <atomic>
#include "BLI_function_ref.hh"
#include "BLI_map.hh"
#include "BLI_optional_ptr.hh"
#include "BLI_user_counter.hh"
#include "BLI_vector.hh"
#include "BLI_virtual_array.hh"
#include "FN_cpp_type.hh"
#include "FN_cpp_type_make.hh"
#include "FN_multi_function.hh"
#include "BKE_customdata.h"
namespace blender::bke {
using fn::CPPType;
using fn::GMutableSpan;
using fn::GVArray;
using fn::GVArrayPtr;
using fn::MultiFunction;
class FieldInputKey {
public:
virtual ~FieldInputKey() = default;
virtual uint64_t hash() const = 0;
virtual const CPPType &type() const = 0;
friend bool operator==(const FieldInputKey &a, const FieldInputKey &b)
{
return a.is_same_as(b);
}
private:
virtual bool is_same_as(const FieldInputKey &other) const
{
UNUSED_VARS(other);
return false;
}
};
class FieldInputValue {
public:
virtual ~FieldInputValue() = default;
};
class IndexFieldInputKey : public FieldInputKey {
public:
uint64_t hash() const override
{
/* Arbitrary number. */
return 78582029;
}
const CPPType &type() const override
{
return CPPType::get<int>();
}
private:
bool is_same_as(const FieldInputKey &other) const override
{
return dynamic_cast<const IndexFieldInputKey *>(&other) != nullptr;
}
};
class CurveParameterFieldInputKey : public FieldInputKey {
public:
uint64_t hash() const override
{
/* Arbitrary number. */
return 928347504059;
}
const CPPType &type() const override
{
return CPPType::get<float>();
}
private:
bool is_same_as(const FieldInputKey &other) const override
{
return dynamic_cast<const CurveParameterFieldInputKey *>(&other) != nullptr;
}
};
class AnonymousAttributeFieldInputKey : public FieldInputKey {
private:
AnonymousCustomDataLayerID *layer_id_;
const CPPType &type_;
public:
AnonymousAttributeFieldInputKey(AnonymousCustomDataLayerID &layer_id, const CPPType &type)
: layer_id_(&layer_id), type_(type)
{
CustomData_anonymous_id_strong_increment(layer_id_);
}
~AnonymousAttributeFieldInputKey()
{
CustomData_anonymous_id_strong_decrement(layer_id_);
}
const CPPType &type() const override
{
return type_;
}
uint64_t hash() const override
{
return get_default_hash(layer_id_);
}
const AnonymousCustomDataLayerID &layer_id() const
{
return *layer_id_;
}
private:
bool is_same_as(const FieldInputKey &other) const override
{
if (const AnonymousAttributeFieldInputKey *other_typed =
dynamic_cast<const AnonymousAttributeFieldInputKey *>(&other)) {
return layer_id_ == other_typed->layer_id_ && type_ == other_typed->type_;
}
return false;
}
};
class PersistentAttributeFieldInputKey : public FieldInputKey {
private:
std::string name_;
const CPPType *type_;
public:
PersistentAttributeFieldInputKey(std::string name, const CPPType &type)
: name_(std::move(name)), type_(&type)
{
}
uint64_t hash() const override
{
return get_default_hash_2(name_, type_);
}
const CPPType &type() const override
{
return *type_;
}
StringRefNull name() const
{
return name_;
}
private:
bool is_same_as(const FieldInputKey &other) const override
{
if (const PersistentAttributeFieldInputKey *other_typed =
dynamic_cast<const PersistentAttributeFieldInputKey *>(&other)) {
return other_typed->type_ == type_ && other_typed->name_ == name_;
}
return false;
}
};
class GVArrayFieldInputValue : public FieldInputValue {
private:
optional_ptr<GVArray> varray_;
public:
GVArrayFieldInputValue(optional_ptr<GVArray> varray) : varray_(std::move(varray))
{
}
const GVArray &varray() const
{
return *varray_;
}
};
class FieldInputs {
private:
using InputMap = Map<std::reference_wrapper<const FieldInputKey>, const FieldInputValue *>;
InputMap inputs_;
friend class Field;
public:
InputMap::KeyIterator begin() const
{
return inputs_.keys().begin();
}
InputMap::KeyIterator end() const
{
return inputs_.keys().end();
}
int tot_inputs() const
{
return inputs_.size();
}
void set_input(const FieldInputKey &key, const FieldInputValue &value)
{
*inputs_.lookup_ptr(key) = &value;
}
const FieldInputValue *get(const FieldInputKey &key) const
{
return inputs_.lookup_default(key, nullptr);
}
template<typename ValueT> const ValueT *get(const FieldInputKey &key) const
{
return dynamic_cast<const ValueT *>(this->get(key));
}
};
class FieldOutput {
private:
optional_ptr<const GVArray> varray_;
public:
FieldOutput(optional_ptr<const GVArray> varray) : varray_(std::move(varray))
{
}
const GVArray &varray_ref() const
{
return *varray_;
}
};
class Field {
private:
mutable std::atomic<int> users_ = 1;
public:
virtual ~Field() = default;
FieldInputs prepare_inputs() const
{
FieldInputs inputs;
this->foreach_input_key([&](const FieldInputKey &key) { inputs.inputs_.add(key, nullptr); });
return inputs;
}
virtual void foreach_input_key(FunctionRef<void(const FieldInputKey &key)> callback) const
{
UNUSED_VARS(callback);
}
virtual const CPPType &output_type() const = 0;
virtual FieldOutput evaluate(IndexMask mask, const FieldInputs &inputs) const = 0;
void user_add() const
{
users_.fetch_add(1);
}
void user_remove() const
{
const int new_users = users_.fetch_sub(1) - 1;
if (new_users == 0) {
delete this;
}
}
};
using FieldPtr = UserCounter<Field>;
template<typename T> class ConstantField : public Field {
private:
T value_;
public:
ConstantField(T value) : value_(std::move(value))
{
}
const CPPType &output_type() const override
{
return CPPType::get<T>();
}
FieldOutput evaluate(IndexMask mask, const FieldInputs &UNUSED(inputs)) const
{
return optional_ptr<const GVArray>{std::make_unique<fn::GVArray_For_SingleValue>(
CPPType::get<T>(), mask.min_array_size(), &value_)};
}
};
template<typename KeyT> class GVArrayInputField : public Field {
private:
KeyT key_;
public:
template<typename... Args> GVArrayInputField(Args &&...args) : key_(std::forward<Args>(args)...)
{
}
void foreach_input_key(FunctionRef<void(const FieldInputKey &key)> callback) const override
{
callback(key_);
}
const CPPType &output_type() const override
{
return key_.type();
}
FieldOutput evaluate(IndexMask mask, const FieldInputs &inputs) const override
{
const GVArrayFieldInputValue *input = inputs.get<GVArrayFieldInputValue>(key_);
if (input == nullptr) {
return FieldOutput{
optional_ptr<const GVArray>{std::make_unique<fn::GVArray_For_SingleValueRef>(
key_.type(), mask.min_array_size(), key_.type().default_value())}};
}
return FieldOutput{optional_ptr<const GVArray>{input->varray()}};
}
};
class MultiFunctionField : public Field {
private:
Vector<FieldPtr> input_fields_;
optional_ptr<const MultiFunction> fn_;
const int output_param_index_;
public:
MultiFunctionField(Vector<FieldPtr> input_fields,
optional_ptr<const MultiFunction> fn,
const int output_param_index)
: input_fields_(std::move(input_fields)),
fn_(std::move(fn)),
output_param_index_(output_param_index)
{
}
const CPPType &output_type() const override
{
return fn_->param_type(output_param_index_).data_type().single_type();
}
void foreach_input_key(FunctionRef<void(const FieldInputKey &key)> callback) const override
{
for (const FieldPtr &field : input_fields_) {
field->foreach_input_key(callback);
}
}
FieldOutput evaluate(IndexMask mask, const FieldInputs &inputs) const final
{
fn::MFParamsBuilder params{*fn_, mask.min_array_size()};
fn::MFContextBuilder context;
ResourceScope &scope = params.resource_scope();
Vector<GMutableSpan> outputs;
int output_span_index = -1;
int input_index = 0;
for (const int param_index : fn_->param_indices()) {
fn::MFParamType param_type = fn_->param_type(param_index);
switch (param_type.category()) {
case fn::MFParamType::SingleInput: {
const Field &field = *input_fields_[input_index];
FieldOutput &output = scope.add_value(field.evaluate(mask, inputs), __func__);
params.add_readonly_single_input(output.varray_ref());
input_index++;
break;
}
case fn::MFParamType::SingleOutput: {
const CPPType &type = param_type.data_type().single_type();
void *buffer = MEM_mallocN_aligned(
mask.min_array_size() * type.size(), type.alignment(), __func__);
GMutableSpan span{type, buffer, mask.min_array_size()};
outputs.append(span);
params.add_uninitialized_single_output(span);
if (param_index == output_param_index_) {
output_span_index = outputs.size() - 1;
}
break;
}
case fn::MFParamType::SingleMutable:
case fn::MFParamType::VectorInput:
case fn::MFParamType::VectorMutable:
case fn::MFParamType::VectorOutput:
BLI_assert_unreachable();
break;
}
}
fn_->call(mask, params, context);
GMutableSpan output_span = outputs[output_span_index];
outputs.remove(output_span_index);
for (GMutableSpan span : outputs) {
span.type().destruct_indices(span.data(), mask);
MEM_freeN(span.data());
}
std::unique_ptr<GVArray> out_array = std::make_unique<fn::GVArray_For_OwnedGSpan>(output_span,
mask);
return FieldOutput{optional_ptr<const GVArray>{std::move(out_array)}};
}
};
class PersistentAttributeField : public GVArrayInputField<PersistentAttributeFieldInputKey> {
public:
PersistentAttributeField(std::string name, const CPPType &type)
: GVArrayInputField<PersistentAttributeFieldInputKey>(std::move(name), type)
{
}
};
class AnonymousAttributeField : public GVArrayInputField<AnonymousAttributeFieldInputKey> {
public:
AnonymousAttributeField(AnonymousCustomDataLayerID &layer_id, const CPPType &type)
: GVArrayInputField<AnonymousAttributeFieldInputKey>(layer_id, type)
{
}
};
class IndexField : public GVArrayInputField<IndexFieldInputKey> {
};
class CurveParameterField : public GVArrayInputField<CurveParameterFieldInputKey> {
};
class FieldRefBase {
protected:
FieldPtr field_;
public:
const FieldPtr &field() const
{
return field_;
}
};
template<typename T> class FieldRef : public FieldRefBase {
public:
FieldRef()
{
field_ = new ConstantField<T>(T());
}
FieldRef(FieldPtr field)
{
field_ = std::move(field);
}
const Field *operator->() const
{
return &*field_;
}
uint64_t hash() const
{
return get_default_hash(&*field_);
}
friend bool operator==(const FieldRef &a, const FieldRef &b)
{
return &*a.field_ == &*b.field_;
}
friend std::ostream &operator<<(std::ostream &stream, const FieldRef &a)
{
stream << &*a.field_;
return stream;
}
};
template<typename T> struct FieldRefCPPTypeParam {
};
class FieldRefCPPType : public CPPType {
private:
const CPPType &field_type_;
FieldPtr (*get_field_)(const void *field_ref);
void (*construct_)(void *dst, FieldPtr field);
public:
template<typename T>
FieldRefCPPType(FieldRefCPPTypeParam<FieldRef<T>> /* unused */, StringRef debug_name)
: CPPType(fn::CPPTypeParam<FieldRef<T>, CPPTypeFlags::BasicType>(), debug_name),
field_type_(CPPType::get<T>())
{
get_field_ = [](const void *field_ref) {
return ((const blender::bke::FieldRef<T> *)field_ref)->field();
};
construct_ = [](void *dst, blender::bke::FieldPtr field) {
new (dst) blender::bke::FieldRef<T>(std::move(field));
};
}
const CPPType &field_type() const
{
return field_type_;
};
FieldPtr get_field(const void *field_ref) const
{
return get_field_(field_ref);
}
void construct(void *dst, FieldPtr field) const
{
construct_(dst, std::move(field));
}
};
} // namespace blender::bke
#define MAKE_FIELD_REF_CPP_TYPE(DEBUG_NAME, FIELD_TYPE) \
template<> \
const blender::fn::CPPType & \
blender::fn::CPPType::get_impl<blender::bke::FieldRef<FIELD_TYPE>>() \
{ \
static blender::bke::FieldRefCPPType cpp_type{ \
blender::bke::FieldRefCPPTypeParam<blender::bke::FieldRef<FIELD_TYPE>>(), \
STRINGIFY(DEBUG_NAME)}; \
return cpp_type; \
}

View File

@@ -128,6 +128,28 @@ class GeometryComponent {
const CustomDataType data_type,
const AttributeInit &initializer);
bool attribute_try_create_anonymous(const AnonymousCustomDataLayerID &layer_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer);
blender::bke::ReadAttributeLookup attribute_try_get_anonymous_for_read(
const AnonymousCustomDataLayerID &layer_id) const;
blender::fn::GVArrayPtr attribute_try_get_anonymous_for_read(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type) const;
blender::fn::GVArrayPtr attribute_try_get_anonymous_for_read(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value) const;
blender::bke::WriteAttributeLookup attribute_try_get_anonymous_for_write(
const AnonymousCustomDataLayerID &layer_id);
/* Try to create the builtin attribute with the given name. No data type or domain has to be
* provided, because those are fixed for builtin attributes. */
bool attribute_try_create_builtin(const blender::StringRef attribute_name,
@@ -139,8 +161,8 @@ class GeometryComponent {
virtual bool is_empty() const;
/* Get a virtual array to read the data of an attribute on the given domain and data type.
* Returns null when the attribute does not exist or cannot be converted to the requested domain
* and data type. */
* Returns null when the attribute does not exist or cannot be converted to the requested
* domain and data type. */
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
@@ -181,14 +203,14 @@ class GeometryComponent {
}
/**
* Returns an "output attribute", which is essentially a mutable virtual array with some commonly
* used convince features. The returned output attribute might be empty if requested attribute
* cannot exist on the geometry.
* Returns an "output attribute", which is essentially a mutable virtual array with some
* commonly used convince features. The returned output attribute might be empty if requested
* attribute cannot exist on the geometry.
*
* The included convenience features are:
* - Implicit type conversion when writing to builtin attributes.
* - If the attribute name exists already, but has a different type/domain, a temporary attribute
* is created that will overwrite the existing attribute in the end.
* - If the attribute name exists already, but has a different type/domain, a temporary
* attribute is created that will overwrite the existing attribute in the end.
*/
blender::bke::OutputAttribute attribute_try_get_for_output(
const blender::StringRef attribute_name,
@@ -197,8 +219,8 @@ class GeometryComponent {
const void *default_value = nullptr);
/* Same as attribute_try_get_for_output, but should be used when the original values in the
* attributes are not read, i.e. the attribute is used only for output. Since values are not read
* from this attribute, no default value is necessary. */
* attributes are not read, i.e. the attribute is used only for output. Since values are not
* read from this attribute, no default value is necessary. */
blender::bke::OutputAttribute attribute_try_get_for_output_only(
const blender::StringRef attribute_name,
const AttributeDomain domain,
@@ -224,6 +246,35 @@ class GeometryComponent {
return this->attribute_try_get_for_output_only(attribute_name, domain, data_type);
}
blender::bke::OutputAttribute attribute_try_get_anonymous_for_output(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value = nullptr);
blender::bke::OutputAttribute attribute_try_get_anonymous_for_output_only(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type);
template<typename T>
blender::bke::OutputAttribute_Typed<T> attribute_try_get_anonymous_for_output(
const AnonymousCustomDataLayerID &id, const AttributeDomain domain, const T default_value)
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_try_get_anonymous_for_output(id, domain, data_type, &default_value);
}
template<typename T>
blender::bke::OutputAttribute_Typed<T> attribute_try_get_anonymous_for_output_only(
const AnonymousCustomDataLayerID &id, const AttributeDomain domain)
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_try_get_anonymous_for_output_only(id, domain, data_type);
}
private:
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
};
@@ -232,12 +283,12 @@ template<typename T>
inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryComponent, T>;
/**
* A geometry set contains zero or more geometry components. There is at most one component of each
* type. Individual components might be shared between multiple geometries. Shared components are
* copied automatically when write access is requested.
* A geometry set contains zero or more geometry components. There is at most one component of
* each type. Individual components might be shared between multiple geometries. Shared
* components are copied automatically when write access is requested.
*
* Copying a geometry set is a relatively cheap operation, because it does not copy the referenced
* geometry components.
* Copying a geometry set is a relatively cheap operation, because it does not copy the
* referenced geometry components.
*/
struct GeometrySet {
private:
@@ -345,6 +396,10 @@ class MeshComponent : public GeometryComponent {
const AttributeDomain from_domain,
const AttributeDomain to_domain) const final;
blender::VArrayPtr<bool> adapt_selection(blender::VArrayPtr<bool> selection,
AttributeDomain from_domain,
AttributeDomain to_domain) const;
bool is_empty() const final;
bool owns_direct_data() const override;
@@ -441,8 +496,8 @@ class InstanceReference {
enum class Type {
/**
* An empty instance. This allows an `InstanceReference` to be default constructed without
* being in an invalid state. There might also be other use cases that we haven't explored much
* yet (such as changing the instance later on, and "disabling" some instances).
* being in an invalid state. There might also be other use cases that we haven't explored
* much yet (such as changing the instance later on, and "disabling" some instances).
*/
None,
Object,
@@ -513,8 +568,8 @@ class InstancesComponent : public GeometryComponent {
blender::Vector<int> instance_ids_;
/* These almost unique ids are generated based on `ids_`, which might not contain unique ids at
* all. They are *almost* unique, because under certain very unlikely circumstances, they are not
* unique. Code using these ids should not crash when they are not unique but can generally
* all. They are *almost* unique, because under certain very unlikely circumstances, they are
* not unique. Code using these ids should not crash when they are not unique but can generally
* expect them to be unique. */
mutable std::mutex almost_unique_ids_mutex_;
mutable blender::Array<int> almost_unique_ids_;

View File

@@ -1415,7 +1415,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_ATTRIBUTE_COMPARE 1015
#define GEO_NODE_POINT_ROTATE 1016
#define GEO_NODE_ATTRIBUTE_VECTOR_MATH 1017
#define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018
// #define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018
#define GEO_NODE_POINT_TRANSLATE 1019
#define GEO_NODE_POINT_SCALE 1020
#define GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE 1021
@@ -1472,6 +1472,18 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_CURVE_SET_HANDLES 1072
#define GEO_NODE_CURVE_SPLINE_TYPE 1073
#define GEO_NODE_CURVE_SELECT_HANDLES 1074
#define GEO_NODE_ATTRIBUTE 1075
#define GEO_NODE_INDEX 1076
#define GEO_NODE_EXTRUDE 1077
#define GEO_NODE_ATTRIBUTE_FREEZE 1078
#define GEO_NODE_ATTRIBUTE_EXTRACT 1079
#define GEO_NODE_NORMAL 1080
#define GEO_NODE_CURVE_PARAMETER 1081
#define GEO_NODE_EXTRUDE_AND_MOVE 1082
#define GEO_NODE_POSITION 1083
#define GEO_NODE_SET_POSITION 1084
#define GEO_NODE_SAMPLE_MESH_SURFACE 1085
#define GEO_NODE_EVALUATE_CURVE 1086
/** \} */
@@ -1485,6 +1497,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define FN_NODE_INPUT_VECTOR 1207
#define FN_NODE_INPUT_STRING 1208
#define FN_NODE_FLOAT_TO_INT 1209
#define FN_NODE_ALIGN_ROTATION_TO_VECTOR 1210
/** \} */

View File

@@ -346,27 +346,33 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
if (layer.name != attribute_name) {
continue;
}
const CustomDataType data_type = (CustomDataType)layer.type;
switch (data_type) {
case CD_PROP_FLOAT:
return this->layer_to_read_attribute<float>(layer, domain_size);
case CD_PROP_FLOAT2:
return this->layer_to_read_attribute<float2>(layer, domain_size);
case CD_PROP_FLOAT3:
return this->layer_to_read_attribute<float3>(layer, domain_size);
case CD_PROP_INT32:
return this->layer_to_read_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
return this->layer_to_read_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_read_attribute<bool>(layer, domain_size);
default:
break;
}
return this->layer_to_read_attribute(layer, domain_size);
}
return {};
}
ReadAttributeLookup CustomDataAttributeProvider::layer_to_read_attribute(
const CustomDataLayer &layer, const int domain_size) const
{
const CustomDataType data_type = (CustomDataType)layer.type;
switch (data_type) {
case CD_PROP_FLOAT:
return this->layer_to_read_attribute<float>(layer, domain_size);
case CD_PROP_FLOAT2:
return this->layer_to_read_attribute<float2>(layer, domain_size);
case CD_PROP_FLOAT3:
return this->layer_to_read_attribute<float3>(layer, domain_size);
case CD_PROP_INT32:
return this->layer_to_read_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
return this->layer_to_read_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_read_attribute<bool>(layer, domain_size);
default:
return {};
}
}
WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
{
@@ -380,27 +386,33 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
continue;
}
CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size);
const CustomDataType data_type = (CustomDataType)layer.type;
switch (data_type) {
case CD_PROP_FLOAT:
return this->layer_to_write_attribute<float>(layer, domain_size);
case CD_PROP_FLOAT2:
return this->layer_to_write_attribute<float2>(layer, domain_size);
case CD_PROP_FLOAT3:
return this->layer_to_write_attribute<float3>(layer, domain_size);
case CD_PROP_INT32:
return this->layer_to_write_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
return this->layer_to_write_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_write_attribute<bool>(layer, domain_size);
default:
break;
}
return this->layer_to_write_attribute(layer, domain_size);
}
return {};
}
WriteAttributeLookup CustomDataAttributeProvider::layer_to_write_attribute(
CustomDataLayer &layer, const int domain_size) const
{
const CustomDataType data_type = (CustomDataType)layer.type;
switch (data_type) {
case CD_PROP_FLOAT:
return this->layer_to_write_attribute<float>(layer, domain_size);
case CD_PROP_FLOAT2:
return this->layer_to_write_attribute<float2>(layer, domain_size);
case CD_PROP_FLOAT3:
return this->layer_to_write_attribute<float3>(layer, domain_size);
case CD_PROP_INT32:
return this->layer_to_write_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
return this->layer_to_write_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_write_attribute<bool>(layer, domain_size);
default:
return {};
}
}
bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
const StringRef attribute_name) const
{
@@ -487,6 +499,84 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
return true;
}
static std::string get_anonymous_attribute_name()
{
static std::atomic<int> index = 0;
const int next_index = index.fetch_add(1);
return "anonymous_attribute_" + std::to_string(next_index);
}
bool CustomDataAttributeProvider::try_create_anonymous(GeometryComponent &component,
const AnonymousCustomDataLayerID &layer_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer) const
{
if (domain_ != domain) {
return false;
}
if (!this->type_is_supported(data_type)) {
return false;
}
CustomData *custom_data = custom_data_access_.get_custom_data(component);
if (custom_data == nullptr) {
return false;
}
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (layer.anonymous_id == &layer_id) {
/* Don't create two layers with the same id. */
return false;
}
}
const int domain_size = component.attribute_domain_size(domain_);
const std::string attribute_name = get_anonymous_attribute_name();
add_named_custom_data_layer_from_attribute_init(
attribute_name, *custom_data, data_type, domain_size, initializer);
const int layer_index = CustomData_get_named_layer_index(
custom_data, data_type, attribute_name.c_str());
CustomDataLayer *layer = &custom_data->layers[layer_index];
layer->flag |= CD_FLAG_ANONYMOUS;
layer->anonymous_id = &layer_id;
CustomData_anonymous_id_weak_increment(&layer_id);
return true;
}
ReadAttributeLookup CustomDataAttributeProvider::try_get_anonymous_for_read(
const GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
if (custom_data == nullptr) {
return {};
}
const int domain_size = component.attribute_domain_size(domain_);
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (layer.anonymous_id != &layer_id) {
continue;
}
return this->layer_to_read_attribute(layer, domain_size);
}
return {};
}
WriteAttributeLookup CustomDataAttributeProvider::try_get_anonymous_for_write(
GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
if (custom_data == nullptr) {
return {};
}
const int domain_size = component.attribute_domain_size(domain_);
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
if (layer.anonymous_id != &layer_id) {
continue;
}
CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size);
return this->layer_to_write_attribute(layer, domain_size);
}
return {};
}
bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const
{
@@ -497,7 +587,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
const CustomDataType data_type = (CustomDataType)layer.type;
if (this->type_is_supported(data_type)) {
AttributeMetaData meta_data{domain_, data_type};
AttributeMetaData meta_data{domain_, data_type, layer.anonymous_id};
if (!callback(layer.name, meta_data)) {
return false;
}
@@ -698,6 +788,56 @@ bool CustomDataAttributes::create_by_move(const blender::StringRef name,
return result != nullptr;
}
bool CustomDataAttributes::create_anonymous(const AnonymousCustomDataLayerID &id,
const CustomDataType data_type)
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
if (layer.anonymous_id == &id) {
/* Don't create two layers with the same id. */
return false;
}
}
const std::string name = get_anonymous_attribute_name();
if (!this->create(name, data_type)) {
return false;
}
const int layer_index = CustomData_get_named_layer_index(&data, data_type, name.c_str());
CustomDataLayer &layer = data.layers[layer_index];
layer.flag |= CD_FLAG_ANONYMOUS;
layer.anonymous_id = &id;
CustomData_anonymous_id_weak_increment(&id);
return true;
}
std::optional<GSpan> CustomDataAttributes::get_anonymous_for_read(
const AnonymousCustomDataLayerID &id) const
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
if (layer.anonymous_id == &id) {
const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
BLI_assert(cpp_type != nullptr);
return GSpan(*cpp_type, layer.data, size_);
}
}
return {};
}
std::optional<GMutableSpan> CustomDataAttributes::get_anonymous_for_write(
const AnonymousCustomDataLayerID &id)
{
for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
if (layer.anonymous_id == &id) {
const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
BLI_assert(cpp_type != nullptr);
return GMutableSpan(*cpp_type, layer.data, size_);
}
}
return {};
}
bool CustomDataAttributes::remove(const blender::StringRef name)
{
bool result = false;
@@ -721,7 +861,7 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call
const AttributeDomain domain) const
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
AttributeMetaData meta_data{domain, (CustomDataType)layer.type, layer.anonymous_id};
if (!callback(layer.name, meta_data)) {
return false;
}
@@ -735,6 +875,14 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call
/** \name Geometry Component
* \{ */
static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
{
const blender::nodes::DataTypeConversions &conversions =
blender::nodes::get_implicit_type_conversions();
return conversions.try_convert(std::move(varray), to_type);
}
const blender::bke::ComponentAttributeProviders *GeometryComponent::get_attribute_providers() const
{
return nullptr;
@@ -875,6 +1023,102 @@ bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
return false;
}
bool GeometryComponent::attribute_try_create_anonymous(const AnonymousCustomDataLayerID &layer_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer)
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
if (dynamic_provider->try_create_anonymous(*this, layer_id, domain, data_type, initializer)) {
return true;
}
}
return false;
}
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_anonymous_for_read(
const AnonymousCustomDataLayerID &layer_id) const
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
ReadAttributeLookup attribute = dynamic_provider->try_get_anonymous_for_read(*this, layer_id);
if (attribute) {
return attribute;
}
}
return {};
}
blender::fn::GVArrayPtr GeometryComponent::attribute_try_get_anonymous_for_read(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type) const
{
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_anonymous_for_read(id);
if (!attribute) {
return {};
}
std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray);
if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) {
varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain);
if (!varray) {
return {};
}
}
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
if (varray->type() != *cpp_type) {
varray = try_adapt_data_type(std::move(varray), *cpp_type);
if (!varray) {
return {};
}
}
return varray;
}
blender::fn::GVArrayPtr GeometryComponent::attribute_try_get_anonymous_for_read(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value) const
{
blender::fn::GVArrayPtr varray = this->attribute_try_get_anonymous_for_read(
id, domain, data_type);
if (varray) {
return varray;
}
const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
if (default_value == nullptr) {
default_value = type->default_value();
}
const int domain_size = this->attribute_domain_size(domain);
return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value);
}
blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_anonymous_for_write(
const AnonymousCustomDataLayerID &layer_id)
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
for (const DynamicAttributesProvider *dynamic_providers :
providers->dynamic_attribute_providers()) {
WriteAttributeLookup attribute = dynamic_providers->try_get_anonymous_for_write(*this,
layer_id);
if (attribute) {
return attribute;
}
}
return {};
}
bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name,
const AttributeInit &initializer)
{
@@ -968,14 +1212,6 @@ std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
return result;
}
static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
{
const blender::nodes::DataTypeConversions &conversions =
blender::nodes::get_implicit_type_conversions();
return conversions.try_convert(std::move(varray), to_type);
}
std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name,
const AttributeDomain domain,
@@ -1227,3 +1463,122 @@ blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_on
{
return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr);
}
class GVMutableAttribute_For_AnonymousOutputAttribute
: public blender::fn::GVMutableArray_For_GMutableSpan {
public:
GeometryComponent *component;
const AnonymousCustomDataLayerID &final_id;
GVMutableAttribute_For_AnonymousOutputAttribute(GMutableSpan data,
GeometryComponent &component,
const AnonymousCustomDataLayerID &final_id)
: blender::fn::GVMutableArray_For_GMutableSpan(data),
component(&component),
final_id(final_id)
{
}
~GVMutableAttribute_For_AnonymousOutputAttribute() override
{
type_->destruct_n(data_, size_);
MEM_freeN(data_);
}
};
static void save_output_anonymous_attribute(blender::bke::OutputAttribute &output_attribute)
{
using namespace blender;
using namespace blender::fn;
using namespace blender::bke;
GVMutableAttribute_For_AnonymousOutputAttribute &varray =
dynamic_cast<GVMutableAttribute_For_AnonymousOutputAttribute &>(output_attribute.varray());
GeometryComponent &component = *varray.component;
WriteAttributeLookup write_attribute = component.attribute_try_get_anonymous_for_write(
varray.final_id);
BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
for (const int i : IndexRange(varray.size())) {
varray.get(i, buffer);
write_attribute.varray->set_by_relocate(i, buffer);
}
}
static blender::bke::OutputAttribute create_output_attribute_anonymous(
GeometryComponent &component,
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type,
const bool ignore_old_values,
const void *default_value)
{
using namespace blender;
using namespace blender::fn;
using namespace blender::bke;
const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
const int domain_size = component.attribute_domain_size(domain);
WriteAttributeLookup attribute = component.attribute_try_get_anonymous_for_write(id);
if (!attribute) {
if (default_value) {
const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
component.attribute_try_create_anonymous(
id, domain, data_type, AttributeInitVArray(&default_varray));
}
else {
component.attribute_try_create_anonymous(id, domain, data_type, AttributeInitDefault());
}
attribute = component.attribute_try_get_anonymous_for_write(id);
if (!attribute) {
/* Can't create the attribute. */
return {};
}
}
if (attribute.domain == domain && attribute.varray->type() == *cpp_type) {
/* Existing generic attribute matches exactly. */
return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values);
}
/* Allocate a new array that lives next to the existing attribute. It will overwrite the existing
* attribute after processing is done. */
void *data = MEM_mallocN_aligned(
cpp_type->size() * domain_size, cpp_type->alignment(), __func__);
if (ignore_old_values) {
/* This does nothing for trivially constructible types, but is necessary for correctness. */
cpp_type->default_construct_n(data, domain);
}
else {
BLI_assert_unreachable();
/* Fill the temporary array with values from the existing attribute. */
GVArrayPtr old_varray = component.attribute_try_get_anonymous_for_read(
id, domain, data_type, default_value);
old_varray->materialize_to_uninitialized(IndexRange(domain_size), data);
}
GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_AnonymousOutputAttribute>(
GMutableSpan{*cpp_type, data, domain_size}, component, id);
return OutputAttribute(std::move(varray), domain, save_output_anonymous_attribute, true);
}
blender::bke::OutputAttribute GeometryComponent::attribute_try_get_anonymous_for_output(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value)
{
return create_output_attribute_anonymous(*this, id, domain, data_type, false, default_value);
}
blender::bke::OutputAttribute GeometryComponent::attribute_try_get_anonymous_for_output_only(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type)
{
return create_output_attribute_anonymous(*this, id, domain, data_type, true, nullptr);
}

View File

@@ -130,6 +130,30 @@ class DynamicAttributesProvider {
return false;
};
/** Returns the id of the new anonymous or null if no new attribute was created. */
virtual bool try_create_anonymous(GeometryComponent &UNUSED(component),
const AnonymousCustomDataLayerID &UNUSED(layer_id),
const AttributeDomain UNUSED(domain),
const CustomDataType UNUSED(data_type),
const AttributeInit &UNUSED(initializer)) const
{
return false;
}
virtual ReadAttributeLookup try_get_anonymous_for_read(
const GeometryComponent &UNUSED(component),
const AnonymousCustomDataLayerID &UNUSED(layer_id)) const
{
return {};
}
virtual WriteAttributeLookup try_get_anonymous_for_write(
GeometryComponent &UNUSED(component),
const AnonymousCustomDataLayerID &UNUSED(layer_id)) const
{
return {};
}
virtual bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const = 0;
virtual void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const = 0;
@@ -167,6 +191,18 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
const CustomDataType data_type,
const AttributeInit &initializer) const final;
bool try_create_anonymous(GeometryComponent &component,
const AnonymousCustomDataLayerID &layer_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer) const final;
ReadAttributeLookup try_get_anonymous_for_read(
const GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const final;
WriteAttributeLookup try_get_anonymous_for_write(
GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -176,6 +212,9 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
}
private:
ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
const int domain_size) const;
template<typename T>
ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
const int domain_size) const
@@ -185,6 +224,9 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
domain_};
}
WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
const int domain_size) const;
template<typename T>
WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
const int domain_size) const

View File

@@ -339,7 +339,11 @@ void CurveEval::assert_valid_point_attributes() const
name,
[&](AttributeMetaData *map_data) {
/* All unique attribute names should be added on the first spline. */
BLI_assert(spline == splines_.first());
/* TODO(Hans/Jacques): This check seems very bad, anonymous attributes with have
* different names on different splines. */
if (meta_data.anonymous_layer_id == nullptr) {
BLI_assert(spline == splines_.first());
}
*map_data = meta_data;
},
[&](AttributeMetaData *map_data) {

View File

@@ -57,6 +57,8 @@
#include "BLO_read_write.h"
#include "atomic_ops.h"
#include "bmesh.h"
#include "CLG_log.h"
@@ -2127,6 +2129,9 @@ bool CustomData_merge(const struct CustomData *source,
if (flag & CD_FLAG_NOCOPY) {
continue;
}
if (CustomData_layer_is_unused_anonymous(layer)) {
continue;
}
if (!(mask & CD_TYPE_AS_MASK(type))) {
continue;
}
@@ -2164,8 +2169,13 @@ bool CustomData_merge(const struct CustomData *source,
newlayer->active_rnd = lastrender;
newlayer->active_clone = lastclone;
newlayer->active_mask = lastmask;
newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY);
newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY | CD_FLAG_ANONYMOUS);
changed = true;
if (layer->flag & CD_FLAG_ANONYMOUS) {
CustomData_anonymous_id_weak_increment(layer->anonymous_id);
newlayer->anonymous_id = layer->anonymous_id;
}
}
}
@@ -2206,6 +2216,10 @@ static void customData_free_layer__internal(CustomDataLayer *layer, int totelem)
{
const LayerTypeInfo *typeInfo;
if (layer->flag & CD_FLAG_ANONYMOUS) {
CustomData_anonymous_id_weak_decrement(layer->anonymous_id);
layer->anonymous_id = NULL;
}
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
typeInfo = layerType_getInfo(layer->type);
@@ -4244,7 +4258,8 @@ void CustomData_blend_write_prepare(CustomData *data,
for (i = 0, j = 0; i < totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
if (layer->flag & CD_FLAG_NOCOPY) { /* Layers with this flag set are not written to file. */
/* Layers with this flag set are not written to file. */
if (layer->flag & (CD_FLAG_NOCOPY | CD_FLAG_ANONYMOUS)) {
data->totlayer--;
// CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name);
}
@@ -5031,6 +5046,68 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap,
MEM_SAFE_FREE(tmp_data_src);
}
AnonymousCustomDataLayerID *CustomData_anonymous_id_new(const char *debug_name)
{
AnonymousCustomDataLayerID *layer_id = MEM_callocN(sizeof(AnonymousCustomDataLayerID), __func__);
layer_id->debug_name = BLI_strdup(debug_name);
return layer_id;
}
static void CustomData_anonymous_id_free(AnonymousCustomDataLayerID *layer_id)
{
BLI_assert(layer_id->strong_references == 0);
BLI_assert(layer_id->tot_references == 0);
MEM_freeN(layer_id->debug_name);
MEM_freeN(layer_id);
}
void CustomData_anonymous_id_strong_decrement(const AnonymousCustomDataLayerID *layer_id)
{
AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
int strong_references = atomic_sub_and_fetch_int32(&mutable_layer_id->strong_references, 1);
BLI_assert(strong_references >= 0);
UNUSED_VARS_NDEBUG(strong_references);
int tot_references = atomic_sub_and_fetch_int32(&mutable_layer_id->tot_references, 1);
BLI_assert(tot_references >= 0);
if (tot_references == 0) {
CustomData_anonymous_id_free(mutable_layer_id);
}
}
void CustomData_anonymous_id_strong_increment(const AnonymousCustomDataLayerID *layer_id)
{
AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
atomic_add_and_fetch_int32(&mutable_layer_id->tot_references, 1);
atomic_add_and_fetch_int32(&mutable_layer_id->strong_references, 1);
}
void CustomData_anonymous_id_weak_decrement(const AnonymousCustomDataLayerID *layer_id)
{
AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
int tot_references = atomic_sub_and_fetch_int32(&mutable_layer_id->tot_references, 1);
BLI_assert(tot_references >= 0);
if (tot_references == 0) {
CustomData_anonymous_id_free(mutable_layer_id);
}
}
void CustomData_anonymous_id_weak_increment(const AnonymousCustomDataLayerID *layer_id)
{
AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
atomic_add_and_fetch_int32(&mutable_layer_id->tot_references, 1);
}
bool CustomData_layer_is_unused_anonymous(const CustomDataLayer *layer)
{
if (layer->flag & CD_FLAG_ANONYMOUS) {
if (layer->anonymous_id->strong_references == 0) {
return true;
}
}
return false;
}
static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external)
{
if (mdlist) {

View File

@@ -1094,6 +1094,165 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
return true;
}
bool try_create_anonymous(GeometryComponent &component,
const AnonymousCustomDataLayerID &layer_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer) const
{
BLI_assert(this->type_is_supported(data_type));
if (domain != ATTR_DOMAIN_POINT) {
return false;
}
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr || curve->splines().size() == 0) {
return false;
}
MutableSpan<SplinePtr> splines = curve->splines();
/* Otherwise just create a custom data layer on each of the splines. */
for (const int i : splines.index_range()) {
if (!splines[i]->attributes.create_anonymous(layer_id, data_type)) {
/* If attribute creation fails on one of the splines, we cannot leave the custom data
* layers in the previous splines around, so delete them before returning. However,
* this is not an expected case. */
BLI_assert_unreachable();
return false;
}
}
/* With a default initializer type, we can keep the values at their initial values. */
if (initializer.type == AttributeInit::Type::Default) {
return true;
}
WriteAttributeLookup write_attribute = this->try_get_anonymous_for_write(component, layer_id);
/* We just created the attribute, it should exist. */
BLI_assert(write_attribute);
const int total_size = curve->control_point_offsets().last();
GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size);
/* TODO: When we can call a variant of #set_all with a virtual array argument,
* this theoretically unnecessary materialize step could be removed. */
GVArray_GSpan source_varray_span{*source_varray};
write_attribute.varray->set_all(source_varray_span.data());
if (initializer.type == AttributeInit::Type::MoveArray) {
MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
}
curve->assert_valid_point_attributes();
return true;
}
ReadAttributeLookup try_get_anonymous_for_read(const GeometryComponent &component,
const AnonymousCustomDataLayerID &layer_id) const
{
const CurveEval *curve = get_curve_from_component_for_read(component);
if (curve == nullptr || curve->splines().size() == 0) {
return {};
}
Span<SplinePtr> splines = curve->splines();
Vector<GSpan> spans; /* GSpan has no default constructor. */
spans.reserve(splines.size());
std::optional<GSpan> first_span = splines[0]->attributes.get_anonymous_for_read(layer_id);
if (!first_span) {
return {};
}
spans.append(*first_span);
for (const int i : IndexRange(1, splines.size() - 1)) {
std::optional<GSpan> span = splines[i]->attributes.get_anonymous_for_read(layer_id);
if (!span) {
/* All splines should have the same set of data layers. It would be possible to recover
* here and return partial data instead, but that would add a lot of complexity for a
* situation we don't even expect to encounter. */
BLI_assert_unreachable();
return {};
}
if (span->type() != spans.last().type()) {
/* Data layer types on separate splines do not match. */
BLI_assert_unreachable();
return {};
}
spans.append(*span);
}
/* First check for the simpler situation when we can return a simpler span virtual array. */
if (spans.size() == 1) {
return {std::make_unique<GVArray_For_GSpan>(spans.first()), ATTR_DOMAIN_POINT};
}
ReadAttributeLookup attribute = {};
Array<int> offsets = curve->control_point_offsets();
attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
using T = decltype(dummy);
Array<Span<T>> data(splines.size());
for (const int i : splines.index_range()) {
data[i] = spans[i].typed<T>();
BLI_assert(data[i].data() != nullptr);
}
attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
});
return attribute;
}
WriteAttributeLookup try_get_anonymous_for_write(
GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr || curve->splines().size() == 0) {
return {};
}
MutableSpan<SplinePtr> splines = curve->splines();
Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */
spans.reserve(splines.size());
std::optional<GMutableSpan> first_span = splines[0]->attributes.get_anonymous_for_write(
layer_id);
if (!first_span) {
return {};
}
spans.append(*first_span);
for (const int i : IndexRange(1, splines.size() - 1)) {
std::optional<GMutableSpan> span = splines[i]->attributes.get_anonymous_for_write(layer_id);
if (!span) {
/* All splines should have the same set of data layers. It would be possible to recover
* here and return partial data instead, but that would add a lot of complexity for a
* situation we don't even expect to encounter. */
BLI_assert_unreachable();
return {};
}
if (span->type() != spans.last().type()) {
/* Data layer types on separate splines do not match. */
BLI_assert_unreachable();
return {};
}
spans.append(*span);
}
/* First check for the simpler situation when we can return a simpler span virtual array. */
if (spans.size() == 1) {
return {std::make_unique<GVMutableArray_For_GMutableSpan>(spans.first()), ATTR_DOMAIN_POINT};
}
WriteAttributeLookup attribute = {};
Array<int> offsets = curve->control_point_offsets();
attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
using T = decltype(dummy);
Array<MutableSpan<T>> data(splines.size());
for (const int i : splines.index_range()) {
data[i] = spans[i].typed<T>();
BLI_assert(data[i].data() != nullptr);
}
attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
});
return attribute;
}
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final
{

View File

@@ -651,6 +651,207 @@ blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
return {};
}
namespace blender::bke::adapt_selection_domain {
static VArrayPtr<bool> varray_from_array(Array<bool> array)
{
return std::make_unique<VArray_For_ArrayContainer<Array<bool>>>(std::move(array));
}
static VArrayPtr<bool> adapt_selection_point_to_face(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totpoly);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
bool poly_is_selected = true;
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
if (!selection->get(loop.v)) {
poly_is_selected = false;
break;
}
}
new_selection[poly_index] = poly_is_selected;
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_point_to_corner(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totloop);
for (const int loop_index : IndexRange(mesh.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
new_selection[loop_index] = selection->get(loop.v);
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_point_to_edge(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totedge);
for (const int edge_index : IndexRange(mesh.totedge)) {
const MEdge &edge = mesh.medge[edge_index];
const bool edge_is_selected = selection->get(edge.v1) && selection->get(edge.v2);
new_selection[edge_index] = edge_is_selected;
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_edge_to_point(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totvert, false);
for (const int edge_index : IndexRange(mesh.totedge)) {
if (selection->get(edge_index)) {
const MEdge &edge = mesh.medge[edge_index];
new_selection[edge.v1] = true;
new_selection[edge.v2] = true;
}
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_edge_to_face(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totpoly);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
bool poly_is_selected = true;
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
if (!selection->get(loop.e)) {
poly_is_selected = false;
break;
}
}
new_selection[poly_index] = poly_is_selected;
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_face_to_point(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totvert, false);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
if (selection->get(poly_index)) {
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
BLI_assert(loop.v < mesh.totvert);
new_selection[loop.v] = true;
}
}
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_face_to_edge(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totedge, false);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
if (selection->get(poly_index)) {
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
new_selection[loop.e] = true;
}
}
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_face_to_corner(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totloop);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
const bool is_selected = selection->get(poly_index);
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
new_selection[loop_index] = is_selected;
}
}
return varray_from_array(std::move(new_selection));
}
} // namespace blender::bke::adapt_selection_domain
blender::VArrayPtr<bool> MeshComponent::adapt_selection(blender::VArrayPtr<bool> selection,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
{
using namespace blender::bke::adapt_selection_domain;
const int from_domain_size = this->attribute_domain_size(from_domain);
BLI_assert(selection->size() == from_domain_size);
if (from_domain == to_domain) {
return selection;
}
if (from_domain_size == 0) {
return selection;
}
if (selection->is_single()) {
return selection;
}
switch (from_domain) {
case ATTR_DOMAIN_CORNER: {
switch (to_domain) {
case ATTR_DOMAIN_POINT:
break;
case ATTR_DOMAIN_FACE:
break;
case ATTR_DOMAIN_EDGE:
break;
default:
break;
}
break;
}
case ATTR_DOMAIN_POINT: {
switch (to_domain) {
case ATTR_DOMAIN_CORNER:
return adapt_selection_point_to_corner(*mesh_, std::move(selection));
case ATTR_DOMAIN_FACE:
return adapt_selection_point_to_face(*mesh_, std::move(selection));
case ATTR_DOMAIN_EDGE:
return adapt_selection_point_to_edge(*mesh_, std::move(selection));
default:
break;
}
break;
}
case ATTR_DOMAIN_FACE: {
switch (to_domain) {
case ATTR_DOMAIN_POINT:
return adapt_selection_face_to_point(*mesh_, std::move(selection));
case ATTR_DOMAIN_CORNER:
return adapt_selection_face_to_corner(*mesh_, std::move(selection));
case ATTR_DOMAIN_EDGE:
return adapt_selection_face_to_edge(*mesh_, std::move(selection));
default:
break;
}
break;
}
case ATTR_DOMAIN_EDGE: {
switch (to_domain) {
case ATTR_DOMAIN_CORNER:
break;
case ATTR_DOMAIN_POINT:
return adapt_selection_edge_to_point(*mesh_, std::move(selection));
case ATTR_DOMAIN_FACE:
return adapt_selection_edge_to_face(*mesh_, std::move(selection));
default:
break;
}
break;
}
default:
break;
}
return {};
}
static Mesh *get_mesh_from_component_for_write(GeometryComponent &component)
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);

View File

@@ -18,6 +18,7 @@
#include "BKE_attribute.h"
#include "BKE_attribute_access.hh"
#include "BKE_field.hh"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"

View File

@@ -5115,13 +5115,16 @@ static void registerGeometryNodes()
{
register_node_type_geo_group();
register_node_type_geo_align_rotation_to_vector();
register_node_type_geo_extrude();
register_node_type_geo_attribute();
register_node_type_geo_attribute_clamp();
register_node_type_geo_attribute_color_ramp();
register_node_type_geo_attribute_combine_xyz();
register_node_type_geo_attribute_compare();
register_node_type_geo_attribute_convert();
register_node_type_geo_attribute_curve_map();
register_node_type_geo_attribute_extract();
register_node_type_geo_attribute_fill();
register_node_type_geo_attribute_map_range();
register_node_type_geo_attribute_math();
@@ -5139,6 +5142,7 @@ static void registerGeometryNodes()
register_node_type_geo_convex_hull();
register_node_type_geo_curve_endpoints();
register_node_type_geo_curve_length();
register_node_type_geo_curve_parameter();
register_node_type_geo_curve_primitive_bezier_segment();
register_node_type_geo_curve_primitive_circle();
register_node_type_geo_curve_primitive_line();
@@ -5156,6 +5160,9 @@ static void registerGeometryNodes()
register_node_type_geo_curve_trim();
register_node_type_geo_delete_geometry();
register_node_type_geo_edge_split();
register_node_type_geo_evaluate_curve();
register_node_type_geo_extrude_and_move();
register_node_type_geo_index();
register_node_type_geo_input_material();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
@@ -5171,6 +5178,7 @@ static void registerGeometryNodes()
register_node_type_geo_mesh_primitive_uv_sphere();
register_node_type_geo_mesh_subdivide();
register_node_type_geo_mesh_to_curve();
register_node_type_geo_normal();
register_node_type_geo_object_info();
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();
@@ -5179,17 +5187,21 @@ static void registerGeometryNodes()
register_node_type_geo_point_separate();
register_node_type_geo_point_translate();
register_node_type_geo_points_to_volume();
register_node_type_geo_position();
register_node_type_geo_raycast();
register_node_type_geo_sample_mesh_surface();
register_node_type_geo_sample_texture();
register_node_type_geo_select_by_handle_type();
register_node_type_geo_select_by_material();
register_node_type_geo_separate_components();
register_node_type_geo_set_position();
register_node_type_geo_subdivision_surface();
register_node_type_geo_switch();
register_node_type_geo_transform();
register_node_type_geo_triangulate();
register_node_type_geo_viewer();
register_node_type_geo_volume_to_mesh();
register_node_type_geo_attribute_freeze();
}
static void registerFunctionNodes()
@@ -5199,6 +5211,7 @@ static void registerFunctionNodes()
register_node_type_fn_float_to_int();
register_node_type_fn_input_string();
register_node_type_fn_input_vector();
register_node_type_fn_align_rotation_to_vector();
register_node_type_fn_random_float();
}

View File

@@ -123,7 +123,7 @@ int Spline::evaluated_edges_size() const
float Spline::length() const
{
Span<float> lengths = this->evaluated_lengths();
return (lengths.size() == 0) ? 0 : this->evaluated_lengths().last();
return lengths.is_empty() ? 0.0f : this->evaluated_lengths().last();
}
int Spline::segments_size() const

View File

@@ -250,6 +250,20 @@ template<typename T> struct DefaultHash<std::unique_ptr<T>> {
}
};
template<typename T> struct DefaultHash<std::shared_ptr<T>> {
uint64_t operator()(const std::shared_ptr<T> &value) const
{
return get_default_hash(value.get());
}
};
template<typename T> struct DefaultHash<std::reference_wrapper<T>> {
uint64_t operator()(const std::reference_wrapper<T> &value) const
{
return get_default_hash(value.get());
}
};
template<typename T1, typename T2> struct DefaultHash<std::pair<T1, T2>> {
uint64_t operator()(const std::pair<T1, T2> &value) const
{

View File

@@ -0,0 +1,88 @@
/*
* 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 bli
*/
#include <memory>
#include "BLI_utildefines.h"
namespace blender {
template<typename T, typename OwnedTPtr = std::unique_ptr<T>> class optional_ptr {
private:
OwnedTPtr owned_ptr_;
T *ptr_ = nullptr;
public:
optional_ptr() = default;
optional_ptr(T &ptr) : ptr_(&ptr)
{
}
optional_ptr(OwnedTPtr owned_ptr) : owned_ptr_(std::move(owned_ptr)), ptr_(&*owned_ptr_)
{
}
bool is_owned() const
{
return static_cast<bool>(owned_ptr_);
}
OwnedTPtr extract_owned()
{
OwnedTPtr ptr = std::move(owned_ptr_);
owned_ptr_ = OwnedTPtr{nullptr};
ptr_ = nullptr;
return ptr;
}
T *operator->()
{
BLI_assert(ptr_ != nullptr);
return ptr_;
}
const T *operator->() const
{
BLI_assert(ptr_ != nullptr);
return ptr_;
}
T &operator*()
{
BLI_assert(ptr_ != nullptr);
return *ptr_;
}
const T &operator*() const
{
BLI_assert(ptr_ != nullptr);
return *ptr_;
}
operator bool() const
{
return ptr_ != nullptr;
}
};
} // namespace blender

View File

@@ -478,8 +478,8 @@ template<typename T> class MutableSpan {
using size_type = int64_t;
protected:
T *data_;
int64_t size_;
T *data_ = nullptr;
int64_t size_ = 0;
public:
constexpr MutableSpan() = default;

View File

@@ -84,12 +84,24 @@ template<typename T> class UserCounter {
return data_;
}
const T *operator->() const
{
BLI_assert(data_ != nullptr);
return data_;
}
T &operator*()
{
BLI_assert(data_ != nullptr);
return *data_;
}
const T &operator*() const
{
BLI_assert(data_ != nullptr);
return *data_;
}
operator bool() const
{
return data_ != nullptr;

View File

@@ -255,7 +255,7 @@ template<typename T> class VMutableArray : public VArray<T> {
}
};
template<typename T> using VArrayPtr = std::unique_ptr<VArray<T>>;
template<typename T> using VArrayPtr = std::unique_ptr<const VArray<T>>;
template<typename T> using VMutableArrayPtr = std::unique_ptr<VMutableArray<T>>;
/**

View File

@@ -653,6 +653,18 @@ TEST(map, LookupKey)
EXPECT_EQ(map.lookup_key_ptr("a"), map.lookup_key_ptr_as("a"));
}
TEST(map, ReferenceWrapperKey)
{
Map<std::reference_wrapper<int>, int> map;
int a = 2;
int b = 5;
map.add(a, 10);
map.add(a, 20);
map.add(b, 20);
EXPECT_EQ(map.lookup(a), 10);
EXPECT_EQ(map.lookup(b), 20);
}
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/

View File

@@ -1446,4 +1446,141 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm,
}
}
/**
* Use to select bmesh vertex data based on an array of bool.
*/
void BM_select_vertices(BMesh *bm, const bool *mask)
{
BMIter iter;
BMVert *v;
int i = 0;
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (mask[i]) {
BM_elem_flag_set(v, BM_ELEM_SELECT, true);
}
else {
BM_elem_flag_set(v, BM_ELEM_SELECT, false);
}
i++;
}
}
/**
* Use to select bmesh edge data based on an array of bool.
*/
void BM_select_edges(BMesh *bm, const bool *mask)
{
BMIter iter;
BMEdge *e;
int i = 0;
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (mask[i]) {
BM_elem_flag_set(e, BM_ELEM_SELECT, true);
}
else {
BM_elem_flag_set(e, BM_ELEM_SELECT, false);
}
i++;
}
}
/**
* Use to select bmesh face data based on an array of bool.
*/
void BM_select_faces(BMesh *bm, const bool *mask)
{
BMIter iter;
BMFace *f;
int i = 0;
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
BM_elem_flag_set(f, BM_ELEM_SELECT, mask[i]);
}
}
void BM_tag_vertices(BMesh *bm, const bool *mask)
{
BMIter iter;
BMVert *v;
int i = 0;
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
BM_elem_flag_set(v, BM_ELEM_TAG, mask[i]);
}
}
/**
* Use to temporary tag bmesh edge data based on an array of bool.
*/
void BM_tag_edges(BMesh *bm, const bool *mask)
{
BMIter iter;
BMEdge *e;
int i = 0;
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
BM_elem_flag_set(e, BM_ELEM_TAG, mask[i]);
}
}
/**
* Use to temporary tag bmesh face data based on an array of bool.
*/
void BM_tag_faces(BMesh *bm, const bool *mask)
{
BMIter iter;
BMFace *f;
int i = 0;
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
BM_elem_flag_set(f, BM_ELEM_TAG, mask[i]);
}
}
void BM_get_tagged_faces(BMesh *bm, bool *selection)
{
BMIter iter;
BMFace *f;
int i = 0;
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
selection[i] = BM_elem_flag_test(f, BM_ELEM_TAG);
}
}
void BM_tag_new_faces(BMesh *bm, BMOperator *b_mesh_operator)
{
BMOIter iter;
BMFace *f;
BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
BMO_ITER (f, &iter, b_mesh_operator->slots_out, "faces.out", BM_FACE) {
BM_elem_flag_enable(f, BM_ELEM_TAG);
}
}
void BM_get_selected_faces(BMesh *bm, bool *selection)
{
BMIter iter;
BMFace *f;
int i = 0;
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
selection[i] = BM_elem_flag_test(f, BM_ELEM_SELECT);
}
}
void BM_get_selected_edges(BMesh *bm, bool *selection)
{
BMIter iter;
BMEdge *e;
int i = 0;
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
selection[i] = BM_elem_flag_test(e, BM_ELEM_SELECT);
}
}
void BM_get_selected_vertices(BMesh *bm, bool *selection)
{
BMIter iter;
BMVert *v;
int i = 0;
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
selection[i] = BM_elem_flag_test(v, BM_ELEM_SELECT);
}
}
/** \} */

View File

@@ -134,3 +134,16 @@ void BM_mesh_vert_coords_apply(BMesh *bm, const float (*vert_coords)[3]);
void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm,
const float (*vert_coords)[3],
const float mat[4][4]);
void BM_select_vertices(BMesh *bm, const bool *mask);
void BM_select_edges(BMesh *bm, const bool *mask);
void BM_select_faces(BMesh *bm, const bool *mask);
void BM_tag_vertices(BMesh *bm, const bool *mask);
void BM_tag_edges(BMesh *bm, const bool *mask);
void BM_tag_faces(BMesh *bm, const bool *mask);
void BM_get_tagged_faces(BMesh *bm, bool *selection);
void BM_get_selected_faces(BMesh *bm, bool *selection);
void BM_get_selected_edges(BMesh *bm, bool *selection);
void BM_get_selected_vertices(BMesh *bm, bool *selection);
void BM_tag_new_faces(BMesh *bm, BMOperator *b_mesh_operator);

View File

@@ -1898,6 +1898,9 @@ static BMOpDefine bmo_inset_individual_def = {
{{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* input faces */
{"thickness", BMO_OP_SLOT_FLT}, /* thickness */
{"depth", BMO_OP_SLOT_FLT}, /* depth */
{"thickness_array", BMO_OP_SLOT_PTR}, /* thickness */
{"depth_array", BMO_OP_SLOT_PTR}, /* depth */
{"use_attributes", BMO_OP_SLOT_BOOL}, /* Use spans for thickness and depth */
{"use_even_offset", BMO_OP_SLOT_BOOL}, /* scale the offset to give more even thickness */
{"use_interpolate", BMO_OP_SLOT_BOOL}, /* blend face data across the inset */
{"use_relative_offset", BMO_OP_SLOT_BOOL}, /* scale the offset by surrounding geometry */
@@ -1929,6 +1932,9 @@ static BMOpDefine bmo_inset_region_def = {
{"use_edge_rail", BMO_OP_SLOT_BOOL}, /* inset the region along existing edges */
{"thickness", BMO_OP_SLOT_FLT}, /* thickness */
{"depth", BMO_OP_SLOT_FLT}, /* depth */
{"thickness_array", BMO_OP_SLOT_PTR}, /* thickness */
{"depth_array", BMO_OP_SLOT_PTR}, /* depth */
{"use_attributes", BMO_OP_SLOT_BOOL}, /* Use spans for thickness and depth */
{"use_outset", BMO_OP_SLOT_BOOL}, /* outset rather than inset */
{{'\0'}},
},
@@ -1940,7 +1946,6 @@ static BMOpDefine bmo_inset_region_def = {
(BMO_OPTYPE_FLAG_NORMALS_CALC |
BMO_OPTYPE_FLAG_SELECT_FLUSH),
};
/*
* Edge-loop Offset.
*

View File

@@ -471,6 +471,7 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op)
for (e = BMO_iter_new(&siter, dupeop.slots_out, "boundary_map.out", 0); e;
e = BMO_iter_step(&siter)) {
BMVert *f_verts[4];
#ifdef USE_EDGE_REGION_FLAGS
BMEdge *f_edges[4];
#endif

View File

@@ -419,6 +419,9 @@ void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
BMOIter oiter;
MemArena *interp_arena = NULL;
const bool use_attributes = BMO_slot_bool_get(op->slots_in, "use_attributes");
const float *thickness_array = BMO_slot_ptr_get(op->slots_in, "thickness_array");
const float *depth_array = BMO_slot_ptr_get(op->slots_in, "depth_array");
const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
const float depth = BMO_slot_float_get(op->slots_in, "depth");
const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
@@ -433,19 +436,37 @@ void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
if (use_interpolate) {
interp_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
}
int i = 0;
if (use_attributes) {
BMO_ITER_INDEX (f, &oiter, op->slots_in, "faces", BM_FACE, i) {
bmo_face_inset_individual(bm,
f,
interp_arena,
thickness_array[i],
depth_array[i],
use_even_offset,
use_relative_offset,
use_interpolate);
BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
bmo_face_inset_individual(bm,
f,
interp_arena,
thickness,
depth,
use_even_offset,
use_relative_offset,
use_interpolate);
if (use_interpolate) {
BLI_memarena_clear(interp_arena);
}
}
}
else {
BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
bmo_face_inset_individual(bm,
f,
interp_arena,
thickness,
depth,
use_even_offset,
use_relative_offset,
use_interpolate);
if (use_interpolate) {
BLI_memarena_clear(interp_arena);
if (use_interpolate) {
BLI_memarena_clear(interp_arena);
}
}
}
@@ -677,12 +698,16 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
const bool use_outset = BMO_slot_bool_get(op->slots_in, "use_outset");
const bool use_boundary = BMO_slot_bool_get(op->slots_in, "use_boundary") &&
(use_outset == false);
const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
const bool use_even_boundary = use_even_offset; /* could make own option */
const bool use_relative_offset = BMO_slot_bool_get(op->slots_in, "use_relative_offset");
const bool use_edge_rail = BMO_slot_bool_get(op->slots_in, "use_edge_rail");
const bool use_interpolate = BMO_slot_bool_get(op->slots_in, "use_interpolate");
const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
const bool use_attributes = BMO_slot_bool_get(op->slots_in, "use_attributes");
const float *thickness_array = BMO_slot_ptr_get(op->slots_in, "thickness_array");
// const float *depth_array = BMO_slot_ptr_get(op->slots_in, "depth_array");
const float depth = BMO_slot_float_get(op->slots_in, "depth");
#ifdef USE_LOOP_CUSTOMDATA_MERGE
const bool has_math_ldata = (use_interpolate && CustomData_has_math(&bm->ldata));
@@ -1096,7 +1121,12 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
}
/* apply the offset */
madd_v3_v3fl(v_split->co, tvec, thickness);
if (use_attributes) {
madd_v3_v3fl(v_split->co, tvec, thickness_array[v_split->head.index]);
}
else {
madd_v3_v3fl(v_split->co, tvec, thickness);
}
}
/* this saves expensive/slow glue check for common cases */

View File

@@ -191,6 +191,7 @@ void OBJECT_OT_skin_radii_equalize(struct wmOperatorType *ot);
void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot);
void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot);
void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot);
void OBJECT_OT_geometry_nodes_modifier_value_or_attribute_toggle(struct wmOperatorType *ot);
/* object_gpencil_modifiers.c */
void OBJECT_OT_gpencil_modifier_add(struct wmOperatorType *ot);

View File

@@ -3242,3 +3242,55 @@ void OBJECT_OT_surfacedeform_bind(wmOperatorType *ot)
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Toggle Value or Attribute Operator
* \{ */
static int geometry_nodes_modifier_value_or_attribute_toggle_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
char modifier_name[MAX_NAME];
RNA_string_get(op->ptr, "modifier_name", modifier_name);
NodesModifierData *nmd = (NodesModifierData *)BKE_modifiers_findby_name(ob, modifier_name);
if (nmd == NULL) {
return OPERATOR_CANCELLED;
}
char prop_path[MAX_NAME];
RNA_string_get(op->ptr, "prop_path", prop_path);
PointerRNA mod_ptr;
RNA_pointer_create(&ob->id, &RNA_Modifier, nmd, &mod_ptr);
const int old_value = RNA_int_get(&mod_ptr, prop_path);
const int new_value = !old_value;
RNA_int_set(&mod_ptr, prop_path, new_value);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
void OBJECT_OT_geometry_nodes_modifier_value_or_attribute_toggle(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Toggle Geometry Nodes Modifier Value or Attribute Toggle";
ot->description = "Toggle between attribute and value";
ot->idname = "OBJECT_OT_geometry_nodes_modifier_value_or_attribute_toggle";
/* api callbacks */
ot->exec = geometry_nodes_modifier_value_or_attribute_toggle_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
RNA_def_string(ot->srna, "prop_path", NULL, 0, "Prop Path", "Prop path");
RNA_def_string(
ot->srna, "modifier_name", NULL, MAX_NAME, "Modifier", "Name of the modifier to edit");
}
/** \} */

View File

@@ -145,6 +145,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_skin_loose_mark_clear);
WM_operatortype_append(OBJECT_OT_skin_radii_equalize);
WM_operatortype_append(OBJECT_OT_skin_armature_create);
WM_operatortype_append(OBJECT_OT_geometry_nodes_modifier_value_or_attribute_toggle);
/* grease pencil modifiers */
WM_operatortype_append(OBJECT_OT_gpencil_modifier_add);

View File

@@ -45,6 +45,7 @@
#include "BLT_translation.h"
#include "BKE_context.h"
#include "BKE_field.hh"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
@@ -767,14 +768,14 @@ static void node_socket_draw(const bNodeSocket *sock,
immVertex2f(pos_id, locx, locy);
}
static void node_socket_draw_multi_input(const float color[4],
const float color_outline[4],
const float width,
const float height,
const int locx,
const int locy)
static void node_socket_draw_rounded_rectangle(const float color[4],
const float color_outline[4],
const float width,
const float height,
const float locx,
const float locy,
const float outline_width)
{
const float outline_width = 1.0f;
/* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. */
const rctf rect = {
locx - width + outline_width * 0.5f,
@@ -788,6 +789,40 @@ static void node_socket_draw_multi_input(const float color[4],
&rect, color, nullptr, 1.0f, color_outline, outline_width, width - outline_width * 0.5f);
}
static bool use_special_non_field_socket_drawing(const bNodeTree *node_tree,
const bNode *node,
const bNodeSocket *socket)
{
if (node_tree->type != NTREE_GEOMETRY) {
return false;
}
if (ELEM(socket->type,
SOCK_MATERIAL,
SOCK_GEOMETRY,
SOCK_TEXTURE,
SOCK_COLLECTION,
SOCK_OBJECT,
SOCK_IMAGE,
SOCK_STRING)) {
return false;
}
if (socket->flag & SOCK_FIELD) {
return false;
}
if (socket->in_out == SOCK_OUT) {
return false;
}
if (node->typeinfo->build_multi_function) {
/* Wow, that's hacky. Don't use vertical bar for function nodes. */
return false;
}
if (ELEM(node->type, NODE_REROUTE, NODE_GROUP, NODE_GROUP_OUTPUT)) {
return false;
}
return true;
}
static const float virtual_node_socket_outline_color[4] = {0.5, 0.5, 0.5, 1.0};
static void node_socket_outline_color_get(const bool selected,
@@ -842,31 +877,68 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal
};
const GPointer value = value_log.value();
if (value.is_type<int>()) {
ss << *value.get<int>() << TIP_(" (Integer)");
const CPPType &type = *value.type();
if (const blender::bke::FieldRefCPPType *field_ref_type =
dynamic_cast<const blender::bke::FieldRefCPPType *>(&type)) {
const CPPType &base_type = field_ref_type->field_type();
blender::bke::FieldPtr field = field_ref_type->get_field(value.get());
blender::bke::FieldInputs field_inputs = field->prepare_inputs();
const int tot_inputs = field_inputs.tot_inputs();
if (tot_inputs > 0) {
ss << "Field Inputs:\n";
int i = 0;
for (const blender::bke::FieldInputKey &key : field_inputs) {
ss << "\u2022 ";
if (const blender::bke::PersistentAttributeFieldInputKey *persistent_attribute_key =
dynamic_cast<const blender::bke::PersistentAttributeFieldInputKey *>(&key)) {
ss << "Persistent attribute: '" << persistent_attribute_key->name() << "'";
}
else if (const blender::bke::AnonymousAttributeFieldInputKey *anonymous_attribute_key =
dynamic_cast<const blender::bke::AnonymousAttributeFieldInputKey *>(&key)) {
ss << "Anonymous attribute: '" << anonymous_attribute_key->layer_id().debug_name << "'";
}
else if (dynamic_cast<const blender::bke::IndexFieldInputKey *>(&key)) {
ss << "Index";
}
if (++i < tot_inputs) {
ss << ".\n";
}
}
}
else {
blender::bke::FieldOutput field_output = field->evaluate({0}, field_inputs);
BUFFER_FOR_CPP_TYPE_VALUE(base_type, buffer);
field_output.varray_ref().get(0, buffer);
if (base_type.is<int>()) {
ss << *(int *)buffer << TIP_(" (Integer)");
}
else if (base_type.is<float>()) {
ss << *(float *)buffer << TIP_(" (Float)");
}
else if (base_type.is<blender::float3>()) {
ss << *(blender::float3 *)buffer << TIP_(" (Vector)");
}
else if (base_type.is<bool>()) {
ss << (*(bool *)buffer ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)");
}
else if (base_type.is<blender::ColorGeometry4f>()) {
ss << *(blender::ColorGeometry4f *)buffer << TIP_(" (Color)");
}
}
}
else if (value.is_type<float>()) {
ss << *value.get<float>() << TIP_(" (Float)");
}
else if (value.is_type<blender::float3>()) {
ss << *value.get<blender::float3>() << TIP_(" (Vector)");
}
else if (value.is_type<bool>()) {
ss << (*value.get<bool>() ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)");
}
else if (value.is_type<std::string>()) {
else if (type.is<std::string>()) {
ss << *value.get<std::string>() << TIP_(" (String)");
}
else if (value.is_type<Object *>()) {
else if (type.is<Object *>()) {
id_to_inspection_string((ID *)*value.get<Object *>());
}
else if (value.is_type<Material *>()) {
else if (type.is<Material *>()) {
id_to_inspection_string((ID *)*value.get<Material *>());
}
else if (value.is_type<Tex *>()) {
else if (type.is<Tex *>()) {
id_to_inspection_string((ID *)*value.get<Tex *>());
}
else if (value.is_type<Collection *>()) {
else if (type.is<Collection *>()) {
id_to_inspection_string((ID *)*value.get<Collection *>());
}
}
@@ -988,24 +1060,27 @@ static void node_socket_draw_nested(const bContext *C,
node_socket_color_get((bContext *)C, ntree, node_ptr, sock, color);
node_socket_outline_color_get(selected, sock->type, outline_color);
node_socket_draw(sock,
color,
outline_color,
size,
sock->locx,
sock->locy,
pos_id,
col_id,
shape_id,
size_id,
outline_col_id);
bNode *node = (bNode *)node_ptr->data;
if (!use_special_non_field_socket_drawing(ntree, node, sock)) {
node_socket_draw(sock,
color,
outline_color,
size,
sock->locx,
sock->locy,
pos_id,
col_id,
shape_id,
size_id,
outline_col_id);
}
if (ntree->type != NTREE_GEOMETRY) {
/* Only geometry nodes has socket value tooltips currently. */
return;
}
bNode *node = (bNode *)node_ptr->data;
uiBlock *block = node->block;
/* Ideally sockets themselves should be buttons, but they aren't currently. So add an invisible
@@ -1365,20 +1440,28 @@ void node_draw_sockets(const View2D *v2d,
if (nodeSocketIsHidden(socket)) {
continue;
}
if (!(socket->flag & SOCK_MULTI_INPUT)) {
continue;
}
const bool is_node_hidden = (node->flag & NODE_HIDDEN);
const float width = NODE_SOCKSIZE;
float height = is_node_hidden ? width : node_socket_calculate_height(socket) - width;
float color[4];
float outline_color[4];
node_socket_color_get((bContext *)C, ntree, &node_ptr, socket, color);
node_socket_outline_color_get(selected, socket->type, outline_color);
node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy);
if (socket->flag & SOCK_MULTI_INPUT) {
const bool is_node_hidden = (node->flag & NODE_HIDDEN);
const float width = NODE_SOCKSIZE;
float height = is_node_hidden ? width : node_socket_calculate_height(socket) - width;
node_socket_draw_rounded_rectangle(
color, outline_color, width, height, socket->locx, socket->locy, 1.0f);
}
else if (use_special_non_field_socket_drawing(ntree, node, socket)) {
node_socket_draw_rounded_rectangle(color,
outline_color,
NODE_SOCKSIZE * 0.6f,
NODE_SOCKSIZE * 1.3,
socket->locx - 0.8f,
socket->locy,
0.75f);
}
}
}

View File

@@ -99,12 +99,23 @@ static void attribute_search_update_fn(
AttributeSearchData *data = static_cast<AttributeSearchData *>(arg);
SpaceNode *snode = CTX_wm_space_node(C);
const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(
*snode, *data->node);
if (node_log == nullptr) {
return;
blender::Vector<const GeometryAttributeInfo *> infos;
if (data->node->type == GEO_NODE_ATTRIBUTE) {
const geo_log::ModifierLog *modifier_log =
geo_log::ModifierLog::find_root_by_node_editor_context(*snode);
if (modifier_log == nullptr) {
return;
}
infos = modifier_log->lookup_available_attributes();
}
else {
const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(
*snode, *data->node);
if (node_log == nullptr) {
return;
}
infos = node_log->lookup_available_attributes();
}
blender::Vector<const GeometryAttributeInfo *> infos = node_log->lookup_available_attributes();
GeometryAttributeInfo &dummy_info = get_dummy_item_info();
@@ -165,6 +176,38 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
bNodeSocketValueString *value = static_cast<bNodeSocketValueString *>(socket.default_value);
BLI_strncpy(value->value, item->name.c_str(), MAX_NAME);
if (data->node->type == GEO_NODE_ATTRIBUTE) {
NodeGeometryAttribute *storage = (NodeGeometryAttribute *)data->node->storage;
switch (item->data_type) {
case CD_PROP_FLOAT: {
storage->output_type = SOCK_FLOAT;
break;
}
case CD_PROP_INT32: {
storage->output_type = SOCK_INT;
break;
}
case CD_PROP_FLOAT2:
case CD_PROP_FLOAT3: {
storage->output_type = SOCK_VECTOR;
break;
}
case CD_PROP_BOOL: {
storage->output_type = SOCK_BOOLEAN;
break;
}
case CD_PROP_COLOR: {
storage->output_type = SOCK_RGBA;
break;
}
default:
BLI_assert_unreachable();
break;
}
snode_update(CTX_wm_space_node(C), (bNode *)data->node);
ntreeUpdateTree(CTX_data_main(C), (bNodeTree *)data->tree);
}
ED_undo_push(C, "Assign Attribute Name");
}

View File

@@ -49,6 +49,9 @@ void GeometryDataSource::foreach_default_column_ids(
if (meta_data.domain != domain_) {
return true;
}
if (meta_data.anonymous_layer_id != nullptr) {
return true;
}
SpreadsheetColumnID column_id;
column_id.name = (char *)name.c_str();
fn(column_id);

View File

@@ -25,6 +25,7 @@
#include <optional>
#include "BLI_optional_ptr.hh"
#include "BLI_virtual_array.hh"
#include "FN_generic_span.hh"
@@ -260,6 +261,23 @@ class GVArray_For_GSpan : public GVArray {
GSpan get_internal_span_impl() const override;
};
class GVArray_For_OwnedGSpan : public GVArray_For_GSpan {
private:
IndexMask indices_to_destruct_;
public:
GVArray_For_OwnedGSpan(const GSpan span) : GVArray_For_OwnedGSpan(span, IndexMask(span.size()))
{
}
GVArray_For_OwnedGSpan(const GSpan span, IndexMask indices_to_destruct)
: GVArray_For_GSpan(span), indices_to_destruct_(indices_to_destruct)
{
}
~GVArray_For_OwnedGSpan();
};
class GVArray_For_Empty : public GVArray {
public:
GVArray_For_Empty(const CPPType &type) : GVArray(type, 0)
@@ -339,11 +357,21 @@ class GVArray_For_SingleValue : public GVArray_For_SingleValueRef {
/* Used to convert a typed virtual array into a generic one. */
template<typename T> class GVArray_For_VArray : public GVArray {
protected:
const VArray<T> *varray_ = nullptr;
optional_ptr<const VArray<T>> varray_;
public:
GVArray_For_VArray(const VArray<T> &varray)
: GVArray(CPPType::get<T>(), varray.size()), varray_(&varray)
: GVArray(CPPType::get<T>(), varray.size()), varray_(varray)
{
}
GVArray_For_VArray(std::unique_ptr<const VArray<T>> varray)
: GVArray_For_VArray(optional_ptr<const VArray<T>>(std::move(varray)))
{
}
GVArray_For_VArray(optional_ptr<const VArray<T>> varray)
: GVArray(CPPType::get<T>(), varray->size()), varray_(std::move(varray))
{
}
@@ -394,21 +422,31 @@ template<typename T> class GVArray_For_VArray : public GVArray {
const void *try_get_internal_varray_impl() const override
{
return varray_;
return &*varray_;
}
};
/* Used to convert any generic virtual array into a typed one. */
template<typename T> class VArray_For_GVArray : public VArray<T> {
protected:
const GVArray *varray_ = nullptr;
optional_ptr<const GVArray> varray_;
public:
VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(&varray)
VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(varray)
{
BLI_assert(varray_->type().template is<T>());
}
VArray_For_GVArray(optional_ptr<const GVArray> varray)
: VArray<T>(varray->size()), varray_(std::move(varray))
{
}
VArray_For_GVArray(GVArrayPtr varray)
: VArray_For_GVArray(optional_ptr<const GVArray>(std::move(varray)))
{
}
protected:
VArray_For_GVArray(const int64_t size) : VArray<T>(size)
{
@@ -447,15 +485,20 @@ template<typename T> class VArray_For_GVArray : public VArray<T> {
/* Used to convert an generic mutable virtual array into a typed one. */
template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArray<T> {
protected:
GVMutableArray *varray_ = nullptr;
optional_ptr<GVMutableArray> varray_;
public:
VMutableArray_For_GVMutableArray(GVMutableArray &varray)
: VMutableArray<T>(varray.size()), varray_(&varray)
: VMutableArray<T>(varray.size()), varray_(varray)
{
BLI_assert(varray.type().template is<T>());
}
VMutableArray_For_GVMutableArray(optional_ptr<GVMutableArray> varray)
: varray_(std::move(varray))
{
}
VMutableArray_For_GVMutableArray(const int64_t size) : VMutableArray<T>(size)
{
}
@@ -480,7 +523,7 @@ template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArr
Span<T> get_internal_span_impl() const override
{
return varray_->get_internal_span().template typed<T>();
return static_cast<const GVArray &>(*varray_).get_internal_span().template typed<T>();
}
bool is_single_impl() const override
@@ -499,7 +542,7 @@ template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArr
/* Used to convert any typed virtual mutable array into a generic one. */
template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableArray {
protected:
VMutableArray<T> *varray_ = nullptr;
optional_ptr<VMutableArray<T>> varray_;
public:
GVMutableArray_For_VMutableArray(VMutableArray<T> &varray)
@@ -507,6 +550,11 @@ template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableAr
{
}
GVMutableArray_For_VMutableArray(optional_ptr<VMutableArray<T>> varray)
: varray_(std::move(varray))
{
}
protected:
GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get<T>(), size)
{
@@ -529,7 +577,7 @@ template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableAr
GSpan get_internal_span_impl() const override
{
Span<T> span = varray_->get_internal_span();
Span<T> span = static_cast<const VArray<T> &>(*varray_).get_internal_span();
return span;
}
@@ -579,12 +627,12 @@ template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableAr
const void *try_get_internal_varray_impl() const override
{
return (const VArray<T> *)varray_;
return static_cast<const VArray<T> *>(&*varray_);
}
void *try_get_internal_mutable_varray_impl() override
{
return varray_;
return &*varray_;
}
};
@@ -629,56 +677,6 @@ template<typename T> class GVArray_Span : public Span<T> {
}
};
template<typename T> class GVArray_For_OwnedVArray : public GVArray_For_VArray<T> {
private:
VArrayPtr<T> owned_varray_;
public:
/* Takes ownership of varray and passes a reference to the base class. */
GVArray_For_OwnedVArray(VArrayPtr<T> varray)
: GVArray_For_VArray<T>(*varray), owned_varray_(std::move(varray))
{
}
};
template<typename T> class VArray_For_OwnedGVArray : public VArray_For_GVArray<T> {
private:
GVArrayPtr owned_varray_;
public:
/* Takes ownership of varray and passes a reference to the base class. */
VArray_For_OwnedGVArray(GVArrayPtr varray)
: VArray_For_GVArray<T>(*varray), owned_varray_(std::move(varray))
{
}
};
template<typename T>
class GVMutableArray_For_OwnedVMutableArray : public GVMutableArray_For_VMutableArray<T> {
private:
VMutableArrayPtr<T> owned_varray_;
public:
/* Takes ownership of varray and passes a reference to the base class. */
GVMutableArray_For_OwnedVMutableArray(VMutableArrayPtr<T> varray)
: GVMutableArray_For_VMutableArray<T>(*varray), owned_varray_(std::move(varray))
{
}
};
template<typename T>
class VMutableArray_For_OwnedGVMutableArray : public VMutableArray_For_GVMutableArray<T> {
private:
GVMutableArrayPtr owned_varray_;
public:
/* Takes ownership of varray and passes a reference to the base class. */
VMutableArray_For_OwnedGVMutableArray(GVMutableArrayPtr varray)
: VMutableArray_For_GVMutableArray<T>(*varray), owned_varray_(std::move(varray))
{
}
};
/* Utility to embed a typed virtual array into a generic one. This avoids one allocation and give
* the compiler more opportunity to optimize the generic virtual array. */
template<typename T, typename VArrayT>
@@ -691,7 +689,7 @@ class GVArray_For_EmbeddedVArray : public GVArray_For_VArray<T> {
GVArray_For_EmbeddedVArray(const int64_t size, Args &&...args)
: GVArray_For_VArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
{
this->varray_ = &embedded_varray_;
this->varray_ = embedded_varray_;
}
};
@@ -706,7 +704,7 @@ class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMuta
GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&...args)
: GVMutableArray_For_VMutableArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
{
this->varray_ = &embedded_varray_;
this->varray_ = embedded_varray_;
}
};

View File

@@ -215,6 +215,16 @@ GSpan GVArray_For_GSpan::get_internal_span_impl() const
return GSpan(*type_, data_, size_);
}
/* --------------------------------------------------------------------
* GVArray_For_OwnedGSpan.
*/
GVArray_For_OwnedGSpan::~GVArray_For_OwnedGSpan()
{
type_->destruct_indices((void *)data_, indices_to_destruct_);
MEM_freeN((void *)data_);
}
/* --------------------------------------------------------------------
* GVMutableArray_For_GMutableSpan.
*/

View File

@@ -31,6 +31,12 @@
extern "C" {
#endif
typedef struct AnonymousCustomDataLayerID {
int strong_references;
int tot_references;
char *debug_name;
} AnonymousCustomDataLayerID;
/** Descriptor and storage for a custom data layer. */
typedef struct CustomDataLayer {
/** Type of data in layer. */
@@ -53,6 +59,7 @@ typedef struct CustomDataLayer {
char name[64];
/** Layer data. */
void *data;
const struct AnonymousCustomDataLayerID *anonymous_id;
} CustomDataLayer;
#define MAX_CUSTOMDATA_LAYER_NAME 64
@@ -242,6 +249,9 @@ enum {
CD_FLAG_EXTERNAL = (1 << 3),
/* Indicates external data is read into memory */
CD_FLAG_IN_MEMORY = (1 << 4),
/* An anonymous custom data layer can be deleted when its identifier is not referenced
* anymore. */
CD_FLAG_ANONYMOUS = (1 << 5),
};
/* Limits */

View File

@@ -178,6 +178,7 @@ typedef enum eNodeSocketDisplayShape {
SOCK_DISPLAY_SHAPE_CIRCLE_DOT = 3,
SOCK_DISPLAY_SHAPE_SQUARE_DOT = 4,
SOCK_DISPLAY_SHAPE_DIAMOND_DOT = 5,
SOCK_DISPLAY_SHAPE_TALL_RECTANGLE = 6,
} eNodeSocketDisplayShape;
/* Socket side (input/output). */
@@ -214,6 +215,11 @@ typedef enum eNodeSocketFlag {
* type is obvious and the name takes up too much space.
*/
SOCK_HIDE_LABEL = (1 << 12),
/**
* For geometry nodes, the result is not a single value, but evaluated as a callback and
* potentially many different values.
*/
SOCK_FIELD = (1 << 13),
} eNodeSocketFlag;
/* TODO: Limit data in bNode to what we want to see saved. */
@@ -1252,16 +1258,12 @@ typedef struct NodeGeometryRotatePoints {
char _pad[3];
} NodeGeometryRotatePoints;
typedef struct NodeGeometryAlignRotationToVector {
typedef struct FunctionNodeAlignRotationToVector {
/* GeometryNodeAlignRotationToVectorAxis */
uint8_t axis;
/* GeometryNodeAlignRotationToVectorPivotAxis */
uint8_t pivot_axis;
/* GeometryNodeAttributeInputMode */
uint8_t input_type_factor;
uint8_t input_type_vector;
} NodeGeometryAlignRotationToVector;
} FunctionNodeAlignRotationToVector;
typedef struct NodeGeometryPointScale {
/* GeometryNodeAttributeInputMode */
@@ -1406,11 +1408,6 @@ typedef struct NodeGeometryCurveResample {
uint8_t mode;
} NodeGeometryCurveResample;
typedef struct NodeGeometryCurveSubdivide {
/* GeometryNodeAttributeInputMode (integer or attribute). */
uint8_t cuts_type;
} NodeGeometryCurveSubdivide;
typedef struct NodeGeometryCurveTrim {
/* GeometryNodeCurveInterpolateMode. */
uint8_t mode;
@@ -1431,12 +1428,27 @@ typedef struct NodeGeometryAttributeTransfer {
typedef struct NodeGeometryRaycast {
/* GeometryNodeRaycastMapMode. */
uint8_t mapping;
uint8_t input_type_ray_direction;
uint8_t input_type_ray_length;
char _pad[1];
} NodeGeometryRaycast;
typedef struct NodeGeometryAttributeFreeze {
/* CustomDataType. */
int8_t data_type;
/* AttributeDomain. */
int8_t domain;
} NodeGeometryAttributeFreeze;
typedef struct NodeGeometryAttribute {
/* eNodeSocketDatatype. */
int8_t output_type;
} NodeGeometryAttribute;
typedef struct NodeGeometryAttributeExtract {
/* CustomDataType. */
int8_t data_type;
/* Boolean that indicates whether the persistent attribute should be removed. */
uint8_t delete_persistent;
} NodeGeometryAttributeExtract;
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1

View File

@@ -209,6 +209,7 @@ DEF_ENUM(rna_enum_attribute_type_items)
DEF_ENUM(rna_enum_attribute_type_with_auto_items)
DEF_ENUM(rna_enum_attribute_domain_items)
DEF_ENUM(rna_enum_attribute_domain_with_auto_items)
DEF_ENUM(rna_enum_attribute_domain_no_face_corner_items)
DEF_ENUM(rna_enum_collection_color_items)

View File

@@ -75,6 +75,14 @@ const EnumPropertyItem rna_enum_attribute_domain_items[] = {
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_attribute_domain_no_face_corner_items[] = {
{ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"},
{ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"},
{ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"},
{ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"},
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = {
{ATTR_DOMAIN_AUTO, "AUTO", 0, "Auto", ""},
{ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"},

View File

@@ -555,11 +555,13 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float
ITEM_FLOAT,
{0, NULL, 0, NULL, NULL},
};
# if 0
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_int[] = {
ITEM_ATTRIBUTE,
ITEM_INTEGER,
{0, NULL, 0, NULL, NULL},
};
# endif
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_boolean[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
@@ -2068,6 +2070,20 @@ static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSE
return itemf_function_check(node_socket_data_type_items, switch_type_supported);
}
static bool attribute_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA);
}
static const EnumPropertyItem *rna_GeometryNodeAttribute_type_itemf(bContext *UNUSED(C),
PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
*r_free = true;
return itemf_function_check(node_socket_data_type_items, attribute_type_supported);
}
static bool attribute_clamp_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR);
@@ -9174,6 +9190,46 @@ static void def_geo_attribute_fill(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_attribute_extract(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeExtract", "storage");
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "data_type");
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf");
RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
prop = RNA_def_property(srna, "delete_persistent", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_ui_text(
prop, "Delete Persistent", "Delete the persistent attribute with the given name");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_attribute_freeze(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeFreeze", "storage");
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf");
RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
RNA_def_property_ui_text(prop, "Domain", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_attribute_convert(StructRNA *srna)
{
PropertyRNA *prop;
@@ -9194,6 +9250,18 @@ static void def_geo_attribute_convert(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_delete(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_no_face_corner_items);
RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
RNA_def_property_ui_text(prop, "Domain", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_attribute_math(StructRNA *srna)
{
PropertyRNA *prop;
@@ -9608,6 +9676,25 @@ static void def_geo_curve_primitive_line(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_extrude_and_move(StructRNA *srna)
{
PropertyRNA *prop;
static const EnumPropertyItem rna_node_geometry_extrude_domain_items[] = {
{0, "VERTEX", 0, "Vertex", "Extrude Vertices"},
{1, "EDGE", 0, "Edge", "Extrude Edges"},
{2, "FACE", 0, "Face", "Extrude Faces"},
{0, NULL, 0, NULL, NULL},
};
prop = RNA_def_property(srna, "extrude_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_node_geometry_extrude_domain_items);
RNA_def_property_enum_default(prop, GEO_NODE_POINT_DISTRIBUTE_RANDOM);
RNA_def_property_ui_text(prop, "Extrude Mode", "Select mesh domain to extrude");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_point_rotate(StructRNA *srna)
{
static const EnumPropertyItem type_items[] = {
@@ -9668,7 +9755,7 @@ static void def_geo_point_rotate(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_align_rotation_to_vector(StructRNA *srna)
static void def_fn_align_rotation_to_vector(StructRNA *srna)
{
static const EnumPropertyItem axis_items[] = {
{GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X,
@@ -9715,7 +9802,7 @@ static void def_geo_align_rotation_to_vector(StructRNA *srna)
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryAlignRotationToVector", "storage");
RNA_def_struct_sdna_from(srna, "FunctionNodeAlignRotationToVector", "storage");
prop = RNA_def_property(srna, "axis", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, axis_items);
@@ -9726,16 +9813,6 @@ static void def_geo_align_rotation_to_vector(StructRNA *srna)
RNA_def_property_enum_items(prop, pivot_axis_items);
RNA_def_property_ui_text(prop, "Pivot Axis", "Axis to rotate around");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "input_type_factor", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float);
RNA_def_property_ui_text(prop, "Input Type Factor", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "input_type_vector", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
RNA_def_property_ui_text(prop, "Input Type Vector", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_point_scale(StructRNA *srna)
@@ -10108,18 +10185,6 @@ static void def_geo_curve_resample(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_curve_subdivide(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSubdivide", "storage");
prop = RNA_def_property(srna, "cuts_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_int);
RNA_def_property_ui_text(prop, "Cuts Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_curve_to_points(StructRNA *srna)
{
PropertyRNA *prop;
@@ -10249,15 +10314,17 @@ static void def_geo_raycast(StructRNA *srna)
RNA_def_property_enum_items(prop, mapping_items);
RNA_def_property_ui_text(prop, "Mapping", "Mapping from the target geometry to hit points");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
prop = RNA_def_property(srna, "input_type_ray_direction", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
RNA_def_property_ui_text(prop, "Input Type Ray Direction", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
static void def_geo_attribute(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "input_type_ray_length", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float);
RNA_def_property_ui_text(prop, "Input Type Ray Length", "");
RNA_def_struct_sdna_from(srna, "NodeGeometryAttribute", "storage");
prop = RNA_def_property(srna, "output_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, node_socket_data_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttribute_type_itemf");
RNA_def_property_ui_text(prop, "Output Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}

View File

@@ -50,6 +50,7 @@
#include "BKE_attribute_math.hh"
#include "BKE_customdata.h"
#include "BKE_field.hh"
#include "BKE_geometry_set_instances.hh"
#include "BKE_global.h"
#include "BKE_idprop.h"
@@ -87,6 +88,8 @@
#include "FN_multi_function.hh"
#include "WM_types.h"
using blender::destruct_ptr;
using blender::float3;
using blender::FunctionRef;
@@ -311,23 +314,39 @@ struct SocketPropertyType {
void (*init_cpp_value)(const IDProperty &property, void *r_value);
};
static const std::string use_attribute_suffix = "_use_attribute";
static const std::string attribute_name_suffix = "_attribute_name";
static IDProperty *socket_add_property(IDProperty *settings_prop_group,
IDProperty *ui_container,
const SocketPropertyType &property_type,
const bNodeSocket &socket)
{
const char *new_prop_name = socket.identifier;
StringRefNull new_prop_name = socket.identifier;
/* Add the property actually storing the data to the modifier's group. */
IDProperty *prop = property_type.create_prop(socket, new_prop_name);
IDProperty *prop = property_type.create_prop(socket, new_prop_name.c_str());
IDP_AddToGroup(settings_prop_group, prop);
{
IDPropertyTemplate idprop = {0};
IDProperty *is_attribute_prop = IDP_New(
IDP_INT, &idprop, (new_prop_name + use_attribute_suffix).c_str());
IDP_AddToGroup(settings_prop_group, is_attribute_prop);
}
{
IDPropertyTemplate idprop = {0};
IDProperty *attribute_prop = IDP_New(
IDP_STRING, &idprop, (new_prop_name + attribute_name_suffix).c_str());
IDP_AddToGroup(settings_prop_group, attribute_prop);
}
prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
/* Make the group in the UI container group to hold the property's UI settings. */
IDProperty *prop_ui_group;
{
IDPropertyTemplate idprop = {0};
prop_ui_group = IDP_New(IDP_GROUP, &idprop, new_prop_name);
prop_ui_group = IDP_New(IDP_GROUP, &idprop, new_prop_name.c_str());
IDP_AddToGroup(ui_container, prop_ui_group);
}
@@ -402,12 +421,15 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
},
[](const IDProperty &property) { return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); },
[](const IDProperty &property, void *r_value) {
float value;
if (property.type == IDP_FLOAT) {
*(float *)r_value = IDP_Float(&property);
value = IDP_Float(&property);
}
else if (property.type == IDP_DOUBLE) {
*(float *)r_value = (float)IDP_Double(&property);
value = (float)IDP_Double(&property);
}
new (r_value) blender::bke::FieldRef<float>(
blender::bke::FieldPtr{new blender::bke::ConstantField<float>(value)});
},
};
return &float_type;
@@ -442,7 +464,11 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
return (PropertyType)((bNodeSocketValueInt *)socket.default_value)->subtype;
},
[](const IDProperty &property) { return property.type == IDP_INT; },
[](const IDProperty &property, void *r_value) { *(int *)r_value = IDP_Int(&property); },
[](const IDProperty &property, void *r_value) {
int value = IDP_Int(&property);
new (r_value) blender::bke::FieldRef<int>(
blender::bke::FieldPtr{new blender::bke::ConstantField<int>(value)});
},
};
return &int_type;
}
@@ -486,7 +512,10 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
property.len == 3;
},
[](const IDProperty &property, void *r_value) {
copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
blender::float3 value;
copy_v3_v3(value, (const float *)IDP_Array(&property));
new (r_value) blender::bke::FieldRef<blender::float3>(
blender::bke::FieldPtr{new blender::bke::ConstantField<blender::float3>(value)});
},
};
return &vector_type;
@@ -518,7 +547,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
nullptr,
[](const IDProperty &property) { return property.type == IDP_INT; },
[](const IDProperty &property, void *r_value) {
*(bool *)r_value = IDP_Int(&property) != 0;
bool value = IDP_Int(&property) != 0;
new (r_value) blender::bke::FieldRef<bool>(
blender::bke::FieldPtr{new blender::bke::ConstantField<bool>(value)});
},
};
return &boolean_type;
@@ -674,6 +705,24 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
if (old_prop != nullptr && property_type->is_correct_type(*old_prop)) {
IDP_CopyPropertyContent(new_prop, old_prop);
}
std::string use_attribute_identifier = socket->identifier + use_attribute_suffix;
IDProperty *new_prop_use_attribute = IDP_GetPropertyFromGroup(
nmd->settings.properties, use_attribute_identifier.c_str());
IDProperty *old_prop_use_attribute = IDP_GetPropertyFromGroup(
old_properties, use_attribute_identifier.c_str());
if (old_prop_use_attribute != nullptr) {
IDP_CopyPropertyContent(new_prop_use_attribute, old_prop_use_attribute);
}
std::string attribute_name_identifier = socket->identifier + attribute_name_suffix;
IDProperty *new_prop_attribute_name = IDP_GetPropertyFromGroup(
nmd->settings.properties, attribute_name_identifier.c_str());
IDProperty *old_prop_attribute_name = IDP_GetPropertyFromGroup(
old_properties, attribute_name_identifier.c_str());
if (old_prop_attribute_name != nullptr) {
IDP_CopyPropertyContent(new_prop_attribute_name, old_prop_attribute_name);
}
}
}
@@ -727,7 +776,13 @@ static void initialize_group_input(NodesModifierData &nmd,
}
const IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties,
socket.identifier);
if (property == nullptr) {
const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup(
nmd.settings.properties, (socket.identifier + use_attribute_suffix).c_str());
const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup(
nmd.settings.properties, (socket.identifier + attribute_name_suffix).c_str());
if (property == nullptr || property_use_attribute == nullptr ||
property_attribute_name == nullptr) {
socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
}
@@ -735,7 +790,19 @@ static void initialize_group_input(NodesModifierData &nmd,
socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
}
property_type->init_cpp_value(*property, r_value);
const bool use_attribute = IDP_Int(property_use_attribute) != 0;
if (use_attribute &&
ELEM(socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_INT, SOCK_BOOLEAN)) {
const char *attribute_name = IDP_String(property_attribute_name);
blender::bke::FieldPtr attribute_field = new blender::bke::PersistentAttributeField(
attribute_name, *socket.typeinfo->get_base_cpp_type());
const blender::bke::FieldRefCPPType &field_cpp_type =
dynamic_cast<const blender::bke::FieldRefCPPType &>(cpp_type);
field_cpp_type.construct(r_value, std::move(attribute_field));
}
else {
property_type->init_cpp_value(*property, r_value);
}
}
static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain)
@@ -1046,6 +1113,7 @@ static void modifyGeometrySet(ModifierData *md,
* the node socket identifier for the property names, since they are unique, but also having
* the correct label displayed in the UI. */
static void draw_property_for_socket(uiLayout *layout,
NodesModifierData *nmd,
PointerRNA *bmain_ptr,
PointerRNA *md_ptr,
const IDProperty *modifier_props,
@@ -1065,12 +1133,30 @@ static void draw_property_for_socket(uiLayout *layout,
return;
}
IDProperty *is_attribute_property = IDP_GetPropertyFromGroup(
modifier_props, (socket.identifier + use_attribute_suffix).c_str());
if (is_attribute_property == nullptr) {
return;
}
char socket_id_esc[sizeof(socket.identifier) * 2];
BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
char rna_path[sizeof(socket_id_esc) + 4];
BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc);
char rna_path_use_attribute[1024];
BLI_snprintf(rna_path_use_attribute,
ARRAY_SIZE(rna_path_use_attribute),
"[\"%s\"]",
(socket_id_esc + use_attribute_suffix).c_str());
char rna_path_attribute[1024];
BLI_snprintf(rna_path_attribute,
ARRAY_SIZE(rna_path_attribute),
"[\"%s\"]",
(socket_id_esc + attribute_name_suffix).c_str());
/* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
* information about what type of ID to select for editing the values. This is because
* pointer IDProperties contain no information about their type. */
@@ -1098,8 +1184,36 @@ static void draw_property_for_socket(uiLayout *layout,
uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "textures", socket.name, ICON_TEXTURE);
break;
}
default:
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
case SOCK_INT:
case SOCK_BOOLEAN: {
uiLayout *row = uiLayoutRow(layout, true);
const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute) != 0;
if (use_attribute) {
uiItemR(row, md_ptr, rna_path_attribute, 0, socket.name, ICON_NONE);
}
else {
uiItemR(row, md_ptr, rna_path, 0, socket.name, ICON_NONE);
}
PointerRNA props;
uiItemFullO(row,
"object.geometry_nodes_modifier_value_or_attribute_toggle",
"",
ICON_SPREADSHEET,
nullptr,
WM_OP_INVOKE_DEFAULT,
0,
&props);
RNA_string_set(&props, "modifier_name", nmd->modifier.name);
RNA_string_set(&props, "prop_path", rna_path_use_attribute);
break;
}
default: {
uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE);
break;
}
}
}
@@ -1130,7 +1244,7 @@ static void panel_draw(const bContext *C, Panel *panel)
RNA_main_pointer_create(bmain, &bmain_ptr);
LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) {
draw_property_for_socket(layout, &bmain_ptr, ptr, nmd->settings.properties, *socket);
draw_property_for_socket(layout, nmd, &bmain_ptr, ptr, nmd->settings.properties, *socket);
}
}

View File

@@ -21,6 +21,7 @@
#include "DEG_depsgraph_query.h"
#include "FN_cpp_type_make.hh"
#include "FN_generic_value_map.hh"
#include "FN_multi_function.hh"
@@ -30,8 +31,15 @@
#include "BLI_task.hh"
#include "BLI_vector_set.hh"
MAKE_FIELD_REF_CPP_TYPE(FloatFieldRef, float)
MAKE_FIELD_REF_CPP_TYPE(IntFieldRef, int)
MAKE_FIELD_REF_CPP_TYPE(BoolFieldRef, bool)
MAKE_FIELD_REF_CPP_TYPE(Float3FieldRef, blender::float3)
MAKE_FIELD_REF_CPP_TYPE(ColorFieldRef, blender::ColorGeometry4f)
namespace blender::modifiers::geometry_nodes {
using bke::FieldPtr;
using fn::CPPType;
using fn::GValueMap;
using nodes::GeoNodeExecParams;
@@ -858,11 +866,9 @@ class GeometryNodesEvaluator {
const MultiFunction &fn,
NodeState &node_state)
{
MFContextBuilder fn_context;
MFParamsBuilder fn_params{fn, 1};
LinearAllocator<> &allocator = local_allocators_.local();
/* Prepare the inputs for the multi function. */
Vector<FieldPtr> input_fields;
for (const int i : node->inputs().index_range()) {
const InputSocketRef &socket_ref = node->input(i);
if (!socket_ref.is_available()) {
@@ -873,23 +879,33 @@ class GeometryNodesEvaluator {
BLI_assert(input_state.was_ready_for_execution);
SingleInputValue &single_value = *input_state.value.single;
BLI_assert(single_value.value != nullptr);
fn_params.add_readonly_single_input(GPointer{*input_state.type, single_value.value});
}
/* Prepare the outputs for the multi function. */
Vector<GMutablePointer> outputs;
for (const int i : node->outputs().index_range()) {
const OutputSocketRef &socket_ref = node->output(i);
if (!socket_ref.is_available()) {
continue;
if (input_state.type->is<bke::FieldRef<float>>()) {
bke::FieldRef<float> field = *(bke::FieldRef<float> *)single_value.value;
input_fields.append(field.field());
}
else if (input_state.type->is<bke::FieldRef<int>>()) {
bke::FieldRef<int> field = *(bke::FieldRef<int> *)single_value.value;
input_fields.append(field.field());
}
else if (input_state.type->is<bke::FieldRef<float3>>()) {
bke::FieldRef<float3> field = *(bke::FieldRef<float3> *)single_value.value;
input_fields.append(field.field());
}
else if (input_state.type->is<bke::FieldRef<bool>>()) {
bke::FieldRef<bool> field = *(bke::FieldRef<bool> *)single_value.value;
input_fields.append(field.field());
}
else if (input_state.type->is<bke::FieldRef<blender::ColorGeometry4f>>()) {
bke::FieldRef<blender::ColorGeometry4f> field =
*(bke::FieldRef<blender::ColorGeometry4f> *)single_value.value;
input_fields.append(field.field());
}
else {
/* Not yet supported. */
BLI_assert_unreachable();
}
const CPPType &type = *get_socket_cpp_type(socket_ref);
void *buffer = allocator.allocate(type.size(), type.alignment());
fn_params.add_uninitialized_single_output(GMutableSpan{type, buffer, 1});
outputs.append({type, buffer});
}
fn.call(IndexRange(1), fn_params, fn_context);
/* Forward the computed outputs. */
int output_index = 0;
for (const int i : node->outputs().index_range()) {
@@ -897,10 +913,67 @@ class GeometryNodesEvaluator {
if (!socket_ref.is_available()) {
continue;
}
const int output_param_index = input_fields.size() + output_index;
OutputState &output_state = node_state.outputs[i];
const DOutputSocket socket{node.context(), &socket_ref};
GMutablePointer value = outputs[output_index];
this->forward_output(socket, value);
bke::FieldPtr out_field = new bke::MultiFunctionField(input_fields, fn, output_param_index);
eNodeSocketDatatype socket_data_type = (eNodeSocketDatatype)socket->typeinfo()->type;
{
bke::FieldInputs field_inputs = out_field->prepare_inputs();
if (field_inputs.tot_inputs() == 0) {
bke::FieldOutput field_output = out_field->evaluate(IndexRange(1), field_inputs);
const fn::GVArray &varray = field_output.varray_ref();
BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
varray.get_to_uninitialized(0, buffer);
if (socket_data_type == SOCK_FLOAT) {
out_field = new bke::ConstantField<float>(*(float *)buffer);
}
else if (socket_data_type == SOCK_VECTOR) {
out_field = new bke::ConstantField<float3>(*(float3 *)buffer);
}
else if (socket_data_type == SOCK_BOOLEAN) {
out_field = new bke::ConstantField<bool>(*(bool *)buffer);
}
else if (socket_data_type == SOCK_RGBA) {
out_field = new bke::ConstantField<ColorGeometry4f>(*(ColorGeometry4f *)buffer);
}
else if (socket_data_type == SOCK_INT) {
out_field = new bke::ConstantField<int>(*(int *)buffer);
}
}
}
if (socket_data_type == SOCK_FLOAT) {
bke::FieldRef<float> *field_ref =
allocator.construct<bke::FieldRef<float>>(out_field).release();
this->forward_output(socket, field_ref);
}
else if (socket_data_type == SOCK_VECTOR) {
bke::FieldRef<float3> *field_ref =
allocator.construct<bke::FieldRef<float3>>(out_field).release();
this->forward_output(socket, field_ref);
}
else if (socket_data_type == SOCK_BOOLEAN) {
bke::FieldRef<bool> *field_ref =
allocator.construct<bke::FieldRef<bool>>(out_field).release();
this->forward_output(socket, field_ref);
}
else if (socket_data_type == SOCK_RGBA) {
bke::FieldRef<blender::ColorGeometry4f> *field_ref =
allocator.construct<bke::FieldRef<blender::ColorGeometry4f>>(out_field).release();
this->forward_output(socket, field_ref);
}
else if (socket_data_type == SOCK_INT) {
bke::FieldRef<int> *field_ref =
allocator.construct<bke::FieldRef<int>>(out_field).release();
this->forward_output(socket, field_ref);
}
else {
/* Not yet supported. */
BLI_assert_unreachable();
}
output_state.has_been_computed = true;
output_index++;
}
@@ -1389,7 +1462,21 @@ class GeometryNodesEvaluator {
return;
}
if (conversions_.is_convertible(from_type, to_type)) {
const bke::FieldRefCPPType *from_field_type = dynamic_cast<const bke::FieldRefCPPType *>(
&from_type);
const bke::FieldRefCPPType *to_field_type = dynamic_cast<const bke::FieldRefCPPType *>(
&to_type);
if (from_field_type != nullptr && to_field_type != nullptr &&
conversions_.is_convertible(from_field_type->field_type(), to_field_type->field_type())) {
const MultiFunction &fn = *conversions_.get_conversion_multi_function(
MFDataType::ForSingle(from_field_type->field_type()),
MFDataType::ForSingle(to_field_type->field_type()));
FieldPtr old_field = from_field_type->get_field(from_value);
FieldPtr new_field = new bke::MultiFunctionField({old_field}, fn, 1);
to_field_type->construct(to_value, std::move(new_field));
}
else if (conversions_.is_convertible(from_type, to_type)) {
/* Do the conversion if possible. */
conversions_.convert_to_uninitialized(from_type, to_type, from_value, to_value);
}

View File

@@ -138,15 +138,18 @@ set(SRC
function/nodes/node_fn_input_string.cc
function/nodes/node_fn_input_vector.cc
function/nodes/node_fn_random_float.cc
function/nodes/node_fn_align_rotation_to_vector.cc
function/node_function_util.cc
geometry/nodes/node_geo_align_rotation_to_vector.cc
geometry/nodes/node_geo_attribute_freeze.cc
geometry/nodes/node_geo_attribute.cc
geometry/nodes/node_geo_attribute_clamp.cc
geometry/nodes/node_geo_attribute_color_ramp.cc
geometry/nodes/node_geo_attribute_combine_xyz.cc
geometry/nodes/node_geo_attribute_compare.cc
geometry/nodes/node_geo_attribute_convert.cc
geometry/nodes/node_geo_attribute_curve_map.cc
geometry/nodes/node_geo_attribute_extract.cc
geometry/nodes/node_geo_attribute_fill.cc
geometry/nodes/node_geo_attribute_map_range.cc
geometry/nodes/node_geo_attribute_math.cc
@@ -166,6 +169,7 @@ set(SRC
geometry/nodes/node_geo_convex_hull.cc
geometry/nodes/node_geo_curve_endpoints.cc
geometry/nodes/node_geo_curve_length.cc
geometry/nodes/node_geo_curve_parameter.cc
geometry/nodes/node_geo_curve_primitive_bezier_segment.cc
geometry/nodes/node_geo_curve_primitive_circle.cc
geometry/nodes/node_geo_curve_primitive_line.cc
@@ -184,6 +188,10 @@ set(SRC
geometry/nodes/node_geo_curve_trim.cc
geometry/nodes/node_geo_delete_geometry.cc
geometry/nodes/node_geo_edge_split.cc
geometry/nodes/node_geo_evaluate_curve.cc
geometry/nodes/node_geo_extrude.cc
geometry/nodes/node_geo_extrude_and_move.cc
geometry/nodes/node_geo_index.cc
geometry/nodes/node_geo_input_material.cc
geometry/nodes/node_geo_is_viewport.cc
geometry/nodes/node_geo_join_geometry.cc
@@ -199,6 +207,7 @@ set(SRC
geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
geometry/nodes/node_geo_mesh_subdivide.cc
geometry/nodes/node_geo_mesh_to_curve.cc
geometry/nodes/node_geo_normal.cc
geometry/nodes/node_geo_object_info.cc
geometry/nodes/node_geo_point_distribute.cc
geometry/nodes/node_geo_point_instance.cc
@@ -207,9 +216,12 @@ set(SRC
geometry/nodes/node_geo_point_separate.cc
geometry/nodes/node_geo_point_translate.cc
geometry/nodes/node_geo_points_to_volume.cc
geometry/nodes/node_geo_position.cc
geometry/nodes/node_geo_raycast.cc
geometry/nodes/node_geo_sample_mesh_surface.cc
geometry/nodes/node_geo_select_by_material.cc
geometry/nodes/node_geo_separate_components.cc
geometry/nodes/node_geo_set_position.cc
geometry/nodes/node_geo_subdivision_surface.cc
geometry/nodes/node_geo_switch.cc
geometry/nodes/node_geo_transform.cc
@@ -289,7 +301,7 @@ set(SRC
shader/nodes/node_shader_tex_image.c
shader/nodes/node_shader_tex_magic.c
shader/nodes/node_shader_tex_musgrave.c
shader/nodes/node_shader_tex_noise.c
shader/nodes/node_shader_tex_noise.cc
shader/nodes/node_shader_tex_pointdensity.c
shader/nodes/node_shader_tex_sky.c
shader/nodes/node_shader_tex_voronoi.c

View File

@@ -26,6 +26,7 @@ void register_node_type_fn_float_to_int(void);
void register_node_type_fn_input_string(void);
void register_node_type_fn_input_vector(void);
void register_node_type_fn_random_float(void);
void register_node_type_fn_align_rotation_to_vector(void);
#ifdef __cplusplus
}

View File

@@ -29,7 +29,10 @@ void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
void register_node_type_geo_custom_group(bNodeType *ntype);
void register_node_type_geo_align_rotation_to_vector(void);
void register_node_type_geo_extrude(void);
void register_node_type_geo_attribute_freeze(void);
void register_node_type_geo_attribute(void);
void register_node_type_geo_attribute_clamp(void);
void register_node_type_geo_attribute_color_ramp(void);
void register_node_type_geo_attribute_combine_xyz(void);
@@ -40,6 +43,7 @@ void register_node_type_geo_attribute_fill(void);
void register_node_type_geo_attribute_map_range(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_attribute_mix(void);
void register_node_type_geo_attribute_extract(void);
void register_node_type_geo_attribute_proximity(void);
void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_remove(void);
@@ -53,6 +57,7 @@ void register_node_type_geo_collection_info(void);
void register_node_type_geo_convex_hull(void);
void register_node_type_geo_curve_endpoints(void);
void register_node_type_geo_curve_length(void);
void register_node_type_geo_curve_parameter(void);
void register_node_type_geo_curve_primitive_bezier_segment(void);
void register_node_type_geo_curve_primitive_circle(void);
void register_node_type_geo_curve_primitive_line(void);
@@ -70,6 +75,8 @@ void register_node_type_geo_curve_to_points(void);
void register_node_type_geo_curve_trim(void);
void register_node_type_geo_delete_geometry(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_evaluate_curve(void);
void register_node_type_geo_index(void);
void register_node_type_geo_input_material(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);
@@ -84,6 +91,7 @@ void register_node_type_geo_mesh_primitive_ico_sphere(void);
void register_node_type_geo_mesh_primitive_line(void);
void register_node_type_geo_mesh_primitive_uv_sphere(void);
void register_node_type_geo_mesh_subdivide(void);
void register_node_type_geo_normal(void);
void register_node_type_geo_mesh_to_curve(void);
void register_node_type_geo_object_info(void);
void register_node_type_geo_point_distribute(void);
@@ -93,17 +101,21 @@ void register_node_type_geo_point_scale(void);
void register_node_type_geo_point_separate(void);
void register_node_type_geo_point_translate(void);
void register_node_type_geo_points_to_volume(void);
void register_node_type_geo_position(void);
void register_node_type_geo_raycast(void);
void register_node_type_geo_sample_mesh_surface(void);
void register_node_type_geo_sample_texture(void);
void register_node_type_geo_select_by_handle_type(void);
void register_node_type_geo_select_by_material(void);
void register_node_type_geo_separate_components(void);
void register_node_type_geo_set_position(void);
void register_node_type_geo_subdivision_surface(void);
void register_node_type_geo_switch(void);
void register_node_type_geo_transform(void);
void register_node_type_geo_triangulate(void);
void register_node_type_geo_viewer(void);
void register_node_type_geo_volume_to_mesh(void);
void register_node_type_geo_extrude_and_move(void);
#ifdef __cplusplus
}

View File

@@ -19,6 +19,7 @@
#include "FN_generic_value_map.hh"
#include "BKE_attribute_access.hh"
#include "BKE_field.hh"
#include "BKE_geometry_set.hh"
#include "BKE_geometry_set_instances.hh"
@@ -32,6 +33,7 @@ struct ModifierData;
namespace blender::nodes {
using bke::FieldPtr;
using bke::geometry_set_realize_instances;
using bke::OutputAttribute;
using bke::OutputAttribute_Typed;
@@ -142,11 +144,28 @@ class GeoNodeExecParams {
*/
template<typename T> T extract_input(StringRef identifier)
{
#ifdef DEBUG
this->check_input_access(identifier, &CPPType::get<T>());
if constexpr (std::is_same_v<T, int> || std::is_same_v<T, float> ||
std::is_same_v<T, float3> || std::is_same_v<T, ColorGeometry4f> ||
std::is_same_v<T, bool>) {
#ifdef DEBUGm
this->check_input_access(identifier, &CPPType::get<bke::FieldRef<T>>());
#endif
GMutablePointer gvalue = this->extract_input(identifier);
return gvalue.relocate_out<T>();
GMutablePointer gvalue = this->extract_input(identifier);
BLI_assert(gvalue.is_type<bke::FieldRef<T>>());
bke::FieldRef<T> field = gvalue.relocate_out<bke::FieldRef<T>>();
bke::FieldInputs inputs = field->prepare_inputs();
bke::FieldOutput output = field->evaluate(IndexRange(1), inputs);
T value;
output.varray_ref().get(0, &value);
return value;
}
else {
#ifdef DEBUG
this->check_input_access(identifier, &CPPType::get<T>());
#endif
GMutablePointer gvalue = this->extract_input(identifier);
return gvalue.relocate_out<T>();
}
}
/**
@@ -167,14 +186,40 @@ class GeoNodeExecParams {
/**
* Get the input value for the input socket with the given identifier.
*/
template<typename T> const T &get_input(StringRef identifier) const
template<typename T> T get_input(StringRef identifier) const
{
GPointer gvalue = provider_->get_input(identifier);
if constexpr (std::is_same_v<T, int> || std::is_same_v<T, float> ||
std::is_same_v<T, float3> || std::is_same_v<T, ColorGeometry4f> ||
std::is_same_v<T, bool>) {
#ifdef DEBUG
this->check_input_access(identifier, &CPPType::get<bke::FieldRef<T>>());
#endif
BLI_assert(gvalue.is_type<bke::FieldRef<T>>());
bke::FieldRef<T> field = *gvalue.get<bke::FieldRef<T>>();
bke::FieldInputs inputs = field->prepare_inputs();
bke::FieldOutput output = field->evaluate(IndexRange(1), inputs);
T value;
output.varray_ref().get(0, &value);
return value;
}
else {
#ifdef DEBUG
this->check_input_access(identifier, &CPPType::get<T>());
#endif
BLI_assert(gvalue.is_type<T>());
return *(const T *)gvalue.get();
}
}
template<typename T> bke::FieldRef<T> get_input_field(StringRef identifier) const
{
#ifdef DEBUG
this->check_input_access(identifier, &CPPType::get<T>());
this->check_input_access(identifier, &CPPType::get<bke::FieldRef<T>>());
#endif
GPointer gvalue = provider_->get_input(identifier);
BLI_assert(gvalue.is_type<T>());
return *(const T *)gvalue.get();
BLI_assert(gvalue.is_type<bke::FieldRef<T>>());
return *(const bke::FieldRef<T> *)gvalue.get<bke::FieldRef<T>>();
}
/**

View File

@@ -299,6 +299,7 @@ class ModifierLog {
static const NodeLog *find_node_by_spreadsheet_editor_context(
const SpaceSpreadsheet &sspreadsheet);
void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const;
Vector<const GeometryAttributeInfo *> lookup_available_attributes() const;
private:
using LogByTreeContext = Map<const DTreeContext *, TreeLog *>;

View File

@@ -267,19 +267,24 @@ DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", Fl
DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "")
DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
DefNode(FunctionNode, FN_NODE_ALIGN_ROTATION_TO_VECTOR, def_fn_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
DefNode(GeometryNode, GEO_NODE_EXTRUDE, 0, "EXTRUDE", Extrude, "Mesh Extrude", "")
DefNode(GeometryNode, GEO_NODE_EXTRUDE_AND_MOVE, def_geo_extrude_and_move, "EXTRUDE_AND_MOVE", ExtrudeAndMove, "Mesh Extrude And Move", "")
DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "ATTRIBUTE_CURVE_MAP", AttributeCurveMap, "Attribute Curve Map", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_EXTRACT, def_geo_attribute_extract, "ATTRIBUTE_EXTRACT", AttributeExtract, "Attribute Extract", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Store Persistent Attribute", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Proximity", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, 0, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
@@ -287,12 +292,14 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separat
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE, def_geo_attribute, "ATTRIBUTE", Attribute, "Attribute", "")
DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "")
DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "")
DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "")
@@ -305,12 +312,14 @@ DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse,
DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "")
DefNode(GeometryNode, GEO_NODE_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "CURVE_SELECT_HANDLES", CurveSelectHandles, "Select by Handle Type", "")
DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "")
DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "")
DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, 0, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "")
DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
DefNode(GeometryNode, GEO_NODE_EVALUATE_CURVE, 0, "EVALUATE_CURVE", EvaluateCurve, "Evaluate Curve", "")
DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
DefNode(GeometryNode, GEO_NODE_INDEX, 0, "INDEX", Index, "Index", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
@@ -326,6 +335,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRI
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "")
DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
DefNode(GeometryNode, GEO_NODE_NORMAL, 0, "NORMAL", Normal, "Face Normal", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "")
@@ -334,8 +344,11 @@ DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE",
DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "")
DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
DefNode(GeometryNode, GEO_NODE_POSITION, 0, "POSITION", Position, "Position", "")
DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
DefNode(GeometryNode, GEO_NODE_SAMPLE_MESH_SURFACE, 0, "SAMPLE_MESH_SURFACE", SampleMeshSurface, "Sample Mesh Surface", "")
DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "")
DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
@@ -343,6 +356,7 @@ DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform"
DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
DefNode(GeometryNode, GEO_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "")
DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FREEZE, def_geo_attribute_freeze, "ATTRIBUTE_FREEZE", AttributeFreeze, "Attribute Freeze", "")
/* undefine macros */
#undef DefNode

View File

@@ -0,0 +1,191 @@
/*
* 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_math_rotation.h"
#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_function_util.hh"
using blender::float3;
static bNodeSocketTemplate fn_node_align_rotation_to_vector_in[] = {
{SOCK_VECTOR, N_("Rotation"), 1.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_EULER},
{SOCK_FLOAT, N_("Factor"), 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, PROP_FACTOR},
{SOCK_VECTOR, N_("Vector"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX},
{-1, ""},
};
static bNodeSocketTemplate fn_node_align_rotation_to_vector_out[] = {
{SOCK_VECTOR, N_("Rotation")},
{-1, ""},
};
static void fn_node_align_rotation_to_vector_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "pivot_axis", 0, IFACE_("Pivot"), ICON_NONE);
}
static void fn_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
{
FunctionNodeAlignRotationToVector *node_storage = (FunctionNodeAlignRotationToVector *)
MEM_callocN(sizeof(FunctionNodeAlignRotationToVector), __func__);
node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
node->storage = node_storage;
}
static float3 align_rotations_auto_pivot(const float3 vector,
const float factor,
const float3 local_main_axis,
const float3 old_rotation_euler)
{
if (is_zero_v3(vector)) {
return float3(0);
}
float old_rotation[3][3];
eul_to_mat3(old_rotation, old_rotation_euler);
float3 old_axis;
mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
const float3 new_axis = vector.normalized();
float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
if (is_zero_v3(rotation_axis)) {
/* The vectors are linearly dependent, so we fall back to another axis. */
rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
if (is_zero_v3(rotation_axis)) {
/* This is now guaranteed to not be zero. */
rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
}
}
const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
const float angle = factor * full_angle;
float rotation[3][3];
axis_angle_to_mat3(rotation, rotation_axis, angle);
float new_rotation_matrix[3][3];
mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
float3 new_rotation;
mat3_to_eul(new_rotation, new_rotation_matrix);
return new_rotation;
}
static float3 align_rotations_fixed_pivot(const float3 vector,
const float factor,
const float3 local_main_axis,
const float3 local_pivot_axis,
const float3 old_rotation_euler)
{
if (is_zero_v3(vector)) {
return float3(0);
}
float old_rotation[3][3];
eul_to_mat3(old_rotation, old_rotation_euler);
float3 old_axis;
mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
float3 pivot_axis;
mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
if (full_angle > M_PI) {
/* Make sure the point is rotated as little as possible. */
full_angle -= 2.0f * M_PI;
}
const float angle = factor * full_angle;
float rotation[3][3];
axis_angle_to_mat3(rotation, pivot_axis, angle);
float new_rotation_matrix[3][3];
mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
float3 new_rotation;
mat3_to_eul(new_rotation, new_rotation_matrix);
return new_rotation;
}
static const blender::fn::MultiFunction &get_multi_function(bNode &node)
{
const FunctionNodeAlignRotationToVector &storage = *(const FunctionNodeAlignRotationToVector *)
node.storage;
float3 local_main_axis{0, 0, 0};
local_main_axis[storage.axis] = 1;
if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) {
static blender::fn::CustomMF_SI_SI_SI_SO<float3, float, float3, float3> auto_pivot{
"Align Rotation Auto Pivot",
[local_main_axis](float3 rotation, float factor, float3 vector) {
return align_rotations_auto_pivot(vector, factor, local_main_axis, rotation);
}};
return auto_pivot;
}
float3 local_pivot_axis{0, 0, 0};
local_pivot_axis[storage.pivot_axis - 1] = 1;
if (local_main_axis == local_pivot_axis) {
return blender::fn::dummy_multi_function;
}
static blender::fn::CustomMF_SI_SI_SI_SO<float3, float, float3, float3> fixed_pivot{
"Align Rotation Fixed Pivot",
[local_main_axis, local_pivot_axis](float3 rotation, float factor, float3 vector) {
return align_rotations_fixed_pivot(
vector, factor, local_main_axis, local_pivot_axis, rotation);
}};
return fixed_pivot;
}
static void fn_node_align_rotation_to_vector_build_multi_function(
blender::nodes::NodeMultiFunctionBuilder &builder)
{
const blender::fn::MultiFunction &fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
void register_node_type_fn_align_rotation_to_vector()
{
static bNodeType ntype;
fn_node_type_base(&ntype,
FN_NODE_ALIGN_ROTATION_TO_VECTOR,
"Align Rotation to Vector",
NODE_CLASS_OP_VECTOR,
0);
node_type_socket_templates(
&ntype, fn_node_align_rotation_to_vector_in, fn_node_align_rotation_to_vector_out);
node_type_init(&ntype, fn_node_align_rotation_to_vector_init);
node_type_storage(&ntype,
"FunctionNodeAlignRotationToVector",
node_free_standard_storage,
node_copy_standard_storage);
ntype.build_multi_function = fn_node_align_rotation_to_vector_build_multi_function;
ntype.draw_buttons = fn_node_align_rotation_to_vector_layout;
nodeRegisterType(&ntype);
}

View File

@@ -23,11 +23,48 @@
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
#include "NOD_type_conversions.hh"
namespace blender::nodes {
using bke::GeometryInstanceGroup;
static void update_multi_type_socket_availabilities(ListBase &socket_list,
const StringRef name,
const CustomDataType type,
const bool name_is_available)
{
LISTBASE_FOREACH (bNodeSocket *, socket, &socket_list) {
if (name == socket->name) {
const bool socket_is_available = name_is_available &&
((socket->type == SOCK_STRING && type == CD_PROP_STRING) ||
(socket->type == SOCK_FLOAT && type == CD_PROP_FLOAT) ||
(socket->type == SOCK_INT && type == CD_PROP_INT32) ||
(socket->type == SOCK_VECTOR && type == CD_PROP_FLOAT3) ||
(socket->type == SOCK_RGBA && type == CD_PROP_COLOR));
nodeSetSocketAvailability(socket, socket_is_available);
}
}
}
void update_multi_type_input_socket_availabilities(bNode &node,
const StringRef name,
const CustomDataType type,
const bool name_is_available)
{
update_multi_type_socket_availabilities(node.inputs, name, type, name_is_available);
}
void update_multi_type_output_socket_availabilities(bNode &node,
const StringRef name,
const CustomDataType type,
const bool name_is_available)
{
update_multi_type_socket_availabilities(node.outputs, name, type, name_is_available);
}
/**
* Update the availability of a group of input sockets with the same name,
* used for switching between attribute inputs or single values.
@@ -55,6 +92,192 @@ void update_attribute_input_socket_availabilities(bNode &node,
}
}
void prepare_field_inputs(bke::FieldInputs &field_inputs,
const GeometryComponent &component,
const AttributeDomain domain,
Vector<std::unique_ptr<bke::FieldInputValue>> &r_values)
{
const int domain_size = component.attribute_domain_size(domain);
for (const bke::FieldInputKey &key : field_inputs) {
std::unique_ptr<bke::FieldInputValue> input_value;
if (const bke::PersistentAttributeFieldInputKey *persistent_attribute_key =
dynamic_cast<const bke::PersistentAttributeFieldInputKey *>(&key)) {
const StringRef name = persistent_attribute_key->name();
const CPPType &cpp_type = persistent_attribute_key->type();
const CustomDataType type = bke::cpp_type_to_custom_data_type(cpp_type);
GVArrayPtr attribute = component.attribute_get_for_read(name, domain, type);
input_value = std::make_unique<bke::GVArrayFieldInputValue>(std::move(attribute));
}
else if (dynamic_cast<const bke::IndexFieldInputKey *>(&key) != nullptr) {
auto index_func = [](int i) { return i; };
VArrayPtr<int> index_varray = std::make_unique<VArray_For_Func<int, decltype(index_func)>>(
domain_size, index_func);
GVArrayPtr index_gvarray = std::make_unique<fn::GVArray_For_VArray<int>>(
std::move(index_varray));
input_value = std::make_unique<bke::GVArrayFieldInputValue>(std::move(index_gvarray));
}
else if (const bke::AnonymousAttributeFieldInputKey *anonymous_attribute_key =
dynamic_cast<const bke::AnonymousAttributeFieldInputKey *>(&key)) {
const AnonymousCustomDataLayerID &layer_id = anonymous_attribute_key->layer_id();
ReadAttributeLookup attribute = component.attribute_try_get_anonymous_for_read(layer_id);
if (!attribute) {
continue;
}
GVArrayPtr varray = std::move(attribute.varray);
if (attribute.domain != domain) {
/* TODO: Not all boolean attributes are selections. */
if (varray->type().is<bool>() && component.type() == GEO_COMPONENT_TYPE_MESH) {
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
VArrayPtr<bool> varray_bool = std::make_unique<fn::VArray_For_GVArray<bool>>(
std::move(varray));
varray_bool = mesh_component.adapt_selection(
std::move(varray_bool), attribute.domain, domain);
if (!varray_bool) {
continue;
}
varray = std::make_unique<fn::GVArray_For_VArray<bool>>(std::move(varray_bool));
}
else {
varray = component.attribute_try_adapt_domain(
std::move(varray), attribute.domain, domain);
}
}
if (!varray) {
continue;
}
const CPPType &type = anonymous_attribute_key->type();
if (varray->type() != type) {
const blender::nodes::DataTypeConversions &conversions = get_implicit_type_conversions();
varray = conversions.try_convert(std::move(varray), type);
}
if (!varray) {
continue;
}
input_value = std::make_unique<bke::GVArrayFieldInputValue>(std::move(varray));
}
else if (dynamic_cast<const bke::CurveParameterFieldInputKey *>(&key)) {
if (component.type() != GEO_COMPONENT_TYPE_CURVE) {
continue;
}
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
const CurveEval *curve = curve_component.get_for_read();
if (curve == nullptr) {
continue;
}
Span<SplinePtr> splines = curve->splines();
Array<int> offsets = curve->control_point_offsets();
Array<float> parameters(offsets.last());
for (const int i_spline : splines.index_range()) {
const int offset = offsets[i_spline];
MutableSpan<float> spline_parameters = parameters.as_mutable_span().slice(
offset, offsets[i_spline + 1] - offset);
spline_parameters.first() = 0.0f;
const Spline &spline = *splines[i_spline];
const Span<float> lengths_eval = spline.evaluated_lengths();
const float total_length_inv = spline.length() == 0.0f ? 0.0f : 1.0f / spline.length();
switch (spline.type()) {
case Spline::Type::Bezier: {
const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline);
const Span<int> control_point_offsets = bezier_spline.control_point_offsets();
for (const int i : IndexRange(1, spline.size() - 1)) {
spline_parameters[i] = lengths_eval[control_point_offsets[i] - 1];
}
break;
}
case Spline::Type::Poly: {
if (spline.is_cyclic()) {
spline_parameters.drop_front(1).copy_from(lengths_eval.drop_back(1));
}
else {
spline_parameters.drop_front(1).copy_from(lengths_eval);
}
break;
}
case Spline::Type::NURBS: {
/* Instead of doing something totally arbirary and wrong for the prototype, just do
* nothing currently. Consult NURBS experts or something or document this heavily if
* it ever makes it to master. */
parameters.as_mutable_span().slice(offset, offsets[i_spline + 1]).fill(0.0f);
break;
}
}
for (float &parameter : spline_parameters) {
parameter *= total_length_inv;
}
}
GVArrayPtr varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<float>>>(
std::move(parameters));
input_value = std::make_unique<bke::GVArrayFieldInputValue>(std::move(varray));
}
field_inputs.set_input(key, *input_value);
r_values.append(std::move(input_value));
}
}
template<typename T>
void fill_attribute_impl(GeometryComponent &component,
OutputAttribute &attribute,
const bke::Field &field)
{
const AttributeDomain domain = attribute.domain();
const int domain_size = attribute->size();
bke::FieldInputs field_inputs = field.prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> input_values;
prepare_field_inputs(field_inputs, component, domain, input_values);
bke::FieldOutput field_output = field.evaluate(IndexMask(domain_size), field_inputs);
for (const int i : IndexRange(domain_size)) {
T value;
field_output.varray_ref().get(i, &value);
attribute->set_by_copy(i, &value);
}
}
void try_freeze_field_on_geometry(GeometryComponent &component,
const AnonymousCustomDataLayerID &layer_id,
AttributeDomain domain,
const bke::Field &field)
{
const CustomDataType data_type = bke::cpp_type_to_custom_data_type(field.output_type());
OutputAttribute attribute = component.attribute_try_get_anonymous_for_output(
layer_id, domain, data_type);
if (!attribute) {
return;
}
switch (data_type) {
case CD_PROP_FLOAT: {
fill_attribute_impl<float>(component, attribute, field);
break;
}
case CD_PROP_FLOAT3: {
fill_attribute_impl<float3>(component, attribute, field);
break;
}
case CD_PROP_COLOR: {
fill_attribute_impl<ColorGeometry4f>(component, attribute, field);
break;
}
case CD_PROP_BOOL: {
fill_attribute_impl<bool>(component, attribute, field);
break;
}
case CD_PROP_INT32: {
fill_attribute_impl<int>(component, attribute, field);
break;
}
default:
break;
}
attribute.save();
}
} // namespace blender::nodes
bool geo_node_poll_default(bNodeType *UNUSED(ntype),

View File

@@ -46,6 +46,16 @@ void update_attribute_input_socket_availabilities(bNode &node,
const GeometryNodeAttributeInputMode mode,
const bool name_is_available = true);
void update_multi_type_input_socket_availabilities(bNode &node,
const StringRef name,
const CustomDataType type,
const bool name_is_available = true);
void update_multi_type_output_socket_availabilities(bNode &node,
const StringRef name,
const CustomDataType type,
const bool name_is_available = true);
Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
const AttributeDomain domain);
@@ -92,4 +102,14 @@ void curve_create_default_rotation_attribute(Span<float3> tangents,
Span<float3> normals,
MutableSpan<float3> rotations);
void prepare_field_inputs(bke::FieldInputs &field_inputs,
const GeometryComponent &component,
const AttributeDomain domain,
Vector<std::unique_ptr<bke::FieldInputValue>> &r_values);
void try_freeze_field_on_geometry(GeometryComponent &component,
const AnonymousCustomDataLayerID &layer_id,
AttributeDomain domain,
const bke::Field &field);
} // namespace blender::nodes

View File

@@ -1,242 +0,0 @@
/*
* 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_math_rotation.h"
#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_align_rotation_to_vector_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Factor")},
{SOCK_FLOAT, N_("Factor"), 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, PROP_FACTOR},
{SOCK_STRING, N_("Vector")},
{SOCK_VECTOR, N_("Vector"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX, PROP_ANGLE},
{-1, ""},
};
static bNodeSocketTemplate geo_node_align_rotation_to_vector_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_node_align_rotation_to_vector_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "pivot_axis", 0, IFACE_("Pivot"), ICON_NONE);
uiLayout *col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "input_type_factor", 0, IFACE_("Factor"), ICON_NONE);
uiItemR(col, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE);
}
namespace blender::nodes {
static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__);
node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
node->storage = node_storage;
}
static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
node->storage;
update_attribute_input_socket_availabilities(
*node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
update_attribute_input_socket_availabilities(
*node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
}
static void align_rotations_auto_pivot(const VArray<float3> &vectors,
const VArray<float> &factors,
const float3 local_main_axis,
const MutableSpan<float3> rotations)
{
threading::parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
for (const int i : range) {
const float3 vector = vectors[i];
if (is_zero_v3(vector)) {
continue;
}
float old_rotation[3][3];
eul_to_mat3(old_rotation, rotations[i]);
float3 old_axis;
mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
const float3 new_axis = vector.normalized();
float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
if (is_zero_v3(rotation_axis)) {
/* The vectors are linearly dependent, so we fall back to another axis. */
rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
if (is_zero_v3(rotation_axis)) {
/* This is now guaranteed to not be zero. */
rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
}
}
const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
const float angle = factors[i] * full_angle;
float rotation[3][3];
axis_angle_to_mat3(rotation, rotation_axis, angle);
float new_rotation_matrix[3][3];
mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
float3 new_rotation;
mat3_to_eul(new_rotation, new_rotation_matrix);
rotations[i] = new_rotation;
}
});
}
static void align_rotations_fixed_pivot(const VArray<float3> &vectors,
const VArray<float> &factors,
const float3 local_main_axis,
const float3 local_pivot_axis,
const MutableSpan<float3> rotations)
{
if (local_main_axis == local_pivot_axis) {
/* Can't compute any meaningful rotation angle in this case. */
return;
}
threading::parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
for (const int i : range) {
const float3 vector = vectors[i];
if (is_zero_v3(vector)) {
continue;
}
float old_rotation[3][3];
eul_to_mat3(old_rotation, rotations[i]);
float3 old_axis;
mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
float3 pivot_axis;
mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
if (full_angle > M_PI) {
/* Make sure the point is rotated as little as possible. */
full_angle -= 2.0f * M_PI;
}
const float angle = factors[i] * full_angle;
float rotation[3][3];
axis_angle_to_mat3(rotation, pivot_axis, angle);
float new_rotation_matrix[3][3];
mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
float3 new_rotation;
mat3_to_eul(new_rotation, new_rotation_matrix);
rotations[i] = new_rotation;
}
});
}
static void align_rotations_on_component(GeometryComponent &component,
const GeoNodeExecParams &params)
{
const bNode &node = params.node();
const NodeGeometryAlignRotationToVector &storage = *(const NodeGeometryAlignRotationToVector *)
node.storage;
OutputAttribute_Typed<float3> rotations = component.attribute_try_get_for_output<float3>(
"rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
if (!rotations) {
return;
}
GVArray_Typed<float> factors = params.get_input_attribute<float>(
"Factor", component, ATTR_DOMAIN_POINT, 1.0f);
GVArray_Typed<float3> vectors = params.get_input_attribute<float3>(
"Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1});
float3 local_main_axis{0, 0, 0};
local_main_axis[storage.axis] = 1;
if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) {
align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations.as_span());
}
else {
float3 local_pivot_axis{0, 0, 0};
local_pivot_axis[storage.pivot_axis - 1] = 1;
align_rotations_fixed_pivot(
vectors, factors, local_main_axis, local_pivot_axis, rotations.as_span());
}
rotations.save();
}
static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
if (geometry_set.has<MeshComponent>()) {
align_rotations_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
}
if (geometry_set.has<PointCloudComponent>()) {
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);
}
} // namespace blender::nodes
void register_node_type_geo_align_rotation_to_vector()
{
static bNodeType ntype;
geo_node_type_base(&ntype,
GEO_NODE_ALIGN_ROTATION_TO_VECTOR,
"Align Rotation to Vector",
NODE_CLASS_GEOMETRY,
0);
node_type_socket_templates(
&ntype, geo_node_align_rotation_to_vector_in, geo_node_align_rotation_to_vector_out);
node_type_init(&ntype, blender::nodes::geo_node_align_rotation_to_vector_init);
node_type_update(&ntype, blender::nodes::geo_node_align_rotation_to_vector_update);
node_type_storage(&ntype,
"NodeGeometryAlignRotationToVector",
node_free_standard_storage,
node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_align_rotation_to_vector_exec;
ntype.draw_buttons = geo_node_align_rotation_to_vector_layout;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,119 @@
/*
* 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 "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_attribute_in[] = {
{SOCK_STRING, N_("Name")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_out[] = {
{SOCK_FLOAT, N_("Attribute")},
{SOCK_INT, N_("Attribute")},
{SOCK_BOOLEAN, N_("Attribute")},
{SOCK_VECTOR, N_("Attribute")},
{SOCK_RGBA, N_("Attribute")},
{-1, ""},
};
static void geo_node_attribute_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "output_type", 0, "", ICON_NONE);
}
static void geo_node_attribute_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryAttribute *data = (NodeGeometryAttribute *)MEM_callocN(sizeof(NodeGeometryAttribute),
__func__);
data->output_type = SOCK_FLOAT;
node->storage = data;
}
namespace blender::nodes {
static void geo_node_attribute_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryAttribute *node_storage = (NodeGeometryAttribute *)node->storage;
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
nodeSetSocketAvailability(socket,
socket->type == (eNodeSocketDatatype)node_storage->output_type);
}
}
static const CPPType *get_cpp_type(const eNodeSocketDatatype data_type)
{
switch (data_type) {
case SOCK_FLOAT:
return &CPPType::get<float>();
case SOCK_VECTOR:
return &CPPType::get<float3>();
case SOCK_RGBA:
return &CPPType::get<ColorGeometry4f>();
case SOCK_BOOLEAN:
return &CPPType::get<bool>();
case SOCK_INT:
return &CPPType::get<int>();
default:
return nullptr;
}
}
static void geo_node_attribute_exec(GeoNodeExecParams params)
{
NodeGeometryAttribute *node_storage = (NodeGeometryAttribute *)params.node().storage;
std::string name = params.extract_input<std::string>("Name");
const CPPType *cpp_type = get_cpp_type((eNodeSocketDatatype)node_storage->output_type);
BLI_assert(cpp_type != nullptr);
bke::FieldPtr field = new bke::PersistentAttributeField(std::move(name), *cpp_type);
if (cpp_type->is<float>()) {
params.set_output("Attribute", bke::FieldRef<float>(std::move(field)));
}
else if (cpp_type->is<int>()) {
params.set_output("Attribute_001", bke::FieldRef<int>(std::move(field)));
}
else if (cpp_type->is<bool>()) {
params.set_output("Attribute_002", bke::FieldRef<bool>(std::move(field)));
}
else if (cpp_type->is<float3>()) {
params.set_output("Attribute_003", bke::FieldRef<float3>(std::move(field)));
}
else if (cpp_type->is<ColorGeometry4f>()) {
params.set_output("Attribute_004", bke::FieldRef<ColorGeometry4f>(std::move(field)));
}
}
} // namespace blender::nodes
void register_node_type_geo_attribute()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE, "Attribute", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, geo_node_attribute_in, geo_node_attribute_out);
node_type_init(&ntype, geo_node_attribute_init);
node_type_update(&ntype, blender::nodes::geo_node_attribute_update);
node_type_storage(
&ntype, "NodeGeometryAttribute", node_free_standard_storage, node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_exec;
ntype.draw_buttons = geo_node_attribute_layout;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,182 @@
/*
* 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 "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_attribute_extract_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_extract_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_VECTOR, N_("Value")},
{SOCK_FLOAT, N_("Value")},
{SOCK_RGBA, N_("Value")},
{SOCK_BOOLEAN, N_("Value")},
{SOCK_INT, N_("Value")},
{-1, ""},
};
static void geo_node_attribute_extract_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
uiItemR(layout, ptr, "delete_persistent", 0, nullptr, ICON_NONE);
}
static void geo_node_attribute_extract_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryAttributeExtract *storage = (NodeGeometryAttributeExtract *)MEM_callocN(
sizeof(NodeGeometryAttributeExtract), __func__);
storage->data_type = CD_PROP_FLOAT;
storage->delete_persistent = false;
node->storage = storage;
}
static void geo_node_attribute_extract_update(bNodeTree *UNUSED(ntree), bNode *node)
{
const NodeGeometryAttributeExtract &storage = *(const NodeGeometryAttributeExtract *)
node->storage;
bNodeSocket *socket_value_vector = (bNodeSocket *)BLI_findlink(&node->outputs, 1);
bNodeSocket *socket_value_float = socket_value_vector->next;
bNodeSocket *socket_value_color4f = socket_value_float->next;
bNodeSocket *socket_value_boolean = socket_value_color4f->next;
bNodeSocket *socket_value_int32 = socket_value_boolean->next;
const CustomDataType data_type = (CustomDataType)storage.data_type;
nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3);
nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT);
nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR);
nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL);
nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32);
}
namespace blender::nodes {
static void convert_attribute(GeometryComponent &component,
const StringRef attribute_name,
const AnonymousCustomDataLayerID &layer_id,
bool delete_persistent)
{
ReadAttributeLookup attribute_lookup = component.attribute_try_get_for_read(attribute_name);
if (!attribute_lookup) {
return;
}
const GVArray &varray = *attribute_lookup.varray;
const CPPType &cpp_type = varray.type();
const CustomDataType data_type = bke::cpp_type_to_custom_data_type(cpp_type);
component.attribute_try_create_anonymous(
layer_id, attribute_lookup.domain, data_type, AttributeInitVArray(&varray));
if (delete_persistent) {
component.attribute_try_delete(attribute_name);
}
}
static void geo_node_attribute_extract_exec(GeoNodeExecParams params)
{
const bNode &node = params.node();
const NodeGeometryAttributeExtract &storage = *(const NodeGeometryAttributeExtract *)
node.storage;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
const bool delete_persistent = storage.delete_persistent;
const std::string attribute_name = params.get_input<std::string>("Attribute");
AnonymousCustomDataLayerID *layer_id = CustomData_anonymous_id_new(attribute_name.c_str());
auto *output_field = new bke::AnonymousAttributeField(*layer_id, *cpp_type);
if (geometry_set.has<MeshComponent>()) {
convert_attribute(geometry_set.get_component_for_write<MeshComponent>(),
attribute_name,
*layer_id,
delete_persistent);
}
if (geometry_set.has<PointCloudComponent>()) {
convert_attribute(geometry_set.get_component_for_write<PointCloudComponent>(),
attribute_name,
*layer_id,
delete_persistent);
}
if (geometry_set.has<CurveComponent>()) {
convert_attribute(geometry_set.get_component_for_write<CurveComponent>(),
attribute_name,
*layer_id,
delete_persistent);
}
params.set_output("Geometry", geometry_set);
switch (data_type) {
case CD_PROP_FLOAT: {
params.set_output("Value_001", bke::FieldRef<float>(output_field));
break;
}
case CD_PROP_FLOAT3: {
params.set_output("Value", bke::FieldRef<float3>(output_field));
break;
}
case CD_PROP_INT32: {
params.set_output("Value_004", bke::FieldRef<int>(output_field));
break;
}
case CD_PROP_BOOL: {
params.set_output("Value_003", bke::FieldRef<bool>(output_field));
break;
}
case CD_PROP_COLOR: {
params.set_output("Value_002", bke::FieldRef<ColorGeometry4f>(output_field));
break;
}
default: {
BLI_assert_unreachable();
}
}
}
} // namespace blender::nodes
void register_node_type_geo_attribute_extract()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_ATTRIBUTE_EXTRACT, "Attribute Extract", NODE_CLASS_ATTRIBUTE, 0);
node_type_socket_templates(
&ntype, geo_node_attribute_extract_in, geo_node_attribute_extract_out);
node_type_init(&ntype, geo_node_attribute_extract_init);
node_type_update(&ntype, geo_node_attribute_extract_update);
node_type_storage(&ntype,
"NodeGeometryAttributeExtract",
node_free_standard_storage,
node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_extract_exec;
ntype.draw_buttons = geo_node_attribute_extract_layout;
nodeRegisterType(&ntype);
}

View File

@@ -22,10 +22,10 @@
static bNodeSocketTemplate geo_node_attribute_fill_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
{SOCK_VECTOR, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_VECTOR, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_INT, N_("Value"), 0, 0, 0, 0, -10000000.0f, 10000000.0f},
{-1, ""},
};
@@ -78,6 +78,26 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, con
return ATTR_DOMAIN_POINT;
}
template<typename T>
void fill_attribute_impl(GeometryComponent &component,
OutputAttribute &attribute,
const GeoNodeExecParams &params,
const StringRef input_name)
{
const AttributeDomain domain = attribute.domain();
const int domain_size = attribute->size();
bke::FieldRef<T> value_field = params.get_input_field<T>(input_name);
bke::FieldInputs field_inputs = value_field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> input_values;
prepare_field_inputs(field_inputs, component, domain, input_values);
bke::FieldOutput field_output = value_field->evaluate(IndexMask(domain_size), field_inputs);
for (const int i : IndexRange(domain_size)) {
T value;
field_output.varray_ref().get(i, &value);
attribute->set_by_copy(i, &value);
}
}
static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams &params)
{
const std::string attribute_name = params.get_input<std::string>("Attribute");
@@ -100,28 +120,23 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
switch (data_type) {
case CD_PROP_FLOAT: {
const float value = params.get_input<float>("Value_001");
attribute->fill(&value);
fill_attribute_impl<float>(component, attribute, params, "Value_001");
break;
}
case CD_PROP_FLOAT3: {
const float3 value = params.get_input<float3>("Value");
attribute->fill(&value);
fill_attribute_impl<float3>(component, attribute, params, "Value");
break;
}
case CD_PROP_COLOR: {
const ColorGeometry4f value = params.get_input<ColorGeometry4f>("Value_002");
attribute->fill(&value);
fill_attribute_impl<ColorGeometry4f>(component, attribute, params, "Value_002");
break;
}
case CD_PROP_BOOL: {
const bool value = params.get_input<bool>("Value_003");
attribute->fill(&value);
fill_attribute_impl<bool>(component, attribute, params, "Value_003");
break;
}
case CD_PROP_INT32: {
const int value = params.get_input<int>("Value_004");
attribute->fill(&value);
fill_attribute_impl<int>(component, attribute, params, "Value_004");
break;
}
default:
@@ -156,7 +171,8 @@ void register_node_type_geo_attribute_fill()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE, 0);
geo_node_type_base(
&ntype, GEO_NODE_ATTRIBUTE_FILL, "Store Persistent Attribute", NODE_CLASS_ATTRIBUTE, 0);
node_type_socket_templates(&ntype, geo_node_attribute_fill_in, geo_node_attribute_fill_out);
node_type_init(&ntype, geo_node_attribute_fill_init);
node_type_update(&ntype, geo_node_attribute_fill_update);

View File

@@ -0,0 +1,204 @@
/*
* 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 "UI_interface.h"
#include "UI_resources.h"
#include "BKE_attribute_math.hh"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_attribute_freeze_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_VECTOR, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_INT, N_("Value"), 0, 0, 0, 0, -10000000.0f, 10000000.0f, PROP_NONE, SOCK_FIELD},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_freeze_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_VECTOR, N_("Attribute"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("Attribute"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_RGBA, N_("Attribute"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_BOOLEAN, N_("Attribute"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_INT, N_("Attribute"), 0, 0, 0, 0, -10000000.0f, 10000000.0f},
{-1, ""},
};
static void geo_node_attribute_freeze_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
}
static void geo_node_attribute_freeze_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryAttributeFreeze *data = (NodeGeometryAttributeFreeze *)MEM_callocN(
sizeof(NodeGeometryAttributeFreeze), __func__);
data->data_type = CD_PROP_FLOAT;
data->domain = ATTR_DOMAIN_POINT;
node->storage = data;
}
static void geo_node_attribute_freeze_update(bNodeTree *UNUSED(ntree), bNode *node)
{
const NodeGeometryAttributeFreeze &storage = *(const NodeGeometryAttributeFreeze *)node->storage;
const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
bNodeSocket *socket_value_attribute_name = (bNodeSocket *)node->inputs.first;
bNodeSocket *socket_value_vector = socket_value_attribute_name->next;
bNodeSocket *socket_value_float = socket_value_vector->next;
bNodeSocket *socket_value_color4f = socket_value_float->next;
bNodeSocket *socket_value_boolean = socket_value_color4f->next;
bNodeSocket *socket_value_int32 = socket_value_boolean->next;
nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3);
nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT);
nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR);
nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL);
nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32);
bNodeSocket *out_socket_value_attribute_name = (bNodeSocket *)node->outputs.first;
bNodeSocket *out_socket_value_vector = out_socket_value_attribute_name->next;
bNodeSocket *out_socket_value_float = out_socket_value_vector->next;
bNodeSocket *out_socket_value_color4f = out_socket_value_float->next;
bNodeSocket *out_socket_value_boolean = out_socket_value_color4f->next;
bNodeSocket *out_socket_value_int32 = out_socket_value_boolean->next;
nodeSetSocketAvailability(out_socket_value_vector, data_type == CD_PROP_FLOAT3);
nodeSetSocketAvailability(out_socket_value_float, data_type == CD_PROP_FLOAT);
nodeSetSocketAvailability(out_socket_value_color4f, data_type == CD_PROP_COLOR);
nodeSetSocketAvailability(out_socket_value_boolean, data_type == CD_PROP_BOOL);
nodeSetSocketAvailability(out_socket_value_int32, data_type == CD_PROP_INT32);
}
namespace blender::nodes {
template<typename T>
void set_output_field(GeoNodeExecParams &params,
AnonymousCustomDataLayerID &layer_id,
const StringRef output_name)
{
params.set_output(
output_name,
bke::FieldRef<T>(new bke::AnonymousAttributeField(layer_id, CPPType::get<T>())));
}
static void set_output_field(GeoNodeExecParams &params,
AnonymousCustomDataLayerID &layer_id,
const CustomDataType data_type)
{
switch (data_type) {
case CD_PROP_FLOAT: {
set_output_field<float>(params, layer_id, "Attribute_001");
break;
}
case CD_PROP_FLOAT3: {
set_output_field<float3>(params, layer_id, "Attribute");
break;
}
case CD_PROP_COLOR: {
set_output_field<ColorGeometry4f>(params, layer_id, "Attribute_002");
break;
}
case CD_PROP_BOOL: {
set_output_field<bool>(params, layer_id, "Attribute_003");
break;
}
case CD_PROP_INT32: {
set_output_field<int>(params, layer_id, "Attribute_004");
break;
}
default:
break;
}
}
static void geo_node_attribute_freeze_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = bke::geometry_set_realize_instances(geometry_set);
const bNode &node = params.node();
const NodeGeometryAttributeFreeze &storage = *(const NodeGeometryAttributeFreeze *)node.storage;
const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain);
AnonymousCustomDataLayerID *id = CustomData_anonymous_id_new("Attribute Freeze");
FieldPtr field;
switch (data_type) {
case CD_PROP_FLOAT:
field = params.get_input_field<float>("Value_001").field();
break;
case CD_PROP_FLOAT3:
field = params.get_input_field<float3>("Value").field();
break;
case CD_PROP_COLOR:
field = params.get_input_field<ColorGeometry4f>("Value_002").field();
break;
case CD_PROP_BOOL:
field = params.get_input_field<bool>("Value_003").field();
break;
case CD_PROP_INT32:
field = params.get_input_field<int>("Value_004").field();
break;
default:
break;
}
static const Array<GeometryComponentType> types = {
GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
for (const GeometryComponentType type : types) {
if (geometry_set.has(type)) {
GeometryComponent &component = geometry_set.get_component_for_write(type);
try_freeze_field_on_geometry(component, *id, domain, *field);
}
}
set_output_field(params, *id, data_type);
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_attribute_freeze()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_ATTRIBUTE_FREEZE, "Attribute Freeze", NODE_CLASS_ATTRIBUTE, 0);
node_type_socket_templates(&ntype, geo_node_attribute_freeze_in, geo_node_attribute_freeze_out);
node_type_storage(&ntype,
"NodeGeometryAttributeFreeze",
node_free_standard_storage,
node_copy_standard_storage);
node_type_init(&ntype, geo_node_attribute_freeze_init);
node_type_update(&ntype, geo_node_attribute_freeze_update);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_freeze_exec;
ntype.draw_buttons = geo_node_attribute_freeze_layout;
nodeRegisterType(&ntype);
}

View File

@@ -31,13 +31,13 @@
static bNodeSocketTemplate geo_node_attribute_proximity_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_GEOMETRY, N_("Target")},
{SOCK_STRING, N_("Distance")},
{SOCK_STRING, N_("Position")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_proximity_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_FLOAT, N_("Distance")},
{SOCK_VECTOR, N_("Position")},
{-1, ""},
};
@@ -66,9 +66,7 @@ static void proximity_calc(MutableSpan<float> distance_span,
BVHTreeFromMesh &tree_data_mesh,
BVHTreeFromPointCloud &tree_data_pointcloud,
const bool bvh_mesh_success,
const bool bvh_pointcloud_success,
const bool store_distances,
const bool store_locations)
const bool bvh_pointcloud_success)
{
IndexRange range = positions.index_range();
threading::parallel_for(range, 512, [&](IndexRange range) {
@@ -107,18 +105,18 @@ static void proximity_calc(MutableSpan<float> distance_span,
}
if (nearest_from_pointcloud.dist_sq < nearest_from_mesh.dist_sq) {
if (store_distances) {
if (!distance_span.is_empty()) {
distance_span[i] = sqrtf(nearest_from_pointcloud.dist_sq);
}
if (store_locations) {
if (!location_span.is_empty()) {
location_span[i] = nearest_from_pointcloud.co;
}
}
else {
if (store_distances) {
if (!distance_span.is_empty()) {
distance_span[i] = sqrtf(nearest_from_mesh.dist_sq);
}
if (store_locations) {
if (!location_span.is_empty()) {
location_span[i] = nearest_from_mesh.co;
}
}
@@ -161,25 +159,24 @@ static bool bvh_from_pointcloud(const PointCloud *target_pointcloud,
}
static void attribute_calc_proximity(GeometryComponent &component,
GeometrySet &geometry_set_target,
GeoNodeExecParams &params)
const GeometrySet &geometry_set_target,
const AnonymousCustomDataLayerID *distance_id,
const AnonymousCustomDataLayerID *location_id,
const GeoNodeExecParams &params)
{
/* This node works on the "point" domain, since that is where positions are stored. */
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
const std::string distance_attribute_name = params.get_input<std::string>("Distance");
OutputAttribute_Typed<float> distance_attribute =
component.attribute_try_get_for_output_only<float>(distance_attribute_name, result_domain);
const std::string location_attribute_name = params.get_input<std::string>("Position");
OutputAttribute_Typed<float3> location_attribute =
component.attribute_try_get_for_output_only<float3>(location_attribute_name, result_domain);
ReadAttributeLookup position_attribute = component.attribute_try_get_for_read("position");
if (!position_attribute || (!distance_attribute && !location_attribute)) {
return;
std::optional<OutputAttribute_Typed<float3>> location_attribute;
std::optional<OutputAttribute_Typed<float>> distance_attribute;
if (location_id != nullptr) {
location_attribute.emplace(component.attribute_try_get_anonymous_for_output_only<float3>(
*location_id, ATTR_DOMAIN_POINT));
}
if (distance_id != nullptr) {
distance_attribute.emplace(component.attribute_try_get_anonymous_for_output_only<float>(
*distance_id, ATTR_DOMAIN_POINT));
}
BLI_assert(position_attribute.varray->type().is<float3>());
const bNode &node = params.node();
const NodeGeometryAttributeProximity &storage = *(const NodeGeometryAttributeProximity *)
@@ -202,21 +199,13 @@ static void attribute_calc_proximity(GeometryComponent &component,
tree_data_pointcloud);
}
GVArray_Typed<float3> positions{*position_attribute.varray};
MutableSpan<float> distance_span = distance_attribute ? distance_attribute.as_span() :
MutableSpan<float>();
MutableSpan<float3> location_span = location_attribute ? location_attribute.as_span() :
MutableSpan<float3>();
proximity_calc(distance_span,
location_span,
proximity_calc(distance_attribute ? distance_attribute->as_span() : MutableSpan<float>(),
location_attribute ? location_attribute->as_span() : MutableSpan<float3>(),
positions,
tree_data_mesh,
tree_data_pointcloud,
bvh_mesh_success,
bvh_pointcloud_success,
distance_attribute, /* Boolean. */
location_attribute); /* Boolean. */
bvh_pointcloud_success);
if (bvh_mesh_success) {
free_bvhtree_from_mesh(&tree_data_mesh);
@@ -225,11 +214,14 @@ static void attribute_calc_proximity(GeometryComponent &component,
free_bvhtree_from_pointcloud(&tree_data_pointcloud);
}
if (distance_attribute) {
distance_attribute.save();
}
if (location_attribute) {
location_attribute.save();
location_attribute->save();
}
if (distance_attribute) {
for (const int i : IndexRange(distance_attribute->as_span().size())) {
std::cout << distance_attribute->as_span()[i] << "\n";
}
distance_attribute->save();
}
}
@@ -244,20 +236,41 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
* for the target geometry set. However, the generic BVH API complicates this. */
geometry_set_target = geometry_set_realize_instances(geometry_set_target);
if (geometry_set.has<MeshComponent>()) {
attribute_calc_proximity(
geometry_set.get_component_for_write<MeshComponent>(), geometry_set_target, params);
}
if (geometry_set.has<PointCloudComponent>()) {
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);
AnonymousCustomDataLayerID *distance = params.output_is_required("Distance") ?
CustomData_anonymous_id_new("Distance") :
nullptr;
AnonymousCustomDataLayerID *location = params.output_is_required("Position") ?
CustomData_anonymous_id_new("Position") :
nullptr;
if (!distance && !location) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
params.set_output("Geometry", geometry_set);
static const Array<GeometryComponentType> types = {
GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
for (const GeometryComponentType type : types) {
if (geometry_set.has(type)) {
attribute_calc_proximity(geometry_set.get_component_for_write(type),
geometry_set_target,
distance,
location,
params);
}
}
if (distance != nullptr) {
params.set_output(
"Distance",
bke::FieldRef<float>(new bke::AnonymousAttributeField(*distance, CPPType::get<float>())));
}
if (location != nullptr) {
params.set_output("Position",
bke::FieldRef<float3>(
new bke::AnonymousAttributeField(*location, CPPType::get<float3>())));
}
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
@@ -266,8 +279,7 @@ void register_node_type_geo_attribute_proximity()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_ATTRIBUTE_PROXIMITY, "Attribute Proximity", NODE_CLASS_ATTRIBUTE, 0);
geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_PROXIMITY, "Proximity", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(
&ntype, geo_node_attribute_proximity_in, geo_node_attribute_proximity_out);
node_type_init(&ntype, geo_attribute_proximity_init);

View File

@@ -42,7 +42,7 @@ static void geo_node_curve_length_exec(GeoNodeExecParams params)
for (const SplinePtr &spline : curve.splines()) {
length += spline->length();
}
params.set_output("Length", length);
params.set_output("Length", bke::FieldRef<float>(new bke::ConstantField(length)));
}
} // namespace blender::nodes

View File

@@ -0,0 +1,42 @@
/*
* 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 "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_curve_parameter_out[] = {
{SOCK_FLOAT, N_("Parameter")},
{-1, ""},
};
namespace blender::nodes {
static void geo_node_curve_parameter_exec(GeoNodeExecParams params)
{
FieldPtr curve_parameter_field = new bke::CurveParameterField();
params.set_output("Parameter", bke::FieldRef<float>(std::move(curve_parameter_field)));
}
} // namespace blender::nodes
void register_node_type_geo_curve_parameter()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_CURVE_PARAMETER, "Curve Parameter", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, nullptr, geo_node_curve_parameter_out);
ntype.geometry_node_execute = blender::nodes::geo_node_curve_parameter_exec;
nodeRegisterType(&ntype);
}

View File

@@ -25,12 +25,12 @@
static bNodeSocketTemplate geo_node_select_by_handle_type_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Selection")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_select_by_handle_type_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_BOOLEAN, N_("Selection")},
{-1, ""},
};
@@ -112,10 +112,11 @@ static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params)
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
const CurveEval *curve = curve_component.get_for_read();
AnonymousCustomDataLayerID *id = CustomData_anonymous_id_new("Selection");
if (curve != nullptr) {
const std::string selection_name = params.extract_input<std::string>("Selection");
OutputAttribute_Typed<bool> selection =
curve_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_POINT);
curve_component.attribute_try_get_anonymous_for_output_only<bool>(*id, ATTR_DOMAIN_POINT);
if (selection) {
select_curve_by_handle_type(*curve, handle_type, mode, selection.as_span());
selection.save();
@@ -123,6 +124,9 @@ static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params)
}
params.set_output("Geometry", std::move(geometry_set));
params.set_output(
"Selection",
bke::FieldRef<bool>(new bke::AnonymousAttributeField(*id, CPPType::get<bool>())));
}
} // namespace blender::nodes

View File

@@ -23,7 +23,7 @@
static bNodeSocketTemplate geo_node_curve_set_handles_in[] = {
{SOCK_GEOMETRY, N_("Curve")},
{SOCK_STRING, N_("Selection")},
{SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -87,9 +87,14 @@ static void geo_node_curve_set_handles_exec(GeoNodeExecParams params)
CurveEval &curve = *curve_component.get_for_write();
MutableSpan<SplinePtr> splines = curve.splines();
const std::string selection_name = params.extract_input<std::string>("Selection");
GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
selection_name, ATTR_DOMAIN_POINT, true);
bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, curve_component, ATTR_DOMAIN_POINT, field_input_values);
bke::FieldOutput field_output = field->evaluate(
IndexRange(curve_component.attribute_domain_size(ATTR_DOMAIN_POINT)), field_inputs);
GVArray_Typed<bool> selection{field_output.varray_ref()};
const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
int point_index = 0;

View File

@@ -25,7 +25,7 @@
static bNodeSocketTemplate geo_node_curve_spline_type_in[] = {
{SOCK_GEOMETRY, N_("Curve")},
{SOCK_STRING, N_("Selection")},
{SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -255,12 +255,17 @@ static void geo_node_curve_spline_type_exec(GeoNodeExecParams params)
return;
}
const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
const CurveEval &curve = *curve_component->get_for_read();
const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>();
const CurveEval &curve = *curve_component.get_for_read();
const std::string selection_name = params.extract_input<std::string>("Selection");
GVArray_Typed<bool> selection = curve_component->attribute_get_for_read(
selection_name, ATTR_DOMAIN_CURVE, true);
bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, curve_component, ATTR_DOMAIN_CURVE, field_input_values);
bke::FieldOutput field_output = field->evaluate(
IndexRange(curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE)), field_inputs);
GVArray_Typed<bool> selection{field_output.varray_ref()};
std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
for (const int i : curve.splines().index_range()) {

View File

@@ -31,8 +31,7 @@ using blender::fn::GVArray_Typed;
static bNodeSocketTemplate geo_node_curve_subdivide_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Cuts")},
{SOCK_INT, N_("Cuts"), 1, 0, 0, 0, 0, 1000},
{SOCK_INT, N_("Cuts"), 1, 0, 0, 0, 0, 1000, PROP_NONE, SOCK_FIELD},
{-1, ""},
};
@@ -41,32 +40,8 @@ static bNodeSocketTemplate geo_node_curve_subdivide_out[] = {
{-1, ""},
};
static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "cuts_type", 0, IFACE_("Cuts"), ICON_NONE);
}
static void geo_node_curve_subdivide_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryCurveSubdivide *data = (NodeGeometryCurveSubdivide *)MEM_callocN(
sizeof(NodeGeometryCurveSubdivide), __func__);
data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER;
node->storage = data;
}
namespace blender::nodes {
static void geo_node_curve_subdivide_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage;
update_attribute_input_socket_availabilities(
*node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type);
}
static Array<int> get_subdivided_offsets(const Spline &spline,
const VArray<int> &cuts,
const int spline_offset)
@@ -363,10 +338,18 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params)
}
const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
GVArray_Typed<int> cuts = params.get_input_attribute<int>(
"Cuts", component, ATTR_DOMAIN_POINT, 0);
bke::FieldRef<int> field = params.get_input_field<int>("Cuts");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, component, ATTR_DOMAIN_POINT, field_input_values);
bke::FieldOutput field_output = field->evaluate(
IndexRange(component.attribute_domain_size(ATTR_DOMAIN_POINT)), field_inputs);
GVArray_Typed<int> cuts{field_output.varray_ref()};
if (cuts->is_single() && cuts->get_internal_single() < 1) {
params.set_output("Geometry", geometry_set);
params.set_output("Geometry", std::move(geometry_set));
return;
}
@@ -383,13 +366,6 @@ void register_node_type_geo_curve_subdivide()
geo_node_type_base(&ntype, GEO_NODE_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_curve_subdivide_in, geo_node_curve_subdivide_out);
ntype.draw_buttons = geo_node_curve_subdivide_layout;
node_type_storage(&ntype,
"NodeGeometryCurveSubdivide",
node_free_standard_storage,
node_copy_standard_storage);
node_type_init(&ntype, geo_node_curve_subdivide_init);
node_type_update(&ntype, blender::nodes::geo_node_curve_subdivide_update);
ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec;
nodeRegisterType(&ntype);
}

View File

@@ -24,6 +24,9 @@
#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
using blender::bke::CustomDataAttributes;
@@ -45,8 +48,7 @@ extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
static bNodeSocketTemplate geo_node_delete_geometry_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Selection")},
{SOCK_BOOLEAN, N_("Invert")},
{SOCK_BOOLEAN, N_("Selection"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -55,6 +57,18 @@ static bNodeSocketTemplate geo_node_delete_geometry_out[] = {
{-1, ""},
};
static void geo_node_delete_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
}
static void geo_node_delete_init(bNodeTree *UNUSED(tree), bNode *node)
{
node->custom1 = ATTR_DOMAIN_POINT;
}
namespace blender::nodes {
template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask)
@@ -101,14 +115,21 @@ static void copy_dynamic_attributes(const CustomDataAttributes &src,
std::optional<GSpan> src_attribute = src.get_for_read(name);
BLI_assert(src_attribute);
if (!dst.create(name, meta_data.data_type)) {
/* Since the source spline of the same type had the attribute, adding it should work. */
BLI_assert_unreachable();
std::optional<GMutableSpan> new_attribute;
if (meta_data.anonymous_layer_id) {
if (!dst.create_anonymous(*meta_data.anonymous_layer_id, meta_data.data_type)) {
BLI_assert_unreachable();
}
new_attribute = dst.get_anonymous_for_write(*meta_data.anonymous_layer_id);
}
else {
if (!dst.create(name, meta_data.data_type)) {
BLI_assert_unreachable();
}
new_attribute = dst.get_for_write(name);
}
std::optional<GMutableSpan> new_attribute = dst.get_for_write(name);
BLI_assert(new_attribute);
attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) {
using T = decltype(dummy);
copy_data(src_attribute->typed<T>(), new_attribute->typed<T>(), mask);
@@ -130,7 +151,8 @@ static SplinePtr spline_delete(const Spline &spline, const IndexMask mask)
}
static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
const StringRef name,
const AttributeDomain domain,
const Span<bool> selection,
const bool invert)
{
Span<SplinePtr> input_splines = input_curve.splines();
@@ -139,8 +161,7 @@ static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
/* Keep track of which splines were copied to the result to copy spline domain attributes. */
Vector<int64_t> copied_splines;
if (input_curve.attributes.get_for_read(name)) {
GVArray_Typed<bool> selection = input_curve.attributes.get_for_read<bool>(name, false);
if (domain == ATTR_DOMAIN_CURVE) {
for (const int i : input_splines.index_range()) {
if (selection[i] == invert) {
output_curve->add_spline(input_splines[i]->copy());
@@ -148,17 +169,18 @@ static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
}
}
}
else {
else if (domain == ATTR_DOMAIN_POINT) {
/* Reuse index vector for each spline. */
Vector<int64_t> indices_to_copy;
Array<int> offsets = input_curve.control_point_offsets();
for (const int i : input_splines.index_range()) {
const Spline &spline = *input_splines[i];
GVArray_Typed<bool> selection = spline.attributes.get_for_read<bool>(name, false);
indices_to_copy.clear();
for (const int i_point : IndexRange(spline.size())) {
if (selection[i_point] == invert) {
if (selection[offsets[i] + i_point] == invert) {
indices_to_copy.append(i_point);
}
}
@@ -187,11 +209,24 @@ static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
static void delete_curve_selection(const CurveComponent &in_component,
CurveComponent &r_component,
const StringRef selection_name,
const GeoNodeExecParams &params,
const bool invert)
{
const AttributeDomain selection_domain = static_cast<AttributeDomain>(params.node().custom1);
bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, in_component, selection_domain, field_input_values);
bke::FieldOutput field_output = field->evaluate(
IndexRange(in_component.attribute_domain_size(selection_domain)), field_inputs);
GVArray_Typed<bool> selection_gvarray{field_output.varray_ref()};
VArray_Span<bool> selection{selection_gvarray};
std::unique_ptr<CurveEval> r_curve = curve_delete(
*in_component.get_for_read(), selection_name, invert);
*in_component.get_for_read(), selection_domain, selection, invert);
if (r_curve) {
r_component.replace(r_curve.release());
}
@@ -202,12 +237,20 @@ static void delete_curve_selection(const CurveComponent &in_component,
static void delete_point_cloud_selection(const PointCloudComponent &in_component,
PointCloudComponent &out_component,
const StringRef selection_name,
const GeoNodeExecParams &params,
const bool invert)
{
const GVArray_Typed<bool> selection_attribute = in_component.attribute_get_for_read<bool>(
selection_name, ATTR_DOMAIN_POINT, false);
VArray_Span<bool> selection{selection_attribute};
const AttributeDomain selection_domain = static_cast<AttributeDomain>(params.node().custom1);
bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, in_component, selection_domain, field_input_values);
bke::FieldOutput field_output = field->evaluate(
IndexRange(in_component.attribute_domain_size(selection_domain)), field_inputs);
GVArray_Typed<bool> selection_gvarray{field_output.varray_ref()};
VArray_Span<bool> selection{selection_gvarray};
const int total = selection.count(invert);
if (total == 0) {
@@ -567,34 +610,21 @@ static Mesh *delete_mesh_selection(const Mesh &mesh_in,
return result;
}
static AttributeDomain get_mesh_selection_domain(MeshComponent &component, const StringRef name)
{
std::optional<AttributeMetaData> selection_attribute = component.attribute_get_meta_data(name);
if (!selection_attribute) {
/* The node will not do anything in this case, but this function must return something. */
return ATTR_DOMAIN_POINT;
}
/* Corners can't be deleted separately, so interpolate corner attributes
* to the face domain. Note that this choice is somewhat arbitrary. */
if (selection_attribute->domain == ATTR_DOMAIN_CORNER) {
return ATTR_DOMAIN_FACE;
}
return selection_attribute->domain;
}
static void delete_mesh_selection(MeshComponent &component,
const Mesh &mesh_in,
const StringRef selection_name,
const GeoNodeExecParams &params,
const bool invert)
{
/* Figure out the best domain to use. */
const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name);
const AttributeDomain selection_domain = static_cast<AttributeDomain>(params.node().custom1);
/* This already checks if the attribute exists, and displays a warning in that case. */
GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
selection_name, selection_domain, false);
bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, component, selection_domain, field_input_values);
bke::FieldOutput field_output = field->evaluate(
IndexRange(component.attribute_domain_size(selection_domain)), field_inputs);
GVArray_Typed<bool> selection{field_output.varray_ref()};
/* Check if there is anything to delete. */
bool delete_nothing = true;
@@ -635,30 +665,25 @@ static void geo_node_delete_geometry_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = bke::geometry_set_realize_instances(geometry_set);
const bool invert = params.extract_input<bool>("Invert");
const std::string selection_name = params.extract_input<std::string>("Selection");
if (selection_name.empty()) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
const bool invert = false;
GeometrySet out_set(geometry_set);
if (geometry_set.has<PointCloudComponent>()) {
delete_point_cloud_selection(*geometry_set.get_component_for_read<PointCloudComponent>(),
out_set.get_component_for_write<PointCloudComponent>(),
selection_name,
params,
invert);
}
if (geometry_set.has<MeshComponent>()) {
delete_mesh_selection(out_set.get_component_for_write<MeshComponent>(),
*geometry_set.get_mesh_for_read(),
selection_name,
params,
invert);
}
if (geometry_set.has<CurveComponent>()) {
delete_curve_selection(*geometry_set.get_component_for_read<CurveComponent>(),
out_set.get_component_for_write<CurveComponent>(),
selection_name,
params,
invert);
}
@@ -674,5 +699,7 @@ void register_node_type_geo_delete_geometry()
geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_delete_geometry_in, geo_node_delete_geometry_out);
ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec;
ntype.draw_buttons = geo_node_delete_layout;
node_type_init(&ntype, geo_node_delete_init);
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,178 @@
/*
* 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 "UI_interface.h"
#include "UI_resources.h"
#include "BKE_attribute_math.hh"
#include "BKE_customdata.h"
#include "BKE_spline.hh"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_evaluate_curve_in[] = {
{SOCK_GEOMETRY, N_("Curve")},
{SOCK_FLOAT,
N_("Length"),
0.0f,
0.0f,
0.0f,
0.0f,
-FLT_MAX,
FLT_MAX,
PROP_TRANSLATION,
SOCK_FIELD},
{SOCK_RGBA, N_("Custom"), 1, 1, 1, 1, 0, 1, PROP_NONE, SOCK_FIELD},
{-1, ""},
};
static bNodeSocketTemplate geo_node_evaluate_curve_out[] = {
{SOCK_VECTOR, N_("Position")},
{SOCK_VECTOR, N_("Tangent")},
{SOCK_VECTOR, N_("Normal")},
{SOCK_RGBA, N_("Custom")},
{-1, ""},
};
namespace blender::nodes {
class EvaluateCurveFunction : public fn::MultiFunction {
private:
GeometrySet geometry_set_;
AnonymousCustomDataLayerID *attribute_id_;
public:
EvaluateCurveFunction(GeometrySet geometry_set, AnonymousCustomDataLayerID *attribute_id)
: geometry_set_(std::move(geometry_set)), attribute_id_(attribute_id)
{
static fn::MFSignature signature = create_signature();
this->set_signature(&signature);
CustomData_anonymous_id_strong_increment(attribute_id_);
}
~EvaluateCurveFunction() override
{
CustomData_anonymous_id_strong_decrement(attribute_id_);
}
static fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Evaluate Curve"};
signature.single_input<float>("Length");
signature.single_output<float3>("Position");
signature.single_output<float3>("Tangent");
signature.single_output<float3>("Normal");
signature.single_output<ColorGeometry4f>("Custom");
return signature.build();
}
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
const VArray<float> &src_lengths = params.readonly_single_input<float>(0, "Length");
MutableSpan<float3> sampled_positions = params.uninitialized_single_output<float3>(1,
"Position");
MutableSpan<float3> sampled_tangents = params.uninitialized_single_output<float3>(2,
"Tangent");
MutableSpan<float3> sampled_normals = params.uninitialized_single_output<float3>(3, "Normal");
MutableSpan<ColorGeometry4f> sampled_custom =
params.uninitialized_single_output<ColorGeometry4f>(4, "Custom");
auto return_default = [&]() {
sampled_positions.fill_indices(mask, {0, 0, 0});
sampled_tangents.fill_indices(mask, {0, 0, 0});
sampled_normals.fill_indices(mask, {0, 0, 0});
sampled_custom.fill_indices(mask, {0, 0, 0, 1});
};
if (!geometry_set_.has_curve()) {
return return_default();
}
const CurveComponent *curve_component = geometry_set_.get_component_for_read<CurveComponent>();
const CurveEval *curve = curve_component->get_for_read();
if (curve->splines().is_empty()) {
return return_default();
}
const Spline &spline = *curve->splines()[0];
std::optional<GSpan> custom_generic = spline.attributes.get_anonymous_for_read(*attribute_id_);
if (!custom_generic) {
return return_default();
}
Span<ColorGeometry4f> custom = (*custom_generic).typed<ColorGeometry4f>();
GVArray_Typed<ColorGeometry4f> evaluated_custom = spline.interpolate_to_evaluated(custom);
const float spline_length = spline.length();
const Span<float3> evaluated_positions = spline.evaluated_positions();
const Span<float3> evaluated_tangents = spline.evaluated_tangents();
const Span<float3> evaluated_normals = spline.evaluated_normals();
for (const int i : mask) {
const float length = std::clamp(src_lengths[i], 0.0f, spline_length);
Spline::LookupResult lookup = spline.lookup_evaluated_length(length);
const int i1 = lookup.evaluated_index;
const int i2 = lookup.next_evaluated_index;
sampled_positions[i] = attribute_math::mix2(
lookup.factor, evaluated_positions[i1], evaluated_positions[i2]);
sampled_tangents[i] = attribute_math::mix2(
lookup.factor, evaluated_tangents[i1], evaluated_tangents[i2])
.normalized();
sampled_normals[i] = attribute_math::mix2(
lookup.factor, evaluated_normals[i1], evaluated_normals[i2])
.normalized();
sampled_custom[i] = attribute_math::mix2(
lookup.factor, evaluated_custom[i1], evaluated_custom[i2]);
}
}
};
static void geo_node_evaluate_curve_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
geometry_set = geometry_set_realize_instances(geometry_set);
FieldPtr curve_field = params.get_input_field<float>("Length").field();
bke::FieldRef<ColorGeometry4f> attribute_field = params.get_input_field<ColorGeometry4f>(
"Custom");
AnonymousCustomDataLayerID *layer_id = CustomData_anonymous_id_new("Evaluate Curve");
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
try_freeze_field_on_geometry(
curve_component, *layer_id, ATTR_DOMAIN_POINT, *attribute_field.field());
auto make_output_field = [&](int out_param_index) -> FieldPtr {
auto fn = std::make_unique<EvaluateCurveFunction>(geometry_set, layer_id);
return new bke::MultiFunctionField(Vector<FieldPtr>{curve_field},
optional_ptr<const fn::MultiFunction>{std::move(fn)},
out_param_index);
};
params.set_output("Position", bke::FieldRef<float3>(make_output_field(1)));
params.set_output("Tangent", bke::FieldRef<float3>(make_output_field(2)));
params.set_output("Normal", bke::FieldRef<float3>(make_output_field(3)));
params.set_output("Custom", bke::FieldRef<ColorGeometry4f>(make_output_field(4)));
}
} // namespace blender::nodes
void register_node_type_geo_evaluate_curve()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_EVALUATE_CURVE, "Evaluate Curve", NODE_CLASS_ATTRIBUTE, 0);
node_type_socket_templates(&ntype, geo_node_evaluate_curve_in, geo_node_evaluate_curve_out);
ntype.geometry_node_execute = blender::nodes::geo_node_evaluate_curve_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,206 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_mesh.h"
#include "BKE_node.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "bmesh.h"
#include "bmesh_tools.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_extrude_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_FLOAT, N_("Distance"), 0.0f, 0, 0, 0, FLT_MIN, FLT_MAX, PROP_DISTANCE, SOCK_FIELD},
{SOCK_FLOAT, N_("Inset"), 0.0f, 0, 0, 0, FLT_MIN, FLT_MAX, PROP_DISTANCE, SOCK_FIELD},
{SOCK_BOOLEAN, N_("Individual")},
{SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
static bNodeSocketTemplate geo_node_extrude_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_BOOLEAN, N_("Top Faces")},
{SOCK_BOOLEAN, N_("Side Faces")},
{-1, ""},
};
namespace blender::nodes {
static Mesh *extrude_mesh(const Mesh *mesh,
const Span<bool> selection,
const Span<float> distance,
const Span<float> inset,
const bool inset_individual_faces,
AnonymousCustomDataLayerID *top_faces_id,
AnonymousCustomDataLayerID *side_faces_id)
{
const BMeshCreateParams bmesh_create_params = {true};
const BMeshFromMeshParams bmesh_from_mesh_params = {
true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}};
BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params);
BM_select_faces(bm, selection.data());
BMOperator op;
if (inset_individual_faces) {
BMO_op_initf(bm,
&op,
0,
"inset_individual faces=%hf use_even_offset=%b thickness=%f depth=%f "
"thickness_array=%p depth_array=%p use_attributes=%b",
BM_ELEM_SELECT,
true,
inset.first(),
distance.first(),
inset.data(),
distance.data(),
true);
}
else {
BMO_op_initf(bm,
&op,
0,
"inset_region faces=%hf use_boundary=%b use_even_offset=%b thickness=%f depth=%f "
"thickness_array=%p depth_array=%p use_attributes=%b",
BM_ELEM_SELECT,
true,
true,
inset.first(),
distance.first(),
inset.data(),
distance.data(),
true);
}
BMO_op_exec(bm, &op);
BM_tag_new_faces(bm, &op);
CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX};
Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh);
MeshComponent component;
component.replace(result, GeometryOwnershipType::Editable);
if (side_faces_id) {
OutputAttribute_Typed<bool> attribute =
component.attribute_try_get_anonymous_for_output_only<bool>(*side_faces_id,
ATTR_DOMAIN_FACE);
BM_get_tagged_faces(bm, attribute.as_span().data());
attribute.save();
}
if (top_faces_id) {
OutputAttribute_Typed<bool> attribute =
component.attribute_try_get_anonymous_for_output_only<bool>(*top_faces_id,
ATTR_DOMAIN_FACE);
BM_get_selected_faces(bm, attribute.as_span().data());
attribute.save();
}
BMO_op_finish(bm, &op);
BM_mesh_free(bm);
BKE_mesh_normals_tag_dirty(result);
return result;
}
static void geo_node_extrude_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = bke::geometry_set_realize_instances(geometry_set);
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
if (mesh_component.has_mesh() && mesh_component.get_for_read()->totpoly > 0) {
const Mesh *input_mesh = mesh_component.get_for_read();
bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, mesh_component, ATTR_DOMAIN_FACE, field_input_values);
bke::FieldOutput field_output = field->evaluate(IndexRange(input_mesh->totpoly), field_inputs);
GVArray_Typed<bool> selection_results{field_output.varray_ref()};
VArray_Span<bool> selection{selection_results};
const bool inset_individual_faces = params.extract_input<bool>("Individual");
const AttributeDomain domain = inset_individual_faces ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT;
bke::FieldRef<float> distance_field = params.get_input_field<float>("Distance");
bke::FieldInputs distance_field_inputs = distance_field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> distance_field_input_values;
prepare_field_inputs(
distance_field_inputs, mesh_component, domain, distance_field_input_values);
bke::FieldOutput distance_field_output = distance_field->evaluate(
IndexRange(mesh_component.attribute_domain_size(domain)), distance_field_inputs);
GVArray_Typed<float> distance_results{distance_field_output.varray_ref()};
VArray_Span<float> distance{distance_results};
bke::FieldRef<float> inset_field = params.get_input_field<float>("Inset");
bke::FieldInputs inset_field_inputs = inset_field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> inset_field_input_values;
prepare_field_inputs(inset_field_inputs, mesh_component, domain, inset_field_input_values);
bke::FieldOutput inset_field_output = inset_field->evaluate(
IndexRange(mesh_component.attribute_domain_size(domain)), inset_field_inputs);
GVArray_Typed<float> inset_results{inset_field_output.varray_ref()};
VArray_Span<float> inset{inset_results};
AnonymousCustomDataLayerID *top_faces_id = params.output_is_required("Top Faces") ?
CustomData_anonymous_id_new("Top Faces") :
nullptr;
AnonymousCustomDataLayerID *side_faces_id = params.output_is_required("Side Faces") ?
CustomData_anonymous_id_new("Side Faces") :
nullptr;
Mesh *result = extrude_mesh(input_mesh,
selection,
distance,
inset,
inset_individual_faces,
top_faces_id,
side_faces_id);
geometry_set.replace_mesh(result);
if (top_faces_id) {
params.set_output("Top Faces",
bke::FieldRef<bool>(new bke::AnonymousAttributeField(
*top_faces_id, CPPType::get<bool>())));
}
if (side_faces_id) {
params.set_output("Side Faces",
bke::FieldRef<bool>(new bke::AnonymousAttributeField(
*side_faces_id, CPPType::get<bool>())));
}
}
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_extrude()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_EXTRUDE, "Extrude", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_extrude_in, geo_node_extrude_out);
ntype.geometry_node_execute = blender::nodes::geo_node_extrude_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,291 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_mesh.h"
#include "BKE_node.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "bmesh.h"
#include "bmesh_tools.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_extrude_and_move_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_VECTOR, N_("Offset"), 0.0f, 0.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
static bNodeSocketTemplate geo_node_extrude_and_move_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_BOOLEAN, N_("Selection")},
{-1, ""},
};
static void geo_node_extrude_and_move_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "extrude_mode", 0, "", ICON_NONE);
}
static void geo_node_extrude_and_move_init(bNodeTree *UNUSED(ntree), bNode *node)
{
node->custom1 = 0;
}
namespace blender::nodes {
static Mesh *extrude_vertices(const Mesh *mesh,
const Span<bool> selection,
const float3 offset,
AnonymousCustomDataLayerID *out_selection_id)
{
const BMeshCreateParams bmesh_create_params = {true};
const BMeshFromMeshParams bmesh_from_mesh_params = {
true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}};
BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params);
BM_select_vertices(bm, selection.data());
BMOperator extrude_op;
BMO_op_initf(bm, &extrude_op, 0, "extrude_vert_indiv verts=%hv", BM_ELEM_SELECT);
BMO_op_exec(bm, &extrude_op);
float mx[3] = {offset.x, offset.y, offset.z};
BMOperator move_op;
BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_SELECT, false);
BMO_slot_buffer_hflag_enable(
bm, extrude_op.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, false);
BMO_op_initf(bm, &move_op, 0, "translate vec=%v verts=%hv", mx, BM_ELEM_SELECT);
BMO_op_exec(bm, &move_op);
BMO_op_finish(bm, &move_op);
BMO_op_finish(bm, &extrude_op);
CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX};
Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh);
MeshComponent component;
component.replace(result, GeometryOwnershipType::Editable);
if (out_selection_id) {
OutputAttribute_Typed<bool> attribute =
component.attribute_try_get_anonymous_for_output_only<bool>(*out_selection_id,
ATTR_DOMAIN_POINT);
BM_get_selected_vertices(bm, attribute.as_span().data());
attribute.save();
}
BM_mesh_free(bm);
BKE_mesh_normals_tag_dirty(result);
return result;
}
static Mesh *extrude_edges(const Mesh *mesh,
const Span<bool> selection,
const float3 offset,
AnonymousCustomDataLayerID *out_selection_id)
{
const BMeshCreateParams bmesh_create_params = {true};
const BMeshFromMeshParams bmesh_from_mesh_params = {
true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}};
BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params);
BM_select_edges(bm, selection.data());
BMOperator extrude_op;
BMO_op_initf(bm,
&extrude_op,
0,
"extrude_edge_only edges=%he use_select_history=%b",
BM_ELEM_SELECT,
true);
BMO_op_exec(bm, &extrude_op);
float o[3] = {offset.x, offset.y, offset.z};
BMOperator move_op;
BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_SELECT, false);
BMO_slot_buffer_hflag_enable(
bm, extrude_op.slots_out, "geom.out", BM_VERT, BM_ELEM_SELECT, false);
BMO_op_initf(bm, &move_op, 0, "translate vec=%v verts=%hv", o, BM_ELEM_SELECT);
BMO_op_exec(bm, &move_op);
BMO_op_finish(bm, &move_op);
BMO_op_finish(bm, &extrude_op);
CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX};
Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh);
MeshComponent component;
component.replace(result, GeometryOwnershipType::Editable);
if (out_selection_id) {
OutputAttribute_Typed<bool> attribute =
component.attribute_try_get_anonymous_for_output_only<bool>(*out_selection_id,
ATTR_DOMAIN_POINT);
BM_get_selected_vertices(bm, attribute.as_span().data());
attribute.save();
}
BM_mesh_free(bm);
BKE_mesh_normals_tag_dirty(result);
return result;
}
static Mesh *extrude_faces(const Mesh *mesh,
const Span<bool> selection,
const float3 offset,
AnonymousCustomDataLayerID *out_selection_id)
{
// TODO: - dont execute on a offset with length 0
// - Check why selection for edges and faces is wired.
// - dedublicate extrude functions
// - checkout hans lazy bmesh mechanism
const BMeshCreateParams bmesh_create_params = {true};
const BMeshFromMeshParams bmesh_from_mesh_params = {
true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}};
BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params);
BM_select_faces(bm, selection.data());
BMOperator extrude_op;
BMO_op_initf(bm, &extrude_op, 0, "extrude_face_region geom=%hf", BM_ELEM_SELECT);
BMO_op_exec(bm, &extrude_op);
float o[3] = {offset.x, offset.y, offset.z};
BMOperator move_op;
BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_SELECT, false);
BMO_slot_buffer_hflag_enable(
bm, extrude_op.slots_out, "geom.out", BM_VERT, BM_ELEM_SELECT, false);
BMO_op_initf(bm, &move_op, 0, "translate vec=%v verts=%hv", o, BM_ELEM_SELECT);
BMO_op_exec(bm, &move_op);
BMO_op_finish(bm, &move_op);
BMO_op_finish(bm, &extrude_op);
CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX};
Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh);
MeshComponent component;
component.replace(result, GeometryOwnershipType::Editable);
if (out_selection_id) {
OutputAttribute_Typed<bool> attribute =
component.attribute_try_get_anonymous_for_output_only<bool>(*out_selection_id,
ATTR_DOMAIN_FACE);
BM_get_selected_faces(bm, attribute.as_span().data());
// face_map.out
// boundary_map.out
attribute.save();
}
BM_mesh_free(bm);
BKE_mesh_normals_tag_dirty(result);
return result;
}
static void geo_node_extrude_and_move_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = bke::geometry_set_realize_instances(geometry_set);
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
if (mesh_component.has_mesh()) {
const Mesh *input_mesh = mesh_component.get_for_read();
AttributeDomain domain = ATTR_DOMAIN_POINT;
int domain_size = input_mesh->totvert;
if (params.node().custom1 == 1) {
domain = ATTR_DOMAIN_EDGE;
domain_size = input_mesh->totedge;
}
else if (params.node().custom1 == 2) {
domain = ATTR_DOMAIN_FACE;
domain_size = input_mesh->totpoly;
}
bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, mesh_component, domain, field_input_values);
bke::FieldOutput field_output = field->evaluate(IndexRange(domain_size), field_inputs);
GVArray_Typed<bool> selection_results{field_output.varray_ref()};
VArray_Span<bool> selection{selection_results};
AnonymousCustomDataLayerID *out_selection_id = params.output_is_required("Selection") ?
CustomData_anonymous_id_new("Selection") :
nullptr;
const float3 offset = params.extract_input<float3>("Offset");
Mesh *result;
if (params.node().custom1 == 1) {
result = extrude_edges(input_mesh, selection, offset, out_selection_id);
}
else if (params.node().custom1 == 2) {
result = extrude_faces(input_mesh, selection, offset, out_selection_id);
}
else {
result = extrude_vertices(input_mesh, selection, offset, out_selection_id);
}
geometry_set.replace_mesh(result);
if (out_selection_id) {
params.set_output("Selection",
bke::FieldRef<bool>(new bke::AnonymousAttributeField(
*out_selection_id, CPPType::get<bool>())));
}
}
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_extrude_and_move()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_EXTRUDE_AND_MOVE, "Mesh Extrude And Move", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_extrude_and_move_in, geo_node_extrude_and_move_out);
node_type_init(&ntype, geo_node_extrude_and_move_init);
ntype.geometry_node_execute = blender::nodes::geo_node_extrude_and_move_exec;
ntype.draw_buttons = geo_node_extrude_and_move_layout;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,45 @@
/*
* 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 "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_index_out[] = {
{SOCK_INT, N_("Index")},
{-1, ""},
};
namespace blender::nodes {
static void geo_node_index_exec(GeoNodeExecParams params)
{
FieldPtr index_field = new bke::IndexField();
params.set_output("Index", bke::FieldRef<int>(std::move(index_field)));
}
} // namespace blender::nodes
void register_node_type_geo_index()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_INDEX, "Index", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, nullptr, geo_node_index_out);
ntype.geometry_node_execute = blender::nodes::geo_node_index_exec;
nodeRegisterType(&ntype);
}

View File

@@ -31,7 +31,8 @@ static void geo_node_is_viewport_exec(GeoNodeExecParams params)
const eEvaluationMode mode = DEG_get_mode(depsgraph);
const bool is_viewport = mode == DAG_EVAL_VIEWPORT;
params.set_output("Is Viewport", is_viewport);
/* This is a field just to avoid a crash, it doesn't seem like it should need to be a field. */
params.set_output("Is Viewport", bke::FieldRef<bool>(new bke::ConstantField(is_viewport)));
}
} // namespace blender::nodes

View File

@@ -36,7 +36,7 @@ static bNodeSocketTemplate geo_node_material_assign_in[] = {
0.0f,
PROP_NONE,
SOCK_HIDE_LABEL},
{SOCK_STRING, N_("Selection")},
{SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -75,8 +75,6 @@ static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask,
static void geo_node_material_assign_exec(GeoNodeExecParams params)
{
Material *material = params.extract_input<Material *>("Material");
const std::string mask_name = params.extract_input<std::string>("Selection");
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
@@ -85,9 +83,17 @@ static void geo_node_material_assign_exec(GeoNodeExecParams params)
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
Mesh *mesh = mesh_component.get_for_write();
if (mesh != nullptr) {
GVArray_Typed<bool> face_mask = mesh_component.attribute_get_for_read<bool>(
mask_name, ATTR_DOMAIN_FACE, true);
assign_material_to_faces(*mesh, face_mask, material);
bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, mesh_component, ATTR_DOMAIN_FACE, field_input_values);
bke::FieldOutput field_output = field->evaluate(
IndexRange(mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE)), field_inputs);
GVArray_Typed<bool> selection{field_output.varray_ref()};
assign_material_to_faces(*mesh, *selection, material);
}
}

View File

@@ -29,7 +29,7 @@ using blender::Array;
static bNodeSocketTemplate geo_node_mesh_to_curve_in[] = {
{SOCK_GEOMETRY, N_("Mesh")},
{SOCK_STRING, N_("Selection")},
{SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -258,15 +258,16 @@ static CurveFromEdgesOutput mesh_to_curve(Span<MVert> verts, Span<std::pair<int,
static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params,
const MeshComponent &component)
{
const Mesh &mesh = *component.get_for_read();
const std::string selection_name = params.extract_input<std::string>("Selection");
if (!selection_name.empty() && !component.attribute_exists(selection_name)) {
params.error_message_add(NodeWarningType::Error,
TIP_("No attribute with name \"") + selection_name + "\"");
}
GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
selection_name, ATTR_DOMAIN_EDGE, true);
bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, component, ATTR_DOMAIN_EDGE, field_input_values);
bke::FieldOutput field_output = field->evaluate(
IndexRange(component.attribute_domain_size(ATTR_DOMAIN_EDGE)), field_inputs);
GVArray_Typed<bool> selection{field_output.varray_ref()};
const Mesh &mesh = *component.get_for_read();
Vector<std::pair<int, int>> selected_edges;
for (const int i : IndexRange(mesh.totedge)) {
if (selection[i]) {

View File

@@ -0,0 +1,45 @@
/*
* 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 "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_normal_out[] = {
{SOCK_VECTOR, N_("Normal")},
{-1, ""},
};
namespace blender::nodes {
static void geo_node_normal_exec(GeoNodeExecParams params)
{
FieldPtr normal_field = new bke::PersistentAttributeField("normal", CPPType::get<float3>());
params.set_output("Normal", bke::FieldRef<float3>(std::move(normal_field)));
}
} // namespace blender::nodes
void register_node_type_geo_normal()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_NORMAL, "Face Normal", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, nullptr, geo_node_normal_out);
ntype.geometry_node_execute = blender::nodes::geo_node_normal_exec;
nodeRegisterType(&ntype);
}

View File

@@ -84,9 +84,9 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
}
}
params.set_output("Location", location);
params.set_output("Rotation", rotation);
params.set_output("Scale", scale);
params.set_output("Location", bke::FieldRef<float3>(new bke::ConstantField<float3>(location)));
params.set_output("Rotation", bke::FieldRef<float3>(new bke::ConstantField<float3>(rotation)));
params.set_output("Scale", bke::FieldRef<float3>(new bke::ConstantField<float3>(scale)));
params.set_output("Geometry", geometry_set);
}

View File

@@ -42,14 +42,16 @@ using blender::bke::GeometryInstanceGroup;
static bNodeSocketTemplate geo_node_point_distribute_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_FLOAT, N_("Distance Min"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_DISTANCE},
{SOCK_FLOAT, N_("Density Max"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
{SOCK_STRING, N_("Density Attribute")},
{SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE, SOCK_FIELD},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};
static bNodeSocketTemplate geo_node_point_distribute_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_VECTOR, N_("Rotation")},
{SOCK_VECTOR, N_("Normal")},
{SOCK_INT, N_("ID")},
{-1, ""},
};
@@ -84,7 +86,7 @@ static float3 normal_to_euler_rotation(const float3 normal)
static void sample_mesh_surface(const Mesh &mesh,
const float4x4 &transform,
const float base_density,
const VArray<float> *density_factors,
const VArray<float> &density,
const int seed,
Vector<float3> &r_positions,
Vector<float3> &r_bary_coords,
@@ -105,19 +107,16 @@ static void sample_mesh_surface(const Mesh &mesh,
const float3 v1_pos = transform * float3(mesh.mvert[v1_index].co);
const float3 v2_pos = transform * float3(mesh.mvert[v2_index].co);
float looptri_density_factor = 1.0f;
if (density_factors != nullptr) {
const float v0_density_factor = std::max(0.0f, density_factors->get(v0_loop));
const float v1_density_factor = std::max(0.0f, density_factors->get(v1_loop));
const float v2_density_factor = std::max(0.0f, density_factors->get(v2_loop));
looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
}
const float v0_density = std::max(0.0f, density[v0_loop]);
const float v1_density = std::max(0.0f, density[v1_loop]);
const float v2_density = std::max(0.0f, density[v2_loop]);
float looptri_density = (v0_density + v1_density + v2_density) / 3.0f;
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
const int looptri_seed = BLI_hash_int(looptri_index + seed);
RandomNumberGenerator looptri_rng(looptri_seed);
const float points_amount_fl = area * base_density * looptri_density_factor;
const float points_amount_fl = area * base_density * looptri_density;
const float add_point_probability = fractf(points_amount_fl);
const bool add_point = add_point_probability > looptri_rng.get_float();
const int point_amount = (int)points_amount_fl + (int)add_point;
@@ -326,34 +325,59 @@ BLI_NOINLINE static void interpolate_existing_attributes(
i_instance++;
}
attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
using T = decltype(dummy);
GVArray_Span<T> source_span{*source_attribute};
});
}
attribute_out.save();
}
}
BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> sets,
Span<int> instance_start_offsets,
GeometryComponent &component,
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
{
OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
"id", ATTR_DOMAIN_POINT);
OutputAttribute_Typed<float3> normal_attribute =
component.attribute_try_get_for_output_only<float3>("normal", ATTR_DOMAIN_POINT);
OutputAttribute_Typed<float3> rotation_attribute =
component.attribute_try_get_for_output_only<float3>("rotation", ATTR_DOMAIN_POINT);
namespace {
struct AnonymousAttributeIDs {
AnonymousCustomDataLayerID *rotation = nullptr;
AnonymousCustomDataLayerID *normal = nullptr;
AnonymousCustomDataLayerID *id = nullptr;
};
} // namespace
MutableSpan<int> result_ids = id_attribute.as_span();
MutableSpan<float3> result_normals = normal_attribute.as_span();
MutableSpan<float3> result_rotations = rotation_attribute.as_span();
BLI_NOINLINE static void compute_special_attributes(
Span<GeometryInstanceGroup> sets,
Span<int> instance_start_offsets,
GeometryComponent &component,
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array,
const AnonymousAttributeIDs &anonymous_attribute_ids)
{
MutableSpan<float3> result_rotations;
MutableSpan<float3> result_normals;
MutableSpan<int> result_ids;
if (anonymous_attribute_ids.rotation != nullptr) {
component.attribute_try_create_anonymous(*anonymous_attribute_ids.rotation,
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT3,
AttributeInitDefault());
WriteAttributeLookup rotation_attribute = component.attribute_try_get_anonymous_for_write(
*anonymous_attribute_ids.rotation);
result_rotations = rotation_attribute.varray->get_internal_span().typed<float3>();
}
if (anonymous_attribute_ids.normal != nullptr) {
component.attribute_try_create_anonymous(*anonymous_attribute_ids.normal,
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT3,
AttributeInitDefault());
WriteAttributeLookup normal_attribute = component.attribute_try_get_anonymous_for_write(
*anonymous_attribute_ids.normal);
result_normals = normal_attribute.varray->get_internal_span().typed<float3>();
}
if (anonymous_attribute_ids.id != nullptr) {
component.attribute_try_create_anonymous(
*anonymous_attribute_ids.id, ATTR_DOMAIN_POINT, CD_PROP_INT32, AttributeInitDefault());
WriteAttributeLookup id_attribute = component.attribute_try_get_anonymous_for_write(
*anonymous_attribute_ids.id);
result_ids = id_attribute.varray->get_internal_span().typed<int>();
}
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : sets) {
@@ -388,19 +412,25 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
const float3 v1_pos = float3(mesh.mvert[v1_index].co);
const float3 v2_pos = float3(mesh.mvert[v2_index].co);
ids[i] = (int)(bary_coord.hash() + (uint64_t)looptri_index);
normal_tri_v3(normals[i], v0_pos, v1_pos, v2_pos);
mul_m3_v3(rotation_matrix, normals[i]);
rotations[i] = normal_to_euler_rotation(normals[i]);
if (!result_ids.is_empty()) {
ids[i] = (int)(bary_coord.hash() + (uint64_t)looptri_index);
}
float3 normal;
if (!result_normals.is_empty() || !result_rotations.is_empty()) {
normal_tri_v3(normal, v0_pos, v1_pos, v2_pos);
mul_m3_v3(rotation_matrix, normal);
}
if (!result_normals.is_empty()) {
normals[i] = normal;
}
if (!result_rotations.is_empty()) {
rotations[i] = normal_to_euler_rotation(normal);
}
}
i_instance++;
}
}
id_attribute.save();
normal_attribute.save();
rotation_attribute.save();
}
BLI_NOINLINE static void add_remaining_point_attributes(
@@ -409,7 +439,8 @@ BLI_NOINLINE static void add_remaining_point_attributes(
const Map<std::string, AttributeKind> &attributes,
GeometryComponent &component,
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
Span<Vector<int>> looptri_indices_array,
const AnonymousAttributeIDs &anonymous_attribute_ids)
{
interpolate_existing_attributes(set_groups,
instance_start_offsets,
@@ -417,50 +448,50 @@ BLI_NOINLINE static void add_remaining_point_attributes(
component,
bary_coords_array,
looptri_indices_array);
compute_special_attributes(
set_groups, instance_start_offsets, component, bary_coords_array, looptri_indices_array);
compute_special_attributes(set_groups,
instance_start_offsets,
component,
bary_coords_array,
looptri_indices_array,
anonymous_attribute_ids);
}
static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
const StringRef density_attribute_name,
const float density,
const float base_density,
const bke::FieldRef<float> &density_field,
const int seed,
MutableSpan<Vector<float3>> positions_all,
MutableSpan<Vector<float3>> bary_coords_all,
MutableSpan<Vector<int>> looptri_indices_all)
{
/* If there is an attribute name, the default value for the densities should be zero so that
* points are only scattered where the attribute exists. Otherwise, just "ignore" the density
* factors. */
const bool use_one_default = density_attribute_name.is_empty();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
const Mesh &mesh = *component.get_for_read();
bke::FieldInputs density_field_inputs = density_field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> density_field_input_values;
prepare_field_inputs(
density_field_inputs, component, ATTR_DOMAIN_CORNER, density_field_input_values);
bke::FieldOutput density_field_output = density_field->evaluate(IndexRange(mesh.totloop),
density_field_inputs);
GVArray_Typed<float> density{density_field_output.varray_ref()};
for (const float4x4 &transform : set_group.transforms) {
Vector<float3> &positions = positions_all[i_instance];
Vector<float3> &bary_coords = bary_coords_all[i_instance];
Vector<int> &looptri_indices = looptri_indices_all[i_instance];
sample_mesh_surface(mesh,
transform,
density,
&*density_factors,
seed,
positions,
bary_coords,
looptri_indices);
sample_mesh_surface(
mesh, transform, base_density, density, seed, positions, bary_coords, looptri_indices);
i_instance++;
}
}
}
static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_groups,
const StringRef density_attribute_name,
const float density,
const float max_density,
const bke::FieldRef<float> &density_field,
const int seed,
const float minimum_distance,
MutableSpan<Vector<float3>> positions_all,
@@ -478,8 +509,14 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
Vector<float3> &positions = positions_all[i_instance];
Vector<float3> &bary_coords = bary_coords_all[i_instance];
Vector<int> &looptri_indices = looptri_indices_all[i_instance];
sample_mesh_surface(
mesh, transform, density, nullptr, seed, positions, bary_coords, looptri_indices);
sample_mesh_surface(mesh,
transform,
max_density,
VArray_For_Single<float>(1.0f, mesh.totloop),
seed,
positions,
bary_coords,
looptri_indices);
instance_start_offsets[i_instance] = initial_points_len;
initial_points_len += positions.size();
@@ -487,11 +524,6 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
}
}
/* If there is an attribute name, the default value for the densities should be zero so that
* points are only scattered where the attribute exists. Otherwise, just "ignore" the density
* factors. */
const bool use_one_default = density_attribute_name.is_empty();
/* Unlike the other result arrays, the elimination mask in stored as a flat array for every
* point, in order to simplify culling points from the KDTree (which needs to know about all
* points at once). */
@@ -507,8 +539,14 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *component.get_for_read();
const GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
bke::FieldInputs density_field_inputs = density_field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> density_field_input_values;
prepare_field_inputs(
density_field_inputs, component, ATTR_DOMAIN_CORNER, density_field_input_values);
bke::FieldOutput density_field_output = density_field->evaluate(IndexRange(mesh.totloop),
density_field_inputs);
GVArray_Typed<float> density{density_field_output.varray_ref()};
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
Vector<float3> &positions = positions_all[i_instance];
@@ -518,7 +556,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
const int offset = instance_start_offsets[i_instance];
update_elimination_mask_based_on_density_factors(
mesh,
density_factors,
density,
bary_coords,
looptri_indices,
elimination_mask.as_mutable_span().slice(offset, positions.size()));
@@ -541,11 +579,10 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
static_cast<GeometryNodePointDistributeMode>(params.node().custom1);
const int seed = params.get_input<int>("Seed") * 5383843;
const float density = params.extract_input<float>("Density Max");
const std::string density_attribute_name = params.extract_input<std::string>(
"Density Attribute");
const float density_max = 1.0f;
bke::FieldRef<float> density_field = params.get_input_field<float>("Density");
if (density <= 0.0f) {
if (density_max <= 0.0f) {
params.set_output("Geometry", GeometrySet());
return;
}
@@ -586,8 +623,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
switch (distribute_method) {
case GEO_NODE_POINT_DISTRIBUTE_RANDOM: {
distribute_points_random(set_groups,
density_attribute_name,
density,
density_max,
density_field,
seed,
positions_all,
bary_coords_all,
@@ -597,8 +634,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
case GEO_NODE_POINT_DISTRIBUTE_POISSON: {
const float minimum_distance = params.extract_input<float>("Distance Min");
distribute_points_poisson_disk(set_groups,
density_attribute_name,
density,
density_max,
density_field,
seed,
minimum_distance,
positions_all,
@@ -629,6 +666,17 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
PointCloudComponent &point_component =
geometry_set_out.get_component_for_write<PointCloudComponent>();
AnonymousAttributeIDs anonymous_attribute_ids;
if (params.output_is_required("Rotation")) {
anonymous_attribute_ids.rotation = CustomData_anonymous_id_new("Rotation");
}
if (params.output_is_required("Normal")) {
anonymous_attribute_ids.normal = CustomData_anonymous_id_new("Normal");
}
if (params.output_is_required("ID")) {
anonymous_attribute_ids.id = CustomData_anonymous_id_new("ID");
}
Map<std::string, AttributeKind> attributes;
bke::geometry_set_gather_instances_attribute_info(
set_groups, {GEO_COMPONENT_TYPE_MESH}, {"position", "normal", "id"}, attributes);
@@ -637,9 +685,26 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
attributes,
point_component,
bary_coords_all,
looptri_indices_all);
looptri_indices_all,
anonymous_attribute_ids);
params.set_output("Geometry", std::move(geometry_set_out));
if (anonymous_attribute_ids.rotation != nullptr) {
params.set_output("Rotation",
bke::FieldRef<float3>(new bke::AnonymousAttributeField(
*anonymous_attribute_ids.rotation, CPPType::get<float3>())));
}
if (anonymous_attribute_ids.normal != nullptr) {
params.set_output("Normal",
bke::FieldRef<float3>(new bke::AnonymousAttributeField(
*anonymous_attribute_ids.normal, CPPType::get<float3>())));
}
if (anonymous_attribute_ids.id != nullptr) {
params.set_output("ID",
bke::FieldRef<int>(new bke::AnonymousAttributeField(
*anonymous_attribute_ids.id, CPPType::get<int>())));
}
}
} // namespace blender::nodes

View File

@@ -38,6 +38,28 @@ static bNodeSocketTemplate geo_node_point_instance_in[] = {
PROP_NONE,
SOCK_HIDE_LABEL},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{SOCK_VECTOR,
N_("Position"),
0.0f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f,
PROP_NONE,
SOCK_HIDE_VALUE | SOCK_FIELD},
{SOCK_VECTOR,
N_("Rotation"),
0.0f,
0.0f,
0.0f,
0.0f,
-10000.0f,
10000.0f,
PROP_EULER,
SOCK_FIELD},
{SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE, SOCK_FIELD},
{SOCK_INT, N_("ID"), -1.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE, SOCK_FIELD},
{-1, ""},
};
@@ -170,13 +192,27 @@ static void add_instances_from_component(InstancesComponent &instances,
const int domain_size = src_geometry.attribute_domain_size(domain);
GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>(
"position", domain, {0, 0, 0});
GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>(
"rotation", domain, {0, 0, 0});
GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>(
"scale", domain, {1, 1, 1});
GVArray_Typed<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1);
bke::FieldRef<float3> position_field = params.get_input_field<float3>("Position");
bke::FieldRef<float3> rotation_field = params.get_input_field<float3>("Rotation");
bke::FieldRef<float3> scale_field = params.get_input_field<float3>("Scale");
bke::FieldRef<int> id_field = params.get_input_field<int>("ID");
Vector<std::unique_ptr<bke::FieldInputValue>> all_inputs;
bke::FieldInputs position_inputs = position_field->prepare_inputs();
prepare_field_inputs(position_inputs, src_geometry, ATTR_DOMAIN_POINT, all_inputs);
bke::FieldInputs rotation_inputs = rotation_field->prepare_inputs();
prepare_field_inputs(rotation_inputs, src_geometry, ATTR_DOMAIN_POINT, all_inputs);
bke::FieldInputs scale_inputs = scale_field->prepare_inputs();
prepare_field_inputs(scale_inputs, src_geometry, ATTR_DOMAIN_POINT, all_inputs);
bke::FieldInputs id_inputs = id_field->prepare_inputs();
prepare_field_inputs(id_inputs, src_geometry, ATTR_DOMAIN_POINT, all_inputs);
IndexMask mask = IndexRange(domain_size);
bke::FieldOutput positions = position_field->evaluate(mask, position_inputs);
bke::FieldOutput rotations = rotation_field->evaluate(mask, rotation_inputs);
bke::FieldOutput scales = scale_field->evaluate(mask, scale_inputs);
bke::FieldOutput ids = id_field->evaluate(mask, id_inputs);
/* The initial size of the component might be non-zero if there are two component types. */
const int start_len = instances.instances_amount();
@@ -192,21 +228,40 @@ static void add_instances_from_component(InstancesComponent &instances,
threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
for (const int i : range) {
handles[i] = handle;
transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
instance_ids[i] = id_attribute[i];
float3 position;
positions.varray_ref().get(i, &position);
float3 rotation;
rotations.varray_ref().get(i, &rotation);
float3 scale;
scales.varray_ref().get(i, &scale);
int id;
ids.varray_ref().get(i, &id);
transforms[i] = float4x4::from_loc_eul_scale(position, rotation, scale);
instance_ids[i] = id;
}
});
}
else {
const int seed = params.get_input<int>("Seed");
Array<uint32_t> ids = get_geometry_element_ids_as_uints(src_geometry, ATTR_DOMAIN_POINT);
threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
for (const int i : range) {
const int index = BLI_hash_int_2d(ids[i], seed) % possible_handles.size();
float3 position;
positions.varray_ref().get(i, &position);
float3 rotation;
rotations.varray_ref().get(i, &rotation);
float3 scale;
scales.varray_ref().get(i, &scale);
int id;
ids.varray_ref().get(i, &id);
const int index = BLI_hash_int_2d(id, seed) % possible_handles.size();
const int handle = possible_handles[index];
handles[i] = handle;
transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
instance_ids[i] = id_attribute[i];
transforms[i] = float4x4::from_loc_eul_scale(position, rotation, scale);
instance_ids[i] = id;
}
});
}

View File

@@ -25,7 +25,7 @@
static bNodeSocketTemplate geo_node_point_instance_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Mask")},
{SOCK_BOOLEAN, N_("Mask"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -99,17 +99,20 @@ static void create_component_points(GeometryComponent &component, const int tota
static void separate_points_from_component(const GeometryComponent &in_component,
GeometryComponent &out_component,
const StringRef mask_name,
const bke::FieldRef<bool> mask_field,
const bool invert)
{
if (!in_component.attribute_domain_supported(ATTR_DOMAIN_POINT) ||
in_component.attribute_domain_size(ATTR_DOMAIN_POINT) == 0) {
return;
}
const int tot_in_points = in_component.attribute_domain_size(ATTR_DOMAIN_POINT);
const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>(
mask_name, ATTR_DOMAIN_POINT, false);
VArray_Span<bool> masks{mask_attribute};
bke::FieldInputs field_inputs = mask_field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, in_component, ATTR_DOMAIN_POINT, field_input_values);
bke::FieldOutput field_output = mask_field->evaluate(IndexRange(tot_in_points), field_inputs);
GVArray_Span<bool> masks{field_output.varray_ref()};
const int total = masks.count(!invert);
if (total == 0) {
@@ -122,7 +125,7 @@ static void separate_points_from_component(const GeometryComponent &in_component
}
static GeometrySet separate_geometry_set(const GeometrySet &set_in,
const StringRef mask_name,
const bke::FieldRef<bool> mask_field,
const bool invert)
{
GeometrySet set_out;
@@ -132,7 +135,7 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in,
continue;
}
GeometryComponent &out_component = set_out.get_component_for_write(component->type());
separate_points_from_component(*component, out_component, mask_name, invert);
separate_points_from_component(*component, out_component, mask_field, invert);
}
return set_out;
}
@@ -145,7 +148,7 @@ static void geo_node_point_separate_exec(GeoNodeExecParams params)
if (wait_for_inputs) {
return;
}
const std::string mask_attribute_name = params.get_input<std::string>("Mask");
bke::FieldRef<bool> mask_field = params.get_input_field<bool>("Mask");
GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry");
/* TODO: This is not necessary-- the input geometry set can be read only,
@@ -153,12 +156,10 @@ static void geo_node_point_separate_exec(GeoNodeExecParams params)
geometry_set = geometry_set_realize_instances(geometry_set);
if (params.lazy_output_is_required("Geometry 1")) {
params.set_output("Geometry 1",
separate_geometry_set(geometry_set, mask_attribute_name, true));
params.set_output("Geometry 1", separate_geometry_set(geometry_set, mask_field, true));
}
if (params.lazy_output_is_required("Geometry 2")) {
params.set_output("Geometry 2",
separate_geometry_set(geometry_set, mask_attribute_name, false));
params.set_output("Geometry 2", separate_geometry_set(geometry_set, mask_field, false));
}
}

View File

@@ -21,8 +21,17 @@
static bNodeSocketTemplate geo_node_point_translate_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Translation")},
{SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{SOCK_VECTOR,
N_("Translation"),
0.0f,
0.0f,
0.0f,
1.0f,
-FLT_MAX,
FLT_MAX,
PROP_TRANSLATION,
SOCK_FIELD},
{SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -31,13 +40,6 @@ static bNodeSocketTemplate geo_node_point_translate_out[] = {
{-1, ""},
};
static void geo_node_point_translate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE);
}
namespace blender::nodes {
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
@@ -47,11 +49,29 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
if (!position_attribute) {
return;
}
GVArray_Typed<float3> attribute = params.get_input_attribute<float3>(
"Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
for (const int i : IndexRange(attribute.size())) {
position_attribute->set(i, position_attribute->get(i) + attribute[i]);
bke::FieldRef<bool> selection_field = params.get_input_field<bool>("Selection");
bke::FieldInputs selection_field_inputs = selection_field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> selection_field_input_values;
prepare_field_inputs(
selection_field_inputs, component, ATTR_DOMAIN_POINT, selection_field_input_values);
bke::FieldOutput selection_field_output = selection_field->evaluate(
IndexRange(component.attribute_domain_size(ATTR_DOMAIN_POINT)), selection_field_inputs);
GVArray_Typed<bool> selection{selection_field_output.varray_ref()};
bke::FieldRef<float3> field = params.get_input_field<float3>("Translation");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, component, ATTR_DOMAIN_POINT, field_input_values);
bke::FieldOutput field_output = field->evaluate(
IndexRange(component.attribute_domain_size(ATTR_DOMAIN_POINT)), field_inputs);
GVArray_Typed<float3> translation{field_output.varray_ref()};
for (const int i : IndexRange(translation.size())) {
if (selection[i]) {
position_attribute->set(i, position_attribute->get(i) + translation[i]);
}
}
position_attribute.save();
@@ -108,6 +128,5 @@ void register_node_type_geo_point_translate()
node_free_standard_storage,
node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_point_translate_exec;
ntype.draw_buttons = geo_node_point_translate_layout;
nodeRegisterType(&ntype);
}

View File

@@ -33,8 +33,7 @@ static bNodeSocketTemplate geo_node_points_to_volume_in[] = {
{SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE},
{SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_STRING, N_("Radius")},
{SOCK_FLOAT, N_("Radius"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_FLOAT, N_("Radius"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_NONE, SOCK_FIELD},
{-1, ""},
};
@@ -50,7 +49,6 @@ static void geo_node_points_to_volume_layout(uiLayout *layout,
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE);
uiItemR(layout, ptr, "input_type_radius", 0, IFACE_("Radius"), ICON_NONE);
}
namespace blender::nodes {
@@ -178,8 +176,15 @@ static void gather_point_data_from_component(const GeoNodeExecParams &params,
{
GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
GVArray_Typed<float> radii = params.get_input_attribute<float>(
"Radius", component, ATTR_DOMAIN_POINT, 0.0f);
bke::FieldRef<float> field = params.get_input_field<float>("Radius");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, component, ATTR_DOMAIN_POINT, field_input_values);
bke::FieldOutput field_output = field->evaluate(
IndexRange(component.attribute_domain_size(ATTR_DOMAIN_POINT)), field_inputs);
GVArray_Typed<float> radii{field_output.varray_ref()};
for (const int i : IndexRange(positions.size())) {
r_positions.append(positions[i]);

View File

@@ -0,0 +1,45 @@
/*
* 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 "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_position_out[] = {
{SOCK_VECTOR, N_("Position")},
{-1, ""},
};
namespace blender::nodes {
static void geo_node_position_exec(GeoNodeExecParams params)
{
FieldPtr position_field = new bke::PersistentAttributeField("position", CPPType::get<float3>());
params.set_output("Position", bke::FieldRef<float3>(std::move(position_field)));
}
} // namespace blender::nodes
void register_node_type_geo_position()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_POSITION, "Position", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, nullptr, geo_node_position_out);
ntype.geometry_node_execute = blender::nodes::geo_node_position_exec;
nodeRegisterType(&ntype);
}

View File

@@ -27,21 +27,28 @@
static bNodeSocketTemplate geo_node_raycast_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_GEOMETRY, N_("Target Geometry")},
{SOCK_STRING, N_("Ray Direction")},
{SOCK_VECTOR, N_("Ray Direction"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX},
{SOCK_STRING, N_("Ray Length")},
{SOCK_FLOAT, N_("Ray Length"), 100.0, 0.0, 0.0, 0.0, 0.0f, FLT_MAX, PROP_DISTANCE},
{SOCK_VECTOR,
N_("Ray Direction"),
0.0,
0.0,
1.0,
0.0,
-FLT_MAX,
FLT_MAX,
PROP_NONE,
SOCK_FIELD},
{SOCK_FLOAT, N_("Ray Length"), 100.0, 0.0, 0.0, 0.0, 0.0f, FLT_MAX, PROP_DISTANCE, SOCK_FIELD},
{SOCK_STRING, N_("Target Attribute")},
{SOCK_STRING, N_("Is Hit")},
{SOCK_STRING, N_("Hit Position")},
{SOCK_STRING, N_("Hit Normal")},
{SOCK_STRING, N_("Hit Distance")},
{SOCK_STRING, N_("Hit Attribute")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_raycast_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_BOOLEAN, N_("Is Hit")},
{SOCK_VECTOR, N_("Hit Position")},
{SOCK_VECTOR, N_("Hit Normal")},
{SOCK_FLOAT, N_("Hit Distance")},
{-1, ""},
};
@@ -50,33 +57,18 @@ static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), Point
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE);
uiItemR(layout, ptr, "input_type_ray_direction", 0, IFACE_("Ray Direction"), ICON_NONE);
uiItemR(layout, ptr, "input_type_ray_length", 0, IFACE_("Ray Length"), ICON_NONE);
}
static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast),
__func__);
data->input_type_ray_direction = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
data->input_type_ray_length = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
node->storage = data;
}
namespace blender::nodes {
static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage;
update_attribute_input_socket_availabilities(
*node,
"Ray Direction",
(GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction);
update_attribute_input_socket_availabilities(
*node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length);
}
static void raycast_to_mesh(const Mesh &mesh,
static void raycast_to_mesh(const Mesh *mesh,
const VArray<float3> &ray_origins,
const VArray<float3> &ray_directions,
const VArray<float> &ray_lengths,
@@ -95,7 +87,7 @@ static void raycast_to_mesh(const Mesh &mesh,
BLI_assert(ray_origins.size() == r_hit_distances.size() || r_hit_distances.is_empty());
BVHTreeFromMesh tree_data;
BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4);
BKE_bvhtree_from_mesh_get(&tree_data, mesh, BVHTREE_FROM_LOOPTRI, 4);
if (tree_data.tree == nullptr) {
free_bvhtree_from_mesh(&tree_data);
return;
@@ -170,10 +162,10 @@ static bke::mesh_surface_sample::eAttributeMapMode get_map_mode(
static void raycast_from_points(const GeoNodeExecParams &params,
const GeometrySet &target_geometry,
GeometryComponent &dst_component,
const StringRef hit_name,
const StringRef hit_position_name,
const StringRef hit_normal_name,
const StringRef hit_distance_name,
const AnonymousCustomDataLayerID *hit_id,
const AnonymousCustomDataLayerID *hit_position_id,
const AnonymousCustomDataLayerID *hit_normal_id,
const AnonymousCustomDataLayerID *hit_distance_id,
const Span<std::string> hit_attribute_names,
const Span<std::string> hit_attribute_output_names)
{
@@ -199,55 +191,87 @@ static void raycast_from_points(const GeoNodeExecParams &params,
GVArray_Typed<float3> ray_origins = dst_component.attribute_get_for_read<float3>(
"position", result_domain, {0, 0, 0});
GVArray_Typed<float3> ray_directions = params.get_input_attribute<float3>(
"Ray Direction", dst_component, result_domain, {0, 0, 0});
GVArray_Typed<float> ray_lengths = params.get_input_attribute<float>(
"Ray Length", dst_component, result_domain, 0);
OutputAttribute_Typed<bool> hit_attribute =
dst_component.attribute_try_get_for_output_only<bool>(hit_name, result_domain);
OutputAttribute_Typed<float3> hit_position_attribute =
dst_component.attribute_try_get_for_output_only<float3>(hit_position_name, result_domain);
OutputAttribute_Typed<float3> hit_normal_attribute =
dst_component.attribute_try_get_for_output_only<float3>(hit_normal_name, result_domain);
OutputAttribute_Typed<float> hit_distance_attribute =
dst_component.attribute_try_get_for_output_only<float>(hit_distance_name, result_domain);
bke::FieldRef<float3> direction_field = params.get_input_field<float3>("Ray Direction");
bke::FieldInputs direction_field_inputs = direction_field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> direction_field_input_values;
prepare_field_inputs(
direction_field_inputs, dst_component, ATTR_DOMAIN_POINT, direction_field_input_values);
bke::FieldOutput direction_field_output = direction_field->evaluate(
IndexRange(ray_origins->size()), direction_field_inputs);
GVArray_Typed<float3> ray_directions{direction_field_output.varray_ref()};
bke::FieldRef<float> ray_length_field = params.get_input_field<float>("Ray Length");
bke::FieldInputs ray_length_field_inputs = ray_length_field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> ray_length_field_input_values;
prepare_field_inputs(
ray_length_field_inputs, dst_component, ATTR_DOMAIN_POINT, ray_length_field_input_values);
bke::FieldOutput ray_length_field_output = ray_length_field->evaluate(
IndexRange(ray_origins->size()), ray_length_field_inputs);
GVArray_Typed<float> ray_lengths{ray_length_field_output.varray_ref()};
std::optional<OutputAttribute_Typed<bool>> is_hit_attribute;
std::optional<OutputAttribute_Typed<float3>> hit_position_attribute;
std::optional<OutputAttribute_Typed<float3>> hit_normal_attribute;
std::optional<OutputAttribute_Typed<float>> hit_distance_attribute;
if (hit_id != nullptr) {
is_hit_attribute.emplace(dst_component.attribute_try_get_anonymous_for_output_only<bool>(
*hit_id, ATTR_DOMAIN_POINT));
}
if (hit_position_id != nullptr) {
hit_position_attribute.emplace(
dst_component.attribute_try_get_anonymous_for_output_only<float3>(*hit_position_id,
ATTR_DOMAIN_POINT));
}
if (hit_normal_id != nullptr) {
hit_normal_attribute.emplace(dst_component.attribute_try_get_anonymous_for_output_only<float3>(
*hit_normal_id, ATTR_DOMAIN_POINT));
}
if (hit_distance_id != nullptr) {
hit_distance_attribute.emplace(
dst_component.attribute_try_get_anonymous_for_output_only<float>(*hit_distance_id,
ATTR_DOMAIN_POINT));
}
/* Positions and looptri indices are always needed for interpolation,
* so create temporary arrays if no output attribute is given. */
Array<int> hit_indices;
Array<float3> hit_positions_internal;
if (!hit_attribute_names.is_empty()) {
hit_indices.reinitialize(ray_origins->size());
if (!hit_position_attribute) {
hit_positions_internal.reinitialize(ray_origins->size());
}
}
const MutableSpan<bool> is_hit = hit_attribute ? hit_attribute.as_span() : MutableSpan<bool>();
const MutableSpan<float3> hit_positions = hit_position_attribute ?
hit_position_attribute.as_span() :
hit_positions_internal;
const MutableSpan<float3> hit_normals = hit_normal_attribute ? hit_normal_attribute.as_span() :
MutableSpan<float3>();
const MutableSpan<float> hit_distances = hit_distance_attribute ?
hit_distance_attribute.as_span() :
MutableSpan<float>();
raycast_to_mesh(*src_mesh,
MutableSpan<float3> hit_positions;
Array<float3> hit_positions_internal;
if (hit_position_attribute) {
hit_positions = hit_position_attribute->as_span();
}
else {
hit_positions_internal.reinitialize(ray_origins->size());
hit_positions = hit_positions_internal;
}
raycast_to_mesh(src_mesh,
ray_origins,
ray_directions,
ray_lengths,
is_hit,
is_hit_attribute ? is_hit_attribute->as_span() : MutableSpan<bool>(),
hit_indices,
hit_positions,
hit_normals,
hit_distances);
hit_normal_attribute ? hit_normal_attribute->as_span() : MutableSpan<float3>(),
hit_distance_attribute ? hit_distance_attribute->as_span() :
MutableSpan<float>());
hit_attribute.save();
hit_position_attribute.save();
hit_normal_attribute.save();
hit_distance_attribute.save();
if (is_hit_attribute) {
is_hit_attribute->save();
}
if (hit_position_attribute) {
hit_position_attribute->save();
}
if (hit_normal_attribute) {
hit_normal_attribute->save();
}
if (hit_distance_attribute) {
hit_distance_attribute->save();
}
/* Custom interpolated attributes */
bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, hit_indices);
@@ -272,17 +296,31 @@ static void geo_node_raycast_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet target_geometry_set = params.extract_input<GeometrySet>("Target Geometry");
const std::string hit_name = params.extract_input<std::string>("Is Hit");
const std::string hit_position_name = params.extract_input<std::string>("Hit Position");
const std::string hit_normal_name = params.extract_input<std::string>("Hit Normal");
const std::string hit_distance_name = params.extract_input<std::string>("Hit Distance");
const Array<std::string> hit_names = {params.extract_input<std::string>("Target Attribute")};
const Array<std::string> hit_output_names = {params.extract_input<std::string>("Hit Attribute")};
const Array<std::string> hit_attribute_names = {
params.extract_input<std::string>("Target Attribute")};
const Array<std::string> hit_attribute_output_names = {
params.extract_input<std::string>("Hit Attribute")};
geometry_set = bke::geometry_set_realize_instances(geometry_set);
target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set);
AnonymousCustomDataLayerID *hit_id = nullptr;
AnonymousCustomDataLayerID *hit_position_id = nullptr;
AnonymousCustomDataLayerID *hit_normal_id = nullptr;
AnonymousCustomDataLayerID *hit_distance_id = nullptr;
if (params.output_is_required("Is Hit")) {
hit_id = CustomData_anonymous_id_new("Is Hit");
}
if (params.output_is_required("Hit Position")) {
hit_position_id = CustomData_anonymous_id_new("Hit Position");
}
if (params.output_is_required("Hit Normal")) {
hit_normal_id = CustomData_anonymous_id_new("Hit Normal");
}
if (params.output_is_required("Hit Distance")) {
hit_distance_id = CustomData_anonymous_id_new("Hit Distance");
}
static const Array<GeometryComponentType> types = {
GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
for (const GeometryComponentType type : types) {
@@ -290,15 +328,36 @@ static void geo_node_raycast_exec(GeoNodeExecParams params)
raycast_from_points(params,
target_geometry_set,
geometry_set.get_component_for_write(type),
hit_name,
hit_position_name,
hit_normal_name,
hit_distance_name,
hit_names,
hit_output_names);
hit_id,
hit_position_id,
hit_normal_id,
hit_distance_id,
hit_attribute_names,
hit_attribute_output_names);
}
}
if (hit_id != nullptr) {
params.set_output(
"Is Hit",
bke::FieldRef<bool>(new bke::AnonymousAttributeField(*hit_id, CPPType::get<bool>())));
}
if (hit_position_id != nullptr) {
params.set_output("Hit Position",
bke::FieldRef<float3>(new bke::AnonymousAttributeField(
*hit_position_id, CPPType::get<float3>())));
}
if (hit_normal_id != nullptr) {
params.set_output("Hit Normal",
bke::FieldRef<float3>(new bke::AnonymousAttributeField(
*hit_normal_id, CPPType::get<float3>())));
}
if (hit_distance_id != nullptr) {
params.set_output("Hit Distance",
bke::FieldRef<float>(new bke::AnonymousAttributeField(
*hit_distance_id, CPPType::get<float>())));
}
params.set_output("Geometry", geometry_set);
}
@@ -310,9 +369,8 @@ void register_node_type_geo_raycast()
geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_raycast_in, geo_node_raycast_out);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
node_type_init(&ntype, geo_node_raycast_init);
node_type_update(&ntype, blender::nodes::geo_node_raycast_update);
node_type_storage(
&ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec;

View File

@@ -0,0 +1,193 @@
/*
* 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 "UI_interface.h"
#include "UI_resources.h"
#include "BLI_kdopbvh.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_attribute_math.hh"
#include "BKE_bvhutils.h"
#include "BKE_customdata.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.hh"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_sample_mesh_surface_in[] = {
{SOCK_GEOMETRY, N_("Mesh")},
{SOCK_VECTOR,
N_("Position"),
0.0f,
0.0f,
0.0f,
1.0f,
-FLT_MAX,
FLT_MAX,
PROP_TRANSLATION,
SOCK_HIDE_VALUE | SOCK_FIELD},
{SOCK_RGBA, N_("Custom"), 1, 1, 1, 1, 0, 1, PROP_NONE, SOCK_FIELD},
{-1, ""},
};
static bNodeSocketTemplate geo_node_sample_mesh_surface_out[] = {
{SOCK_VECTOR, N_("Position")},
{SOCK_VECTOR, N_("Normal")},
{SOCK_FLOAT, N_("Distance")},
{SOCK_RGBA, N_("Custom")},
{-1, ""},
};
namespace blender::nodes {
class SampleMeshSurfaceFunction : public fn::MultiFunction {
private:
GeometrySet geometry_set_;
AnonymousCustomDataLayerID *attribute_id_;
public:
SampleMeshSurfaceFunction(GeometrySet geometry_set, AnonymousCustomDataLayerID *attribute_id)
: geometry_set_(std::move(geometry_set)), attribute_id_(attribute_id)
{
static fn::MFSignature signature = create_signature();
this->set_signature(&signature);
CustomData_anonymous_id_strong_increment(attribute_id_);
}
~SampleMeshSurfaceFunction() override
{
CustomData_anonymous_id_strong_decrement(attribute_id_);
}
static blender::fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Sample Mesh Surface"};
signature.single_input<float3>("Position");
signature.single_output<float3>("Position");
signature.single_output<float3>("Normal");
signature.single_output<float>("Distance");
signature.single_output<ColorGeometry4f>("Custom");
return signature.build();
}
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
const VArray<float3> &src_positions = params.readonly_single_input<float3>(0, "Position");
MutableSpan<float3> sampled_positions = params.uninitialized_single_output<float3>(1,
"Position");
MutableSpan<float3> sampled_normals = params.uninitialized_single_output<float3>(2, "Normal");
MutableSpan<float> sampled_distances = params.uninitialized_single_output<float>(3,
"Distance");
MutableSpan<ColorGeometry4f> sampled_custom =
params.uninitialized_single_output<ColorGeometry4f>(4, "Custom");
auto return_default = [&]() {
sampled_positions.fill_indices(mask, {0, 0, 0});
sampled_normals.fill_indices(mask, {0, 0, 0});
sampled_distances.fill_indices(mask, 0.0f);
sampled_custom.fill_indices(mask, {0, 0, 0, 1});
};
if (!geometry_set_.has_mesh()) {
return return_default();
}
const MeshComponent *mesh_component = geometry_set_.get_component_for_read<MeshComponent>();
const Mesh *mesh = mesh_component->get_for_read();
GVArrayPtr attribute_ptr = mesh_component->attribute_try_get_anonymous_for_read(
*attribute_id_, ATTR_DOMAIN_CORNER, CD_PROP_COLOR, nullptr);
if (!attribute_ptr) {
return return_default();
}
GVArray_Typed<ColorGeometry4f> attribute{*attribute_ptr};
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(mesh);
BVHTreeFromMesh tree_data;
BKE_bvhtree_from_mesh_get(&tree_data, mesh, BVHTREE_FROM_LOOPTRI, 2);
for (const int i : mask) {
BVHTreeNearest nearest;
nearest.dist_sq = FLT_MAX;
const float3 src_position = src_positions[i];
BLI_bvhtree_find_nearest(
tree_data.tree, src_position, &nearest, tree_data.nearest_callback, &tree_data);
sampled_positions[i] = nearest.co;
sampled_normals[i] = nearest.no;
sampled_distances[i] = sqrtf(nearest.dist_sq);
const MLoopTri &looptri = looptris[nearest.index];
float3 v1 = mesh->mvert[mesh->mloop[looptri.tri[0]].v].co;
float3 v2 = mesh->mvert[mesh->mloop[looptri.tri[1]].v].co;
float3 v3 = mesh->mvert[mesh->mloop[looptri.tri[2]].v].co;
ColorGeometry4f col1 = attribute[looptri.tri[0]];
ColorGeometry4f col2 = attribute[looptri.tri[1]];
ColorGeometry4f col3 = attribute[looptri.tri[2]];
float3 bary_coords;
interp_weights_tri_v3(bary_coords, v1, v2, v3, nearest.co);
ColorGeometry4f final_col = attribute_math::mix3(bary_coords, col1, col2, col3);
sampled_custom[i] = final_col;
}
}
};
static void geo_node_sample_mesh_surface_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
geometry_set = geometry_set_realize_instances(geometry_set);
FieldPtr position_field = params.get_input_field<float3>("Position").field();
bke::FieldRef<ColorGeometry4f> attribute_field = params.get_input_field<ColorGeometry4f>(
"Custom");
AnonymousCustomDataLayerID *layer_id = CustomData_anonymous_id_new("Sample Mesh Surface");
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
try_freeze_field_on_geometry(
mesh_component, *layer_id, ATTR_DOMAIN_CORNER, *attribute_field.field());
auto make_output_field = [&](int out_param_index) -> FieldPtr {
auto fn = std::make_unique<SampleMeshSurfaceFunction>(geometry_set, layer_id);
return new bke::MultiFunctionField(Vector<FieldPtr>{position_field},
optional_ptr<const fn::MultiFunction>{std::move(fn)},
out_param_index);
};
params.set_output("Position", bke::FieldRef<float3>(make_output_field(1)));
params.set_output("Normal", bke::FieldRef<float3>(make_output_field(2)));
params.set_output("Distance", bke::FieldRef<float>(make_output_field(3)));
params.set_output("Custom", bke::FieldRef<ColorGeometry4f>(make_output_field(4)));
}
} // namespace blender::nodes
void register_node_type_geo_sample_mesh_surface()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_SAMPLE_MESH_SURFACE, "Sample Mesh Surface", NODE_CLASS_ATTRIBUTE, 0);
node_type_socket_templates(
&ntype, geo_node_sample_mesh_surface_in, geo_node_sample_mesh_surface_out);
ntype.geometry_node_execute = blender::nodes::geo_node_sample_mesh_surface_exec;
nodeRegisterType(&ntype);
}

View File

@@ -38,12 +38,12 @@ static bNodeSocketTemplate geo_node_select_by_material_in[] = {
0.0f,
PROP_NONE,
SOCK_HIDE_LABEL},
{SOCK_STRING, N_("Selection")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_select_by_material_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_BOOLEAN, N_("Selection")},
{-1, ""},
};
@@ -60,6 +60,7 @@ static void select_mesh_by_material(const Mesh &mesh,
material_indices.append(i);
}
}
threading::parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
r_selection[i] = material_indices.contains(mesh.mpoly[i].mat_nr);
@@ -70,25 +71,35 @@ static void select_mesh_by_material(const Mesh &mesh,
static void geo_node_select_by_material_exec(GeoNodeExecParams params)
{
Material *material = params.extract_input<Material *>("Material");
const std::string selection_name = params.extract_input<std::string>("Selection");
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
AnonymousCustomDataLayerID *id = params.output_is_required("Selection") ?
CustomData_anonymous_id_new("Selection") :
nullptr;
if (id == nullptr) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
if (geometry_set.has<MeshComponent>()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
const Mesh *mesh = mesh_component.get_for_read();
if (mesh != nullptr) {
OutputAttribute_Typed<bool> selection =
mesh_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_FACE);
if (selection) {
select_mesh_by_material(*mesh, material, selection.as_span());
selection.save();
}
mesh_component.attribute_try_create_anonymous(
*id, ATTR_DOMAIN_FACE, CD_PROP_BOOL, AttributeInitDefault());
WriteAttributeLookup attribute = mesh_component.attribute_try_get_anonymous_for_write(*id);
MutableSpan<bool> selection = attribute.varray->get_internal_span().typed<bool>();
select_mesh_by_material(*mesh, material, selection);
}
}
params.set_output("Geometry", std::move(geometry_set));
params.set_output(
"Selection",
bke::FieldRef<bool>(new bke::AnonymousAttributeField(*id, CPPType::get<bool>())));
}
} // namespace blender::nodes

View File

@@ -0,0 +1,109 @@
/*
* 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 "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_set_position_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_VECTOR,
N_("Position"),
0.0f,
0.0f,
0.0f,
1.0f,
-FLT_MAX,
FLT_MAX,
PROP_TRANSLATION,
SOCK_FIELD},
{SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
static bNodeSocketTemplate geo_node_set_position_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
{
OutputAttribute_Typed<float3> position_attribute =
component.attribute_try_get_for_output<float3>("position", ATTR_DOMAIN_POINT, {0, 0, 0});
if (!position_attribute) {
return;
}
bke::FieldRef<bool> selection_field = params.get_input_field<bool>("Selection");
bke::FieldInputs selection_field_inputs = selection_field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> selection_field_input_values;
prepare_field_inputs(
selection_field_inputs, component, ATTR_DOMAIN_POINT, selection_field_input_values);
bke::FieldOutput selection_field_output = selection_field->evaluate(
IndexRange(component.attribute_domain_size(ATTR_DOMAIN_POINT)), selection_field_inputs);
GVArray_Typed<bool> selection{selection_field_output.varray_ref()};
bke::FieldRef<float3> field = params.get_input_field<float3>("Position");
bke::FieldInputs field_inputs = field->prepare_inputs();
Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
prepare_field_inputs(field_inputs, component, ATTR_DOMAIN_POINT, field_input_values);
bke::FieldOutput field_output = field->evaluate(
IndexRange(component.attribute_domain_size(ATTR_DOMAIN_POINT)), field_inputs);
GVArray_Typed<float3> new_positions{field_output.varray_ref()};
for (const int i : IndexRange(new_positions.size())) {
if (selection[i]) {
position_attribute->set(i, new_positions[i]);
}
}
position_attribute.save();
}
static void geo_node_set_position_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
}
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));
}
} // namespace blender::nodes
void register_node_type_geo_set_position()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_SET_POSITION, "Set Position", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_set_position_in, geo_node_set_position_out);
ntype.geometry_node_execute = blender::nodes::geo_node_set_position_exec;
nodeRegisterType(&ntype);
}

View File

@@ -22,16 +22,16 @@
static bNodeSocketTemplate geo_node_switch_in[] = {
{SOCK_BOOLEAN, N_("Switch")},
{SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
{SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000},
{SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000},
{SOCK_BOOLEAN, N_("False")},
{SOCK_BOOLEAN, N_("True")},
{SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
{SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
{SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0},
{SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0},
{SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000, PROP_NONE, SOCK_FIELD},
{SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000, PROP_NONE, SOCK_FIELD},
{SOCK_BOOLEAN, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_BOOLEAN, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_STRING, N_("False")},
{SOCK_STRING, N_("True")},
{SOCK_GEOMETRY, N_("False")},
@@ -91,6 +91,31 @@ static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node)
}
}
template<typename T>
static void output_input_field(GeoNodeExecParams &params,
const bool input,
const StringRef input_suffix,
const StringRef output_identifier)
{
const std::string name_a = "False" + input_suffix;
const std::string name_b = "True" + input_suffix;
if (input) {
params.set_input_unused(name_a);
if (params.lazy_require_input(name_b)) {
return;
}
params.set_output(output_identifier, params.get_input_field<T>(name_b));
}
else {
params.set_input_unused(name_b);
if (params.lazy_require_input(name_a)) {
return;
}
params.set_output(output_identifier, params.get_input_field<T>(name_a));
}
}
template<typename T>
static void output_input(GeoNodeExecParams &params,
const bool input,
@@ -124,19 +149,19 @@ static void geo_node_switch_exec(GeoNodeExecParams params)
const bool input = params.get_input<bool>("Switch");
switch ((eNodeSocketDatatype)storage.input_type) {
case SOCK_FLOAT: {
output_input<float>(params, input, "", "Output");
output_input_field<float>(params, input, "", "Output");
break;
}
case SOCK_INT: {
output_input<int>(params, input, "_001", "Output_001");
output_input_field<int>(params, input, "_001", "Output_001");
break;
}
case SOCK_BOOLEAN: {
output_input<bool>(params, input, "_002", "Output_002");
output_input_field<bool>(params, input, "_002", "Output_002");
break;
}
case SOCK_VECTOR: {
output_input<float3>(params, input, "_003", "Output_003");
output_input_field<float3>(params, input, "_003", "Output_003");
break;
}
case SOCK_RGBA: {

View File

@@ -106,6 +106,26 @@ void ModifierLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const
}
}
Vector<const GeometryAttributeInfo *> ModifierLog::lookup_available_attributes() const
{
Vector<const GeometryAttributeInfo *> attributes;
Set<StringRef> names;
this->foreach_node_log([&](const NodeLog &node_log) {
for (const SocketLog &socket_log : node_log.input_logs()) {
const ValueLog *value_log = socket_log.value();
if (const GeometryValueLog *geo_value_log = dynamic_cast<const GeometryValueLog *>(
value_log)) {
for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) {
if (names.add(attribute.name)) {
attributes.append(&attribute);
}
}
}
}
});
return attributes;
}
const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const
{
const destruct_ptr<NodeLog> *node_log = node_logs_.lookup_ptr_as(node_name);
@@ -162,6 +182,9 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful
bke::geometry_set_instances_attribute_foreach(
geometry_set,
[&](StringRefNull attribute_name, const AttributeMetaData &meta_data) {
if (meta_data.anonymous_layer_id != nullptr) {
return true;
}
this->attributes_.append({attribute_name, meta_data.domain, meta_data.data_type});
return true;
},

View File

@@ -32,6 +32,7 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_field.hh"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_node.h"
@@ -610,8 +611,15 @@ static bNodeSocketType *make_socket_type_bool()
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value;
};
socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
socktype->get_geometry_nodes_cpp_type = []() {
return &blender::fn::CPPType::get<blender::bke::FieldRef<bool>>();
};
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
bool value;
socket.typeinfo->get_base_cpp_value(socket, &value);
new (r_value) blender::bke::FieldRef<bool>(
blender::bke::FieldPtr{new blender::bke::ConstantField<bool>(value)});
};
return socktype;
}
@@ -622,8 +630,15 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value;
};
socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
socktype->get_geometry_nodes_cpp_type = []() {
return &blender::fn::CPPType::get<blender::bke::FieldRef<float>>();
};
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
float value;
socket.typeinfo->get_base_cpp_value(socket, &value);
new (r_value) blender::bke::FieldRef<float>(
blender::bke::FieldPtr{new blender::bke::ConstantField<float>(value)});
};
return socktype;
}
@@ -634,8 +649,15 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value;
};
socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
socktype->get_geometry_nodes_cpp_type = []() {
return &blender::fn::CPPType::get<blender::bke::FieldRef<int>>();
};
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
int value;
socket.typeinfo->get_base_cpp_value(socket, &value);
new (r_value) blender::bke::FieldRef<int>(
blender::bke::FieldPtr{new blender::bke::ConstantField<int>(value)});
};
return socktype;
}
@@ -646,8 +668,22 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value;
};
socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
socktype->get_geometry_nodes_cpp_type = []() {
return &blender::fn::CPPType::get<blender::bke::FieldRef<blender::float3>>();
};
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
if (socket.in_out == SOCK_IN && (socket.flag & SOCK_HIDE_VALUE)) {
new (r_value) blender::bke::FieldRef<blender::float3>(
blender::bke::FieldPtr{new blender::bke::PersistentAttributeField(
"position", blender::fn::CPPType::get<blender::float3>())});
}
else {
blender::float3 value;
socket.typeinfo->get_base_cpp_value(socket, &value);
new (r_value) blender::bke::FieldRef<blender::float3>(
blender::bke::FieldPtr{new blender::bke::ConstantField<blender::float3>(value)});
}
};
return socktype;
}
@@ -660,8 +696,15 @@ static bNodeSocketType *make_socket_type_rgba()
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
};
socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
socktype->get_geometry_nodes_cpp_type = []() {
return &blender::fn::CPPType::get<blender::bke::FieldRef<blender::ColorGeometry4f>>();
};
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
blender::ColorGeometry4f value;
socket.typeinfo->get_base_cpp_value(socket, &value);
new (r_value) blender::bke::FieldRef<blender::ColorGeometry4f>(
blender::bke::FieldPtr{new blender::bke::ConstantField<blender::ColorGeometry4f>(value)});
};
return socktype;
}

View File

@@ -1,103 +0,0 @@
/*
* 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.
*
* The Original Code is Copyright (C) 2005 Blender Foundation.
* All rights reserved.
*/
#include "../node_shader_util.h"
/* **************** NOISE ******************** */
static bNodeSocketTemplate sh_node_tex_noise_in[] = {
{SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
{SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f},
{SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
{SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{-1, ""},
};
static bNodeSocketTemplate sh_node_tex_noise_out[] = {
{SOCK_FLOAT,
N_("Fac"),
0.0f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f,
PROP_FACTOR,
SOCK_NO_INTERNAL_LINK},
{SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK},
{-1, ""},
};
static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeTexNoise *tex = MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise");
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
BKE_texture_colormapping_default(&tex->base.color_mapping);
tex->dimensions = 3;
node->storage = tex;
}
static int node_shader_gpu_tex_noise(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
GPUNodeStack *in,
GPUNodeStack *out)
{
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
NodeTexNoise *tex = (NodeTexNoise *)node->storage;
static const char *names[] = {
"",
"node_noise_texture_1d",
"node_noise_texture_2d",
"node_noise_texture_3d",
"node_noise_texture_4d",
};
return GPU_stack_link(mat, node, names[tex->dimensions], in, out);
}
static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector");
bNodeSocket *sockW = nodeFindSocket(node, SOCK_IN, "W");
NodeTexNoise *tex = (NodeTexNoise *)node->storage;
nodeSetSocketAvailability(sockVector, tex->dimensions != 1);
nodeSetSocketAvailability(sockW, tex->dimensions == 1 || tex->dimensions == 4);
}
/* node type definition */
void register_node_type_sh_tex_noise(void)
{
static bNodeType ntype;
sh_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0);
node_type_socket_templates(&ntype, sh_node_tex_noise_in, sh_node_tex_noise_out);
node_type_init(&ntype, node_shader_init_tex_noise);
node_type_storage(
&ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage);
node_type_gpu(&ntype, node_shader_gpu_tex_noise);
node_type_update(&ntype, node_shader_update_tex_noise);
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,222 @@
/*
* 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.
*
* The Original Code is Copyright (C) 2005 Blender Foundation.
* All rights reserved.
*/
#include "BLI_noise.h"
#include "../node_shader_util.h"
/* **************** NOISE ******************** */
static bNodeSocketTemplate sh_node_tex_noise_in[] = {
{SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
{SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f},
{SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
{SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{-1, ""},
};
static bNodeSocketTemplate sh_node_tex_noise_out[] = {
{SOCK_FLOAT,
N_("Fac"),
0.0f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f,
PROP_FACTOR,
SOCK_NO_INTERNAL_LINK},
{SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK},
{-1, ""},
};
static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeTexNoise *tex = (NodeTexNoise *)MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise");
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
BKE_texture_colormapping_default(&tex->base.color_mapping);
tex->dimensions = 3;
node->storage = tex;
}
static int node_shader_gpu_tex_noise(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
GPUNodeStack *in,
GPUNodeStack *out)
{
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
NodeTexNoise *tex = (NodeTexNoise *)node->storage;
static const char *names[] = {
"",
"node_noise_texture_1d",
"node_noise_texture_2d",
"node_noise_texture_3d",
"node_noise_texture_4d",
};
return GPU_stack_link(mat, node, names[tex->dimensions], in, out);
}
static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector");
bNodeSocket *sockW = nodeFindSocket(node, SOCK_IN, "W");
NodeTexNoise *tex = (NodeTexNoise *)node->storage;
nodeSetSocketAvailability(sockVector, tex->dimensions != 1);
nodeSetSocketAvailability(sockW, tex->dimensions == 1 || tex->dimensions == 4);
}
class NoiseTextureFunction3D : public blender::fn::MultiFunction {
public:
NoiseTextureFunction3D()
{
static blender::fn::MFSignature signature = create_signature();
this->set_signature(&signature);
}
static blender::fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Noise Texture"};
signature.single_input<blender::float3>("Vector");
signature.single_input<float>("Scale");
signature.single_input<float>("Detail");
signature.single_input<float>("Roughness");
signature.single_input<float>("Distortion");
signature.single_output<float>("Fac");
signature.single_output<blender::ColorGeometry4f>("Color");
return signature.build();
}
void call(blender::IndexMask mask,
blender::fn::MFParams params,
blender::fn::MFContext UNUSED(context)) const override
{
const blender::VArray<blender::float3> &vectors =
params.readonly_single_input<blender::float3>(0, "Vector");
const blender::VArray<float> &scales = params.readonly_single_input<float>(1, "Scale");
const blender::VArray<float> &details = params.readonly_single_input<float>(2, "Detail");
blender::MutableSpan<float> r_values = params.uninitialized_single_output<float>(5, "Fac");
blender::MutableSpan<blender::ColorGeometry4f> r_colors =
params.uninitialized_single_output<blender::ColorGeometry4f>(6, "Color");
for (int i : mask) {
const blender::float3 vector = vectors[i];
const float scale = scales[i];
const float noise_size = safe_divide(1.0f, scale);
const float detail = details[i];
const float noise1 = BLI_noise_generic_turbulence(
noise_size, vector.x, vector.y, vector.z, detail, false, 1);
const float noise2 = BLI_noise_generic_turbulence(
noise_size, vector.y, vector.x + 100.0f, vector.z, detail, false, 1);
const float noise3 = BLI_noise_generic_turbulence(
noise_size, vector.z + 100.0f, vector.y, vector.x, detail, false, 1);
r_values[i] = noise1;
r_colors[i] = {noise1, noise2, noise3, 1.0f};
}
}
};
class NoiseTextureFunction1D : public blender::fn::MultiFunction {
public:
NoiseTextureFunction1D()
{
static blender::fn::MFSignature signature = create_signature();
this->set_signature(&signature);
}
static blender::fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Noise Texture"};
signature.single_input<float>("W");
signature.single_input<float>("Scale");
signature.single_input<float>("Detail");
signature.single_input<float>("Roughness");
signature.single_input<float>("Distortion");
signature.single_output<float>("Fac");
signature.single_output<blender::ColorGeometry4f>("Color");
return signature.build();
}
void call(blender::IndexMask mask,
blender::fn::MFParams params,
blender::fn::MFContext UNUSED(context)) const override
{
const blender::VArray<float> &inputs = params.readonly_single_input<float>(0, "W");
const blender::VArray<float> &scales = params.readonly_single_input<float>(1, "Scale");
const blender::VArray<float> &details = params.readonly_single_input<float>(2, "Detail");
blender::MutableSpan<float> r_values = params.uninitialized_single_output<float>(5, "Fac");
blender::MutableSpan<blender::ColorGeometry4f> r_colors =
params.uninitialized_single_output<blender::ColorGeometry4f>(6, "Color");
for (int i : mask) {
const float value = inputs[i];
const float scale = scales[i];
const float noise_size = safe_divide(1.0f, scale);
const float detail = details[i];
const float noise1 = BLI_noise_generic_turbulence(
noise_size, value, value, value, detail, false, 1);
const float noise2 = BLI_noise_generic_turbulence(
noise_size, value, value + 100.0f, value, detail, false, 1);
const float noise3 = BLI_noise_generic_turbulence(
noise_size, value + 100.0f, value, value, detail, false, 1);
r_values[i] = noise1;
r_colors[i] = {noise1, noise2, noise3, 1.0f};
}
}
};
static void sh_node_tex_noise_build_multi_function(
blender::nodes::NodeMultiFunctionBuilder &builder)
{
/* TODO: Not only support 1D and 3D. */
NodeTexNoise *tex = (NodeTexNoise *)builder.node().storage;
if (tex->dimensions == 1) {
builder.construct_and_set_matching_fn<NoiseTextureFunction1D>();
}
else if (tex->dimensions == 3) {
builder.construct_and_set_matching_fn<NoiseTextureFunction3D>();
}
}
/* node type definition */
void register_node_type_sh_tex_noise(void)
{
static bNodeType ntype;
sh_fn_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0);
node_type_socket_templates(&ntype, sh_node_tex_noise_in, sh_node_tex_noise_out);
node_type_init(&ntype, node_shader_init_tex_noise);
node_type_storage(
&ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage);
node_type_gpu(&ntype, node_shader_gpu_tex_noise);
node_type_update(&ntype, node_shader_update_tex_noise);
ntype.build_multi_function = sh_node_tex_noise_build_multi_function;
nodeRegisterType(&ntype);
}