Compare commits
147 Commits
temp-pose-
...
virtual-ar
Author | SHA1 | Date | |
---|---|---|---|
0176b0f217 | |||
54adef5ac4 | |||
a45e9ad95a | |||
d9df155004 | |||
dc87e3e2f4 | |||
e524a6ecf7 | |||
fc37b265c8 | |||
b2a0f69275 | |||
7eda4cde71 | |||
463b38b0e0 | |||
cf6d10ef46 | |||
0f81dafe6c | |||
537460b86d | |||
0bf630493f | |||
c7a8bcfa37 | |||
fa4b2d25cb | |||
bfc0f483c6 | |||
7bbead1e87 | |||
ddeaa42a35 | |||
fc719e98aa | |||
cff359cdff | |||
649803ea55 | |||
83cd472416 | |||
76d7e3a0cc | |||
43b56426f6 | |||
7aa38de085 | |||
0edfa5350e | |||
278b19745b | |||
afd8e4bce7 | |||
be34d14575 | |||
68c4ba3482 | |||
1266df87c8 | |||
bb9c83b9ff | |||
ea7eff1cc8 | |||
4bef49e32b | |||
ca37d8485c | |||
![]() |
58818cba40 | ||
a2e4d81849 | |||
382b06c80c | |||
45eafd6562 | |||
073ef4d265 | |||
919558854d | |||
43b08d0578 | |||
cb4646a6df | |||
dc8a43c875 | |||
0567f2b0bb | |||
43baf38758 | |||
62bff15377 | |||
ec241eb0d0 | |||
fa8d566c3b | |||
018fa1fce3 | |||
32d3b07b03 | |||
09eb04c0a8 | |||
5425388e60 | |||
fa4265517a | |||
57f87a22b2 | |||
f0cdfa3539 | |||
a26f710d62 | |||
b117fe7817 | |||
ced26bacb7 | |||
a4877f9e54 | |||
32849bb7b9 | |||
3b71133dc5 | |||
27005f58c5 | |||
bd8fd78b40 | |||
5b2353b230 | |||
51991ffd38 | |||
6e39da7948 | |||
659dc3f593 | |||
d169314f9b | |||
61c3d7aa2c | |||
ecc2db8a3a | |||
27e13a608f | |||
5c067189e3 | |||
d2c047999b | |||
6e29a9459b | |||
4cf3010714 | |||
6243ddd265 | |||
05dbbd83f0 | |||
87da0a30a0 | |||
413da88b89 | |||
9eaec84655 | |||
8c720c2771 | |||
cd6a5b1b19 | |||
0ca666880d | |||
f3c257cc06 | |||
e4da3694ab | |||
0a6b03ef08 | |||
2d5aef8af8 | |||
4f110ac739 | |||
77496742d9 | |||
8ea0243916 | |||
e37bc4e28d | |||
30fe7038bc | |||
d9859ef766 | |||
99289e8d51 | |||
4226d550c0 | |||
0d68a1dc77 | |||
d1c39eb90f | |||
6ba48a78d2 | |||
90791063eb | |||
626b7e4556 | |||
5a079d42ab | |||
2091edbda6 | |||
e891fc8f11 | |||
3ce30fa159 | |||
6e08dcd5d3 | |||
af504c01b7 | |||
df2c96e8a9 | |||
37da2b60fe | |||
5239a774c5 | |||
ccce11d651 | |||
0b8c280d99 | |||
19389787ff | |||
61cafa7605 | |||
f07159485e | |||
81a5c6f826 | |||
534977ad3a | |||
d609705cb9 | |||
36446b587b | |||
ff00a89a69 | |||
a3dfcd003a | |||
c6a96dcaf9 | |||
e439cc5e69 | |||
8116372289 | |||
14c3f379c9 | |||
5f40621496 | |||
569dc4e7f0 | |||
4bddffbeb8 | |||
c8f93066e3 | |||
2367dd2a81 | |||
5f564f2f55 | |||
5b12dacb11 | |||
1352e7bb25 | |||
c0b7b16167 | |||
406424e09d | |||
bfb6ba2fa7 | |||
bb579c4964 | |||
45883793e5 | |||
99921148e2 | |||
d4e3b8c356 | |||
418d364836 | |||
7df5a34580 | |||
4be6da2586 | |||
f19e83f347 | |||
488fc4eb50 | |||
5b9cad04c6 |
@@ -85,8 +85,8 @@ class VersionInfo:
|
||||
version_number = int(self._parse_header_file(blender_h, 'BLENDER_VERSION'))
|
||||
version_number_patch = int(self._parse_header_file(blender_h, 'BLENDER_VERSION_PATCH'))
|
||||
version_numbers = (version_number // 100, version_number % 100, version_number_patch)
|
||||
self.short_version = "%d.%02d" % (version_numbers[0], version_numbers[1])
|
||||
self.version = "%d.%02d.%d" % version_numbers
|
||||
self.short_version = "%d.%d" % (version_numbers[0], version_numbers[1])
|
||||
self.version = "%d.%d.%d" % version_numbers
|
||||
self.version_cycle = self._parse_header_file(blender_h, 'BLENDER_VERSION_CYCLE')
|
||||
self.hash = self._parse_header_file(buildinfo_h, 'BUILD_HASH')[1:-1]
|
||||
|
||||
|
@@ -923,10 +923,6 @@ function(get_blender_version)
|
||||
math(EXPR _out_version_major "${_out_version} / 100")
|
||||
math(EXPR _out_version_minor "${_out_version} % 100")
|
||||
|
||||
# Zero pad the minor version so `_out_version_minor` is always two characters.
|
||||
# This is needed if the minor version is a single digit.
|
||||
string(REGEX REPLACE "^([0-9])$" "0\\1" _out_version_minor "${_out_version_minor}")
|
||||
|
||||
# output vars
|
||||
set(BLENDER_VERSION "${_out_version_major}.${_out_version_minor}" PARENT_SCOPE)
|
||||
set(BLENDER_VERSION_MAJOR "${_out_version_major}" PARENT_SCOPE)
|
||||
|
@@ -38,7 +38,7 @@ PROJECT_NAME = Blender
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = "V2.93"
|
||||
PROJECT_NUMBER = "V3.0"
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
@@ -561,6 +561,7 @@ static OCIO_GPUDisplayShader &getGPUDisplayShader(
|
||||
GpuShaderDescRcPtr shaderdesc_to_scene_linear = GpuShaderDesc::CreateShaderDesc();
|
||||
shaderdesc_to_scene_linear->setLanguage(GPU_LANGUAGE_GLSL_1_3);
|
||||
shaderdesc_to_scene_linear->setFunctionName("OCIO_to_scene_linear");
|
||||
shaderdesc_to_scene_linear->setResourcePrefix("to_scene");
|
||||
(*(ConstProcessorRcPtr *)processor_to_scene_linear)
|
||||
->getDefaultGPUProcessor()
|
||||
->extractGpuShaderInfo(shaderdesc_to_scene_linear);
|
||||
@@ -569,6 +570,7 @@ static OCIO_GPUDisplayShader &getGPUDisplayShader(
|
||||
GpuShaderDescRcPtr shaderdesc_to_display = GpuShaderDesc::CreateShaderDesc();
|
||||
shaderdesc_to_display->setLanguage(GPU_LANGUAGE_GLSL_1_3);
|
||||
shaderdesc_to_display->setFunctionName("OCIO_to_display");
|
||||
shaderdesc_to_scene_linear->setResourcePrefix("to_display");
|
||||
(*(ConstProcessorRcPtr *)processor_to_display)
|
||||
->getDefaultGPUProcessor()
|
||||
->extractGpuShaderInfo(shaderdesc_to_display);
|
||||
|
Binary file not shown.
Submodule release/scripts/addons updated: 1dbdb95ed9...81815ea92c
Submodule release/scripts/addons_contrib updated: 5523cbaed6...8970953d4a
@@ -99,6 +99,15 @@ class PREFERENCES_OT_copy_prev(Operator):
|
||||
version = bpy.app.version
|
||||
version_new = ((version[0] * 100) + version[1])
|
||||
version_old = ((version[0] * 100) + version[1]) - 1
|
||||
|
||||
# Special case, remove when the version is > 3.0.
|
||||
if version_new == 300:
|
||||
version_new = 294
|
||||
version_old = 293
|
||||
else:
|
||||
print("TODO: remove exception!")
|
||||
# End special case.
|
||||
|
||||
# Ensure we only try to copy files from a point release.
|
||||
# The check below ensures the second numbers match.
|
||||
while (version_new % 100) // 10 == (version_old % 100) // 10:
|
||||
|
@@ -71,10 +71,10 @@ class POINTCLOUD_MT_add_attribute(Menu):
|
||||
layout = self.layout
|
||||
pointcloud = context.pointcloud
|
||||
|
||||
self.add_standard_attribute(layout, pointcloud, 'Radius', 'FLOAT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'Color', 'FLOAT_COLOR', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'Particle ID', 'INT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'Velocity', 'FLOAT_VECTOR', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'radius', 'FLOAT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'color', 'FLOAT_COLOR', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'id', 'INT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'velocity', 'FLOAT_VECTOR', 'POINT')
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@@ -246,11 +246,12 @@ class IMAGE_MT_image(Menu):
|
||||
layout.separator()
|
||||
layout.operator("image.pack", text="Pack")
|
||||
|
||||
if ima:
|
||||
if ima and context.area.ui_type == 'IMAGE_EDITOR':
|
||||
layout.separator()
|
||||
layout.operator("palette.extract_from_image", text="Extract Palette")
|
||||
layout.operator("gpencil.image_to_grease_pencil", text="Generate Grease Pencil")
|
||||
|
||||
|
||||
class IMAGE_MT_image_flip(Menu):
|
||||
bl_label = "Flip"
|
||||
|
||||
|
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "FN_cpp_type.hh"
|
||||
#include "FN_generic_span.hh"
|
||||
#include "FN_generic_virtual_array.hh"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
|
||||
@@ -30,49 +31,78 @@
|
||||
namespace blender::bke {
|
||||
|
||||
using fn::CPPType;
|
||||
using fn::GVArray;
|
||||
using fn::GVMutableArray;
|
||||
|
||||
const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
|
||||
CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
|
||||
CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types);
|
||||
AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains);
|
||||
|
||||
/**
|
||||
* This class offers an indirection for reading an attribute.
|
||||
* This is useful for the following reasons:
|
||||
* - Blender does not store all attributes the same way.
|
||||
* The simplest case are custom data layers with primitive types.
|
||||
* A bit more complex are mesh attributes like the position of vertices,
|
||||
* which are embedded into the MVert struct.
|
||||
* Even more complex to access are vertex weights.
|
||||
* - Sometimes attributes are stored on one domain, but we want to access
|
||||
* the attribute on a different domain. Therefore, we have to interpolate
|
||||
* between the domains.
|
||||
*/
|
||||
class ReadAttribute {
|
||||
protected:
|
||||
const AttributeDomain domain_;
|
||||
const CPPType &cpp_type_;
|
||||
const CustomDataType custom_data_type_;
|
||||
const int64_t size_;
|
||||
struct ReadAttributeLookup {
|
||||
std::unique_ptr<GVArray> varray;
|
||||
AttributeDomain domain;
|
||||
|
||||
/* Protects the span below, so that no two threads initialize it at the same time. */
|
||||
mutable std::mutex span_mutex_;
|
||||
/* When it is not null, it points to the attribute array or a temporary array that contains all
|
||||
* the attribute values. */
|
||||
mutable void *array_buffer_ = nullptr;
|
||||
/* Is true when the buffer above is owned by the attribute accessor. */
|
||||
mutable bool array_is_temporary_ = false;
|
||||
operator bool() const
|
||||
{
|
||||
return this->varray.get() != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct WriteAttributeLookup {
|
||||
std::unique_ptr<GVMutableArray> varray;
|
||||
AttributeDomain domain;
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->varray.get() != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class OutputAttribute {
|
||||
public:
|
||||
using SaveFn = std::function<void(OutputAttribute &)>;
|
||||
|
||||
private:
|
||||
std::unique_ptr<GVMutableArray> varray_;
|
||||
AttributeDomain domain_;
|
||||
SaveFn save_;
|
||||
std::optional<fn::GVMutableArray_GSpan> optional_span_varray_;
|
||||
bool ignore_old_values_ = false;
|
||||
|
||||
public:
|
||||
ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
|
||||
: domain_(domain),
|
||||
cpp_type_(cpp_type),
|
||||
custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
|
||||
size_(size)
|
||||
OutputAttribute() = default;
|
||||
|
||||
OutputAttribute(std::unique_ptr<GVMutableArray> varray,
|
||||
AttributeDomain domain,
|
||||
SaveFn save,
|
||||
const bool ignore_old_values)
|
||||
: varray_(std::move(varray)),
|
||||
domain_(domain),
|
||||
save_(std::move(save)),
|
||||
ignore_old_values_(ignore_old_values)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ReadAttribute();
|
||||
operator bool() const
|
||||
{
|
||||
return varray_.get() != nullptr;
|
||||
}
|
||||
|
||||
GVMutableArray &operator*()
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
GVMutableArray *operator->()
|
||||
{
|
||||
return varray_.get();
|
||||
}
|
||||
|
||||
GVMutableArray &varray()
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
AttributeDomain domain() const
|
||||
{
|
||||
@@ -81,238 +111,91 @@ class ReadAttribute {
|
||||
|
||||
const CPPType &cpp_type() const
|
||||
{
|
||||
return cpp_type_;
|
||||
return varray_->type();
|
||||
}
|
||||
|
||||
CustomDataType custom_data_type() const
|
||||
{
|
||||
return custom_data_type_;
|
||||
return cpp_type_to_custom_data_type(this->cpp_type());
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
fn::GMutableSpan as_span()
|
||||
{
|
||||
return size_;
|
||||
if (!optional_span_varray_.has_value()) {
|
||||
const bool materialize_old_values = !ignore_old_values_;
|
||||
optional_span_varray_.emplace(*varray_, materialize_old_values);
|
||||
}
|
||||
fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_;
|
||||
return span_varray;
|
||||
}
|
||||
|
||||
void get(const int64_t index, void *r_value) const
|
||||
template<typename T> MutableSpan<T> as_span()
|
||||
{
|
||||
BLI_assert(index < size_);
|
||||
this->get_internal(index, r_value);
|
||||
return this->as_span().typed<T>();
|
||||
}
|
||||
|
||||
/* Get a span that contains all attribute values. */
|
||||
fn::GSpan get_span() const;
|
||||
|
||||
template<typename T> Span<T> get_span() const
|
||||
{
|
||||
return this->get_span().typed<T>();
|
||||
}
|
||||
|
||||
protected:
|
||||
/* r_value is expected to be uninitialized. */
|
||||
virtual void get_internal(const int64_t index, void *r_value) const = 0;
|
||||
|
||||
virtual void initialize_span() const;
|
||||
void save();
|
||||
};
|
||||
|
||||
/**
|
||||
* This exists for similar reasons as the ReadAttribute class, except that
|
||||
* it does not deal with interpolation between domains.
|
||||
*/
|
||||
class WriteAttribute {
|
||||
protected:
|
||||
const AttributeDomain domain_;
|
||||
const CPPType &cpp_type_;
|
||||
const CustomDataType custom_data_type_;
|
||||
const int64_t size_;
|
||||
|
||||
/* When not null, this points either to the attribute array or to a temporary array. */
|
||||
void *array_buffer_ = nullptr;
|
||||
/* True, when the buffer points to a temporary array. */
|
||||
bool array_is_temporary_ = false;
|
||||
/* This helps to protect against forgetting to apply changes done to the array. */
|
||||
bool array_should_be_applied_ = false;
|
||||
template<typename T> class OutputAttribute_Typed {
|
||||
private:
|
||||
OutputAttribute attribute_;
|
||||
std::optional<fn::GVMutableArray_Typed<T>> optional_varray_;
|
||||
VMutableArray<T> *varray_ = nullptr;
|
||||
|
||||
public:
|
||||
WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
|
||||
: domain_(domain),
|
||||
cpp_type_(cpp_type),
|
||||
custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
|
||||
size_(size)
|
||||
OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute))
|
||||
{
|
||||
if (attribute_) {
|
||||
optional_varray_.emplace(attribute_.varray());
|
||||
varray_ = &**optional_varray_;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~WriteAttribute();
|
||||
operator bool() const
|
||||
{
|
||||
return varray_ != nullptr;
|
||||
}
|
||||
|
||||
VMutableArray<T> &operator*()
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
VMutableArray<T> *operator->()
|
||||
{
|
||||
return varray_;
|
||||
}
|
||||
|
||||
VMutableArray<T> &varray()
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
AttributeDomain domain() const
|
||||
{
|
||||
return domain_;
|
||||
return attribute_.domain();
|
||||
}
|
||||
|
||||
const CPPType &cpp_type() const
|
||||
{
|
||||
return cpp_type_;
|
||||
return CPPType::get<T>();
|
||||
}
|
||||
|
||||
CustomDataType custom_data_type() const
|
||||
{
|
||||
return custom_data_type_;
|
||||
return cpp_type_to_custom_data_type(this->cpp_type());
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
MutableSpan<T> as_span()
|
||||
{
|
||||
return size_;
|
||||
return attribute_.as_span<T>();
|
||||
}
|
||||
|
||||
void get(const int64_t index, void *r_value) const
|
||||
void save()
|
||||
{
|
||||
BLI_assert(index < size_);
|
||||
this->get_internal(index, r_value);
|
||||
}
|
||||
|
||||
void set(const int64_t index, const void *value)
|
||||
{
|
||||
BLI_assert(index < size_);
|
||||
this->set_internal(index, value);
|
||||
}
|
||||
|
||||
/* Get a span that new attribute values can be written into. When all values have been changed,
|
||||
* #apply_span has to be called. */
|
||||
fn::GMutableSpan get_span();
|
||||
/* The span returned by this method might not contain the current attribute values. */
|
||||
fn::GMutableSpan get_span_for_write_only();
|
||||
/* Write the changes to the span into the actual attribute, if they aren't already. */
|
||||
void apply_span();
|
||||
|
||||
template<typename T> MutableSpan<T> get_span()
|
||||
{
|
||||
return this->get_span().typed<T>();
|
||||
}
|
||||
|
||||
template<typename T> MutableSpan<T> get_span_for_write_only()
|
||||
{
|
||||
return this->get_span_for_write_only().typed<T>();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void get_internal(const int64_t index, void *r_value) const = 0;
|
||||
virtual void set_internal(const int64_t index, const void *value) = 0;
|
||||
|
||||
virtual void initialize_span(const bool write_only);
|
||||
virtual void apply_span_if_necessary();
|
||||
};
|
||||
|
||||
using ReadAttributePtr = std::unique_ptr<ReadAttribute>;
|
||||
using WriteAttributePtr = std::unique_ptr<WriteAttribute>;
|
||||
|
||||
/* This provides type safe access to an attribute.
|
||||
* The underlying ReadAttribute is owned optionally. */
|
||||
template<typename T> class TypedReadAttribute {
|
||||
private:
|
||||
std::unique_ptr<const ReadAttribute> owned_attribute_;
|
||||
const ReadAttribute *attribute_;
|
||||
|
||||
public:
|
||||
TypedReadAttribute(ReadAttributePtr attribute) : TypedReadAttribute(*attribute)
|
||||
{
|
||||
owned_attribute_ = std::move(attribute);
|
||||
BLI_assert(owned_attribute_);
|
||||
}
|
||||
|
||||
TypedReadAttribute(const ReadAttribute &attribute) : attribute_(&attribute)
|
||||
{
|
||||
BLI_assert(attribute_->cpp_type().is<T>());
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return attribute_->size();
|
||||
}
|
||||
|
||||
T operator[](const int64_t index) const
|
||||
{
|
||||
BLI_assert(index < attribute_->size());
|
||||
T value;
|
||||
value.~T();
|
||||
attribute_->get(index, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Get a span to that contains all attribute values for faster and more convenient access. */
|
||||
Span<T> get_span() const
|
||||
{
|
||||
return attribute_->get_span().template typed<T>();
|
||||
attribute_.save();
|
||||
}
|
||||
};
|
||||
|
||||
/* This provides type safe access to an attribute.
|
||||
* The underlying WriteAttribute is owned optionally. */
|
||||
template<typename T> class TypedWriteAttribute {
|
||||
private:
|
||||
std::unique_ptr<WriteAttribute> owned_attribute_;
|
||||
WriteAttribute *attribute_;
|
||||
|
||||
public:
|
||||
TypedWriteAttribute(WriteAttributePtr attribute) : TypedWriteAttribute(*attribute)
|
||||
{
|
||||
owned_attribute_ = std::move(attribute);
|
||||
BLI_assert(owned_attribute_);
|
||||
}
|
||||
|
||||
TypedWriteAttribute(WriteAttribute &attribute) : attribute_(&attribute)
|
||||
{
|
||||
BLI_assert(attribute_->cpp_type().is<T>());
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return attribute_->size();
|
||||
}
|
||||
|
||||
T operator[](const int64_t index) const
|
||||
{
|
||||
BLI_assert(index < attribute_->size());
|
||||
T value;
|
||||
value.~T();
|
||||
attribute_->get(index, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void set(const int64_t index, const T &value)
|
||||
{
|
||||
attribute_->set(index, &value);
|
||||
}
|
||||
|
||||
/* Get a span that new values can be written into. Once all values have been updated #apply_span
|
||||
* has to be called. */
|
||||
MutableSpan<T> get_span()
|
||||
{
|
||||
return attribute_->get_span().typed<T>();
|
||||
}
|
||||
/* The span returned by this method might not contain the current attribute values. */
|
||||
MutableSpan<T> get_span_for_write_only()
|
||||
{
|
||||
return attribute_->get_span_for_write_only().typed<T>();
|
||||
}
|
||||
|
||||
/* Write back all changes to the actual attribute, if necessary. */
|
||||
void apply_span()
|
||||
{
|
||||
attribute_->apply_span();
|
||||
}
|
||||
};
|
||||
|
||||
using BooleanReadAttribute = TypedReadAttribute<bool>;
|
||||
using FloatReadAttribute = TypedReadAttribute<float>;
|
||||
using Float2ReadAttribute = TypedReadAttribute<float2>;
|
||||
using Float3ReadAttribute = TypedReadAttribute<float3>;
|
||||
using Int32ReadAttribute = TypedReadAttribute<int>;
|
||||
using Color4fReadAttribute = TypedReadAttribute<Color4f>;
|
||||
using BooleanWriteAttribute = TypedWriteAttribute<bool>;
|
||||
using FloatWriteAttribute = TypedWriteAttribute<float>;
|
||||
using Float2WriteAttribute = TypedWriteAttribute<float2>;
|
||||
using Float3WriteAttribute = TypedWriteAttribute<float3>;
|
||||
using Int32WriteAttribute = TypedWriteAttribute<int>;
|
||||
using Color4fWriteAttribute = TypedWriteAttribute<Color4f>;
|
||||
|
||||
} // namespace blender::bke
|
||||
|
@@ -31,7 +31,7 @@ extern "C" {
|
||||
*/
|
||||
|
||||
/* Blender major and minor version. */
|
||||
#define BLENDER_VERSION 293
|
||||
#define BLENDER_VERSION 300
|
||||
/* Blender patch version for bugfix releases. */
|
||||
#define BLENDER_VERSION_PATCH 0
|
||||
/** Blender release cycle stage: alpha/beta/rc/release. */
|
||||
@@ -39,7 +39,7 @@ extern "C" {
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 17
|
||||
#define BLENDER_FILE_SUBVERSION 0
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and show a warning if the file
|
||||
|
@@ -55,60 +55,6 @@ class ComponentAttributeProviders;
|
||||
|
||||
class GeometryComponent;
|
||||
|
||||
/**
|
||||
* An #OutputAttributePtr wraps a #WriteAttributePtr that might not be stored in its final
|
||||
* destination yet. Therefore, once the attribute has been filled with data, the #save method has
|
||||
* to be called, to store the attribute where it belongs (possibly by replacing an existing
|
||||
* attribute with the same name).
|
||||
*
|
||||
* This is useful for example in the Attribute Color Ramp node, when the same attribute name is
|
||||
* used as input and output. Typically the input is a float attribute, and the output is a color.
|
||||
* Those two attributes cannot exist at the same time, due to a name collision. To handle this
|
||||
* situation well, first the output colors have to be computed before the input floats are deleted.
|
||||
* Therefore, the outputs have to be written to a temporary buffer that replaces the existing
|
||||
* attribute once all computations are done.
|
||||
*/
|
||||
class OutputAttributePtr {
|
||||
private:
|
||||
blender::bke::WriteAttributePtr attribute_;
|
||||
|
||||
public:
|
||||
OutputAttributePtr() = default;
|
||||
OutputAttributePtr(blender::bke::WriteAttributePtr attribute);
|
||||
OutputAttributePtr(GeometryComponent &component,
|
||||
AttributeDomain domain,
|
||||
std::string name,
|
||||
CustomDataType data_type);
|
||||
|
||||
~OutputAttributePtr();
|
||||
|
||||
/* Returns false, when this wrapper is empty. */
|
||||
operator bool() const
|
||||
{
|
||||
return static_cast<bool>(attribute_);
|
||||
}
|
||||
|
||||
/* Get a reference to the underlying #WriteAttribute. */
|
||||
blender::bke::WriteAttribute &get()
|
||||
{
|
||||
BLI_assert(attribute_);
|
||||
return *attribute_;
|
||||
}
|
||||
|
||||
blender::bke::WriteAttribute &operator*()
|
||||
{
|
||||
return *attribute_;
|
||||
}
|
||||
|
||||
blender::bke::WriteAttribute *operator->()
|
||||
{
|
||||
return attribute_.get();
|
||||
}
|
||||
|
||||
void save();
|
||||
void apply_span_and_save();
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains information about an attribute in a geometry component.
|
||||
* More information can be added in the future. E.g. whether the attribute is builtin and how it is
|
||||
@@ -161,21 +107,25 @@ class GeometryComponent {
|
||||
/* Can only be used with supported domain types. */
|
||||
virtual int attribute_domain_size(const AttributeDomain domain) const;
|
||||
|
||||
bool attribute_is_builtin(const blender::StringRef attribute_name) const;
|
||||
|
||||
/* Get read-only access to the highest priority attribute with the given name.
|
||||
* Returns null if the attribute does not exist. */
|
||||
blender::bke::ReadAttributePtr attribute_try_get_for_read(
|
||||
blender::bke::ReadAttributeLookup attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name) const;
|
||||
|
||||
/* Get read and write access to the highest priority attribute with the given name.
|
||||
* Returns null if the attribute does not exist. */
|
||||
blender::bke::WriteAttributePtr attribute_try_get_for_write(
|
||||
blender::bke::WriteAttributeLookup attribute_try_get_for_write(
|
||||
const blender::StringRef attribute_name);
|
||||
|
||||
/* Get a read-only attribute for the domain based on the given attribute. This can be used to
|
||||
* interpolate from one domain to another.
|
||||
* Returns null if the interpolation is not implemented. */
|
||||
virtual blender::bke::ReadAttributePtr attribute_try_adapt_domain(
|
||||
blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const;
|
||||
virtual std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
|
||||
std::unique_ptr<blender::fn::GVArray> varray,
|
||||
const AttributeDomain from_domain,
|
||||
const AttributeDomain to_domain) const;
|
||||
|
||||
/* Returns true when the attribute has been deleted. */
|
||||
bool attribute_try_delete(const blender::StringRef attribute_name);
|
||||
@@ -185,80 +135,94 @@ class GeometryComponent {
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type);
|
||||
|
||||
/* 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);
|
||||
|
||||
blender::Set<std::string> attribute_names() const;
|
||||
bool attribute_foreach(const AttributeForeachCallback callback) const;
|
||||
|
||||
virtual bool is_empty() const;
|
||||
|
||||
/* Get a read-only attribute for the given domain and data type.
|
||||
* Returns null when it does not exist. */
|
||||
blender::bke::ReadAttributePtr attribute_try_get_for_read(
|
||||
/* 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. */
|
||||
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type) const;
|
||||
|
||||
/* Get a read-only attribute interpolated to the input domain, leaving the data type unchanged.
|
||||
* Returns null when the attribute does not exist. */
|
||||
blender::bke::ReadAttributePtr attribute_try_get_for_read(
|
||||
/* Get a virtual array to read the data of an attribute on the given domain. The data type is
|
||||
* left unchanged. Returns null when the attribute does not exist or cannot be adapted to the
|
||||
* requested domain. */
|
||||
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name, const AttributeDomain domain) const;
|
||||
|
||||
/* Get a read-only attribute for the given domain and data type.
|
||||
* Returns a constant attribute based on the default value if the attribute does not exist.
|
||||
* Never returns null. */
|
||||
blender::bke::ReadAttributePtr attribute_get_for_read(const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value) const;
|
||||
|
||||
/* Get a typed read-only attribute for the given domain and type. */
|
||||
template<typename T>
|
||||
blender::bke::TypedReadAttribute<T> attribute_get_for_read(
|
||||
/* Get a virtual array to read the data of an attribute. If that is not possible, the returned
|
||||
* virtual array will contain a default value. This never returns null. */
|
||||
std::unique_ptr<blender::fn::GVArray> attribute_get_for_read(
|
||||
const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const T &default_value) const
|
||||
{
|
||||
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
|
||||
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->attribute_get_for_read(attribute_name, domain, type, &default_value);
|
||||
}
|
||||
const CustomDataType data_type,
|
||||
const void *default_value = nullptr) const;
|
||||
|
||||
/* Get a read-only dummy attribute that always returns the same value. */
|
||||
blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *value) const;
|
||||
|
||||
/* Create a read-only dummy attribute that always returns the same value.
|
||||
* The given value is converted to the correct type if necessary. */
|
||||
blender::bke::ReadAttributePtr attribute_get_constant_for_read_converted(
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType in_data_type,
|
||||
const CustomDataType out_data_type,
|
||||
const void *value) const;
|
||||
|
||||
/* Get a read-only dummy attribute that always returns the same value. */
|
||||
/* Should be used instead of the method above when the requested data type is known at compile
|
||||
* time for better type safety. */
|
||||
template<typename T>
|
||||
blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain,
|
||||
const T &value) const
|
||||
blender::fn::GVArray_Typed<T> attribute_get_for_read(const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const T &default_value) const
|
||||
{
|
||||
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
|
||||
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->attribute_get_constant_for_read(domain, type, &value);
|
||||
std::unique_ptr varray = this->attribute_get_for_read(
|
||||
attribute_name, domain, type, &default_value);
|
||||
return blender::fn::GVArray_Typed<T>(std::move(varray));
|
||||
}
|
||||
|
||||
/**
|
||||
* If an attribute with the given params exist, it is returned.
|
||||
* If no attribute with the given name exists, create it and
|
||||
* fill it with the default value if it is provided.
|
||||
* If an attribute with the given name but different domain or type exists, a temporary attribute
|
||||
* is created that has to be saved after the output has been computed. This avoids deleting
|
||||
* another attribute, before a computation is finished.
|
||||
* Returns an "output attribute", which is essentially a mutable virtual array with some commonly
|
||||
* used convience features. The returned output attribute might be empty if requested attribute
|
||||
* cannot exist on the geometry.
|
||||
*
|
||||
* This might return no attribute when the attribute cannot exist on the component.
|
||||
* 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.
|
||||
*/
|
||||
OutputAttributePtr attribute_try_get_for_output(const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value = nullptr);
|
||||
blender::bke::OutputAttribute attribute_try_get_for_output(
|
||||
const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
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. */
|
||||
blender::bke::OutputAttribute attribute_try_get_for_output_only(
|
||||
const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type);
|
||||
|
||||
/* Statically typed method corresponding to the equally named generic one. */
|
||||
template<typename T>
|
||||
blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output(
|
||||
const blender::StringRef attribute_name, 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_for_output(attribute_name, domain, data_type, &default_value);
|
||||
}
|
||||
|
||||
/* Statically typed method corresponding to the equally named generic one. */
|
||||
template<typename T>
|
||||
blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only(
|
||||
const blender::StringRef attribute_name, 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_for_output_only(attribute_name, domain, data_type);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
|
||||
@@ -377,8 +341,10 @@ class MeshComponent : public GeometryComponent {
|
||||
Mesh *get_for_write();
|
||||
|
||||
int attribute_domain_size(const AttributeDomain domain) const final;
|
||||
blender::bke::ReadAttributePtr attribute_try_adapt_domain(
|
||||
blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const final;
|
||||
std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
|
||||
std::unique_ptr<blender::fn::GVArray> varray,
|
||||
const AttributeDomain from_domain,
|
||||
const AttributeDomain to_domain) const final;
|
||||
|
||||
bool is_empty() const final;
|
||||
|
||||
|
@@ -137,7 +137,7 @@ static char *blender_version_decimal(const int version)
|
||||
{
|
||||
static char version_str[5];
|
||||
BLI_assert(version < 1000);
|
||||
BLI_snprintf(version_str, sizeof(version_str), "%d.%02d", version / 100, version % 100);
|
||||
BLI_snprintf(version_str, sizeof(version_str), "%d.%d", version / 100, version % 100);
|
||||
return version_str;
|
||||
}
|
||||
|
||||
|
@@ -44,194 +44,10 @@ using blender::float3;
|
||||
using blender::Set;
|
||||
using blender::StringRef;
|
||||
using blender::StringRefNull;
|
||||
using blender::bke::ReadAttributePtr;
|
||||
using blender::bke::WriteAttributePtr;
|
||||
using blender::fn::GMutableSpan;
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Attribute Accessor implementations
|
||||
* \{ */
|
||||
|
||||
ReadAttribute::~ReadAttribute()
|
||||
{
|
||||
if (array_is_temporary_ && array_buffer_ != nullptr) {
|
||||
cpp_type_.destruct_n(array_buffer_, size_);
|
||||
MEM_freeN(array_buffer_);
|
||||
}
|
||||
}
|
||||
|
||||
fn::GSpan ReadAttribute::get_span() const
|
||||
{
|
||||
if (size_ == 0) {
|
||||
return fn::GSpan(cpp_type_);
|
||||
}
|
||||
if (array_buffer_ == nullptr) {
|
||||
std::lock_guard lock{span_mutex_};
|
||||
if (array_buffer_ == nullptr) {
|
||||
this->initialize_span();
|
||||
}
|
||||
}
|
||||
return fn::GSpan(cpp_type_, array_buffer_, size_);
|
||||
}
|
||||
|
||||
void ReadAttribute::initialize_span() const
|
||||
{
|
||||
const int element_size = cpp_type_.size();
|
||||
array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
|
||||
array_is_temporary_ = true;
|
||||
for (const int i : IndexRange(size_)) {
|
||||
this->get_internal(i, POINTER_OFFSET(array_buffer_, i * element_size));
|
||||
}
|
||||
}
|
||||
|
||||
WriteAttribute::~WriteAttribute()
|
||||
{
|
||||
if (array_should_be_applied_) {
|
||||
CLOG_ERROR(&LOG, "Forgot to call apply_span.");
|
||||
}
|
||||
if (array_is_temporary_ && array_buffer_ != nullptr) {
|
||||
cpp_type_.destruct_n(array_buffer_, size_);
|
||||
MEM_freeN(array_buffer_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a mutable span that can be modified. When all modifications to the attribute are done,
|
||||
* #apply_span should be called. */
|
||||
fn::GMutableSpan WriteAttribute::get_span()
|
||||
{
|
||||
if (size_ == 0) {
|
||||
return fn::GMutableSpan(cpp_type_);
|
||||
}
|
||||
if (array_buffer_ == nullptr) {
|
||||
this->initialize_span(false);
|
||||
}
|
||||
array_should_be_applied_ = true;
|
||||
return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
|
||||
}
|
||||
|
||||
fn::GMutableSpan WriteAttribute::get_span_for_write_only()
|
||||
{
|
||||
if (size_ == 0) {
|
||||
return fn::GMutableSpan(cpp_type_);
|
||||
}
|
||||
if (array_buffer_ == nullptr) {
|
||||
this->initialize_span(true);
|
||||
}
|
||||
array_should_be_applied_ = true;
|
||||
return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
|
||||
}
|
||||
|
||||
void WriteAttribute::initialize_span(const bool write_only)
|
||||
{
|
||||
const int element_size = cpp_type_.size();
|
||||
array_buffer_ = MEM_mallocN_aligned(element_size * size_, cpp_type_.alignment(), __func__);
|
||||
array_is_temporary_ = true;
|
||||
if (write_only) {
|
||||
/* This does nothing for trivial types, but is necessary for general correctness. */
|
||||
cpp_type_.construct_default_n(array_buffer_, size_);
|
||||
}
|
||||
else {
|
||||
for (const int i : IndexRange(size_)) {
|
||||
this->get(i, POINTER_OFFSET(array_buffer_, i * element_size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteAttribute::apply_span()
|
||||
{
|
||||
this->apply_span_if_necessary();
|
||||
array_should_be_applied_ = false;
|
||||
}
|
||||
|
||||
void WriteAttribute::apply_span_if_necessary()
|
||||
{
|
||||
/* Only works when the span has been initialized beforehand. */
|
||||
BLI_assert(array_buffer_ != nullptr);
|
||||
|
||||
const int element_size = cpp_type_.size();
|
||||
for (const int i : IndexRange(size_)) {
|
||||
this->set_internal(i, POINTER_OFFSET(array_buffer_, i * element_size));
|
||||
}
|
||||
}
|
||||
|
||||
/* This is used by the #OutputAttributePtr class. */
|
||||
class TemporaryWriteAttribute final : public WriteAttribute {
|
||||
public:
|
||||
GMutableSpan data;
|
||||
GeometryComponent &component;
|
||||
std::string final_name;
|
||||
|
||||
TemporaryWriteAttribute(AttributeDomain domain,
|
||||
GMutableSpan data,
|
||||
GeometryComponent &component,
|
||||
std::string final_name)
|
||||
: WriteAttribute(domain, data.type(), data.size()),
|
||||
data(data),
|
||||
component(component),
|
||||
final_name(std::move(final_name))
|
||||
{
|
||||
}
|
||||
|
||||
~TemporaryWriteAttribute() override
|
||||
{
|
||||
if (data.data() != nullptr) {
|
||||
cpp_type_.destruct_n(data.data(), data.size());
|
||||
MEM_freeN(data.data());
|
||||
}
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
data.type().copy_to_uninitialized(data[index], r_value);
|
||||
}
|
||||
|
||||
void set_internal(const int64_t index, const void *value) override
|
||||
{
|
||||
data.type().copy_to_initialized(value, data[index]);
|
||||
}
|
||||
|
||||
void initialize_span(const bool UNUSED(write_only)) override
|
||||
{
|
||||
array_buffer_ = data.data();
|
||||
array_is_temporary_ = false;
|
||||
}
|
||||
|
||||
void apply_span_if_necessary() override
|
||||
{
|
||||
/* Do nothing, because the span contains the attribute itself already. */
|
||||
}
|
||||
};
|
||||
|
||||
class ConvertedReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
const CPPType &from_type_;
|
||||
const CPPType &to_type_;
|
||||
ReadAttributePtr base_attribute_;
|
||||
const nodes::DataTypeConversions &conversions_;
|
||||
|
||||
public:
|
||||
ConvertedReadAttribute(ReadAttributePtr base_attribute, const CPPType &to_type)
|
||||
: ReadAttribute(base_attribute->domain(), to_type, base_attribute->size()),
|
||||
from_type_(base_attribute->cpp_type()),
|
||||
to_type_(to_type),
|
||||
base_attribute_(std::move(base_attribute)),
|
||||
conversions_(nodes::get_implicit_type_conversions())
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
|
||||
base_attribute_->get(index, buffer);
|
||||
conversions_.convert(from_type_, to_type_, buffer, r_value);
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type)
|
||||
{
|
||||
switch (type) {
|
||||
@@ -366,7 +182,17 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains)
|
||||
return highest_priority_domain;
|
||||
}
|
||||
|
||||
ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read(
|
||||
void OutputAttribute::save()
|
||||
{
|
||||
if (optional_span_varray_.has_value()) {
|
||||
optional_span_varray_->save();
|
||||
}
|
||||
if (save_) {
|
||||
save_(*this);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<GVArray> BuiltinCustomDataLayerProvider::try_get_for_read(
|
||||
const GeometryComponent &component) const
|
||||
{
|
||||
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
||||
@@ -382,7 +208,7 @@ ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read(
|
||||
return as_read_attribute_(data, domain_size);
|
||||
}
|
||||
|
||||
WriteAttributePtr BuiltinCustomDataLayerProvider::try_get_for_write(
|
||||
std::unique_ptr<GVMutableArray> BuiltinCustomDataLayerProvider::try_get_for_write(
|
||||
GeometryComponent &component) const
|
||||
{
|
||||
if (writable_ != Writable) {
|
||||
@@ -461,7 +287,7 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component)
|
||||
return data != nullptr;
|
||||
}
|
||||
|
||||
ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
|
||||
ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
|
||||
const GeometryComponent &component, const StringRef attribute_name) const
|
||||
{
|
||||
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
||||
@@ -494,7 +320,7 @@ ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
|
||||
return {};
|
||||
}
|
||||
|
||||
WriteAttributePtr CustomDataAttributeProvider::try_get_for_write(
|
||||
WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
|
||||
GeometryComponent &component, const StringRef attribute_name) const
|
||||
{
|
||||
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
||||
@@ -593,7 +419,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
|
||||
return true;
|
||||
}
|
||||
|
||||
ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read(
|
||||
ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
|
||||
const GeometryComponent &component, const StringRef attribute_name) const
|
||||
{
|
||||
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
||||
@@ -604,14 +430,14 @@ ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read(
|
||||
if (layer.type == stored_type_) {
|
||||
if (layer.name == attribute_name) {
|
||||
const int domain_size = component.attribute_domain_size(domain_);
|
||||
return as_read_attribute_(layer.data, domain_size);
|
||||
return {as_read_attribute_(layer.data, domain_size), domain_};
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write(
|
||||
WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
|
||||
GeometryComponent &component, const StringRef attribute_name) const
|
||||
{
|
||||
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
||||
@@ -628,7 +454,7 @@ WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write(
|
||||
if (data_old != data_new) {
|
||||
custom_data_access_.update_custom_data_pointers(component);
|
||||
}
|
||||
return as_write_attribute_(layer.data, domain_size);
|
||||
return {as_write_attribute_(layer.data, domain_size), domain_};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -706,7 +532,17 @@ int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain
|
||||
return 0;
|
||||
}
|
||||
|
||||
ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
|
||||
bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_name) const
|
||||
{
|
||||
using namespace blender::bke;
|
||||
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
||||
if (providers == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return providers->builtin_attribute_providers().contains_as(attribute_name);
|
||||
}
|
||||
|
||||
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
|
||||
const StringRef attribute_name) const
|
||||
{
|
||||
using namespace blender::bke;
|
||||
@@ -717,11 +553,11 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
|
||||
const BuiltinAttributeProvider *builtin_provider =
|
||||
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
|
||||
if (builtin_provider != nullptr) {
|
||||
return builtin_provider->try_get_for_read(*this);
|
||||
return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()};
|
||||
}
|
||||
for (const DynamicAttributesProvider *dynamic_provider :
|
||||
providers->dynamic_attribute_providers()) {
|
||||
ReadAttributePtr attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
|
||||
ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
|
||||
if (attribute) {
|
||||
return attribute;
|
||||
}
|
||||
@@ -729,16 +565,19 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
|
||||
return {};
|
||||
}
|
||||
|
||||
ReadAttributePtr GeometryComponent::attribute_try_adapt_domain(
|
||||
ReadAttributePtr attribute, const AttributeDomain new_domain) const
|
||||
std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_domain(
|
||||
std::unique_ptr<blender::fn::GVArray> varray,
|
||||
const AttributeDomain from_domain,
|
||||
const AttributeDomain to_domain) const
|
||||
{
|
||||
if (attribute && attribute->domain() == new_domain) {
|
||||
return attribute;
|
||||
if (from_domain == to_domain) {
|
||||
return varray;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef attribute_name)
|
||||
blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write(
|
||||
const StringRef attribute_name)
|
||||
{
|
||||
using namespace blender::bke;
|
||||
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
||||
@@ -748,11 +587,11 @@ WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef
|
||||
const BuiltinAttributeProvider *builtin_provider =
|
||||
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
|
||||
if (builtin_provider != nullptr) {
|
||||
return builtin_provider->try_get_for_write(*this);
|
||||
return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()};
|
||||
}
|
||||
for (const DynamicAttributesProvider *dynamic_provider :
|
||||
providers->dynamic_attribute_providers()) {
|
||||
WriteAttributePtr attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
|
||||
WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
|
||||
if (attribute) {
|
||||
return attribute;
|
||||
}
|
||||
@@ -812,6 +651,24 @@ bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name)
|
||||
{
|
||||
using namespace blender::bke;
|
||||
if (attribute_name.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
||||
if (providers == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const BuiltinAttributeProvider *builtin_provider =
|
||||
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
|
||||
if (builtin_provider == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return builtin_provider->try_create(*this);
|
||||
}
|
||||
|
||||
Set<std::string> GeometryComponent::attribute_names() const
|
||||
{
|
||||
Set<std::string> attributes;
|
||||
@@ -865,264 +722,235 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
|
||||
|
||||
bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const
|
||||
{
|
||||
ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
|
||||
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
|
||||
if (attribute) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute,
|
||||
const blender::fn::CPPType &to_type)
|
||||
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::fn::CPPType &from_type = attribute->cpp_type();
|
||||
if (from_type == to_type) {
|
||||
return attribute;
|
||||
}
|
||||
|
||||
const blender::nodes::DataTypeConversions &conversions =
|
||||
blender::nodes::get_implicit_type_conversions();
|
||||
if (!conversions.is_convertible(from_type, to_type)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::make_unique<blender::bke::ConvertedReadAttribute>(std::move(attribute), to_type);
|
||||
return conversions.try_convert(std::move(varray), to_type);
|
||||
}
|
||||
|
||||
ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
|
||||
std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read(
|
||||
const StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type) const
|
||||
{
|
||||
ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
|
||||
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (domain != ATTR_DOMAIN_AUTO && attribute->domain() != domain) {
|
||||
attribute = this->attribute_try_adapt_domain(std::move(attribute), domain);
|
||||
if (!attribute) {
|
||||
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 (attribute->cpp_type() != *cpp_type) {
|
||||
attribute = try_adapt_data_type(std::move(attribute), *cpp_type);
|
||||
if (!attribute) {
|
||||
if (varray->type() != *cpp_type) {
|
||||
varray = try_adapt_data_type(std::move(varray), *cpp_type);
|
||||
if (!varray) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return attribute;
|
||||
return varray;
|
||||
}
|
||||
|
||||
ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name,
|
||||
const AttributeDomain domain) const
|
||||
std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read(
|
||||
const StringRef attribute_name, const AttributeDomain domain) const
|
||||
{
|
||||
if (!this->attribute_domain_supported(domain)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
|
||||
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (attribute->domain() != domain) {
|
||||
attribute = this->attribute_try_adapt_domain(std::move(attribute), domain);
|
||||
if (attribute.domain != domain) {
|
||||
return this->attribute_try_adapt_domain(std::move(attribute.varray), attribute.domain, domain);
|
||||
}
|
||||
|
||||
return std::move(attribute.varray);
|
||||
}
|
||||
|
||||
std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read(
|
||||
const StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value) const
|
||||
{
|
||||
std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read(
|
||||
attribute_name, 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);
|
||||
}
|
||||
|
||||
class GVMutableAttribute_For_OutputAttribute
|
||||
: public blender::fn::GVMutableArray_For_GMutableSpan {
|
||||
public:
|
||||
GeometryComponent *component;
|
||||
std::string final_name;
|
||||
|
||||
GVMutableAttribute_For_OutputAttribute(GMutableSpan data,
|
||||
GeometryComponent &component,
|
||||
std::string final_name)
|
||||
: blender::fn::GVMutableArray_For_GMutableSpan(data),
|
||||
component(&component),
|
||||
final_name(std::move(final_name))
|
||||
{
|
||||
}
|
||||
|
||||
~GVMutableAttribute_For_OutputAttribute() override
|
||||
{
|
||||
type_->destruct_n(data_, size_);
|
||||
MEM_freeN(data_);
|
||||
}
|
||||
};
|
||||
|
||||
static void save_output_attribute(blender::bke::OutputAttribute &output_attribute)
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::fn;
|
||||
using namespace blender::bke;
|
||||
|
||||
GVMutableAttribute_For_OutputAttribute &varray =
|
||||
dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray());
|
||||
|
||||
GeometryComponent &component = *varray.component;
|
||||
const StringRefNull name = varray.final_name;
|
||||
const AttributeDomain domain = output_attribute.domain();
|
||||
const CustomDataType data_type = output_attribute.custom_data_type();
|
||||
const CPPType &cpp_type = output_attribute.cpp_type();
|
||||
|
||||
component.attribute_try_delete(name);
|
||||
if (!component.attribute_try_create(varray.final_name, domain, data_type)) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Could not create the '%s' attribute with type '%s'.",
|
||||
name.c_str(),
|
||||
cpp_type.name().c_str());
|
||||
return;
|
||||
}
|
||||
WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name);
|
||||
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(
|
||||
GeometryComponent &component,
|
||||
const blender::StringRef attribute_name,
|
||||
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;
|
||||
|
||||
if (attribute_name.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type);
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions();
|
||||
|
||||
if (component.attribute_is_builtin(attribute_name)) {
|
||||
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
|
||||
if (!attribute) {
|
||||
component.attribute_try_create_builtin(attribute_name);
|
||||
attribute = component.attribute_try_get_for_write(attribute_name);
|
||||
if (!attribute) {
|
||||
/* Builtin attribute does not exist and can't be created. */
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (attribute.domain != domain) {
|
||||
/* Builtin attribute is on different domain. */
|
||||
return {};
|
||||
}
|
||||
std::unique_ptr<GVMutableArray> varray = std::move(attribute.varray);
|
||||
if (varray->type() == *cpp_type) {
|
||||
/* Builtin attribute matches exactly. */
|
||||
return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
|
||||
}
|
||||
/* Builtin attribute is on the same domain but has a different data type. */
|
||||
varray = conversions.try_convert(std::move(varray), *cpp_type);
|
||||
return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
|
||||
}
|
||||
|
||||
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
|
||||
if (!attribute) {
|
||||
component.attribute_try_create(attribute_name, domain, data_type);
|
||||
attribute = component.attribute_try_get_for_write(attribute_name);
|
||||
if (!attribute) {
|
||||
/* Can't create the attribute. */
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value) const
|
||||
{
|
||||
ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name, domain, data_type);
|
||||
if (attribute) {
|
||||
return attribute;
|
||||
if (attribute.domain == domain && attribute.varray->type() == *cpp_type) {
|
||||
/* Existing generic attribute matches exactly. */
|
||||
return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values);
|
||||
}
|
||||
return this->attribute_get_constant_for_read(domain, data_type, default_value);
|
||||
}
|
||||
|
||||
blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read(
|
||||
const AttributeDomain domain, const CustomDataType data_type, const void *value) const
|
||||
{
|
||||
BLI_assert(this->attribute_domain_supported(domain));
|
||||
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
if (value == nullptr) {
|
||||
value = cpp_type->default_value();
|
||||
}
|
||||
const int domain_size = this->attribute_domain_size(domain);
|
||||
return std::make_unique<blender::bke::ConstantReadAttribute>(
|
||||
domain, domain_size, *cpp_type, value);
|
||||
}
|
||||
|
||||
blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read_converted(
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType in_data_type,
|
||||
const CustomDataType out_data_type,
|
||||
const void *value) const
|
||||
{
|
||||
BLI_assert(this->attribute_domain_supported(domain));
|
||||
if (value == nullptr || in_data_type == out_data_type) {
|
||||
return this->attribute_get_constant_for_read(domain, out_data_type, value);
|
||||
}
|
||||
|
||||
const blender::fn::CPPType *in_cpp_type = blender::bke::custom_data_type_to_cpp_type(
|
||||
in_data_type);
|
||||
const blender::fn::CPPType *out_cpp_type = blender::bke::custom_data_type_to_cpp_type(
|
||||
out_data_type);
|
||||
BLI_assert(in_cpp_type != nullptr);
|
||||
BLI_assert(out_cpp_type != nullptr);
|
||||
|
||||
const blender::nodes::DataTypeConversions &conversions =
|
||||
blender::nodes::get_implicit_type_conversions();
|
||||
BLI_assert(conversions.is_convertible(*in_cpp_type, *out_cpp_type));
|
||||
|
||||
void *out_value = alloca(out_cpp_type->size());
|
||||
conversions.convert(*in_cpp_type, *out_cpp_type, value, out_value);
|
||||
|
||||
const int domain_size = this->attribute_domain_size(domain);
|
||||
blender::bke::ReadAttributePtr attribute = std::make_unique<blender::bke::ConstantReadAttribute>(
|
||||
domain, domain_size, *out_cpp_type, out_value);
|
||||
|
||||
out_cpp_type->destruct(out_value);
|
||||
return attribute;
|
||||
}
|
||||
|
||||
OutputAttributePtr GeometryComponent::attribute_try_get_for_output(const StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value)
|
||||
{
|
||||
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
|
||||
WriteAttributePtr attribute = this->attribute_try_get_for_write(attribute_name);
|
||||
|
||||
/* If the attribute doesn't exist, make a new one with the correct type. */
|
||||
if (!attribute) {
|
||||
this->attribute_try_create(attribute_name, domain, data_type);
|
||||
attribute = this->attribute_try_get_for_write(attribute_name);
|
||||
if (attribute && default_value != nullptr) {
|
||||
void *data = attribute->get_span_for_write_only().data();
|
||||
cpp_type->fill_initialized(default_value, data, attribute->size());
|
||||
attribute->apply_span();
|
||||
}
|
||||
return OutputAttributePtr(std::move(attribute));
|
||||
}
|
||||
|
||||
/* If an existing attribute has a matching domain and type, just use that. */
|
||||
if (attribute->domain() == domain && attribute->cpp_type() == *cpp_type) {
|
||||
return OutputAttributePtr(std::move(attribute));
|
||||
}
|
||||
|
||||
/* Otherwise create a temporary buffer to use before saving the new attribute. */
|
||||
return OutputAttributePtr(*this, domain, attribute_name, data_type);
|
||||
}
|
||||
|
||||
/* Construct from an attribute that already exists in the geometry component. */
|
||||
OutputAttributePtr::OutputAttributePtr(WriteAttributePtr attribute)
|
||||
: attribute_(std::move(attribute))
|
||||
{
|
||||
}
|
||||
|
||||
/* Construct a temporary attribute that has to replace an existing one later on. */
|
||||
OutputAttributePtr::OutputAttributePtr(GeometryComponent &component,
|
||||
AttributeDomain domain,
|
||||
std::string final_name,
|
||||
CustomDataType data_type)
|
||||
{
|
||||
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
|
||||
const int domain_size = component.attribute_domain_size(domain);
|
||||
void *buffer = MEM_malloc_arrayN(domain_size, cpp_type->size(), __func__);
|
||||
GMutableSpan new_span{*cpp_type, buffer, domain_size};
|
||||
|
||||
/* Copy converted values from conflicting attribute, in case the value is read.
|
||||
* TODO: An optimization could be to not do this, when the caller says that the attribute will
|
||||
* only be written. */
|
||||
ReadAttributePtr src_attribute = component.attribute_get_for_read(
|
||||
final_name, domain, data_type, nullptr);
|
||||
for (const int i : blender::IndexRange(domain_size)) {
|
||||
src_attribute->get(i, new_span[i]);
|
||||
/* 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->construct_default_n(data, domain);
|
||||
}
|
||||
else {
|
||||
/* Fill the temporary array with values from the existing attribute. */
|
||||
std::unique_ptr<GVArray> old_varray = component.attribute_get_for_read(
|
||||
attribute_name, domain, data_type, default_value);
|
||||
old_varray->materialize_to_uninitialized(IndexRange(domain_size), data);
|
||||
}
|
||||
std::unique_ptr<GVMutableArray> varray =
|
||||
std::make_unique<GVMutableAttribute_For_OutputAttribute>(
|
||||
GMutableSpan{*cpp_type, data, domain_size}, component, attribute_name);
|
||||
|
||||
attribute_ = std::make_unique<blender::bke::TemporaryWriteAttribute>(
|
||||
domain, new_span, component, std::move(final_name));
|
||||
return OutputAttribute(std::move(varray), domain, save_output_attribute, true);
|
||||
}
|
||||
|
||||
/* Store the computed attribute. If it was stored from the beginning already, nothing is done. This
|
||||
* might delete another attribute with the same name. */
|
||||
void OutputAttributePtr::save()
|
||||
blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output(
|
||||
const StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value)
|
||||
{
|
||||
if (!attribute_) {
|
||||
CLOG_WARN(&LOG, "Trying to save an attribute that does not exist anymore.");
|
||||
return;
|
||||
}
|
||||
|
||||
blender::bke::TemporaryWriteAttribute *attribute =
|
||||
dynamic_cast<blender::bke::TemporaryWriteAttribute *>(attribute_.get());
|
||||
|
||||
if (attribute == nullptr) {
|
||||
/* The attribute is saved already. */
|
||||
attribute_.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
StringRefNull name = attribute->final_name;
|
||||
const blender::fn::CPPType &cpp_type = attribute->cpp_type();
|
||||
|
||||
/* Delete an existing attribute with the same name if necessary. */
|
||||
attribute->component.attribute_try_delete(name);
|
||||
|
||||
if (!attribute->component.attribute_try_create(
|
||||
name, attribute_->domain(), attribute_->custom_data_type())) {
|
||||
/* Cannot create the target attribute for some reason. */
|
||||
CLOG_WARN(&LOG,
|
||||
"Creating the '%s' attribute with type '%s' failed.",
|
||||
name.c_str(),
|
||||
cpp_type.name().c_str());
|
||||
attribute_.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
WriteAttributePtr new_attribute = attribute->component.attribute_try_get_for_write(name);
|
||||
|
||||
GMutableSpan temp_span = attribute->data;
|
||||
GMutableSpan new_span = new_attribute->get_span_for_write_only();
|
||||
BLI_assert(temp_span.size() == new_span.size());
|
||||
|
||||
/* Currently we copy over the attribute. In the future we want to reuse the buffer. */
|
||||
cpp_type.move_to_initialized_n(temp_span.data(), new_span.data(), new_span.size());
|
||||
new_attribute->apply_span();
|
||||
|
||||
attribute_.reset();
|
||||
return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value);
|
||||
}
|
||||
|
||||
OutputAttributePtr::~OutputAttributePtr()
|
||||
blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only(
|
||||
const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type)
|
||||
{
|
||||
if (attribute_) {
|
||||
CLOG_ERROR(&LOG, "Forgot to call #save or #apply_span_and_save.");
|
||||
}
|
||||
return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr);
|
||||
}
|
||||
|
||||
/* Utility function to call #apply_span and #save in the right order. */
|
||||
void OutputAttributePtr::apply_span_and_save()
|
||||
{
|
||||
BLI_assert(attribute_);
|
||||
attribute_->apply_span();
|
||||
this->save();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@@ -24,167 +24,6 @@
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
class ConstantReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
void *value_;
|
||||
|
||||
public:
|
||||
ConstantReadAttribute(AttributeDomain domain,
|
||||
const int64_t size,
|
||||
const CPPType &type,
|
||||
const void *value)
|
||||
: ReadAttribute(domain, type, size)
|
||||
{
|
||||
value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
|
||||
type.copy_to_uninitialized(value, value_);
|
||||
}
|
||||
|
||||
~ConstantReadAttribute() override
|
||||
{
|
||||
this->cpp_type_.destruct(value_);
|
||||
MEM_freeN(value_);
|
||||
}
|
||||
|
||||
void get_internal(const int64_t UNUSED(index), void *r_value) const override
|
||||
{
|
||||
this->cpp_type_.copy_to_uninitialized(value_, r_value);
|
||||
}
|
||||
|
||||
void initialize_span() const override
|
||||
{
|
||||
const int element_size = cpp_type_.size();
|
||||
array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
|
||||
array_is_temporary_ = true;
|
||||
cpp_type_.fill_uninitialized(value_, array_buffer_, size_);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class ArrayReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
Span<T> data_;
|
||||
|
||||
public:
|
||||
ArrayReadAttribute(AttributeDomain domain, Span<T> data)
|
||||
: ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
new (r_value) T(data_[index]);
|
||||
}
|
||||
|
||||
void initialize_span() const override
|
||||
{
|
||||
/* The data will not be modified, so this const_cast is fine. */
|
||||
array_buffer_ = const_cast<T *>(data_.data());
|
||||
array_is_temporary_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
Array<T> data_;
|
||||
|
||||
public:
|
||||
OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data)
|
||||
: ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(data))
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
new (r_value) T(data_[index]);
|
||||
}
|
||||
|
||||
void initialize_span() const override
|
||||
{
|
||||
/* The data will not be modified, so this const_cast is fine. */
|
||||
array_buffer_ = const_cast<T *>(data_.data());
|
||||
array_is_temporary_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
|
||||
class DerivedArrayReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
Span<StructT> data_;
|
||||
|
||||
public:
|
||||
DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data)
|
||||
: ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
const StructT &struct_value = data_[index];
|
||||
const ElemT value = GetFunc(struct_value);
|
||||
new (r_value) ElemT(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
|
||||
private:
|
||||
MutableSpan<T> data_;
|
||||
|
||||
public:
|
||||
ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data)
|
||||
: WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
new (r_value) T(data_[index]);
|
||||
}
|
||||
|
||||
void set_internal(const int64_t index, const void *value) override
|
||||
{
|
||||
data_[index] = *reinterpret_cast<const T *>(value);
|
||||
}
|
||||
|
||||
void initialize_span(const bool UNUSED(write_only)) override
|
||||
{
|
||||
array_buffer_ = data_.data();
|
||||
array_is_temporary_ = false;
|
||||
}
|
||||
|
||||
void apply_span_if_necessary() override
|
||||
{
|
||||
/* Do nothing, because the span contains the attribute itself already. */
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StructT,
|
||||
typename ElemT,
|
||||
ElemT (*GetFunc)(const StructT &),
|
||||
void (*SetFunc)(StructT &, const ElemT &)>
|
||||
class DerivedArrayWriteAttribute final : public WriteAttribute {
|
||||
private:
|
||||
MutableSpan<StructT> data_;
|
||||
|
||||
public:
|
||||
DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data)
|
||||
: WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
const StructT &struct_value = data_[index];
|
||||
const ElemT value = GetFunc(struct_value);
|
||||
new (r_value) ElemT(value);
|
||||
}
|
||||
|
||||
void set_internal(const int64_t index, const void *value) override
|
||||
{
|
||||
StructT &struct_value = data_[index];
|
||||
const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
|
||||
SetFunc(struct_value, typed_value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility to group together multiple functions that are used to access custom data on geometry
|
||||
* components in a generic way.
|
||||
@@ -244,8 +83,9 @@ class BuiltinAttributeProvider {
|
||||
{
|
||||
}
|
||||
|
||||
virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0;
|
||||
virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0;
|
||||
virtual std::unique_ptr<GVArray> try_get_for_read(const GeometryComponent &component) const = 0;
|
||||
virtual std::unique_ptr<GVMutableArray> try_get_for_write(
|
||||
GeometryComponent &component) const = 0;
|
||||
virtual bool try_delete(GeometryComponent &component) const = 0;
|
||||
virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0;
|
||||
virtual bool exists(const GeometryComponent &component) const = 0;
|
||||
@@ -272,10 +112,10 @@ class BuiltinAttributeProvider {
|
||||
*/
|
||||
class DynamicAttributesProvider {
|
||||
public:
|
||||
virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const = 0;
|
||||
virtual WriteAttributePtr try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const = 0;
|
||||
virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const = 0;
|
||||
virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const = 0;
|
||||
virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
|
||||
virtual bool try_create(GeometryComponent &UNUSED(component),
|
||||
const StringRef UNUSED(attribute_name),
|
||||
@@ -309,11 +149,11 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
{
|
||||
}
|
||||
|
||||
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
|
||||
WriteAttributePtr try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
|
||||
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
|
||||
|
||||
@@ -332,18 +172,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer,
|
||||
const int domain_size) const
|
||||
ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
|
||||
const int domain_size) const
|
||||
{
|
||||
return std::make_unique<ArrayReadAttribute<T>>(
|
||||
domain_, Span(static_cast<const T *>(layer.data), domain_size));
|
||||
return {std::make_unique<fn::GVArray_For_Span<T>>(
|
||||
Span(static_cast<const T *>(layer.data), domain_size)),
|
||||
domain_};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const
|
||||
WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
|
||||
const int domain_size) const
|
||||
{
|
||||
return std::make_unique<ArrayWriteAttribute<T>>(
|
||||
domain_, MutableSpan(static_cast<T *>(layer.data), domain_size));
|
||||
return {std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
|
||||
MutableSpan(static_cast<T *>(layer.data), domain_size)),
|
||||
domain_};
|
||||
}
|
||||
|
||||
bool type_is_supported(CustomDataType data_type) const
|
||||
@@ -357,8 +200,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
*/
|
||||
class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
|
||||
private:
|
||||
using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
|
||||
using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
|
||||
using AsReadAttribute = std::unique_ptr<GVArray> (*)(const void *data, const int domain_size);
|
||||
using AsWriteAttribute = std::unique_ptr<GVMutableArray> (*)(void *data, const int domain_size);
|
||||
const AttributeDomain domain_;
|
||||
const CustomDataType attribute_type_;
|
||||
const CustomDataType stored_type_;
|
||||
@@ -382,10 +225,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
|
||||
{
|
||||
}
|
||||
|
||||
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
WriteAttributePtr try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
|
||||
bool foreach_attribute(const GeometryComponent &component,
|
||||
const AttributeForeachCallback callback) const final;
|
||||
@@ -398,8 +241,8 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
|
||||
* the #MVert struct, but is exposed as float3 attribute.
|
||||
*/
|
||||
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
|
||||
using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
|
||||
using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
|
||||
using AsReadAttribute = std::unique_ptr<GVArray> (*)(const void *data, const int domain_size);
|
||||
using AsWriteAttribute = std::unique_ptr<GVMutableArray> (*)(void *data, const int domain_size);
|
||||
using UpdateOnRead = void (*)(const GeometryComponent &component);
|
||||
using UpdateOnWrite = void (*)(GeometryComponent &component);
|
||||
const CustomDataType stored_type_;
|
||||
@@ -430,8 +273,8 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
|
||||
{
|
||||
}
|
||||
|
||||
ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final;
|
||||
WriteAttributePtr try_get_for_write(GeometryComponent &component) const final;
|
||||
std::unique_ptr<GVArray> try_get_for_read(const GeometryComponent &component) const final;
|
||||
std::unique_ptr<GVMutableArray> try_get_for_write(GeometryComponent &component) const final;
|
||||
bool try_delete(GeometryComponent &component) const final;
|
||||
bool try_create(GeometryComponent &component) const final;
|
||||
bool exists(const GeometryComponent &component) const final;
|
||||
|
@@ -132,7 +132,7 @@ static void blender_version_init(void)
|
||||
|
||||
BLI_snprintf(blender_version_string,
|
||||
ARRAY_SIZE(blender_version_string),
|
||||
"%d.%02d.%d%s",
|
||||
"%d.%01d.%d%s",
|
||||
BLENDER_VERSION / 100,
|
||||
BLENDER_VERSION % 100,
|
||||
BLENDER_VERSION_PATCH,
|
||||
|
@@ -32,7 +32,7 @@
|
||||
/* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */
|
||||
extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id);
|
||||
|
||||
using blender::bke::ReadAttributePtr;
|
||||
using blender::fn::GVArray;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Geometry Component Implementation
|
||||
@@ -201,14 +201,14 @@ namespace blender::bke {
|
||||
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
|
||||
const TypedReadAttribute<T> &attribute,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totvert);
|
||||
attribute_math::DefaultMixer<T> mixer(r_values);
|
||||
|
||||
for (const int loop_index : IndexRange(mesh.totloop)) {
|
||||
const T value = attribute[loop_index];
|
||||
const T value = old_values[loop_index];
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
const int point_index = loop.v;
|
||||
mixer.mix_in(point_index, value);
|
||||
@@ -216,43 +216,42 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static std::unique_ptr<GVArray> adapt_mesh_domain_corner_to_point(const Mesh &mesh,
|
||||
std::unique_ptr<GVArray> varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
std::unique_ptr<GVArray> new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
/* We compute all interpolated values at once, because for this interpolation, one has to
|
||||
* iterate over all loops anyway. */
|
||||
Array<T> values(mesh.totvert);
|
||||
adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_corner_to_point_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
|
||||
const TypedReadAttribute<T> &attribute,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totloop);
|
||||
|
||||
for (const int loop_index : IndexRange(mesh.totloop)) {
|
||||
const int vertex_index = mesh.mloop[loop_index].v;
|
||||
r_values[loop_index] = attribute[vertex_index];
|
||||
r_values[loop_index] = old_values[vertex_index];
|
||||
}
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static std::unique_ptr<GVArray> adapt_mesh_domain_point_to_corner(const Mesh &mesh,
|
||||
std::unique_ptr<GVArray> varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
std::unique_ptr<GVArray> new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
/* It is not strictly necessary to compute the value for all corners here. Instead one could
|
||||
@@ -260,11 +259,10 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
|
||||
* when an algorithm only accesses very few of the corner values. However, for the algorithms
|
||||
* we currently have, precomputing the array is fine. Also, it is easier to implement. */
|
||||
Array<T> values(mesh.totloop);
|
||||
adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,7 +272,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
|
||||
*/
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
|
||||
Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totpoly);
|
||||
@@ -291,26 +289,25 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_corner_to_face(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static std::unique_ptr<GVArray> adapt_mesh_domain_corner_to_face(const Mesh &mesh,
|
||||
std::unique_ptr<GVArray> varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
std::unique_ptr<GVArray> new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totpoly);
|
||||
adapt_mesh_domain_corner_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
|
||||
Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totedge);
|
||||
@@ -332,26 +329,25 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static std::unique_ptr<GVArray> adapt_mesh_domain_corner_to_edge(const Mesh &mesh,
|
||||
std::unique_ptr<GVArray> varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
std::unique_ptr<GVArray> new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totedge);
|
||||
adapt_mesh_domain_corner_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_corner_to_edge_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
|
||||
Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totvert);
|
||||
@@ -370,26 +366,25 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_face_to_point(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static std::unique_ptr<GVArray> adapt_mesh_domain_face_to_point(const Mesh &mesh,
|
||||
std::unique_ptr<GVArray> varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
std::unique_ptr<GVArray> new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totvert);
|
||||
adapt_mesh_domain_face_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_face_to_point_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totloop);
|
||||
@@ -401,26 +396,25 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
|
||||
}
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_face_to_corner(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static std::unique_ptr<GVArray> adapt_mesh_domain_face_to_corner(const Mesh &mesh,
|
||||
std::unique_ptr<GVArray> varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
std::unique_ptr<GVArray> new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totloop);
|
||||
adapt_mesh_domain_face_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_face_to_corner_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totedge);
|
||||
@@ -437,21 +431,20 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static std::unique_ptr<GVArray> adapt_mesh_domain_face_to_edge(const Mesh &mesh,
|
||||
std::unique_ptr<GVArray> varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
std::unique_ptr<GVArray> new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totedge);
|
||||
adapt_mesh_domain_face_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_face_to_edge_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -461,7 +454,7 @@ static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
|
||||
*/
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totpoly);
|
||||
@@ -478,21 +471,20 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static std::unique_ptr<GVArray> adapt_mesh_domain_point_to_face(const Mesh &mesh,
|
||||
std::unique_ptr<GVArray> varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
std::unique_ptr<GVArray> new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totpoly);
|
||||
adapt_mesh_domain_point_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_point_to_face_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,7 +494,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
|
||||
*/
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totedge);
|
||||
@@ -517,26 +509,25 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_point_to_edge(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static std::unique_ptr<GVArray> adapt_mesh_domain_point_to_edge(const Mesh &mesh,
|
||||
std::unique_ptr<GVArray> varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
std::unique_ptr<GVArray> new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totedge);
|
||||
adapt_mesh_domain_point_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totloop);
|
||||
@@ -558,26 +549,25 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static std::unique_ptr<GVArray> adapt_mesh_domain_edge_to_corner(const Mesh &mesh,
|
||||
std::unique_ptr<GVArray> varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
std::unique_ptr<GVArray> new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totloop);
|
||||
adapt_mesh_domain_edge_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_edge_to_corner_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totvert);
|
||||
@@ -593,21 +583,20 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static std::unique_ptr<GVArray> adapt_mesh_domain_edge_to_point(const Mesh &mesh,
|
||||
std::unique_ptr<GVArray> varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
std::unique_ptr<GVArray> new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totvert);
|
||||
adapt_mesh_domain_edge_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_edge_to_point_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -617,7 +606,7 @@ static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
|
||||
*/
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totpoly);
|
||||
@@ -634,87 +623,87 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_edge_to_face(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static std::unique_ptr<GVArray> adapt_mesh_domain_edge_to_face(const Mesh &mesh,
|
||||
std::unique_ptr<GVArray> varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
std::unique_ptr<GVArray> new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totpoly);
|
||||
adapt_mesh_domain_edge_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute,
|
||||
const AttributeDomain new_domain) const
|
||||
std::unique_ptr<GVArray> MeshComponent::attribute_try_adapt_domain(
|
||||
std::unique_ptr<GVArray> varray,
|
||||
const AttributeDomain from_domain,
|
||||
const AttributeDomain to_domain) const
|
||||
{
|
||||
if (!attribute) {
|
||||
if (!varray) {
|
||||
return {};
|
||||
}
|
||||
if (attribute->size() == 0) {
|
||||
if (varray->size() == 0) {
|
||||
return {};
|
||||
}
|
||||
const AttributeDomain old_domain = attribute->domain();
|
||||
if (old_domain == new_domain) {
|
||||
return attribute;
|
||||
if (from_domain == to_domain) {
|
||||
return varray;
|
||||
}
|
||||
|
||||
switch (old_domain) {
|
||||
switch (from_domain) {
|
||||
case ATTR_DOMAIN_CORNER: {
|
||||
switch (new_domain) {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(varray));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
switch (new_domain) {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(varray));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_FACE: {
|
||||
switch (new_domain) {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(varray));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_EDGE: {
|
||||
switch (new_domain) {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(varray));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -743,25 +732,23 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
template<typename StructT,
|
||||
typename ElemT,
|
||||
ElemT (*GetFunc)(const StructT &),
|
||||
AttributeDomain Domain>
|
||||
static ReadAttributePtr make_derived_read_attribute(const void *data, const int domain_size)
|
||||
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
|
||||
static std::unique_ptr<GVArray> make_derived_read_attribute(const void *data,
|
||||
const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayReadAttribute<StructT, ElemT, GetFunc>>(
|
||||
Domain, Span<StructT>((const StructT *)data, domain_size));
|
||||
return std::make_unique<fn::GVArray_For_DerivedSpan<StructT, ElemT, GetFunc>>(
|
||||
Span<StructT>((const StructT *)data, domain_size));
|
||||
}
|
||||
|
||||
template<typename StructT,
|
||||
typename ElemT,
|
||||
ElemT (*GetFunc)(const StructT &),
|
||||
void (*SetFunc)(StructT &, const ElemT &),
|
||||
AttributeDomain Domain>
|
||||
static WriteAttributePtr make_derived_write_attribute(void *data, const int domain_size)
|
||||
void (*SetFunc)(StructT &, ElemT)>
|
||||
static std::unique_ptr<GVMutableArray> make_derived_write_attribute(void *data,
|
||||
const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayWriteAttribute<StructT, ElemT, GetFunc, SetFunc>>(
|
||||
Domain, MutableSpan<StructT>((StructT *)data, domain_size));
|
||||
return std::make_unique<fn::GVMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(
|
||||
MutableSpan<StructT>((StructT *)data, domain_size));
|
||||
}
|
||||
|
||||
static float3 get_vertex_position(const MVert &vert)
|
||||
@@ -769,7 +756,7 @@ static float3 get_vertex_position(const MVert &vert)
|
||||
return float3(vert.co);
|
||||
}
|
||||
|
||||
static void set_vertex_position(MVert &vert, const float3 &position)
|
||||
static void set_vertex_position(MVert &vert, float3 position)
|
||||
{
|
||||
copy_v3_v3(vert.co, position);
|
||||
}
|
||||
@@ -787,7 +774,7 @@ static int get_material_index(const MPoly &mpoly)
|
||||
return static_cast<int>(mpoly.mat_nr);
|
||||
}
|
||||
|
||||
static void set_material_index(MPoly &mpoly, const int &index)
|
||||
static void set_material_index(MPoly &mpoly, int index)
|
||||
{
|
||||
mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
|
||||
}
|
||||
@@ -797,7 +784,7 @@ static bool get_shade_smooth(const MPoly &mpoly)
|
||||
return mpoly.flag & ME_SMOOTH;
|
||||
}
|
||||
|
||||
static void set_shade_smooth(MPoly &mpoly, const bool &value)
|
||||
static void set_shade_smooth(MPoly &mpoly, bool value)
|
||||
{
|
||||
SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH);
|
||||
}
|
||||
@@ -807,7 +794,7 @@ static float2 get_loop_uv(const MLoopUV &uv)
|
||||
return float2(uv.uv);
|
||||
}
|
||||
|
||||
static void set_loop_uv(MLoopUV &uv, const float2 &co)
|
||||
static void set_loop_uv(MLoopUV &uv, float2 co)
|
||||
{
|
||||
copy_v2_v2(uv.uv, co);
|
||||
}
|
||||
@@ -821,7 +808,7 @@ static Color4f get_loop_color(const MLoopCol &col)
|
||||
return linear_color;
|
||||
}
|
||||
|
||||
static void set_loop_color(MLoopCol &col, const Color4f &linear_color)
|
||||
static void set_loop_color(MLoopCol &col, Color4f linear_color)
|
||||
{
|
||||
linearrgb_to_srgb_uchar4(&col.r, linear_color);
|
||||
}
|
||||
@@ -831,71 +818,62 @@ static float get_crease(const MEdge &edge)
|
||||
return edge.crease / 255.0f;
|
||||
}
|
||||
|
||||
static void set_crease(MEdge &edge, const float &value)
|
||||
static void set_crease(MEdge &edge, float value)
|
||||
{
|
||||
edge.crease = round_fl_to_uchar_clamp(value * 255.0f);
|
||||
}
|
||||
|
||||
class VertexWeightWriteAttribute final : public WriteAttribute {
|
||||
class VMutableArray_For_VertexWeights final : public VMutableArray<float> {
|
||||
private:
|
||||
MDeformVert *dverts_;
|
||||
const int dvert_index_;
|
||||
|
||||
public:
|
||||
VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
|
||||
: WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
|
||||
dverts_(dverts),
|
||||
dvert_index_(dvert_index)
|
||||
VMutableArray_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index)
|
||||
: VMutableArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
float get_impl(const int64_t index) const override
|
||||
{
|
||||
get_internal(dverts_, dvert_index_, index, r_value);
|
||||
return get_internal(dverts_, dvert_index_, index);
|
||||
}
|
||||
|
||||
void set_internal(const int64_t index, const void *value) override
|
||||
void set_impl(const int64_t index, const float value) override
|
||||
{
|
||||
MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
|
||||
weight->weight = *reinterpret_cast<const float *>(value);
|
||||
weight->weight = value;
|
||||
}
|
||||
|
||||
static void get_internal(const MDeformVert *dverts,
|
||||
const int dvert_index,
|
||||
const int64_t index,
|
||||
void *r_value)
|
||||
static float get_internal(const MDeformVert *dverts, const int dvert_index, const int64_t index)
|
||||
{
|
||||
if (dverts == nullptr) {
|
||||
*(float *)r_value = 0.0f;
|
||||
return;
|
||||
return 0.0f;
|
||||
}
|
||||
const MDeformVert &dvert = dverts[index];
|
||||
for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
|
||||
if (weight.def_nr == dvert_index) {
|
||||
*(float *)r_value = weight.weight;
|
||||
return;
|
||||
return weight.weight;
|
||||
}
|
||||
}
|
||||
*(float *)r_value = 0.0f;
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class VertexWeightReadAttribute final : public ReadAttribute {
|
||||
class VArray_For_VertexWeights final : public VArray<float> {
|
||||
private:
|
||||
const MDeformVert *dverts_;
|
||||
const int dvert_index_;
|
||||
|
||||
public:
|
||||
VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
|
||||
: ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
|
||||
dverts_(dverts),
|
||||
dvert_index_(dvert_index)
|
||||
VArray_For_VertexWeights(const MDeformVert *dverts, const int totvert, const int dvert_index)
|
||||
: VArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
float get_impl(const int64_t index) const override
|
||||
{
|
||||
VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
|
||||
return VMutableArray_For_VertexWeights::get_internal(dverts_, dvert_index_, index);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -904,8 +882,8 @@ class VertexWeightReadAttribute final : public ReadAttribute {
|
||||
*/
|
||||
class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
public:
|
||||
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
|
||||
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
|
||||
@@ -917,15 +895,17 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
}
|
||||
if (mesh == nullptr || mesh->dvert == nullptr) {
|
||||
static const float default_value = 0.0f;
|
||||
return std::make_unique<ConstantReadAttribute>(
|
||||
ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value);
|
||||
return {std::make_unique<fn::GVArray_For_SingleValueRef>(
|
||||
CPPType::get<float>(), mesh->totvert, &default_value),
|
||||
ATTR_DOMAIN_POINT};
|
||||
}
|
||||
return std::make_unique<VertexWeightReadAttribute>(
|
||||
mesh->dvert, mesh->totvert, vertex_group_index);
|
||||
return {std::make_unique<fn::GVArray_For_EmbeddedVArray<float, VArray_For_VertexWeights>>(
|
||||
mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
|
||||
ATTR_DOMAIN_POINT};
|
||||
}
|
||||
|
||||
WriteAttributePtr try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
|
||||
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
|
||||
@@ -946,8 +926,11 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
|
||||
&mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
|
||||
}
|
||||
return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
|
||||
mesh->dvert, mesh->totvert, vertex_group_index);
|
||||
return {
|
||||
std::make_unique<
|
||||
fn::GVMutableArray_For_EmbeddedVMutableArray<float, VMutableArray_For_VertexWeights>>(
|
||||
mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
|
||||
ATTR_DOMAIN_POINT};
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
|
||||
@@ -1009,7 +992,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
{
|
||||
}
|
||||
|
||||
ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final
|
||||
std::unique_ptr<GVArray> try_get_for_read(const GeometryComponent &component) const final
|
||||
{
|
||||
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
|
||||
const Mesh *mesh = mesh_component.get_for_read();
|
||||
@@ -1022,8 +1005,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
|
||||
const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
|
||||
|
||||
return std::make_unique<ArrayReadAttribute<float3>>(
|
||||
ATTR_DOMAIN_FACE, Span<float3>((const float3 *)data, mesh->totpoly));
|
||||
return std::make_unique<fn::GVArray_For_Span<float3>>(
|
||||
Span<float3>((const float3 *)data, mesh->totpoly));
|
||||
}
|
||||
|
||||
Array<float3> normals(mesh->totpoly);
|
||||
@@ -1032,10 +1015,11 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]);
|
||||
}
|
||||
|
||||
return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_FACE, std::move(normals));
|
||||
return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
|
||||
}
|
||||
|
||||
WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
|
||||
std::unique_ptr<GVMutableArray> try_get_for_write(
|
||||
GeometryComponent &UNUSED(component)) const final
|
||||
{
|
||||
return {};
|
||||
}
|
||||
@@ -1105,12 +1089,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
point_access,
|
||||
make_derived_read_attribute<MVert, float3, get_vertex_position, ATTR_DOMAIN_POINT>,
|
||||
make_derived_write_attribute<MVert,
|
||||
float3,
|
||||
get_vertex_position,
|
||||
set_vertex_position,
|
||||
ATTR_DOMAIN_POINT>,
|
||||
make_derived_read_attribute<MVert, float3, get_vertex_position>,
|
||||
make_derived_write_attribute<MVert, float3, get_vertex_position, set_vertex_position>,
|
||||
tag_normals_dirty_when_writing_position);
|
||||
|
||||
static NormalAttributeProvider normal;
|
||||
@@ -1124,12 +1104,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
face_access,
|
||||
make_derived_read_attribute<MPoly, int, get_material_index, ATTR_DOMAIN_FACE>,
|
||||
make_derived_write_attribute<MPoly,
|
||||
int,
|
||||
get_material_index,
|
||||
set_material_index,
|
||||
ATTR_DOMAIN_FACE>,
|
||||
make_derived_read_attribute<MPoly, int, get_material_index>,
|
||||
make_derived_write_attribute<MPoly, int, get_material_index, set_material_index>,
|
||||
nullptr);
|
||||
|
||||
static BuiltinCustomDataLayerProvider shade_smooth(
|
||||
@@ -1141,12 +1117,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
face_access,
|
||||
make_derived_read_attribute<MPoly, bool, get_shade_smooth, ATTR_DOMAIN_FACE>,
|
||||
make_derived_write_attribute<MPoly,
|
||||
bool,
|
||||
get_shade_smooth,
|
||||
set_shade_smooth,
|
||||
ATTR_DOMAIN_FACE>,
|
||||
make_derived_read_attribute<MPoly, bool, get_shade_smooth>,
|
||||
make_derived_write_attribute<MPoly, bool, get_shade_smooth, set_shade_smooth>,
|
||||
nullptr);
|
||||
|
||||
static BuiltinCustomDataLayerProvider crease(
|
||||
@@ -1158,8 +1130,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
edge_access,
|
||||
make_derived_read_attribute<MEdge, float, get_crease, ATTR_DOMAIN_EDGE>,
|
||||
make_derived_write_attribute<MEdge, float, get_crease, set_crease, ATTR_DOMAIN_EDGE>,
|
||||
make_derived_read_attribute<MEdge, float, get_crease>,
|
||||
make_derived_write_attribute<MEdge, float, get_crease, set_crease>,
|
||||
nullptr);
|
||||
|
||||
static NamedLegacyCustomDataProvider uvs(
|
||||
@@ -1167,20 +1139,16 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
CD_PROP_FLOAT2,
|
||||
CD_MLOOPUV,
|
||||
corner_access,
|
||||
make_derived_read_attribute<MLoopUV, float2, get_loop_uv, ATTR_DOMAIN_CORNER>,
|
||||
make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv, ATTR_DOMAIN_CORNER>);
|
||||
make_derived_read_attribute<MLoopUV, float2, get_loop_uv>,
|
||||
make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv>);
|
||||
|
||||
static NamedLegacyCustomDataProvider vertex_colors(
|
||||
ATTR_DOMAIN_CORNER,
|
||||
CD_PROP_COLOR,
|
||||
CD_MLOOPCOL,
|
||||
corner_access,
|
||||
make_derived_read_attribute<MLoopCol, Color4f, get_loop_color, ATTR_DOMAIN_CORNER>,
|
||||
make_derived_write_attribute<MLoopCol,
|
||||
Color4f,
|
||||
get_loop_color,
|
||||
set_loop_color,
|
||||
ATTR_DOMAIN_CORNER>);
|
||||
make_derived_read_attribute<MLoopCol, Color4f, get_loop_color>,
|
||||
make_derived_write_attribute<MLoopCol, Color4f, get_loop_color, set_loop_color>);
|
||||
|
||||
static VertexGroupsAttributeProvider vertex_groups;
|
||||
static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
|
||||
|
@@ -140,16 +140,18 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
template<typename T, AttributeDomain Domain>
|
||||
static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size)
|
||||
template<typename T>
|
||||
static std::unique_ptr<GVArray> make_array_read_attribute(const void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size));
|
||||
return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size));
|
||||
}
|
||||
|
||||
template<typename T, AttributeDomain Domain>
|
||||
static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size)
|
||||
template<typename T>
|
||||
static std::unique_ptr<GVMutableArray> make_array_write_attribute(void *data,
|
||||
const int domain_size)
|
||||
{
|
||||
return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size));
|
||||
return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
|
||||
MutableSpan<T>((T *)data, domain_size));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,30 +181,28 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
|
||||
},
|
||||
update_custom_data_pointers};
|
||||
|
||||
static BuiltinCustomDataLayerProvider position(
|
||||
"position",
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_FLOAT3,
|
||||
CD_PROP_FLOAT3,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
point_access,
|
||||
make_array_read_attribute<float3, ATTR_DOMAIN_POINT>,
|
||||
make_array_write_attribute<float3, ATTR_DOMAIN_POINT>,
|
||||
nullptr);
|
||||
static BuiltinCustomDataLayerProvider radius(
|
||||
"radius",
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_FLOAT,
|
||||
CD_PROP_FLOAT,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
make_array_read_attribute<float, ATTR_DOMAIN_POINT>,
|
||||
make_array_write_attribute<float, ATTR_DOMAIN_POINT>,
|
||||
nullptr);
|
||||
static BuiltinCustomDataLayerProvider position("position",
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_FLOAT3,
|
||||
CD_PROP_FLOAT3,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
point_access,
|
||||
make_array_read_attribute<float3>,
|
||||
make_array_write_attribute<float3>,
|
||||
nullptr);
|
||||
static BuiltinCustomDataLayerProvider radius("radius",
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_FLOAT,
|
||||
CD_PROP_FLOAT,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
make_array_read_attribute<float>,
|
||||
make_array_write_attribute<float>,
|
||||
nullptr);
|
||||
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
|
||||
return ComponentAttributeProviders({&position, &radius}, {&point_custom_data});
|
||||
}
|
||||
|
@@ -36,30 +36,42 @@ static void geometry_set_collect_recursive_collection(const Collection &collecti
|
||||
const float4x4 &transform,
|
||||
Vector<GeometryInstanceGroup> &r_sets);
|
||||
|
||||
static void add_final_mesh_as_geometry_component(const Object &object, GeometrySet &geometry_set)
|
||||
{
|
||||
Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(&const_cast<Object &>(object),
|
||||
false);
|
||||
|
||||
if (mesh != nullptr) {
|
||||
BKE_mesh_wrapper_ensure_mdata(mesh);
|
||||
|
||||
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
|
||||
mesh_component.copy_vertex_group_names_from_object(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances.
|
||||
*/
|
||||
static GeometrySet object_get_geometry_set_for_read(const Object &object)
|
||||
{
|
||||
/* Objects evaluated with a nodes modifier will have a geometry set already. */
|
||||
if (object.type == OB_MESH && object.mode == OB_MODE_EDIT) {
|
||||
GeometrySet geometry_set;
|
||||
if (object.runtime.geometry_set_eval != nullptr) {
|
||||
/* `geometry_set_eval` only contains non-mesh components, see `editbmesh_build_data`. */
|
||||
geometry_set = *object.runtime.geometry_set_eval;
|
||||
}
|
||||
add_final_mesh_as_geometry_component(object, geometry_set);
|
||||
return geometry_set;
|
||||
}
|
||||
if (object.runtime.geometry_set_eval != nullptr) {
|
||||
return *object.runtime.geometry_set_eval;
|
||||
}
|
||||
|
||||
/* Otherwise, construct a new geometry set with the component based on the object type. */
|
||||
GeometrySet new_geometry_set;
|
||||
|
||||
GeometrySet geometry_set;
|
||||
if (object.type == OB_MESH) {
|
||||
Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(
|
||||
&const_cast<Object &>(object), false);
|
||||
|
||||
if (mesh != nullptr) {
|
||||
BKE_mesh_wrapper_ensure_mdata(mesh);
|
||||
|
||||
MeshComponent &mesh_component = new_geometry_set.get_component_for_write<MeshComponent>();
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
|
||||
mesh_component.copy_vertex_group_names_from_object(object);
|
||||
}
|
||||
add_final_mesh_as_geometry_component(object, geometry_set);
|
||||
}
|
||||
|
||||
/* TODO: Cover the case of point-clouds without modifiers-- they may not be covered by the
|
||||
@@ -68,7 +80,7 @@ static GeometrySet object_get_geometry_set_for_read(const Object &object)
|
||||
/* TODO: Add volume support. */
|
||||
|
||||
/* Return by value since there is not always an existing geometry set owned elsewhere to use. */
|
||||
return new_geometry_set;
|
||||
return geometry_set;
|
||||
}
|
||||
|
||||
static void geometry_set_collect_recursive_collection_instance(
|
||||
@@ -438,12 +450,13 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
|
||||
result.attribute_try_create(entry.key, domain_output, data_type_output);
|
||||
WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name);
|
||||
if (!write_attribute || &write_attribute->cpp_type() != cpp_type ||
|
||||
write_attribute->domain() != domain_output) {
|
||||
WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name);
|
||||
if (!write_attribute || &write_attribute.varray->type() != cpp_type ||
|
||||
write_attribute.domain != domain_output) {
|
||||
continue;
|
||||
}
|
||||
fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
|
||||
|
||||
fn::GVMutableArray_GSpan dst_span{*write_attribute.varray};
|
||||
|
||||
int offset = 0;
|
||||
for (const GeometryInstanceGroup &set_group : set_groups) {
|
||||
@@ -455,11 +468,11 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
|
||||
if (domain_size == 0) {
|
||||
continue; /* Domain size is 0, so no need to increment the offset. */
|
||||
}
|
||||
ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
|
||||
std::unique_ptr<GVArray> source_attribute = component.attribute_try_get_for_read(
|
||||
name, domain_output, data_type_output);
|
||||
|
||||
if (source_attribute) {
|
||||
fn::GSpan src_span = source_attribute->get_span();
|
||||
fn::GVArray_GSpan src_span{*source_attribute};
|
||||
const void *src_buffer = src_span.data();
|
||||
for (const int UNUSED(i) : set_group.transforms.index_range()) {
|
||||
void *dst_buffer = dst_span[offset];
|
||||
@@ -474,7 +487,7 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
|
||||
}
|
||||
}
|
||||
|
||||
write_attribute->apply_span();
|
||||
dst_span.save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4135,7 +4135,7 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph,
|
||||
const bool use_hidden)
|
||||
{
|
||||
bool ok = false;
|
||||
if ((ob->transflag & OB_DUPLI) == 0) {
|
||||
if ((ob->transflag & OB_DUPLI) == 0 && ob->runtime.geometry_set_eval == NULL) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
@@ -176,6 +176,13 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
|
||||
|
||||
CustomData_MeshMasks cddata_masks = scene->customdata_mask;
|
||||
CustomData_MeshMasks_update(&cddata_masks, &CD_MASK_BAREMESH);
|
||||
/* Custom attributes should not be removed automatically. They might be used by the render
|
||||
* engine or scripts. They can still be removed explicitly using geometry nodes. */
|
||||
cddata_masks.vmask |= CD_MASK_PROP_ALL;
|
||||
cddata_masks.emask |= CD_MASK_PROP_ALL;
|
||||
cddata_masks.fmask |= CD_MASK_PROP_ALL;
|
||||
cddata_masks.pmask |= CD_MASK_PROP_ALL;
|
||||
cddata_masks.lmask |= CD_MASK_PROP_ALL;
|
||||
/* Make sure Freestyle edge/face marks appear in DM for render (see T40315).
|
||||
* Due to Line Art implementation, edge marks should also be shown in viewport. */
|
||||
#ifdef WITH_FREESTYLE
|
||||
|
@@ -94,7 +94,7 @@ template<typename T> class Span {
|
||||
using iterator = const T *;
|
||||
using size_type = int64_t;
|
||||
|
||||
private:
|
||||
protected:
|
||||
const T *data_ = nullptr;
|
||||
int64_t size_ = 0;
|
||||
|
||||
@@ -477,7 +477,7 @@ template<typename T> class MutableSpan {
|
||||
using iterator = T *;
|
||||
using size_type = int64_t;
|
||||
|
||||
private:
|
||||
protected:
|
||||
T *data_;
|
||||
int64_t size_;
|
||||
|
||||
|
@@ -37,6 +37,7 @@
|
||||
* see of the increased compile time and binary size is worth it.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender {
|
||||
@@ -71,6 +72,11 @@ template<typename T> class VArray {
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return IndexRange(size_);
|
||||
}
|
||||
|
||||
/* Returns true when the virtual array is stored as a span internally. */
|
||||
bool is_span() const
|
||||
{
|
||||
@@ -82,13 +88,13 @@ template<typename T> class VArray {
|
||||
|
||||
/* Returns the internally used span of the virtual array. This invokes undefined behavior is the
|
||||
* virtual array is not stored as a span internally. */
|
||||
Span<T> get_span() const
|
||||
Span<T> get_internal_span() const
|
||||
{
|
||||
BLI_assert(this->is_span());
|
||||
if (size_ == 0) {
|
||||
return {};
|
||||
}
|
||||
return this->get_span_impl();
|
||||
return this->get_internal_span_impl();
|
||||
}
|
||||
|
||||
/* Returns true when the virtual array returns the same value for every index. */
|
||||
@@ -102,20 +108,35 @@ template<typename T> class VArray {
|
||||
|
||||
/* Returns the value that is returned for every index. This invokes undefined behavior if the
|
||||
* virtual array would not return the same value for every index. */
|
||||
T get_single() const
|
||||
T get_internal_single() const
|
||||
{
|
||||
BLI_assert(this->is_single());
|
||||
if (size_ == 1) {
|
||||
return this->get(0);
|
||||
}
|
||||
return this->get_single_impl();
|
||||
return this->get_internal_single_impl();
|
||||
}
|
||||
|
||||
/* Get the element at a specific index. Note that this operator cannot be used to assign values
|
||||
* to an index, because the return value is not a reference. */
|
||||
T operator[](const int64_t index) const
|
||||
{
|
||||
return this->get(index);
|
||||
}
|
||||
|
||||
/* Copy the entire virtual array into a span. */
|
||||
void materialize(MutableSpan<T> r_span) const
|
||||
{
|
||||
BLI_assert(size_ == r_span.size());
|
||||
this->materialize_impl(r_span);
|
||||
}
|
||||
|
||||
void materialize_to_uninitialized(MutableSpan<T> r_span) const
|
||||
{
|
||||
BLI_assert(size_ == r_span.size());
|
||||
this->materialize_to_uninitialized_impl(r_span);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual T get_impl(const int64_t index) const = 0;
|
||||
|
||||
@@ -124,7 +145,7 @@ template<typename T> class VArray {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual Span<T> get_span_impl() const
|
||||
virtual Span<T> get_internal_span_impl() const
|
||||
{
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
@@ -135,56 +156,198 @@ template<typename T> class VArray {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual T get_single_impl() const
|
||||
virtual T get_internal_single_impl() const
|
||||
{
|
||||
/* Provide a default implementation, so that subclasses don't have to provide it. This method
|
||||
* should never be called because `is_single_impl` returns false by default. */
|
||||
BLI_assert_unreachable();
|
||||
return T();
|
||||
}
|
||||
|
||||
virtual void materialize_impl(MutableSpan<T> r_span) const
|
||||
{
|
||||
if (this->is_span()) {
|
||||
const Span<T> span = this->get_internal_span();
|
||||
initialized_copy_n(span.data(), size_, r_span.data());
|
||||
}
|
||||
else if (this->is_single()) {
|
||||
const T single = this->get_internal_single();
|
||||
initialized_fill_n(r_span.data(), size_, single);
|
||||
}
|
||||
else {
|
||||
const int64_t size = size_;
|
||||
for (int64_t i = 0; i < size; i++) {
|
||||
r_span[i] = this->get(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void materialize_to_uninitialized_impl(MutableSpan<T> r_span) const
|
||||
{
|
||||
if (this->is_span()) {
|
||||
const Span<T> span = this->get_internal_span();
|
||||
uninitialized_copy_n(span.data(), size_, r_span.data());
|
||||
}
|
||||
else if (this->is_single()) {
|
||||
const T single = this->get_internal_single();
|
||||
uninitialized_fill_n(r_span.data(), size_, single);
|
||||
}
|
||||
else {
|
||||
const int64_t size = size_;
|
||||
T *dst = r_span.data();
|
||||
for (int64_t i = 0; i < size; i++) {
|
||||
new (dst + i) T(this->get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Similar to VArray, but the elements are mutable. */
|
||||
template<typename T> class VMutableArray : public VArray<T> {
|
||||
public:
|
||||
VMutableArray(const int64_t size) : VArray<T>(size)
|
||||
{
|
||||
}
|
||||
|
||||
void set(const int64_t index, T value)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < this->size_);
|
||||
this->set_impl(index, std::move(value));
|
||||
}
|
||||
|
||||
/* Copy the values from the source span to all elements in the virtual array. */
|
||||
void set_all(Span<T> src)
|
||||
{
|
||||
BLI_assert(src.size() == this->size_);
|
||||
this->set_all_impl(src);
|
||||
}
|
||||
|
||||
MutableSpan<T> get_internal_span()
|
||||
{
|
||||
BLI_assert(this->is_span());
|
||||
Span<T> span = static_cast<const VArray<T> *>(this)->get_internal_span();
|
||||
return MutableSpan<T>(const_cast<T *>(span.data()), span.size());
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void set_impl(const int64_t index, T value) = 0;
|
||||
|
||||
virtual void set_all_impl(Span<T> src)
|
||||
{
|
||||
if (this->is_span()) {
|
||||
const MutableSpan<T> span = this->get_internal_span();
|
||||
initialized_copy_n(src.data(), this->size_, span.data());
|
||||
}
|
||||
else {
|
||||
const int64_t size = this->size_;
|
||||
for (int64_t i = 0; i < size; i++) {
|
||||
this->set(i, src[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A virtual array implementation for a span. This class is final so that it can be devirtualized
|
||||
* by the compiler in some cases (e.g. when #devirtualize_varray is used).
|
||||
* A virtual array implementation for a span. Methods in this class are final so that it can be
|
||||
* devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is used).
|
||||
*/
|
||||
template<typename T> class VArrayForSpan final : public VArray<T> {
|
||||
private:
|
||||
const T *data_;
|
||||
template<typename T> class VArray_For_Span : public VArray<T> {
|
||||
protected:
|
||||
const T *data_ = nullptr;
|
||||
|
||||
public:
|
||||
VArrayForSpan(const Span<T> data) : VArray<T>(data.size()), data_(data.data())
|
||||
VArray_For_Span(const Span<T> data) : VArray<T>(data.size()), data_(data.data())
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
T get_impl(const int64_t index) const override
|
||||
VArray_For_Span(const int64_t size) : VArray<T>(size)
|
||||
{
|
||||
}
|
||||
|
||||
T get_impl(const int64_t index) const final
|
||||
{
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
bool is_span_impl() const final
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Span<T> get_internal_span_impl() const final
|
||||
{
|
||||
return Span<T>(data_, this->size_);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class VMutableArray_For_MutableSpan : public VMutableArray<T> {
|
||||
protected:
|
||||
T *data_ = nullptr;
|
||||
|
||||
public:
|
||||
VMutableArray_For_MutableSpan(const MutableSpan<T> data)
|
||||
: VMutableArray<T>(data.size()), data_(data.data())
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
VMutableArray_For_MutableSpan(const int64_t size) : VMutableArray<T>(size)
|
||||
{
|
||||
}
|
||||
|
||||
T get_impl(const int64_t index) const final
|
||||
{
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
void set_impl(const int64_t index, T value) final
|
||||
{
|
||||
data_[index] = value;
|
||||
}
|
||||
|
||||
bool is_span_impl() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Span<T> get_span_impl() const override
|
||||
Span<T> get_internal_span_impl() const override
|
||||
{
|
||||
return Span<T>(data_, this->size_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A variant of `VArray_For_Span` that owns the underlying data.
|
||||
* The `Container` type has to implement a `size()` and `data()` method.
|
||||
* The `data()` method has to return a pointer to the first element in the continuous array of
|
||||
* elements.
|
||||
*/
|
||||
template<typename Container, typename T = typename Container::value_type>
|
||||
class VArray_For_ArrayContainer : public VArray_For_Span<T> {
|
||||
private:
|
||||
Container container_;
|
||||
|
||||
public:
|
||||
VArray_For_ArrayContainer(Container container)
|
||||
: VArray_For_Span<T>((int64_t)container.size()), container_(std::move(container))
|
||||
{
|
||||
this->data_ = container_.data();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A virtual array implementation that returns the same value for every index. This class is final
|
||||
* so that it can be devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is
|
||||
* used).
|
||||
*/
|
||||
template<typename T> class VArrayForSingle final : public VArray<T> {
|
||||
template<typename T> class VArray_For_Single final : public VArray<T> {
|
||||
private:
|
||||
T value_;
|
||||
|
||||
public:
|
||||
VArrayForSingle(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
|
||||
VArray_For_Single(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -199,7 +362,7 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
|
||||
return this->size_ == 1;
|
||||
}
|
||||
|
||||
Span<T> get_span_impl() const override
|
||||
Span<T> get_internal_span_impl() const override
|
||||
{
|
||||
return Span<T>(&value_, 1);
|
||||
}
|
||||
@@ -209,12 +372,170 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
T get_single_impl() const override
|
||||
T get_internal_single_impl() const override
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* In many cases a virtual array is a span internally. In those cases, access to individual could
|
||||
* be much more efficient than calling a virtual method. When the underlying virtual array is not a
|
||||
* span, this class allocates a new array and copies the values over.
|
||||
*
|
||||
* This should be used in those cases:
|
||||
* - All elements in the virtual array are accessed multiple times.
|
||||
* - In most cases, the underlying virtual array is a span, so no copy is necessary to benefit
|
||||
* from faster access.
|
||||
* - An API is called, that does not accept virtual arrays, but only spans.
|
||||
*/
|
||||
template<typename T> class VArray_Span final : public Span<T> {
|
||||
private:
|
||||
const VArray<T> &varray_;
|
||||
Array<T> owned_data_;
|
||||
|
||||
public:
|
||||
VArray_Span(const VArray<T> &varray) : Span<T>(), varray_(varray)
|
||||
{
|
||||
this->size_ = varray_.size();
|
||||
if (varray_.is_span()) {
|
||||
this->data_ = varray_.get_internal_span().data();
|
||||
}
|
||||
else {
|
||||
owned_data_.~Array();
|
||||
new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
|
||||
varray_.materialize_to_uninitialized(owned_data_);
|
||||
this->data_ = owned_data_.data();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Same as VArray_Span, but for a mutable span.
|
||||
* The important thing to note is that when changing this span, the results might not be
|
||||
* immediately reflected in the underlying virtual array (only when the virtual array is a span
|
||||
* internally). The #save method can be used to write all changes to the underlying virtual array,
|
||||
* if necessary.
|
||||
*/
|
||||
template<typename T> class VMutableArray_Span final : public MutableSpan<T> {
|
||||
private:
|
||||
VMutableArray<T> &varray_;
|
||||
Array<T> owned_data_;
|
||||
bool save_has_been_called_ = false;
|
||||
bool show_not_saved_warning_ = true;
|
||||
|
||||
public:
|
||||
/* Create a span for any virtual array. This is cheap when the virtual array is a span itself. If
|
||||
* not, a new array has to be allocated as a wrapper for the underlying virtual array. */
|
||||
VMutableArray_Span(VMutableArray<T> &varray, const bool copy_values_to_span = true)
|
||||
: MutableSpan<T>(), varray_(varray)
|
||||
{
|
||||
this->size_ = varray_.size();
|
||||
if (varray_.is_span()) {
|
||||
this->data_ = varray_.get_internal_span().data();
|
||||
}
|
||||
else {
|
||||
if (copy_values_to_span) {
|
||||
owned_data_.~Array();
|
||||
new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
|
||||
varray_.materialize_to_uninitialized(owned_data_);
|
||||
}
|
||||
else {
|
||||
owned_data_.reinitialize(varray_.size());
|
||||
}
|
||||
this->data_ = owned_data_.data();
|
||||
}
|
||||
}
|
||||
|
||||
~VMutableArray_Span()
|
||||
{
|
||||
if (show_not_saved_warning_) {
|
||||
if (!save_has_been_called_) {
|
||||
std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write back all values from a temporary allocated array to the underlying virtual array. */
|
||||
void save()
|
||||
{
|
||||
save_has_been_called_ = true;
|
||||
if (this->data_ != owned_data_.data()) {
|
||||
return;
|
||||
}
|
||||
varray_.set_all(owned_data_);
|
||||
}
|
||||
|
||||
void disable_not_applied_warning()
|
||||
{
|
||||
show_not_saved_warning_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This class makes it easy to create a virtual array for an existing function or lambda. The
|
||||
* `GetFunc` should take a single `index` argument and return the value at that index.
|
||||
*/
|
||||
template<typename T, typename GetFunc> class VArray_For_Func final : public VArray<T> {
|
||||
private:
|
||||
GetFunc get_func_;
|
||||
|
||||
public:
|
||||
VArray_For_Func(const int64_t size, GetFunc get_func)
|
||||
: VArray<T>(size), get_func_(std::move(get_func))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
T get_impl(const int64_t index) const override
|
||||
{
|
||||
return get_func_(index);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
|
||||
class VArray_For_DerivedSpan : public VArray<ElemT> {
|
||||
private:
|
||||
const StructT *data_;
|
||||
|
||||
public:
|
||||
VArray_For_DerivedSpan(const Span<StructT> data) : VArray<ElemT>(data.size()), data_(data.data())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
ElemT get_impl(const int64_t index) const override
|
||||
{
|
||||
return GetFunc(data_[index]);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StructT,
|
||||
typename ElemT,
|
||||
ElemT (*GetFunc)(const StructT &),
|
||||
void (*SetFunc)(StructT &, ElemT)>
|
||||
class VMutableArray_For_DerivedSpan : public VMutableArray<ElemT> {
|
||||
private:
|
||||
StructT *data_;
|
||||
|
||||
public:
|
||||
VMutableArray_For_DerivedSpan(const MutableSpan<StructT> data)
|
||||
: VMutableArray<ElemT>(data.size()), data_(data.data())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
ElemT get_impl(const int64_t index) const override
|
||||
{
|
||||
return GetFunc(data_[index]);
|
||||
}
|
||||
|
||||
void set_impl(const int64_t index, ElemT value) override
|
||||
{
|
||||
SetFunc(data_[index], std::move(value));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate multiple versions of the given function optimized for different virtual arrays.
|
||||
* One has to be careful with nesting multiple devirtualizations, because that results in an
|
||||
@@ -229,14 +550,14 @@ inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool
|
||||
/* Support disabling the devirtualization to simplify benchmarking. */
|
||||
if (enable) {
|
||||
if (varray.is_single()) {
|
||||
/* `VArrayForSingle` can be used for devirtualization, because it is declared `final`. */
|
||||
const VArrayForSingle<T> varray_single{varray.get_single(), varray.size()};
|
||||
/* `VArray_For_Single` can be used for devirtualization, because it is declared `final`. */
|
||||
const VArray_For_Single<T> varray_single{varray.get_internal_single(), varray.size()};
|
||||
func(varray_single);
|
||||
return;
|
||||
}
|
||||
if (varray.is_span()) {
|
||||
/* `VArrayForSpan` can be used for devirtualization, because it is declared `final`. */
|
||||
const VArrayForSpan<T> varray_span{varray.get_span()};
|
||||
/* `VArray_For_Span` can be used for devirtualization, because it is declared `final`. */
|
||||
const VArray_For_Span<T> varray_span{varray.get_internal_span()};
|
||||
func(varray_span);
|
||||
return;
|
||||
}
|
||||
@@ -262,26 +583,26 @@ inline void devirtualize_varray2(const VArray<T1> &varray1,
|
||||
const bool is_single1 = varray1.is_single();
|
||||
const bool is_single2 = varray2.is_single();
|
||||
if (is_span1 && is_span2) {
|
||||
const VArrayForSpan<T1> varray1_span{varray1.get_span()};
|
||||
const VArrayForSpan<T2> varray2_span{varray2.get_span()};
|
||||
const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()};
|
||||
const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()};
|
||||
func(varray1_span, varray2_span);
|
||||
return;
|
||||
}
|
||||
if (is_span1 && is_single2) {
|
||||
const VArrayForSpan<T1> varray1_span{varray1.get_span()};
|
||||
const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
|
||||
const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()};
|
||||
const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()};
|
||||
func(varray1_span, varray2_single);
|
||||
return;
|
||||
}
|
||||
if (is_single1 && is_span2) {
|
||||
const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
|
||||
const VArrayForSpan<T2> varray2_span{varray2.get_span()};
|
||||
const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()};
|
||||
const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()};
|
||||
func(varray1_single, varray2_span);
|
||||
return;
|
||||
}
|
||||
if (is_single1 && is_single2) {
|
||||
const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
|
||||
const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
|
||||
const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()};
|
||||
const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()};
|
||||
func(varray1_single, varray2_single);
|
||||
return;
|
||||
}
|
||||
|
@@ -1,26 +1,29 @@
|
||||
/* Apache License, Version 2.0 */
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_strict_flags.h"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace blender::tests {
|
||||
|
||||
TEST(virtual_array, ForSpan)
|
||||
TEST(virtual_array, Span)
|
||||
{
|
||||
std::array<int, 5> data = {3, 4, 5, 6, 7};
|
||||
VArrayForSpan<int> varray{data};
|
||||
VArray_For_Span<int> varray{data};
|
||||
EXPECT_EQ(varray.size(), 5);
|
||||
EXPECT_EQ(varray.get(0), 3);
|
||||
EXPECT_EQ(varray.get(4), 7);
|
||||
EXPECT_TRUE(varray.is_span());
|
||||
EXPECT_FALSE(varray.is_single());
|
||||
EXPECT_EQ(varray.get_span().data(), data.data());
|
||||
EXPECT_EQ(varray.get_internal_span().data(), data.data());
|
||||
}
|
||||
|
||||
TEST(virtual_array, ForSingle)
|
||||
TEST(virtual_array, Single)
|
||||
{
|
||||
VArrayForSingle<int> varray{10, 4};
|
||||
VArray_For_Single<int> varray{10, 4};
|
||||
EXPECT_EQ(varray.size(), 4);
|
||||
EXPECT_EQ(varray.get(0), 10);
|
||||
EXPECT_EQ(varray.get(3), 10);
|
||||
@@ -28,4 +31,124 @@ TEST(virtual_array, ForSingle)
|
||||
EXPECT_TRUE(varray.is_single());
|
||||
}
|
||||
|
||||
TEST(virtual_array, Array)
|
||||
{
|
||||
Array<int> array = {1, 2, 3, 5, 8};
|
||||
{
|
||||
VArray_For_ArrayContainer varray{array};
|
||||
EXPECT_EQ(varray.size(), 5);
|
||||
EXPECT_EQ(varray[0], 1);
|
||||
EXPECT_EQ(varray[2], 3);
|
||||
EXPECT_EQ(varray[3], 5);
|
||||
EXPECT_TRUE(varray.is_span());
|
||||
}
|
||||
{
|
||||
VArray_For_ArrayContainer varray{std::move(array)};
|
||||
EXPECT_EQ(varray.size(), 5);
|
||||
EXPECT_EQ(varray[0], 1);
|
||||
EXPECT_EQ(varray[2], 3);
|
||||
EXPECT_EQ(varray[3], 5);
|
||||
EXPECT_TRUE(varray.is_span());
|
||||
}
|
||||
{
|
||||
VArray_For_ArrayContainer varray{array}; /* NOLINT: bugprone-use-after-move */
|
||||
EXPECT_TRUE(varray.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(virtual_array, Vector)
|
||||
{
|
||||
Vector<int> vector = {9, 8, 7, 6};
|
||||
VArray_For_ArrayContainer varray{std::move(vector)};
|
||||
EXPECT_EQ(varray.size(), 4);
|
||||
EXPECT_EQ(varray[0], 9);
|
||||
EXPECT_EQ(varray[3], 6);
|
||||
}
|
||||
|
||||
TEST(virtual_array, StdVector)
|
||||
{
|
||||
std::vector<int> vector = {5, 6, 7, 8};
|
||||
VArray_For_ArrayContainer varray{std::move(vector)};
|
||||
EXPECT_EQ(varray.size(), 4);
|
||||
EXPECT_EQ(varray[0], 5);
|
||||
EXPECT_EQ(varray[1], 6);
|
||||
}
|
||||
|
||||
TEST(virtual_array, StdArray)
|
||||
{
|
||||
std::array<int, 4> array = {2, 3, 4, 5};
|
||||
VArray_For_ArrayContainer varray{array};
|
||||
EXPECT_EQ(varray.size(), 4);
|
||||
EXPECT_EQ(varray[0], 2);
|
||||
EXPECT_EQ(varray[1], 3);
|
||||
}
|
||||
|
||||
TEST(virtual_array, VectorSet)
|
||||
{
|
||||
VectorSet<int> vector_set = {5, 3, 7, 3, 3, 5, 1};
|
||||
VArray_For_ArrayContainer varray{std::move(vector_set)};
|
||||
EXPECT_TRUE(vector_set.is_empty()); /* NOLINT: bugprone-use-after-move. */
|
||||
EXPECT_EQ(varray.size(), 4);
|
||||
EXPECT_EQ(varray[0], 5);
|
||||
EXPECT_EQ(varray[1], 3);
|
||||
EXPECT_EQ(varray[2], 7);
|
||||
EXPECT_EQ(varray[3], 1);
|
||||
}
|
||||
|
||||
TEST(virtual_array, Func)
|
||||
{
|
||||
auto func = [](int64_t index) { return (int)(index * index); };
|
||||
VArray_For_Func<int, decltype(func)> varray{10, func};
|
||||
EXPECT_EQ(varray.size(), 10);
|
||||
EXPECT_EQ(varray[0], 0);
|
||||
EXPECT_EQ(varray[3], 9);
|
||||
EXPECT_EQ(varray[9], 81);
|
||||
}
|
||||
|
||||
TEST(virtual_array, AsSpan)
|
||||
{
|
||||
auto func = [](int64_t index) { return (int)(10 * index); };
|
||||
VArray_For_Func<int, decltype(func)> func_varray{10, func};
|
||||
VArray_Span span_varray{func_varray};
|
||||
EXPECT_EQ(span_varray.size(), 10);
|
||||
Span<int> span = span_varray;
|
||||
EXPECT_EQ(span.size(), 10);
|
||||
EXPECT_EQ(span[0], 0);
|
||||
EXPECT_EQ(span[3], 30);
|
||||
EXPECT_EQ(span[6], 60);
|
||||
}
|
||||
|
||||
static int get_x(const std::array<int, 3> &item)
|
||||
{
|
||||
return item[0];
|
||||
}
|
||||
|
||||
static void set_x(std::array<int, 3> &item, int value)
|
||||
{
|
||||
item[0] = value;
|
||||
}
|
||||
|
||||
TEST(virtual_array, DerivedSpan)
|
||||
{
|
||||
Vector<std::array<int, 3>> vector;
|
||||
vector.append({3, 4, 5});
|
||||
vector.append({1, 1, 1});
|
||||
{
|
||||
VArray_For_DerivedSpan<std::array<int, 3>, int, get_x> varray{vector};
|
||||
EXPECT_EQ(varray.size(), 2);
|
||||
EXPECT_EQ(varray[0], 3);
|
||||
EXPECT_EQ(varray[1], 1);
|
||||
}
|
||||
{
|
||||
VMutableArray_For_DerivedSpan<std::array<int, 3>, int, get_x, set_x> varray{vector};
|
||||
EXPECT_EQ(varray.size(), 2);
|
||||
EXPECT_EQ(varray[0], 3);
|
||||
EXPECT_EQ(varray[1], 1);
|
||||
varray.set(0, 10);
|
||||
varray.set(1, 20);
|
||||
EXPECT_EQ(vector[0][0], 10);
|
||||
EXPECT_EQ(vector[1][0], 20);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::tests
|
||||
|
@@ -226,7 +226,10 @@ int BLO_library_link_copypaste(struct Main *mainl, BlendHandle *bh, const uint64
|
||||
* Struct for temporarily loading datablocks from a blend file.
|
||||
*/
|
||||
typedef struct TempLibraryContext {
|
||||
struct Main *temp_main;
|
||||
/** Temporary main used for library data. */
|
||||
struct Main *bmain_lib;
|
||||
/** Temporary main used to load data into (currently initialized from `real_main`). */
|
||||
struct Main *bmain_base;
|
||||
struct BlendHandle *blendhandle;
|
||||
struct LibraryLink_Params liblink_params;
|
||||
struct Library *lib;
|
||||
|
@@ -21,6 +21,9 @@
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
@@ -32,15 +35,20 @@ TempLibraryContext *BLO_library_temp_load_id(struct Main *real_main,
|
||||
struct ReportList *reports)
|
||||
{
|
||||
TempLibraryContext *temp_lib_ctx = MEM_callocN(sizeof(*temp_lib_ctx), __func__);
|
||||
temp_lib_ctx->bmain_base = BKE_main_new();
|
||||
|
||||
/* Copy the file path so any path remapping is performed properly. */
|
||||
STRNCPY(temp_lib_ctx->bmain_base->name, real_main->name);
|
||||
|
||||
temp_lib_ctx->blendhandle = BLO_blendhandle_from_file(blend_file_path, reports);
|
||||
|
||||
BLO_library_link_params_init(&temp_lib_ctx->liblink_params, real_main, 0, LIB_TAG_TEMP_MAIN);
|
||||
BLO_library_link_params_init(
|
||||
&temp_lib_ctx->liblink_params, temp_lib_ctx->bmain_base, 0, LIB_TAG_TEMP_MAIN);
|
||||
|
||||
temp_lib_ctx->temp_main = BLO_library_link_begin(
|
||||
temp_lib_ctx->bmain_lib = BLO_library_link_begin(
|
||||
&temp_lib_ctx->blendhandle, blend_file_path, &temp_lib_ctx->liblink_params);
|
||||
|
||||
temp_lib_ctx->temp_id = BLO_library_link_named_part(temp_lib_ctx->temp_main,
|
||||
temp_lib_ctx->temp_id = BLO_library_link_named_part(temp_lib_ctx->bmain_lib,
|
||||
&temp_lib_ctx->blendhandle,
|
||||
idcode,
|
||||
idname,
|
||||
@@ -51,8 +59,13 @@ TempLibraryContext *BLO_library_temp_load_id(struct Main *real_main,
|
||||
|
||||
void BLO_library_temp_free(TempLibraryContext *temp_lib_ctx)
|
||||
{
|
||||
/* This moves the temporary ID and any indirectly loaded data into `bmain_base`
|
||||
* only to free `bmain_base`, while redundant this is the typical code-path for library linking,
|
||||
* it's more convenient to follow this convention rather than create a new code-path for this
|
||||
* one-off use case. */
|
||||
BLO_library_link_end(
|
||||
temp_lib_ctx->temp_main, &temp_lib_ctx->blendhandle, &temp_lib_ctx->liblink_params);
|
||||
temp_lib_ctx->bmain_lib, &temp_lib_ctx->blendhandle, &temp_lib_ctx->liblink_params);
|
||||
BLO_blendhandle_close(temp_lib_ctx->blendhandle);
|
||||
BKE_main_free(temp_lib_ctx->bmain_base);
|
||||
MEM_freeN(temp_lib_ctx);
|
||||
}
|
||||
|
@@ -1574,7 +1574,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
if (scene->ed != NULL) {
|
||||
if (scene->toolsettings->sequencer_tool_settings == NULL) {
|
||||
scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init();
|
||||
}
|
||||
}
|
||||
@@ -2009,18 +2009,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Versioning code until next subversion bump goes here.
|
||||
*
|
||||
* \note Be sure to check when bumping the version:
|
||||
* - "versioning_userdef.c", #blo_do_versions_userdef
|
||||
* - "versioning_userdef.c", #do_versions_theme
|
||||
*
|
||||
* \note Keep this message at the bottom of the function.
|
||||
*/
|
||||
{
|
||||
/* Keep this block, even when empty. */
|
||||
|
||||
if (!MAIN_VERSION_ATLEAST(bmain, 293, 18)) {
|
||||
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
|
||||
if (ntree->type == NTREE_GEOMETRY) {
|
||||
version_node_socket_name(ntree, GEO_NODE_VOLUME_TO_MESH, "Grid", "Density");
|
||||
@@ -2069,4 +2058,17 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Versioning code until next subversion bump goes here.
|
||||
*
|
||||
* \note Be sure to check when bumping the version:
|
||||
* - "versioning_userdef.c", #blo_do_versions_userdef
|
||||
* - "versioning_userdef.c", #do_versions_theme
|
||||
*
|
||||
* \note Keep this message at the bottom of the function.
|
||||
*/
|
||||
{
|
||||
/* Keep this block, even when empty. */
|
||||
}
|
||||
}
|
||||
|
@@ -19,7 +19,6 @@ set(INC
|
||||
../include
|
||||
../../blenkernel
|
||||
../../blenlib
|
||||
../../blenfont
|
||||
../../blentranslation
|
||||
../../depsgraph
|
||||
../../gpu
|
||||
|
@@ -33,7 +33,6 @@
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_nla.h"
|
||||
@@ -51,27 +50,15 @@
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "ED_armature.h"
|
||||
#include "ED_keyframes_draw.h"
|
||||
#include "ED_markers.h"
|
||||
#include "ED_numinput.h"
|
||||
#include "ED_screen.h"
|
||||
#include "ED_space_api.h"
|
||||
|
||||
#include "GPU_immediate.h"
|
||||
#include "GPU_immediate_util.h"
|
||||
#include "GPU_matrix.h"
|
||||
#include "GPU_state.h"
|
||||
|
||||
#include "armature_intern.h"
|
||||
|
||||
#include "BLF_api.h"
|
||||
|
||||
/* Pixel distance from 0% to 100%. */
|
||||
#define SLIDE_PIXEL_DISTANCE (300 * U.dpi_fac)
|
||||
|
||||
/* **************************************************** */
|
||||
/* == POSE 'SLIDING' TOOLS ==
|
||||
*
|
||||
@@ -123,43 +110,15 @@ typedef struct tPoseSlideOp {
|
||||
/** unused for now, but can later get used for storing runtime settings.... */
|
||||
short flag;
|
||||
|
||||
/* Store overlay settings when invoking the operator. Bones will be temporarily hidden. */
|
||||
int overlay_flag;
|
||||
|
||||
/** which transforms/channels are affected (ePoseSlide_Channels) */
|
||||
short channels;
|
||||
/** axis-limits for transforms (ePoseSlide_AxisLock) */
|
||||
short axislock;
|
||||
|
||||
/* Allow overshoot or clamp between 0% and 100%. */
|
||||
bool overshoot;
|
||||
|
||||
/* Reduces percentage delta from mouse movement. */
|
||||
bool precision;
|
||||
|
||||
/* Move percentage in 10% steps. */
|
||||
bool increments;
|
||||
|
||||
/* Pixel value in area space where the slider starts. */
|
||||
int slider_start[2];
|
||||
|
||||
/* Draw callback handler. */
|
||||
void *draw_handle;
|
||||
|
||||
/* Accumulative, unclamped and unrounded percentage. */
|
||||
float raw_percentage;
|
||||
|
||||
/* 0-1 value for determining the influence of whatever is relevant. */
|
||||
/** 0-1 value for determining the influence of whatever is relevant */
|
||||
float percentage;
|
||||
|
||||
/* Cursor position in region space. */
|
||||
int cursor_region_x;
|
||||
int cursor_region_y;
|
||||
|
||||
/* Last cursor position in screen space used for mouse movement delta calculation. */
|
||||
int last_cursor_x;
|
||||
|
||||
/* Numeric input. */
|
||||
/** numeric input */
|
||||
NumInput num;
|
||||
|
||||
struct tPoseSlideObject *ob_data_array;
|
||||
@@ -228,135 +187,6 @@ static const EnumPropertyItem prop_axis_lock_types[] = {
|
||||
|
||||
/* ------------------------------------ */
|
||||
|
||||
/* Draw a given string centered at the given coordinates. */
|
||||
static void draw_centered_string_with_background(
|
||||
const char *string, const float x, const float y, const uchar color_bg[4], const int fontid)
|
||||
{
|
||||
const int bg_margin = 5 * U.dpi_fac;
|
||||
float string_pixel_size[2];
|
||||
/* Get width and height of printed string to center above ticks. */
|
||||
BLF_width_and_height(
|
||||
fontid, string, sizeof(string), &string_pixel_size[0], &string_pixel_size[1]);
|
||||
const unsigned char col[3] = {color_bg[0], color_bg[1], color_bg[2]};
|
||||
const rctf background_rect = {.xmin = x - string_pixel_size[0] / 2 - bg_margin,
|
||||
.xmax = x + string_pixel_size[0] / 2 + bg_margin,
|
||||
.ymin = y - string_pixel_size[1] / 2 - bg_margin,
|
||||
.ymax = y + string_pixel_size[1] / 2 + bg_margin};
|
||||
UI_draw_roundbox_3ub_alpha(&background_rect, true, 4.0f, col, color_bg[3]);
|
||||
BLF_position(fontid, x - string_pixel_size[0] / 2, y - string_pixel_size[1] / 2, 0.0f);
|
||||
BLF_draw(fontid, string, sizeof(string));
|
||||
}
|
||||
|
||||
/* Draw an on screen Slider for a Pose Slide Operator. */
|
||||
static void pose_slide_draw_2d_slider(const struct bContext *UNUSED(C), ARegion *region, void *arg)
|
||||
{
|
||||
tPoseSlideOp *pso = arg;
|
||||
|
||||
/* Only draw in region from which the Operator was started. */
|
||||
if (region != pso->region) {
|
||||
return;
|
||||
}
|
||||
|
||||
uchar color_text[4];
|
||||
uchar color_line[4];
|
||||
uchar color_handle[4];
|
||||
uchar color_bg[4];
|
||||
|
||||
char percentage_string[256];
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
||||
|
||||
/* Get theme colors. Text gets a different color when the value is clamped.*/
|
||||
UI_GetThemeColor4ubv(TH_TEXT, color_text);
|
||||
UI_GetThemeColor4ubv(TH_TEXT, color_line);
|
||||
UI_GetThemeColor4ubv(TH_ACTIVE, color_handle);
|
||||
UI_GetThemeColor4ubv(TH_HEADER, color_bg);
|
||||
color_bg[3] = 160;
|
||||
|
||||
/* Get the default font. */
|
||||
const uiStyle *style = UI_style_get();
|
||||
const uiFontStyle *fstyle = &style->widget;
|
||||
const int fontid = fstyle->uifont_id;
|
||||
BLF_color3ubv(fontid, color_text);
|
||||
BLF_rotation(fontid, 0.0f);
|
||||
|
||||
const float line_width = 4 * U.dpi_fac;
|
||||
const float base_tick_height = 16.0 * U.dpi_fac;
|
||||
const float text_offset = 24.0 * U.dpi_fac;
|
||||
const float overshoot_range_delta = 0.2;
|
||||
int initial_tick_percentage;
|
||||
int handle_pos_x;
|
||||
float overshoot_length_delta;
|
||||
|
||||
if (pso->overshoot) {
|
||||
initial_tick_percentage = (int)((pso->percentage - 0.5f - overshoot_range_delta) * 100);
|
||||
handle_pos_x = pso->slider_start[0] + (SLIDE_PIXEL_DISTANCE / 2);
|
||||
overshoot_length_delta = SLIDE_PIXEL_DISTANCE * overshoot_range_delta;
|
||||
}
|
||||
else {
|
||||
initial_tick_percentage = 0;
|
||||
handle_pos_x = pso->slider_start[0] + SLIDE_PIXEL_DISTANCE * pso->percentage;
|
||||
overshoot_length_delta = 0;
|
||||
}
|
||||
|
||||
/* Draw main line. */
|
||||
const struct rctf main_line_rect = {.xmin = pso->slider_start[0] - overshoot_length_delta,
|
||||
.xmax = pso->slider_start[0] + SLIDE_PIXEL_DISTANCE +
|
||||
overshoot_length_delta,
|
||||
.ymin = pso->slider_start[1] - line_width / 2,
|
||||
.ymax = pso->slider_start[1] + line_width / 2};
|
||||
UI_draw_roundbox_3ub_alpha(&main_line_rect, true, 0, color_line, 255);
|
||||
UI_draw_roundbox_3ub_alpha(&main_line_rect, false, 0, color_bg, 255);
|
||||
|
||||
/* Draw percentage ticks. */
|
||||
const int tick_increment = 10;
|
||||
const int percentage_range = pso->overshoot ? 100 + 100 * overshoot_range_delta * 2 : 100;
|
||||
|
||||
/* Round initial_tick_percentage up to the next tick_increment. */
|
||||
int tick_percentage = ceil((float)initial_tick_percentage / tick_increment) * tick_increment;
|
||||
|
||||
for (; tick_percentage <= initial_tick_percentage + percentage_range;
|
||||
tick_percentage += tick_increment) {
|
||||
const float x = handle_pos_x +
|
||||
SLIDE_PIXEL_DISTANCE * (((float)tick_percentage / 100) - pso->percentage);
|
||||
/* 0.5 steps are considered main ticks and are drawn thicker with
|
||||
* percentage indicator string below. */
|
||||
const bool is_main_tick = tick_percentage % 50 == 0;
|
||||
const float tick_height = is_main_tick ? base_tick_height : base_tick_height / 2;
|
||||
|
||||
const struct rctf tick_rect = {.xmin = x - (line_width / 2),
|
||||
.xmax = x + (line_width / 2),
|
||||
.ymin = pso->slider_start[1] - (tick_height / 2),
|
||||
.ymax = pso->slider_start[1] + (tick_height / 2)};
|
||||
|
||||
UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255);
|
||||
UI_draw_roundbox_3ub_alpha(&tick_rect, false, 1, color_bg, 255);
|
||||
|
||||
/* Draw percentage on main ticks. */
|
||||
if (is_main_tick) {
|
||||
BLI_snprintf(percentage_string, sizeof(percentage_string), "%d%%", tick_percentage);
|
||||
draw_centered_string_with_background(
|
||||
percentage_string, x, pso->slider_start[1] - text_offset, color_bg, fontid);
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw handle indicating current percentage. */
|
||||
const struct rctf handle_rect = {.xmin = handle_pos_x - (line_width),
|
||||
.xmax = handle_pos_x + (line_width),
|
||||
.ymin = pso->slider_start[1] - (base_tick_height / 2),
|
||||
.ymax = pso->slider_start[1] + (base_tick_height / 2)};
|
||||
|
||||
UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255);
|
||||
UI_draw_roundbox_3ub_alpha(&handle_rect, false, 1, color_bg, 255);
|
||||
|
||||
BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", pso->percentage * 100);
|
||||
draw_centered_string_with_background(
|
||||
percentage_string, handle_pos_x, pso->slider_start[1] + text_offset, color_bg, fontid);
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
/* operator init */
|
||||
static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
|
||||
{
|
||||
@@ -375,7 +205,6 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
|
||||
|
||||
/* set range info from property values - these may get overridden for the invoke() */
|
||||
pso->percentage = RNA_float_get(op->ptr, "percentage");
|
||||
pso->raw_percentage = pso->percentage;
|
||||
pso->prevFrame = RNA_int_get(op->ptr, "prev_frame");
|
||||
pso->nextFrame = RNA_int_get(op->ptr, "next_frame");
|
||||
|
||||
@@ -428,10 +257,6 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
|
||||
pso->num.val_flag[0] |= NUM_NO_NEGATIVE;
|
||||
pso->num.unit_type[0] = B_UNIT_NONE; /* percentages don't have any units... */
|
||||
|
||||
/* Register UI drawing callback. */
|
||||
pso->draw_handle = ED_region_draw_cb_activate(
|
||||
pso->region->type, pose_slide_draw_2d_slider, pso, REGION_DRAW_POST_PIXEL);
|
||||
|
||||
/* return status is whether we've got all the data we were requested to get */
|
||||
return 1;
|
||||
}
|
||||
@@ -441,13 +266,6 @@ static void pose_slide_exit(wmOperator *op)
|
||||
{
|
||||
tPoseSlideOp *pso = op->customdata;
|
||||
|
||||
/* Hide Bone Overlay. */
|
||||
View3D *v3d = pso->area->spacedata.first;
|
||||
v3d->overlay.flag = pso->overlay_flag;
|
||||
|
||||
/* Remove UI drawing callback. */
|
||||
ED_region_draw_cb_exit(pso->region->type, pso->draw_handle);
|
||||
|
||||
/* if data exists, clear its data and exit */
|
||||
if (pso) {
|
||||
/* free the temp pchan links and their data */
|
||||
@@ -784,7 +602,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
|
||||
|
||||
static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], float default_value)
|
||||
{
|
||||
/* We only slide to the rest pose. So only use the default rest pose value. */
|
||||
/* We only slide to the rest pose. So only use the default rest pose value */
|
||||
const int lock = pso->axislock;
|
||||
for (int idx = 0; idx < 3; idx++) {
|
||||
if ((lock == 0) || ((lock & PS_LOCK_X) && (idx == 0)) || ((lock & PS_LOCK_Y) && (idx == 1)) ||
|
||||
@@ -803,7 +621,7 @@ static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], flo
|
||||
|
||||
static void pose_slide_rest_pose_apply_other_rot(tPoseSlideOp *pso, float vec[4], bool quat)
|
||||
{
|
||||
/* We only slide to the rest pose. So only use the default rest pose value. */
|
||||
/* We only slide to the rest pose. So only use the default rest pose value */
|
||||
float default_values[] = {1.0f, 0.0f, 0.0f, 0.0f};
|
||||
if (!quat) {
|
||||
/* Axis Angle */
|
||||
@@ -971,18 +789,14 @@ static void pose_slide_reset(tPoseSlideOp *pso)
|
||||
|
||||
/* ------------------------------------ */
|
||||
|
||||
/* Draw percentage indicator in workspace footer. */
|
||||
/* draw percentage indicator in header */
|
||||
/* TODO: Include hints about locks here... */
|
||||
static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso)
|
||||
static void pose_slide_draw_status(tPoseSlideOp *pso)
|
||||
{
|
||||
char status_str[UI_MAX_DRAW_STR];
|
||||
char limits_str[UI_MAX_DRAW_STR];
|
||||
char axis_str[50];
|
||||
char mode_str[32];
|
||||
char overshoot_str[50];
|
||||
char precision_str[50];
|
||||
char increments_str[50];
|
||||
char bone_vis_str[50];
|
||||
|
||||
switch (pso->mode) {
|
||||
case POSESLIDE_PUSH:
|
||||
@@ -1057,51 +871,25 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pso->overshoot) {
|
||||
BLI_strncpy(overshoot_str, TIP_("[E] - Disable overshoot"), sizeof(overshoot_str));
|
||||
}
|
||||
else {
|
||||
BLI_strncpy(overshoot_str, TIP_("E - Enable overshoot"), sizeof(overshoot_str));
|
||||
}
|
||||
|
||||
if (pso->precision) {
|
||||
BLI_strncpy(precision_str, TIP_("[Shift] - Precision active"), sizeof(precision_str));
|
||||
}
|
||||
else {
|
||||
BLI_strncpy(precision_str, TIP_("Shift - Hold for precision"), sizeof(precision_str));
|
||||
}
|
||||
|
||||
if (pso->increments) {
|
||||
BLI_strncpy(increments_str, TIP_("[Ctrl] - Increments active"), sizeof(increments_str));
|
||||
}
|
||||
else {
|
||||
BLI_strncpy(increments_str, TIP_("Ctrl - Hold for 10% increments"), sizeof(increments_str));
|
||||
}
|
||||
|
||||
BLI_strncpy(bone_vis_str, TIP_("[H] - Toggle bone visibility"), sizeof(increments_str));
|
||||
|
||||
if (hasNumInput(&pso->num)) {
|
||||
Scene *scene = pso->scene;
|
||||
char str_offs[NUM_STR_REP_LEN];
|
||||
char str_ofs[NUM_STR_REP_LEN];
|
||||
|
||||
outputNumInput(&pso->num, str_offs, &scene->unit);
|
||||
outputNumInput(&pso->num, str_ofs, &scene->unit);
|
||||
|
||||
BLI_snprintf(status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_offs, limits_str);
|
||||
BLI_snprintf(
|
||||
status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_ofs, limits_str);
|
||||
}
|
||||
else {
|
||||
BLI_snprintf(status_str,
|
||||
sizeof(status_str),
|
||||
"%s: %d %% | %s | %s | %s | %s | %s",
|
||||
mode_str,
|
||||
(int)(pso->percentage * 100.0f),
|
||||
limits_str,
|
||||
overshoot_str,
|
||||
precision_str,
|
||||
increments_str,
|
||||
bone_vis_str);
|
||||
sizeof(status_str),
|
||||
"%s: %d %% | %s",
|
||||
mode_str,
|
||||
(int)(pso->percentage * 100.0f),
|
||||
limits_str);
|
||||
}
|
||||
|
||||
ED_workspace_status_text(C, status_str);
|
||||
ED_area_status_text(pso->area, status_str);
|
||||
}
|
||||
|
||||
/* common code for invoke() methods */
|
||||
@@ -1187,74 +975,24 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p
|
||||
WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL);
|
||||
|
||||
/* header print */
|
||||
pose_slide_draw_status(C, pso);
|
||||
pose_slide_draw_status(pso);
|
||||
|
||||
/* add a modal handler for this operator */
|
||||
WM_event_add_modal_handler(C, op);
|
||||
|
||||
/* Hide Bone Overlay. */
|
||||
View3D *v3d = pso->area->spacedata.first;
|
||||
pso->overlay_flag = v3d->overlay.flag;
|
||||
v3d->overlay.flag |= V3D_OVERLAY_HIDE_BONES;
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
/* Calculate percentage based on mouse movement, clamp or round to increments if
|
||||
* enabled by the user. Store the new percentage value.
|
||||
/* calculate percentage based on position of mouse (we only use x-axis for now.
|
||||
* since this is more convenient for users to do), and store new percentage value
|
||||
*/
|
||||
static void pose_slide_mouse_update_percentage(tPoseSlideOp *pso,
|
||||
wmOperator *op,
|
||||
const wmEvent *event)
|
||||
{
|
||||
const float percentage_delta = (event->x - pso->last_cursor_x) / ((float)(SLIDE_PIXEL_DISTANCE));
|
||||
/* Reduced percentage delta in precision mode (shift held). */
|
||||
pso->raw_percentage += pso->precision ? (percentage_delta / 8) : percentage_delta;
|
||||
pso->percentage = pso->raw_percentage;
|
||||
pso->last_cursor_x = event->x;
|
||||
/* Modulo mval so the value wraps properly in area space with the cursor. */
|
||||
if (event->mval[0] > 0) {
|
||||
pso->cursor_region_x = event->mval[0] % pso->region->winx;
|
||||
}
|
||||
else {
|
||||
pso->cursor_region_x = (event->mval[0] % pso->region->winx) + pso->region->winx;
|
||||
}
|
||||
pso->cursor_region_y = event->mval[1];
|
||||
|
||||
if (!pso->overshoot) {
|
||||
pso->percentage = clamp_f(pso->percentage, 0, 1);
|
||||
}
|
||||
|
||||
if (pso->increments) {
|
||||
pso->percentage = round(pso->percentage * 10) / 10;
|
||||
}
|
||||
|
||||
pso->percentage = (event->x - pso->region->winrct.xmin) / ((float)pso->region->winx);
|
||||
RNA_float_set(op->ptr, "percentage", pso->percentage);
|
||||
}
|
||||
|
||||
/* Calculate the starting percentage so that 0% or 100% will always be in the area. */
|
||||
static void pose_slide_calculate_start_percentage(tPoseSlideOp *pso, const wmEvent *event)
|
||||
{
|
||||
pso->last_cursor_x = event->x;
|
||||
/* A bit of padding to both sides so the text stays on screen. */
|
||||
const int padding = 16 * U.dpi_fac;
|
||||
|
||||
/* Clamp it so 0% or 100% is always on screen. */
|
||||
/* Catching edge case where slider doesn't fit in window. */
|
||||
if (padding < pso->region->winx - SLIDE_PIXEL_DISTANCE - padding) {
|
||||
pso->last_cursor_x = clamp_i(pso->last_cursor_x,
|
||||
pso->region->winrct.xmin + padding + SLIDE_PIXEL_DISTANCE / 2,
|
||||
pso->region->winrct.xmax - padding - SLIDE_PIXEL_DISTANCE / 2);
|
||||
}
|
||||
else {
|
||||
pso->last_cursor_x = clamp_i(pso->last_cursor_x,
|
||||
pso->region->winrct.xmin + padding + SLIDE_PIXEL_DISTANCE / 2,
|
||||
INT_MAX);
|
||||
}
|
||||
pso->slider_start[0] = (pso->region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2);
|
||||
pso->slider_start[1] = 64 * U.dpi_fac;
|
||||
}
|
||||
|
||||
/* handle an event to toggle channels mode */
|
||||
static void pose_slide_toggle_channels_mode(wmOperator *op,
|
||||
tPoseSlideOp *pso,
|
||||
@@ -1318,12 +1056,9 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
case EVT_PADENTER: {
|
||||
if (event->val == KM_PRESS) {
|
||||
/* return to normal cursor and header status */
|
||||
ED_workspace_status_text(C, NULL);
|
||||
ED_area_status_text(pso->area, NULL);
|
||||
WM_cursor_modal_restore(win);
|
||||
|
||||
/* Depsgraph updates + redraws. Redraw needed to remove UI. */
|
||||
pose_slide_refresh(C, pso);
|
||||
|
||||
/* insert keyframes as required... */
|
||||
pose_slide_autoKeyframe(C, pso);
|
||||
pose_slide_exit(op);
|
||||
@@ -1338,13 +1073,13 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
case RIGHTMOUSE: {
|
||||
if (event->val == KM_PRESS) {
|
||||
/* return to normal cursor and header status */
|
||||
ED_workspace_status_text(C, NULL);
|
||||
ED_area_status_text(pso->area, NULL);
|
||||
WM_cursor_modal_restore(win);
|
||||
|
||||
/* reset transforms back to original state */
|
||||
pose_slide_reset(pso);
|
||||
|
||||
/* Depsgraph updates + redraws.*/
|
||||
/* depsgraph updates + redraws */
|
||||
pose_slide_refresh(C, pso);
|
||||
|
||||
/* clean up temp data */
|
||||
@@ -1443,58 +1178,10 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Overshoot. */
|
||||
case EVT_EKEY: {
|
||||
pso->overshoot = !pso->overshoot;
|
||||
do_pose_update = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Precision mode. */
|
||||
case EVT_LEFTSHIFTKEY:
|
||||
case EVT_RIGHTSHIFTKEY: {
|
||||
pso->precision = true;
|
||||
do_pose_update = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Increments mode. */
|
||||
case EVT_LEFTCTRLKEY:
|
||||
case EVT_RIGHTCTRLKEY: {
|
||||
pso->increments = true;
|
||||
do_pose_update = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Toggle Bone visibility. */
|
||||
case EVT_HKEY: {
|
||||
View3D *v3d = pso->area->spacedata.first;
|
||||
v3d->overlay.flag ^= V3D_OVERLAY_HIDE_BONES;
|
||||
}
|
||||
|
||||
default: /* Some other unhandled key... */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Precision and stepping only active while button is held. */
|
||||
else if (event->val == KM_RELEASE) {
|
||||
switch (event->type) {
|
||||
case EVT_LEFTSHIFTKEY:
|
||||
case EVT_RIGHTSHIFTKEY: {
|
||||
pso->precision = false;
|
||||
do_pose_update = true;
|
||||
break;
|
||||
}
|
||||
case EVT_LEFTCTRLKEY:
|
||||
case EVT_RIGHTCTRLKEY: {
|
||||
pso->increments = false;
|
||||
do_pose_update = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* unhandled event - maybe it was some view manipulation? */
|
||||
/* allow to pass through */
|
||||
@@ -1506,10 +1193,8 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
/* Perform pose updates - in response to some user action
|
||||
* (e.g. pressing a key or moving the mouse). */
|
||||
if (do_pose_update) {
|
||||
pose_slide_mouse_update_percentage(pso, op, event);
|
||||
|
||||
/* update percentage indicator in header */
|
||||
pose_slide_draw_status(C, pso);
|
||||
pose_slide_draw_status(pso);
|
||||
|
||||
/* reset transforms (to avoid accumulation errors) */
|
||||
pose_slide_reset(pso);
|
||||
@@ -1562,11 +1247,11 @@ static void pose_slide_opdef_properties(wmOperatorType *ot)
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_float_factor(ot->srna,
|
||||
"percentage",
|
||||
"factor",
|
||||
0.5f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
"Percentage",
|
||||
"Factor",
|
||||
"Weighting factor for which keyframe is favored more",
|
||||
0.0,
|
||||
1.0);
|
||||
@@ -1625,8 +1310,6 @@ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *ev
|
||||
|
||||
pso = op->customdata;
|
||||
|
||||
pose_slide_calculate_start_percentage(pso, event);
|
||||
|
||||
/* Initialize percentage so that it won't pop on first mouse move. */
|
||||
pose_slide_mouse_update_percentage(pso, op, event);
|
||||
|
||||
@@ -1666,7 +1349,7 @@ void POSE_OT_push(wmOperatorType *ot)
|
||||
ot->poll = ED_operator_posemode;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
||||
|
||||
/* Properties */
|
||||
pose_slide_opdef_properties(ot);
|
||||
@@ -1687,8 +1370,6 @@ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *e
|
||||
|
||||
pso = op->customdata;
|
||||
|
||||
pose_slide_calculate_start_percentage(pso, event);
|
||||
|
||||
/* Initialize percentage so that it won't pop on first mouse move. */
|
||||
pose_slide_mouse_update_percentage(pso, op, event);
|
||||
|
||||
@@ -1728,7 +1409,7 @@ void POSE_OT_relax(wmOperatorType *ot)
|
||||
ot->poll = ED_operator_posemode;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
||||
|
||||
/* Properties */
|
||||
pose_slide_opdef_properties(ot);
|
||||
@@ -1748,8 +1429,6 @@ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEven
|
||||
|
||||
pso = op->customdata;
|
||||
|
||||
pso->last_cursor_x = event->x;
|
||||
|
||||
/* Initialize percentage so that it won't pop on first mouse move. */
|
||||
pose_slide_mouse_update_percentage(pso, op, event);
|
||||
|
||||
@@ -1789,7 +1468,7 @@ void POSE_OT_push_rest(wmOperatorType *ot)
|
||||
ot->poll = ED_operator_posemode;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
||||
|
||||
/* Properties */
|
||||
pose_slide_opdef_properties(ot);
|
||||
@@ -1810,8 +1489,6 @@ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEve
|
||||
|
||||
pso = op->customdata;
|
||||
|
||||
pose_slide_calculate_start_percentage(pso, event);
|
||||
|
||||
/* Initialize percentage so that it won't pop on first mouse move. */
|
||||
pose_slide_mouse_update_percentage(pso, op, event);
|
||||
|
||||
@@ -1851,7 +1528,7 @@ void POSE_OT_relax_rest(wmOperatorType *ot)
|
||||
ot->poll = ED_operator_posemode;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
||||
|
||||
/* Properties */
|
||||
pose_slide_opdef_properties(ot);
|
||||
@@ -1872,8 +1549,6 @@ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEven
|
||||
|
||||
pso = op->customdata;
|
||||
|
||||
pose_slide_calculate_start_percentage(pso, event);
|
||||
|
||||
/* Initialize percentage so that it won't pop on first mouse move. */
|
||||
pose_slide_mouse_update_percentage(pso, op, event);
|
||||
|
||||
@@ -1913,7 +1588,7 @@ void POSE_OT_breakdown(wmOperatorType *ot)
|
||||
ot->poll = ED_operator_posemode;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
||||
|
||||
/* Properties */
|
||||
pose_slide_opdef_properties(ot);
|
||||
|
@@ -68,7 +68,9 @@ typedef struct SnapGizmo3D {
|
||||
struct {
|
||||
int x;
|
||||
int y;
|
||||
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
|
||||
short shift, ctrl, alt, oskey;
|
||||
#endif
|
||||
} last_eventstate;
|
||||
|
||||
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
|
||||
@@ -93,39 +95,36 @@ typedef struct SnapGizmo3D {
|
||||
bool is_enabled;
|
||||
} SnapGizmo3D;
|
||||
|
||||
static bool eventstate_cmp(SnapGizmo3D *snap_gizmo, const wmEvent *event)
|
||||
{
|
||||
if ((event->x == snap_gizmo->last_eventstate.x) && (event->y == snap_gizmo->last_eventstate.y) &&
|
||||
(event->ctrl == snap_gizmo->last_eventstate.ctrl) &&
|
||||
(event->shift == snap_gizmo->last_eventstate.shift) &&
|
||||
(event->alt == snap_gizmo->last_eventstate.alt) &&
|
||||
(event->oskey == snap_gizmo->last_eventstate.oskey)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Checks if the current event is different from the one captured in the last update. */
|
||||
static bool eventstate_has_changed(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
|
||||
{
|
||||
if (wm && wm->winactive) {
|
||||
const wmEvent *event = wm->winactive->eventstate;
|
||||
return eventstate_cmp(snap_gizmo, event) == false;
|
||||
if ((event->x != snap_gizmo->last_eventstate.x) ||
|
||||
(event->y != snap_gizmo->last_eventstate.y)) {
|
||||
return true;
|
||||
}
|
||||
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
|
||||
if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) {
|
||||
if ((event->ctrl != snap_gizmo->last_eventstate.ctrl) ||
|
||||
(event->shift != snap_gizmo->last_eventstate.shift) ||
|
||||
(event->alt != snap_gizmo->last_eventstate.alt) ||
|
||||
(event->oskey != snap_gizmo->last_eventstate.oskey)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Copies the current eventstate. */
|
||||
static void eventstate_save(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
|
||||
static void eventstate_save_xy(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
|
||||
{
|
||||
if (wm && wm->winactive) {
|
||||
const wmEvent *event = wm->winactive->eventstate;
|
||||
snap_gizmo->last_eventstate.x = event->x;
|
||||
snap_gizmo->last_eventstate.y = event->y;
|
||||
snap_gizmo->last_eventstate.ctrl = event->ctrl;
|
||||
snap_gizmo->last_eventstate.shift = event->shift;
|
||||
snap_gizmo->last_eventstate.alt = event->alt;
|
||||
snap_gizmo->last_eventstate.oskey = event->oskey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,16 +136,20 @@ static bool invert_snap(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
|
||||
}
|
||||
|
||||
const wmEvent *event = wm->winactive->eventstate;
|
||||
if (eventstate_cmp(snap_gizmo, event)) {
|
||||
if ((event->ctrl == snap_gizmo->last_eventstate.ctrl) &&
|
||||
(event->shift == snap_gizmo->last_eventstate.shift) &&
|
||||
(event->alt == snap_gizmo->last_eventstate.alt) &&
|
||||
(event->oskey == snap_gizmo->last_eventstate.oskey)) {
|
||||
/* Nothing has changed. */
|
||||
return snap_gizmo->invert_snap;
|
||||
}
|
||||
|
||||
if (snap_gizmo->keymap == NULL) {
|
||||
/* Lazy initialization. */
|
||||
snap_gizmo->keymap = WM_modalkeymap_find(wm->defaultconf, "Generic Gizmo Tweak Modal Map");
|
||||
RNA_enum_value_from_id(snap_gizmo->keymap->modal_items, "SNAP_ON", &snap_gizmo->snap_on);
|
||||
}
|
||||
/* Save new eventstate. */
|
||||
snap_gizmo->last_eventstate.ctrl = event->ctrl;
|
||||
snap_gizmo->last_eventstate.shift = event->shift;
|
||||
snap_gizmo->last_eventstate.alt = event->alt;
|
||||
snap_gizmo->last_eventstate.oskey = event->oskey;
|
||||
|
||||
const int snap_on = snap_gizmo->snap_on;
|
||||
|
||||
wmKeyMap *keymap = WM_keymap_active(wm, snap_gizmo->keymap);
|
||||
@@ -335,8 +338,9 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
|
||||
Scene *scene = DEG_get_input_scene(depsgraph);
|
||||
|
||||
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
|
||||
if ((snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE) == 0) {
|
||||
if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) {
|
||||
snap_gizmo->invert_snap = invert_snap(snap_gizmo, wm);
|
||||
|
||||
const ToolSettings *ts = scene->toolsettings;
|
||||
if (snap_gizmo->invert_snap != !(ts->snap_flag & SCE_SNAP)) {
|
||||
snap_gizmo->snap_elem = 0;
|
||||
@@ -344,8 +348,7 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
eventstate_save(snap_gizmo, wm);
|
||||
eventstate_save_xy(snap_gizmo, wm);
|
||||
|
||||
snap_gizmo->is_enabled = true;
|
||||
|
||||
@@ -555,8 +558,14 @@ static void gizmo_snap_rna_snap_elem_index_get_fn(struct PointerRNA *ptr,
|
||||
|
||||
static void snap_gizmo_setup(wmGizmo *gz)
|
||||
{
|
||||
/* Flags. */
|
||||
gz->flag |= WM_GIZMO_NO_TOOLTIP;
|
||||
|
||||
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
|
||||
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
|
||||
snap_gizmo->keymap = WM_modalkeymap_find(gz->parent_gzgroup->type->keyconf,
|
||||
"Generic Gizmo Tweak Modal Map");
|
||||
RNA_enum_value_from_id(snap_gizmo->keymap->modal_items, "SNAP_ON", &snap_gizmo->snap_on);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void snap_gizmo_draw(const bContext *C, wmGizmo *gz)
|
||||
|
@@ -228,7 +228,7 @@ static void pick_link(const bContext *C,
|
||||
BLI_assert(nldrag->last_node_hovered_while_dragging_a_link != NULL);
|
||||
|
||||
sort_multi_input_socket_links(
|
||||
snode, nldrag->last_node_hovered_while_dragging_a_link, NULL,NULL);
|
||||
snode, nldrag->last_node_hovered_while_dragging_a_link, NULL, NULL);
|
||||
|
||||
/* Send changed event to original link->tonode. */
|
||||
if (node) {
|
||||
@@ -901,7 +901,7 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
|
||||
existing_link_connected_to_fromsock->multi_input_socket_index;
|
||||
continue;
|
||||
}
|
||||
if(link->tosock && link->tosock->flag & SOCK_MULTI_INPUT){
|
||||
if (link->tosock && link->tosock->flag & SOCK_MULTI_INPUT) {
|
||||
sort_multi_input_socket_links(snode, tnode, link, cursor);
|
||||
}
|
||||
}
|
||||
@@ -2260,7 +2260,12 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
|
||||
node_remove_extra_links(snode, link);
|
||||
link->flag &= ~NODE_LINKFLAG_HILITE;
|
||||
|
||||
nodeAddLink(snode->edittree, select, best_output, node, sockto);
|
||||
bNodeLink *new_link = nodeAddLink(snode->edittree, select, best_output, node, sockto);
|
||||
|
||||
/* Copy the socket index for the new link, and reset it for the old link. This way the
|
||||
* relative order of links is preserved, and the links get drawn in the right place. */
|
||||
new_link->multi_input_socket_index = link->multi_input_socket_index;
|
||||
link->multi_input_socket_index = 0;
|
||||
|
||||
/* set up insert offset data, it needs stuff from here */
|
||||
if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) {
|
||||
@@ -2277,8 +2282,6 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
|
||||
snode_update(snode, select);
|
||||
ED_node_tag_update_id((ID *)snode->edittree);
|
||||
ED_node_tag_update_id(snode->id);
|
||||
|
||||
sort_multi_input_socket_links(snode, node, NULL, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -665,6 +665,10 @@ static const EnumPropertyItem *outliner_id_itemf(bContext *C,
|
||||
PropertyRNA *UNUSED(prop),
|
||||
bool *r_free)
|
||||
{
|
||||
if (C == NULL) {
|
||||
return DummyRNA_NULL_items;
|
||||
}
|
||||
|
||||
EnumPropertyItem item_tmp = {0}, *item = NULL;
|
||||
int totitem = 0;
|
||||
int i = 0;
|
||||
|
@@ -78,35 +78,45 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
|
||||
{
|
||||
std::lock_guard lock{mutex_};
|
||||
|
||||
bke::ReadAttributePtr attribute_ptr = component_->attribute_try_get_for_read(column_id.name);
|
||||
if (!attribute_ptr) {
|
||||
bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
const bke::ReadAttribute *attribute = scope_.add(std::move(attribute_ptr), __func__);
|
||||
if (attribute->domain() != domain_) {
|
||||
const fn::GVArray *varray = scope_.add(std::move(attribute.varray), __func__);
|
||||
if (attribute.domain != domain_) {
|
||||
return {};
|
||||
}
|
||||
int domain_size = attribute->size();
|
||||
switch (attribute->custom_data_type()) {
|
||||
int domain_size = varray->size();
|
||||
const CustomDataType type = bke::cpp_type_to_custom_data_type(varray->type());
|
||||
switch (type) {
|
||||
case CD_PROP_FLOAT:
|
||||
if (column_id.index != -1) {
|
||||
return {};
|
||||
}
|
||||
return column_values_from_function(
|
||||
column_id.name, domain_size, [attribute](int index, CellValue &r_cell_value) {
|
||||
column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
|
||||
float value;
|
||||
attribute->get(index, &value);
|
||||
varray->get(index, &value);
|
||||
r_cell_value.value_float = value;
|
||||
});
|
||||
case CD_PROP_INT32:
|
||||
if (column_id.index != -1) {
|
||||
return {};
|
||||
}
|
||||
return column_values_from_function(
|
||||
column_id.name, domain_size, [attribute](int index, CellValue &r_cell_value) {
|
||||
column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
|
||||
int value;
|
||||
attribute->get(index, &value);
|
||||
varray->get(index, &value);
|
||||
r_cell_value.value_int = value;
|
||||
});
|
||||
case CD_PROP_BOOL:
|
||||
if (column_id.index != -1) {
|
||||
return {};
|
||||
}
|
||||
return column_values_from_function(
|
||||
column_id.name, domain_size, [attribute](int index, CellValue &r_cell_value) {
|
||||
column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
|
||||
bool value;
|
||||
attribute->get(index, &value);
|
||||
varray->get(index, &value);
|
||||
r_cell_value.value_bool = value;
|
||||
});
|
||||
case CD_PROP_FLOAT2: {
|
||||
@@ -116,11 +126,9 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
|
||||
const std::array<const char *, 2> suffixes = {" X", " Y"};
|
||||
const std::string name = StringRef(column_id.name) + suffixes[column_id.index];
|
||||
return column_values_from_function(
|
||||
name,
|
||||
domain_size,
|
||||
[attribute, axis = column_id.index](int index, CellValue &r_cell_value) {
|
||||
name, domain_size, [varray, axis = column_id.index](int index, CellValue &r_cell_value) {
|
||||
float2 value;
|
||||
attribute->get(index, &value);
|
||||
varray->get(index, &value);
|
||||
r_cell_value.value_float = value[axis];
|
||||
});
|
||||
}
|
||||
@@ -131,11 +139,9 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
|
||||
const std::array<const char *, 3> suffixes = {" X", " Y", " Z"};
|
||||
const std::string name = StringRef(column_id.name) + suffixes[column_id.index];
|
||||
return column_values_from_function(
|
||||
name,
|
||||
domain_size,
|
||||
[attribute, axis = column_id.index](int index, CellValue &r_cell_value) {
|
||||
name, domain_size, [varray, axis = column_id.index](int index, CellValue &r_cell_value) {
|
||||
float3 value;
|
||||
attribute->get(index, &value);
|
||||
varray->get(index, &value);
|
||||
r_cell_value.value_float = value[axis];
|
||||
});
|
||||
}
|
||||
@@ -146,11 +152,9 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
|
||||
const std::array<const char *, 4> suffixes = {" R", " G", " B", " A"};
|
||||
const std::string name = StringRef(column_id.name) + suffixes[column_id.index];
|
||||
return column_values_from_function(
|
||||
name,
|
||||
domain_size,
|
||||
[attribute, axis = column_id.index](int index, CellValue &r_cell_value) {
|
||||
name, domain_size, [varray, axis = column_id.index](int index, CellValue &r_cell_value) {
|
||||
Color4f value;
|
||||
attribute->get(index, &value);
|
||||
varray->get(index, &value);
|
||||
r_cell_value.value_float = value[axis];
|
||||
});
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@ namespace blender::fn {
|
||||
* A generic span. It behaves just like a blender::Span<T>, but the type is only known at run-time.
|
||||
*/
|
||||
class GSpan {
|
||||
private:
|
||||
protected:
|
||||
const CPPType *type_;
|
||||
const void *data_;
|
||||
int64_t size_;
|
||||
@@ -92,7 +92,7 @@ class GSpan {
|
||||
* known at run-time.
|
||||
*/
|
||||
class GMutableSpan {
|
||||
private:
|
||||
protected:
|
||||
const CPPType *type_;
|
||||
void *data_;
|
||||
int64_t size_;
|
||||
|
@@ -123,7 +123,7 @@ template<typename T> class GVectorArray_TypedMutableRef {
|
||||
|
||||
void extend(const int64_t index, const VArray<T> &values)
|
||||
{
|
||||
GVArrayForVArray<T> array{values};
|
||||
GVArray_For_VArray<T> array{values};
|
||||
this->extend(index, array);
|
||||
}
|
||||
|
||||
@@ -134,12 +134,12 @@ template<typename T> class GVectorArray_TypedMutableRef {
|
||||
};
|
||||
|
||||
/* A generic virtual vector array implementation for a `GVectorArray`. */
|
||||
class GVVectorArrayForGVectorArray : public GVVectorArray {
|
||||
class GVVectorArray_For_GVectorArray : public GVVectorArray {
|
||||
private:
|
||||
const GVectorArray &vector_array_;
|
||||
|
||||
public:
|
||||
GVVectorArrayForGVectorArray(const GVectorArray &vector_array)
|
||||
GVVectorArray_For_GVectorArray(const GVectorArray &vector_array)
|
||||
: GVVectorArray(vector_array.type(), vector_array.size()), vector_array_(vector_array)
|
||||
{
|
||||
}
|
||||
|
@@ -23,12 +23,17 @@
|
||||
* the data type is only known at runtime.
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "FN_generic_span.hh"
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
template<typename T> class GVArray_Typed;
|
||||
template<typename T> class GVMutableArray_Typed;
|
||||
|
||||
/* A generically typed version of `VArray<T>`. */
|
||||
class GVArray {
|
||||
protected:
|
||||
@@ -86,13 +91,13 @@ class GVArray {
|
||||
|
||||
/* Returns the internally used span of the virtual array. This invokes undefined behavior is the
|
||||
* virtual array is not stored as a span internally. */
|
||||
GSpan get_span() const
|
||||
GSpan get_internal_span() const
|
||||
{
|
||||
BLI_assert(this->is_span());
|
||||
if (size_ == 0) {
|
||||
return GSpan(*type_);
|
||||
}
|
||||
return this->get_span_impl();
|
||||
return this->get_internal_span_impl();
|
||||
}
|
||||
|
||||
/* Returns true when the virtual array returns the same value for every index. */
|
||||
@@ -107,57 +112,133 @@ class GVArray {
|
||||
/* Copies the value that is used for every element into `r_value`, which is expected to point to
|
||||
* initialized memory. This invokes undefined behavior if the virtual array would not return the
|
||||
* same value for every index. */
|
||||
void get_single(void *r_value) const
|
||||
void get_internal_single(void *r_value) const
|
||||
{
|
||||
BLI_assert(this->is_single());
|
||||
if (size_ == 1) {
|
||||
this->get(0, r_value);
|
||||
}
|
||||
this->get_single_impl(r_value);
|
||||
this->get_internal_single_impl(r_value);
|
||||
}
|
||||
|
||||
/* Same as `get_single`, but `r_value` points to initialized memory. */
|
||||
/* Same as `get_internal_single`, but `r_value` points to initialized memory. */
|
||||
void get_single_to_uninitialized(void *r_value) const
|
||||
{
|
||||
type_->construct_default(r_value);
|
||||
this->get_single(r_value);
|
||||
this->get_internal_single(r_value);
|
||||
}
|
||||
|
||||
void materialize_to_uninitialized(const IndexMask mask, void *dst) const;
|
||||
|
||||
template<typename T> const VArray<T> *try_get_internal_varray() const
|
||||
{
|
||||
BLI_assert(type_->is<T>());
|
||||
return (const VArray<T> *)this->try_get_internal_varray_impl();
|
||||
}
|
||||
|
||||
/* Create a typed virtual array for this generic virtual array. */
|
||||
template<typename T> GVArray_Typed<T> typed() const
|
||||
{
|
||||
return GVArray_Typed<T>(*this);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void get_impl(const int64_t index, void *r_value) const;
|
||||
virtual void get_to_uninitialized_impl(const int64_t index, void *r_value) const = 0;
|
||||
|
||||
virtual bool is_span_impl() const;
|
||||
virtual GSpan get_span_impl() const;
|
||||
virtual GSpan get_internal_span_impl() const;
|
||||
|
||||
virtual bool is_single_impl() const;
|
||||
virtual void get_single_impl(void *UNUSED(r_value)) const;
|
||||
virtual void get_internal_single_impl(void *UNUSED(r_value)) const;
|
||||
|
||||
virtual const void *try_get_internal_varray_impl() const;
|
||||
};
|
||||
|
||||
class GVArrayForGSpan : public GVArray {
|
||||
/* Similar to GVArray, but supports changing the elements in the virtual array. */
|
||||
class GVMutableArray : public GVArray {
|
||||
public:
|
||||
GVMutableArray(const CPPType &type, const int64_t size) : GVArray(type, size)
|
||||
{
|
||||
}
|
||||
|
||||
void set_by_copy(const int64_t index, const void *value)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < size_);
|
||||
this->set_by_copy_impl(index, value);
|
||||
}
|
||||
|
||||
void set_by_move(const int64_t index, void *value)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < size_);
|
||||
this->set_by_move_impl(index, value);
|
||||
}
|
||||
|
||||
void set_by_relocate(const int64_t index, void *value)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < size_);
|
||||
this->set_by_relocate_impl(index, value);
|
||||
}
|
||||
|
||||
GMutableSpan get_internal_span()
|
||||
{
|
||||
BLI_assert(this->is_span());
|
||||
GSpan span = static_cast<const GVArray *>(this)->get_internal_span();
|
||||
return GMutableSpan(span.type(), const_cast<void *>(span.data()), span.size());
|
||||
}
|
||||
|
||||
template<typename T> VMutableArray<T> *try_get_internal_mutable_varray()
|
||||
{
|
||||
BLI_assert(type_->is<T>());
|
||||
return (VMutableArray<T> *)this->try_get_internal_mutable_varray_impl();
|
||||
}
|
||||
|
||||
/* Create a typed virtual array for this generic virtual array. */
|
||||
template<typename T> GVMutableArray_Typed<T> typed()
|
||||
{
|
||||
return GVMutableArray_Typed<T>(*this);
|
||||
}
|
||||
|
||||
void fill(const void *value);
|
||||
|
||||
protected:
|
||||
const void *data_;
|
||||
virtual void set_by_copy_impl(const int64_t index, const void *value);
|
||||
virtual void set_by_relocate_impl(const int64_t index, void *value);
|
||||
virtual void set_by_move_impl(const int64_t index, void *value) = 0;
|
||||
|
||||
virtual void *try_get_internal_mutable_varray_impl();
|
||||
};
|
||||
|
||||
class GVArray_For_GSpan : public GVArray {
|
||||
protected:
|
||||
const void *data_ = nullptr;
|
||||
const int64_t element_size_;
|
||||
|
||||
public:
|
||||
GVArrayForGSpan(const GSpan span)
|
||||
GVArray_For_GSpan(const GSpan span)
|
||||
: GVArray(span.type(), span.size()), data_(span.data()), element_size_(span.type().size())
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
GVArray_For_GSpan(const CPPType &type, const int64_t size)
|
||||
: GVArray(type, size), element_size_(type.size())
|
||||
{
|
||||
}
|
||||
|
||||
void get_impl(const int64_t index, void *r_value) const override;
|
||||
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
|
||||
|
||||
bool is_span_impl() const override;
|
||||
GSpan get_span_impl() const override;
|
||||
GSpan get_internal_span_impl() const override;
|
||||
};
|
||||
|
||||
class GVArrayForEmpty : public GVArray {
|
||||
class GVArray_For_Empty : public GVArray {
|
||||
public:
|
||||
GVArrayForEmpty(const CPPType &type) : GVArray(type, 0)
|
||||
GVArray_For_Empty(const CPPType &type) : GVArray(type, 0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -168,108 +249,618 @@ class GVArrayForEmpty : public GVArray {
|
||||
}
|
||||
};
|
||||
|
||||
class GVArrayForSingleValueRef : public GVArray {
|
||||
private:
|
||||
const void *value_;
|
||||
class GVMutableArray_For_GMutableSpan : public GVMutableArray {
|
||||
protected:
|
||||
void *data_ = nullptr;
|
||||
const int64_t element_size_;
|
||||
|
||||
public:
|
||||
GVArrayForSingleValueRef(const CPPType &type, const int64_t size, const void *value)
|
||||
GVMutableArray_For_GMutableSpan(const GMutableSpan span)
|
||||
: GVMutableArray(span.type(), span.size()),
|
||||
data_(span.data()),
|
||||
element_size_(span.type().size())
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
GVMutableArray_For_GMutableSpan(const CPPType &type, const int64_t size)
|
||||
: GVMutableArray(type, size), element_size_(type.size())
|
||||
{
|
||||
}
|
||||
|
||||
void get_impl(const int64_t index, void *r_value) const override;
|
||||
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
|
||||
|
||||
void set_by_copy_impl(const int64_t index, const void *value) override;
|
||||
void set_by_move_impl(const int64_t index, void *value) override;
|
||||
void set_by_relocate_impl(const int64_t index, void *value) override;
|
||||
|
||||
bool is_span_impl() const override;
|
||||
GSpan get_internal_span_impl() const override;
|
||||
};
|
||||
|
||||
/* Generic virtual array where each element has the same value. The value is not owned. */
|
||||
class GVArray_For_SingleValueRef : public GVArray {
|
||||
protected:
|
||||
const void *value_ = nullptr;
|
||||
|
||||
public:
|
||||
GVArray_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value)
|
||||
: GVArray(type, size), value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
GVArray_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArray(type, size)
|
||||
{
|
||||
}
|
||||
|
||||
void get_impl(const int64_t index, void *r_value) const override;
|
||||
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
|
||||
|
||||
bool is_span_impl() const override;
|
||||
GSpan get_span_impl() const override;
|
||||
GSpan get_internal_span_impl() const override;
|
||||
|
||||
bool is_single_impl() const override;
|
||||
void get_single_impl(void *r_value) const override;
|
||||
void get_internal_single_impl(void *r_value) const override;
|
||||
};
|
||||
|
||||
template<typename T> class GVArrayForVArray : public GVArray {
|
||||
private:
|
||||
const VArray<T> &array_;
|
||||
/* Same as GVArray_For_SingleValueRef, but the value is owned. */
|
||||
class GVArray_For_SingleValue : public GVArray_For_SingleValueRef {
|
||||
public:
|
||||
GVArray_For_SingleValue(const CPPType &type, const int64_t size, const void *value);
|
||||
~GVArray_For_SingleValue();
|
||||
};
|
||||
|
||||
/* 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;
|
||||
|
||||
public:
|
||||
GVArrayForVArray(const VArray<T> &array)
|
||||
: GVArray(CPPType::get<T>(), array.size()), array_(array)
|
||||
GVArray_For_VArray(const VArray<T> &varray)
|
||||
: GVArray(CPPType::get<T>(), varray.size()), varray_(&varray)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
GVArray_For_VArray(const int64_t size) : GVArray(CPPType::get<T>(), size)
|
||||
{
|
||||
}
|
||||
|
||||
void get_impl(const int64_t index, void *r_value) const override
|
||||
{
|
||||
*(T *)r_value = array_.get(index);
|
||||
*(T *)r_value = varray_->get(index);
|
||||
}
|
||||
|
||||
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
|
||||
{
|
||||
new (r_value) T(array_.get(index));
|
||||
new (r_value) T(varray_->get(index));
|
||||
}
|
||||
|
||||
bool is_span_impl() const override
|
||||
{
|
||||
return array_.is_span();
|
||||
return varray_->is_span();
|
||||
}
|
||||
|
||||
GSpan get_span_impl() const override
|
||||
GSpan get_internal_span_impl() const override
|
||||
{
|
||||
return GSpan(array_.get_span());
|
||||
return GSpan(varray_->get_internal_span());
|
||||
}
|
||||
|
||||
bool is_single_impl() const override
|
||||
{
|
||||
return array_.is_single();
|
||||
return varray_->is_single();
|
||||
}
|
||||
|
||||
void get_single_impl(void *r_value) const override
|
||||
void get_internal_single_impl(void *r_value) const override
|
||||
{
|
||||
*(T *)r_value = array_.get_single();
|
||||
*(T *)r_value = varray_->get_internal_single();
|
||||
}
|
||||
|
||||
const void *try_get_internal_varray_impl() const override
|
||||
{
|
||||
return varray_;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class VArrayForGVArray : public VArray<T> {
|
||||
private:
|
||||
const GVArray &array_;
|
||||
/* 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;
|
||||
|
||||
public:
|
||||
VArrayForGVArray(const GVArray &array) : VArray<T>(array.size()), array_(array)
|
||||
VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(&varray)
|
||||
{
|
||||
BLI_assert(array_.type().template is<T>());
|
||||
BLI_assert(varray_->type().template is<T>());
|
||||
}
|
||||
|
||||
protected:
|
||||
VArray_For_GVArray(const int64_t size) : VArray<T>(size)
|
||||
{
|
||||
}
|
||||
|
||||
T get_impl(const int64_t index) const override
|
||||
{
|
||||
T value;
|
||||
array_.get(index, &value);
|
||||
varray_->get(index, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool is_span_impl() const override
|
||||
{
|
||||
return array_.is_span();
|
||||
return varray_->is_span();
|
||||
}
|
||||
|
||||
Span<T> get_span_impl() const override
|
||||
Span<T> get_internal_span_impl() const override
|
||||
{
|
||||
return array_.get_span().template typed<T>();
|
||||
return varray_->get_internal_span().template typed<T>();
|
||||
}
|
||||
|
||||
bool is_single_impl() const override
|
||||
{
|
||||
return array_.is_single();
|
||||
return varray_->is_single();
|
||||
}
|
||||
|
||||
T get_single_impl() const override
|
||||
T get_internal_single_impl() const override
|
||||
{
|
||||
T value;
|
||||
array_.get_single(&value);
|
||||
varray_->get_internal_single(&value);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/* 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;
|
||||
|
||||
public:
|
||||
VMutableArray_For_GVMutableArray(GVMutableArray &varray)
|
||||
: VMutableArray<T>(varray.size()), varray_(&varray)
|
||||
{
|
||||
BLI_assert(varray.type().template is<T>());
|
||||
}
|
||||
|
||||
VMutableArray_For_GVMutableArray(const int64_t size) : VMutableArray<T>(size)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
T get_impl(const int64_t index) const override
|
||||
{
|
||||
T value;
|
||||
varray_->get(index, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void set_impl(const int64_t index, T value) override
|
||||
{
|
||||
varray_->set_by_relocate(index, &value);
|
||||
}
|
||||
|
||||
bool is_span_impl() const override
|
||||
{
|
||||
return varray_->is_span();
|
||||
}
|
||||
|
||||
Span<T> get_internal_span_impl() const override
|
||||
{
|
||||
return varray_->get_internal_span().template typed<T>();
|
||||
}
|
||||
|
||||
bool is_single_impl() const override
|
||||
{
|
||||
return varray_->is_single();
|
||||
}
|
||||
|
||||
T get_internal_single_impl() const override
|
||||
{
|
||||
T value;
|
||||
varray_->get_internal_single(&value);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/* 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;
|
||||
|
||||
public:
|
||||
GVMutableArray_For_VMutableArray(VMutableArray<T> &varray)
|
||||
: GVMutableArray(CPPType::get<T>(), varray.size()), varray_(&varray)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get<T>(), size)
|
||||
{
|
||||
}
|
||||
|
||||
void get_impl(const int64_t index, void *r_value) const override
|
||||
{
|
||||
*(T *)r_value = varray_->get(index);
|
||||
}
|
||||
|
||||
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
|
||||
{
|
||||
new (r_value) T(varray_->get(index));
|
||||
}
|
||||
|
||||
bool is_span_impl() const override
|
||||
{
|
||||
return varray_->is_span();
|
||||
}
|
||||
|
||||
GSpan get_internal_span_impl() const override
|
||||
{
|
||||
Span<T> span = varray_->get_internal_span();
|
||||
return span;
|
||||
}
|
||||
|
||||
bool is_single_impl() const override
|
||||
{
|
||||
return varray_->is_single();
|
||||
}
|
||||
|
||||
void get_internal_single_impl(void *r_value) const override
|
||||
{
|
||||
*(T *)r_value = varray_->get_internal_single();
|
||||
}
|
||||
|
||||
void set_by_copy_impl(const int64_t index, const void *value) override
|
||||
{
|
||||
const T &value_ = *(const T *)value;
|
||||
varray_->set(index, value_);
|
||||
}
|
||||
|
||||
void set_by_relocate_impl(const int64_t index, void *value) override
|
||||
{
|
||||
T &value_ = *(T *)value;
|
||||
varray_->set(index, std::move(value_));
|
||||
value_.~T();
|
||||
}
|
||||
|
||||
void set_by_move_impl(const int64_t index, void *value) override
|
||||
{
|
||||
T &value_ = *(T *)value;
|
||||
varray_->set(index, std::move(value_));
|
||||
}
|
||||
|
||||
const void *try_get_internal_varray_impl() const override
|
||||
{
|
||||
return (const VArray<T> *)varray_;
|
||||
}
|
||||
|
||||
void *try_get_internal_mutable_varray_impl() override
|
||||
{
|
||||
return varray_;
|
||||
}
|
||||
};
|
||||
|
||||
/* A generic version of VArray_Span. */
|
||||
class GVArray_GSpan : public GSpan {
|
||||
private:
|
||||
const GVArray &varray_;
|
||||
void *owned_data_ = nullptr;
|
||||
|
||||
public:
|
||||
GVArray_GSpan(const GVArray &varray);
|
||||
~GVArray_GSpan();
|
||||
};
|
||||
|
||||
/* A generic version of VMutableArray_Span. */
|
||||
class GVMutableArray_GSpan : public GMutableSpan {
|
||||
private:
|
||||
GVMutableArray &varray_;
|
||||
void *owned_data_ = nullptr;
|
||||
bool save_has_been_called_ = false;
|
||||
bool show_not_saved_warning_ = true;
|
||||
|
||||
public:
|
||||
GVMutableArray_GSpan(GVMutableArray &varray, bool copy_values_to_span = true);
|
||||
~GVMutableArray_GSpan();
|
||||
|
||||
void save();
|
||||
void disable_not_applied_warning();
|
||||
};
|
||||
|
||||
/* Similar to GVArray_GSpan, but the resulting span is typed. */
|
||||
template<typename T> class GVArray_Span : public Span<T> {
|
||||
private:
|
||||
GVArray_GSpan varray_gspan_;
|
||||
|
||||
public:
|
||||
GVArray_Span(const GVArray &varray) : varray_gspan_(varray)
|
||||
{
|
||||
BLI_assert(varray.type().is<T>());
|
||||
this->data_ = (const T *)varray_gspan_.data();
|
||||
this->size_ = varray_gspan_.size();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class GVArray_For_OwnedVArray : public GVArray_For_VArray<T> {
|
||||
private:
|
||||
std::unique_ptr<VArray<T>> owned_varray_;
|
||||
|
||||
public:
|
||||
/* Takes ownership of varray and passes a reference to the base class. */
|
||||
GVArray_For_OwnedVArray(std::unique_ptr<VArray<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:
|
||||
std::unique_ptr<VArray<T>> owned_varray_;
|
||||
|
||||
public:
|
||||
/* Takes ownership of varray and passes a reference to the base class. */
|
||||
VArray_For_OwnedGVArray(std::unique_ptr<GVArray> varray)
|
||||
: VArray_For_GVArray<T>(*varray), owned_varray_(std::move(varray))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class GVMutableArray_For_OwnedVMutableArray : public GVMutableArray_For_VMutableArray<T> {
|
||||
private:
|
||||
std::unique_ptr<VMutableArray<T>> owned_varray_;
|
||||
|
||||
public:
|
||||
/* Takes ownership of varray and passes a reference to the base class. */
|
||||
GVMutableArray_For_OwnedVMutableArray(std::unique_ptr<VMutableArray<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:
|
||||
std::unique_ptr<GVMutableArray> owned_varray_;
|
||||
|
||||
public:
|
||||
/* Takes ownership of varray and passes a reference to the base class. */
|
||||
VMutableArray_For_OwnedGVMutableArray(std::unique_ptr<GVMutableArray> 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>
|
||||
class GVArray_For_EmbeddedVArray : public GVArray_For_VArray<T> {
|
||||
private:
|
||||
VArrayT embedded_varray_;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
GVArray_For_EmbeddedVArray(const int64_t size, Args &&... args)
|
||||
: GVArray_For_VArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
|
||||
{
|
||||
this->varray_ = &embedded_varray_;
|
||||
}
|
||||
};
|
||||
|
||||
/* Same as GVArray_For_EmbeddedVArray, but for mutable virtual arrays. */
|
||||
template<typename T, typename VMutableArrayT>
|
||||
class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMutableArray<T> {
|
||||
private:
|
||||
VMutableArrayT embedded_varray_;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&... args)
|
||||
: GVMutableArray_For_VMutableArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
|
||||
{
|
||||
this->varray_ = &embedded_varray_;
|
||||
}
|
||||
};
|
||||
|
||||
/* Same as VArray_For_ArrayContainer, but for a generic virtual array. */
|
||||
template<typename Container, typename T = typename Container::value_type>
|
||||
class GVArray_For_ArrayContainer
|
||||
: public GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>> {
|
||||
public:
|
||||
GVArray_For_ArrayContainer(Container container)
|
||||
: GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>>(
|
||||
container.size(), std::move(container))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* Same as VArray_For_DerivedSpan, but for a generic virtual array. */
|
||||
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
|
||||
class GVArray_For_DerivedSpan
|
||||
: public GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>> {
|
||||
public:
|
||||
GVArray_For_DerivedSpan(const Span<StructT> data)
|
||||
: GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>>(
|
||||
data.size(), data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* Same as VMutableArray_For_DerivedSpan, but for a generic virtual array. */
|
||||
template<typename StructT,
|
||||
typename ElemT,
|
||||
ElemT (*GetFunc)(const StructT &),
|
||||
void (*SetFunc)(StructT &, ElemT)>
|
||||
class GVMutableArray_For_DerivedSpan
|
||||
: public GVMutableArray_For_EmbeddedVMutableArray<
|
||||
ElemT,
|
||||
VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>> {
|
||||
public:
|
||||
GVMutableArray_For_DerivedSpan(const MutableSpan<StructT> data)
|
||||
: GVMutableArray_For_EmbeddedVMutableArray<
|
||||
ElemT,
|
||||
VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(data.size(), data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* Same as VArray_For_Span, but for a generic virtual array. */
|
||||
template<typename T>
|
||||
class GVArray_For_Span : public GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>> {
|
||||
public:
|
||||
GVArray_For_Span(const Span<T> data)
|
||||
: GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>>(data.size(), data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* Same as VMutableArray_For_MutableSpan, but for a generic virtual array. */
|
||||
template<typename T>
|
||||
class GVMutableArray_For_MutableSpan
|
||||
: public GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>> {
|
||||
public:
|
||||
GVMutableArray_For_MutableSpan(const MutableSpan<T> data)
|
||||
: GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>>(data.size(),
|
||||
data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility class to create the "best" typed virtual array for a given generic virtual array.
|
||||
* In most cases we don't just want to use VArray_For_GVArray, because it adds an additional
|
||||
* indirection on element-access that can be avoided in many cases (e.g. when the virtual array is
|
||||
* just a span or single value).
|
||||
*
|
||||
* This is not a virtual array itself, but is used to get a virtual array.
|
||||
*/
|
||||
template<typename T> class GVArray_Typed {
|
||||
private:
|
||||
const VArray<T> *varray_;
|
||||
/* Of these optional virtual arrays, at most one is constructed at any time. */
|
||||
std::optional<VArray_For_Span<T>> varray_span_;
|
||||
std::optional<VArray_For_Single<T>> varray_single_;
|
||||
std::optional<VArray_For_GVArray<T>> varray_any_;
|
||||
std::unique_ptr<GVArray> owned_gvarray_;
|
||||
|
||||
public:
|
||||
explicit GVArray_Typed(const GVArray &gvarray)
|
||||
{
|
||||
BLI_assert(gvarray.type().is<T>());
|
||||
if (gvarray.is_span()) {
|
||||
const GSpan span = gvarray.get_internal_span();
|
||||
varray_span_.emplace(span.typed<T>());
|
||||
varray_ = &*varray_span_;
|
||||
}
|
||||
else if (gvarray.is_single()) {
|
||||
T single_value;
|
||||
gvarray.get_internal_single(&single_value);
|
||||
varray_single_.emplace(single_value, gvarray.size());
|
||||
varray_ = &*varray_single_;
|
||||
}
|
||||
else if (const VArray<T> *internal_varray = gvarray.try_get_internal_varray<T>()) {
|
||||
varray_ = internal_varray;
|
||||
}
|
||||
else {
|
||||
varray_any_.emplace(gvarray);
|
||||
varray_ = &*varray_any_;
|
||||
}
|
||||
}
|
||||
|
||||
/* Same as the constructor above, but also takes ownership of the passed in virtual array. */
|
||||
explicit GVArray_Typed(std::unique_ptr<GVArray> gvarray) : GVArray_Typed(*gvarray)
|
||||
{
|
||||
owned_gvarray_ = std::move(gvarray);
|
||||
}
|
||||
|
||||
const VArray<T> &operator*() const
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
const VArray<T> *operator->() const
|
||||
{
|
||||
return varray_;
|
||||
}
|
||||
|
||||
/* Support implicit cast to the typed virtual array for convenience when `varray->typed<T>()` is
|
||||
* used within an expression. */
|
||||
operator const VArray<T> &() const
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
T operator[](const int64_t index) const
|
||||
{
|
||||
return varray_->get(index);
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return varray_->size();
|
||||
}
|
||||
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return IndexRange(this->size());
|
||||
}
|
||||
};
|
||||
|
||||
/* Same as GVArray_Typed, but for mutable virtual arrays. */
|
||||
template<typename T> class GVMutableArray_Typed {
|
||||
private:
|
||||
VMutableArray<T> *varray_;
|
||||
std::optional<VMutableArray_For_MutableSpan<T>> varray_span_;
|
||||
std::optional<VMutableArray_For_GVMutableArray<T>> varray_any_;
|
||||
std::unique_ptr<GVMutableArray> owned_gvarray_;
|
||||
|
||||
public:
|
||||
explicit GVMutableArray_Typed(GVMutableArray &gvarray)
|
||||
{
|
||||
BLI_assert(gvarray.type().is<T>());
|
||||
if (gvarray.is_span()) {
|
||||
const GMutableSpan span = gvarray.get_internal_span();
|
||||
varray_span_.emplace(span.typed<T>());
|
||||
varray_ = &*varray_span_;
|
||||
}
|
||||
else if (VMutableArray<T> *internal_varray = gvarray.try_get_internal_mutable_varray<T>()) {
|
||||
varray_ = internal_varray;
|
||||
}
|
||||
else {
|
||||
varray_any_.emplace(gvarray);
|
||||
varray_ = &*varray_any_;
|
||||
}
|
||||
}
|
||||
|
||||
explicit GVMutableArray_Typed(std::unique_ptr<GVMutableArray> gvarray)
|
||||
: GVMutableArray_Typed(*gvarray)
|
||||
{
|
||||
owned_gvarray_ = std::move(gvarray);
|
||||
}
|
||||
|
||||
VMutableArray<T> &operator*()
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
VMutableArray<T> *operator->()
|
||||
{
|
||||
return varray_;
|
||||
}
|
||||
|
||||
operator VMutableArray<T> &()
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
T operator[](const int64_t index) const
|
||||
{
|
||||
return varray_->get(index);
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return varray_->size();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::fn
|
||||
|
@@ -100,13 +100,13 @@ class GVVectorArray {
|
||||
}
|
||||
};
|
||||
|
||||
class GVArrayForGVVectorArrayIndex : public GVArray {
|
||||
class GVArray_For_GVVectorArrayIndex : public GVArray {
|
||||
private:
|
||||
const GVVectorArray &vector_array_;
|
||||
const int64_t index_;
|
||||
|
||||
public:
|
||||
GVArrayForGVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index)
|
||||
GVArray_For_GVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index)
|
||||
: GVArray(vector_array.type(), vector_array.get_vector_size(index)),
|
||||
vector_array_(vector_array),
|
||||
index_(index)
|
||||
@@ -118,12 +118,12 @@ class GVArrayForGVVectorArrayIndex : public GVArray {
|
||||
void get_to_uninitialized_impl(const int64_t index_in_vector, void *r_value) const override;
|
||||
};
|
||||
|
||||
class GVVectorArrayForSingleGVArray : public GVVectorArray {
|
||||
class GVVectorArray_For_SingleGVArray : public GVVectorArray {
|
||||
private:
|
||||
const GVArray &array_;
|
||||
|
||||
public:
|
||||
GVVectorArrayForSingleGVArray(const GVArray &array, const int64_t size)
|
||||
GVVectorArray_For_SingleGVArray(const GVArray &array, const int64_t size)
|
||||
: GVVectorArray(array.type(), size), array_(array)
|
||||
{
|
||||
}
|
||||
@@ -137,12 +137,12 @@ class GVVectorArrayForSingleGVArray : public GVVectorArray {
|
||||
bool is_single_vector_impl() const override;
|
||||
};
|
||||
|
||||
class GVVectorArrayForSingleGSpan : public GVVectorArray {
|
||||
class GVVectorArray_For_SingleGSpan : public GVVectorArray {
|
||||
private:
|
||||
const GSpan span_;
|
||||
|
||||
public:
|
||||
GVVectorArrayForSingleGSpan(const GSpan span, const int64_t size)
|
||||
GVVectorArray_For_SingleGSpan(const GSpan span, const int64_t size)
|
||||
: GVVectorArray(span.type(), size), span_(span)
|
||||
{
|
||||
}
|
||||
@@ -156,12 +156,12 @@ class GVVectorArrayForSingleGSpan : public GVVectorArray {
|
||||
bool is_single_vector_impl() const override;
|
||||
};
|
||||
|
||||
template<typename T> class VVectorArrayForGVVectorArray : public VVectorArray<T> {
|
||||
template<typename T> class VVectorArray_For_GVVectorArray : public VVectorArray<T> {
|
||||
private:
|
||||
const GVVectorArray &vector_array_;
|
||||
|
||||
public:
|
||||
VVectorArrayForGVVectorArray(const GVVectorArray &vector_array)
|
||||
VVectorArray_For_GVVectorArray(const GVVectorArray &vector_array)
|
||||
: VVectorArray<T>(vector_array.size()), vector_array_(vector_array)
|
||||
{
|
||||
}
|
||||
|
@@ -55,13 +55,13 @@ class MFParamsBuilder {
|
||||
|
||||
template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "")
|
||||
{
|
||||
this->add_readonly_single_input(scope_.construct<GVArrayForSingleValueRef>(
|
||||
this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>(
|
||||
__func__, CPPType::get<T>(), min_array_size_, value),
|
||||
expected_name);
|
||||
}
|
||||
void add_readonly_single_input(const GSpan span, StringRef expected_name = "")
|
||||
{
|
||||
this->add_readonly_single_input(scope_.construct<GVArrayForGSpan>(__func__, span),
|
||||
this->add_readonly_single_input(scope_.construct<GVArray_For_GSpan>(__func__, span),
|
||||
expected_name);
|
||||
}
|
||||
void add_readonly_single_input(const GVArray &ref, StringRef expected_name = "")
|
||||
@@ -74,7 +74,7 @@ class MFParamsBuilder {
|
||||
void add_readonly_vector_input(const GVectorArray &vector_array, StringRef expected_name = "")
|
||||
{
|
||||
this->add_readonly_vector_input(
|
||||
scope_.construct<GVVectorArrayForGVectorArray>(__func__, vector_array), expected_name);
|
||||
scope_.construct<GVVectorArray_For_GVectorArray>(__func__, vector_array), expected_name);
|
||||
}
|
||||
void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "")
|
||||
{
|
||||
@@ -177,7 +177,7 @@ class MFParams {
|
||||
template<typename T> const VArray<T> &readonly_single_input(int param_index, StringRef name = "")
|
||||
{
|
||||
const GVArray &array = this->readonly_single_input(param_index, name);
|
||||
return builder_->scope_.construct<VArrayForGVArray<T>>(__func__, array);
|
||||
return builder_->scope_.construct<VArray_For_GVArray<T>>(__func__, array);
|
||||
}
|
||||
const GVArray &readonly_single_input(int param_index, StringRef name = "")
|
||||
{
|
||||
@@ -202,7 +202,7 @@ class MFParams {
|
||||
const VVectorArray<T> &readonly_vector_input(int param_index, StringRef name = "")
|
||||
{
|
||||
const GVVectorArray &vector_array = this->readonly_vector_input(param_index, name);
|
||||
return builder_->scope_.construct<VVectorArrayForGVVectorArray<T>>(__func__, vector_array);
|
||||
return builder_->scope_.construct<VVectorArray_For_GVVectorArray<T>>(__func__, vector_array);
|
||||
}
|
||||
const GVVectorArray &readonly_vector_input(int param_index, StringRef name = "")
|
||||
{
|
||||
|
@@ -60,21 +60,21 @@ void GVectorArray::extend(const int64_t index, const GVArray &values)
|
||||
|
||||
void GVectorArray::extend(const int64_t index, const GSpan values)
|
||||
{
|
||||
GVArrayForGSpan varray{values};
|
||||
GVArray_For_GSpan varray{values};
|
||||
this->extend(index, varray);
|
||||
}
|
||||
|
||||
void GVectorArray::extend(IndexMask mask, const GVVectorArray &values)
|
||||
{
|
||||
for (const int i : mask) {
|
||||
GVArrayForGVVectorArrayIndex array{values, i};
|
||||
GVArray_For_GVVectorArrayIndex array{values, i};
|
||||
this->extend(i, array);
|
||||
}
|
||||
}
|
||||
|
||||
void GVectorArray::extend(IndexMask mask, const GVectorArray &values)
|
||||
{
|
||||
GVVectorArrayForGVectorArray virtual_values{values};
|
||||
GVVectorArray_For_GVectorArray virtual_values{values};
|
||||
this->extend(mask, virtual_values);
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,10 @@
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* GVArray.
|
||||
*/
|
||||
|
||||
void GVArray::materialize_to_uninitialized(const IndexMask mask, void *dst) const
|
||||
{
|
||||
for (const int64_t i : mask) {
|
||||
@@ -37,7 +41,7 @@ bool GVArray::is_span_impl() const
|
||||
return false;
|
||||
}
|
||||
|
||||
GSpan GVArray::get_span_impl() const
|
||||
GSpan GVArray::get_internal_span_impl() const
|
||||
{
|
||||
BLI_assert(false);
|
||||
return GSpan(*type_);
|
||||
@@ -48,60 +52,246 @@ bool GVArray::is_single_impl() const
|
||||
return false;
|
||||
}
|
||||
|
||||
void GVArray::get_single_impl(void *UNUSED(r_value)) const
|
||||
void GVArray::get_internal_single_impl(void *UNUSED(r_value)) const
|
||||
{
|
||||
BLI_assert(false);
|
||||
}
|
||||
|
||||
void GVArrayForGSpan::get_impl(const int64_t index, void *r_value) const
|
||||
const void *GVArray::try_get_internal_varray_impl() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* GVMutableArray.
|
||||
*/
|
||||
|
||||
void GVMutableArray::set_by_copy_impl(const int64_t index, const void *value)
|
||||
{
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer);
|
||||
type_->copy_to_uninitialized(value, buffer);
|
||||
this->set_by_move_impl(index, buffer);
|
||||
type_->destruct(buffer);
|
||||
}
|
||||
|
||||
void GVMutableArray::set_by_relocate_impl(const int64_t index, void *value)
|
||||
{
|
||||
this->set_by_move_impl(index, value);
|
||||
type_->destruct(value);
|
||||
}
|
||||
|
||||
void *GVMutableArray::try_get_internal_mutable_varray_impl()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GVMutableArray::fill(const void *value)
|
||||
{
|
||||
if (this->is_span()) {
|
||||
const GMutableSpan span = this->get_internal_span();
|
||||
type_->fill_initialized(value, span.data(), size_);
|
||||
}
|
||||
else {
|
||||
for (int64_t i : IndexRange(size_)) {
|
||||
this->set_by_copy(i, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* GVArray_For_GSpan.
|
||||
*/
|
||||
|
||||
void GVArray_For_GSpan::get_impl(const int64_t index, void *r_value) const
|
||||
{
|
||||
type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
|
||||
}
|
||||
|
||||
void GVArrayForGSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const
|
||||
void GVArray_For_GSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const
|
||||
{
|
||||
type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
|
||||
}
|
||||
|
||||
bool GVArrayForGSpan::is_span_impl() const
|
||||
bool GVArray_For_GSpan::is_span_impl() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
GSpan GVArrayForGSpan::get_span_impl() const
|
||||
GSpan GVArray_For_GSpan::get_internal_span_impl() const
|
||||
{
|
||||
return GSpan(*type_, data_, size_);
|
||||
}
|
||||
|
||||
void GVArrayForSingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const
|
||||
/* --------------------------------------------------------------------
|
||||
* GVMutableArray_For_GMutableSpan.
|
||||
*/
|
||||
|
||||
void GVMutableArray_For_GMutableSpan::get_impl(const int64_t index, void *r_value) const
|
||||
{
|
||||
type_->copy_to_initialized(value_, r_value);
|
||||
type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
|
||||
}
|
||||
|
||||
void GVArrayForSingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index),
|
||||
void *r_value) const
|
||||
void GVMutableArray_For_GMutableSpan::get_to_uninitialized_impl(const int64_t index,
|
||||
void *r_value) const
|
||||
{
|
||||
type_->copy_to_uninitialized(value_, r_value);
|
||||
type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
|
||||
}
|
||||
|
||||
bool GVArrayForSingleValueRef::is_span_impl() const
|
||||
void GVMutableArray_For_GMutableSpan::set_by_copy_impl(const int64_t index, const void *value)
|
||||
{
|
||||
return size_ == 1;
|
||||
type_->copy_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index));
|
||||
}
|
||||
|
||||
GSpan GVArrayForSingleValueRef::get_span_impl() const
|
||||
void GVMutableArray_For_GMutableSpan::set_by_move_impl(const int64_t index, void *value)
|
||||
{
|
||||
return GSpan{*type_, value_, 1};
|
||||
type_->move_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index));
|
||||
}
|
||||
|
||||
bool GVArrayForSingleValueRef::is_single_impl() const
|
||||
void GVMutableArray_For_GMutableSpan::set_by_relocate_impl(const int64_t index, void *value)
|
||||
{
|
||||
type_->relocate_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index));
|
||||
}
|
||||
|
||||
bool GVMutableArray_For_GMutableSpan::is_span_impl() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void GVArrayForSingleValueRef::get_single_impl(void *r_value) const
|
||||
GSpan GVMutableArray_For_GMutableSpan::get_internal_span_impl() const
|
||||
{
|
||||
return GSpan(*type_, data_, size_);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* GVArray_For_SingleValueRef.
|
||||
*/
|
||||
|
||||
void GVArray_For_SingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const
|
||||
{
|
||||
type_->copy_to_initialized(value_, r_value);
|
||||
}
|
||||
|
||||
void GVArray_For_SingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index),
|
||||
void *r_value) const
|
||||
{
|
||||
type_->copy_to_uninitialized(value_, r_value);
|
||||
}
|
||||
|
||||
bool GVArray_For_SingleValueRef::is_span_impl() const
|
||||
{
|
||||
return size_ == 1;
|
||||
}
|
||||
|
||||
GSpan GVArray_For_SingleValueRef::get_internal_span_impl() const
|
||||
{
|
||||
return GSpan{*type_, value_, 1};
|
||||
}
|
||||
|
||||
bool GVArray_For_SingleValueRef::is_single_impl() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void GVArray_For_SingleValueRef::get_internal_single_impl(void *r_value) const
|
||||
{
|
||||
type_->copy_to_initialized(value_, r_value);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* GVArray_For_SingleValue.
|
||||
*/
|
||||
|
||||
GVArray_For_SingleValue::GVArray_For_SingleValue(const CPPType &type,
|
||||
const int64_t size,
|
||||
const void *value)
|
||||
: GVArray_For_SingleValueRef(type, size)
|
||||
{
|
||||
value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
|
||||
type.copy_to_uninitialized(value, (void *)value_);
|
||||
}
|
||||
|
||||
GVArray_For_SingleValue::~GVArray_For_SingleValue()
|
||||
{
|
||||
type_->destruct((void *)value_);
|
||||
MEM_freeN((void *)value_);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* GVArray_GSpan.
|
||||
*/
|
||||
|
||||
GVArray_GSpan::GVArray_GSpan(const GVArray &varray) : GSpan(varray.type()), varray_(varray)
|
||||
{
|
||||
size_ = varray_.size();
|
||||
if (varray_.is_span()) {
|
||||
data_ = varray_.get_internal_span().data();
|
||||
}
|
||||
else {
|
||||
owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__);
|
||||
varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_);
|
||||
data_ = owned_data_;
|
||||
}
|
||||
}
|
||||
|
||||
GVArray_GSpan::~GVArray_GSpan()
|
||||
{
|
||||
if (owned_data_ != nullptr) {
|
||||
type_->destruct_n(owned_data_, size_);
|
||||
MEM_freeN(owned_data_);
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* GVMutableArray_GSpan.
|
||||
*/
|
||||
|
||||
GVMutableArray_GSpan::GVMutableArray_GSpan(GVMutableArray &varray, const bool copy_values_to_span)
|
||||
: GMutableSpan(varray.type()), varray_(varray)
|
||||
{
|
||||
size_ = varray_.size();
|
||||
if (varray_.is_span()) {
|
||||
data_ = varray_.get_internal_span().data();
|
||||
}
|
||||
else {
|
||||
owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__);
|
||||
if (copy_values_to_span) {
|
||||
varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_);
|
||||
}
|
||||
else {
|
||||
type_->construct_default_n(owned_data_, size_);
|
||||
}
|
||||
data_ = owned_data_;
|
||||
}
|
||||
}
|
||||
|
||||
GVMutableArray_GSpan::~GVMutableArray_GSpan()
|
||||
{
|
||||
if (show_not_saved_warning_) {
|
||||
if (!save_has_been_called_) {
|
||||
std::cout << "Warning: Call `apply()` to make sure that changes persist in all cases.\n";
|
||||
}
|
||||
}
|
||||
if (owned_data_ != nullptr) {
|
||||
type_->destruct_n(owned_data_, size_);
|
||||
MEM_freeN(owned_data_);
|
||||
}
|
||||
}
|
||||
|
||||
void GVMutableArray_GSpan::save()
|
||||
{
|
||||
save_has_been_called_ = true;
|
||||
if (data_ != owned_data_) {
|
||||
return;
|
||||
}
|
||||
const int64_t element_size = type_->size();
|
||||
for (int64_t i : IndexRange(size_)) {
|
||||
varray_.set_by_copy(i, POINTER_OFFSET(owned_data_, element_size * i));
|
||||
}
|
||||
}
|
||||
|
||||
void GVMutableArray_GSpan::disable_not_applied_warning()
|
||||
{
|
||||
show_not_saved_warning_ = false;
|
||||
}
|
||||
|
||||
} // namespace blender::fn
|
||||
|
@@ -18,48 +18,48 @@
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
void GVArrayForGVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const
|
||||
void GVArray_For_GVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const
|
||||
{
|
||||
vector_array_.get_vector_element(index_, index_in_vector, r_value);
|
||||
}
|
||||
|
||||
void GVArrayForGVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector,
|
||||
void *r_value) const
|
||||
void GVArray_For_GVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector,
|
||||
void *r_value) const
|
||||
{
|
||||
type_->construct_default(r_value);
|
||||
vector_array_.get_vector_element(index_, index_in_vector, r_value);
|
||||
}
|
||||
|
||||
int64_t GVVectorArrayForSingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const
|
||||
int64_t GVVectorArray_For_SingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const
|
||||
{
|
||||
return array_.size();
|
||||
}
|
||||
|
||||
void GVVectorArrayForSingleGVArray::get_vector_element_impl(const int64_t UNUSED(index),
|
||||
const int64_t index_in_vector,
|
||||
void *r_value) const
|
||||
void GVVectorArray_For_SingleGVArray::get_vector_element_impl(const int64_t UNUSED(index),
|
||||
const int64_t index_in_vector,
|
||||
void *r_value) const
|
||||
{
|
||||
array_.get(index_in_vector, r_value);
|
||||
}
|
||||
|
||||
bool GVVectorArrayForSingleGVArray::is_single_vector_impl() const
|
||||
bool GVVectorArray_For_SingleGVArray::is_single_vector_impl() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t GVVectorArrayForSingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const
|
||||
int64_t GVVectorArray_For_SingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const
|
||||
{
|
||||
return span_.size();
|
||||
}
|
||||
|
||||
void GVVectorArrayForSingleGSpan::get_vector_element_impl(const int64_t UNUSED(index),
|
||||
const int64_t index_in_vector,
|
||||
void *r_value) const
|
||||
void GVVectorArray_For_SingleGSpan::get_vector_element_impl(const int64_t UNUSED(index),
|
||||
const int64_t index_in_vector,
|
||||
void *r_value) const
|
||||
{
|
||||
type_->copy_to_initialized(span_[index_in_vector], r_value);
|
||||
}
|
||||
|
||||
bool GVVectorArrayForSingleGSpan::is_single_vector_impl() const
|
||||
bool GVVectorArray_For_SingleGSpan::is_single_vector_impl() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -974,11 +974,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS
|
||||
if (any_value->type == ValueType::OwnSingle) {
|
||||
OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
|
||||
if (value->is_single_allocated) {
|
||||
return scope.construct<GVArrayForSingleValueRef>(
|
||||
return scope.construct<GVArray_For_SingleValueRef>(
|
||||
__func__, value->span.type(), min_array_size_, value->span.data());
|
||||
}
|
||||
|
||||
return scope.construct<GVArrayForGSpan>(__func__, value->span);
|
||||
return scope.construct<GVArray_For_GSpan>(__func__, value->span);
|
||||
}
|
||||
if (any_value->type == ValueType::InputSingle) {
|
||||
InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
|
||||
@@ -987,11 +987,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS
|
||||
if (any_value->type == ValueType::OutputSingle) {
|
||||
OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
|
||||
BLI_assert(value->is_computed);
|
||||
return scope.construct<GVArrayForGSpan>(__func__, value->span);
|
||||
return scope.construct<GVArray_For_GSpan>(__func__, value->span);
|
||||
}
|
||||
|
||||
BLI_assert(false);
|
||||
return scope.construct<GVArrayForEmpty>(__func__, CPPType::get<float>());
|
||||
return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
|
||||
}
|
||||
|
||||
const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket,
|
||||
@@ -1004,7 +1004,7 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu
|
||||
if (any_value->type == ValueType::OwnSingle) {
|
||||
OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
|
||||
BLI_assert(value->span.size() == 1);
|
||||
return scope.construct<GVArrayForGSpan>(__func__, value->span);
|
||||
return scope.construct<GVArray_For_GSpan>(__func__, value->span);
|
||||
}
|
||||
if (any_value->type == ValueType::InputSingle) {
|
||||
InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
|
||||
@@ -1015,11 +1015,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu
|
||||
OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
|
||||
BLI_assert(value->is_computed);
|
||||
BLI_assert(value->span.size() == 1);
|
||||
return scope.construct<GVArrayForGSpan>(__func__, value->span);
|
||||
return scope.construct<GVArray_For_GSpan>(__func__, value->span);
|
||||
}
|
||||
|
||||
BLI_assert(false);
|
||||
return scope.construct<GVArrayForEmpty>(__func__, CPPType::get<float>());
|
||||
return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
|
||||
}
|
||||
|
||||
const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
|
||||
@@ -1033,10 +1033,10 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
|
||||
OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
|
||||
if (value->vector_array->size() == 1) {
|
||||
GSpan span = (*value->vector_array)[0];
|
||||
return scope.construct<GVVectorArrayForSingleGSpan>(__func__, span, min_array_size_);
|
||||
return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_);
|
||||
}
|
||||
|
||||
return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
|
||||
return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
|
||||
}
|
||||
if (any_value->type == ValueType::InputVector) {
|
||||
InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
|
||||
@@ -1044,11 +1044,11 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
|
||||
}
|
||||
if (any_value->type == ValueType::OutputVector) {
|
||||
OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
|
||||
return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
|
||||
return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
|
||||
}
|
||||
|
||||
BLI_assert(false);
|
||||
return scope.construct<GVVectorArrayForSingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
|
||||
return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
|
||||
}
|
||||
|
||||
const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
|
||||
@@ -1061,7 +1061,7 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
|
||||
if (any_value->type == ValueType::OwnVector) {
|
||||
OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
|
||||
BLI_assert(value->vector_array->size() == 1);
|
||||
return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
|
||||
return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
|
||||
}
|
||||
if (any_value->type == ValueType::InputVector) {
|
||||
InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
|
||||
@@ -1071,11 +1071,11 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
|
||||
if (any_value->type == ValueType::OutputVector) {
|
||||
OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
|
||||
BLI_assert(value->vector_array->size() == 1);
|
||||
return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
|
||||
return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
|
||||
}
|
||||
|
||||
BLI_assert(false);
|
||||
return scope.construct<GVVectorArrayForSingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
|
||||
return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@@ -223,7 +223,7 @@ TEST(multi_function_network, Test2)
|
||||
Array<int> output_value_2(5, -1);
|
||||
|
||||
MFParamsBuilder params(network_fn, 5);
|
||||
GVVectorArrayForSingleGSpan inputs_1{input_value_1.as_span(), 5};
|
||||
GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5};
|
||||
params.add_readonly_vector_input(inputs_1);
|
||||
params.add_readonly_single_input(&input_value_2);
|
||||
params.add_vector_output(output_value_1);
|
||||
|
@@ -303,7 +303,7 @@ void GpencilIO::prepare_stroke_export_colors(Object *ob, bGPDstroke *gps)
|
||||
|
||||
/* Stroke color. */
|
||||
copy_v4_v4(stroke_color_, gp_style->stroke_rgba);
|
||||
avg_opacity_ = 0;
|
||||
avg_opacity_ = 0.0f;
|
||||
/* Get average vertex color and apply. */
|
||||
float avg_color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
for (const bGPDspoint &pt : Span(gps->points, gps->totpoints)) {
|
||||
|
@@ -69,7 +69,6 @@ GpencilExporterPDF::GpencilExporterPDF(const char *filename, const GpencilIOPara
|
||||
|
||||
pdf_ = nullptr;
|
||||
page_ = nullptr;
|
||||
gstate_ = nullptr;
|
||||
}
|
||||
|
||||
bool GpencilExporterPDF::new_document()
|
||||
@@ -169,16 +168,29 @@ void GpencilExporterPDF::export_gpencil_layers()
|
||||
if (!ED_gpencil_stroke_material_visible(ob, gps)) {
|
||||
continue;
|
||||
}
|
||||
/* Duplicate the stroke to apply any layer thickness change. */
|
||||
bGPDstroke *gps_duplicate = BKE_gpencil_stroke_duplicate(gps, true, false);
|
||||
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob,
|
||||
gps_duplicate->mat_nr + 1);
|
||||
/* Skip invisible lines. */
|
||||
prepare_stroke_export_colors(ob, gps);
|
||||
const float fill_opacity = fill_color_[3] * gpl->opacity;
|
||||
const float stroke_opacity = stroke_color_[3] * stroke_average_opacity_get() *
|
||||
gpl->opacity;
|
||||
if ((fill_opacity < GPENCIL_ALPHA_OPACITY_THRESH) &&
|
||||
(stroke_opacity < GPENCIL_ALPHA_OPACITY_THRESH)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
|
||||
const bool is_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) &&
|
||||
(gp_style->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH));
|
||||
(gp_style->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) &&
|
||||
(stroke_opacity > GPENCIL_ALPHA_OPACITY_THRESH));
|
||||
const bool is_fill = ((gp_style->flag & GP_MATERIAL_FILL_SHOW) &&
|
||||
(gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH));
|
||||
prepare_stroke_export_colors(ob, gps_duplicate);
|
||||
|
||||
if ((!is_stroke) && (!is_fill)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Duplicate the stroke to apply any layer thickness change. */
|
||||
bGPDstroke *gps_duplicate = BKE_gpencil_stroke_duplicate(gps, true, false);
|
||||
|
||||
/* Apply layer thickness change. */
|
||||
gps_duplicate->thickness += gpl->line_change;
|
||||
@@ -283,29 +295,35 @@ void GpencilExporterPDF::color_set(bGPDlayer *gpl, const bool do_fill)
|
||||
{
|
||||
const float fill_opacity = fill_color_[3] * gpl->opacity;
|
||||
const float stroke_opacity = stroke_color_[3] * stroke_average_opacity_get() * gpl->opacity;
|
||||
const bool need_state = (do_fill && fill_opacity < 1.0f) || (stroke_opacity < 1.0f);
|
||||
|
||||
HPDF_Page_GSave(page_);
|
||||
gstate_ = HPDF_CreateExtGState(pdf_);
|
||||
HPDF_ExtGState gstate = (need_state) ? HPDF_CreateExtGState(pdf_) : nullptr;
|
||||
|
||||
float col[3];
|
||||
if (do_fill) {
|
||||
interp_v3_v3v3(col, fill_color_, gpl->tintcolor, gpl->tintcolor[3]);
|
||||
linearrgb_to_srgb_v3_v3(col, col);
|
||||
CLAMP3(col, 0.0f, 1.0f);
|
||||
|
||||
HPDF_ExtGState_SetAlphaFill(gstate_, clamp_f(fill_opacity, 0.0f, 1.0f));
|
||||
HPDF_Page_SetRGBFill(page_, col[0], col[1], col[2]);
|
||||
if (gstate) {
|
||||
HPDF_ExtGState_SetAlphaFill(gstate, clamp_f(fill_opacity, 0.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
else {
|
||||
interp_v3_v3v3(col, stroke_color_, gpl->tintcolor, gpl->tintcolor[3]);
|
||||
linearrgb_to_srgb_v3_v3(col, col);
|
||||
CLAMP3(col, 0.0f, 1.0f);
|
||||
|
||||
HPDF_ExtGState_SetAlphaFill(gstate_, clamp_f(stroke_opacity, 0.0f, 1.0f));
|
||||
HPDF_ExtGState_SetAlphaStroke(gstate_, clamp_f(stroke_opacity, 0.0f, 1.0f));
|
||||
HPDF_Page_SetRGBFill(page_, col[0], col[1], col[2]);
|
||||
HPDF_Page_SetRGBStroke(page_, col[0], col[1], col[2]);
|
||||
if (gstate) {
|
||||
HPDF_ExtGState_SetAlphaFill(gstate, clamp_f(stroke_opacity, 0.0f, 1.0f));
|
||||
HPDF_ExtGState_SetAlphaStroke(gstate, clamp_f(stroke_opacity, 0.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
if (gstate) {
|
||||
HPDF_Page_SetExtGState(page_, gstate);
|
||||
}
|
||||
HPDF_Page_SetExtGState(page_, gstate_);
|
||||
}
|
||||
} // namespace blender::io::gpencil
|
||||
|
@@ -49,8 +49,6 @@ class GpencilExporterPDF : public GpencilExporter {
|
||||
HPDF_Doc pdf_;
|
||||
/* PDF page. */
|
||||
HPDF_Page page_;
|
||||
/* State. */
|
||||
HPDF_ExtGState gstate_;
|
||||
|
||||
bool create_document();
|
||||
bool add_page();
|
||||
|
@@ -278,8 +278,14 @@ typedef struct wmWindow {
|
||||
char event_queue_check_click;
|
||||
/** Enable when #KM_PRESS events are not handled (keyboard/mouse-buttons only). */
|
||||
char event_queue_check_drag;
|
||||
/**
|
||||
* Enable when the drag was handled,
|
||||
* to avoid mouse-motion continually triggering drag events which are not handled
|
||||
* but add overhead to gizmo handling (for example), see T87511.
|
||||
*/
|
||||
char event_queue_check_drag_handled;
|
||||
|
||||
char _pad0[2];
|
||||
char _pad0[1];
|
||||
|
||||
/** Internal, lock pie creation from this event until released. */
|
||||
short pie_event_type_lock;
|
||||
@@ -374,7 +380,12 @@ typedef struct wmKeyMapItem {
|
||||
/** Unique identifier. Positive for kmi that override builtins, negative otherwise. */
|
||||
short id;
|
||||
char _pad[2];
|
||||
/** Rna pointer to access properties. */
|
||||
/**
|
||||
* RNA pointer to access properties.
|
||||
*
|
||||
* \note The `ptr.owner_id` value must be NULL, as a signal not to use the context
|
||||
* when running property callbacks such as ENUM item functions.
|
||||
*/
|
||||
struct PointerRNA *ptr;
|
||||
} wmKeyMapItem;
|
||||
|
||||
|
@@ -636,6 +636,12 @@ typedef enum StructFlag {
|
||||
STRUCT_PUBLIC_NAMESPACE = (1 << 9),
|
||||
/** All subtypes are added too. */
|
||||
STRUCT_PUBLIC_NAMESPACE_INHERIT = (1 << 10),
|
||||
/**
|
||||
* When the #PointerRNA.owner_id is NULL, this signifies the property should be accessed
|
||||
* without any context (the key-map UI and import/export for example).
|
||||
* So accessing the property should not read from the current context to derive values/limits.
|
||||
*/
|
||||
STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID = (1 << 11),
|
||||
} StructFlag;
|
||||
|
||||
typedef int (*StructValidateFunc)(struct PointerRNA *ptr, void *data, int *have_function);
|
||||
|
@@ -1623,35 +1623,35 @@ void RNA_property_enum_items_ex(bContext *C,
|
||||
|
||||
*r_free = false;
|
||||
|
||||
if (!use_static && eprop->item_fn && (C != NULL || (prop->flag & PROP_ENUM_NO_CONTEXT))) {
|
||||
const EnumPropertyItem *item;
|
||||
if (!use_static && (eprop->item_fn != NULL)) {
|
||||
const bool no_context = (prop->flag & PROP_ENUM_NO_CONTEXT) ||
|
||||
((ptr->type->flag & STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID) &&
|
||||
(ptr->owner_id == NULL));
|
||||
if (C != NULL || no_context) {
|
||||
const EnumPropertyItem *item;
|
||||
|
||||
if (prop->flag & PROP_ENUM_NO_CONTEXT) {
|
||||
item = eprop->item_fn(NULL, ptr, prop, r_free);
|
||||
}
|
||||
else {
|
||||
item = eprop->item_fn(C, ptr, prop, r_free);
|
||||
}
|
||||
item = eprop->item_fn(no_context ? NULL : C, ptr, prop, r_free);
|
||||
|
||||
/* any callbacks returning NULL should be fixed */
|
||||
BLI_assert(item != NULL);
|
||||
/* any callbacks returning NULL should be fixed */
|
||||
BLI_assert(item != NULL);
|
||||
|
||||
if (r_totitem) {
|
||||
int tot;
|
||||
for (tot = 0; item[tot].identifier; tot++) {
|
||||
/* pass */
|
||||
if (r_totitem) {
|
||||
int tot;
|
||||
for (tot = 0; item[tot].identifier; tot++) {
|
||||
/* pass */
|
||||
}
|
||||
*r_totitem = tot;
|
||||
}
|
||||
*r_totitem = tot;
|
||||
}
|
||||
|
||||
*r_item = item;
|
||||
}
|
||||
else {
|
||||
*r_item = eprop->item;
|
||||
if (r_totitem) {
|
||||
*r_totitem = eprop->totitem;
|
||||
*r_item = item;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*r_item = eprop->item;
|
||||
if (r_totitem) {
|
||||
*r_totitem = eprop->totitem;
|
||||
}
|
||||
}
|
||||
|
||||
void RNA_property_enum_items(bContext *C,
|
||||
@@ -1753,43 +1753,43 @@ void RNA_property_enum_items_gettexted_all(bContext *C,
|
||||
*r_totitem = eprop->totitem;
|
||||
}
|
||||
|
||||
if (eprop->item_fn && (C != NULL || (prop->flag & PROP_ENUM_NO_CONTEXT))) {
|
||||
const EnumPropertyItem *item;
|
||||
int i;
|
||||
bool free = false;
|
||||
if (eprop->item_fn != NULL) {
|
||||
const bool no_context = (prop->flag & PROP_ENUM_NO_CONTEXT) ||
|
||||
((ptr->type->flag & STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID) &&
|
||||
(ptr->owner_id == NULL));
|
||||
if (C != NULL || no_context) {
|
||||
const EnumPropertyItem *item;
|
||||
int i;
|
||||
bool free = false;
|
||||
|
||||
if (prop->flag & PROP_ENUM_NO_CONTEXT) {
|
||||
item = eprop->item_fn(NULL, ptr, prop, &free);
|
||||
}
|
||||
else {
|
||||
item = eprop->item_fn(C, ptr, prop, &free);
|
||||
}
|
||||
item = eprop->item_fn(no_context ? NULL : NULL, ptr, prop, &free);
|
||||
|
||||
/* any callbacks returning NULL should be fixed */
|
||||
BLI_assert(item != NULL);
|
||||
/* any callbacks returning NULL should be fixed */
|
||||
BLI_assert(item != NULL);
|
||||
|
||||
for (i = 0; i < eprop->totitem; i++) {
|
||||
bool exists = false;
|
||||
int i_fixed;
|
||||
for (i = 0; i < eprop->totitem; i++) {
|
||||
bool exists = false;
|
||||
int i_fixed;
|
||||
|
||||
/* Items that do not exist on list are returned,
|
||||
* but have their names/identifiers NULL'ed out. */
|
||||
for (i_fixed = 0; item[i_fixed].identifier; i_fixed++) {
|
||||
if (STREQ(item[i_fixed].identifier, item_array[i].identifier)) {
|
||||
exists = true;
|
||||
break;
|
||||
/* Items that do not exist on list are returned,
|
||||
* but have their names/identifiers NULL'ed out. */
|
||||
for (i_fixed = 0; item[i_fixed].identifier; i_fixed++) {
|
||||
if (STREQ(item[i_fixed].identifier, item_array[i].identifier)) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
item_array[i].name = NULL;
|
||||
item_array[i].identifier = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
item_array[i].name = NULL;
|
||||
item_array[i].identifier = "";
|
||||
if (free) {
|
||||
MEM_freeN((void *)item);
|
||||
}
|
||||
}
|
||||
|
||||
if (free) {
|
||||
MEM_freeN((void *)item);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_INTERNATIONAL
|
||||
|
@@ -2197,7 +2197,10 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
|
||||
|
||||
prop = RNA_def_property(srna, "use_mask_layer", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_USE_MASK);
|
||||
RNA_def_property_ui_text(prop, "Mask Layer", "Mask pixels from underlying layers drawing");
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Use Mask",
|
||||
"The visibility of drawings on this layer is affected by the layers in its masks list");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_lights", PROP_BOOLEAN, PROP_NONE);
|
||||
|
@@ -2483,6 +2483,10 @@ const EnumPropertyItem *rna_TransformOrientation_itemf(bContext *C,
|
||||
PropertyRNA *UNUSED(prop),
|
||||
bool *r_free)
|
||||
{
|
||||
if (C == NULL) {
|
||||
return rna_enum_transform_orientation_items;
|
||||
}
|
||||
|
||||
Scene *scene;
|
||||
if (ptr->owner_id && (GS(ptr->owner_id->name) == ID_SCE)) {
|
||||
scene = (Scene *)ptr->owner_id;
|
||||
@@ -2493,11 +2497,15 @@ const EnumPropertyItem *rna_TransformOrientation_itemf(bContext *C,
|
||||
return rna_TransformOrientation_impl_itemf(scene, false, r_free);
|
||||
}
|
||||
|
||||
const EnumPropertyItem *rna_TransformOrientation_with_scene_itemf(bContext *UNUSED(C),
|
||||
const EnumPropertyItem *rna_TransformOrientation_with_scene_itemf(bContext *C,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *UNUSED(prop),
|
||||
bool *r_free)
|
||||
{
|
||||
if (C == NULL) {
|
||||
return rna_enum_transform_orientation_items;
|
||||
}
|
||||
|
||||
Scene *scene = (Scene *)ptr->owner_id;
|
||||
TransformOrientationSlot *orient_slot = ptr->data;
|
||||
bool include_default = (orient_slot != &scene->orientation_slots[SCE_ORIENT_DEFAULT]);
|
||||
|
@@ -779,7 +779,7 @@ static const EnumPropertyItem *rna_userdef_audio_device_itemf(bContext *UNUSED(C
|
||||
RNA_enum_item_add(&item, &totitem, &new_item);
|
||||
}
|
||||
|
||||
# ifndef NDEBUG
|
||||
# if !defined(NDEBUG) || !defined(WITH_AUDASPACE)
|
||||
if (i == 0) {
|
||||
EnumPropertyItem new_item = {i, "SOUND_NONE", 0, "No Sound", ""};
|
||||
RNA_enum_item_add(&item, &totitem, &new_item);
|
||||
|
@@ -880,6 +880,7 @@ static PointerRNA rna_KeyMapItem_properties_get(PointerRNA *ptr)
|
||||
wmKeyMapItem *kmi = ptr->data;
|
||||
|
||||
if (kmi->ptr) {
|
||||
BLI_assert(kmi->ptr->owner_id == NULL);
|
||||
return *(kmi->ptr);
|
||||
}
|
||||
|
||||
@@ -1974,7 +1975,7 @@ static void rna_def_operator(BlenderRNA *brna)
|
||||
RNA_def_struct_refine_func(srna, "rna_OperatorProperties_refine");
|
||||
RNA_def_struct_idprops_func(srna, "rna_OperatorProperties_idprops");
|
||||
RNA_def_struct_property_tags(srna, rna_enum_operator_property_tags);
|
||||
RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES);
|
||||
RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES | STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID);
|
||||
RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
|
||||
}
|
||||
|
||||
|
@@ -179,6 +179,14 @@ static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Objec
|
||||
}
|
||||
else if (ELEM(object.type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) {
|
||||
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_GEOMETRY, "Nodes Modifier");
|
||||
/* We don't know exactly what attributes from the other object we will need. */
|
||||
CustomData_MeshMasks mask;
|
||||
mask.vmask = CD_MASK_PROP_ALL | CD_MASK_MDEFORMVERT;
|
||||
mask.pmask = CD_MASK_PROP_ALL;
|
||||
mask.lmask = CD_MASK_PROP_ALL;
|
||||
mask.fmask = CD_MASK_PROP_ALL;
|
||||
mask.emask = CD_MASK_PROP_ALL;
|
||||
DEG_add_customdata_mask(ctx->node, &object, &mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -454,10 +462,6 @@ class GeometryNodesEvaluator {
|
||||
{
|
||||
const bNode &bnode = params.node();
|
||||
|
||||
if (DEG_is_active(depsgraph_)) {
|
||||
this->store_ui_hints(node, params);
|
||||
}
|
||||
|
||||
/* Use the geometry-node-execute callback if it exists. */
|
||||
if (bnode.typeinfo->geometry_node_execute != nullptr) {
|
||||
bnode.typeinfo->geometry_node_execute(params);
|
||||
@@ -475,41 +479,6 @@ class GeometryNodesEvaluator {
|
||||
this->execute_unknown_node(node, params);
|
||||
}
|
||||
|
||||
void store_ui_hints(const DNode node, GeoNodeExecParams params) const
|
||||
{
|
||||
for (const InputSocketRef *socket_ref : node->inputs()) {
|
||||
if (!socket_ref->is_available()) {
|
||||
continue;
|
||||
}
|
||||
if (socket_ref->bsocket()->type != SOCK_GEOMETRY) {
|
||||
continue;
|
||||
}
|
||||
if (socket_ref->is_multi_input_socket()) {
|
||||
/* Not needed currently. */
|
||||
continue;
|
||||
}
|
||||
|
||||
bNodeTree *btree_cow = node->btree();
|
||||
bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
|
||||
const NodeTreeEvaluationContext context(*self_object_, *modifier_);
|
||||
|
||||
const GeometrySet &geometry_set = params.get_input<GeometrySet>(socket_ref->identifier());
|
||||
|
||||
blender::bke::geometry_set_instances_attribute_foreach(
|
||||
geometry_set,
|
||||
[&](StringRefNull attribute_name, const AttributeMetaData &meta_data) {
|
||||
BKE_nodetree_attribute_hint_add(*btree_original,
|
||||
context,
|
||||
*node->bnode(),
|
||||
attribute_name,
|
||||
meta_data.domain,
|
||||
meta_data.data_type);
|
||||
return true;
|
||||
},
|
||||
8);
|
||||
}
|
||||
}
|
||||
|
||||
void execute_multi_function_node(const DNode node,
|
||||
GeoNodeExecParams params,
|
||||
const MultiFunction &fn)
|
||||
@@ -583,7 +552,8 @@ class GeometryNodesEvaluator {
|
||||
else {
|
||||
void *buffer = allocator_.allocate(to_type.size(), to_type.alignment());
|
||||
if (conversions_.is_convertible(from_type, to_type)) {
|
||||
conversions_.convert(from_type, to_type, value_to_forward.get(), buffer);
|
||||
conversions_.convert_to_uninitialized(
|
||||
from_type, to_type, value_to_forward.get(), buffer);
|
||||
}
|
||||
else {
|
||||
to_type.copy_to_uninitialized(to_type.default_value(), buffer);
|
||||
@@ -653,7 +623,7 @@ class GeometryNodesEvaluator {
|
||||
if (conversions_.is_convertible(type, required_type)) {
|
||||
void *converted_buffer = allocator_.allocate(required_type.size(),
|
||||
required_type.alignment());
|
||||
conversions_.convert(type, required_type, buffer, converted_buffer);
|
||||
conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer);
|
||||
type.destruct(buffer);
|
||||
return {required_type, converted_buffer};
|
||||
}
|
||||
@@ -1230,6 +1200,37 @@ static void log_preview_socket_value(const Span<GPointer> values,
|
||||
}
|
||||
}
|
||||
|
||||
static void log_ui_hints(const DSocket socket,
|
||||
const Span<GPointer> values,
|
||||
Object *self_object,
|
||||
NodesModifierData *nmd)
|
||||
{
|
||||
const DNode node = socket.node();
|
||||
if (node->is_reroute_node() || socket->typeinfo()->type != SOCK_GEOMETRY) {
|
||||
return;
|
||||
}
|
||||
bNodeTree *btree_cow = node->btree();
|
||||
bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
|
||||
const NodeTreeEvaluationContext context{*self_object, nmd->modifier};
|
||||
for (const GPointer data : values) {
|
||||
if (data.type() == &CPPType::get<GeometrySet>()) {
|
||||
const GeometrySet &geometry_set = *(const GeometrySet *)data.get();
|
||||
blender::bke::geometry_set_instances_attribute_foreach(
|
||||
geometry_set,
|
||||
[&](StringRefNull attribute_name, const AttributeMetaData &meta_data) {
|
||||
BKE_nodetree_attribute_hint_add(*btree_original,
|
||||
context,
|
||||
*node->bnode(),
|
||||
attribute_name,
|
||||
meta_data.domain,
|
||||
meta_data.data_type);
|
||||
return true;
|
||||
},
|
||||
8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate a node group to compute the output geometry.
|
||||
* Currently, this uses a fairly basic and inefficient algorithm that might compute things more
|
||||
@@ -1296,6 +1297,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
|
||||
if (!keys.is_empty()) {
|
||||
log_preview_socket_value(values, ctx->object, keys);
|
||||
}
|
||||
log_ui_hints(socket, values, ctx->object, nmd);
|
||||
};
|
||||
|
||||
GeometryNodesEvaluator evaluator{group_inputs,
|
||||
|
@@ -33,29 +33,26 @@ struct ModifierData;
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
using bke::BooleanReadAttribute;
|
||||
using bke::BooleanWriteAttribute;
|
||||
using bke::Color4fReadAttribute;
|
||||
using bke::Color4fWriteAttribute;
|
||||
using bke::Float2ReadAttribute;
|
||||
using bke::Float2WriteAttribute;
|
||||
using bke::Float3ReadAttribute;
|
||||
using bke::Float3WriteAttribute;
|
||||
using bke::FloatReadAttribute;
|
||||
using bke::FloatWriteAttribute;
|
||||
using bke::geometry_set_realize_instances;
|
||||
using bke::Int32ReadAttribute;
|
||||
using bke::Int32WriteAttribute;
|
||||
using bke::OutputAttribute;
|
||||
using bke::OutputAttribute_Typed;
|
||||
using bke::PersistentDataHandleMap;
|
||||
using bke::PersistentObjectHandle;
|
||||
using bke::ReadAttribute;
|
||||
using bke::ReadAttributePtr;
|
||||
using bke::WriteAttribute;
|
||||
using bke::WriteAttributePtr;
|
||||
using bke::ReadAttributeLookup;
|
||||
using bke::WriteAttributeLookup;
|
||||
using fn::CPPType;
|
||||
using fn::GMutablePointer;
|
||||
using fn::GMutableSpan;
|
||||
using fn::GPointer;
|
||||
using fn::GSpan;
|
||||
using fn::GValueMap;
|
||||
using fn::GVArray;
|
||||
using fn::GVArray_GSpan;
|
||||
using fn::GVArray_Span;
|
||||
using fn::GVArray_Typed;
|
||||
using fn::GVMutableArray;
|
||||
using fn::GVMutableArray_GSpan;
|
||||
using fn::GVMutableArray_Typed;
|
||||
|
||||
class GeoNodeExecParams {
|
||||
private:
|
||||
@@ -217,20 +214,22 @@ class GeoNodeExecParams {
|
||||
* \note This will add an error message if the string socket is active and
|
||||
* the input attribute does not exist.
|
||||
*/
|
||||
ReadAttributePtr get_input_attribute(const StringRef name,
|
||||
const GeometryComponent &component,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType type,
|
||||
const void *default_value) const;
|
||||
std::unique_ptr<GVArray> get_input_attribute(const StringRef name,
|
||||
const GeometryComponent &component,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType type,
|
||||
const void *default_value) const;
|
||||
|
||||
template<typename T>
|
||||
bke::TypedReadAttribute<T> get_input_attribute(const StringRef name,
|
||||
const GeometryComponent &component,
|
||||
const AttributeDomain domain,
|
||||
const T &default_value) const
|
||||
GVArray_Typed<T> get_input_attribute(const StringRef name,
|
||||
const GeometryComponent &component,
|
||||
const AttributeDomain domain,
|
||||
const T &default_value) const
|
||||
{
|
||||
const CustomDataType type = bke::cpp_type_to_custom_data_type(CPPType::get<T>());
|
||||
return this->get_input_attribute(name, component, domain, type, &default_value);
|
||||
std::unique_ptr<GVArray> varray = this->get_input_attribute(
|
||||
name, component, domain, type, &default_value);
|
||||
return GVArray_Typed<T>(std::move(varray));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -21,20 +21,45 @@
|
||||
namespace blender::nodes {
|
||||
|
||||
using fn::CPPType;
|
||||
using fn::GVArray;
|
||||
|
||||
struct ConversionFunctions {
|
||||
const fn::MultiFunction *multi_function;
|
||||
void (*convert_single_to_initialized)(const void *src, void *dst);
|
||||
void (*convert_single_to_uninitialized)(const void *src, void *dst);
|
||||
};
|
||||
|
||||
class DataTypeConversions {
|
||||
private:
|
||||
Map<std::pair<fn::MFDataType, fn::MFDataType>, const fn::MultiFunction *> conversions_;
|
||||
Map<std::pair<fn::MFDataType, fn::MFDataType>, ConversionFunctions> conversions_;
|
||||
|
||||
public:
|
||||
void add(fn::MFDataType from_type, fn::MFDataType to_type, const fn::MultiFunction &fn)
|
||||
void add(fn::MFDataType from_type,
|
||||
fn::MFDataType to_type,
|
||||
const fn::MultiFunction &fn,
|
||||
void (*convert_single_to_initialized)(const void *src, void *dst),
|
||||
void (*convert_single_to_uninitialized)(const void *src, void *dst))
|
||||
{
|
||||
conversions_.add_new({from_type, to_type}, &fn);
|
||||
conversions_.add_new({from_type, to_type},
|
||||
{&fn, convert_single_to_initialized, convert_single_to_uninitialized});
|
||||
}
|
||||
|
||||
const fn::MultiFunction *get_conversion(fn::MFDataType from, fn::MFDataType to) const
|
||||
const ConversionFunctions *get_conversion_functions(fn::MFDataType from, fn::MFDataType to) const
|
||||
{
|
||||
return conversions_.lookup_default({from, to}, nullptr);
|
||||
return conversions_.lookup_ptr({from, to});
|
||||
}
|
||||
|
||||
const ConversionFunctions *get_conversion_functions(const CPPType &from, const CPPType &to) const
|
||||
{
|
||||
return this->get_conversion_functions(fn::MFDataType::ForSingle(from),
|
||||
fn::MFDataType::ForSingle(to));
|
||||
}
|
||||
|
||||
const fn::MultiFunction *get_conversion_multi_function(fn::MFDataType from,
|
||||
fn::MFDataType to) const
|
||||
{
|
||||
const ConversionFunctions *functions = this->get_conversion_functions(from, to);
|
||||
return functions ? functions->multi_function : nullptr;
|
||||
}
|
||||
|
||||
bool is_convertible(const CPPType &from_type, const CPPType &to_type) const
|
||||
@@ -43,10 +68,16 @@ class DataTypeConversions {
|
||||
{fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)});
|
||||
}
|
||||
|
||||
void convert(const CPPType &from_type,
|
||||
const CPPType &to_type,
|
||||
const void *from_value,
|
||||
void *to_value) const;
|
||||
void convert_to_uninitialized(const CPPType &from_type,
|
||||
const CPPType &to_type,
|
||||
const void *from_value,
|
||||
void *to_value) const;
|
||||
|
||||
std::unique_ptr<fn::GVArray> try_convert(std::unique_ptr<fn::GVArray> varray,
|
||||
const CPPType &to_type) const;
|
||||
|
||||
std::unique_ptr<fn::GVMutableArray> try_convert(std::unique_ptr<fn::GVMutableArray> varray,
|
||||
const CPPType &to_type) const;
|
||||
};
|
||||
|
||||
const DataTypeConversions &get_implicit_type_conversions();
|
||||
|
@@ -67,6 +67,8 @@ static void geometry_node_tree_get_from_context(const bContext *C,
|
||||
|
||||
static void geometry_node_tree_update(bNodeTree *ntree)
|
||||
{
|
||||
ntreeSetOutput(ntree);
|
||||
|
||||
/* Needed to give correct types to reroutes. */
|
||||
ntree_update_reroute_nodes(ntree);
|
||||
}
|
||||
|
@@ -50,10 +50,10 @@ static void geo_node_align_rotation_to_vector_layout(uiLayout *layout,
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static void align_rotations_auto_pivot(const Float3ReadAttribute &vectors,
|
||||
const FloatReadAttribute &factors,
|
||||
static void align_rotations_auto_pivot(const VArray<float3> &vectors,
|
||||
const VArray<float> &factors,
|
||||
const float3 local_main_axis,
|
||||
MutableSpan<float3> rotations)
|
||||
const MutableSpan<float3> rotations)
|
||||
{
|
||||
for (const int i : IndexRange(vectors.size())) {
|
||||
const float3 vector = vectors[i];
|
||||
@@ -93,11 +93,11 @@ static void align_rotations_auto_pivot(const Float3ReadAttribute &vectors,
|
||||
}
|
||||
}
|
||||
|
||||
static void align_rotations_fixed_pivot(const Float3ReadAttribute &vectors,
|
||||
const FloatReadAttribute &factors,
|
||||
static void align_rotations_fixed_pivot(const VArray<float3> &vectors,
|
||||
const VArray<float> &factors,
|
||||
const float3 local_main_axis,
|
||||
const float3 local_pivot_axis,
|
||||
MutableSpan<float3> rotations)
|
||||
const MutableSpan<float3> rotations)
|
||||
{
|
||||
if (local_main_axis == local_pivot_axis) {
|
||||
/* Can't compute any meaningful rotation angle in this case. */
|
||||
@@ -144,30 +144,30 @@ static void align_rotations_on_component(GeometryComponent &component,
|
||||
const NodeGeometryAlignRotationToVector &storage = *(const NodeGeometryAlignRotationToVector *)
|
||||
node.storage;
|
||||
|
||||
OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
|
||||
"rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
|
||||
if (!rotation_attribute) {
|
||||
OutputAttribute_Typed<float3> rotations = component.attribute_try_get_for_output<float3>(
|
||||
"rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
if (!rotations) {
|
||||
return;
|
||||
}
|
||||
MutableSpan<float3> rotations = rotation_attribute->get_span<float3>();
|
||||
|
||||
FloatReadAttribute factors = params.get_input_attribute<float>(
|
||||
GVArray_Typed<float> factors = params.get_input_attribute<float>(
|
||||
"Factor", component, ATTR_DOMAIN_POINT, 1.0f);
|
||||
Float3ReadAttribute vectors = params.get_input_attribute<float3>(
|
||||
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);
|
||||
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);
|
||||
align_rotations_fixed_pivot(
|
||||
vectors, factors, local_main_axis, local_pivot_axis, rotations.as_span());
|
||||
}
|
||||
|
||||
rotation_attribute.apply_span_and_save();
|
||||
rotations.save();
|
||||
}
|
||||
|
||||
static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
|
||||
|
@@ -112,10 +112,13 @@ template<> inline Color4f clamp_value(const Color4f val, const Color4f min, cons
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void clamp_attribute(Span<T> read_span, MutableSpan<T> span, const T min, const T max)
|
||||
static void clamp_attribute(const VArray<T> &inputs,
|
||||
const MutableSpan<T> outputs,
|
||||
const T min,
|
||||
const T max)
|
||||
{
|
||||
for (const int i : span.index_range()) {
|
||||
span[i] = clamp_value<T>(read_span[i], min, max);
|
||||
for (const int i : IndexRange(outputs.size())) {
|
||||
outputs[i] = clamp_value<T>(inputs[i], min, max);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,13 +126,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef source_name,
|
||||
StringRef result_name)
|
||||
{
|
||||
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute->domain();
|
||||
return result_attribute.domain;
|
||||
}
|
||||
ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
|
||||
ReadAttributeLookup source_attribute = component.attribute_try_get_for_read(source_name);
|
||||
if (source_attribute) {
|
||||
return source_attribute->domain();
|
||||
return source_attribute.domain;
|
||||
}
|
||||
return ATTR_DOMAIN_POINT;
|
||||
}
|
||||
@@ -154,10 +157,10 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
|
||||
const AttributeDomain domain = get_result_domain(component, attribute_name, result_name);
|
||||
const int operation = static_cast<int>(storage.operation);
|
||||
|
||||
ReadAttributePtr attribute_input = component.attribute_try_get_for_read(
|
||||
std::unique_ptr<GVArray> attribute_input = component.attribute_try_get_for_read(
|
||||
attribute_name, domain, data_type);
|
||||
|
||||
OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
|
||||
OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
|
||||
result_name, domain, data_type);
|
||||
|
||||
if (!attribute_result) {
|
||||
@@ -169,8 +172,6 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
|
||||
|
||||
switch (data_type) {
|
||||
case CD_PROP_FLOAT3: {
|
||||
Span<float3> read_span = attribute_input->get_span<float3>();
|
||||
MutableSpan<float3> span = attribute_result->get_span_for_write_only<float3>();
|
||||
float3 min = params.get_input<float3>("Min");
|
||||
float3 max = params.get_input<float3>("Max");
|
||||
if (operation == NODE_CLAMP_RANGE) {
|
||||
@@ -184,38 +185,35 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
|
||||
std::swap(min.z, max.z);
|
||||
}
|
||||
}
|
||||
clamp_attribute<float3>(read_span, span, min, max);
|
||||
MutableSpan<float3> results = attribute_result.as_span<float3>();
|
||||
clamp_attribute<float3>(attribute_input->typed<float3>(), results, min, max);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_FLOAT: {
|
||||
Span<float> read_span = attribute_input->get_span<float>();
|
||||
MutableSpan<float> span = attribute_result->get_span_for_write_only<float>();
|
||||
const float min = params.get_input<float>("Min_001");
|
||||
const float max = params.get_input<float>("Max_001");
|
||||
MutableSpan<float> results = attribute_result.as_span<float>();
|
||||
if (operation == NODE_CLAMP_RANGE && min > max) {
|
||||
clamp_attribute<float>(read_span, span, max, min);
|
||||
clamp_attribute<float>(attribute_input->typed<float>(), results, max, min);
|
||||
}
|
||||
else {
|
||||
clamp_attribute<float>(read_span, span, min, max);
|
||||
clamp_attribute<float>(attribute_input->typed<float>(), results, min, max);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CD_PROP_INT32: {
|
||||
Span<int> read_span = attribute_input->get_span<int>();
|
||||
MutableSpan<int> span = attribute_result->get_span_for_write_only<int>();
|
||||
const int min = params.get_input<int>("Min_002");
|
||||
const int max = params.get_input<int>("Max_002");
|
||||
MutableSpan<int> results = attribute_result.as_span<int>();
|
||||
if (operation == NODE_CLAMP_RANGE && min > max) {
|
||||
clamp_attribute<int>(read_span, span, max, min);
|
||||
clamp_attribute<int>(attribute_input->typed<int>(), results, max, min);
|
||||
}
|
||||
else {
|
||||
clamp_attribute<int>(read_span, span, min, max);
|
||||
clamp_attribute<int>(attribute_input->typed<int>(), results, min, max);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CD_PROP_COLOR: {
|
||||
Span<Color4f> read_span = attribute_input->get_span<Color4f>();
|
||||
MutableSpan<Color4f> span = attribute_result->get_span_for_write_only<Color4f>();
|
||||
Color4f min = params.get_input<Color4f>("Min_003");
|
||||
Color4f max = params.get_input<Color4f>("Max_003");
|
||||
if (operation == NODE_CLAMP_RANGE) {
|
||||
@@ -232,7 +230,8 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
|
||||
std::swap(min.a, max.a);
|
||||
}
|
||||
}
|
||||
clamp_attribute<Color4f>(read_span, span, min, max);
|
||||
MutableSpan<Color4f> results = attribute_result.as_span<Color4f>();
|
||||
clamp_attribute<Color4f>(attribute_input->typed<Color4f>(), results, min, max);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -241,7 +240,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
|
||||
}
|
||||
}
|
||||
|
||||
attribute_result.apply_span_and_save();
|
||||
attribute_result.save();
|
||||
}
|
||||
|
||||
static void geo_node_attribute_clamp_exec(GeoNodeExecParams params)
|
||||
|
@@ -47,15 +47,15 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef result_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute->domain();
|
||||
return result_attribute.domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the input attribute's domain if it exists. */
|
||||
ReadAttributePtr input_attribute = component.attribute_try_get_for_read(input_name);
|
||||
ReadAttributeLookup input_attribute = component.attribute_try_get_for_read(input_name);
|
||||
if (input_attribute) {
|
||||
return input_attribute->domain();
|
||||
return input_attribute.domain;
|
||||
}
|
||||
|
||||
return ATTR_DOMAIN_POINT;
|
||||
@@ -71,27 +71,25 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon
|
||||
/* Always output a color attribute for now. We might want to allow users to customize.
|
||||
* Using the type of an existing attribute could work, but does not have a real benefit
|
||||
* currently. */
|
||||
const CustomDataType result_type = CD_PROP_COLOR;
|
||||
const AttributeDomain result_domain = get_result_domain(component, input_name, result_name);
|
||||
|
||||
OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
|
||||
result_name, result_domain, result_type);
|
||||
OutputAttribute_Typed<Color4f> attribute_result =
|
||||
component.attribute_try_get_for_output_only<Color4f>(result_name, result_domain);
|
||||
if (!attribute_result) {
|
||||
return;
|
||||
}
|
||||
|
||||
FloatReadAttribute attribute_in = component.attribute_get_for_read<float>(
|
||||
GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
|
||||
input_name, result_domain, 0.0f);
|
||||
|
||||
Span<float> data_in = attribute_in.get_span();
|
||||
MutableSpan<Color4f> data_out = attribute_result->get_span_for_write_only<Color4f>();
|
||||
MutableSpan<Color4f> results = attribute_result.as_span();
|
||||
|
||||
ColorBand *color_ramp = &node_storage->color_ramp;
|
||||
for (const int i : data_in.index_range()) {
|
||||
BKE_colorband_evaluate(color_ramp, data_in[i], data_out[i]);
|
||||
for (const int i : IndexRange(attribute_in.size())) {
|
||||
BKE_colorband_evaluate(color_ramp, attribute_in[i], results[i]);
|
||||
}
|
||||
|
||||
attribute_result.apply_span_and_save();
|
||||
attribute_result.save();
|
||||
}
|
||||
|
||||
static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
|
||||
|
@@ -77,9 +77,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef result_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute->domain();
|
||||
return result_attribute.domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
|
||||
@@ -94,27 +94,24 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa
|
||||
}
|
||||
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
|
||||
|
||||
OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
|
||||
result_name, result_domain, CD_PROP_FLOAT3);
|
||||
OutputAttribute_Typed<float3> attribute_result =
|
||||
component.attribute_try_get_for_output_only<float3>(result_name, result_domain);
|
||||
if (!attribute_result) {
|
||||
return;
|
||||
}
|
||||
FloatReadAttribute attribute_x = params.get_input_attribute<float>(
|
||||
GVArray_Typed<float> attribute_x = params.get_input_attribute<float>(
|
||||
"X", component, result_domain, 0.0f);
|
||||
FloatReadAttribute attribute_y = params.get_input_attribute<float>(
|
||||
GVArray_Typed<float> attribute_y = params.get_input_attribute<float>(
|
||||
"Y", component, result_domain, 0.0f);
|
||||
FloatReadAttribute attribute_z = params.get_input_attribute<float>(
|
||||
GVArray_Typed<float> attribute_z = params.get_input_attribute<float>(
|
||||
"Z", component, result_domain, 0.0f);
|
||||
|
||||
MutableSpan<float3> results = attribute_result->get_span_for_write_only<float3>();
|
||||
for (const int i : results.index_range()) {
|
||||
for (const int i : IndexRange(attribute_result->size())) {
|
||||
const float x = attribute_x[i];
|
||||
const float y = attribute_y[i];
|
||||
const float z = attribute_z[i];
|
||||
const float3 result = float3(x, y, z);
|
||||
results[i] = result;
|
||||
attribute_result->set(i, {x, y, z});
|
||||
}
|
||||
attribute_result.apply_span_and_save();
|
||||
}
|
||||
|
||||
static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
|
||||
|
@@ -81,21 +81,18 @@ static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *n
|
||||
nodeSetSocketAvailability(socket_threshold, operation_tests_equality(*node_storage));
|
||||
}
|
||||
|
||||
static void do_math_operation(const FloatReadAttribute &input_a,
|
||||
const FloatReadAttribute &input_b,
|
||||
static void do_math_operation(const VArray<float> &input_a,
|
||||
const VArray<float> &input_b,
|
||||
const FloatCompareOperation operation,
|
||||
MutableSpan<bool> span_result)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float> span_a = input_a.get_span();
|
||||
Span<float> span_b = input_b.get_span();
|
||||
|
||||
if (try_dispatch_float_math_fl_fl_to_bool(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
for (const int i : IndexRange(size)) {
|
||||
const float a = span_a[i];
|
||||
const float b = span_b[i];
|
||||
const float a = input_a[i];
|
||||
const float b = input_b[i];
|
||||
const bool out = math_function(a, b);
|
||||
span_result[i] = out;
|
||||
}
|
||||
@@ -107,8 +104,8 @@ static void do_math_operation(const FloatReadAttribute &input_a,
|
||||
BLI_assert(false);
|
||||
}
|
||||
|
||||
static void do_equal_operation_float(const FloatReadAttribute &input_a,
|
||||
const FloatReadAttribute &input_b,
|
||||
static void do_equal_operation_float(const VArray<float> &input_a,
|
||||
const VArray<float> &input_b,
|
||||
const float threshold,
|
||||
MutableSpan<bool> span_result)
|
||||
{
|
||||
@@ -120,8 +117,8 @@ static void do_equal_operation_float(const FloatReadAttribute &input_a,
|
||||
}
|
||||
}
|
||||
|
||||
static void do_equal_operation_float3(const Float3ReadAttribute &input_a,
|
||||
const Float3ReadAttribute &input_b,
|
||||
static void do_equal_operation_float3(const VArray<float3> &input_a,
|
||||
const VArray<float3> &input_b,
|
||||
const float threshold,
|
||||
MutableSpan<bool> span_result)
|
||||
{
|
||||
@@ -134,8 +131,8 @@ static void do_equal_operation_float3(const Float3ReadAttribute &input_a,
|
||||
}
|
||||
}
|
||||
|
||||
static void do_equal_operation_color4f(const Color4fReadAttribute &input_a,
|
||||
const Color4fReadAttribute &input_b,
|
||||
static void do_equal_operation_color4f(const VArray<Color4f> &input_a,
|
||||
const VArray<Color4f> &input_b,
|
||||
const float threshold,
|
||||
MutableSpan<bool> span_result)
|
||||
{
|
||||
@@ -148,8 +145,8 @@ static void do_equal_operation_color4f(const Color4fReadAttribute &input_a,
|
||||
}
|
||||
}
|
||||
|
||||
static void do_equal_operation_bool(const BooleanReadAttribute &input_a,
|
||||
const BooleanReadAttribute &input_b,
|
||||
static void do_equal_operation_bool(const VArray<bool> &input_a,
|
||||
const VArray<bool> &input_b,
|
||||
const float UNUSED(threshold),
|
||||
MutableSpan<bool> span_result)
|
||||
{
|
||||
@@ -161,8 +158,8 @@ static void do_equal_operation_bool(const BooleanReadAttribute &input_a,
|
||||
}
|
||||
}
|
||||
|
||||
static void do_not_equal_operation_float(const FloatReadAttribute &input_a,
|
||||
const FloatReadAttribute &input_b,
|
||||
static void do_not_equal_operation_float(const VArray<float> &input_a,
|
||||
const VArray<float> &input_b,
|
||||
const float threshold,
|
||||
MutableSpan<bool> span_result)
|
||||
{
|
||||
@@ -174,8 +171,8 @@ static void do_not_equal_operation_float(const FloatReadAttribute &input_a,
|
||||
}
|
||||
}
|
||||
|
||||
static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a,
|
||||
const Float3ReadAttribute &input_b,
|
||||
static void do_not_equal_operation_float3(const VArray<float3> &input_a,
|
||||
const VArray<float3> &input_b,
|
||||
const float threshold,
|
||||
MutableSpan<bool> span_result)
|
||||
{
|
||||
@@ -188,8 +185,8 @@ static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a,
|
||||
}
|
||||
}
|
||||
|
||||
static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a,
|
||||
const Color4fReadAttribute &input_b,
|
||||
static void do_not_equal_operation_color4f(const VArray<Color4f> &input_a,
|
||||
const VArray<Color4f> &input_b,
|
||||
const float threshold,
|
||||
MutableSpan<bool> span_result)
|
||||
{
|
||||
@@ -202,8 +199,8 @@ static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a,
|
||||
}
|
||||
}
|
||||
|
||||
static void do_not_equal_operation_bool(const BooleanReadAttribute &input_a,
|
||||
const BooleanReadAttribute &input_b,
|
||||
static void do_not_equal_operation_bool(const VArray<bool> &input_a,
|
||||
const VArray<bool> &input_b,
|
||||
const float UNUSED(threshold),
|
||||
MutableSpan<bool> span_result)
|
||||
{
|
||||
@@ -237,9 +234,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef result_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute->domain();
|
||||
return result_attribute.domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
|
||||
@@ -254,20 +251,19 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
|
||||
node_storage->operation);
|
||||
const std::string result_name = params.get_input<std::string>("Result");
|
||||
|
||||
const CustomDataType result_type = CD_PROP_BOOL;
|
||||
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
|
||||
|
||||
OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
|
||||
result_name, result_domain, result_type);
|
||||
OutputAttribute_Typed<bool> attribute_result = component.attribute_try_get_for_output_only<bool>(
|
||||
result_name, result_domain);
|
||||
if (!attribute_result) {
|
||||
return;
|
||||
}
|
||||
|
||||
const CustomDataType input_data_type = get_data_type(component, params, *node_storage);
|
||||
|
||||
ReadAttributePtr attribute_a = params.get_input_attribute(
|
||||
std::unique_ptr<GVArray> attribute_a = params.get_input_attribute(
|
||||
"A", component, result_domain, input_data_type, nullptr);
|
||||
ReadAttributePtr attribute_b = params.get_input_attribute(
|
||||
std::unique_ptr<GVArray> attribute_b = params.get_input_attribute(
|
||||
"B", component, result_domain, input_data_type, nullptr);
|
||||
|
||||
if (!attribute_a || !attribute_b) {
|
||||
@@ -275,7 +271,7 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
|
||||
return;
|
||||
}
|
||||
|
||||
MutableSpan<bool> result_span = attribute_result->get_span_for_write_only<bool>();
|
||||
MutableSpan<bool> result_span = attribute_result.as_span();
|
||||
|
||||
/* Use specific types for correct equality operations, but for other operations we use implicit
|
||||
* conversions and float comparison. In other words, the comparison is not element-wise. */
|
||||
@@ -283,38 +279,47 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
|
||||
const float threshold = params.get_input<float>("Threshold");
|
||||
if (operation == NODE_FLOAT_COMPARE_EQUAL) {
|
||||
if (input_data_type == CD_PROP_FLOAT) {
|
||||
do_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
|
||||
do_equal_operation_float(
|
||||
attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
|
||||
}
|
||||
else if (input_data_type == CD_PROP_FLOAT3) {
|
||||
do_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
|
||||
do_equal_operation_float3(
|
||||
attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
|
||||
}
|
||||
else if (input_data_type == CD_PROP_COLOR) {
|
||||
do_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
|
||||
do_equal_operation_color4f(
|
||||
attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span);
|
||||
}
|
||||
else if (input_data_type == CD_PROP_BOOL) {
|
||||
do_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
|
||||
do_equal_operation_bool(
|
||||
attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
|
||||
}
|
||||
}
|
||||
else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) {
|
||||
if (input_data_type == CD_PROP_FLOAT) {
|
||||
do_not_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
|
||||
do_not_equal_operation_float(
|
||||
attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
|
||||
}
|
||||
else if (input_data_type == CD_PROP_FLOAT3) {
|
||||
do_not_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
|
||||
do_not_equal_operation_float3(
|
||||
attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
|
||||
}
|
||||
else if (input_data_type == CD_PROP_COLOR) {
|
||||
do_not_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
|
||||
do_not_equal_operation_color4f(
|
||||
attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span);
|
||||
}
|
||||
else if (input_data_type == CD_PROP_BOOL) {
|
||||
do_not_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
|
||||
do_not_equal_operation_bool(
|
||||
attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
do_math_operation(*attribute_a, *attribute_b, operation, result_span);
|
||||
do_math_operation(
|
||||
attribute_a->typed<float>(), attribute_b->typed<float>(), operation, result_span);
|
||||
}
|
||||
|
||||
attribute_result.apply_span_and_save();
|
||||
attribute_result.save();
|
||||
}
|
||||
|
||||
static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
|
||||
|
@@ -55,13 +55,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef source_name,
|
||||
StringRef result_name)
|
||||
{
|
||||
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute->domain();
|
||||
return result_attribute.domain;
|
||||
}
|
||||
ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
|
||||
ReadAttributeLookup source_attribute = component.attribute_try_get_for_read(source_name);
|
||||
if (source_attribute) {
|
||||
return source_attribute->domain();
|
||||
return source_attribute.domain;
|
||||
}
|
||||
return ATTR_DOMAIN_POINT;
|
||||
}
|
||||
@@ -78,7 +78,7 @@ static void attribute_convert_calc(GeometryComponent &component,
|
||||
component, source_name, result_name) :
|
||||
domain;
|
||||
|
||||
ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
|
||||
std::unique_ptr<GVArray> source_attribute = component.attribute_try_get_for_read(
|
||||
source_name, result_domain, result_type);
|
||||
if (!source_attribute) {
|
||||
params.error_message_add(NodeWarningType::Error,
|
||||
@@ -86,25 +86,22 @@ static void attribute_convert_calc(GeometryComponent &component,
|
||||
return;
|
||||
}
|
||||
|
||||
OutputAttributePtr result_attribute = component.attribute_try_get_for_output(
|
||||
OutputAttribute result_attribute = component.attribute_try_get_for_output_only(
|
||||
result_name, result_domain, result_type);
|
||||
if (!result_attribute) {
|
||||
return;
|
||||
}
|
||||
|
||||
fn::GSpan source_span = source_attribute->get_span();
|
||||
fn::GMutableSpan result_span = result_attribute->get_span_for_write_only();
|
||||
if (source_span.is_empty() || result_span.is_empty()) {
|
||||
return;
|
||||
}
|
||||
GVArray_GSpan source_span{*source_attribute};
|
||||
GMutableSpan result_span = result_attribute.as_span();
|
||||
|
||||
BLI_assert(source_span.size() == result_span.size());
|
||||
|
||||
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(result_type);
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
|
||||
cpp_type->copy_to_initialized_n(source_span.data(), result_span.data(), result_span.size());
|
||||
|
||||
result_attribute.apply_span_and_save();
|
||||
result_attribute.save();
|
||||
}
|
||||
|
||||
static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
|
||||
|
@@ -72,9 +72,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef attribute_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(attribute_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute->domain();
|
||||
return result_attribute.domain;
|
||||
}
|
||||
return ATTR_DOMAIN_POINT;
|
||||
}
|
||||
@@ -93,7 +93,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
|
||||
get_result_domain(component, attribute_name) :
|
||||
domain;
|
||||
|
||||
OutputAttributePtr attribute = component.attribute_try_get_for_output(
|
||||
OutputAttribute attribute = component.attribute_try_get_for_output_only(
|
||||
attribute_name, result_domain, data_type);
|
||||
if (!attribute) {
|
||||
return;
|
||||
@@ -102,38 +102,34 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
|
||||
switch (data_type) {
|
||||
case CD_PROP_FLOAT: {
|
||||
const float value = params.get_input<float>("Value_001");
|
||||
MutableSpan<float> attribute_span = attribute->get_span_for_write_only<float>();
|
||||
attribute_span.fill(value);
|
||||
attribute->fill(&value);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_FLOAT3: {
|
||||
const float3 value = params.get_input<float3>("Value");
|
||||
MutableSpan<float3> attribute_span = attribute->get_span_for_write_only<float3>();
|
||||
attribute_span.fill(value);
|
||||
attribute->fill(&value);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_COLOR: {
|
||||
const Color4f value = params.get_input<Color4f>("Value_002");
|
||||
MutableSpan<Color4f> attribute_span = attribute->get_span_for_write_only<Color4f>();
|
||||
attribute_span.fill(value);
|
||||
attribute->fill(&value);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_BOOL: {
|
||||
const bool value = params.get_input<bool>("Value_003");
|
||||
MutableSpan<bool> attribute_span = attribute->get_span_for_write_only<bool>();
|
||||
attribute_span.fill(value);
|
||||
attribute->fill(&value);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_INT32: {
|
||||
const int value = params.get_input<int>("Value_004");
|
||||
MutableSpan<int> attribute_span = attribute->get_span_for_write_only<int>();
|
||||
attribute_span.fill(value);
|
||||
attribute->fill(&value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
attribute.apply_span_and_save();
|
||||
attribute.save();
|
||||
}
|
||||
|
||||
static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
|
||||
|
@@ -192,8 +192,8 @@ static float map_smootherstep(const float value,
|
||||
return min_to + factor_mapped * (max_to - min_to);
|
||||
}
|
||||
|
||||
static void map_range_float(FloatReadAttribute attribute_input,
|
||||
FloatWriteAttribute attribute_result,
|
||||
static void map_range_float(const VArray<float> &attribute_input,
|
||||
MutableSpan<float> results,
|
||||
const GeoNodeExecParams ¶ms)
|
||||
{
|
||||
const bNode &node = params.node();
|
||||
@@ -204,32 +204,31 @@ static void map_range_float(FloatReadAttribute attribute_input,
|
||||
const float min_to = params.get_input<float>("To Min");
|
||||
const float max_to = params.get_input<float>("To Max");
|
||||
|
||||
Span<float> span = attribute_input.get_span();
|
||||
MutableSpan<float> result_span = attribute_result.get_span();
|
||||
VArray_Span<float> span{attribute_input};
|
||||
|
||||
switch (interpolation_type) {
|
||||
case NODE_MAP_RANGE_LINEAR: {
|
||||
for (int i : span.index_range()) {
|
||||
result_span[i] = map_linear(span[i], min_from, max_from, min_to, max_to);
|
||||
results[i] = map_linear(span[i], min_from, max_from, min_to, max_to);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_STEPPED: {
|
||||
const float steps = params.get_input<float>("Steps");
|
||||
for (int i : span.index_range()) {
|
||||
result_span[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps);
|
||||
results[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_SMOOTHSTEP: {
|
||||
for (int i : span.index_range()) {
|
||||
result_span[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to);
|
||||
results[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_SMOOTHERSTEP: {
|
||||
for (int i : span.index_range()) {
|
||||
result_span[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to);
|
||||
results[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -241,14 +240,14 @@ static void map_range_float(FloatReadAttribute attribute_input,
|
||||
const float clamp_min = min_to < max_to ? min_to : max_to;
|
||||
const float clamp_max = min_to < max_to ? max_to : min_to;
|
||||
|
||||
for (int i : result_span.index_range()) {
|
||||
result_span[i] = std::clamp(result_span[i], clamp_min, clamp_max);
|
||||
for (int i : results.index_range()) {
|
||||
results[i] = std::clamp(results[i], clamp_min, clamp_max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void map_range_float3(Float3ReadAttribute attribute_input,
|
||||
Float3WriteAttribute attribute_result,
|
||||
static void map_range_float3(const VArray<float3> &attribute_input,
|
||||
const MutableSpan<float3> results,
|
||||
const GeoNodeExecParams ¶ms)
|
||||
{
|
||||
const bNode &node = params.node();
|
||||
@@ -259,43 +258,39 @@ static void map_range_float3(Float3ReadAttribute attribute_input,
|
||||
const float3 min_to = params.get_input<float3>("To Min_001");
|
||||
const float3 max_to = params.get_input<float3>("To Max_001");
|
||||
|
||||
Span<float3> span = attribute_input.get_span();
|
||||
MutableSpan<float3> result_span = attribute_result.get_span();
|
||||
VArray_Span<float3> span{attribute_input};
|
||||
|
||||
switch (interpolation_type) {
|
||||
case NODE_MAP_RANGE_LINEAR: {
|
||||
for (int i : span.index_range()) {
|
||||
result_span[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
|
||||
result_span[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
|
||||
result_span[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
|
||||
results[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
|
||||
results[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
|
||||
results[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_STEPPED: {
|
||||
const float3 steps = params.get_input<float3>("Steps_001");
|
||||
for (int i : span.index_range()) {
|
||||
result_span[i].x = map_stepped(
|
||||
span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x);
|
||||
result_span[i].y = map_stepped(
|
||||
span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y);
|
||||
result_span[i].z = map_stepped(
|
||||
span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z);
|
||||
results[i].x = map_stepped(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x);
|
||||
results[i].y = map_stepped(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y);
|
||||
results[i].z = map_stepped(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_SMOOTHSTEP: {
|
||||
for (int i : span.index_range()) {
|
||||
result_span[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
|
||||
result_span[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
|
||||
result_span[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
|
||||
results[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
|
||||
results[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
|
||||
results[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_SMOOTHERSTEP: {
|
||||
for (int i : span.index_range()) {
|
||||
result_span[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
|
||||
result_span[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
|
||||
result_span[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
|
||||
results[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
|
||||
results[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
|
||||
results[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -313,8 +308,8 @@ static void map_range_float3(Float3ReadAttribute attribute_input,
|
||||
clamp_min.z = min_to.z < max_to.z ? min_to.z : max_to.z;
|
||||
clamp_max.z = min_to.z < max_to.z ? max_to.z : min_to.z;
|
||||
|
||||
for (int i : result_span.index_range()) {
|
||||
clamp_v3_v3v3(result_span[i], clamp_min, clamp_max);
|
||||
for (int i : results.index_range()) {
|
||||
clamp_v3_v3v3(results[i], clamp_min, clamp_max);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,13 +318,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef source_name,
|
||||
StringRef result_name)
|
||||
{
|
||||
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute->domain();
|
||||
return result_attribute.domain;
|
||||
}
|
||||
ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
|
||||
ReadAttributeLookup source_attribute = component.attribute_try_get_for_read(source_name);
|
||||
if (source_attribute) {
|
||||
return source_attribute->domain();
|
||||
return source_attribute.domain;
|
||||
}
|
||||
return ATTR_DOMAIN_POINT;
|
||||
}
|
||||
@@ -349,7 +344,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
|
||||
|
||||
const AttributeDomain domain = get_result_domain(component, input_name, result_name);
|
||||
|
||||
ReadAttributePtr attribute_input = component.attribute_try_get_for_read(
|
||||
std::unique_ptr<GVArray> attribute_input = component.attribute_try_get_for_read(
|
||||
input_name, domain, data_type);
|
||||
|
||||
if (!attribute_input) {
|
||||
@@ -358,7 +353,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
|
||||
return;
|
||||
}
|
||||
|
||||
OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
|
||||
OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
|
||||
result_name, domain, data_type);
|
||||
if (!attribute_result) {
|
||||
params.error_message_add(NodeWarningType::Error,
|
||||
@@ -369,18 +364,19 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
|
||||
|
||||
switch (data_type) {
|
||||
case CD_PROP_FLOAT: {
|
||||
map_range_float(*attribute_input, *attribute_result, params);
|
||||
map_range_float(attribute_input->typed<float>(), attribute_result.as_span<float>(), params);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_FLOAT3: {
|
||||
map_range_float3(*attribute_input, *attribute_result, params);
|
||||
map_range_float3(
|
||||
attribute_input->typed<float3>(), attribute_result.as_span<float3>(), params);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
attribute_result.apply_span_and_save();
|
||||
attribute_result.save();
|
||||
}
|
||||
|
||||
static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
|
||||
|
@@ -149,9 +149,9 @@ static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node
|
||||
operation_use_input_c(operation));
|
||||
}
|
||||
|
||||
static void do_math_operation(Span<float> span_a,
|
||||
Span<float> span_b,
|
||||
Span<float> span_c,
|
||||
static void do_math_operation(const VArray<float> &span_a,
|
||||
const VArray<float> &span_b,
|
||||
const VArray<float> &span_c,
|
||||
MutableSpan<float> span_result,
|
||||
const NodeMathOperation operation)
|
||||
{
|
||||
@@ -165,8 +165,8 @@ static void do_math_operation(Span<float> span_a,
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation(Span<float> span_a,
|
||||
Span<float> span_b,
|
||||
static void do_math_operation(const VArray<float> &span_a,
|
||||
const VArray<float> &span_b,
|
||||
MutableSpan<float> span_result,
|
||||
const NodeMathOperation operation)
|
||||
{
|
||||
@@ -180,7 +180,7 @@ static void do_math_operation(Span<float> span_a,
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation(Span<float> span_input,
|
||||
static void do_math_operation(const VArray<float> &span_input,
|
||||
MutableSpan<float> span_result,
|
||||
const NodeMathOperation operation)
|
||||
{
|
||||
@@ -200,9 +200,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef result_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute->domain();
|
||||
return result_attribute.domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
|
||||
@@ -224,56 +224,39 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
|
||||
const std::string result_name = params.get_input<std::string>("Result");
|
||||
|
||||
/* The result type of this node is always float. */
|
||||
const CustomDataType result_type = CD_PROP_FLOAT;
|
||||
const AttributeDomain result_domain = get_result_domain(
|
||||
component, params, operation, result_name);
|
||||
|
||||
OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
|
||||
result_name, result_domain, result_type);
|
||||
OutputAttribute_Typed<float> attribute_result =
|
||||
component.attribute_try_get_for_output_only<float>(result_name, result_domain);
|
||||
if (!attribute_result) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReadAttributePtr attribute_a = params.get_input_attribute(
|
||||
"A", component, result_domain, result_type, nullptr);
|
||||
if (!attribute_a) {
|
||||
return;
|
||||
}
|
||||
GVArray_Typed<float> attribute_a = params.get_input_attribute<float>(
|
||||
"A", component, result_domain, 0.0f);
|
||||
|
||||
/* Note that passing the data with `get_span<float>()` works
|
||||
MutableSpan<float> result_span = attribute_result.as_span();
|
||||
|
||||
/* Note that passing the data with `get_internal_span<float>()` works
|
||||
* because the attributes were accessed with #CD_PROP_FLOAT. */
|
||||
if (operation_use_input_b(operation)) {
|
||||
ReadAttributePtr attribute_b = params.get_input_attribute(
|
||||
"B", component, result_domain, result_type, nullptr);
|
||||
if (!attribute_b) {
|
||||
return;
|
||||
}
|
||||
GVArray_Typed<float> attribute_b = params.get_input_attribute<float>(
|
||||
"B", component, result_domain, 0.0f);
|
||||
if (operation_use_input_c(operation)) {
|
||||
ReadAttributePtr attribute_c = params.get_input_attribute(
|
||||
"C", component, result_domain, result_type, nullptr);
|
||||
if (!attribute_c) {
|
||||
return;
|
||||
}
|
||||
do_math_operation(attribute_a->get_span<float>(),
|
||||
attribute_b->get_span<float>(),
|
||||
attribute_c->get_span<float>(),
|
||||
attribute_result->get_span_for_write_only<float>(),
|
||||
operation);
|
||||
GVArray_Typed<float> attribute_c = params.get_input_attribute<float>(
|
||||
"C", component, result_domain, 0.0f);
|
||||
do_math_operation(attribute_a, attribute_b, attribute_c, result_span, operation);
|
||||
}
|
||||
else {
|
||||
do_math_operation(attribute_a->get_span<float>(),
|
||||
attribute_b->get_span<float>(),
|
||||
attribute_result->get_span_for_write_only<float>(),
|
||||
operation);
|
||||
do_math_operation(attribute_a, attribute_b, result_span, operation);
|
||||
}
|
||||
}
|
||||
else {
|
||||
do_math_operation(attribute_a->get_span<float>(),
|
||||
attribute_result->get_span_for_write_only<float>(),
|
||||
operation);
|
||||
do_math_operation(attribute_a, result_span, operation);
|
||||
}
|
||||
|
||||
attribute_result.apply_span_and_save();
|
||||
attribute_result.save();
|
||||
}
|
||||
|
||||
static void geo_node_attribute_math_exec(GeoNodeExecParams params)
|
||||
|
@@ -58,10 +58,10 @@ static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C),
|
||||
namespace blender::nodes {
|
||||
|
||||
static void do_mix_operation_float(const int blend_mode,
|
||||
const FloatReadAttribute &factors,
|
||||
const FloatReadAttribute &inputs_a,
|
||||
const FloatReadAttribute &inputs_b,
|
||||
FloatWriteAttribute results)
|
||||
const VArray<float> &factors,
|
||||
const VArray<float> &inputs_a,
|
||||
const VArray<float> &inputs_b,
|
||||
VMutableArray<float> &results)
|
||||
{
|
||||
const int size = results.size();
|
||||
for (const int i : IndexRange(size)) {
|
||||
@@ -75,10 +75,10 @@ static void do_mix_operation_float(const int blend_mode,
|
||||
}
|
||||
|
||||
static void do_mix_operation_float3(const int blend_mode,
|
||||
const FloatReadAttribute &factors,
|
||||
const Float3ReadAttribute &inputs_a,
|
||||
const Float3ReadAttribute &inputs_b,
|
||||
Float3WriteAttribute results)
|
||||
const VArray<float> &factors,
|
||||
const VArray<float3> &inputs_a,
|
||||
const VArray<float3> &inputs_b,
|
||||
VMutableArray<float3> &results)
|
||||
{
|
||||
const int size = results.size();
|
||||
for (const int i : IndexRange(size)) {
|
||||
@@ -91,10 +91,10 @@ static void do_mix_operation_float3(const int blend_mode,
|
||||
}
|
||||
|
||||
static void do_mix_operation_color4f(const int blend_mode,
|
||||
const FloatReadAttribute &factors,
|
||||
const Color4fReadAttribute &inputs_a,
|
||||
const Color4fReadAttribute &inputs_b,
|
||||
Color4fWriteAttribute results)
|
||||
const VArray<float> &factors,
|
||||
const VArray<Color4f> &inputs_a,
|
||||
const VArray<Color4f> &inputs_b,
|
||||
VMutableArray<Color4f> &results)
|
||||
{
|
||||
const int size = results.size();
|
||||
for (const int i : IndexRange(size)) {
|
||||
@@ -108,22 +108,31 @@ static void do_mix_operation_color4f(const int blend_mode,
|
||||
|
||||
static void do_mix_operation(const CustomDataType result_type,
|
||||
int blend_mode,
|
||||
const FloatReadAttribute &attribute_factor,
|
||||
const ReadAttribute &attribute_a,
|
||||
const ReadAttribute &attribute_b,
|
||||
WriteAttribute &attribute_result)
|
||||
const VArray<float> &attribute_factor,
|
||||
const GVArray &attribute_a,
|
||||
const GVArray &attribute_b,
|
||||
GVMutableArray &attribute_result)
|
||||
{
|
||||
if (result_type == CD_PROP_FLOAT) {
|
||||
do_mix_operation_float(
|
||||
blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
|
||||
do_mix_operation_float(blend_mode,
|
||||
attribute_factor,
|
||||
attribute_a.typed<float>(),
|
||||
attribute_b.typed<float>(),
|
||||
attribute_result.typed<float>());
|
||||
}
|
||||
else if (result_type == CD_PROP_FLOAT3) {
|
||||
do_mix_operation_float3(
|
||||
blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
|
||||
do_mix_operation_float3(blend_mode,
|
||||
attribute_factor,
|
||||
attribute_a.typed<float3>(),
|
||||
attribute_b.typed<float3>(),
|
||||
attribute_result.typed<float3>());
|
||||
}
|
||||
else if (result_type == CD_PROP_COLOR) {
|
||||
do_mix_operation_color4f(
|
||||
blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
|
||||
do_mix_operation_color4f(blend_mode,
|
||||
attribute_factor,
|
||||
attribute_a.typed<Color4f>(),
|
||||
attribute_b.typed<Color4f>(),
|
||||
attribute_result.typed<Color4f>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,9 +141,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef result_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute->domain();
|
||||
return result_attribute.domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
|
||||
@@ -158,17 +167,17 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa
|
||||
|
||||
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
|
||||
|
||||
OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
|
||||
OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
|
||||
result_name, result_domain, result_type);
|
||||
if (!attribute_result) {
|
||||
return;
|
||||
}
|
||||
|
||||
FloatReadAttribute attribute_factor = params.get_input_attribute<float>(
|
||||
GVArray_Typed<float> attribute_factor = params.get_input_attribute<float>(
|
||||
"Factor", component, result_domain, 0.5f);
|
||||
ReadAttributePtr attribute_a = params.get_input_attribute(
|
||||
std::unique_ptr<GVArray> attribute_a = params.get_input_attribute(
|
||||
"A", component, result_domain, result_type, nullptr);
|
||||
ReadAttributePtr attribute_b = params.get_input_attribute(
|
||||
std::unique_ptr<GVArray> attribute_b = params.get_input_attribute(
|
||||
"B", component, result_domain, result_type, nullptr);
|
||||
|
||||
do_mix_operation(result_type,
|
||||
|
@@ -62,7 +62,7 @@ namespace blender::nodes {
|
||||
|
||||
static void proximity_calc(MutableSpan<float> distance_span,
|
||||
MutableSpan<float3> location_span,
|
||||
Span<float3> positions,
|
||||
const VArray<float3> &positions,
|
||||
BVHTreeFromMesh &tree_data_mesh,
|
||||
BVHTreeFromPointCloud &tree_data_pointcloud,
|
||||
const bool bvh_mesh_success,
|
||||
@@ -169,19 +169,18 @@ static void attribute_calc_proximity(GeometryComponent &component,
|
||||
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
|
||||
|
||||
const std::string distance_attribute_name = params.get_input<std::string>("Distance");
|
||||
OutputAttributePtr distance_attribute = component.attribute_try_get_for_output(
|
||||
distance_attribute_name, result_domain, CD_PROP_FLOAT);
|
||||
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");
|
||||
OutputAttributePtr location_attribute = component.attribute_try_get_for_output(
|
||||
location_attribute_name, result_domain, CD_PROP_FLOAT3);
|
||||
|
||||
ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position");
|
||||
BLI_assert(position_attribute->custom_data_type() == CD_PROP_FLOAT3);
|
||||
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;
|
||||
}
|
||||
BLI_assert(position_attribute.varray->type().is<float3>());
|
||||
|
||||
const bNode &node = params.node();
|
||||
const NodeGeometryAttributeProximity &storage = *(const NodeGeometryAttributeProximity *)
|
||||
@@ -204,18 +203,15 @@ static void attribute_calc_proximity(GeometryComponent &component,
|
||||
tree_data_pointcloud);
|
||||
}
|
||||
|
||||
Span<float3> position_span = position_attribute->get_span<float3>();
|
||||
|
||||
MutableSpan<float> distance_span = distance_attribute ?
|
||||
distance_attribute->get_span_for_write_only<float>() :
|
||||
MutableSpan<float>();
|
||||
MutableSpan<float3> location_span = location_attribute ?
|
||||
location_attribute->get_span_for_write_only<float3>() :
|
||||
MutableSpan<float3>();
|
||||
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,
|
||||
position_span,
|
||||
positions,
|
||||
tree_data_mesh,
|
||||
tree_data_pointcloud,
|
||||
bvh_mesh_success,
|
||||
@@ -231,10 +227,10 @@ static void attribute_calc_proximity(GeometryComponent &component,
|
||||
}
|
||||
|
||||
if (distance_attribute) {
|
||||
distance_attribute.apply_span_and_save();
|
||||
distance_attribute.save();
|
||||
}
|
||||
if (location_attribute) {
|
||||
location_attribute.apply_span_and_save();
|
||||
location_attribute.save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -173,12 +173,12 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
|
||||
const int domain_size = component.attribute_domain_size(domain);
|
||||
|
||||
/* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */
|
||||
ReadAttributePtr hash_attribute = component.attribute_try_get_for_read("id", domain);
|
||||
std::unique_ptr<GVArray> hash_attribute = component.attribute_try_get_for_read("id", domain);
|
||||
Array<uint32_t> hashes(domain_size);
|
||||
if (hash_attribute) {
|
||||
BLI_assert(hashes.size() == hash_attribute->size());
|
||||
const CPPType &cpp_type = hash_attribute->cpp_type();
|
||||
fn::GSpan items = hash_attribute->get_span();
|
||||
const CPPType &cpp_type = hash_attribute->type();
|
||||
GVArray_GSpan items{*hash_attribute};
|
||||
for (const int i : hashes.index_range()) {
|
||||
hashes[i] = cpp_type.hash(items[i]);
|
||||
}
|
||||
@@ -199,9 +199,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef attribute_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(attribute_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute->domain();
|
||||
return result_attribute.domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the input domain chosen in the interface. */
|
||||
@@ -228,15 +228,13 @@ static void randomize_attribute_on_component(GeometryComponent &component,
|
||||
|
||||
const AttributeDomain domain = get_result_domain(component, params, attribute_name);
|
||||
|
||||
OutputAttributePtr attribute = component.attribute_try_get_for_output(
|
||||
OutputAttribute attribute = component.attribute_try_get_for_output(
|
||||
attribute_name, domain, data_type);
|
||||
if (!attribute) {
|
||||
return;
|
||||
}
|
||||
|
||||
fn::GMutableSpan span = (operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) ?
|
||||
attribute->get_span_for_write_only() :
|
||||
attribute->get_span();
|
||||
GMutableSpan span = attribute.as_span();
|
||||
|
||||
Array<uint32_t> hashes = get_geometry_element_ids_as_uints(component, domain);
|
||||
|
||||
@@ -269,8 +267,8 @@ static void randomize_attribute_on_component(GeometryComponent &component,
|
||||
}
|
||||
}
|
||||
|
||||
attribute.apply_span_and_save();
|
||||
} // namespace blender::nodes
|
||||
attribute.save();
|
||||
}
|
||||
|
||||
static void geo_node_random_attribute_exec(GeoNodeExecParams params)
|
||||
{
|
||||
|
@@ -53,15 +53,16 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef map_attribute_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_attribute_name);
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(
|
||||
result_attribute_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute->domain();
|
||||
return result_attribute.domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the name of the map attribute. */
|
||||
ReadAttributePtr map_attribute = component.attribute_try_get_for_read(map_attribute_name);
|
||||
ReadAttributeLookup map_attribute = component.attribute_try_get_for_read(map_attribute_name);
|
||||
if (map_attribute) {
|
||||
return map_attribute->domain();
|
||||
return map_attribute.domain;
|
||||
}
|
||||
|
||||
/* The node won't execute in this case, but we still have to return a value. */
|
||||
@@ -85,16 +86,16 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
|
||||
const AttributeDomain result_domain = get_result_domain(
|
||||
component, result_attribute_name, mapping_name);
|
||||
|
||||
OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
|
||||
result_attribute_name, result_domain, CD_PROP_COLOR);
|
||||
OutputAttribute_Typed<Color4f> attribute_out =
|
||||
component.attribute_try_get_for_output_only<Color4f>(result_attribute_name, result_domain);
|
||||
if (!attribute_out) {
|
||||
return;
|
||||
}
|
||||
|
||||
Float3ReadAttribute mapping_attribute = component.attribute_get_for_read<float3>(
|
||||
GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>(
|
||||
mapping_name, result_domain, {0, 0, 0});
|
||||
|
||||
MutableSpan<Color4f> colors = attribute_out->get_span<Color4f>();
|
||||
MutableSpan<Color4f> colors = attribute_out.as_span();
|
||||
for (const int i : IndexRange(mapping_attribute.size())) {
|
||||
TexResult texture_result = {0};
|
||||
const float3 position = mapping_attribute[i];
|
||||
@@ -103,7 +104,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
|
||||
BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
|
||||
colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
|
||||
}
|
||||
attribute_out.apply_span_and_save();
|
||||
attribute_out.save();
|
||||
}
|
||||
|
||||
static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
|
||||
|
@@ -77,17 +77,17 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
{
|
||||
/* Use the highest priority domain from any existing attribute outputs. */
|
||||
Vector<AttributeDomain, 3> output_domains;
|
||||
ReadAttributePtr attribute_x = component.attribute_try_get_for_read(result_name_x);
|
||||
ReadAttributePtr attribute_y = component.attribute_try_get_for_read(result_name_y);
|
||||
ReadAttributePtr attribute_z = component.attribute_try_get_for_read(result_name_z);
|
||||
ReadAttributeLookup attribute_x = component.attribute_try_get_for_read(result_name_x);
|
||||
ReadAttributeLookup attribute_y = component.attribute_try_get_for_read(result_name_y);
|
||||
ReadAttributeLookup attribute_z = component.attribute_try_get_for_read(result_name_z);
|
||||
if (attribute_x) {
|
||||
output_domains.append(attribute_x->domain());
|
||||
output_domains.append(attribute_x.domain);
|
||||
}
|
||||
if (attribute_y) {
|
||||
output_domains.append(attribute_y->domain());
|
||||
output_domains.append(attribute_y.domain);
|
||||
}
|
||||
if (attribute_z) {
|
||||
output_domains.append(attribute_z->domain());
|
||||
output_domains.append(attribute_z.domain);
|
||||
}
|
||||
if (output_domains.size() > 0) {
|
||||
return bke::attribute_domain_highest_priority(output_domains);
|
||||
@@ -107,37 +107,32 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa
|
||||
}
|
||||
|
||||
/* The node is only for float3 to float conversions. */
|
||||
const CustomDataType input_type = CD_PROP_FLOAT3;
|
||||
const CustomDataType result_type = CD_PROP_FLOAT;
|
||||
const AttributeDomain result_domain = get_result_domain(
|
||||
component, params, result_name_x, result_name_y, result_name_z);
|
||||
|
||||
ReadAttributePtr attribute_input = params.get_input_attribute(
|
||||
"Vector", component, result_domain, input_type, nullptr);
|
||||
if (!attribute_input) {
|
||||
return;
|
||||
}
|
||||
const Span<float3> input_span = attribute_input->get_span<float3>();
|
||||
GVArray_Typed<float3> attribute_input = params.get_input_attribute<float3>(
|
||||
"Vector", component, result_domain, {0, 0, 0});
|
||||
VArray_Span<float3> input_span{*attribute_input};
|
||||
|
||||
OutputAttributePtr attribute_result_x = component.attribute_try_get_for_output(
|
||||
result_name_x, result_domain, result_type);
|
||||
OutputAttributePtr attribute_result_y = component.attribute_try_get_for_output(
|
||||
result_name_y, result_domain, result_type);
|
||||
OutputAttributePtr attribute_result_z = component.attribute_try_get_for_output(
|
||||
result_name_z, result_domain, result_type);
|
||||
OutputAttribute_Typed<float> attribute_result_x =
|
||||
component.attribute_try_get_for_output_only<float>(result_name_x, result_domain);
|
||||
OutputAttribute_Typed<float> attribute_result_y =
|
||||
component.attribute_try_get_for_output_only<float>(result_name_y, result_domain);
|
||||
OutputAttribute_Typed<float> attribute_result_z =
|
||||
component.attribute_try_get_for_output_only<float>(result_name_z, result_domain);
|
||||
|
||||
/* Only extract the components for the outputs with a given attribute. */
|
||||
if (attribute_result_x) {
|
||||
extract_input(0, input_span, attribute_result_x->get_span_for_write_only<float>());
|
||||
attribute_result_x.apply_span_and_save();
|
||||
extract_input(0, input_span, attribute_result_x.as_span());
|
||||
attribute_result_x.save();
|
||||
}
|
||||
if (attribute_result_y) {
|
||||
extract_input(1, input_span, attribute_result_y->get_span_for_write_only<float>());
|
||||
attribute_result_y.apply_span_and_save();
|
||||
extract_input(1, input_span, attribute_result_y.as_span());
|
||||
attribute_result_y.save();
|
||||
}
|
||||
if (attribute_result_z) {
|
||||
extract_input(2, input_span, attribute_result_z->get_span_for_write_only<float>());
|
||||
attribute_result_z.apply_span_and_save();
|
||||
extract_input(2, input_span, attribute_result_z.as_span());
|
||||
attribute_result_z.save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -168,16 +168,16 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod
|
||||
operation_use_input_c(operation));
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
|
||||
const Float3ReadAttribute &input_b,
|
||||
Float3WriteAttribute result,
|
||||
static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a,
|
||||
const VArray<float3> &input_b,
|
||||
VMutableArray<float3> &result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
Span<float3> span_b = input_b.get_span();
|
||||
MutableSpan<float3> span_result = result.get_span_for_write_only();
|
||||
VArray_Span<float3> span_a{input_a};
|
||||
VArray_Span<float3> span_b{input_b};
|
||||
VMutableArray_Span<float3> span_result{result, false};
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_fl3_to_fl3(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
@@ -189,25 +189,25 @@ static void do_math_operation_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
span_result.save();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
|
||||
const Float3ReadAttribute &input_b,
|
||||
const Float3ReadAttribute &input_c,
|
||||
Float3WriteAttribute result,
|
||||
static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a,
|
||||
const VArray<float3> &input_b,
|
||||
const VArray<float3> &input_c,
|
||||
VMutableArray<float3> &result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
Span<float3> span_b = input_b.get_span();
|
||||
Span<float3> span_c = input_c.get_span();
|
||||
MutableSpan<float3> span_result = result.get_span_for_write_only();
|
||||
VArray_Span<float3> span_a{input_a};
|
||||
VArray_Span<float3> span_b{input_b};
|
||||
VArray_Span<float3> span_c{input_c};
|
||||
VMutableArray_Span<float3> span_result{result};
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
@@ -220,25 +220,25 @@ static void do_math_operation_fl3_fl3_fl3_to_fl3(const Float3ReadAttribute &inpu
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
span_result.save();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
|
||||
const Float3ReadAttribute &input_b,
|
||||
const FloatReadAttribute &input_c,
|
||||
Float3WriteAttribute result,
|
||||
static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a,
|
||||
const VArray<float3> &input_b,
|
||||
const VArray<float> &input_c,
|
||||
VMutableArray<float3> &result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
Span<float3> span_b = input_b.get_span();
|
||||
Span<float> span_c = input_c.get_span();
|
||||
MutableSpan<float3> span_result = result.get_span_for_write_only();
|
||||
VArray_Span<float3> span_a{input_a};
|
||||
VArray_Span<float3> span_b{input_b};
|
||||
VArray_Span<float> span_c{input_c};
|
||||
VMutableArray_Span<float3> span_result{result, false};
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_fl3_fl_to_fl3(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
@@ -251,23 +251,23 @@ static void do_math_operation_fl3_fl3_fl_to_fl3(const Float3ReadAttribute &input
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
span_result.save();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_fl3_to_fl(const Float3ReadAttribute &input_a,
|
||||
const Float3ReadAttribute &input_b,
|
||||
FloatWriteAttribute result,
|
||||
static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a,
|
||||
const VArray<float3> &input_b,
|
||||
VMutableArray<float> &result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
Span<float3> span_b = input_b.get_span();
|
||||
MutableSpan<float> span_result = result.get_span_for_write_only();
|
||||
VArray_Span<float3> span_a{input_a};
|
||||
VArray_Span<float3> span_b{input_b};
|
||||
VMutableArray_Span<float> span_result{result, false};
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_fl3_to_fl(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
@@ -279,23 +279,23 @@ static void do_math_operation_fl3_fl3_to_fl(const Float3ReadAttribute &input_a,
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
span_result.save();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
|
||||
const FloatReadAttribute &input_b,
|
||||
Float3WriteAttribute result,
|
||||
static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a,
|
||||
const VArray<float> &input_b,
|
||||
VMutableArray<float3> &result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
Span<float> span_b = input_b.get_span();
|
||||
MutableSpan<float3> span_result = result.get_span_for_write_only();
|
||||
VArray_Span<float3> span_a{input_a};
|
||||
VArray_Span<float> span_b{input_b};
|
||||
VMutableArray_Span<float3> span_result{result, false};
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_fl_to_fl3(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
@@ -307,21 +307,21 @@ static void do_math_operation_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
span_result.save();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_to_fl3(const Float3ReadAttribute &input_a,
|
||||
Float3WriteAttribute result,
|
||||
static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a,
|
||||
VMutableArray<float3> &result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
MutableSpan<float3> span_result = result.get_span_for_write_only();
|
||||
VArray_Span<float3> span_a{input_a};
|
||||
VMutableArray_Span<float3> span_result{result, false};
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_to_fl3(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
@@ -332,21 +332,21 @@ static void do_math_operation_fl3_to_fl3(const Float3ReadAttribute &input_a,
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
span_result.save();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
}
|
||||
|
||||
static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a,
|
||||
FloatWriteAttribute result,
|
||||
static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a,
|
||||
VMutableArray<float> &result,
|
||||
const NodeVectorMathOperation operation)
|
||||
{
|
||||
const int size = input_a.size();
|
||||
|
||||
Span<float3> span_a = input_a.get_span();
|
||||
MutableSpan<float> span_result = result.get_span_for_write_only();
|
||||
VArray_Span<float3> span_a{input_a};
|
||||
VMutableArray_Span<float> span_result{result, false};
|
||||
|
||||
bool success = try_dispatch_float_math_fl3_to_fl(
|
||||
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
|
||||
@@ -357,7 +357,7 @@ static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a,
|
||||
}
|
||||
});
|
||||
|
||||
result.apply_span();
|
||||
span_result.save();
|
||||
|
||||
/* The operation is not supported by this node currently. */
|
||||
BLI_assert(success);
|
||||
@@ -370,9 +370,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef result_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute->domain();
|
||||
return result_attribute.domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
|
||||
@@ -406,13 +406,13 @@ static void attribute_vector_math_calc(GeometryComponent &component,
|
||||
const AttributeDomain result_domain = get_result_domain(
|
||||
component, params, operation, result_name);
|
||||
|
||||
ReadAttributePtr attribute_a = params.get_input_attribute(
|
||||
std::unique_ptr<GVArray> attribute_a = params.get_input_attribute(
|
||||
"A", component, result_domain, read_type_a, nullptr);
|
||||
if (!attribute_a) {
|
||||
return;
|
||||
}
|
||||
ReadAttributePtr attribute_b;
|
||||
ReadAttributePtr attribute_c;
|
||||
std::unique_ptr<GVArray> attribute_b;
|
||||
std::unique_ptr<GVArray> attribute_c;
|
||||
if (use_input_b) {
|
||||
attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr);
|
||||
if (!attribute_b) {
|
||||
@@ -427,7 +427,7 @@ static void attribute_vector_math_calc(GeometryComponent &component,
|
||||
}
|
||||
|
||||
/* Get result attribute first, in case it has to overwrite one of the existing attributes. */
|
||||
OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
|
||||
OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
|
||||
result_name, result_domain, result_type);
|
||||
if (!attribute_result) {
|
||||
return;
|
||||
@@ -445,17 +445,27 @@ static void attribute_vector_math_calc(GeometryComponent &component,
|
||||
case NODE_VECTOR_MATH_MODULO:
|
||||
case NODE_VECTOR_MATH_MINIMUM:
|
||||
case NODE_VECTOR_MATH_MAXIMUM:
|
||||
do_math_operation_fl3_fl3_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation);
|
||||
do_math_operation_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
|
||||
attribute_b->typed<float3>(),
|
||||
attribute_result->typed<float3>(),
|
||||
operation);
|
||||
break;
|
||||
case NODE_VECTOR_MATH_DOT_PRODUCT:
|
||||
case NODE_VECTOR_MATH_DISTANCE:
|
||||
do_math_operation_fl3_fl3_to_fl(*attribute_a, *attribute_b, *attribute_result, operation);
|
||||
do_math_operation_fl3_fl3_to_fl(attribute_a->typed<float3>(),
|
||||
attribute_b->typed<float3>(),
|
||||
attribute_result->typed<float>(),
|
||||
operation);
|
||||
break;
|
||||
case NODE_VECTOR_MATH_LENGTH:
|
||||
do_math_operation_fl3_to_fl(*attribute_a, *attribute_result, operation);
|
||||
do_math_operation_fl3_to_fl(
|
||||
attribute_a->typed<float3>(), attribute_result->typed<float>(), operation);
|
||||
break;
|
||||
case NODE_VECTOR_MATH_SCALE:
|
||||
do_math_operation_fl3_fl_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation);
|
||||
do_math_operation_fl3_fl_to_fl3(attribute_a->typed<float3>(),
|
||||
attribute_b->typed<float>(),
|
||||
attribute_result->typed<float3>(),
|
||||
operation);
|
||||
break;
|
||||
case NODE_VECTOR_MATH_NORMALIZE:
|
||||
case NODE_VECTOR_MATH_FLOOR:
|
||||
@@ -465,16 +475,23 @@ static void attribute_vector_math_calc(GeometryComponent &component,
|
||||
case NODE_VECTOR_MATH_SINE:
|
||||
case NODE_VECTOR_MATH_COSINE:
|
||||
case NODE_VECTOR_MATH_TANGENT:
|
||||
do_math_operation_fl3_to_fl3(*attribute_a, *attribute_result, operation);
|
||||
do_math_operation_fl3_to_fl3(
|
||||
attribute_a->typed<float3>(), attribute_result->typed<float3>(), operation);
|
||||
break;
|
||||
case NODE_VECTOR_MATH_WRAP:
|
||||
case NODE_VECTOR_MATH_FACEFORWARD:
|
||||
do_math_operation_fl3_fl3_fl3_to_fl3(
|
||||
*attribute_a, *attribute_b, *attribute_c, *attribute_result, operation);
|
||||
do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
|
||||
attribute_b->typed<float3>(),
|
||||
attribute_c->typed<float3>(),
|
||||
attribute_result->typed<float3>(),
|
||||
operation);
|
||||
break;
|
||||
case NODE_VECTOR_MATH_REFRACT:
|
||||
do_math_operation_fl3_fl3_fl_to_fl3(
|
||||
*attribute_a, *attribute_b, *attribute_c, *attribute_result, operation);
|
||||
do_math_operation_fl3_fl3_fl_to_fl3(attribute_a->typed<float3>(),
|
||||
attribute_b->typed<float3>(),
|
||||
attribute_c->typed<float>(),
|
||||
attribute_result->typed<float3>(),
|
||||
operation);
|
||||
break;
|
||||
}
|
||||
attribute_result.save();
|
||||
|
@@ -39,15 +39,12 @@ static void compute_min_max_from_position_and_transform(const GeometryComponent
|
||||
float3 &r_min,
|
||||
float3 &r_max)
|
||||
{
|
||||
ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position");
|
||||
if (!position_attribute) {
|
||||
BLI_assert(component.attribute_domain_size(ATTR_DOMAIN_POINT) == 0);
|
||||
return;
|
||||
}
|
||||
Span<float3> positions = position_attribute->get_span<float3>();
|
||||
GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
|
||||
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
|
||||
for (const float4x4 &transform : transforms) {
|
||||
for (const float3 &position : positions) {
|
||||
for (const int i : positions.index_range()) {
|
||||
const float3 position = positions[i];
|
||||
const float3 transformed_position = transform * position;
|
||||
minmax_v3v3_v3(r_min, r_max, transformed_position);
|
||||
}
|
||||
|
@@ -149,10 +149,10 @@ static void determine_final_data_type_and_domain(Span<const GeometryComponent *>
|
||||
Vector<CustomDataType> data_types;
|
||||
Vector<AttributeDomain> domains;
|
||||
for (const GeometryComponent *component : components) {
|
||||
ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name);
|
||||
ReadAttributeLookup attribute = component->attribute_try_get_for_read(attribute_name);
|
||||
if (attribute) {
|
||||
data_types.append(attribute->custom_data_type());
|
||||
domains.append(attribute->domain());
|
||||
data_types.append(bke::cpp_type_to_custom_data_type(attribute.varray->type()));
|
||||
domains.append(attribute.domain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
|
||||
StringRef attribute_name,
|
||||
const CustomDataType data_type,
|
||||
const AttributeDomain domain,
|
||||
fn::GMutableSpan dst_span)
|
||||
GMutableSpan dst_span)
|
||||
{
|
||||
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
@@ -175,10 +175,10 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
|
||||
if (domain_size == 0) {
|
||||
continue;
|
||||
}
|
||||
ReadAttributePtr read_attribute = component->attribute_get_for_read(
|
||||
std::unique_ptr<GVArray> read_attribute = component->attribute_get_for_read(
|
||||
attribute_name, domain, data_type, nullptr);
|
||||
|
||||
fn::GSpan src_span = read_attribute->get_span();
|
||||
GVArray_GSpan src_span{*read_attribute};
|
||||
const void *src_buffer = src_span.data();
|
||||
void *dst_buffer = dst_span[offset];
|
||||
cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size);
|
||||
@@ -201,16 +201,14 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
|
||||
AttributeDomain domain;
|
||||
determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain);
|
||||
|
||||
OutputAttributePtr write_attribute = result.attribute_try_get_for_output(
|
||||
OutputAttribute write_attribute = result.attribute_try_get_for_output_only(
|
||||
attribute_name, domain, data_type);
|
||||
if (!write_attribute ||
|
||||
&write_attribute->cpp_type() != bke::custom_data_type_to_cpp_type(data_type) ||
|
||||
write_attribute->domain() != domain) {
|
||||
if (!write_attribute) {
|
||||
continue;
|
||||
}
|
||||
fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
|
||||
GMutableSpan dst_span = write_attribute.as_span();
|
||||
fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span);
|
||||
write_attribute.apply_span_and_save();
|
||||
write_attribute.save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -194,9 +194,9 @@ static void calculate_uvs(Mesh *mesh,
|
||||
{
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
|
||||
"uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
|
||||
MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
|
||||
OutputAttribute_Typed<float2> uv_attribute =
|
||||
mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
|
||||
MutableSpan<float2> uvs = uv_attribute.as_span();
|
||||
|
||||
Array<float2> circle(verts_num);
|
||||
float angle = 0.0f;
|
||||
@@ -271,7 +271,7 @@ static void calculate_uvs(Mesh *mesh,
|
||||
}
|
||||
}
|
||||
|
||||
uv_attribute.apply_span_and_save();
|
||||
uv_attribute.save();
|
||||
}
|
||||
|
||||
Mesh *create_cylinder_or_cone_mesh(const float radius_top,
|
||||
|
@@ -44,9 +44,9 @@ static void calculate_uvs(
|
||||
{
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
|
||||
"uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
|
||||
MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
|
||||
OutputAttribute_Typed<float2> uv_attribute =
|
||||
mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
|
||||
MutableSpan<float2> uvs = uv_attribute.as_span();
|
||||
|
||||
const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x;
|
||||
const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y;
|
||||
@@ -56,7 +56,7 @@ static void calculate_uvs(
|
||||
uvs[i].y = (co.y + size_y * 0.5f) * dy;
|
||||
}
|
||||
|
||||
uv_attribute.apply_span_and_save();
|
||||
uv_attribute.save();
|
||||
}
|
||||
|
||||
static Mesh *create_grid_mesh(const int verts_x,
|
||||
|
@@ -224,9 +224,9 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r
|
||||
{
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
|
||||
"uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
|
||||
MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
|
||||
OutputAttribute_Typed<float2> uv_attribute =
|
||||
mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
|
||||
MutableSpan<float2> uvs = uv_attribute.as_span();
|
||||
|
||||
int loop_index = 0;
|
||||
const float dy = 1.0f / rings;
|
||||
@@ -256,7 +256,7 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r
|
||||
uvs[loop_index++] = float2(segment / segments, 1.0f - dy);
|
||||
}
|
||||
|
||||
uv_attribute.apply_span_and_save();
|
||||
uv_attribute.save();
|
||||
}
|
||||
|
||||
static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings)
|
||||
|
@@ -91,7 +91,7 @@ static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
|
||||
static void sample_mesh_surface(const Mesh &mesh,
|
||||
const float4x4 &transform,
|
||||
const float base_density,
|
||||
const FloatReadAttribute *density_factors,
|
||||
const VArray<float> *density_factors,
|
||||
const int seed,
|
||||
Vector<float3> &r_positions,
|
||||
Vector<float3> &r_bary_coords,
|
||||
@@ -113,9 +113,9 @@ static void sample_mesh_surface(const Mesh &mesh,
|
||||
|
||||
float looptri_density_factor = 1.0f;
|
||||
if (density_factors != nullptr) {
|
||||
const float v0_density_factor = std::max(0.0f, (*density_factors)[v0_loop]);
|
||||
const float v1_density_factor = std::max(0.0f, (*density_factors)[v1_loop]);
|
||||
const float v2_density_factor = std::max(0.0f, (*density_factors)[v2_loop]);
|
||||
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 area = area_tri_v3(v0_pos, v1_pos, v2_pos);
|
||||
@@ -203,7 +203,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points(
|
||||
|
||||
BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
|
||||
const Mesh &mesh,
|
||||
const FloatReadAttribute &density_factors,
|
||||
const VArray<float> &density_factors,
|
||||
Span<float3> bary_coords,
|
||||
Span<int> looptri_indices,
|
||||
MutableSpan<bool> elimination_mask)
|
||||
@@ -363,13 +363,13 @@ BLI_NOINLINE static void interpolate_existing_attributes(
|
||||
StringRef attribute_name = entry.key;
|
||||
const CustomDataType output_data_type = entry.value.data_type;
|
||||
/* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
|
||||
OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
|
||||
OutputAttribute attribute_out = component.attribute_try_get_for_output_only(
|
||||
attribute_name, ATTR_DOMAIN_POINT, output_data_type);
|
||||
if (!attribute_out) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fn::GMutableSpan out_span = attribute_out->get_span_for_write_only();
|
||||
GMutableSpan out_span = attribute_out.as_span();
|
||||
|
||||
int i_instance = 0;
|
||||
for (const GeometryInstanceGroup &set_group : set_groups) {
|
||||
@@ -380,44 +380,41 @@ BLI_NOINLINE static void interpolate_existing_attributes(
|
||||
/* Use a dummy read without specifying a domain or data type in order to
|
||||
* get the existing attribute's domain. Interpolation is done manually based
|
||||
* on the bary coords in #interpolate_attribute. */
|
||||
ReadAttributePtr dummy_attribute = source_component.attribute_try_get_for_read(
|
||||
ReadAttributeLookup dummy_attribute = source_component.attribute_try_get_for_read(
|
||||
attribute_name);
|
||||
if (!dummy_attribute) {
|
||||
i_instance += set_group.transforms.size();
|
||||
continue;
|
||||
}
|
||||
|
||||
const AttributeDomain source_domain = dummy_attribute->domain();
|
||||
ReadAttributePtr source_attribute = source_component.attribute_get_for_read(
|
||||
const AttributeDomain source_domain = dummy_attribute.domain;
|
||||
std::unique_ptr<GVArray> source_attribute = source_component.attribute_get_for_read(
|
||||
attribute_name, source_domain, output_data_type, nullptr);
|
||||
if (!source_attribute) {
|
||||
i_instance += set_group.transforms.size();
|
||||
continue;
|
||||
}
|
||||
fn::GSpan source_span = source_attribute->get_span();
|
||||
|
||||
attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
|
||||
GVArray_Span<T> source_span{*source_attribute};
|
||||
|
||||
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
|
||||
const int offset = instance_start_offsets[i_instance];
|
||||
Span<float3> bary_coords = bary_coords_array[i_instance];
|
||||
Span<int> looptri_indices = looptri_indices_array[i_instance];
|
||||
|
||||
MutableSpan<T> instance_span = out_span.typed<T>().slice(offset, bary_coords.size());
|
||||
interpolate_attribute<T>(mesh,
|
||||
bary_coords,
|
||||
looptri_indices,
|
||||
source_domain,
|
||||
source_span.typed<T>(),
|
||||
instance_span);
|
||||
interpolate_attribute<T>(
|
||||
mesh, bary_coords, looptri_indices, source_domain, source_span, instance_span);
|
||||
|
||||
i_instance++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
attribute_out.apply_span_and_save();
|
||||
attribute_out.save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,16 +424,16 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
|
||||
Span<Vector<float3>> bary_coords_array,
|
||||
Span<Vector<int>> looptri_indices_array)
|
||||
{
|
||||
OutputAttributePtr id_attribute = component.attribute_try_get_for_output(
|
||||
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
|
||||
OutputAttributePtr normal_attribute = component.attribute_try_get_for_output(
|
||||
"normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
|
||||
OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
|
||||
"rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
|
||||
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);
|
||||
|
||||
MutableSpan<int> result_ids = id_attribute->get_span_for_write_only<int>();
|
||||
MutableSpan<float3> result_normals = normal_attribute->get_span_for_write_only<float3>();
|
||||
MutableSpan<float3> result_rotations = rotation_attribute->get_span_for_write_only<float3>();
|
||||
MutableSpan<int> result_ids = id_attribute.as_span();
|
||||
MutableSpan<float3> result_normals = normal_attribute.as_span();
|
||||
MutableSpan<float3> result_rotations = rotation_attribute.as_span();
|
||||
|
||||
int i_instance = 0;
|
||||
for (const GeometryInstanceGroup &set_group : sets) {
|
||||
@@ -480,9 +477,9 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
|
||||
}
|
||||
}
|
||||
|
||||
id_attribute.apply_span_and_save();
|
||||
normal_attribute.apply_span_and_save();
|
||||
rotation_attribute.apply_span_and_save();
|
||||
id_attribute.save();
|
||||
normal_attribute.save();
|
||||
rotation_attribute.save();
|
||||
}
|
||||
|
||||
BLI_NOINLINE static void add_remaining_point_attributes(
|
||||
@@ -520,7 +517,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
|
||||
for (const GeometryInstanceGroup &set_group : set_groups) {
|
||||
const GeometrySet &set = set_group.geometry_set;
|
||||
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
|
||||
const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
|
||||
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();
|
||||
for (const float4x4 &transform : set_group.transforms) {
|
||||
@@ -530,7 +527,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
|
||||
sample_mesh_surface(mesh,
|
||||
transform,
|
||||
density,
|
||||
&density_factors,
|
||||
&*density_factors,
|
||||
seed,
|
||||
positions,
|
||||
bary_coords,
|
||||
@@ -589,7 +586,7 @@ 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 FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
|
||||
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);
|
||||
|
||||
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
|
||||
@@ -622,7 +619,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
|
||||
const GeometryNodePointDistributeMode distribute_method =
|
||||
static_cast<GeometryNodePointDistributeMode>(params.node().custom1);
|
||||
|
||||
const int seed = params.get_input<int>("Seed");
|
||||
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");
|
||||
|
@@ -174,13 +174,13 @@ static void add_instances_from_geometry_component(InstancesComponent &instances,
|
||||
Array<std::optional<InstancedData>> instances_data = get_instanced_data(
|
||||
params, src_geometry, domain_size);
|
||||
|
||||
Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>(
|
||||
GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>(
|
||||
"position", domain, {0, 0, 0});
|
||||
Float3ReadAttribute rotations = src_geometry.attribute_get_for_read<float3>(
|
||||
GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>(
|
||||
"rotation", domain, {0, 0, 0});
|
||||
Float3ReadAttribute scales = src_geometry.attribute_get_for_read<float3>(
|
||||
GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>(
|
||||
"scale", domain, {1, 1, 1});
|
||||
Int32ReadAttribute ids = src_geometry.attribute_get_for_read<int>("id", domain, -1);
|
||||
GVArray_Typed<int> ids = src_geometry.attribute_get_for_read<int>("id", domain, -1);
|
||||
|
||||
for (const int i : IndexRange(domain_size)) {
|
||||
if (instances_data[i].has_value()) {
|
||||
|
@@ -60,8 +60,8 @@ static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C),
|
||||
namespace blender::nodes {
|
||||
|
||||
static void point_rotate__axis_angle__object_space(const int domain_size,
|
||||
const Float3ReadAttribute &axis,
|
||||
const FloatReadAttribute &angles,
|
||||
const VArray<float3> &axis,
|
||||
const VArray<float> &angles,
|
||||
MutableSpan<float3> rotations)
|
||||
{
|
||||
for (const int i : IndexRange(domain_size)) {
|
||||
@@ -76,8 +76,8 @@ static void point_rotate__axis_angle__object_space(const int domain_size,
|
||||
}
|
||||
|
||||
static void point_rotate__axis_angle__point_space(const int domain_size,
|
||||
const Float3ReadAttribute &axis,
|
||||
const FloatReadAttribute &angles,
|
||||
const VArray<float3> &axis,
|
||||
const VArray<float> &angles,
|
||||
MutableSpan<float3> rotations)
|
||||
{
|
||||
for (const int i : IndexRange(domain_size)) {
|
||||
@@ -92,7 +92,7 @@ static void point_rotate__axis_angle__point_space(const int domain_size,
|
||||
}
|
||||
|
||||
static void point_rotate__euler__object_space(const int domain_size,
|
||||
const Float3ReadAttribute &eulers,
|
||||
const VArray<float3> &eulers,
|
||||
MutableSpan<float3> rotations)
|
||||
{
|
||||
for (const int i : IndexRange(domain_size)) {
|
||||
@@ -107,7 +107,7 @@ static void point_rotate__euler__object_space(const int domain_size,
|
||||
}
|
||||
|
||||
static void point_rotate__euler__point_space(const int domain_size,
|
||||
const Float3ReadAttribute &eulers,
|
||||
const VArray<float3> &eulers,
|
||||
MutableSpan<float3> rotations)
|
||||
{
|
||||
for (const int i : IndexRange(domain_size)) {
|
||||
@@ -127,19 +127,19 @@ static void point_rotate_on_component(GeometryComponent &component,
|
||||
const bNode &node = params.node();
|
||||
const NodeGeometryRotatePoints &storage = *(const NodeGeometryRotatePoints *)node.storage;
|
||||
|
||||
OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
|
||||
"rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
|
||||
OutputAttribute_Typed<float3> rotation_attribute =
|
||||
component.attribute_try_get_for_output<float3>("rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
if (!rotation_attribute) {
|
||||
return;
|
||||
}
|
||||
|
||||
MutableSpan<float3> rotations = rotation_attribute->get_span<float3>();
|
||||
MutableSpan<float3> rotations = rotation_attribute.as_span();
|
||||
const int domain_size = rotations.size();
|
||||
|
||||
if (storage.type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE) {
|
||||
Float3ReadAttribute axis = params.get_input_attribute<float3>(
|
||||
GVArray_Typed<float3> axis = params.get_input_attribute<float3>(
|
||||
"Axis", component, ATTR_DOMAIN_POINT, {0, 0, 1});
|
||||
FloatReadAttribute angles = params.get_input_attribute<float>(
|
||||
GVArray_Typed<float> angles = params.get_input_attribute<float>(
|
||||
"Angle", component, ATTR_DOMAIN_POINT, 0);
|
||||
|
||||
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
|
||||
@@ -150,7 +150,7 @@ static void point_rotate_on_component(GeometryComponent &component,
|
||||
}
|
||||
}
|
||||
else {
|
||||
Float3ReadAttribute eulers = params.get_input_attribute<float3>(
|
||||
GVArray_Typed<float3> eulers = params.get_input_attribute<float3>(
|
||||
"Rotation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
|
||||
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
|
||||
@@ -161,7 +161,7 @@ static void point_rotate_on_component(GeometryComponent &component,
|
||||
}
|
||||
}
|
||||
|
||||
rotation_attribute.apply_span_and_save();
|
||||
rotation_attribute.save();
|
||||
}
|
||||
|
||||
static void geo_node_point_rotate_exec(GeoNodeExecParams params)
|
||||
|
@@ -50,7 +50,7 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
|
||||
* for the factor. But for it's simpler to simply always use float3, since that is usually
|
||||
* expected anyway. */
|
||||
static const float3 scale_default = float3(1.0f);
|
||||
OutputAttributePtr scale_attribute = component.attribute_try_get_for_output(
|
||||
OutputAttribute_Typed<float3> scale_attribute = component.attribute_try_get_for_output(
|
||||
"scale", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, &scale_default);
|
||||
if (!scale_attribute) {
|
||||
return;
|
||||
@@ -63,27 +63,27 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
|
||||
const CustomDataType data_type = (input_type == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ? CD_PROP_FLOAT :
|
||||
CD_PROP_FLOAT3;
|
||||
|
||||
ReadAttributePtr attribute = params.get_input_attribute(
|
||||
std::unique_ptr<GVArray> attribute = params.get_input_attribute(
|
||||
"Factor", component, ATTR_DOMAIN_POINT, data_type, nullptr);
|
||||
if (!attribute) {
|
||||
return;
|
||||
}
|
||||
|
||||
MutableSpan<float3> scale_span = scale_attribute->get_span<float3>();
|
||||
MutableSpan<float3> scale_span = scale_attribute.as_span();
|
||||
if (data_type == CD_PROP_FLOAT) {
|
||||
Span<float> factors = attribute->get_span<float>();
|
||||
GVArray_Typed<float> factors{*attribute};
|
||||
for (const int i : scale_span.index_range()) {
|
||||
scale_span[i] = scale_span[i] * factors[i];
|
||||
}
|
||||
}
|
||||
else if (data_type == CD_PROP_FLOAT3) {
|
||||
Span<float3> factors = attribute->get_span<float3>();
|
||||
GVArray_Typed<float3> factors{*attribute};
|
||||
for (const int i : scale_span.index_range()) {
|
||||
scale_span[i] = scale_span[i] * factors[i];
|
||||
}
|
||||
}
|
||||
|
||||
scale_attribute.apply_span_and_save();
|
||||
scale_attribute.save();
|
||||
}
|
||||
|
||||
static void geo_node_point_scale_exec(GeoNodeExecParams params)
|
||||
|
@@ -58,27 +58,27 @@ static void copy_attributes_based_on_mask(const GeometryComponent &in_component,
|
||||
const bool invert)
|
||||
{
|
||||
for (const std::string &name : in_component.attribute_names()) {
|
||||
ReadAttributePtr attribute = in_component.attribute_try_get_for_read(name);
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name);
|
||||
const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type());
|
||||
|
||||
/* Only copy point attributes. Theoretically this could interpolate attributes on other
|
||||
* domains to the point domain, but that would conflict with attributes that are built-in
|
||||
* on other domains, which causes creating the attributes to fail. */
|
||||
if (attribute->domain() != ATTR_DOMAIN_POINT) {
|
||||
if (attribute.domain != ATTR_DOMAIN_POINT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
OutputAttributePtr result_attribute = result_component.attribute_try_get_for_output(
|
||||
OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
|
||||
name, ATTR_DOMAIN_POINT, data_type);
|
||||
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
Span<T> span = attribute->get_span<T>();
|
||||
MutableSpan<T> out_span = result_attribute->get_span_for_write_only<T>();
|
||||
GVArray_Span<T> span{*attribute.varray};
|
||||
MutableSpan<T> out_span = result_attribute.as_span<T>();
|
||||
copy_data_based_on_mask(span, masks, invert, out_span);
|
||||
});
|
||||
|
||||
result_attribute.apply_span_and_save();
|
||||
result_attribute.save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,9 +107,9 @@ static void separate_points_from_component(const GeometryComponent &in_component
|
||||
return;
|
||||
}
|
||||
|
||||
const BooleanReadAttribute mask_attribute = in_component.attribute_get_for_read<bool>(
|
||||
const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>(
|
||||
mask_name, ATTR_DOMAIN_POINT, false);
|
||||
Span<bool> masks = mask_attribute.get_span();
|
||||
VArray_Span<bool> masks{mask_attribute};
|
||||
|
||||
const int total = masks.count(!invert);
|
||||
if (total == 0) {
|
||||
|
@@ -42,24 +42,19 @@ namespace blender::nodes {
|
||||
|
||||
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
|
||||
{
|
||||
OutputAttributePtr position_attribute = component.attribute_try_get_for_output(
|
||||
"position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
|
||||
OutputAttribute_Typed<float3> position_attribute =
|
||||
component.attribute_try_get_for_output<float3>("position", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
if (!position_attribute) {
|
||||
return;
|
||||
}
|
||||
ReadAttributePtr attribute = params.get_input_attribute(
|
||||
"Translation", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr);
|
||||
if (!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]);
|
||||
}
|
||||
|
||||
Span<float3> data = attribute->get_span<float3>();
|
||||
MutableSpan<float3> scale_span = position_attribute->get_span<float3>();
|
||||
for (const int i : scale_span.index_range()) {
|
||||
scale_span[i] = scale_span[i] + data[i];
|
||||
}
|
||||
|
||||
position_attribute.apply_span_and_save();
|
||||
position_attribute.save();
|
||||
}
|
||||
|
||||
static void geo_node_point_translate_exec(GeoNodeExecParams params)
|
||||
|
@@ -147,13 +147,15 @@ static void gather_point_data_from_component(const GeoNodeExecParams ¶ms,
|
||||
Vector<float3> &r_positions,
|
||||
Vector<float> &r_radii)
|
||||
{
|
||||
Float3ReadAttribute positions = component.attribute_get_for_read<float3>(
|
||||
GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
|
||||
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
FloatReadAttribute radii = params.get_input_attribute<float>(
|
||||
GVArray_Typed<float> radii = params.get_input_attribute<float>(
|
||||
"Radius", component, ATTR_DOMAIN_POINT, 0.0f);
|
||||
|
||||
r_positions.extend(positions.get_span());
|
||||
r_radii.extend(radii.get_span());
|
||||
for (const int i : IndexRange(positions.size())) {
|
||||
r_positions.append(positions[i]);
|
||||
r_radii.append(radii[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void convert_to_grid_index_space(const float voxel_size,
|
||||
|
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "NOD_geometry_exec.hh"
|
||||
#include "NOD_type_callbacks.hh"
|
||||
#include "NOD_type_conversions.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
@@ -53,22 +54,29 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
|
||||
const GeometryComponent &component,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType type,
|
||||
const void *default_value) const
|
||||
std::unique_ptr<GVArray> GeoNodeExecParams::get_input_attribute(const StringRef name,
|
||||
const GeometryComponent &component,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType type,
|
||||
const void *default_value) const
|
||||
{
|
||||
const bNodeSocket *found_socket = this->find_available_socket(name);
|
||||
BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */
|
||||
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type);
|
||||
const int64_t domain_size = component.attribute_domain_size(domain);
|
||||
|
||||
if (default_value == nullptr) {
|
||||
default_value = cpp_type->default_value();
|
||||
}
|
||||
|
||||
if (found_socket == nullptr) {
|
||||
return component.attribute_get_constant_for_read(domain, type, default_value);
|
||||
return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
|
||||
}
|
||||
|
||||
if (found_socket->type == SOCK_STRING) {
|
||||
const std::string name = this->get_input<std::string>(found_socket->identifier);
|
||||
/* Try getting the attribute without the default value. */
|
||||
ReadAttributePtr attribute = component.attribute_try_get_for_read(name, domain, type);
|
||||
std::unique_ptr<GVArray> attribute = component.attribute_try_get_for_read(name, domain, type);
|
||||
if (attribute) {
|
||||
return attribute;
|
||||
}
|
||||
@@ -80,25 +88,29 @@ ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
|
||||
this->error_message_add(NodeWarningType::Error,
|
||||
TIP_("No attribute with name \"") + name + "\"");
|
||||
}
|
||||
return component.attribute_get_constant_for_read(domain, type, default_value);
|
||||
return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
|
||||
}
|
||||
const DataTypeConversions &conversions = get_implicit_type_conversions();
|
||||
if (found_socket->type == SOCK_FLOAT) {
|
||||
const float value = this->get_input<float>(found_socket->identifier);
|
||||
return component.attribute_get_constant_for_read_converted(
|
||||
domain, CD_PROP_FLOAT, type, &value);
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
|
||||
conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer);
|
||||
return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
|
||||
}
|
||||
if (found_socket->type == SOCK_VECTOR) {
|
||||
const float3 value = this->get_input<float3>(found_socket->identifier);
|
||||
return component.attribute_get_constant_for_read_converted(
|
||||
domain, CD_PROP_FLOAT3, type, &value);
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
|
||||
conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer);
|
||||
return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
|
||||
}
|
||||
if (found_socket->type == SOCK_RGBA) {
|
||||
const Color4f value = this->get_input<Color4f>(found_socket->identifier);
|
||||
return component.attribute_get_constant_for_read_converted(
|
||||
domain, CD_PROP_COLOR, type, &value);
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
|
||||
conversions.convert_to_uninitialized(CPPType::get<Color4f>(), *cpp_type, &value, buffer);
|
||||
return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
|
||||
}
|
||||
BLI_assert(false);
|
||||
return component.attribute_get_constant_for_read(domain, type, default_value);
|
||||
return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
|
||||
}
|
||||
|
||||
CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
|
||||
@@ -114,11 +126,11 @@ CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
|
||||
|
||||
if (found_socket->type == SOCK_STRING) {
|
||||
const std::string name = this->get_input<std::string>(found_socket->identifier);
|
||||
ReadAttributePtr attribute = component.attribute_try_get_for_read(name);
|
||||
ReadAttributeLookup attribute = component.attribute_try_get_for_read(name);
|
||||
if (!attribute) {
|
||||
return default_type;
|
||||
}
|
||||
return attribute->custom_data_type();
|
||||
return bke::cpp_type_to_custom_data_type(attribute.varray->type());
|
||||
}
|
||||
if (found_socket->type == SOCK_FLOAT) {
|
||||
return CD_PROP_FLOAT;
|
||||
@@ -157,9 +169,9 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
|
||||
|
||||
if (found_socket->type == SOCK_STRING) {
|
||||
const std::string name = this->get_input<std::string>(found_socket->identifier);
|
||||
ReadAttributePtr attribute = component.attribute_try_get_for_read(name);
|
||||
ReadAttributeLookup attribute = component.attribute_try_get_for_read(name);
|
||||
if (attribute) {
|
||||
input_domains.append(attribute->domain());
|
||||
input_domains.append(attribute.domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -219,8 +219,8 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
|
||||
const fn::MFDataType from_type = from_socket->data_type();
|
||||
|
||||
if (from_type != to_type) {
|
||||
const fn::MultiFunction *conversion_fn = get_implicit_type_conversions().get_conversion(
|
||||
from_type, to_type);
|
||||
const fn::MultiFunction *conversion_fn =
|
||||
get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type);
|
||||
if (conversion_fn != nullptr) {
|
||||
fn::MFNode &node = common.network.add_function(*conversion_fn);
|
||||
common.network.add_link(*from_socket, node.input(0));
|
||||
|
@@ -24,85 +24,195 @@
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
using fn::GVMutableArray;
|
||||
using fn::MFDataType;
|
||||
|
||||
template<typename From, typename To>
|
||||
template<typename From, typename To, To (*ConversionF)(const From &)>
|
||||
static void add_implicit_conversion(DataTypeConversions &conversions)
|
||||
{
|
||||
static fn::CustomMF_Convert<From, To> function;
|
||||
conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
|
||||
const CPPType &from_type = CPPType::get<From>();
|
||||
const CPPType &to_type = CPPType::get<To>();
|
||||
const std::string conversion_name = from_type.name() + " to " + to_type.name();
|
||||
|
||||
static fn::CustomMF_SI_SO<From, To> multi_function{conversion_name, ConversionF};
|
||||
static auto convert_single_to_initialized = [](const void *src, void *dst) {
|
||||
*(To *)dst = ConversionF(*(const From *)src);
|
||||
};
|
||||
static auto convert_single_to_uninitialized = [](const void *src, void *dst) {
|
||||
new (dst) To(ConversionF(*(const From *)src));
|
||||
};
|
||||
conversions.add(fn::MFDataType::ForSingle<From>(),
|
||||
fn::MFDataType::ForSingle<To>(),
|
||||
multi_function,
|
||||
convert_single_to_initialized,
|
||||
convert_single_to_uninitialized);
|
||||
}
|
||||
|
||||
template<typename From, typename To, typename ConversionF>
|
||||
static void add_implicit_conversion(DataTypeConversions &conversions,
|
||||
StringRef name,
|
||||
ConversionF conversion)
|
||||
static float2 float_to_float2(const float &a)
|
||||
{
|
||||
static fn::CustomMF_SI_SO<From, To> function{name, conversion};
|
||||
conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
|
||||
return float2(a);
|
||||
}
|
||||
static float3 float_to_float3(const float &a)
|
||||
{
|
||||
return float3(a);
|
||||
}
|
||||
static int32_t float_to_int(const float &a)
|
||||
{
|
||||
return (int32_t)a;
|
||||
}
|
||||
static bool float_to_bool(const float &a)
|
||||
{
|
||||
return a > 0.0f;
|
||||
}
|
||||
static Color4f float_to_color(const float &a)
|
||||
{
|
||||
return Color4f(a, a, a, 1.0f);
|
||||
}
|
||||
|
||||
static float3 float2_to_float3(const float2 &a)
|
||||
{
|
||||
return float3(a.x, a.y, 0.0f);
|
||||
}
|
||||
static float float2_to_float(const float2 &a)
|
||||
{
|
||||
return (a.x + a.y) / 2.0f;
|
||||
}
|
||||
static int float2_to_int(const float2 &a)
|
||||
{
|
||||
return (int32_t)((a.x + a.y) / 2.0f);
|
||||
}
|
||||
static bool float2_to_bool(const float2 &a)
|
||||
{
|
||||
return !is_zero_v2(a);
|
||||
}
|
||||
static Color4f float2_to_color(const float2 &a)
|
||||
{
|
||||
return Color4f(a.x, a.y, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
static bool float3_to_bool(const float3 &a)
|
||||
{
|
||||
return !is_zero_v3(a);
|
||||
}
|
||||
static float float3_to_float(const float3 &a)
|
||||
{
|
||||
return (a.x + a.y + a.z) / 3.0f;
|
||||
}
|
||||
static int float3_to_int(const float3 &a)
|
||||
{
|
||||
return (int)((a.x + a.y + a.z) / 3.0f);
|
||||
}
|
||||
static float2 float3_to_float2(const float3 &a)
|
||||
{
|
||||
return float2(a);
|
||||
}
|
||||
static Color4f float3_to_color(const float3 &a)
|
||||
{
|
||||
return Color4f(a.x, a.y, a.z, 1.0f);
|
||||
}
|
||||
|
||||
static bool int_to_bool(const int32_t &a)
|
||||
{
|
||||
return a > 0;
|
||||
}
|
||||
static float int_to_float(const int32_t &a)
|
||||
{
|
||||
return (float)a;
|
||||
}
|
||||
static float2 int_to_float2(const int32_t &a)
|
||||
{
|
||||
return float2((float)a);
|
||||
}
|
||||
static float3 int_to_float3(const int32_t &a)
|
||||
{
|
||||
return float3((float)a);
|
||||
}
|
||||
static Color4f int_to_color(const int32_t &a)
|
||||
{
|
||||
return Color4f((float)a, (float)a, (float)a, 1.0f);
|
||||
}
|
||||
|
||||
static float bool_to_float(const bool &a)
|
||||
{
|
||||
return (bool)a;
|
||||
}
|
||||
static int32_t bool_to_int(const bool &a)
|
||||
{
|
||||
return (int32_t)a;
|
||||
}
|
||||
static float2 bool_to_float2(const bool &a)
|
||||
{
|
||||
return (a) ? float2(1.0f) : float2(0.0f);
|
||||
}
|
||||
static float3 bool_to_float3(const bool &a)
|
||||
{
|
||||
return (a) ? float3(1.0f) : float3(0.0f);
|
||||
}
|
||||
static Color4f bool_to_color(const bool &a)
|
||||
{
|
||||
return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
static bool color_to_bool(const Color4f &a)
|
||||
{
|
||||
return rgb_to_grayscale(a) > 0.0f;
|
||||
}
|
||||
static float color_to_float(const Color4f &a)
|
||||
{
|
||||
return rgb_to_grayscale(a);
|
||||
}
|
||||
static int32_t color_to_int(const Color4f &a)
|
||||
{
|
||||
return (int)rgb_to_grayscale(a);
|
||||
}
|
||||
static float2 color_to_float2(const Color4f &a)
|
||||
{
|
||||
return float2(a.r, a.g);
|
||||
}
|
||||
static float3 color_to_float3(const Color4f &a)
|
||||
{
|
||||
return float3(a.r, a.g, a.b);
|
||||
}
|
||||
|
||||
static DataTypeConversions create_implicit_conversions()
|
||||
{
|
||||
DataTypeConversions conversions;
|
||||
add_implicit_conversion<float, float2>(conversions);
|
||||
add_implicit_conversion<float, float3>(conversions);
|
||||
add_implicit_conversion<float, int32_t>(conversions);
|
||||
add_implicit_conversion<float, bool>(
|
||||
conversions, "float to boolean", [](float a) { return a > 0.0f; });
|
||||
add_implicit_conversion<float, Color4f>(
|
||||
conversions, "float to Color4f", [](float a) { return Color4f(a, a, a, 1.0f); });
|
||||
|
||||
add_implicit_conversion<float2, float3>(
|
||||
conversions, "float2 to float3", [](float2 a) { return float3(a.x, a.y, 0.0f); });
|
||||
add_implicit_conversion<float2, float>(
|
||||
conversions, "float2 to float", [](float2 a) { return (a.x + a.y) / 2.0f; });
|
||||
add_implicit_conversion<float2, int32_t>(
|
||||
conversions, "float2 to int32_t", [](float2 a) { return (int32_t)((a.x + a.y) / 2.0f); });
|
||||
add_implicit_conversion<float2, bool>(
|
||||
conversions, "float2 to bool", [](float2 a) { return !is_zero_v2(a); });
|
||||
add_implicit_conversion<float2, Color4f>(
|
||||
conversions, "float2 to Color4f", [](float2 a) { return Color4f(a.x, a.y, 0.0f, 1.0f); });
|
||||
add_implicit_conversion<float, float2, float_to_float2>(conversions);
|
||||
add_implicit_conversion<float, float3, float_to_float3>(conversions);
|
||||
add_implicit_conversion<float, int32_t, float_to_int>(conversions);
|
||||
add_implicit_conversion<float, bool, float_to_bool>(conversions);
|
||||
add_implicit_conversion<float, Color4f, float_to_color>(conversions);
|
||||
|
||||
add_implicit_conversion<float3, bool>(
|
||||
conversions, "float3 to boolean", [](float3 a) { return !is_zero_v3(a); });
|
||||
add_implicit_conversion<float3, float>(
|
||||
conversions, "float3 to float", [](float3 a) { return (a.x + a.y + a.z) / 3.0f; });
|
||||
add_implicit_conversion<float3, int32_t>(
|
||||
conversions, "float3 to int32_t", [](float3 a) { return (int)((a.x + a.y + a.z) / 3.0f); });
|
||||
add_implicit_conversion<float3, float2>(conversions);
|
||||
add_implicit_conversion<float3, Color4f>(
|
||||
conversions, "float3 to Color4f", [](float3 a) { return Color4f(a.x, a.y, a.z, 1.0f); });
|
||||
add_implicit_conversion<float2, float3, float2_to_float3>(conversions);
|
||||
add_implicit_conversion<float2, float, float2_to_float>(conversions);
|
||||
add_implicit_conversion<float2, int32_t, float2_to_int>(conversions);
|
||||
add_implicit_conversion<float2, bool, float2_to_bool>(conversions);
|
||||
add_implicit_conversion<float2, Color4f, float2_to_color>(conversions);
|
||||
|
||||
add_implicit_conversion<int32_t, bool>(
|
||||
conversions, "int32 to boolean", [](int32_t a) { return a > 0; });
|
||||
add_implicit_conversion<int32_t, float>(conversions);
|
||||
add_implicit_conversion<int32_t, float2>(
|
||||
conversions, "int32 to float2", [](int32_t a) { return float2((float)a); });
|
||||
add_implicit_conversion<int32_t, float3>(
|
||||
conversions, "int32 to float3", [](int32_t a) { return float3((float)a); });
|
||||
add_implicit_conversion<int32_t, Color4f>(conversions, "int32 to Color4f", [](int32_t a) {
|
||||
return Color4f((float)a, (float)a, (float)a, 1.0f);
|
||||
});
|
||||
add_implicit_conversion<float3, bool, float3_to_bool>(conversions);
|
||||
add_implicit_conversion<float3, float, float3_to_float>(conversions);
|
||||
add_implicit_conversion<float3, int32_t, float3_to_int>(conversions);
|
||||
add_implicit_conversion<float3, float2, float3_to_float2>(conversions);
|
||||
add_implicit_conversion<float3, Color4f, float3_to_color>(conversions);
|
||||
|
||||
add_implicit_conversion<bool, float>(conversions);
|
||||
add_implicit_conversion<bool, int32_t>(conversions);
|
||||
add_implicit_conversion<bool, float2>(
|
||||
conversions, "boolean to float2", [](bool a) { return (a) ? float2(1.0f) : float2(0.0f); });
|
||||
add_implicit_conversion<bool, float3>(
|
||||
conversions, "boolean to float3", [](bool a) { return (a) ? float3(1.0f) : float3(0.0f); });
|
||||
add_implicit_conversion<bool, Color4f>(conversions, "boolean to Color4f", [](bool a) {
|
||||
return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
});
|
||||
add_implicit_conversion<int32_t, bool, int_to_bool>(conversions);
|
||||
add_implicit_conversion<int32_t, float, int_to_float>(conversions);
|
||||
add_implicit_conversion<int32_t, float2, int_to_float2>(conversions);
|
||||
add_implicit_conversion<int32_t, float3, int_to_float3>(conversions);
|
||||
add_implicit_conversion<int32_t, Color4f, int_to_color>(conversions);
|
||||
|
||||
add_implicit_conversion<Color4f, bool>(
|
||||
conversions, "Color4f to boolean", [](Color4f a) { return rgb_to_grayscale(a) > 0.0f; });
|
||||
add_implicit_conversion<Color4f, float>(
|
||||
conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); });
|
||||
add_implicit_conversion<Color4f, float2>(
|
||||
conversions, "Color4f to float2", [](Color4f a) { return float2(a.r, a.g); });
|
||||
add_implicit_conversion<Color4f, float3>(
|
||||
conversions, "Color4f to float3", [](Color4f a) { return float3(a.r, a.g, a.b); });
|
||||
add_implicit_conversion<bool, float, bool_to_float>(conversions);
|
||||
add_implicit_conversion<bool, int32_t, bool_to_int>(conversions);
|
||||
add_implicit_conversion<bool, float2, bool_to_float2>(conversions);
|
||||
add_implicit_conversion<bool, float3, bool_to_float3>(conversions);
|
||||
add_implicit_conversion<bool, Color4f, bool_to_color>(conversions);
|
||||
|
||||
add_implicit_conversion<Color4f, bool, color_to_bool>(conversions);
|
||||
add_implicit_conversion<Color4f, float, color_to_float>(conversions);
|
||||
add_implicit_conversion<Color4f, int32_t, color_to_int>(conversions);
|
||||
add_implicit_conversion<Color4f, float2, color_to_float2>(conversions);
|
||||
add_implicit_conversion<Color4f, float3, color_to_float3>(conversions);
|
||||
|
||||
return conversions;
|
||||
}
|
||||
@@ -113,20 +223,125 @@ const DataTypeConversions &get_implicit_type_conversions()
|
||||
return conversions;
|
||||
}
|
||||
|
||||
void DataTypeConversions::convert(const CPPType &from_type,
|
||||
const CPPType &to_type,
|
||||
const void *from_value,
|
||||
void *to_value) const
|
||||
void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type,
|
||||
const CPPType &to_type,
|
||||
const void *from_value,
|
||||
void *to_value) const
|
||||
{
|
||||
const fn::MultiFunction *fn = this->get_conversion(MFDataType::ForSingle(from_type),
|
||||
MFDataType::ForSingle(to_type));
|
||||
BLI_assert(fn != nullptr);
|
||||
if (from_type == to_type) {
|
||||
from_type.copy_to_uninitialized(from_value, to_value);
|
||||
return;
|
||||
}
|
||||
|
||||
fn::MFContextBuilder context;
|
||||
fn::MFParamsBuilder params{*fn, 1};
|
||||
params.add_readonly_single_input(fn::GSpan(from_type, from_value, 1));
|
||||
params.add_uninitialized_single_output(fn::GMutableSpan(to_type, to_value, 1));
|
||||
fn->call({0}, params, context);
|
||||
const ConversionFunctions *functions = this->get_conversion_functions(
|
||||
MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type));
|
||||
BLI_assert(functions != nullptr);
|
||||
|
||||
functions->convert_single_to_uninitialized(from_value, to_value);
|
||||
}
|
||||
|
||||
class GVArray_For_ConvertedGVArray : public GVArray {
|
||||
private:
|
||||
std::unique_ptr<GVArray> varray_;
|
||||
const CPPType &from_type_;
|
||||
ConversionFunctions old_to_new_conversions_;
|
||||
|
||||
public:
|
||||
GVArray_For_ConvertedGVArray(std::unique_ptr<GVArray> varray,
|
||||
const CPPType &to_type,
|
||||
const DataTypeConversions &conversions)
|
||||
: GVArray(to_type, varray->size()), varray_(std::move(varray)), from_type_(varray_->type())
|
||||
{
|
||||
old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type);
|
||||
}
|
||||
|
||||
private:
|
||||
void get_impl(const int64_t index, void *r_value) const override
|
||||
{
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
|
||||
varray_->get(index, buffer);
|
||||
old_to_new_conversions_.convert_single_to_initialized(buffer, r_value);
|
||||
from_type_.destruct(buffer);
|
||||
}
|
||||
|
||||
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
|
||||
{
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
|
||||
varray_->get(index, buffer);
|
||||
old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value);
|
||||
from_type_.destruct(buffer);
|
||||
}
|
||||
};
|
||||
|
||||
class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArray {
|
||||
private:
|
||||
std::unique_ptr<GVMutableArray> varray_;
|
||||
const CPPType &from_type_;
|
||||
ConversionFunctions old_to_new_conversions_;
|
||||
ConversionFunctions new_to_old_conversions_;
|
||||
|
||||
public:
|
||||
GVMutableArray_For_ConvertedGVMutableArray(std::unique_ptr<GVMutableArray> varray,
|
||||
const CPPType &to_type,
|
||||
const DataTypeConversions &conversions)
|
||||
: GVMutableArray(to_type, varray->size()),
|
||||
varray_(std::move(varray)),
|
||||
from_type_(varray_->type())
|
||||
{
|
||||
old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type);
|
||||
new_to_old_conversions_ = *conversions.get_conversion_functions(to_type, from_type_);
|
||||
}
|
||||
|
||||
private:
|
||||
void get_impl(const int64_t index, void *r_value) const override
|
||||
{
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
|
||||
varray_->get(index, buffer);
|
||||
old_to_new_conversions_.convert_single_to_initialized(buffer, r_value);
|
||||
from_type_.destruct(buffer);
|
||||
}
|
||||
|
||||
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
|
||||
{
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
|
||||
varray_->get(index, buffer);
|
||||
old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value);
|
||||
from_type_.destruct(buffer);
|
||||
}
|
||||
|
||||
void set_by_move_impl(const int64_t index, void *value) override
|
||||
{
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
|
||||
new_to_old_conversions_.convert_single_to_uninitialized(value, buffer);
|
||||
varray_->set_by_relocate(index, buffer);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<fn::GVArray> DataTypeConversions::try_convert(std::unique_ptr<fn::GVArray> varray,
|
||||
const CPPType &to_type) const
|
||||
{
|
||||
const CPPType &from_type = varray->type();
|
||||
if (from_type == to_type) {
|
||||
return varray;
|
||||
}
|
||||
if (!this->is_convertible(from_type, to_type)) {
|
||||
return {};
|
||||
}
|
||||
return std::make_unique<GVArray_For_ConvertedGVArray>(std::move(varray), to_type, *this);
|
||||
}
|
||||
|
||||
std::unique_ptr<fn::GVMutableArray> DataTypeConversions::try_convert(
|
||||
std::unique_ptr<fn::GVMutableArray> varray, const CPPType &to_type) const
|
||||
{
|
||||
const CPPType &from_type = varray->type();
|
||||
if (from_type == to_type) {
|
||||
return varray;
|
||||
}
|
||||
if (!this->is_convertible(from_type, to_type)) {
|
||||
return {};
|
||||
}
|
||||
return std::make_unique<GVMutableArray_For_ConvertedGVMutableArray>(
|
||||
std::move(varray), to_type, *this);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
@@ -731,15 +731,6 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
|
||||
BLI_buffer_declare_static(wmGizmo *, visible_3d_gizmos, BLI_BUFFER_NOP, 128);
|
||||
bool do_step[WM_GIZMOMAP_DRAWSTEP_MAX];
|
||||
|
||||
int mval[2] = {UNPACK2(event->mval)};
|
||||
|
||||
/* Ensure for drag events we use the location where the user clicked.
|
||||
* Without this click-dragging on a gizmo can accidentally act on the wrong gizmo. */
|
||||
if (WM_event_is_mouse_drag(event)) {
|
||||
mval[0] += event->x - event->prevclickx;
|
||||
mval[1] += event->y - event->prevclicky;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(do_step); i++) {
|
||||
do_step[i] = WM_gizmo_context_check_drawstep(C, i);
|
||||
}
|
||||
@@ -775,7 +766,7 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
|
||||
}
|
||||
else if (step == WM_GIZMOMAP_DRAWSTEP_2D) {
|
||||
if ((gz = wm_gizmogroup_find_intersected_gizmo(
|
||||
wm, gzgroup, C, event_modifier, mval, r_part))) {
|
||||
wm, gzgroup, C, event_modifier, event->mval, r_part))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -787,7 +778,7 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
|
||||
/* 2D gizmos get priority. */
|
||||
if (gz == NULL) {
|
||||
gz = gizmo_find_intersected_3d(
|
||||
C, mval, visible_3d_gizmos.data, visible_3d_gizmos.count, r_part);
|
||||
C, event->mval, visible_3d_gizmos.data, visible_3d_gizmos.count, r_part);
|
||||
}
|
||||
}
|
||||
BLI_buffer_free(&visible_3d_gizmos);
|
||||
|
@@ -186,6 +186,7 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
win->addmousemove = true;
|
||||
win->event_queue_check_click = 0;
|
||||
win->event_queue_check_drag = 0;
|
||||
win->event_queue_check_drag_handled = 0;
|
||||
BLO_read_data_address(reader, &win->stereo3d_format);
|
||||
|
||||
/* Multi-view always fallback to anaglyph at file opening
|
||||
|
@@ -1621,7 +1621,7 @@ int WM_operator_name_call_with_properties(struct bContext *C,
|
||||
{
|
||||
PointerRNA props_ptr;
|
||||
wmOperatorType *ot = WM_operatortype_find(opstring, false);
|
||||
RNA_pointer_create(NULL, ot->srna, properties, &props_ptr);
|
||||
RNA_pointer_create(G_MAIN->wm.first, ot->srna, properties, &props_ptr);
|
||||
return WM_operator_name_call_ptr(C, ot, context, &props_ptr);
|
||||
}
|
||||
|
||||
@@ -2941,6 +2941,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
|
||||
if (wm_action_not_handled(action)) {
|
||||
if (win->event_queue_check_drag) {
|
||||
if (WM_event_drag_test(event, &event->prevclickx)) {
|
||||
win->event_queue_check_drag_handled = true;
|
||||
|
||||
int x = event->x;
|
||||
int y = event->y;
|
||||
short val = event->val;
|
||||
@@ -2984,6 +2986,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
|
||||
if (event->is_repeat == false) {
|
||||
win->event_queue_check_click = true;
|
||||
win->event_queue_check_drag = true;
|
||||
win->event_queue_check_drag_handled = false;
|
||||
}
|
||||
}
|
||||
else if (event->val == KM_RELEASE) {
|
||||
@@ -3470,6 +3473,13 @@ void wm_event_do_handlers(bContext *C)
|
||||
win->event_queue_check_click = false;
|
||||
}
|
||||
|
||||
/* If the drag even was handled, don't attempt to keep re-handing the same
|
||||
* drag event on every cursor motion, see: T87511. */
|
||||
if (win->event_queue_check_drag_handled) {
|
||||
win->event_queue_check_drag = false;
|
||||
win->event_queue_check_drag_handled = false;
|
||||
}
|
||||
|
||||
/* Update previous mouse position for following events to use. */
|
||||
win->eventstate->prevx = event->x;
|
||||
win->eventstate->prevy = event->y;
|
||||
|
@@ -80,6 +80,9 @@ static wmKeyMapItem *wm_keymap_item_copy(wmKeyMapItem *kmi)
|
||||
kmin->ptr = MEM_callocN(sizeof(PointerRNA), "UserKeyMapItemPtr");
|
||||
WM_operator_properties_create(kmin->ptr, kmin->idname);
|
||||
|
||||
/* Signal for no context, see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
|
||||
kmin->ptr->owner_id = NULL;
|
||||
|
||||
kmin->properties = IDP_CopyProperty(kmin->properties);
|
||||
kmin->ptr->data = kmin->properties;
|
||||
}
|
||||
@@ -106,6 +109,9 @@ static void wm_keymap_item_properties_set(wmKeyMapItem *kmi)
|
||||
{
|
||||
WM_operator_properties_alloc(&(kmi->ptr), &(kmi->properties), kmi->idname);
|
||||
WM_operator_properties_sanitize(kmi->ptr, 1);
|
||||
|
||||
/* Signal for no context, see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
|
||||
kmi->ptr->owner_id = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,6 +142,9 @@ static void wm_keymap_item_properties_update_ot(wmKeyMapItem *kmi)
|
||||
kmi->ptr->data = kmi->properties;
|
||||
}
|
||||
WM_operator_properties_sanitize(kmi->ptr, 1);
|
||||
|
||||
/* Signal for no context, see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
|
||||
kmi->ptr->owner_id = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@@ -252,7 +252,7 @@ void WM_operatortype_props_advanced_end(wmOperatorType *ot)
|
||||
return;
|
||||
}
|
||||
|
||||
RNA_pointer_create(NULL, ot->srna, NULL, &struct_ptr);
|
||||
WM_operator_properties_create_ptr(&struct_ptr, ot);
|
||||
|
||||
RNA_STRUCT_BEGIN (&struct_ptr, prop) {
|
||||
counter++;
|
||||
|
@@ -583,7 +583,8 @@ char *WM_prop_pystring_assign(bContext *C, PointerRNA *ptr, PropertyRNA *prop, i
|
||||
|
||||
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
|
||||
{
|
||||
RNA_pointer_create(NULL, ot->srna, NULL, ptr);
|
||||
/* Set the ID so the context can be accessed: see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
|
||||
RNA_pointer_create(G_MAIN->wm.first, ot->srna, NULL, ptr);
|
||||
}
|
||||
|
||||
void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
|
||||
@@ -594,7 +595,8 @@ void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
|
||||
WM_operator_properties_create_ptr(ptr, ot);
|
||||
}
|
||||
else {
|
||||
RNA_pointer_create(NULL, &RNA_OperatorProperties, NULL, ptr);
|
||||
/* Set the ID so the context can be accessed: see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
|
||||
RNA_pointer_create(G_MAIN->wm.first, &RNA_OperatorProperties, NULL, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1205,7 +1207,7 @@ IDProperty *WM_operator_last_properties_ensure_idprops(wmOperatorType *ot)
|
||||
void WM_operator_last_properties_ensure(wmOperatorType *ot, PointerRNA *ptr)
|
||||
{
|
||||
IDProperty *props = WM_operator_last_properties_ensure_idprops(ot);
|
||||
RNA_pointer_create(NULL, ot->srna, props, ptr);
|
||||
RNA_pointer_create(G_MAIN->wm.first, ot->srna, props, ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -130,14 +130,11 @@ set(SRC
|
||||
|
||||
# MSVC 2010 gives linking errors with the manifest
|
||||
if(WIN32 AND NOT UNIX)
|
||||
string(SUBSTRING ${BLENDER_VERSION} 0 1 bver1)
|
||||
string(SUBSTRING ${BLENDER_VERSION} 2 1 bver2)
|
||||
string(SUBSTRING ${BLENDER_VERSION} 3 1 bver3)
|
||||
add_definitions(
|
||||
-DBLEN_VER_RC_STR="${BLENDER_VERSION}"
|
||||
-DBLEN_VER_RC_1=${bver1}
|
||||
-DBLEN_VER_RC_2=${bver2}
|
||||
-DBLEN_VER_RC_3=${bver3}
|
||||
-DBLEN_VER_RC_1=${BLENDER_VERSION_MAJOR}
|
||||
-DBLEN_VER_RC_2=${BLENDER_VERSION_MINOR}
|
||||
-DBLEN_VER_RC_3=${BLENDER_VERSION_PATCH}
|
||||
-DBLEN_VER_RC_4=0
|
||||
)
|
||||
|
||||
|
@@ -72,7 +72,7 @@ enum {
|
||||
|
||||
/* for the callbacks: */
|
||||
#ifndef WITH_PYTHON_MODULE
|
||||
# define BLEND_VERSION_FMT "Blender %d.%02d.%d"
|
||||
# define BLEND_VERSION_FMT "Blender %d.%d.%d"
|
||||
# define BLEND_VERSION_ARG (BLENDER_VERSION / 100), (BLENDER_VERSION % 100), BLENDER_VERSION_PATCH
|
||||
#endif
|
||||
|
||||
|
Submodule source/tools updated: f1db70de64...f99d29ae3e
Reference in New Issue
Block a user