Compare commits
142 Commits
tmp-workbe
...
temp-geome
Author | SHA1 | Date | |
---|---|---|---|
85ca4e33a6 | |||
3aee13b349 | |||
ec5160ee94 | |||
7be52f6985 | |||
b4c49e595f | |||
ea3ee04fa8 | |||
4838436387 | |||
62d485e470 | |||
cd6eb65482 | |||
d337c20cf9 | |||
a715aec6a3 | |||
eba32b5f4a | |||
1d859c9183 | |||
a3d02965da | |||
45e58a7e92 | |||
c920b83b84 | |||
34e0d8079f | |||
a376073f1e | |||
29ac510198 | |||
d7cf8babf2 | |||
3becd4cb61 | |||
52bb0d42b3 | |||
acae8f430d | |||
bbb692ffb5 | |||
df6a819982 | |||
48f2643556 | |||
d4b441851f | |||
e0406d029d | |||
c5c82f801a | |||
78c0e7c015 | |||
fd0ac1aec5 | |||
0827cce9de | |||
2bba77045f | |||
00ecf29ec4 | |||
172e713cc6 | |||
5c32227025 | |||
fa9d1cb2a9 | |||
577a12f840 | |||
8dad29d0ea | |||
4eda4fd49a | |||
1db53a8923 | |||
20aba8a4c0 | |||
1d53bf0412 | |||
1177debb01 | |||
047daf5442 | |||
f2112199a4 | |||
36de290807 | |||
c09a535333 | |||
4e78bb3f0c | |||
49f33e9820 | |||
6f525e0d98 | |||
963448a2af | |||
9506ef320e | |||
1700fde0dc | |||
d5a83a5a32 | |||
a7dc3d1e90 | |||
6fd836f53b | |||
40455c21fa | |||
2d0442e943 | |||
d55046b167 | |||
4233128fa5 | |||
798496233a | |||
991f6b15f3 | |||
aef45a4ef2 | |||
504e3c563f | |||
8645ea16de | |||
5e292d923f | |||
1ab1d6665c | |||
b1c98ad8ee | |||
e9d95fddf9 | |||
782d240a1b | |||
36055ba366 | |||
236576fc91 | |||
e7d57e84bb | |||
b5573bfbf4 | |||
3b2522650b | |||
eacc1f1ff4 | |||
9562a90632 | |||
2ceeaf95a1 | |||
c8de9dc2a4 | |||
f8f2873b44 | |||
648fa6d89e | |||
d24419564e | |||
184be40aa1 | |||
6131506689 | |||
e5f43cf4b7 | |||
770d70127f | |||
801034ba6a | |||
17442bcb7b | |||
ba9561ab0d | |||
c659af0c13 | |||
dd8be48466 | |||
5914b8ed6b | |||
50da3cfbd6 | |||
131e8c0d59 | |||
aedd43b33f | |||
adb437ad3f | |||
c106ed8fcd | |||
11a6a56982 | |||
0a7ab583a5 | |||
09009ce062 | |||
dc502fad43 | |||
3abce9e633 | |||
dad5c3991c | |||
dab3a07659 | |||
53a724c804 | |||
bacde38b67 | |||
d9b128baf2 | |||
ae6c063b33 | |||
67e7aee853 | |||
302ab3ff7c | |||
4352d1beb5 | |||
32cb953b9b | |||
1d92a4d1a0 | |||
2472b0f0ef | |||
626be25646 | |||
8132383662 | |||
f95214e9ae | |||
dde42e19e3 | |||
f90a83f816 | |||
433b4ae22a | |||
23e217eafc | |||
87153cf019 | |||
08122a7794 | |||
58af171bc0 | |||
c98a535f5f | |||
8262e6a0c1 | |||
c00cc9fd60 | |||
0a31b1f044 | |||
5e3b33dfe8 | |||
f716060408 | |||
aa092f4e6f | |||
ad8dfb3823 | |||
bb0e0fde32 | |||
3472f83f00 | |||
1b2b07fb18 | |||
64a11ba6a2 | |||
26d8d2884c | |||
12014ccfbc | |||
34cebfbb78 | |||
2cc4e1572a | |||
ceb9e7d71e |
@@ -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"),
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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;
|
||||
|
553
source/blender/blenkernel/BKE_field.hh
Normal file
553
source/blender/blenkernel/BKE_field.hh
Normal 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; \
|
||||
}
|
@@ -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_;
|
||||
|
@@ -1420,7 +1420,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
|
||||
@@ -1477,6 +1477,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
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -1490,6 +1502,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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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) {
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -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);
|
||||
|
@@ -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"
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
{
|
||||
|
88
source/blender/blenlib/BLI_optional_ptr.hh
Normal file
88
source/blender/blenlib/BLI_optional_ptr.hh
Normal 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
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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>>;
|
||||
|
||||
/**
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@@ -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);
|
@@ -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.
|
||||
*
|
||||
|
@@ -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
|
||||
|
@@ -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 */
|
||||
|
@@ -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);
|
||||
|
@@ -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");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@@ -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);
|
||||
|
@@ -4176,7 +4176,32 @@ void node_draw_link_bezier(const View2D *v2d,
|
||||
int th_col2,
|
||||
int th_col3)
|
||||
{
|
||||
const float dim_factor = node_link_dim_factor(v2d, link);
|
||||
float dim_factor = node_link_dim_factor(v2d, link);
|
||||
|
||||
if (link->fromsock) {
|
||||
const SocketSingleState from_single_state = get_socket_single_state(
|
||||
snode->edittree, link->fromnode, link->fromsock);
|
||||
if (ELEM(from_single_state,
|
||||
SocketSingleState::RequiredSingle,
|
||||
SocketSingleState::CurrentlySingle)) {
|
||||
th_col1 = th_col2 = TH_ACTIVE;
|
||||
}
|
||||
else {
|
||||
dim_factor *= 0.7f;
|
||||
}
|
||||
}
|
||||
|
||||
if (link->fromsock && link->tosock) {
|
||||
const SocketSingleState to_single_state = get_socket_single_state(
|
||||
snode->edittree, link->tonode, link->tosock);
|
||||
const SocketSingleState from_single_state = get_socket_single_state(
|
||||
snode->edittree, link->fromnode, link->fromsock);
|
||||
|
||||
if (to_single_state == SocketSingleState::RequiredSingle &&
|
||||
from_single_state == SocketSingleState::MaybeField) {
|
||||
th_col1 = th_col2 = TH_REDALERT;
|
||||
}
|
||||
}
|
||||
|
||||
float vec[4][2];
|
||||
const bool highlighted = link->flag & NODE_LINK_TEMP_HIGHLIGHT;
|
||||
@@ -4256,7 +4281,7 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link)
|
||||
if (link->flag & NODE_LINK_VALID) {
|
||||
/* special indicated link, on drop-node */
|
||||
if (link->flag & NODE_LINKFLAG_HILITE) {
|
||||
th_col1 = th_col2 = TH_ACTIVE;
|
||||
// th_col1 = th_col2 = TH_ACTIVE;
|
||||
}
|
||||
else if (link->flag & NODE_LINK_MUTED) {
|
||||
th_col1 = th_col2 = TH_REDALERT;
|
||||
@@ -4264,10 +4289,10 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link)
|
||||
else {
|
||||
/* Regular link, highlight if connected to selected node. */
|
||||
if (link->fromnode && link->fromnode->flag & SELECT) {
|
||||
th_col1 = TH_EDGE_SELECT;
|
||||
// th_col1 = TH_EDGE_SELECT;
|
||||
}
|
||||
if (link->tonode && link->tonode->flag & SELECT) {
|
||||
th_col2 = TH_EDGE_SELECT;
|
||||
// th_col2 = TH_EDGE_SELECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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,97 @@ 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);
|
||||
}
|
||||
|
||||
#define PRIMITIVE_SOCKETS SOCK_FLOAT, SOCK_VECTOR, SOCK_INT, SOCK_BOOLEAN, SOCK_RGBA
|
||||
|
||||
static bool is_required_single(const bNodeTree *node_tree,
|
||||
const bNode *node,
|
||||
const bNodeSocket *socket)
|
||||
{
|
||||
if (socket->flag & SOCK_ALWAYS_SINGLE) {
|
||||
return true;
|
||||
}
|
||||
if (socket->in_out == SOCK_IN) {
|
||||
LISTBASE_FOREACH (const bNodeSocket *, output, &node->outputs) {
|
||||
if (ELEM(output->type, PRIMITIVE_SOCKETS)) {
|
||||
if (is_required_single(node_tree, node, output)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
LISTBASE_FOREACH (const bNodeLink *, link, &node_tree->links) {
|
||||
if (link->fromsock == socket) {
|
||||
if (is_required_single(node_tree, link->tonode, link->tosock)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_maybe_field(const bNodeTree *node_tree,
|
||||
const bNode *node,
|
||||
const bNodeSocket *socket)
|
||||
{
|
||||
if (socket->flag & SOCK_ALWAYS_FIELD) {
|
||||
return true;
|
||||
}
|
||||
if (socket->in_out == SOCK_IN) {
|
||||
bool found_link = false;
|
||||
LISTBASE_FOREACH (const bNodeLink *, link, &node_tree->links) {
|
||||
if (link->tosock == socket) {
|
||||
found_link = true;
|
||||
if (is_maybe_field(node_tree, link->fromnode, link->fromsock)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found_link && (socket->flag & SOCK_HIDE_VALUE) && socket->type == SOCK_VECTOR) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LISTBASE_FOREACH (const bNodeSocket *, input, &node->inputs) {
|
||||
if (ELEM(input->type, PRIMITIVE_SOCKETS) && !(input->flag & SOCK_ALWAYS_SINGLE)) {
|
||||
if (is_maybe_field(node_tree, node, input)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SocketSingleState get_socket_single_state(const bNodeTree *node_tree,
|
||||
const bNode *node,
|
||||
const bNodeSocket *socket)
|
||||
{
|
||||
if (node_tree->type != NTREE_GEOMETRY) {
|
||||
return SocketSingleState::MaybeField;
|
||||
}
|
||||
if (socket->type == -1) {
|
||||
return SocketSingleState::MaybeField;
|
||||
}
|
||||
if (socket->flag & SOCK_ALWAYS_SINGLE) {
|
||||
return SocketSingleState::RequiredSingle;
|
||||
}
|
||||
if (socket->flag & SOCK_ALWAYS_FIELD) {
|
||||
return SocketSingleState::MaybeField;
|
||||
}
|
||||
if (!ELEM(socket->type, PRIMITIVE_SOCKETS)) {
|
||||
return SocketSingleState::RequiredSingle;
|
||||
}
|
||||
if (is_required_single(node_tree, node, socket)) {
|
||||
return SocketSingleState::RequiredSingle;
|
||||
}
|
||||
if (is_maybe_field(node_tree, node, socket)) {
|
||||
return SocketSingleState::MaybeField;
|
||||
}
|
||||
return SocketSingleState::CurrentlySingle;
|
||||
}
|
||||
|
||||
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 +934,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 +1117,28 @@ 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;
|
||||
|
||||
const SocketSingleState single_state = get_socket_single_state(ntree, node, sock);
|
||||
if (ELEM(single_state, SocketSingleState::MaybeField, SocketSingleState::CurrentlySingle)) {
|
||||
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 +1498,52 @@ 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);
|
||||
const SocketSingleState single_state = get_socket_single_state(ntree, node, socket);
|
||||
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 (ELEM(single_state,
|
||||
SocketSingleState::CurrentlySingle,
|
||||
SocketSingleState::RequiredSingle)) {
|
||||
node_socket_draw_rounded_rectangle(color,
|
||||
outline_color,
|
||||
NODE_SOCKSIZE * 0.6f,
|
||||
NODE_SOCKSIZE * 1.3,
|
||||
socket->locx - 0.8f,
|
||||
socket->locy,
|
||||
0.75f);
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
|
||||
if (nodeSocketIsHidden(socket)) {
|
||||
continue;
|
||||
}
|
||||
const SocketSingleState single_state = get_socket_single_state(ntree, node, socket);
|
||||
if (ELEM(
|
||||
single_state, SocketSingleState::CurrentlySingle, SocketSingleState::RequiredSingle)) {
|
||||
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_rounded_rectangle(color,
|
||||
outline_color,
|
||||
NODE_SOCKSIZE * 0.6f,
|
||||
NODE_SOCKSIZE * 1.3,
|
||||
socket->locx - 0.8f,
|
||||
socket->locy,
|
||||
0.75f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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");
|
||||
}
|
||||
|
||||
|
@@ -340,3 +340,14 @@ extern const char *node_context_dir[];
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
enum class SocketSingleState {
|
||||
RequiredSingle,
|
||||
CurrentlySingle,
|
||||
MaybeField,
|
||||
};
|
||||
SocketSingleState get_socket_single_state(const struct bNodeTree *node_tree,
|
||||
const struct bNode *node,
|
||||
const struct bNodeSocket *socket);
|
||||
#endif
|
||||
|
@@ -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);
|
||||
|
@@ -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_;
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
@@ -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 */
|
||||
|
@@ -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,8 @@ typedef enum eNodeSocketFlag {
|
||||
* type is obvious and the name takes up too much space.
|
||||
*/
|
||||
SOCK_HIDE_LABEL = (1 << 12),
|
||||
SOCK_ALWAYS_SINGLE = (1 << 13),
|
||||
SOCK_ALWAYS_FIELD = (1 << 14),
|
||||
} eNodeSocketFlag;
|
||||
|
||||
/* TODO: Limit data in bNode to what we want to see saved. */
|
||||
@@ -1252,16 +1255,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 +1405,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 +1425,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
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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"},
|
||||
|
@@ -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");
|
||||
}
|
||||
|
||||
|
@@ -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"
|
||||
@@ -86,6 +87,8 @@
|
||||
#include "NOD_geometry_nodes_eval_log.hh"
|
||||
#include "NOD_node_tree_multi_function.hh"
|
||||
|
||||
#include "WM_types.h"
|
||||
|
||||
using blender::destruct_ptr;
|
||||
using blender::float3;
|
||||
using blender::FunctionRef;
|
||||
@@ -310,23 +313,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);
|
||||
}
|
||||
|
||||
@@ -401,12 +420,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;
|
||||
@@ -441,7 +463,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;
|
||||
}
|
||||
@@ -485,7 +511,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;
|
||||
@@ -517,7 +546,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;
|
||||
@@ -673,6 +704,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,7 +775,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;
|
||||
}
|
||||
@@ -734,7 +789,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)
|
||||
@@ -1045,6 +1112,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,
|
||||
@@ -1064,12 +1132,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. */
|
||||
@@ -1097,8 +1183,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1129,7 +1243,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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>>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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 *>;
|
||||
|
@@ -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
|
||||
|
@@ -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_expand_in_mf_network(
|
||||
blender::nodes::NodeMFNetworkBuilder &builder)
|
||||
{
|
||||
const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
|
||||
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.expand_in_mf_network = fn_node_align_rotation_to_vector_expand_in_mf_network;
|
||||
ntype.draw_buttons = fn_node_align_rotation_to_vector_layout;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -22,7 +22,7 @@
|
||||
#include "UI_resources.h"
|
||||
|
||||
static bNodeSocketTemplate fn_node_input_vector_out[] = {
|
||||
{SOCK_VECTOR, N_("Vector")},
|
||||
{SOCK_VECTOR, N_("Vector"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
|
@@ -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 ¶meter : 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),
|
||||
|
@@ -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
|
||||
|
@@ -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 ¶ms)
|
||||
{
|
||||
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);
|
||||
}
|
119
source/blender/nodes/geometry/nodes/node_geo_attribute.cc
Normal file
119
source/blender/nodes/geometry/nodes/node_geo_attribute.cc
Normal 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);
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{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 ¶ms,
|
||||
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 ¶ms)
|
||||
{
|
||||
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);
|
||||
|
249
source/blender/nodes/geometry/nodes/node_geo_attribute_freeze.cc
Normal file
249
source/blender/nodes/geometry/nodes/node_geo_attribute_freeze.cc
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{SOCK_INT, N_("Value"), 0, 0, 0, 0, -10000000.0f, 10000000.0f, PROP_NONE},
|
||||
{-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,
|
||||
PROP_NONE,
|
||||
SOCK_ALWAYS_FIELD},
|
||||
{SOCK_FLOAT,
|
||||
N_("Attribute"),
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
PROP_NONE,
|
||||
SOCK_ALWAYS_FIELD},
|
||||
{SOCK_RGBA,
|
||||
N_("Attribute"),
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
PROP_NONE,
|
||||
SOCK_ALWAYS_FIELD},
|
||||
{SOCK_BOOLEAN,
|
||||
N_("Attribute"),
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
PROP_NONE,
|
||||
SOCK_ALWAYS_FIELD},
|
||||
{SOCK_INT,
|
||||
N_("Attribute"),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
-10000000.0f,
|
||||
10000000.0f,
|
||||
PROP_NONE,
|
||||
SOCK_ALWAYS_FIELD},
|
||||
{-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 ¶ms,
|
||||
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 ¶ms,
|
||||
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);
|
||||
}
|
@@ -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 ¶ms)
|
||||
const GeometrySet &geometry_set_target,
|
||||
const AnonymousCustomDataLayerID *distance_id,
|
||||
const AnonymousCustomDataLayerID *location_id,
|
||||
const GeoNodeExecParams ¶ms)
|
||||
{
|
||||
/* 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);
|
||||
|
@@ -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
|
||||
|
@@ -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"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{-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);
|
||||
}
|
@@ -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
|
||||
|
@@ -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},
|
||||
{-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;
|
||||
|
@@ -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},
|
||||
{-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()) {
|
||||
|
@@ -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},
|
||||
{-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);
|
||||
}
|
||||
|
@@ -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},
|
||||
{-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 ¶ms,
|
||||
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 ¶ms,
|
||||
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 ¶ms,
|
||||
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);
|
||||
}
|
||||
|
@@ -33,8 +33,9 @@ static bNodeSocketTemplate geo_node_edge_split_in[] = {
|
||||
0.0f,
|
||||
0.0f,
|
||||
DEG2RADF(180.0f),
|
||||
PROP_ANGLE},
|
||||
{SOCK_BOOLEAN, N_("Sharp Edges")},
|
||||
PROP_ANGLE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_BOOLEAN, N_("Sharp Edges"), 0, 0, 0, 0, 0, 1, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
|
169
source/blender/nodes/geometry/nodes/node_geo_evaluate_curve.cc
Normal file
169
source/blender/nodes/geometry/nodes/node_geo_evaluate_curve.cc
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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_RGBA, N_("Custom"), 1, 1, 1, 1, 0, 1, PROP_NONE},
|
||||
{-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);
|
||||
}
|
206
source/blender/nodes/geometry/nodes/node_geo_extrude.cc
Normal file
206
source/blender/nodes/geometry/nodes/node_geo_extrude.cc
Normal 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_FLOAT, N_("Inset"), 0.0f, 0, 0, 0, FLT_MIN, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_BOOLEAN, N_("Individual"), 0, 0, 0, 0, 0, 1, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate geo_node_extrude_out[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_BOOLEAN, N_("Top Faces"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{SOCK_BOOLEAN, N_("Side Faces"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{-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);
|
||||
}
|
300
source/blender/nodes/geometry/nodes/node_geo_extrude_and_move.cc
Normal file
300
source/blender/nodes/geometry/nodes/node_geo_extrude_and_move.cc
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* 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_ALWAYS_SINGLE},
|
||||
{SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate geo_node_extrude_and_move_out[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_BOOLEAN, N_("Selection"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{-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);
|
||||
}
|
45
source/blender/nodes/geometry/nodes/node_geo_index.cc
Normal file
45
source/blender/nodes/geometry/nodes/node_geo_index.cc
Normal 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"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{-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);
|
||||
}
|
@@ -19,7 +19,7 @@
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_is_viewport_out[] = {
|
||||
{SOCK_BOOLEAN, N_("Is Viewport")},
|
||||
{SOCK_BOOLEAN, N_("Is Viewport"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
@@ -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},
|
||||
{-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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -26,8 +26,17 @@
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_mesh_primitive_circle_in[] = {
|
||||
{SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096},
|
||||
{SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT,
|
||||
N_("Radius"),
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
|
@@ -26,10 +26,37 @@
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_mesh_primitive_cone_in[] = {
|
||||
{SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096},
|
||||
{SOCK_FLOAT, N_("Radius Top"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_FLOAT, N_("Radius Bottom"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_FLOAT, N_("Depth"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT,
|
||||
N_("Radius Top"),
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT,
|
||||
N_("Radius Bottom"),
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT,
|
||||
N_("Depth"),
|
||||
2.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
|
@@ -25,7 +25,16 @@
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_mesh_primitive_cube_in[] = {
|
||||
{SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_FLOAT,
|
||||
N_("Size"),
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
|
@@ -26,9 +26,27 @@
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_mesh_primitive_cylinder_in[] = {
|
||||
{SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096},
|
||||
{SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_FLOAT, N_("Depth"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT,
|
||||
N_("Radius"),
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT,
|
||||
N_("Depth"),
|
||||
2.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
|
@@ -26,10 +26,28 @@
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_mesh_primitive_grid_in[] = {
|
||||
{SOCK_FLOAT, N_("Size X"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_FLOAT, N_("Size Y"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_INT, N_("Vertices X"), 3, 0.0f, 0.0f, 0.0f, 2, 1000},
|
||||
{SOCK_INT, N_("Vertices Y"), 3, 0.0f, 0.0f, 0.0f, 2, 1000},
|
||||
{SOCK_FLOAT,
|
||||
N_("Size X"),
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT,
|
||||
N_("Size Y"),
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_INT, N_("Vertices X"), 3, 0.0f, 0.0f, 0.0f, 2, 1000, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_INT, N_("Vertices Y"), 3, 0.0f, 0.0f, 0.0f, 2, 1000, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
|
@@ -25,8 +25,17 @@
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_mesh_primitive_ico_sphere_in[] = {
|
||||
{SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_INT, N_("Subdivisions"), 1, 0, 0, 0, 1, 7},
|
||||
{SOCK_FLOAT,
|
||||
N_("Radius"),
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_INT, N_("Subdivisions"), 1, 0, 0, 0, 1, 7, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
|
@@ -26,8 +26,17 @@
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_mesh_primitive_line_in[] = {
|
||||
{SOCK_INT, N_("Count"), 10, 0.0f, 0.0f, 0.0f, 1, 10000},
|
||||
{SOCK_FLOAT, N_("Resolution"), 1.0f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_INT, N_("Count"), 10, 0.0f, 0.0f, 0.0f, 1, 10000, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT,
|
||||
N_("Resolution"),
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.01f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_VECTOR,
|
||||
N_("Start Location"),
|
||||
0.0f,
|
||||
@@ -37,7 +46,16 @@ static bNodeSocketTemplate geo_node_mesh_primitive_line_in[] = {
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
PROP_TRANSLATION},
|
||||
{SOCK_VECTOR, N_("Offset"), 0.0f, 0.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
|
||||
{SOCK_VECTOR,
|
||||
N_("Offset"),
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
0.0f,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
PROP_TRANSLATION,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
|
@@ -26,9 +26,18 @@
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_in[] = {
|
||||
{SOCK_INT, N_("Segments"), 32, 0.0f, 0.0f, 0.0f, 3, 1024},
|
||||
{SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 2, 1024},
|
||||
{SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_INT, N_("Segments"), 32, 0.0f, 0.0f, 0.0f, 3, 1024, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 2, 1024, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT,
|
||||
N_("Radius"),
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
|
@@ -25,7 +25,7 @@
|
||||
|
||||
static bNodeSocketTemplate geo_node_mesh_subdivide_in[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6},
|
||||
{SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
|
@@ -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},
|
||||
{-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]) {
|
||||
|
45
source/blender/nodes/geometry/nodes/node_geo_normal.cc
Normal file
45
source/blender/nodes/geometry/nodes/node_geo_normal.cc
Normal 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"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{-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);
|
||||
}
|
@@ -27,9 +27,9 @@ static bNodeSocketTemplate geo_node_object_info_in[] = {
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate geo_node_object_info_out[] = {
|
||||
{SOCK_VECTOR, N_("Location")},
|
||||
{SOCK_VECTOR, N_("Rotation")},
|
||||
{SOCK_VECTOR, N_("Scale")},
|
||||
{SOCK_VECTOR, N_("Location"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_VECTOR, N_("Rotation"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_VECTOR, N_("Scale"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{-1, ""},
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -41,15 +41,26 @@ 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_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
|
||||
{SOCK_FLOAT,
|
||||
N_("Distance Min"),
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
100000.0f,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
|
||||
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000, PROP_NONE, SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate geo_node_point_distribute_out[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_VECTOR, N_("Rotation"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{SOCK_VECTOR, N_("Normal"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{SOCK_INT, N_("ID"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
@@ -84,7 +95,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 +116,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 +334,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 +421,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 +448,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 +457,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 +518,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 +533,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 +548,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 +565,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 +588,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 +632,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 +643,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 +675,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 +694,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
|
||||
|
@@ -38,6 +38,10 @@ 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_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_EULER},
|
||||
{SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE},
|
||||
{SOCK_INT, N_("ID"), -1.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
@@ -170,13 +174,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 +210,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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -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},
|
||||
{-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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -21,8 +21,8 @@
|
||||
|
||||
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_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
@@ -31,13 +31,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 +40,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 +119,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);
|
||||
}
|
||||
|
@@ -30,11 +30,37 @@
|
||||
|
||||
static bNodeSocketTemplate geo_node_points_to_volume_in[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{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_("Density"),
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_NONE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT,
|
||||
N_("Voxel Size"),
|
||||
0.3f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.01f,
|
||||
FLT_MAX,
|
||||
PROP_DISTANCE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT,
|
||||
N_("Voxel Amount"),
|
||||
64.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
FLT_MAX,
|
||||
PROP_NONE,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_FLOAT, N_("Radius"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_NONE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
@@ -50,7 +76,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 +203,15 @@ static void gather_point_data_from_component(const GeoNodeExecParams ¶ms,
|
||||
{
|
||||
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]);
|
||||
|
45
source/blender/nodes/geometry/nodes/node_geo_position.cc
Normal file
45
source/blender/nodes/geometry/nodes/node_geo_position.cc
Normal 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"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{-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);
|
||||
}
|
@@ -27,21 +27,19 @@
|
||||
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_VECTOR, N_("Ray Direction"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{SOCK_FLOAT, N_("Ray Length"), 100.0, 0.0, 0.0, 0.0, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{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"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{SOCK_VECTOR, N_("Hit Position"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{SOCK_VECTOR, N_("Hit Normal"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{SOCK_FLOAT, N_("Hit Distance"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_ALWAYS_FIELD},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
@@ -50,33 +48,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 +78,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 +153,10 @@ static bke::mesh_surface_sample::eAttributeMapMode get_map_mode(
|
||||
static void raycast_from_points(const GeoNodeExecParams ¶ms,
|
||||
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 +182,87 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms,
|
||||
|
||||
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 +287,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 +319,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 +360,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;
|
||||
|
@@ -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_RGBA, N_("Custom"), 1, 1, 1, 1, 0, 1, PROP_NONE},
|
||||
{-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);
|
||||
}
|
@@ -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
|
||||
|
100
source/blender/nodes/geometry/nodes/node_geo_set_position.cc
Normal file
100
source/blender/nodes/geometry/nodes/node_geo_set_position.cc
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE},
|
||||
{-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);
|
||||
}
|
@@ -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_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000, PROP_NONE},
|
||||
{SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000, PROP_NONE},
|
||||
{SOCK_BOOLEAN, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{SOCK_BOOLEAN, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0, -FLT_MAX, FLT_MAX, PROP_NONE},
|
||||
{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 ¶ms,
|
||||
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 ¶ms,
|
||||
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: {
|
||||
|
@@ -34,9 +34,36 @@
|
||||
|
||||
static bNodeSocketTemplate geo_node_transform_in[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
|
||||
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
|
||||
{SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
|
||||
{SOCK_VECTOR,
|
||||
N_("Translation"),
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
PROP_TRANSLATION,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_VECTOR,
|
||||
N_("Rotation"),
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
PROP_EULER,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{SOCK_VECTOR,
|
||||
N_("Scale"),
|
||||
1.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
PROP_XYZ,
|
||||
SOCK_ALWAYS_SINGLE},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
|
@@ -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;
|
||||
},
|
||||
|
@@ -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"
|
||||
@@ -611,8 +612,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;
|
||||
}
|
||||
|
||||
@@ -623,8 +631,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;
|
||||
}
|
||||
|
||||
@@ -635,8 +650,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;
|
||||
}
|
||||
|
||||
@@ -647,8 +669,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;
|
||||
}
|
||||
|
||||
@@ -661,8 +697,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;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user