Geometry Node: Multi-input socket tooltip #104468

Merged
Jacques Lucke merged 33 commits from mod_moder/blender:multi_input_tooltip into main 2024-04-22 19:49:08 +02:00
19 changed files with 152 additions and 130 deletions
Showing only changes of commit 58a75cd9ef - Show all commits

View File

@ -603,7 +603,10 @@ void ANIM_armature_bonecoll_name_set(bArmature *armature, BoneCollection *bcoll,
bonecoll_ensure_name_unique(armature, bcoll);
/* Bone collections can be reached via .collections (4.0+) and .collections_all (4.1+).
* Animation data from 4.0 should have been versioned to only use `.collections_all`. */
BKE_animdata_fix_paths_rename_all(&armature->id, "collections", old_name, bcoll->name);
BKE_animdata_fix_paths_rename_all(&armature->id, "collections_all", old_name, bcoll->name);
}
void ANIM_armature_bonecoll_remove_from_index(bArmature *armature, int index)

View File

@ -98,18 +98,19 @@ void BKE_gpencil_cache_data_init(Depsgraph *depsgraph, Object *ob)
}
if (mmd->cache_data) {
BKE_shrinkwrap_free_tree(mmd->cache_data);
MEM_SAFE_FREE(mmd->cache_data);
MEM_delete(mmd->cache_data);
mmd->cache_data = nullptr;
}
Object *ob_target = DEG_get_evaluated_object(depsgraph, ob);
Mesh *target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target);
mmd->cache_data = static_cast<ShrinkwrapTreeData *>(
MEM_callocN(sizeof(ShrinkwrapTreeData), __func__));
mmd->cache_data = MEM_new<ShrinkwrapTreeData>(__func__);
if (BKE_shrinkwrap_init_tree(
mmd->cache_data, target, mmd->shrink_type, mmd->shrink_mode, false))
{
}
else {
MEM_SAFE_FREE(mmd->cache_data);
MEM_delete(mmd->cache_data);
mmd->cache_data = nullptr;
}
break;
}
@ -136,7 +137,8 @@ void BKE_gpencil_cache_data_clear(Object *ob)
ShrinkwrapGpencilModifierData *mmd = (ShrinkwrapGpencilModifierData *)md;
if ((mmd) && (mmd->cache_data)) {
BKE_shrinkwrap_free_tree(mmd->cache_data);
MEM_SAFE_FREE(mmd->cache_data);
MEM_delete(mmd->cache_data);
mmd->cache_data = nullptr;
}
break;
}

View File

@ -276,12 +276,6 @@ static void compositor_engine_draw(void *data)
* the GPU for extended periods of time and sub-optimally schedule work for execution. */
GPU_flush();
}
else {
/* Realtime Compositor is not supported on macOS with the OpenGL backend. */
blender::StringRef("Viewport compositor is only supported on MacOS with the Metal Backend.")
.copy(compositor_data->info, GPU_INFO_SIZE);
return;
}
#endif
/* Execute Compositor render commands. */

View File

@ -3259,14 +3259,6 @@ void DRW_gpu_context_enable_ex(bool /*restore*/)
void DRW_gpu_context_disable_ex(bool restore)
{
if (DST.system_gpu_context != nullptr) {
#ifdef __APPLE__
/* Need to flush before disabling draw context, otherwise it does not
* always finish drawing and viewport can be empty or partially drawn */
if (GPU_type_matches_ex(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL)) {
GPU_flush();
}
#endif
if (BLI_thread_is_main() && restore) {
wm_window_reset_drawable();
}

View File

@ -1105,20 +1105,6 @@ void UI_widgetbase_draw_cache_end()
GPU_blend(GPU_BLEND_NONE);
}
/* Disable cached/instanced drawing and enforce single widget drawing pipeline.
* Works around interface artifacts happening on certain driver and hardware
* configurations. */
static bool draw_widgetbase_batch_skip_draw_cache()
{
/* MacOS is known to have issues on Mac Mini and MacBook Pro with Intel Iris GPU.
* For example, #78307. */
if (GPU_type_matches_ex(GPU_DEVICE_INTEL, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL)) {
return true;
}
return false;
}
static void draw_widgetbase_batch(uiWidgetBase *wtb)
{
wtb->uniform_params.tria_type = wtb->tria1.type;
@ -1127,7 +1113,7 @@ static void draw_widgetbase_batch(uiWidgetBase *wtb)
copy_v2_v2(wtb->uniform_params.tria1_center, wtb->tria1.center);
copy_v2_v2(wtb->uniform_params.tria2_center, wtb->tria2.center);
if (g_widget_base_batch.enabled && !draw_widgetbase_batch_skip_draw_cache()) {
if (g_widget_base_batch.enabled) {
g_widget_base_batch.params[g_widget_base_batch.count] = wtb->uniform_params;
g_widget_base_batch.count++;

View File

@ -284,16 +284,6 @@ void immDrawPixelsTexTiled_scaling_clipping(IMMDrawPixelsTexState *state,
immAttr2f(texco, left / float(tex_w), top / float(tex_h));
immVertex2f(pos, rast_x + offset_left * xzoom, rast_y + top * yzoom * scaleY);
immEnd();
/* NOTE: Weirdly enough this is only required on macOS. Without this there is some sort of
* bleeding of data is happening from tiles which are drawn later on.
* This doesn't seem to be too slow,
* but still would be nice to have fast and nice solution. */
#ifdef __APPLE__
if (GPU_type_matches_ex(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL)) {
GPU_flush();
}
#endif
}
}

View File

@ -105,12 +105,11 @@ static EnumPropertyItem solver_modes[] = {
struct TrimOperation {
gesture::Operation op;
/* Operation-generated geometry. */
Mesh *mesh;
float (*true_mesh_co)[3];
float depth_front;
float depth_back;
/* Operator properties. */
bool use_cursor_depth;
OperationType mode;
@ -119,6 +118,7 @@ struct TrimOperation {
ExtrudeMode extrude_mode;
};
/* Recalculate the mesh normals for the generated trim mesh. */
static void update_normals(gesture::GestureData &gesture_data)
{
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
@ -151,8 +151,8 @@ static void update_normals(gesture::GestureData &gesture_data)
trim_operation->mesh = result;
}
/* Get the origin and normal that are going to be used for calculating the depth and position the
* trimming geometry. */
/* Get the origin and normal that are going to be used for calculating the depth and position of
* the trimming geometry. */
static void get_origin_and_normal(gesture::GestureData &gesture_data,
float *r_origin,
float *r_normal)
@ -182,7 +182,10 @@ static void get_origin_and_normal(gesture::GestureData &gesture_data,
}
}
static void calculate_depth(gesture::GestureData &gesture_data)
/* Calculates the depth of the drawn shape inside the scene.*/
static void calculate_depth(gesture::GestureData &gesture_data,
float &r_depth_front,
float &r_depth_back)
{
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
@ -197,8 +200,8 @@ static void calculate_depth(gesture::GestureData &gesture_data)
get_origin_and_normal(gesture_data, shape_origin, shape_normal);
plane_from_point_normal_v3(shape_plane, shape_origin, shape_normal);
trim_operation->depth_front = FLT_MAX;
trim_operation->depth_back = -FLT_MAX;
float depth_front = FLT_MAX;
float depth_back = -FLT_MAX;
for (int i = 0; i < totvert; i++) {
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
@ -210,8 +213,8 @@ static void calculate_depth(gesture::GestureData &gesture_data)
float world_space_vco[3];
mul_v3_m4v3(world_space_vco, vc->obact->object_to_world().ptr(), vco);
const float dist = dist_signed_to_plane_v3(world_space_vco, shape_plane);
trim_operation->depth_front = min_ff(dist, trim_operation->depth_front);
trim_operation->depth_back = max_ff(dist, trim_operation->depth_back);
depth_front = min_ff(dist, depth_front);
depth_back = max_ff(dist, depth_back);
}
if (trim_operation->use_cursor_depth) {
@ -225,15 +228,13 @@ static void calculate_depth(gesture::GestureData &gesture_data)
mid_point_depth = ss->gesture_initial_hit ?
dist_signed_to_plane_v3(world_space_gesture_initial_location,
shape_plane) :
(trim_operation->depth_back + trim_operation->depth_front) * 0.5f;
(depth_back + depth_front) * 0.5f;
}
else {
/* When using normal orientation, if the stroke started over the mesh, position the mid point
* at 0 distance from the shape plane. This positions the trimming shape half inside of the
* surface. */
mid_point_depth = ss->gesture_initial_hit ?
0.0f :
(trim_operation->depth_back + trim_operation->depth_front) * 0.5f;
mid_point_depth = ss->gesture_initial_hit ? 0.0f : (depth_back + depth_front) * 0.5f;
}
float depth_radius;
@ -260,9 +261,12 @@ static void calculate_depth(gesture::GestureData &gesture_data)
}
}
trim_operation->depth_front = mid_point_depth - depth_radius;
trim_operation->depth_back = mid_point_depth + depth_radius;
depth_front = mid_point_depth - depth_radius;
depth_back = mid_point_depth + depth_radius;
}
r_depth_front = depth_front;
r_depth_back = depth_back;
}
static void generate_geometry(gesture::GestureData &gesture_data)
@ -281,20 +285,6 @@ static void generate_geometry(gesture::GestureData &gesture_data)
trim_operation->true_mesh_co = static_cast<float(*)[3]>(
MEM_malloc_arrayN(trim_totverts, sizeof(float[3]), "mesh orco"));
float depth_front = trim_operation->depth_front;
float depth_back = trim_operation->depth_back;
float pad_factor = 0.0f;
if (!trim_operation->use_cursor_depth) {
pad_factor = (depth_back - depth_front) * 0.01f + 0.001f;
/* When using cursor depth, don't modify the depth set by the cursor radius. If full depth is
* used, adding a little padding to the trimming shape can help avoiding booleans with coplanar
* faces. */
depth_front -= pad_factor;
depth_back += pad_factor;
}
float shape_origin[3];
float shape_normal[3];
float shape_plane[4];
@ -306,6 +296,20 @@ static void generate_geometry(gesture::GestureData &gesture_data)
/* Write vertices coordinates OperationType::Difference for the front face. */
MutableSpan<float3> positions = trim_operation->mesh->vert_positions_for_write();
float depth_front;
float depth_back;
calculate_depth(gesture_data, depth_front, depth_back);
if (!trim_operation->use_cursor_depth) {
float pad_factor = (depth_back - depth_front) * 0.01f + 0.001f;
/* When using cursor depth, don't modify the depth set by the cursor radius. If full depth is
* used, adding a little padding to the trimming shape can help avoiding booleans with coplanar
* faces. */
depth_front -= pad_factor;
depth_back += pad_factor;
}
float depth_point[3];
/* Get origin point for OrientationType::View.
@ -436,11 +440,16 @@ static void generate_geometry(gesture::GestureData &gesture_data)
update_normals(gesture_data);
}
static void free_geometry(gesture::GestureData &gesture_data)
static void gesture_begin(bContext &C, gesture::GestureData &gesture_data)
{
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
BKE_id_free(nullptr, trim_operation->mesh);
MEM_freeN(trim_operation->true_mesh_co);
Object *object = gesture_data.vc.obact;
SculptSession *ss = object->sculpt;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(&C);
generate_geometry(gesture_data);
SCULPT_topology_islands_invalidate(ss);
BKE_sculpt_update_object_for_edit(depsgraph, gesture_data.vc.obact, false);
undo::push_node(gesture_data.vc.obact, nullptr, undo::Type::Geometry);
}
static int bm_face_isect_pair(BMFace *f, void * /*user_data*/)
@ -545,19 +554,6 @@ static void apply_trim(gesture::GestureData &gesture_data)
result, static_cast<Mesh *>(gesture_data.vc.obact->data), gesture_data.vc.obact);
}
static void gesture_begin(bContext &C, gesture::GestureData &gesture_data)
{
Object *object = gesture_data.vc.obact;
SculptSession *ss = object->sculpt;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(&C);
calculate_depth(gesture_data);
generate_geometry(gesture_data);
SCULPT_topology_islands_invalidate(ss);
BKE_sculpt_update_object_for_edit(depsgraph, gesture_data.vc.obact, false);
undo::push_node(gesture_data.vc.obact, nullptr, undo::Type::Geometry);
}
static void gesture_apply_for_symmetry_pass(bContext & /*C*/, gesture::GestureData &gesture_data)
{
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
@ -570,6 +566,13 @@ static void gesture_apply_for_symmetry_pass(bContext & /*C*/, gesture::GestureDa
apply_trim(gesture_data);
}
static void free_geometry(gesture::GestureData &gesture_data)
{
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
BKE_id_free(nullptr, trim_operation->mesh);
MEM_freeN(trim_operation->true_mesh_co);
}
static void gesture_end(bContext & /*C*/, gesture::GestureData &gesture_data)
{
Object *object = gesture_data.vc.obact;

View File

@ -1385,7 +1385,8 @@ static void create_inspection_string_for_generic_value(const bNodeSocket &socket
((*static_cast<bool *>(socket_value)) ? TIP_("True") : TIP_("False")));
}
else if (socket_type.is<float4x4>()) {
const float4x4 &value = *static_cast<const float4x4 *>(socket_value);
/* Transpose to be able to print row by row. */
const float4x4 value = math::transpose(*static_cast<const float4x4 *>(socket_value));
ss << value[0] << ",\n";
ss << value[1] << ",\n";
ss << value[2] << ",\n";

View File

@ -7,6 +7,7 @@
#include <fmt/format.h>
#include "BLI_math_matrix.hh"
#include "BLI_math_quaternion_types.hh"
#include "BLI_math_vector_types.hh"
@ -395,7 +396,8 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
UI_but_func_tooltip_set(
but,
[](bContext * /*C*/, void *argN, const char * /*tip*/) {
const float4x4 &value = *static_cast<const float4x4 *>(argN);
/* Transpose to be able to print row by row. */
const float4x4 value = math::transpose(*static_cast<const float4x4 *>(argN));
std::stringstream ss;
ss << value[0] << ",\n";
ss << value[1] << ",\n";

View File

@ -238,6 +238,10 @@ ReverseUVSampler::Result ReverseUVSampler::sample(const float2 &query_uv) const
* the lookup can fail for floating point accuracy reasons when the uv is almost exact on an
* edge. */
const float edge_epsilon = 0.00001f;
/* If uv triangles are very small, it may look like the query hits multiple triangles due to
* floating point precision issues. Better just pick one of the triangles instead of failing the
* entire operation in this case. */
const float area_epsilon = 0.00001f;
for (const int tri_i : tri_indices) {
const int3 &tri = corner_tris_[tri_i];
@ -260,8 +264,14 @@ ReverseUVSampler::Result ReverseUVSampler::sample(const float2 &query_uv) const
const float worse_dist = std::max(dist, best_dist);
/* Allow ignoring multiple triangle intersections if the uv is almost exactly on an edge. */
if (worse_dist < -edge_epsilon) {
/* The uv sample is in multiple triangles. */
return Result{ResultType::Multiple};
const int3 &best_tri = corner_tris_[tri_i];
const float best_tri_area = area_tri_v2(
uv_map_[best_tri[0]], uv_map_[best_tri[1]], uv_map_[best_tri[2]]);
const float current_tri_area = area_tri_v2(uv_0, uv_1, uv_2);
if (best_tri_area > area_epsilon && current_tri_area > area_epsilon) {
/* The uv sample is in multiple triangles. */
return Result{ResultType::Multiple};
}
}
}

View File

@ -139,12 +139,12 @@ static void bake_modifier(Main * /*bmain*/,
/* Recalculate shrinkwrap data. */
if (mmd->cache_data) {
BKE_shrinkwrap_free_tree(mmd->cache_data);
MEM_SAFE_FREE(mmd->cache_data);
MEM_delete(mmd->cache_data);
mmd->cache_data = nullptr;
}
Object *ob_target = DEG_get_evaluated_object(depsgraph, mmd->target);
Mesh *target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target);
mmd->cache_data = static_cast<ShrinkwrapTreeData *>(
MEM_callocN(sizeof(ShrinkwrapTreeData), __func__));
mmd->cache_data = MEM_new<ShrinkwrapTreeData>(__func__);
if (BKE_shrinkwrap_init_tree(
mmd->cache_data, target, mmd->shrink_type, mmd->shrink_mode, false))
{
@ -157,7 +157,8 @@ static void bake_modifier(Main * /*bmain*/,
/* Free data. */
if (mmd->cache_data) {
BKE_shrinkwrap_free_tree(mmd->cache_data);
MEM_SAFE_FREE(mmd->cache_data);
MEM_delete(mmd->cache_data);
mmd->cache_data = nullptr;
}
}
}
@ -172,7 +173,7 @@ static void free_data(GpencilModifierData *md)
ShrinkwrapGpencilModifierData *mmd = (ShrinkwrapGpencilModifierData *)md;
if (mmd->cache_data) {
BKE_shrinkwrap_free_tree(mmd->cache_data);
MEM_SAFE_FREE(mmd->cache_data);
MEM_delete(mmd->cache_data);
}
}

View File

@ -467,7 +467,6 @@ void gpu_shader_create_info_init()
GPU_OS_ANY,
GPU_DRIVER_ANY,
GPU_BACKEND_OPENGL) ||
GPU_type_matches_ex(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL) ||
GPU_crappy_amd_driver())
{
draw_modelmat = draw_modelmat_legacy;

View File

@ -1902,7 +1902,7 @@ void MSLGeneratorInterface::prepare_from_createinfo(const shader::ShaderCreateIn
case shader::ShaderCreateInfo::Resource::BindType::IMAGE: {
/* Flatten qualifier flags into final access state. */
MSLTextureSamplerAccess access;
if (bool(res.image.qualifiers & Qualifier::READ_WRITE)) {
if ((res.image.qualifiers & Qualifier::READ_WRITE) == Qualifier::READ_WRITE) {
access = MSLTextureSamplerAccess::TEXTURE_ACCESS_READWRITE;
}
else if (bool(res.image.qualifiers & Qualifier::WRITE)) {

View File

@ -437,6 +437,12 @@ static const char *load_face_element(PlyReadBuffer &file,
if (count < 1 || count > 255) {
return "Invalid face size, must be between 1 and 255";
}
/* Previous python based importer was accepting faces with fewer
* than 3 vertices, and silently dropping them. */
if (count < 3) {
fprintf(stderr, "PLY Importer: ignoring face %i (%i vertices)\n", i, count);
continue;
}
for (int j = 0; j < count; j++) {
int index;
@ -467,15 +473,22 @@ static const char *load_face_element(PlyReadBuffer &file,
scratch.resize(count * data_type_size[prop.type]);
file.read_bytes(scratch.data(), scratch.size());
ptr = scratch.data();
if (header.type == PlyFormatType::BINARY_BE) {
endian_switch_array((uint8_t *)ptr, data_type_size[prop.type], count);
/* Previous python based importer was accepting faces with fewer
* than 3 vertices, and silently dropping them. */
if (count < 3) {
fprintf(stderr, "PLY Importer: ignoring face %i (%i vertices)\n", i, count);
}
for (int j = 0; j < count; ++j) {
uint32_t index = get_binary_value<uint32_t>(prop.type, ptr);
data->face_vertices.append(index);
else {
ptr = scratch.data();
if (header.type == PlyFormatType::BINARY_BE) {
endian_switch_array((uint8_t *)ptr, data_type_size[prop.type], count);
}
for (int j = 0; j < count; ++j) {
uint32_t index = get_binary_value<uint32_t>(prop.type, ptr);
data->face_vertices.append(index);
}
data->face_sizes.append(count);
}
data->face_sizes.append(count);
/* Skip any properties after vertex indices. */
for (int j = prop_index + 1; j < element.properties.size(); j++) {

View File

@ -909,8 +909,7 @@ void USDMeshReader::read_custom_data(const ImportSettings *settings,
/* To avoid unnecessarily reloading static primvars during animation,
* early out if not first load and this primvar isn't animated. */
const bool is_time_varying = primvar_varying_map_.lookup_default(name, false);
if (!new_mesh && !is_time_varying) {
if (!new_mesh && primvar_varying_map_.contains(name) && !primvar_varying_map_.lookup(name)) {
continue;
}

View File

@ -69,11 +69,6 @@ set(SRC
dna_utils.cc
makesdna.cc
${SRC_BLENLIB}
../../../../intern/guardedalloc/intern/leak_detector.cc
../../../../intern/guardedalloc/intern/mallocn.c
../../../../intern/guardedalloc/intern/mallocn_guarded_impl.c
../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c
../../../../intern/guardedalloc/intern/memory_usage.cc
${dna_header_include_file}
${dna_header_string_file}
)

View File

@ -222,12 +222,6 @@ set(SRC
${DEFSRC}
${APISRC}
../../../../intern/clog/clog.c
../../../../intern/guardedalloc/intern/leak_detector.cc
../../../../intern/guardedalloc/intern/mallocn.c
../../../../intern/guardedalloc/intern/mallocn_guarded_impl.c
../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c
../../../../intern/guardedalloc/intern/memory_usage.cc
# Needed for defaults.
../../../../release/datafiles/userdef/userdef_default.c
../../../../release/datafiles/userdef/userdef_default_theme.c

View File

@ -74,7 +74,7 @@ static void free_data(ModifierData *md)
if (smd->cache_data) {
BKE_shrinkwrap_free_tree(smd->cache_data);
MEM_SAFE_FREE(smd->cache_data);
MEM_delete(smd->cache_data);
}
}
@ -196,17 +196,18 @@ static void ensure_shrinkwrap_cache_data(GreasePencilShrinkwrapModifierData &smd
{
if (smd.cache_data) {
BKE_shrinkwrap_free_tree(smd.cache_data);
MEM_SAFE_FREE(smd.cache_data);
MEM_delete(smd.cache_data);
smd.cache_data = nullptr;
}
Object *target_ob = DEG_get_evaluated_object(ctx.depsgraph, smd.target);
Mesh *target_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(target_ob);
smd.cache_data = static_cast<ShrinkwrapTreeData *>(
MEM_callocN(sizeof(ShrinkwrapTreeData), __func__));
smd.cache_data = MEM_new<ShrinkwrapTreeData>(__func__);
const bool tree_ok = BKE_shrinkwrap_init_tree(
smd.cache_data, target_mesh, smd.shrink_type, smd.shrink_mode, false);
if (!tree_ok) {
MEM_SAFE_FREE(smd.cache_data);
MEM_delete(smd.cache_data);
smd.cache_data = nullptr;
}
}

View File

@ -14,7 +14,9 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Matrix>("Matrix");
b.add_output<decl::Matrix>("Matrix");
b.add_output<decl::Matrix>("Matrix").description(
"The inverted matrix or the identity matrix if the input is not invertable");
b.add_output<decl::Bool>("Invertable").description("True if the input matrix is invertable");
}
static void search_link_ops(GatherLinkSearchOpParams &params)
@ -24,10 +26,45 @@ static void search_link_ops(GatherLinkSearchOpParams &params)
}
}
class InvertMatrixFunction : public mf::MultiFunction {
public:
InvertMatrixFunction()
{
static mf::Signature signature = []() {
mf::Signature signature;
mf::SignatureBuilder builder{"Invert Matrix", signature};
builder.single_input<float4x4>("Matrix");
builder.single_output<float4x4>("Matrix", mf::ParamFlag::SupportsUnusedOutput);
builder.single_output<bool>("Invertable", mf::ParamFlag::SupportsUnusedOutput);
return signature;
}();
this->set_signature(&signature);
}
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
{
const VArraySpan<float4x4> in_matrices = params.readonly_single_input<float4x4>(0, "Matrix");
MutableSpan<float4x4> out_matrices = params.uninitialized_single_output_if_required<float4x4>(
1, "Matrix");
MutableSpan<bool> out_invertable = params.uninitialized_single_output_if_required<bool>(
2, "Invertable");
mask.foreach_index([&](const int64_t i) {
const float4x4 &matrix = in_matrices[i];
bool success;
float4x4 inverted_matrix = math::invert(matrix, success);
if (!out_matrices.is_empty()) {
out_matrices[i] = success ? inverted_matrix : float4x4::identity();
}
if (!out_invertable.is_empty()) {
out_invertable[i] = success;
}
});
}
};
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); });
static InvertMatrixFunction fn;
builder.set_matching_fn(fn);
}