Geometry Nodes: Matrix socket type, attribute type, and initial nodes

Implements the design from #116067.
The socket type is called "Matrix" but it is often referred to as "Transform"
when that's what it is semantically. The attribute type is "4x4 Matrix" since
that's a lower level choice. Currently matrix sockets are always passed
around internally as `float4x4`, but that can be optimized in the future
when smaller types would give the same behavior.

A new "Matrix" utilities category has the following set of initial nodes"
- **Combine Transform**
- **Separate Transform**
- **Multiply Matrices**
- **Transform Direction**
- **Transform Vector**
- **Invert Matrix**
- **Transpose Matrix**

The nodes and socket type are behind an experimental flag for now,
which will give us time to make sure it's the right set of initial nodes.
The viewer node overlay doesn't support matrices-- they aren't supported
for rendering in general. They also aren't supported in the modifier interface
currently. But they are supported in the spreadsheet, where the value is
displayed in a tooltip.

Pull Request: https://projects.blender.org/blender/blender/pulls/116166
This commit is contained in:
Hans Goudey 2024-02-13 18:59:36 +01:00 committed by Hans Goudey
parent 21cea65ea6
commit 1cfe9dd08c
59 changed files with 1345 additions and 147 deletions

@ -533,7 +533,7 @@ class NODE_MT_category_GEO_UTILITIES(Menu):
bl_idname = "NODE_MT_category_GEO_UTILITIES"
bl_label = "Utilities"
def draw(self, _context):
def draw(self, context):
layout = self.layout
layout.menu("NODE_MT_geometry_node_GEO_COLOR")
layout.menu("NODE_MT_category_GEO_TEXT")
@ -541,6 +541,8 @@ class NODE_MT_category_GEO_UTILITIES(Menu):
layout.separator()
layout.menu("NODE_MT_category_GEO_UTILITIES_FIELD")
layout.menu("NODE_MT_category_GEO_UTILITIES_MATH")
if context.preferences.experimental.use_new_matrix_socket:
layout.menu("NODE_MT_category_utilities_matrix")
layout.menu("NODE_MT_category_GEO_UTILITIES_ROTATION")
layout.menu("NODE_MT_category_GEO_UTILITIES_DEPRECATED")
layout.separator()
@ -592,6 +594,22 @@ class NODE_MT_category_GEO_UTILITIES_ROTATION(Menu):
node_add_menu.draw_assets_for_catalog(layout, "Utilities/Rotation")
class NODE_MT_category_utilities_matrix(Menu):
bl_idname = "NODE_MT_category_utilities_matrix"
bl_label = "Matrix"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "FunctionNodeCombineTransform")
node_add_menu.add_node_type(layout, "FunctionNodeInvertMatrix")
node_add_menu.add_node_type(layout, "FunctionNodeMatrixMultiply")
node_add_menu.add_node_type(layout, "FunctionNodeSeparateTransform")
node_add_menu.add_node_type(layout, "FunctionNodeTransformDirection")
node_add_menu.add_node_type(layout, "FunctionNodeTransformPoint")
node_add_menu.add_node_type(layout, "FunctionNodeTransposeMatrix")
node_add_menu.draw_assets_for_catalog(layout, "Utilities/Matrix")
class NODE_MT_category_GEO_UTILITIES_MATH(Menu):
bl_idname = "NODE_MT_category_GEO_UTILITIES_MATH"
bl_label = "Math"
@ -779,6 +797,7 @@ classes = (
NODE_MT_category_GEO_UTILITIES_FIELD,
NODE_MT_category_GEO_UTILITIES_MATH,
NODE_MT_category_GEO_UTILITIES_ROTATION,
NODE_MT_category_utilities_matrix,
NODE_MT_category_GEO_UTILITIES_DEPRECATED,
NODE_MT_category_GEO_GROUP,
)

@ -2661,6 +2661,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
({"property": "use_sculpt_texture_paint"}, ("blender/blender/issues/96225", "#96225")),
({"property": "use_experimental_compositors"}, ("blender/blender/issues/88150", "#88150")),
({"property": "use_grease_pencil_version3"}, ("blender/blender/projects/6", "Grease Pencil 3.0")),
({"property": "use_new_matrix_socket"}, ("blender/blender/issues/116067", "Matrix Socket")),
({"property": "enable_overlay_next"}, ("blender/blender/issues/102179", "#102179")),
({"property": "use_extension_repos"}, ("/blender/blender/issues/106254", "#106254")),
),

@ -35,7 +35,8 @@ inline void convert_to_static_type(const CPPType &cpp_type, const Func &func)
int8_t,
ColorGeometry4f,
ColorGeometry4b,
math::Quaternion>([&](auto type_tag) {
math::Quaternion,
float4x4>([&](auto type_tag) {
using T = typename decltype(type_tag)::type;
if constexpr (std::is_same_v<T, void>) {
/* It's expected that the given cpp type is one of the supported ones. */
@ -517,6 +518,26 @@ class ColorGeometry4bMixer {
void finalize(const IndexMask &mask);
};
class float4x4Mixer {
private:
MutableSpan<float4x4> buffer_;
Array<float> total_weights_;
Array<float3> location_buffer_;
Array<float3> expmap_buffer_;
Array<float3> scale_buffer_;
public:
float4x4Mixer(MutableSpan<float4x4> buffer);
/**
* \param mask: Only initialize these indices. Other indices in the buffer will be invalid.
*/
float4x4Mixer(MutableSpan<float4x4> buffer, const IndexMask &mask);
void set(int64_t index, const float4x4 &value, float weight = 1.0f);
void mix_in(int64_t index, const float4x4 &value, float weight = 1.0f);
void finalize();
void finalize(const IndexMask &mask);
};
template<typename T> struct DefaultMixerStruct {
/* Use void by default. This can be checked for in `if constexpr` statements. */
using type = void;
@ -538,6 +559,9 @@ template<> struct DefaultMixerStruct<ColorGeometry4f> {
template<> struct DefaultMixerStruct<ColorGeometry4b> {
using type = ColorGeometry4bMixer;
};
template<> struct DefaultMixerStruct<float4x4> {
using type = float4x4Mixer;
};
template<> struct DefaultMixerStruct<int> {
static double int_to_double(const int &value)
{

@ -1366,6 +1366,13 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define FN_NODE_ROTATE_VECTOR 1229
#define FN_NODE_ROTATE_ROTATION 1230
#define FN_NODE_INVERT_ROTATION 1231
#define FN_NODE_TRANSFORM_POINT 1232
#define FN_NODE_TRANSFORM_DIRECTION 1233
#define FN_NODE_MATRIX_MULTIPLY 1234
#define FN_NODE_COMBINE_TRANSFORM 1235
#define FN_NODE_SEPARATE_TRANSFORM 1236
#define FN_NODE_INVERT_MATRIX 1237
#define FN_NODE_TRANSPOSE_MATRIX 1238
/** \} */

@ -130,6 +130,7 @@ static const bNodeSocketStaticTypeInfo node_socket_subtypes[] = {
{"NodeSocketIntFactor", "NodeTreeInterfaceSocketIntFactor", SOCK_INT, PROP_FACTOR},
{"NodeSocketBool", "NodeTreeInterfaceSocketBool", SOCK_BOOLEAN, PROP_NONE},
{"NodeSocketRotation", "NodeTreeInterfaceSocketRotation", SOCK_ROTATION, PROP_NONE},
{"NodeSocketMatrix", "NodeTreeInterfaceSocketMatrix", SOCK_MATRIX, PROP_NONE},
{"NodeSocketVector", "NodeTreeInterfaceSocketVector", SOCK_VECTOR, PROP_NONE},
{"NodeSocketVectorTranslation",
"NodeTreeInterfaceSocketVectorTranslation",
@ -206,6 +207,7 @@ template<typename Fn> bool socket_data_to_static_type(const eNodeSocketDatatype
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_MATRIX:
case SOCK_GEOMETRY:
return true;
}

@ -50,6 +50,8 @@ const blender::CPPType *custom_data_type_to_cpp_type(const eCustomDataType type)
return &CPPType::get<ColorGeometry4b>();
case CD_PROP_QUATERNION:
return &CPPType::get<math::Quaternion>();
case CD_PROP_FLOAT4X4:
return &CPPType::get<float4x4>();
case CD_PROP_STRING:
return &CPPType::get<MStringProperty>();
default:
@ -89,6 +91,9 @@ eCustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type)
if (type.is<math::Quaternion>()) {
return CD_PROP_QUATERNION;
}
if (type.is<float4x4>()) {
return CD_PROP_FLOAT4X4;
}
if (type.is<MStringProperty>()) {
return CD_PROP_STRING;
}
@ -167,6 +172,8 @@ static int attribute_data_type_complexity(const eCustomDataType data_type)
return 8;
case CD_PROP_COLOR:
return 9;
case CD_PROP_FLOAT4X4:
return 10;
#if 0 /* These attribute types are not supported yet. */
case CD_PROP_STRING:
return 10;

@ -3,6 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array_utils.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_quaternion.hh"
#include "BKE_attribute_math.hh"
@ -36,6 +37,40 @@ math::Quaternion mix4(const float4 &weights,
return math::Quaternion::expmap(expmap_mixed);
}
template<> float4x4 mix2(const float factor, const float4x4 &a, const float4x4 &b)
{
return math::interpolate(a, b, factor);
}
template<>
float4x4 mix3(const float3 &weights, const float4x4 &v0, const float4x4 &v1, const float4x4 &v2)
{
const float3 location = mix3(weights, v0.location(), v1.location(), v2.location());
const math::Quaternion rotation = mix3(
weights, math::to_quaternion(v0), math::to_quaternion(v1), math::to_quaternion(v2));
const float3 scale = mix3(weights, math::to_scale(v0), math::to_scale(v1), math::to_scale(v2));
return math::from_loc_rot_scale<float4x4>(location, rotation, scale);
}
template<>
float4x4 mix4(const float4 &weights,
const float4x4 &v0,
const float4x4 &v1,
const float4x4 &v2,
const float4x4 &v3)
{
const float3 location = mix4(
weights, v0.location(), v1.location(), v2.location(), v3.location());
const math::Quaternion rotation = mix4(weights,
math::to_quaternion(v0),
math::to_quaternion(v1),
math::to_quaternion(v2),
math::to_quaternion(v3));
const float3 scale = mix4(
weights, math::to_scale(v0), math::to_scale(v1), math::to_scale(v2), math::to_scale(v3));
return math::from_loc_rot_scale<float4x4>(location, rotation, scale);
}
ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer,
ColorGeometry4f default_color)
: ColorGeometry4fMixer(buffer, buffer.index_range(), default_color)
@ -160,6 +195,58 @@ void ColorGeometry4bMixer::finalize(const IndexMask &mask)
});
}
float4x4Mixer::float4x4Mixer(MutableSpan<float4x4> buffer)
: float4x4Mixer(buffer, buffer.index_range())
{
}
float4x4Mixer::float4x4Mixer(MutableSpan<float4x4> buffer, const IndexMask & /*mask*/)
: buffer_(buffer),
total_weights_(buffer.size(), 0.0f),
location_buffer_(buffer.size(), float3(0)),
expmap_buffer_(buffer.size(), float3(0)),
scale_buffer_(buffer.size(), float3(0))
{
}
void float4x4Mixer::float4x4Mixer::set(int64_t index, const float4x4 &value, const float weight)
{
location_buffer_[index] = value.location() * weight;
expmap_buffer_[index] = math::to_quaternion(value).expmap() * weight;
scale_buffer_[index] = math::to_scale(value) * weight;
total_weights_[index] = weight;
}
void float4x4Mixer::mix_in(int64_t index, const float4x4 &value, float weight)
{
location_buffer_[index] += value.location() * weight;
expmap_buffer_[index] += math::to_quaternion(value).expmap() * weight;
scale_buffer_[index] += math::to_scale(value) * weight;
total_weights_[index] += weight;
}
void float4x4Mixer::finalize()
{
this->finalize(buffer_.index_range());
}
void float4x4Mixer::finalize(const IndexMask &mask)
{
mask.foreach_index([&](const int64_t i) {
const float weight = total_weights_[i];
if (weight > 0.0f) {
const float weight_inv = math::rcp(weight);
buffer_[i] = math::from_loc_rot_scale<float4x4>(
location_buffer_[i] * weight_inv,
math::Quaternion::expmap(expmap_buffer_[i] * weight_inv),
scale_buffer_[i] * weight_inv);
}
else {
buffer_[i] = float4x4::identity();
}
});
}
void gather(const GSpan src, const Span<int> map, GMutableSpan dst)
{
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {

@ -1042,6 +1042,10 @@ static std::shared_ptr<io::serialize::Value> serialize_primitive_value(
const math::Quaternion value = *static_cast<const math::Quaternion *>(value_ptr);
return serialize_float_array({&value.x, 4});
}
case CD_PROP_FLOAT4X4: {
const float4x4 value = *static_cast<const float4x4 *>(value_ptr);
return serialize_float_array({value.base_ptr(), value.col_len * value.row_len});
}
default:
break;
}
@ -1160,6 +1164,9 @@ template<typename T>
case CD_PROP_QUATERNION: {
return deserialize_float_array(io_value, {static_cast<float *>(r_value), 4});
}
case CD_PROP_FLOAT4X4: {
return deserialize_float_array(io_value, {static_cast<float *>(r_value), 4 * 4});
}
default:
break;
}

@ -47,6 +47,7 @@ Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(const Span<voi
case SOCK_INT:
case SOCK_BOOLEAN:
case SOCK_ROTATION:
case SOCK_MATRIX:
case SOCK_RGBA: {
auto &value_variant = *static_cast<SocketValueVariant *>(socket_value);
if (value_variant.is_context_dependent_field()) {
@ -131,6 +132,7 @@ Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(const Span<voi
case SOCK_INT:
case SOCK_BOOLEAN:
case SOCK_ROTATION:
case SOCK_MATRIX:
case SOCK_RGBA: {
const CPPType &base_type = *socket_type_to_geo_nodes_base_cpp_type(socket_type);
if (const auto *item = dynamic_cast<const PrimitiveBakeItem *>(&bake_item)) {

@ -24,6 +24,7 @@
#include "BLI_endian_switch.h"
#include "BLI_index_range.hh"
#include "BLI_math_color_blend.h"
#include "BLI_math_matrix.hh"
#include "BLI_math_quaternion_types.hh"
#include "BLI_math_vector.hh"
#include "BLI_mempool.h"
@ -113,6 +114,7 @@ bool CustomData_MeshMasks_are_matching(const CustomData_MeshMasks *mask_ref,
struct LayerTypeInfo {
int size; /* the memory size of one element of this layer's data */
int alignment;
/** name of the struct used, for file writing */
const char *structname;
@ -1526,6 +1528,16 @@ static void layerDefault_propquaternion(void *data, const int count)
MutableSpan(static_cast<math::Quaternion *>(data), count).fill(math::Quaternion::identity());
}
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#math::Quaternion, #CD_PROP_FLOAT4X4)
* \{ */
static void layerDefault_propfloat4x4(void *data, const int count)
{
using namespace blender;
MutableSpan(static_cast<float4x4 *>(data), count).fill(float4x4::identity());
}
/** \} */
/* -------------------------------------------------------------------- */
@ -1534,11 +1546,30 @@ static void layerDefault_propquaternion(void *data, const int count)
static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
/* 0: CD_MVERT */ /* DEPRECATED */
{sizeof(MVert), "MVert", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(MVert),
alignof(MVert),
"MVert",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 1: CD_MSTICKY */ /* DEPRECATED */
{sizeof(float[2]), "", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(float[2]),
alignof(float2),
"",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 2: CD_MDEFORMVERT */
{sizeof(MDeformVert),
alignof(MDeformVert),
"MDeformVert",
1,
nullptr,
@ -1549,11 +1580,30 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
layerConstruct_mdeformvert},
/* 3: CD_MEDGE */ /* DEPRECATED */
{sizeof(MEdge), "MEdge", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(MEdge),
alignof(MEdge),
"MEdge",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 4: CD_MFACE */
{sizeof(MFace), "MFace", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(MFace),
alignof(MFace),
"MFace",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 5: CD_MTFACE */
{sizeof(MTFace),
alignof(MTFace),
"MTFace",
1,
N_("UVMap"),
@ -1576,18 +1626,31 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerMaxNum_tface},
/* 6: CD_MCOL */
/* 4 MCol structs per face */
{sizeof(MCol[4]), "MCol", 4,
N_("Col"), nullptr, nullptr,
layerInterp_mcol, layerSwap_mcol, layerDefault_mcol,
nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr},
{sizeof(MCol[4]),
alignof(MCol[4]),
"MCol",
4,
N_("Col"),
nullptr,
nullptr,
layerInterp_mcol,
layerSwap_mcol,
layerDefault_mcol},
/* 7: CD_ORIGINDEX */
{sizeof(int), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, layerDefault_origindex},
{sizeof(int),
alignof(int),
"",
0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
layerDefault_origindex},
/* 8: CD_NORMAL */
/* 3 floats per normal vector */
{sizeof(float[3]),
alignof(blender::float3),
"vec3f",
1,
nullptr,
@ -1605,9 +1668,10 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
layerCopyValue_normal},
/* 9: CD_FACEMAP */ /* DEPRECATED */
{sizeof(int), ""},
{sizeof(int), alignof(int), ""},
/* 10: CD_PROP_FLOAT */
{sizeof(MFloatProperty),
alignof(float),
"MFloatProperty",
1,
N_("Float"),
@ -1620,6 +1684,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerValidate_propFloat},
/* 11: CD_PROP_INT32 */
{sizeof(MIntProperty),
alignof(int),
"MIntProperty",
1,
N_("Int"),
@ -1629,6 +1694,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr},
/* 12: CD_PROP_STRING */
{sizeof(MStringProperty),
alignof(MStringProperty),
"MStringProperty",
1,
N_("String"),
@ -1638,6 +1704,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr},
/* 13: CD_ORIGSPACE */
{sizeof(OrigSpaceFace),
alignof(OrigSpaceFace),
"OrigSpaceFace",
1,
N_("UVMap"),
@ -1647,15 +1714,25 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerSwap_origspace_face,
layerDefault_origspace_face},
/* 14: CD_ORCO */
{sizeof(float[3]), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(float[3]),
alignof(blender::float3),
"",
0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 15: CD_MTEXPOLY */ /* DEPRECATED */
/* NOTE: when we expose the UV Map / TexFace split to the user,
* change this back to face Texture. */
{sizeof(int), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(int), alignof(int), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 16: CD_MLOOPUV */ /* DEPRECATED */
{sizeof(MLoopUV), "MLoopUV", 1, N_("UVMap")},
{sizeof(MLoopUV), alignof(MLoopUV), "MLoopUV", 1, N_("UVMap")},
/* 17: CD_PROP_BYTE_COLOR */
{sizeof(MLoopCol),
alignof(MLoopCol),
"MLoopCol",
1,
N_("Col"),
@ -1677,9 +1754,19 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
nullptr},
/* 18: CD_TANGENT */
{sizeof(float[4][4]), "", 0, N_("Tangent"), nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(float[4][4]),
alignof(float[4][4]),
"",
0,
N_("Tangent"),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 19: CD_MDISPS */
{sizeof(MDisps),
alignof(MDisps),
"MDisps",
1,
nullptr,
@ -1700,11 +1787,30 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerWrite_mdisps,
layerFilesize_mdisps},
/* 20: CD_PREVIEW_MCOL */
{},
{sizeof(blender::float4x4),
alignof(blender::float4x4),
"mat4x4f",
1,
N_("4 by 4 Float Matrix"),
nullptr,
nullptr,
nullptr,
nullptr,
layerDefault_propfloat4x4},
/* 21: CD_ID_MCOL */ /* DEPRECATED */
{sizeof(MCol[4]), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(MCol[4]),
alignof(MCol[4]),
"",
0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 22: CD_TEXTURE_MCOL */
{sizeof(MCol[4]),
alignof(MCol[4]),
"MCol",
4,
N_("TexturedCol"),
@ -1714,13 +1820,40 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerSwap_mcol,
layerDefault_mcol},
/* 23: CD_CLOTH_ORCO */
{sizeof(float[3]), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(float[3]),
alignof(float[3]),
"",
0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 24: CD_RECAST */
{sizeof(MRecast), "MRecast", 1, N_("Recast"), nullptr, nullptr, nullptr, nullptr},
{sizeof(MRecast),
alignof(MRecast),
"MRecast",
1,
N_("Recast"),
nullptr,
nullptr,
nullptr,
nullptr},
/* 25: CD_MPOLY */ /* DEPRECATED */
{sizeof(MPoly), "MPoly", 1, N_("NGon Face"), nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(MPoly),
alignof(MPoly),
"MPoly",
1,
N_("NGon Face"),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 26: CD_MLOOP */ /* DEPRECATED */
{sizeof(MLoop),
alignof(MLoop),
"MLoop",
1,
N_("NGon Face-Vertex"),
@ -1730,15 +1863,23 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
nullptr},
/* 27: CD_SHAPE_KEYINDEX */
{sizeof(int), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(int), alignof(int), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 28: CD_SHAPEKEY */
{sizeof(float[3]), "", 0, N_("ShapeKey"), nullptr, nullptr, layerInterp_shapekey},
{sizeof(float[3]),
alignof(float[3]),
"",
0,
N_("ShapeKey"),
nullptr,
nullptr,
layerInterp_shapekey},
/* 29: CD_BWEIGHT */ /* DEPRECATED */
{sizeof(MFloatProperty), "MFloatProperty", 1},
{sizeof(MFloatProperty), alignof(MFloatProperty), "MFloatProperty", 1},
/* 30: CD_CREASE */ /* DEPRECATED */
{sizeof(float), ""},
{sizeof(float), alignof(float), ""},
/* 31: CD_ORIGSPACE_MLOOP */
{sizeof(OrigSpaceLoop),
alignof(OrigSpaceLoop),
"OrigSpaceLoop",
1,
N_("OS Loop"),
@ -1759,6 +1900,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{},
/* 33: CD_BM_ELEM_PYPTR */
{sizeof(void *),
alignof(void *),
"",
1,
nullptr,
@ -1768,9 +1910,10 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
nullptr},
/* 34: CD_PAINT_MASK */ /* DEPRECATED */
{sizeof(float), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(float), alignof(float), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 35: CD_GRID_PAINT_MASK */
{sizeof(GridPaintMask),
alignof(GridPaintMask),
"GridPaintMask",
1,
nullptr,
@ -1782,6 +1925,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerConstruct_grid_paint_mask},
/* 36: CD_MVERT_SKIN */
{sizeof(MVertSkin),
alignof(MVertSkin),
"MVertSkin",
1,
nullptr,
@ -1792,6 +1936,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerDefault_mvert_skin},
/* 37: CD_FREESTYLE_EDGE */
{sizeof(FreestyleEdge),
alignof(FreestyleEdge),
"FreestyleEdge",
1,
nullptr,
@ -1802,6 +1947,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr},
/* 38: CD_FREESTYLE_FACE */
{sizeof(FreestyleFace),
alignof(FreestyleFace),
"FreestyleFace",
1,
nullptr,
@ -1811,23 +1957,87 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
nullptr},
/* 39: CD_MLOOPTANGENT */
{sizeof(float[4]), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(float[4]),
alignof(float[4]),
"",
0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 40: CD_TESSLOOPNORMAL */
{sizeof(short[4][3]), "", 0, nullptr, nullptr, nullptr, nullptr, layerSwap_flnor, nullptr},
{sizeof(short[4][3]),
alignof(short[4][3]),
"",
0,
nullptr,
nullptr,
nullptr,
nullptr,
layerSwap_flnor,
nullptr},
/* 41: CD_CUSTOMLOOPNORMAL */
{sizeof(short[2]), "vec2s", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(short[2]),
alignof(short[2]),
"vec2s",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 42: CD_SCULPT_FACE_SETS */ /* DEPRECATED */
{sizeof(int), ""},
{sizeof(int), alignof(int), ""},
/* 43: CD_LOCATION */
{sizeof(float[3]), "vec3f", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(float[3]),
alignof(float[3]),
"vec3f",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 44: CD_RADIUS */
{sizeof(float), "MFloatProperty", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(float),
alignof(float),
"MFloatProperty",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 45: CD_PROP_INT8 */
{sizeof(int8_t), "MInt8Property", 1, N_("Int8"), nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(int8_t),
alignof(int8_t),
"MInt8Property",
1,
N_("Int8"),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 46: CD_PROP_INT32_2D */
{sizeof(blender::int2), "vec2i", 1, N_("Int 2D"), nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(blender::int2),
alignof(blender::int2),
"vec2i",
1,
N_("Int 2D"),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 47: CD_PROP_COLOR */
{sizeof(MPropCol),
alignof(MPropCol),
"MPropCol",
1,
N_("Color"),
@ -1850,6 +2060,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr},
/* 48: CD_PROP_FLOAT3 */
{sizeof(float[3]),
alignof(blender::float3),
"vec3f",
1,
N_("Float3"),
@ -1866,6 +2077,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerAdd_propfloat3},
/* 49: CD_PROP_FLOAT2 */
{sizeof(float[2]),
alignof(float2),
"vec2f",
1,
N_("Float2"),
@ -1884,6 +2096,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerCopyValue_propfloat2},
/* 50: CD_PROP_BOOL */
{sizeof(bool),
alignof(bool),
"bool",
1,
N_("Boolean"),
@ -1898,9 +2111,19 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
nullptr},
/* 51: CD_HAIRLENGTH */ /* DEPRECATED */ /* UNUSED */
{sizeof(float), "float", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{sizeof(float),
alignof(float),
"float",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 52: CD_PROP_QUATERNION */
{sizeof(float[4]),
alignof(blender::float4),
"vec4f",
1,
N_("Quaternion"),
@ -2134,12 +2357,15 @@ static bool customdata_typemap_is_valid(const CustomData *data)
static void *copy_layer_data(const eCustomDataType type, const void *data, const int totelem)
{
const LayerTypeInfo &type_info = *layerType_getInfo(type);
const int64_t size_in_bytes = int64_t(totelem) * type_info.size;
void *new_data = MEM_mallocN_aligned(size_in_bytes, type_info.alignment, __func__);
if (type_info.copy) {
void *new_data = MEM_malloc_arrayN(size_t(totelem), type_info.size, __func__);
type_info.copy(data, new_data, totelem);
return new_data;
}
return MEM_dupallocN(data);
else {
memcpy(new_data, data, size_in_bytes);
}
return new_data;
}
static void free_layer_data(const eCustomDataType type, const void *data, const int totelem)
@ -2389,7 +2615,7 @@ void CustomData_realloc(CustomData *data,
const int64_t old_size_in_bytes = int64_t(old_size) * typeInfo->size;
const int64_t new_size_in_bytes = int64_t(new_size) * typeInfo->size;
void *new_layer_data = MEM_mallocN(new_size_in_bytes, __func__);
void *new_layer_data = MEM_mallocN_aligned(new_size_in_bytes, typeInfo->alignment, __func__);
/* Copy data to new array. */
if (old_size_in_bytes) {
if (typeInfo->copy) {
@ -2877,23 +3103,27 @@ static CustomDataLayer *customData_add_layer__internal(
* leaks into the new layer. */
memset(&new_layer, 0, sizeof(CustomDataLayer));
const int64_t size_in_bytes = int64_t(totelem) * type_info.size;
const char *alloc_name = layerType_getName(type);
if (alloctype.has_value()) {
switch (*alloctype) {
case CD_SET_DEFAULT: {
if (totelem > 0) {
new_layer.data = MEM_mallocN_aligned(size_in_bytes, type_info.alignment, alloc_name);
if (type_info.set_default_value) {
new_layer.data = MEM_malloc_arrayN(totelem, type_info.size, layerType_getName(type));
type_info.set_default_value(new_layer.data, totelem);
}
else {
new_layer.data = MEM_calloc_arrayN(totelem, type_info.size, layerType_getName(type));
/* Alternatively, #MEM_calloc_arrayN is faster, but has no aligned version. */
memset(new_layer.data, 0, size_in_bytes);
}
}
break;
}
case CD_CONSTRUCT: {
if (totelem > 0) {
new_layer.data = MEM_malloc_arrayN(totelem, type_info.size, layerType_getName(type));
new_layer.data = MEM_mallocN_aligned(size_in_bytes, type_info.alignment, alloc_name);
if (type_info.construct) {
type_info.construct(new_layer.data, totelem);
}

@ -132,6 +132,9 @@ static void sort_indices(MutableSpan<int> indices, const Span<T> values, const i
const float4 value2_quat = float4(value2);
return value1_quat[component_i] < value2_quat[component_i];
}
if constexpr (std::is_same_v<T, float4x4>) {
return value1.base_ptr()[component_i] < value2.base_ptr()[component_i];
}
if constexpr (std::is_same_v<T, int2>) {
for (int i = 0; i < 2; i++) {
if (value1[i] != value2[i]) {
@ -248,6 +251,10 @@ static bool values_different(const T value1,
const float4 value2_f = float4(value2);
return compare_threshold_relative(value1_f[component_i], value2_f[component_i], threshold);
}
if constexpr (std::is_same_v<T, float4x4>) {
return compare_threshold_relative(
value1.base_ptr()[component_i], value2.base_ptr()[component_i], threshold);
}
BLI_assert_unreachable();
}
@ -575,6 +582,9 @@ static std::optional<MeshMismatch> sort_domain_using_attributes(
else if constexpr (is_same_any_v<T, math::Quaternion, ColorGeometry4f>) {
num_loops = 4;
}
else if constexpr (is_same_any_v<T, float4x4>) {
num_loops = 16;
}
for (const int component_i : IndexRange(num_loops)) {
sort_per_set_based_on_attributes(
maps.set_sizes, maps.from_sorted1, maps.from_sorted2, values1, values2, component_i);

@ -353,6 +353,7 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
case SOCK_RGBA:
case SOCK_BOOLEAN:
case SOCK_ROTATION:
case SOCK_MATRIX:
case SOCK_INT:
case SOCK_STRING:
case SOCK_CUSTOM:
@ -703,10 +704,12 @@ static void write_node_socket_default_value(BlendWriter *writer, const bNodeSock
case SOCK_ROTATION:
BLO_write_struct(writer, bNodeSocketValueRotation, sock->default_value);
break;
case SOCK_MENU: {
case SOCK_MENU:
BLO_write_struct(writer, bNodeSocketValueMenu, sock->default_value);
break;
}
case SOCK_MATRIX:
/* Matrix sockets currently have no default value. */
break;
case SOCK_CUSTOM:
/* Custom node sockets where default_value is defined uses custom properties for storage. */
break;
@ -939,6 +942,7 @@ static bool is_node_socket_supported(const bNodeSocket *sock)
case SOCK_MATERIAL:
case SOCK_ROTATION:
case SOCK_MENU:
case SOCK_MATRIX:
return true;
}
return false;
@ -1863,6 +1867,7 @@ static void socket_id_user_increment(bNodeSocket *sock)
case SOCK_RGBA:
case SOCK_BOOLEAN:
case SOCK_ROTATION:
case SOCK_MATRIX:
case SOCK_INT:
case SOCK_STRING:
case SOCK_MENU:
@ -1910,6 +1915,7 @@ static bool socket_id_user_decrement(bNodeSocket *sock)
case SOCK_RGBA:
case SOCK_BOOLEAN:
case SOCK_ROTATION:
case SOCK_MATRIX:
case SOCK_INT:
case SOCK_STRING:
case SOCK_MENU:
@ -1965,6 +1971,7 @@ void nodeModifySocketType(bNodeTree *ntree,
case SOCK_SHADER:
case SOCK_BOOLEAN:
case SOCK_ROTATION:
case SOCK_MATRIX:
case SOCK_CUSTOM:
case SOCK_OBJECT:
case SOCK_IMAGE:
@ -2071,6 +2078,8 @@ const char *nodeStaticSocketType(const int type, const int subtype)
return "NodeSocketBool";
case SOCK_ROTATION:
return "NodeSocketRotation";
case SOCK_MATRIX:
return "NodeSocketMatrix";
case SOCK_VECTOR:
switch (PropertySubType(subtype)) {
case PROP_TRANSLATION:
@ -2154,6 +2163,8 @@ const char *nodeStaticSocketInterfaceTypeNew(const int type, const int subtype)
return "NodeTreeInterfaceSocketBool";
case SOCK_ROTATION:
return "NodeTreeInterfaceSocketRotation";
case SOCK_MATRIX:
return "NodeTreeInterfaceSocketMatrix";
case SOCK_VECTOR:
switch (PropertySubType(subtype)) {
case PROP_TRANSLATION:
@ -2209,6 +2220,8 @@ const char *nodeStaticSocketLabel(const int type, const int /*subtype*/)
return "Boolean";
case SOCK_ROTATION:
return "Rotation";
case SOCK_MATRIX:
return "Matrix";
case SOCK_VECTOR:
return "Vector";
case SOCK_RGBA:
@ -2756,6 +2769,9 @@ static void *socket_value_storage(bNodeSocket &socket)
return &socket.default_value_typed<bNodeSocketValueRotation>()->value_euler;
case SOCK_MENU:
return &socket.default_value_typed<bNodeSocketValueMenu>()->value;
case SOCK_MATRIX:
/* Matrix sockets currently have no default value. */
return nullptr;
case SOCK_STRING:
/* We don't want do this now! */
return nullptr;
@ -4344,6 +4360,8 @@ std::optional<eCustomDataType> socket_type_to_custom_data_type(eNodeSocketDataty
return CD_PROP_BOOL;
case SOCK_ROTATION:
return CD_PROP_QUATERNION;
case SOCK_MATRIX:
return CD_PROP_FLOAT4X4;
case SOCK_INT:
return CD_PROP_INT32;
case SOCK_STRING:
@ -4372,6 +4390,8 @@ std::optional<eNodeSocketDatatype> custom_data_type_to_socket_type(eCustomDataTy
return SOCK_RGBA;
case CD_PROP_QUATERNION:
return SOCK_ROTATION;
case CD_PROP_FLOAT4X4:
return SOCK_MATRIX;
default:
return std::nullopt;
}
@ -4434,6 +4454,9 @@ std::optional<eNodeSocketDatatype> geo_nodes_base_cpp_type_to_socket_type(const
if (type.is<math::Quaternion>()) {
return SOCK_ROTATION;
}
if (type.is<float4x4>()) {
return SOCK_MATRIX;
}
if (type.is<std::string>()) {
return SOCK_STRING;
}

@ -54,6 +54,9 @@ template<typename T> static std::optional<eNodeSocketDatatype> static_type_to_so
if constexpr (is_single_or_field_or_grid_v<T, math::Quaternion>) {
return SOCK_ROTATION;
}
if constexpr (is_same_any_v<T, float4x4, fn::Field<float4x4>>) {
return SOCK_MATRIX;
}
if constexpr (is_same_any_v<T, std::string>) {
return SOCK_STRING;
}
@ -195,6 +198,10 @@ void SocketValueVariant::store_single(const eNodeSocketDatatype socket_type, con
value_.emplace<math::Quaternion>(*static_cast<const math::Quaternion *>(value));
break;
}
case SOCK_MATRIX: {
value_.emplace<float4x4>(*static_cast<const float4x4 *>(value));
break;
}
case SOCK_RGBA: {
value_.emplace<ColorGeometry4f>(*static_cast<const ColorGeometry4f *>(value));
break;
@ -281,6 +288,8 @@ void *SocketValueVariant::allocate_single(const eNodeSocketDatatype socket_type)
return value_.allocate<bool>();
case SOCK_ROTATION:
return value_.allocate<math::Quaternion>();
case SOCK_MATRIX:
return value_.allocate<float4x4>();
case SOCK_RGBA:
return value_.allocate<ColorGeometry4f>();
case SOCK_STRING:
@ -344,6 +353,9 @@ INSTANTIATE_SINGLE_AND_FIELD_AND_GRID(blender::math::Quaternion)
INSTANTIATE(std::string)
INSTANTIATE(fn::GField)
INSTANTIATE(float4x4)
INSTANTIATE(fn::Field<float4x4>)
#ifdef WITH_OPENVDB
INSTANTIATE(GVolumeGrid)
#endif

@ -349,11 +349,21 @@ static ColorGeometry4f byte_color_to_color(const ColorGeometry4b &a)
return a.decode();
}
static math::Quaternion float4x4_to_quaternion(const float4x4 &a)
{
return math::to_quaternion(a);
}
static float3 quaternion_to_float3(const math::Quaternion &a)
{
return float3(math::to_euler(a).xyz());
}
static float4x4 quaternion_to_float4x4(const math::Quaternion &a)
{
return math::from_rotation<float4x4>(a);
}
static DataTypeConversions create_implicit_conversions()
{
DataTypeConversions conversions;
@ -441,7 +451,10 @@ static DataTypeConversions create_implicit_conversions()
add_implicit_conversion<ColorGeometry4b, float3, byte_color_to_float3>(conversions);
add_implicit_conversion<ColorGeometry4b, ColorGeometry4f, byte_color_to_color>(conversions);
add_implicit_conversion<float4x4, math::Quaternion, float4x4_to_quaternion>(conversions);
add_implicit_conversion<math::Quaternion, float3, quaternion_to_float3>(conversions);
add_implicit_conversion<math::Quaternion, float4x4, quaternion_to_float4x4>(conversions);
return conversions;
}

@ -23,11 +23,13 @@ GPUVertFormat init_format_for_attribute(const eCustomDataType data_type,
bke::attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
using Converter = AttributeConverter<T>;
GPU_vertformat_attr_add(&format,
vbo_name.c_str(),
Converter::gpu_component_type,
Converter::gpu_component_len,
Converter::gpu_fetch_mode);
if constexpr (!std::is_void_v<typename Converter::VBOType>) {
GPU_vertformat_attr_add(&format,
vbo_name.c_str(),
Converter::gpu_component_type,
Converter::gpu_component_len,
Converter::gpu_fetch_mode);
}
});
return format;
}
@ -38,18 +40,20 @@ void vertbuf_data_extract_direct(const GSpan attribute, GPUVertBuf &vbo)
using T = decltype(dummy);
using Converter = AttributeConverter<T>;
using VBOType = typename Converter::VBOType;
const Span<T> src = attribute.typed<T>();
MutableSpan<VBOType> data(static_cast<VBOType *>(GPU_vertbuf_get_data(&vbo)),
attribute.size());
if constexpr (std::is_same_v<T, VBOType>) {
array_utils::copy(src, data);
}
else {
threading::parallel_for(src.index_range(), 8192, [&](const IndexRange range) {
for (const int i : range) {
data[i] = Converter::convert(src[i]);
}
});
if constexpr (!std::is_void_v<VBOType>) {
const Span<T> src = attribute.typed<T>();
MutableSpan<VBOType> data(static_cast<VBOType *>(GPU_vertbuf_get_data(&vbo)),
attribute.size());
if constexpr (std::is_same_v<T, VBOType>) {
array_utils::copy(src, data);
}
else {
threading::parallel_for(src.index_range(), 8192, [&](const IndexRange range) {
for (const int i : range) {
data[i] = Converter::convert(src[i]);
}
});
}
}
});
}

@ -582,9 +582,11 @@ struct PBVHBatches {
using T = decltype(dummy);
using Converter = AttributeConverter<T>;
using VBOType = typename Converter::VBOType;
std::fill_n(static_cast<VBOType *>(GPU_vertbuf_get_data(vbo.vert_buf)),
GPU_vertbuf_get_vertex_len(vbo.vert_buf),
VBOType());
if constexpr (!std::is_void_v<VBOType>) {
std::fill_n(static_cast<VBOType *>(GPU_vertbuf_get_data(vbo.vert_buf)),
GPU_vertbuf_get_vertex_len(vbo.vert_buf),
VBOType());
}
});
}
}
@ -754,18 +756,20 @@ struct PBVHBatches {
const GVArraySpan attribute = *attributes.lookup_or_default(name, domain, data_type);
bke::attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
switch (domain) {
case bke::AttrDomain::Point:
extract_data_vert_faces<T>(args, attribute.typed<T>(), vert_buf);
break;
case bke::AttrDomain::Face:
extract_data_face_faces<T>(args, attribute.typed<T>(), vert_buf);
break;
case bke::AttrDomain::Corner:
extract_data_corner_faces<T>(args, attribute.typed<T>(), vert_buf);
break;
default:
BLI_assert_unreachable();
if constexpr (!std::is_void_v<typename AttributeConverter<T>::VBOType>) {
switch (domain) {
case bke::AttrDomain::Point:
extract_data_vert_faces<T>(args, attribute.typed<T>(), vert_buf);
break;
case bke::AttrDomain::Face:
extract_data_face_faces<T>(args, attribute.typed<T>(), vert_buf);
break;
case bke::AttrDomain::Corner:
extract_data_corner_faces<T>(args, attribute.typed<T>(), vert_buf);
break;
default:
BLI_assert_unreachable();
}
}
});
}
@ -920,18 +924,20 @@ struct PBVHBatches {
const int cd_offset = CustomData_get_offset_named(&custom_data, data_type, request.name);
bke::attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
switch (domain) {
case bke::AttrDomain::Point:
extract_data_vert_bmesh<T>(args, cd_offset, *vbo.vert_buf);
break;
case bke::AttrDomain::Face:
extract_data_face_bmesh<T>(args, cd_offset, *vbo.vert_buf);
break;
case bke::AttrDomain::Corner:
extract_data_corner_bmesh<T>(args, cd_offset, *vbo.vert_buf);
break;
default:
BLI_assert_unreachable();
if constexpr (!std::is_void_v<typename AttributeConverter<T>::VBOType>) {
switch (domain) {
case bke::AttrDomain::Point:
extract_data_vert_bmesh<T>(args, cd_offset, *vbo.vert_buf);
break;
case bke::AttrDomain::Face:
extract_data_face_bmesh<T>(args, cd_offset, *vbo.vert_buf);
break;
case bke::AttrDomain::Corner:
extract_data_corner_bmesh<T>(args, cd_offset, *vbo.vert_buf);
break;
default:
BLI_assert_unreachable();
}
}
});
}

@ -199,21 +199,23 @@ static void extract_attribute(const MeshRenderData &mr,
bke::attribute_math::convert_to_static_type(request.cd_type, [&](auto dummy) {
using T = decltype(dummy);
switch (request.domain) {
case bke::AttrDomain::Point:
extract_data_bmesh_vert<T>(*mr.bm, cd_offset, vbo);
break;
case bke::AttrDomain::Edge:
extract_data_bmesh_edge<T>(*mr.bm, cd_offset, vbo);
break;
case bke::AttrDomain::Face:
extract_data_bmesh_face<T>(*mr.bm, cd_offset, vbo);
break;
case bke::AttrDomain::Corner:
extract_data_bmesh_loop<T>(*mr.bm, cd_offset, vbo);
break;
default:
BLI_assert_unreachable();
if constexpr (!std::is_void_v<typename AttributeConverter<T>::VBOType>) {
switch (request.domain) {
case bke::AttrDomain::Point:
extract_data_bmesh_vert<T>(*mr.bm, cd_offset, vbo);
break;
case bke::AttrDomain::Edge:
extract_data_bmesh_edge<T>(*mr.bm, cd_offset, vbo);
break;
case bke::AttrDomain::Face:
extract_data_bmesh_face<T>(*mr.bm, cd_offset, vbo);
break;
case bke::AttrDomain::Corner:
extract_data_bmesh_loop<T>(*mr.bm, cd_offset, vbo);
break;
default:
BLI_assert_unreachable();
}
}
});
}
@ -225,21 +227,23 @@ static void extract_attribute(const MeshRenderData &mr,
bke::attribute_math::convert_to_static_type(request.cd_type, [&](auto dummy) {
using T = decltype(dummy);
switch (request.domain) {
case bke::AttrDomain::Point:
extract_data_mesh_mapped_corner(attribute.typed<T>(), mr.corner_verts, vbo);
break;
case bke::AttrDomain::Edge:
extract_data_mesh_mapped_corner(attribute.typed<T>(), mr.corner_edges, vbo);
break;
case bke::AttrDomain::Face:
extract_data_mesh_face(mr.faces, attribute.typed<T>(), vbo);
break;
case bke::AttrDomain::Corner:
vertbuf_data_extract_direct(attribute.typed<T>(), vbo);
break;
default:
BLI_assert_unreachable();
if constexpr (!std::is_void_v<typename AttributeConverter<T>::VBOType>) {
switch (request.domain) {
case bke::AttrDomain::Point:
extract_data_mesh_mapped_corner(attribute.typed<T>(), mr.corner_verts, vbo);
break;
case bke::AttrDomain::Edge:
extract_data_mesh_mapped_corner(attribute.typed<T>(), mr.corner_edges, vbo);
break;
case bke::AttrDomain::Face:
extract_data_mesh_face(mr.faces, attribute.typed<T>(), vbo);
break;
case bke::AttrDomain::Corner:
vertbuf_data_extract_direct(attribute.typed<T>(), vbo);
break;
default:
BLI_assert_unreachable();
}
}
});
}
@ -282,12 +286,14 @@ static void extract_attr_init_subdiv(const DRWSubdivCache &subdiv_cache,
bke::attribute_math::convert_to_static_type(request.cd_type, [&](auto dummy) {
using T = decltype(dummy);
using Converter = AttributeConverter<T>;
draw_subdiv_interp_custom_data(subdiv_cache,
src_data,
dst_buffer,
Converter::gpu_component_type,
Converter::gpu_component_len,
0);
if constexpr (!std::is_void_v<typename Converter::VBOType>) {
draw_subdiv_interp_custom_data(subdiv_cache,
src_data,
dst_buffer,
Converter::gpu_component_type,
Converter::gpu_component_len,
0);
}
});
GPU_vertbuf_discard(src_data);

@ -1201,6 +1201,7 @@ static const float std_node_socket_colors[][4] = {
{0.92, 0.46, 0.51, 1.0}, /* SOCK_MATERIAL */
{0.65, 0.39, 0.78, 1.0}, /* SOCK_ROTATION */
{0.40, 0.40, 0.40, 1.0}, /* SOCK_MENU */
{0.72, 0.20, 0.52, 1.0}, /* SOCK_MATRIX */
};
/* Callback for colors that does not depend on the socket pointer argument to get the type. */
@ -1236,6 +1237,7 @@ static const SocketColorFn std_node_socket_color_funcs[] = {
std_node_socket_color_fn<SOCK_MATERIAL>,
std_node_socket_color_fn<SOCK_ROTATION>,
std_node_socket_color_fn<SOCK_MENU>,
std_node_socket_color_fn<SOCK_MATRIX>,
};
/* draw function for file output node sockets,
@ -1350,6 +1352,10 @@ static void std_node_socket_draw(
uiItemR(column, ptr, "default_value", DEFAULT_FLAGS, text, ICON_NONE);
break;
}
case SOCK_MATRIX: {
uiItemL(layout, text, ICON_NONE);
break;
}
case SOCK_RGBA: {
if (text[0] == '\0') {
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, "", ICON_NONE);
@ -1528,6 +1534,7 @@ static void std_node_socket_interface_draw(ID *id,
}
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_MATRIX:
break;
case SOCK_CUSTOM:

@ -1303,6 +1303,14 @@ static void create_inspection_string_for_generic_value(const bNodeSocket &socket
ss << fmt::format(TIP_("{} (Boolean)"),
((*static_cast<bool *>(socket_value)) ? TIP_("True") : TIP_("False")));
}
else if (socket_type.is<float4x4>()) {
const float4x4 &value = *static_cast<const float4x4 *>(socket_value);
ss << value[0] << ",\n";
ss << value[1] << ",\n";
ss << value[2] << ",\n";
ss << value[3] << ",\n";
ss << TIP_("(Matrix)");
}
}
static void create_inspection_string_for_field_info(const bNodeSocket &socket,

@ -157,6 +157,7 @@ static eCustomDataType data_type_in_attribute_input_node(const eCustomDataType t
case CD_PROP_COLOR:
case CD_PROP_BOOL:
case CD_PROP_QUATERNION:
case CD_PROP_FLOAT4X4:
return type;
case CD_PROP_BYTE_COLOR:
return CD_PROP_COLOR;

@ -2230,6 +2230,7 @@ static int get_main_socket_priority(const bNodeSocket *socket)
case SOCK_OBJECT:
case SOCK_IMAGE:
case SOCK_ROTATION:
case SOCK_MATRIX:
case SOCK_GEOMETRY:
case SOCK_COLLECTION:
case SOCK_TEXTURE:

@ -391,6 +391,9 @@ static Vector<NodeLinkItem> ui_node_link_items(NodeLinkArg *arg,
else if (dynamic_cast<const decl::Rotation *>(&socket_decl)) {
item.socket_type = SOCK_ROTATION;
}
else if (dynamic_cast<const decl::Matrix *>(&socket_decl)) {
item.socket_type = SOCK_MATRIX;
}
else if (dynamic_cast<const decl::String *>(&socket_decl)) {
item.socket_type = SOCK_STRING;
}

@ -341,6 +341,7 @@ static float get_default_column_width(const ColumnValues &values)
static const float float_width = 3;
switch (values.type()) {
case SPREADSHEET_VALUE_TYPE_BOOL:
case SPREADSHEET_VALUE_TYPE_FLOAT4X4:
return 2.0f;
case SPREADSHEET_VALUE_TYPE_INT8:
case SPREADSHEET_VALUE_TYPE_INT32:

@ -60,6 +60,9 @@ eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type)
if (type.is<math::Quaternion>()) {
return SPREADSHEET_VALUE_TYPE_QUATERNION;
}
if (type.is<float4x4>()) {
return SPREADSHEET_VALUE_TYPE_FLOAT4X4;
}
return SPREADSHEET_VALUE_TYPE_UNKNOWN;
}

@ -209,6 +209,9 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
const float4 value = float4(data.get<math::Quaternion>(real_index));
this->draw_float_vector(params, Span(&value.x, 4));
}
else if (data.type().is<float4x4>()) {
this->draw_float4x4(params, data.get<float4x4>(real_index));
}
else if (data.type().is<bke::InstanceReference>()) {
const bke::InstanceReference value = data.get<bke::InstanceReference>(real_index);
switch (value.type()) {
@ -398,6 +401,40 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
}
}
void draw_float4x4(const CellDrawParams &params, const float4x4 &value) const
{
uiBut *but = uiDefIconTextBut(params.block,
UI_BTYPE_LABEL,
0,
ICON_NONE,
"...",
params.xmin,
params.ymin,
params.width,
params.height,
nullptr,
0,
0,
0,
0,
nullptr);
/* Center alignment. */
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
UI_but_func_tooltip_set(
but,
[](bContext * /*C*/, void *argN, const char * /*tip*/) {
const float4x4 &value = *static_cast<const float4x4 *>(argN);
std::stringstream ss;
ss << value[0] << ",\n";
ss << value[1] << ",\n";
ss << value[2] << ",\n";
ss << value[3];
return ss.str();
},
MEM_new<float4x4>(__func__, value),
MEM_freeN);
}
int column_width(int column_index) const final
{
return spreadsheet_layout_.columns[column_index].width;

@ -109,6 +109,7 @@ static std::string value_string(const SpreadsheetRowFilter &row_filter,
case SPREADSHEET_VALUE_TYPE_STRING:
return row_filter.value_string;
case SPREADSHEET_VALUE_TYPE_QUATERNION:
case SPREADSHEET_VALUE_TYPE_FLOAT4X4:
case SPREADSHEET_VALUE_TYPE_UNKNOWN:
return "";
}
@ -253,6 +254,7 @@ static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel)
break;
case SPREADSHEET_VALUE_TYPE_UNKNOWN:
case SPREADSHEET_VALUE_TYPE_QUATERNION:
case SPREADSHEET_VALUE_TYPE_FLOAT4X4:
uiItemL(layout, IFACE_("Unsupported column type"), ICON_ERROR);
break;
}

@ -138,7 +138,7 @@ typedef enum eCustomDataType {
CD_PROP_BYTE_COLOR = 17,
CD_TANGENT = 18,
CD_MDISPS = 19,
/* CD_PREVIEW_MCOL = 20, */ /* UNUSED */
CD_PROP_FLOAT4X4 = 20,
/* CD_ID_MCOL = 21, */
/* CD_TEXTURE_MLOOPCOL = 22, */ /* UNUSED */
CD_CLOTH_ORCO = 23,
@ -230,6 +230,7 @@ using eCustomDataMask = uint64_t;
#define CD_MASK_PROP_INT8 (1ULL << CD_PROP_INT8)
#define CD_MASK_PROP_INT32_2D (1ULL << CD_PROP_INT32_2D)
#define CD_MASK_PROP_QUATERNION (1ULL << CD_PROP_QUATERNION)
#define CD_MASK_PROP_FLOAT4X4 (1ULL << CD_PROP_FLOAT4X4)
/** Multi-resolution loop data. */
#define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK)
@ -241,7 +242,7 @@ using eCustomDataMask = uint64_t;
#define CD_MASK_PROP_ALL \
(CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 | \
CD_MASK_PROP_COLOR | CD_MASK_PROP_STRING | CD_MASK_PROP_BYTE_COLOR | CD_MASK_PROP_BOOL | \
CD_MASK_PROP_INT8 | CD_MASK_PROP_INT32_2D | CD_MASK_PROP_QUATERNION)
CD_MASK_PROP_INT8 | CD_MASK_PROP_INT32_2D | CD_MASK_PROP_QUATERNION | CD_MASK_PROP_FLOAT4X4)
/* All color attributes */
#define CD_MASK_COLOR_ALL (CD_MASK_PROP_COLOR | CD_MASK_PROP_BYTE_COLOR)

@ -263,6 +263,7 @@ typedef enum eNodeSocketDatatype {
SOCK_MATERIAL = 13,
SOCK_ROTATION = 14,
SOCK_MENU = 15,
SOCK_MATRIX = 16,
} eNodeSocketDatatype;
/** Socket shape. */

@ -2037,6 +2037,7 @@ typedef enum eSpreadsheetColumnValueType {
SPREADSHEET_VALUE_TYPE_INT8 = 9,
SPREADSHEET_VALUE_TYPE_INT32_2D = 10,
SPREADSHEET_VALUE_TYPE_QUATERNION = 11,
SPREADSHEET_VALUE_TYPE_FLOAT4X4 = 12,
} eSpreadsheetColumnValueType;
/**

@ -716,12 +716,13 @@ typedef struct UserDef_Experimental {
char use_extended_asset_browser;
char use_sculpt_texture_paint;
char use_grease_pencil_version3;
char use_new_matrix_socket;
char enable_overlay_next;
char use_new_volume_nodes;
char use_shader_node_previews;
char use_extension_repos;
char _pad[4];
char _pad[3];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;

@ -49,6 +49,10 @@ typedef struct vec4i {
typedef struct vec4f {
float x, y, z, w;
} vec4f;
typedef struct mat4x4f {
float value[4][4];
} mat4x4f;
/*
typedef struct vec4d {
double x, y, z, w;

@ -47,6 +47,7 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
{CD_PROP_INT8, "INT8", 0, "8-Bit Integer", "Smaller integer with a range from -128 to 127"},
{CD_PROP_INT32_2D, "INT32_2D", 0, "2D Integer Vector", "32-bit signed integer vector"},
{CD_PROP_QUATERNION, "QUATERNION", 0, "Quaternion", "Floating point quaternion rotation"},
{CD_PROP_FLOAT4X4, "FLOAT4X4", 0, "4x4 Matrix", "Floating point matrix"},
{0, nullptr, 0, nullptr, nullptr},
};
@ -76,6 +77,7 @@ const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = {
{CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"},
{CD_PROP_INT32_2D, "INT32_2D", 0, "2D Integer Vector", "32-bit signed integer vector"},
{CD_PROP_QUATERNION, "QUATERNION", 0, "Quaternion", "Floating point quaternion rotation"},
{CD_PROP_FLOAT4X4, "FLOAT4X4", 0, "4x4 Matrix", "Floating point matrix"},
{0, nullptr, 0, nullptr, nullptr},
};
@ -207,6 +209,8 @@ static StructRNA *srna_by_custom_data_layer_type(const eCustomDataType type)
return &RNA_Int2Attribute;
case CD_PROP_QUATERNION:
return &RNA_QuaternionAttribute;
case CD_PROP_FLOAT4X4:
return &RNA_Float4x4Attribute;
default:
return nullptr;
}
@ -1096,6 +1100,40 @@ static void rna_def_attribute_quaternion(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Attribute_update_data");
}
static void rna_def_attribute_float4x4(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "Float4x4Attribute", "Attribute");
RNA_def_struct_sdna(srna, "CustomDataLayer");
RNA_def_struct_ui_text(
srna, "4x4 Matrix Attribute", "Geometry attribute that stores a 4 by 4 float matrix");
prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "Float4x4AttributeValue");
RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_collection_funcs(prop,
"rna_Attribute_data_begin",
"rna_iterator_array_next",
"rna_iterator_array_end",
"rna_iterator_array_get",
"rna_Attribute_data_length",
nullptr,
nullptr,
nullptr);
srna = RNA_def_struct(brna, "Float4x4AttributeValue", nullptr);
RNA_def_struct_sdna(srna, "mat4x4f");
RNA_def_struct_ui_text(srna, "Matrix Attribute Value", "Matrix value in geometry attribute");
prop = RNA_def_property(srna, "value", PROP_FLOAT, PROP_MATRIX);
RNA_def_property_ui_text(prop, "Value", "Matrix");
RNA_def_property_float_sdna(prop, nullptr, "value");
RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4);
RNA_def_property_update(prop, 0, "rna_Attribute_update_data");
}
static void rna_def_attribute_float2(BlenderRNA *brna)
{
StructRNA *srna;
@ -1182,6 +1220,7 @@ static void rna_def_attribute(BlenderRNA *brna)
rna_def_attribute_int(brna);
rna_def_attribute_int2(brna);
rna_def_attribute_quaternion(brna);
rna_def_attribute_float4x4(brna);
rna_def_attribute_string(brna);
rna_def_attribute_bool(brna);
rna_def_attribute_float2(brna);

@ -25,6 +25,7 @@ const EnumPropertyItem rna_enum_node_socket_type_items[] = {
{SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""},
{SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
{SOCK_ROTATION, "ROTATION", 0, "Rotation", ""},
{SOCK_MATRIX, "MATRIX", 0, "Matrix", ""},
{SOCK_STRING, "STRING", 0, "String", ""},
{SOCK_RGBA, "RGBA", 0, "RGBA", ""},
{SOCK_SHADER, "SHADER", 0, "Shader", ""},
@ -1011,6 +1012,30 @@ static void rna_def_node_socket_interface_rotation(BlenderRNA *brna, const char
rna_def_node_tree_interface_socket_builtin(srna);
}
static void rna_def_node_socket_matrix(BlenderRNA *brna, const char *identifier)
{
StructRNA *srna;
srna = RNA_def_struct(brna, identifier, "NodeSocketStandard");
RNA_def_struct_ui_text(srna, "Matrix Node Socket", "Matrix value socket of a node");
RNA_def_struct_sdna(srna, "bNodeSocket");
RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr);
}
static void rna_def_node_socket_interface_matrix(BlenderRNA *brna, const char *identifier)
{
StructRNA *srna;
srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket");
RNA_def_struct_ui_text(srna, "Matrix Node Socket Interface", "Matrix value socket of a node");
RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket");
RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr);
rna_def_node_tree_interface_socket_builtin(srna);
}
static void rna_def_node_socket_vector(BlenderRNA *brna,
const char *identifier,
PropertySubType subtype)
@ -1528,6 +1553,7 @@ static const bNodeSocketStaticTypeInfo node_socket_subtypes[] = {
{"NodeSocketIntFactor", "NodeTreeInterfaceSocketIntFactor", SOCK_INT, PROP_FACTOR},
{"NodeSocketBool", "NodeTreeInterfaceSocketBool", SOCK_BOOLEAN, PROP_NONE},
{"NodeSocketRotation", "NodeTreeInterfaceSocketRotation", SOCK_ROTATION, PROP_NONE},
{"NodeSocketMatrix", "NodeTreeInterfaceSocketMatrix", SOCK_MATRIX, PROP_NONE},
{"NodeSocketVector", "NodeTreeInterfaceSocketVector", SOCK_VECTOR, PROP_NONE},
{"NodeSocketVectorTranslation",
"NodeTreeInterfaceSocketVectorTranslation",
@ -1577,6 +1603,9 @@ static void rna_def_node_socket_subtypes(BlenderRNA *brna)
case SOCK_ROTATION:
rna_def_node_socket_rotation(brna, identifier);
break;
case SOCK_MATRIX:
rna_def_node_socket_matrix(brna, identifier);
break;
case SOCK_VECTOR:
rna_def_node_socket_vector(brna, identifier, info.subtype);
break;
@ -1640,6 +1669,9 @@ void rna_def_node_socket_interface_subtypes(BlenderRNA *brna)
case SOCK_ROTATION:
rna_def_node_socket_interface_rotation(brna, identifier);
break;
case SOCK_MATRIX:
rna_def_node_socket_interface_matrix(brna, identifier);
break;
case SOCK_VECTOR:
rna_def_node_socket_interface_vector(brna, identifier, info.subtype);
break;

@ -77,6 +77,7 @@ const EnumPropertyItem rna_enum_node_socket_data_type_items[] = {
{SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""},
{SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
{SOCK_ROTATION, "ROTATION", 0, "Rotation", ""},
{SOCK_MATRIX, "MATRIX", 0, "Matrix", ""},
{SOCK_STRING, "STRING", 0, "String", ""},
{SOCK_MENU, "MENU", 0, "Menu", ""},
{SOCK_RGBA, "RGBA", 0, "Color", ""},
@ -1894,7 +1895,8 @@ static bool generic_attribute_type_supported(const EnumPropertyItem *item)
CD_PROP_BOOL,
CD_PROP_INT32,
CD_PROP_BYTE_COLOR,
CD_PROP_QUATERNION);
CD_PROP_QUATERNION,
CD_PROP_FLOAT4X4);
}
static bool generic_attribute_type_supported_with_socket(const EnumPropertyItem *item)

@ -7120,6 +7120,12 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, 0, "rna_userdef_keyconfig_reload_update");
prop = RNA_def_property(srna, "use_new_matrix_socket", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "use_new_matrix_socket", 1);
RNA_def_property_ui_text(
prop, "Matrix Socket", "Enable the matrix socket type for geometry nodes");
RNA_def_property_update(prop, 0, "rna_userdef_ui_update");
prop = RNA_def_property(srna, "use_viewport_debug", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "use_viewport_debug", 1);
RNA_def_property_ui_text(prop,

@ -824,7 +824,7 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md)
IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket->identifier);
if (property == nullptr) {
if (type == SOCK_GEOMETRY) {
if (ELEM(type, SOCK_GEOMETRY, SOCK_MATRIX)) {
geometry_socket_count++;
}
else {

@ -87,8 +87,15 @@ class GeoNodeExecParams {
}
template<typename T>
static inline constexpr bool is_field_base_type_v =
is_same_any_v<T, float, int, bool, ColorGeometry4f, float3, std::string, math::Quaternion>;
static inline constexpr bool is_field_base_type_v = is_same_any_v<T,
float,
int,
bool,
ColorGeometry4f,
float3,
std::string,
math::Quaternion,
float4x4>;
template<typename T>
static inline constexpr bool stored_as_SocketValueVariant_v =

@ -158,6 +158,22 @@ class RotationBuilder : public SocketDeclarationBuilder<Rotation> {
RotationBuilder &default_value(const math::EulerXYZ &value);
};
class MatrixBuilder;
class Matrix : public SocketDeclaration {
public:
friend MatrixBuilder;
using Builder = MatrixBuilder;
bNodeSocket &build(bNodeTree &ntree, bNode &node) const override;
bool matches(const bNodeSocket &socket) const override;
bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override;
bool can_connect(const bNodeSocket &socket) const override;
};
class MatrixBuilder : public SocketDeclarationBuilder<Matrix> {};
class StringBuilder;
class String : public SocketDeclaration {

@ -267,6 +267,7 @@ DefNode(FunctionNode, FN_NODE_AXIS_ANGLE_TO_ROTATION, 0, "AXIS_ANGLE_TO_ROTATION
DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, 0, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_COMBINE_COLOR, 0, "COMBINE_COLOR", CombineColor, "Combine Color", "")
DefNode(FunctionNode, FN_NODE_QUATERNION_TO_ROTATION, 0, "QUATERNION_TO_ROTATION", QuaternionToRotation, "Quaternion to Rotation", "")
DefNode(FunctionNode, FN_NODE_COMBINE_TRANSFORM, 0, "COMBINE_TRANSFORM", CombineTransform, "Combine Transform", "")
DefNode(FunctionNode, FN_NODE_COMPARE, 0, "COMPARE", Compare, "Compare", "")
DefNode(FunctionNode, FN_NODE_EULER_TO_ROTATION, 0, "EULER_TO_ROTATION", EulerToRotation, "Euler to Rotation", "")
DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "")
@ -276,7 +277,9 @@ DefNode(FunctionNode, FN_NODE_INPUT_INT, def_fn_input_int, "INPUT_INT", InputInt
DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARACTERS", InputSpecialCharacters, "Special Characters", "")
DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "")
DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
DefNode(FunctionNode, FN_NODE_INVERT_MATRIX, 0, "INVERT_MATRIX", InvertMatrix, "Invert Matrix", "")
DefNode(FunctionNode, FN_NODE_INVERT_ROTATION, 0, "INVERT_ROTATION", InvertRotation, "Invert Rotation", "")
DefNode(FunctionNode, FN_NODE_MATRIX_MULTIPLY, 0, "MATRIX_MULTIPLY", MatrixMultiply, "Multiply Matrices", "")
DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "")
DefNode(FunctionNode, FN_NODE_REPLACE_STRING, 0, "REPLACE_STRING", ReplaceString, "Replace String", "")
DefNode(FunctionNode, FN_NODE_ROTATE_EULER, def_fn_rotate_euler, "ROTATE_EULER", RotateEuler, "Rotate Euler", "")
@ -286,8 +289,12 @@ DefNode(FunctionNode, FN_NODE_ROTATION_TO_AXIS_ANGLE, 0, "ROTATION_TO_AXIS_ANGLE
DefNode(FunctionNode, FN_NODE_ROTATION_TO_EULER, 0, "ROTATION_TO_EULER", RotationToEuler, "Rotation to Euler", "")
DefNode(FunctionNode, FN_NODE_SEPARATE_COLOR, 0, "SEPARATE_COLOR", SeparateColor, "Separate Color", "")
DefNode(FunctionNode, FN_NODE_ROTATION_TO_QUATERNION, 0, "ROTATION_TO_QUATERNION", RotationToQuaternion, "Rotation to Quaternion", "")
DefNode(FunctionNode, FN_NODE_SEPARATE_TRANSFORM, 0, "SEPARATE_TRANSFORM", SeparateTransform, "Separate Transform", "")
DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Slice String", "")
DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "")
DefNode(FunctionNode, FN_NODE_TRANSFORM_DIRECTION, 0, "TRANSFORM_DIRECTION", TransformDirection, "Transform Direction", "")
DefNode(FunctionNode, FN_NODE_TRANSFORM_POINT, 0, "TRANSFORM_POINT", TransformPoint, "Transform Point", "")
DefNode(FunctionNode, FN_NODE_TRANSPOSE_MATRIX, 0, "TRANSPOSE_MATRIX", TransposeMatrix, "Transpose Matrix", "")
DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "")
DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, 0, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "Add the values of an evaluated field together and output the running total for each element")

@ -51,12 +51,16 @@ struct SimulationItemsAccessor {
}
static bool supports_socket_type(const eNodeSocketDatatype socket_type)
{
if (socket_type == SOCK_MATRIX) {
return U.experimental.use_new_matrix_socket;
}
return ELEM(socket_type,
SOCK_FLOAT,
SOCK_VECTOR,
SOCK_RGBA,
SOCK_BOOLEAN,
SOCK_ROTATION,
SOCK_MATRIX,
SOCK_INT,
SOCK_STRING,
SOCK_GEOMETRY);
@ -115,12 +119,16 @@ struct RepeatItemsAccessor {
}
static bool supports_socket_type(const eNodeSocketDatatype socket_type)
{
if (socket_type == SOCK_MATRIX) {
return U.experimental.use_new_matrix_socket;
}
return ELEM(socket_type,
SOCK_FLOAT,
SOCK_VECTOR,
SOCK_RGBA,
SOCK_BOOLEAN,
SOCK_ROTATION,
SOCK_MATRIX,
SOCK_INT,
SOCK_STRING,
SOCK_GEOMETRY,

@ -31,7 +31,10 @@ set(SRC
nodes/node_fn_input_special_characters.cc
nodes/node_fn_input_string.cc
nodes/node_fn_input_vector.cc
nodes/node_fn_invert_matrix.cc
nodes/node_fn_invert_rotation.cc
nodes/node_fn_combine_transform.cc
nodes/node_fn_matrix_multiply.cc
nodes/node_fn_quaternion_to_rotation.cc
nodes/node_fn_random_value.cc
nodes/node_fn_replace_string.cc
@ -42,8 +45,12 @@ set(SRC
nodes/node_fn_rotation_to_euler.cc
nodes/node_fn_rotation_to_quaternion.cc
nodes/node_fn_separate_color.cc
nodes/node_fn_separate_transform.cc
nodes/node_fn_slice_string.cc
nodes/node_fn_string_length.cc
nodes/node_fn_transform_direction.cc
nodes/node_fn_transform_point.cc
nodes/node_fn_transpose_matrix.cc
nodes/node_fn_value_to_string.cc
node_function_util.cc

@ -0,0 +1,98 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_matrix.hh"
#include "BLI_math_rotation.hh"
#include "NOD_socket_search_link.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_combine_transform_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Vector>("Location").subtype(PROP_TRANSLATION);
b.add_input<decl::Rotation>("Rotation");
b.add_input<decl::Vector>("Scale").default_value(float3(1)).subtype(PROP_XYZ);
b.add_output<decl::Matrix>("Transform");
}
static void search_link_ops(GatherLinkSearchOpParams &params)
{
if (U.experimental.use_new_matrix_socket) {
nodes::search_link_ops_for_basic_node(params);
}
}
class CombineTransformFunction : public mf::MultiFunction {
public:
CombineTransformFunction()
{
static const mf::Signature signature = []() {
mf::Signature signature;
mf::SignatureBuilder builder{"Combine Transform", signature};
builder.single_input<float3>("Location");
builder.single_input<math::Quaternion>("Rotation");
builder.single_input<float3>("Scale");
builder.single_output<float4x4>("Transform");
return signature;
}();
this->set_signature(&signature);
}
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
{
const VArray location = params.readonly_single_input<float3>(0, "Location");
const VArray rotation = params.readonly_single_input<math::Quaternion>(1, "Rotation");
const VArray scale = params.readonly_single_input<float3>(2, "Scale");
MutableSpan transforms = params.uninitialized_single_output<float4x4>(3, "Transform");
const std::optional<float3> location_single = location.get_if_single();
const std::optional<math::Quaternion> rotation_single = rotation.get_if_single();
const std::optional<float3> scale_single = scale.get_if_single();
const bool no_translation = location_single && math::is_zero(*location_single);
const bool no_rotation = rotation_single && math::angle_of(*rotation_single).radian() < 1e-7f;
const bool no_scale = scale_single && math::is_equal(*scale_single, float3(1), 1e-7f);
if (no_rotation && no_scale) {
mask.foreach_index(
[&](const int64_t i) { transforms[i] = math::from_location<float4x4>(location[i]); });
}
else if (no_translation && no_scale) {
mask.foreach_index(
[&](const int64_t i) { transforms[i] = math::from_rotation<float4x4>(rotation[i]); });
}
else if (no_translation && no_rotation) {
mask.foreach_index(
[&](const int64_t i) { transforms[i] = math::from_scale<float4x4>(scale[i]); });
}
else {
mask.foreach_index([&](const int64_t i) {
transforms[i] = math::from_loc_rot_scale<float4x4>(location[i], rotation[i], scale[i]);
});
}
}
};
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static CombineTransformFunction fn;
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_COMBINE_TRANSFORM, "Combine Transform", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.gather_link_search_ops = search_link_ops;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_combine_transform_cc

@ -0,0 +1,45 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_matrix.hh"
#include "NOD_socket_search_link.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_invert_matrix_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Matrix>("Matrix");
b.add_output<decl::Matrix>("Matrix");
}
static void search_link_ops(GatherLinkSearchOpParams &params)
{
if (U.experimental.use_new_matrix_socket) {
nodes::search_link_ops_for_basic_node(params);
}
}
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static auto fn = mf::build::SI1_SO<float4x4, float4x4>(
"Invert Matrix", [](float4x4 matrix) { return math::invert(matrix); });
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_INVERT_MATRIX, "Invert Matrix", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.gather_link_search_ops = search_link_ops;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_invert_matrix_cc

@ -0,0 +1,46 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_matrix.hh"
#include "NOD_socket_search_link.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_matrix_multiply_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Matrix>("Matrix");
b.add_input<decl::Matrix>("Matrix", "Matrix_001");
b.add_output<decl::Matrix>("Matrix");
}
static void search_link_ops(GatherLinkSearchOpParams &params)
{
if (U.experimental.use_new_matrix_socket) {
nodes::search_link_ops_for_basic_node(params);
}
}
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static auto fn = mf::build::SI2_SO<float4x4, float4x4, float4x4>(
"Multiply Matrices", [](float4x4 a, float4x4 b) { return a * b; });
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_MATRIX_MULTIPLY, "Multiply Matrices", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.gather_link_search_ops = search_link_ops;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_matrix_multiply_cc

@ -0,0 +1,92 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_matrix.hh"
#include "BLI_math_rotation.hh"
#include "NOD_socket_search_link.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_separate_transform_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Matrix>("Transform");
b.add_output<decl::Vector>("Location").subtype(PROP_TRANSLATION);
b.add_output<decl::Rotation>("Rotation");
b.add_output<decl::Vector>("Scale").subtype(PROP_XYZ);
};
static void search_link_ops(GatherLinkSearchOpParams &params)
{
if (U.experimental.use_new_matrix_socket) {
nodes::search_link_ops_for_basic_node(params);
}
}
class SeparateTransformFunction : public mf::MultiFunction {
public:
SeparateTransformFunction()
{
static const mf::Signature signature = []() {
mf::Signature signature;
mf::SignatureBuilder builder{"Separate Transform", signature};
builder.single_input<float4x4>("Transform");
builder.single_output<float3>("Location", mf::ParamFlag::SupportsUnusedOutput);
builder.single_output<math::Quaternion>("Rotation", mf::ParamFlag::SupportsUnusedOutput);
builder.single_output<float3>("Scale", mf::ParamFlag::SupportsUnusedOutput);
return signature;
}();
this->set_signature(&signature);
}
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
{
const VArraySpan transforms = params.readonly_single_input<float4x4>(0, "Transform");
MutableSpan location = params.uninitialized_single_output_if_required<float3>(1, "Location");
MutableSpan rotation = params.uninitialized_single_output_if_required<math::Quaternion>(
2, "Rotation");
MutableSpan scale = params.uninitialized_single_output_if_required<float3>(3, "Scale");
if (!location.is_empty()) {
mask.foreach_index_optimized<int64_t>(
[&](const int64_t i) { location[i] = transforms[i].location(); });
}
if (rotation.is_empty() && !scale.is_empty()) {
mask.foreach_index([&](const int64_t i) { location[i] = math::to_scale(transforms[i]); });
}
else if (!rotation.is_empty() && scale.is_empty()) {
mask.foreach_index(
[&](const int64_t i) { rotation[i] = math::to_quaternion(transforms[i]); });
}
else if (!rotation.is_empty() && !scale.is_empty()) {
mask.foreach_index([&](const int64_t i) {
math::to_rot_scale(float3x3(transforms[i]), rotation[i], scale[i]);
});
}
}
};
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static SeparateTransformFunction fn;
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(
&ntype, FN_NODE_SEPARATE_TRANSFORM, "Separate Transform", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.gather_link_search_ops = search_link_ops;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_separate_transform_cc

@ -0,0 +1,49 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_matrix.hh"
#include "NOD_socket_search_link.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_transform_direction_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Vector>("Direction").subtype(PROP_XYZ);
b.add_input<decl::Matrix>("Transform");
b.add_output<decl::Vector>("Direction").subtype(PROP_XYZ);
}
static void search_link_ops(GatherLinkSearchOpParams &params)
{
if (U.experimental.use_new_matrix_socket) {
nodes::search_link_ops_for_basic_node(params);
}
}
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static auto fn = mf::build::SI2_SO<float3, float4x4, float3>(
"Transform Direction", [](float3 direction, float4x4 matrix) {
return math::transform_direction(matrix, direction);
});
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(
&ntype, FN_NODE_TRANSFORM_DIRECTION, "Transform Direction", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.gather_link_search_ops = search_link_ops;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_transform_direction_cc

@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_matrix.hh"
#include "NOD_socket_search_link.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_transform_point_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Vector>("Vector").subtype(PROP_XYZ);
b.add_input<decl::Matrix>("Transform");
b.add_output<decl::Vector>("Vector").subtype(PROP_XYZ);
}
static void search_link_ops(GatherLinkSearchOpParams &params)
{
if (U.experimental.use_new_matrix_socket) {
nodes::search_link_ops_for_basic_node(params);
}
}
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static auto fn = mf::build::SI2_SO<float3, float4x4, float3>(
"Transform Point",
[](float3 point, float4x4 matrix) { return math::transform_point(matrix, point); });
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_TRANSFORM_POINT, "Transform Point", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.gather_link_search_ops = search_link_ops;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_transform_point_cc

@ -0,0 +1,45 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_matrix.hh"
#include "NOD_socket_search_link.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_transpose_matrix_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Matrix>("Matrix");
b.add_output<decl::Matrix>("Matrix");
}
static void search_link_ops(GatherLinkSearchOpParams &params)
{
if (U.experimental.use_new_matrix_socket) {
nodes::search_link_ops_for_basic_node(params);
}
}
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static auto fn = mf::build::SI1_SO<float4x4, float4x4>(
"Transpose Matrix", [](float4x4 matrix) { return math::transpose(matrix); });
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_TRANSPOSE_MATRIX, "Transpose Matrix", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.gather_link_search_ops = search_link_ops;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_transpose_matrix_cc

@ -96,6 +96,15 @@ static bool geometry_node_tree_validate_link(eNodeSocketDatatype type_a,
/* Floats and vectors implicitly convert to rotations. */
return true;
}
/* Support implicit conversions between matrices and rotations. */
if (type_a == SOCK_MATRIX && type_b == SOCK_ROTATION) {
return true;
}
if (type_a == SOCK_ROTATION && type_b == SOCK_MATRIX) {
return true;
}
if (type_a == SOCK_ROTATION && type_b == SOCK_VECTOR) {
/* Rotations implicitly convert to vectors. */
return true;
@ -106,12 +115,16 @@ static bool geometry_node_tree_validate_link(eNodeSocketDatatype type_a,
static bool geometry_node_tree_socket_type_valid(bNodeTreeType * /*treetype*/,
bNodeSocketType *socket_type)
{
if (socket_type->type == SOCK_MATRIX) {
return U.experimental.use_new_matrix_socket;
}
return blender::bke::nodeIsStaticSocketType(socket_type) && ELEM(socket_type->type,
SOCK_FLOAT,
SOCK_VECTOR,
SOCK_RGBA,
SOCK_BOOLEAN,
SOCK_ROTATION,
SOCK_MATRIX,
SOCK_INT,
SOCK_STRING,
SOCK_OBJECT,

@ -50,6 +50,9 @@ const EnumPropertyItem *attribute_type_type_with_socket_fn(bContext * /*C*/,
bool generic_attribute_type_supported(const EnumPropertyItem &item)
{
if (item.value == SOCK_MATRIX) {
return U.experimental.use_new_matrix_socket;
}
return ELEM(item.value,
CD_PROP_FLOAT,
CD_PROP_FLOAT2,
@ -58,7 +61,8 @@ bool generic_attribute_type_supported(const EnumPropertyItem &item)
CD_PROP_BOOL,
CD_PROP_INT32,
CD_PROP_BYTE_COLOR,
CD_PROP_QUATERNION);
CD_PROP_QUATERNION,
CD_PROP_FLOAT4X4);
}
const EnumPropertyItem *domain_experimental_grease_pencil_version3_fn(bContext * /*C*/,

@ -88,6 +88,10 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
/* Don't implement quaternion blurring for now. */
return;
}
if (fixed_data_type == CD_PROP_FLOAT4X4) {
/* Don't implement matrix blurring for now. */
return;
}
if (fixed_data_type == CD_PROP_BOOL) {
/* This node does not support boolean sockets, use integer instead. */
fixed_data_type = CD_PROP_INT32;

@ -290,11 +290,15 @@ static void node_rna(StructRNA *srna)
*r_free = true;
return enum_items_filter(rna_enum_node_socket_data_type_items,
[](const EnumPropertyItem &item) -> bool {
if (item.value == SOCK_MATRIX) {
return U.experimental.use_new_matrix_socket;
}
return ELEM(item.value,
SOCK_FLOAT,
SOCK_INT,
SOCK_BOOLEAN,
SOCK_ROTATION,
SOCK_MATRIX,
SOCK_VECTOR,
SOCK_STRING,
SOCK_RGBA,

@ -427,9 +427,9 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
Span<NodeSimulationItem> simulation_items_;
int skip_input_index_;
/**
* Start index of the simulation state inputs that are used when the simulation is skipped. Those
* inputs are linked directly to the simulation input node. Those inputs only exist internally,
* but not in the UI.
* Start index of the simulation state inputs that are used when the simulation is skipped.
* Those inputs are linked directly to the simulation input node. Those inputs only exist
* internally, but not in the UI.
*/
int skip_inputs_offset_;
/**
@ -648,8 +648,8 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
}
const bool skip = skip_variant->get<bool>();
/* Instead of outputting the values directly, convert them to a bake state and then back. This
* ensures that some geometry processing happens on the data consistently (e.g. removing
/* Instead of outputting the values directly, convert them to a bake state and then back.
* This ensures that some geometry processing happens on the data consistently (e.g. removing
* anonymous attributes). */
std::optional<bke::bake::BakeState> bake_state = this->get_bake_state_from_inputs(
params, data_block_map, skip);
@ -945,7 +945,8 @@ void mix_baked_data_item(const eNodeSocketDatatype socket_type,
case SOCK_INT:
case SOCK_BOOLEAN:
case SOCK_ROTATION:
case SOCK_RGBA: {
case SOCK_RGBA:
case SOCK_MATRIX: {
const CPPType &type = node_geo_simulation_cc::get_simulation_item_cpp_type(socket_type);
SocketValueVariant prev_value_variant = *static_cast<const SocketValueVariant *>(prev);
SocketValueVariant next_value_variant = *static_cast<const SocketValueVariant *>(next);

@ -222,11 +222,15 @@ static void node_rna(StructRNA *srna)
*r_free = true;
return enum_items_filter(rna_enum_node_socket_data_type_items,
[](const EnumPropertyItem &item) -> bool {
if (item.value == SOCK_MATRIX) {
return U.experimental.use_new_matrix_socket;
}
return ELEM(item.value,
SOCK_FLOAT,
SOCK_INT,
SOCK_BOOLEAN,
SOCK_ROTATION,
SOCK_MATRIX,
SOCK_VECTOR,
SOCK_STRING,
SOCK_RGBA,

@ -93,7 +93,6 @@ static bool node_needs_own_transform_relation(const bNode &node)
node.storage);
return storage.transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE;
}
if (node.type == GEO_NODE_SELF_OBJECT) {
return true;
}
@ -346,6 +345,7 @@ std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
socket.socket_data);
return bke::idprop::create(identifier, reinterpret_cast<ID *>(value->value));
}
case SOCK_MATRIX:
case SOCK_CUSTOM:
case SOCK_GEOMETRY:
case SOCK_SHADER:
@ -387,6 +387,7 @@ bool id_property_type_matches_socket(const bNodeTreeInterfaceSocket &socket,
case SOCK_MATERIAL:
return property.type == IDP_ID;
case SOCK_CUSTOM:
case SOCK_MATRIX:
case SOCK_GEOMETRY:
case SOCK_SHADER:
return false;
@ -674,12 +675,15 @@ static Vector<OutputAttributeToStore> compute_attributes_to_store(
for (const OutputAttributeInfo &output_info : outputs_info) {
const CPPType &type = output_info.field.cpp_type();
const bke::AttributeValidator validator = attributes.lookup_validator(output_info.name);
OutputAttributeToStore store{
component_type,
domain,
output_info.name,
GMutableSpan{
type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size}};
type,
MEM_mallocN_aligned(type.size() * domain_size, type.alignment(), __func__),
domain_size}};
fn::GField field = validator.validate_field_if_necessary(output_info.field);
field_evaluator.add_with_destination(std::move(field), store.data);
attributes_to_store.append(store);
@ -868,7 +872,7 @@ void update_input_properties_from_node_tree(const bNodeTree &tree,
if (new_prop == nullptr) {
/* Out of the set of supported input sockets, only
* geometry sockets aren't added to the modifier. */
BLI_assert(socket_type == SOCK_GEOMETRY);
BLI_assert(ELEM(socket_type, SOCK_GEOMETRY, SOCK_MATRIX));
continue;
}

@ -245,6 +245,11 @@ static SocketDeclarationPtr declaration_for_interface_socket(
dst = std::move(decl);
break;
}
case SOCK_MATRIX: {
std::unique_ptr<decl::Matrix> decl = std::make_unique<decl::Matrix>();
dst = std::move(decl);
break;
}
case SOCK_INT: {
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueInt>(io_socket);
std::unique_ptr<decl::Int> decl = std::make_unique<decl::Int>();

@ -414,6 +414,8 @@ std::unique_ptr<SocketDeclaration> make_declaration_for_socket_type(
return std::make_unique<decl::Bool>();
case SOCK_ROTATION:
return std::make_unique<decl::Rotation>();
case SOCK_MATRIX:
return std::make_unique<decl::Matrix>();
case SOCK_INT:
return std::make_unique<decl::Int>();
case SOCK_STRING:
@ -449,6 +451,8 @@ BaseSocketDeclarationBuilder &NodeDeclarationBuilder::add_input(
return this->add_input<decl::Bool>(name, identifier);
case SOCK_ROTATION:
return this->add_input<decl::Rotation>(name, identifier);
case SOCK_MATRIX:
return this->add_input<decl::Matrix>(name, identifier);
case SOCK_INT:
return this->add_input<decl::Int>(name, identifier);
case SOCK_STRING:
@ -492,6 +496,8 @@ BaseSocketDeclarationBuilder &NodeDeclarationBuilder::add_output(
return this->add_output<decl::Bool>(name, identifier);
case SOCK_ROTATION:
return this->add_output<decl::Rotation>(name, identifier);
case SOCK_MATRIX:
return this->add_output<decl::Matrix>(name, identifier);
case SOCK_INT:
return this->add_output<decl::Int>(name, identifier);
case SOCK_STRING:

@ -13,6 +13,8 @@
#include "BLI_color.hh"
#include "BLI_listbase.h"
#include "BLI_math_euler.hh"
#include "BLI_math_matrix.h"
#include "BLI_math_matrix.hh"
#include "BLI_math_quaternion_types.hh"
#include "BLI_math_vector.h"
#include "BLI_math_vector_types.hh"
@ -298,6 +300,9 @@ static std::optional<eNodeSocketDatatype> decl_to_data_type(const SocketDeclarat
else if (dynamic_cast<const decl::Rotation *>(&socket_decl)) {
return SOCK_ROTATION;
}
else if (dynamic_cast<const decl::Matrix *>(&socket_decl)) {
return SOCK_MATRIX;
}
else if (dynamic_cast<const decl::String *>(&socket_decl)) {
return SOCK_STRING;
}
@ -554,7 +559,8 @@ bool socket_type_supports_fields(const eNodeSocketDatatype socket_type)
SOCK_BOOLEAN,
SOCK_INT,
SOCK_ROTATION,
SOCK_MENU);
SOCK_MENU,
SOCK_MATRIX);
}
} // namespace blender::nodes
@ -699,6 +705,7 @@ void node_socket_init_default_value_data(eNodeSocketDatatype datatype, int subty
case SOCK_CUSTOM:
case SOCK_GEOMETRY:
case SOCK_MATRIX:
case SOCK_SHADER:
break;
}
@ -797,6 +804,7 @@ void node_socket_copy_default_value_data(eNodeSocketDatatype datatype, void *to,
case SOCK_CUSTOM:
case SOCK_GEOMETRY:
case SOCK_MATRIX:
case SOCK_SHADER:
break;
}
@ -978,6 +986,22 @@ static bNodeSocketType *make_socket_type_rotation()
return socktype;
}
static bNodeSocketType *make_socket_type_matrix()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATRIX, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<float4x4>();
socktype->get_base_cpp_value = [](const void * /*socket_value*/, void *r_value) {
*static_cast<float4x4 *>(r_value) = float4x4::identity();
};
socktype->geometry_nodes_cpp_type = &blender::CPPType::get<SocketValueVariant>();
socktype->get_geometry_nodes_cpp_value = [](const void * /*socket_value*/, void *r_value) {
new (r_value) SocketValueVariant(float4x4::identity());
};
static SocketValueVariant default_value{float4x4::identity()};
socktype->geometry_nodes_default_cpp_value = &default_value;
return socktype;
}
static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype);
@ -1172,6 +1196,7 @@ void register_standard_node_socket_types()
nodeRegisterSocketType(make_socket_type_bool());
nodeRegisterSocketType(make_socket_type_rotation());
nodeRegisterSocketType(make_socket_type_matrix());
nodeRegisterSocketType(make_socket_type_vector(PROP_NONE));
nodeRegisterSocketType(make_socket_type_vector(PROP_TRANSLATION));

@ -417,9 +417,9 @@ bool Rotation::can_connect(const bNodeSocket &socket) const
return false;
}
if (this->in_out == SOCK_IN) {
return ELEM(socket.type, SOCK_ROTATION, SOCK_FLOAT, SOCK_VECTOR);
return ELEM(socket.type, SOCK_ROTATION, SOCK_FLOAT, SOCK_VECTOR, SOCK_MATRIX);
}
return ELEM(socket.type, SOCK_ROTATION, SOCK_VECTOR);
return ELEM(socket.type, SOCK_ROTATION, SOCK_VECTOR, SOCK_MATRIX);
}
bNodeSocket &Rotation::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const
@ -434,6 +434,57 @@ bNodeSocket &Rotation::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocke
/** \} */
/* -------------------------------------------------------------------- */
/** \name #Matrix
* \{ */
bNodeSocket &Matrix::build(bNodeTree &ntree, bNode &node) const
{
bNodeSocket &socket = *nodeAddStaticSocket(&ntree,
&node,
this->in_out,
SOCK_MATRIX,
PROP_NONE,
this->identifier.c_str(),
this->name.c_str());
this->set_common_flags(socket);
return socket;
}
bool Matrix::matches(const bNodeSocket &socket) const
{
if (!this->matches_common_data(socket)) {
return false;
}
if (socket.type != SOCK_MATRIX) {
return false;
}
return true;
}
bool Matrix::can_connect(const bNodeSocket &socket) const
{
if (!sockets_can_connect(*this, socket)) {
return false;
}
if (this->in_out == SOCK_IN) {
return ELEM(socket.type, SOCK_MATRIX, SOCK_FLOAT, SOCK_VECTOR, SOCK_MATRIX);
}
return ELEM(socket.type, SOCK_MATRIX, SOCK_VECTOR, SOCK_MATRIX);
}
bNodeSocket &Matrix::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const
{
if (socket.type != SOCK_MATRIX) {
BLI_assert(socket.in_out == this->in_out);
return this->build(ntree, node);
}
this->set_common_flags(socket);
return socket;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name #String
* \{ */