Refactoring: Corrections and unifications in mathematics vfont gizmos #107193

Closed
Iliya Katushenock wants to merge 19 commits from mod_moder/blender:tmp_fix_text_cursor_transform into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
20 changed files with 652 additions and 234 deletions
Showing only changes of commit 42d306f377 - Show all commits

View File

@ -220,6 +220,7 @@ class NODE_MT_geometry_node_GEO_GEOMETRY_SAMPLE(Menu):
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeProximity")
node_add_menu.add_node_type(layout, "GeometryNodeIndexOfNearest")
node_add_menu.add_node_type(layout, "GeometryNodeRaycast")
node_add_menu.add_node_type(layout, "GeometryNodeSampleIndex")
node_add_menu.add_node_type(layout, "GeometryNodeSampleNearest")

View File

@ -1581,6 +1581,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define GEO_NODE_SDF_VOLUME_SPHERE 1196
#define GEO_NODE_MEAN_FILTER_SDF_VOLUME 1197
#define GEO_NODE_OFFSET_SDF_VOLUME 1198
#define GEO_NODE_INDEX_OF_NEAREST 1199
/** \} */

View File

@ -1020,8 +1020,6 @@ static void multires_unsubdivide_extract_grids(MultiresUnsubdivideContext *conte
* so they can be used from #BMesh. */
multires_unsubdivide_add_original_index_datalayers(base_mesh);
const int base_l_layer_index = CustomData_get_named_layer_index(
&base_mesh->ldata, CD_PROP_INT32, lname);
BMesh *bm_base_mesh = get_bmesh_from_mesh(base_mesh);
BMIter iter, iter_a, iter_b;
BMVert *v;
@ -1031,8 +1029,8 @@ static void multires_unsubdivide_extract_grids(MultiresUnsubdivideContext *conte
BM_mesh_elem_table_ensure(bm_base_mesh, BM_FACE);
/* Get the data-layer that contains the loops indices. */
const int base_l_offset = CustomData_get_n_offset(
&bm_base_mesh->ldata, CD_PROP_INT32, base_l_layer_index);
const int base_l_offset = CustomData_get_offset_named(
&bm_base_mesh->ldata, CD_PROP_INT32, lname);
const blender::OffsetIndices polys = base_mesh->polys();
const blender::Span<int> corner_verts = base_mesh->corner_verts();

View File

@ -105,6 +105,23 @@ inline void BLI_kdtree_nd_(range_search_cb_cpp)(const KDTree *tree,
},
const_cast<Fn *>(&fn));
}
template<typename Fn>
inline int BLI_kdtree_nd_(find_nearest_cb_cpp)(const KDTree *tree,
const float co[KD_DIMS],
KDTreeNearest *r_nearest,
Fn &&fn)
{
return BLI_kdtree_nd_(find_nearest_cb)(
tree,
co,
[](void *user_data, const int index, const float *co, const float dist_sq) {
Fn &fn = *static_cast<Fn *>(user_data);
return fn(index, co, dist_sq);
},
&fn,
r_nearest);
}
#endif
#undef _BLI_CONCAT_AUX

View File

@ -151,6 +151,10 @@ short ED_transform_calc_orientation_from_type_ex(const struct Scene *scene,
int pivot_point,
float r_mat[3][3]);
bool ED_transform_calc_pivot_pos(const struct bContext *C,
const short pivot_type,
float r_pivot_pos[3]);
/* transform gizmos */
void VIEW3D_GGT_xform_gizmo(struct wmGizmoGroupType *gzgt);
@ -201,7 +205,8 @@ struct TransformCalcParams {
*/
int ED_transform_calc_gizmo_stats(const struct bContext *C,
const struct TransformCalcParams *params,
struct TransformBounds *tbounds);
struct TransformBounds *tbounds,
struct RegionView3D *rv3d);
/**
* Iterates over all the strips and finds the closest snapping candidate of either \a frame_1 or \a

View File

@ -187,6 +187,7 @@ static SeqRenderData sequencer_thumbnail_context_init(const bContext *C)
SEQ_render_new_render_data(bmain, depsgraph, scene, 0, 0, sseq->render_size, false, &context);
context.view_id = BKE_scene_multiview_view_id_get(&scene->r, STEREO_LEFT_NAME);
context.use_proxies = false;
context.scene = scene;
return context;
}

View File

@ -138,13 +138,15 @@ static void WIDGETGROUP_tool_generic_refresh(const bContext *C, wmGizmoGroup *gz
orientation = V3D_ORIENT_GLOBAL; /* dummy, use view. */
}
RegionView3D *rv3d = CTX_wm_region_data(C);
struct TransformBounds tbounds;
const bool hide = ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
.use_only_center = true,
.orientation_index = orientation + 1,
},
&tbounds) == 0;
&tbounds,
rv3d) == 0;
WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, hide);
if (hide) {

View File

@ -305,7 +305,7 @@ bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
}
else {
/* If there's no selection, `lastofs` is unmodified and last value since static. */
is_set = calculateTransformCenter(C, V3D_AROUND_CENTER_MEDIAN, lastofs, nullptr);
is_set = ED_transform_calc_pivot_pos(C, V3D_AROUND_CENTER_MEDIAN, lastofs);
}
copy_v3_v3(r_dyn_ofs, lastofs);

View File

@ -8,6 +8,8 @@
* Used for 3D View
*/
#include "BLI_function_ref.hh"
#include "DNA_armature_types.h"
#include "DNA_gpencil_legacy_types.h"
#include "DNA_lattice_types.h"
@ -438,19 +440,6 @@ static void calc_tw_center(TransformBounds *tbounds, const float co[3])
}
}
static void calc_tw_center_with_matrix(TransformBounds *tbounds,
const float co[3],
const bool use_matrix,
const float matrix[4][4])
{
float co_world[3];
if (use_matrix) {
mul_v3_m4v3(co_world, matrix, co);
co = co_world;
}
calc_tw_center(tbounds, co);
}
static void protectflag_to_drawflags(short protectflag, short *drawflags)
{
if (protectflag & OB_LOCK_LOCX) {
@ -484,51 +473,51 @@ static void protectflag_to_drawflags(short protectflag, short *drawflags)
}
}
/* for pose mode */
static void protectflag_to_drawflags_pchan(RegionView3D *rv3d,
const bPoseChannel *pchan,
short orientation_index)
{
/* Protect-flags apply to local space in pose mode, so only let them influence axis
* visibility if we show the global orientation, otherwise it's confusing. */
if (ELEM(orientation_index, V3D_ORIENT_LOCAL, V3D_ORIENT_GIMBAL)) {
protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag);
}
}
/* For editmode. */
static void protectflag_to_drawflags_ebone(RegionView3D *rv3d, const EditBone *ebo)
{
if (ebo->flag & BONE_EDITMODE_LOCKED) {
protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag);
}
}
int ED_transform_calc_gizmo_stats(const bContext *C,
const TransformCalcParams *params,
TransformBounds *tbounds)
/**
* Run \a user_fn for each coordinate of elements selected in View3D (vertices, particles...).
* \note Each coordinate has the space matrix of the active object.
*
* \param orient_index: A #TransformOrientationSlot.type. Here used for calculating \a r_drawflags.
* \param use_curve_handles: If true, the handles of curves are traversed.
* \param use_only_center: For objects in object mode, defines whether the corners of the bounds or
* just the center are traversed.
* \param user_fn: Callback that runs on each coordinate.
* \param r_mat: Returns the space matrix of the coordinates.
* \param r_drawflags: Drawing flags for gizmos. Usually stored in #RegionView3D::drawflags.
*/
static int gizmo_3d_foreach_selected(const bContext *C,
const short orient_index,
const bool use_curve_handles,
const bool use_only_center,
blender::FunctionRef<void(const blender::float3 &)> user_fn,
const float (**r_mat)[4],
short *r_drawflags)
{
using namespace blender;
const auto run_coord_with_matrix =
[&](const float co[3], const bool use_matrix, const float matrix[4][4]) {
float co_world[3];
if (use_matrix) {
mul_v3_m4v3(co_world, matrix, co);
co = co_world;
}
user_fn(co);
};
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Scene *scene = CTX_data_scene(C);
/* TODO(sergey): This function is used from operator's modal() and from gizmo's refresh().
* Is it fine to possibly evaluate dependency graph here? */
Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = static_cast<View3D *>(area->spacedata.first);
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
Base *base;
bGPdata *gpd = CTX_data_gpencil_data(C);
const bool is_gp_edit = GPENCIL_ANY_MODE(gpd);
const bool is_curve_edit = GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
int a, totsel = 0;
const int pivot_point = scene->toolsettings->transform_pivot_point;
const short orient_index = params->orientation_index ?
(params->orientation_index - 1) :
BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
BKE_view_layer_synced_ensure(scene, view_layer);
Object *ob = BKE_view_layer_active_object_get(view_layer);
Object *obedit = OBEDIT_FROM_OBACT(ob);
@ -539,42 +528,6 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
}
}
tbounds->use_matrix_space = false;
/* transform widget matrix */
unit_m4(rv3d->twmat);
unit_m3(rv3d->tw_axis_matrix);
zero_v3(rv3d->tw_axis_min);
zero_v3(rv3d->tw_axis_max);
rv3d->twdrawflag = short(0xFFFF);
/* global, local or normal orientation?
* if we could check 'totsel' now, this should be skipped with no selection. */
if (ob) {
float mat[3][3];
ED_transform_calc_orientation_from_type_ex(
scene, view_layer, v3d, rv3d, ob, obedit, orient_index, pivot_point, mat);
copy_m4_m3(rv3d->twmat, mat);
}
/* transform widget centroid/center */
reset_tw_center(tbounds);
copy_m3_m4(tbounds->axis, rv3d->twmat);
if (params->use_local_axis && (ob && ob->mode & (OB_MODE_EDIT | OB_MODE_POSE))) {
float diff_mat[3][3];
copy_m3_m4(diff_mat, ob->object_to_world);
normalize_m3(diff_mat);
invert_m3(diff_mat);
mul_m3_m3m3(tbounds->axis, tbounds->axis, diff_mat);
normalize_m3(tbounds->axis);
tbounds->use_matrix_space = true;
copy_m4_m4(tbounds->matrix_space, ob->object_to_world);
}
if (is_gp_edit) {
float diff_mat[4][4];
const bool use_mat_local = true;
@ -605,7 +558,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
for (uint32_t j = 0; j < 3; j++) {
if (BEZT_ISSEL_IDX(bezt, j)) {
calc_tw_center_with_matrix(tbounds, bezt->vec[j], use_mat_local, diff_mat);
run_coord_with_matrix(bezt->vec[j], use_mat_local, diff_mat);
totsel++;
}
}
@ -622,7 +575,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
/* Change selection status of all points, then make the stroke match */
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (pt->flag & GP_SPOINT_SELECT) {
calc_tw_center_with_matrix(tbounds, &pt->x, use_mat_local, diff_mat);
run_coord_with_matrix(&pt->x, use_mat_local, diff_mat);
totsel++;
}
}
@ -631,11 +584,6 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
}
}
}
/* selection center */
if (totsel) {
mul_v3_fl(tbounds->center, 1.0f / float(totsel)); /* centroid! */
}
}
else if (obedit) {
@ -676,7 +624,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
calc_tw_center_with_matrix(tbounds, eve->co, use_mat_local, mat_local);
run_coord_with_matrix(eve->co, use_mat_local, mat_local);
totsel++;
}
}
@ -695,18 +643,24 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
LISTBASE_FOREACH (EditBone *, ebo, arm->edbo) {
if (EBONE_VISIBLE(arm, ebo)) {
if (ebo->flag & BONE_TIPSEL) {
calc_tw_center_with_matrix(tbounds, ebo->tail, use_mat_local, mat_local);
run_coord_with_matrix(ebo->tail, use_mat_local, mat_local);
totsel++;
}
if ((ebo->flag & BONE_ROOTSEL) &&
/* don't include same point multiple times */
((ebo->flag & BONE_CONNECTED) && (ebo->parent != nullptr) &&
(ebo->parent->flag & BONE_TIPSEL) && EBONE_VISIBLE(arm, ebo->parent)) == 0) {
calc_tw_center_with_matrix(tbounds, ebo->head, use_mat_local, mat_local);
run_coord_with_matrix(ebo->head, use_mat_local, mat_local);
totsel++;
}
if (ebo->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) {
protectflag_to_drawflags_ebone(rv3d, ebo);
if (r_drawflags) {
if (ebo->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) {
if (ebo->flag & BONE_EDITMODE_LOCKED) {
protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE,
r_drawflags);
}
}
}
}
}
}
@ -737,23 +691,23 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
*/
if (v3d->overlay.handle_display == CURVE_HANDLE_NONE) {
if (bezt->f2 & SELECT) {
calc_tw_center_with_matrix(tbounds, bezt->vec[1], use_mat_local, mat_local);
run_coord_with_matrix(bezt->vec[1], use_mat_local, mat_local);
totsel++;
}
}
else if (bezt->f2 & SELECT) {
calc_tw_center_with_matrix(tbounds, bezt->vec[1], use_mat_local, mat_local);
run_coord_with_matrix(bezt->vec[1], use_mat_local, mat_local);
totsel++;
}
else {
if (bezt->f1 & SELECT) {
const float *co = bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 0];
calc_tw_center_with_matrix(tbounds, co, use_mat_local, mat_local);
const float *co = bezt->vec[!use_curve_handles ? 1 : 0];
run_coord_with_matrix(co, use_mat_local, mat_local);
totsel++;
}
if (bezt->f3 & SELECT) {
const float *co = bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 2];
calc_tw_center_with_matrix(tbounds, co, use_mat_local, mat_local);
const float *co = bezt->vec[!use_curve_handles ? 1 : 2];
run_coord_with_matrix(co, use_mat_local, mat_local);
totsel++;
}
}
@ -765,7 +719,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
a = nu->pntsu * nu->pntsv;
while (a--) {
if (bp->f1 & SELECT) {
calc_tw_center_with_matrix(tbounds, bp->vec, use_mat_local, mat_local);
run_coord_with_matrix(bp->vec, use_mat_local, mat_local);
totsel++;
}
bp++;
@ -787,7 +741,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) {
if (ml->flag & SELECT) {
calc_tw_center_with_matrix(tbounds, &ml->x, use_mat_local, mat_local);
run_coord_with_matrix(&ml->x, use_mat_local, mat_local);
totsel++;
}
}
@ -807,7 +761,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
while (a--) {
if (bp->f1 & SELECT) {
calc_tw_center_with_matrix(tbounds, bp->vec, use_mat_local, mat_local);
run_coord_with_matrix(bp->vec, use_mat_local, mat_local);
totsel++;
}
bp++;
@ -832,7 +786,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
const Span<float3> positions = deformation.positions;
totsel += selected_points.size();
for (const int point_i : selected_points) {
calc_tw_center_with_matrix(tbounds, positions[point_i], use_mat_local, mat_local.ptr());
run_coord_with_matrix(positions[point_i], use_mat_local, mat_local.ptr());
}
}
FOREACH_EDIT_OBJECT_END();
@ -840,14 +794,6 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
#undef FOREACH_EDIT_OBJECT_BEGIN
#undef FOREACH_EDIT_OBJECT_END
/* selection center */
if (totsel) {
mul_v3_fl(tbounds->center, 1.0f / float(totsel)); /* centroid! */
mul_m4_v3(obedit->object_to_world, tbounds->center);
mul_m4_v3(obedit->object_to_world, tbounds->min);
mul_m4_v3(obedit->object_to_world, tbounds->max);
}
}
else if (ob && (ob->mode & OB_MODE_POSE)) {
invert_m4_m4(ob->world_to_object, ob->object_to_world);
@ -873,27 +819,24 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
if (!(pchan->bone->flag & BONE_TRANSFORM)) {
continue;
}
calc_tw_center_with_matrix(tbounds, pchan->pose_head, use_mat_local, mat_local);
protectflag_to_drawflags_pchan(rv3d, pchan, orient_index);
run_coord_with_matrix(pchan->pose_head, use_mat_local, mat_local);
totsel++;
if (r_drawflags) {
/* Protect-flags apply to local space in pose mode, so only let them influence axis
* visibility if we show the global orientation, otherwise it's confusing. */
if (ELEM(orient_index, V3D_ORIENT_LOCAL, V3D_ORIENT_GIMBAL)) {
protectflag_to_drawflags(pchan->protectflag, r_drawflags);
}
}
}
}
MEM_freeN(objects);
if (totsel) {
mul_v3_fl(tbounds->center, 1.0f / float(totsel)); /* centroid! */
mul_m4_v3(ob->object_to_world, tbounds->center);
mul_m4_v3(ob->object_to_world, tbounds->min);
mul_m4_v3(ob->object_to_world, tbounds->max);
}
}
else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
if (ob->mode & OB_MODE_SCULPT) {
totsel = 1;
calc_tw_center_with_matrix(tbounds, ob->sculpt->pivot_pos, false, ob->object_to_world);
mul_m4_v3(ob->object_to_world, tbounds->center);
mul_m4_v3(ob->object_to_world, tbounds->min);
mul_m4_v3(ob->object_to_world, tbounds->max);
run_coord_with_matrix(ob->sculpt->pivot_pos, false, ob->object_to_world);
}
}
else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
@ -911,16 +854,11 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) {
if (ek->flag & PEK_SELECT) {
calc_tw_center(tbounds, (ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co);
user_fn((ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co);
totsel++;
}
}
}
/* selection center */
if (totsel) {
mul_v3_fl(tbounds->center, 1.0f / float(totsel)); /* centroid! */
}
}
}
else {
@ -944,46 +882,139 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
/* Get the boundbox out of the evaluated object. */
const BoundBox *bb = nullptr;
if (params->use_only_center == false) {
if (use_only_center == false) {
bb = BKE_object_boundbox_get(base->object);
}
if (params->use_only_center || (bb == nullptr)) {
calc_tw_center(tbounds, base->object->object_to_world[3]);
if (use_only_center || (bb == nullptr)) {
user_fn(base->object->object_to_world[3]);
}
else {
for (uint j = 0; j < 8; j++) {
float co[3];
mul_v3_m4v3(co, base->object->object_to_world, bb->vec[j]);
calc_tw_center(tbounds, co);
user_fn(co);
}
}
if (orient_index == V3D_ORIENT_GLOBAL) {
/* Protect-flags apply to world space in object mode,
* so only let them influence axis visibility if we show the global orientation,
* otherwise it's confusing. */
protectflag_to_drawflags(base->object->protectflag & OB_LOCK_LOC, &rv3d->twdrawflag);
}
else if (ELEM(orient_index, V3D_ORIENT_LOCAL, V3D_ORIENT_GIMBAL)) {
protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag);
}
totsel++;
}
/* selection center */
if (totsel) {
mul_v3_fl(tbounds->center, 1.0f / float(totsel)); /* centroid! */
if (r_drawflags) {
if (orient_index == V3D_ORIENT_GLOBAL) {
/* Protect-flags apply to world space in object mode,
* so only let them influence axis visibility if we show the global orientation,
* otherwise it's confusing. */
protectflag_to_drawflags(base->object->protectflag & OB_LOCK_LOC, r_drawflags);
}
else if (ELEM(orient_index, V3D_ORIENT_LOCAL, V3D_ORIENT_GIMBAL)) {
protectflag_to_drawflags(base->object->protectflag, r_drawflags);
}
}
}
}
if (totsel == 0) {
unit_m4(rv3d->twmat);
if (r_mat && ob) {
*r_mat = ob->object_to_world;
}
else {
copy_v3_v3(rv3d->tw_axis_min, tbounds->axis_min);
copy_v3_v3(rv3d->tw_axis_max, tbounds->axis_max);
copy_m3_m3(rv3d->tw_axis_matrix, tbounds->axis);
return totsel;
}
int ED_transform_calc_gizmo_stats(const bContext *C,
const TransformCalcParams *params,
TransformBounds *tbounds,
RegionView3D *rv3d)
{
ScrArea *area = CTX_wm_area(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = static_cast<View3D *>(area->spacedata.first);
int totsel = 0;
const int pivot_point = scene->toolsettings->transform_pivot_point;
const short orient_index = params->orientation_index ?
(params->orientation_index - 1) :
BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
BKE_view_layer_synced_ensure(scene, view_layer);
Object *ob = BKE_view_layer_active_object_get(view_layer);
Object *obedit = OBEDIT_FROM_OBACT(ob);
if (ob && ob->mode & OB_MODE_WEIGHT_PAINT) {
Object *obpose = BKE_object_pose_armature_get(ob);
if (obpose != nullptr) {
ob = obpose;
}
}
tbounds->use_matrix_space = false;
unit_m3(tbounds->axis);
/* global, local or normal orientation?
* if we could check 'totsel' now, this should be skipped with no selection. */
if (ob) {
float mat[3][3];
ED_transform_calc_orientation_from_type_ex(
scene, view_layer, v3d, rv3d, ob, obedit, orient_index, pivot_point, mat);
copy_m3_m3(tbounds->axis, mat);
}
reset_tw_center(tbounds);
if (rv3d) {
/* transform widget centroid/center */
copy_m4_m3(rv3d->twmat, tbounds->axis);
rv3d->twdrawflag = short(0xFFFF);
}
if (params->use_local_axis && (ob && ob->mode & (OB_MODE_EDIT | OB_MODE_POSE))) {
float diff_mat[3][3];
copy_m3_m4(diff_mat, ob->object_to_world);
normalize_m3(diff_mat);
invert_m3(diff_mat);
mul_m3_m3m3(tbounds->axis, tbounds->axis, diff_mat);
normalize_m3(tbounds->axis);
tbounds->use_matrix_space = true;
copy_m4_m4(tbounds->matrix_space, ob->object_to_world);
}
const auto gizmo_3d_tbounds_calc_fn = [&](const blender::float3 &co) {
calc_tw_center(tbounds, co);
};
totsel = gizmo_3d_foreach_selected(C,
orient_index,
(pivot_point != V3D_AROUND_LOCAL_ORIGINS),
params->use_only_center,
gizmo_3d_tbounds_calc_fn,
nullptr,
rv3d ? &rv3d->twdrawflag : nullptr);
if (totsel) {
mul_v3_fl(tbounds->center, 1.0f / float(totsel)); /* centroid! */
bGPdata *gpd = CTX_data_gpencil_data(C);
const bool is_gp_edit = GPENCIL_ANY_MODE(gpd);
if (!is_gp_edit && (obedit || (ob && (ob->mode & (OB_MODE_POSE | OB_MODE_SCULPT))))) {
if (ob && (ob->mode & OB_MODE_POSE)) {
invert_m4_m4(ob->world_to_object, ob->object_to_world);
}
mul_m4_v3(ob->object_to_world, tbounds->center);
mul_m4_v3(ob->object_to_world, tbounds->min);
mul_m4_v3(ob->object_to_world, tbounds->max);
}
}
if (rv3d) {
if (totsel == 0) {
unit_m4(rv3d->twmat);
unit_m3(rv3d->tw_axis_matrix);
zero_v3(rv3d->tw_axis_min);
zero_v3(rv3d->tw_axis_max);
}
else {
copy_m3_m3(rv3d->tw_axis_matrix, tbounds->axis);
copy_v3_v3(rv3d->tw_axis_min, tbounds->axis_min);
copy_v3_v3(rv3d->tw_axis_max, tbounds->axis_max);
}
}
return totsel;
@ -999,46 +1030,87 @@ static void gizmo_get_idot(const RegionView3D *rv3d, float r_idot[3])
}
}
void gizmo_prepare_mat(const bContext *C, RegionView3D *rv3d, const TransformBounds *tbounds)
static bool gizmo_3d_calc_pos(const bContext *C,
const Scene *scene,
const TransformBounds *tbounds,
const short pivot_type,
float r_pivot_pos[3])
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
zero_v3(r_pivot_pos);
switch (scene->toolsettings->transform_pivot_point) {
case V3D_AROUND_CENTER_BOUNDS:
switch (pivot_type) {
case V3D_AROUND_CURSOR:
copy_v3_v3(r_pivot_pos, scene->cursor.location);
return true;
case V3D_AROUND_ACTIVE: {
mid_v3_v3v3(rv3d->twmat[3], tbounds->min, tbounds->max);
if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) {
BKE_view_layer_synced_ensure(scene, view_layer);
Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob != nullptr) {
/* Grease Pencil uses object origin. */
bGPdata *gpd = CTX_data_gpencil_data(C);
if (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE)) {
ED_object_calc_active_center(ob, false, rv3d->twmat[3]);
}
else {
if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) {
SculptSession *ss = ob->sculpt;
copy_v3_v3(rv3d->twmat[3], ss->pivot_pos);
}
else {
ED_object_calc_active_center(ob, false, rv3d->twmat[3]);
}
}
ViewLayer *view_layer = CTX_data_view_layer(C);
BKE_view_layer_synced_ensure(scene, view_layer);
Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob != nullptr) {
if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) {
SculptSession *ss = ob->sculpt;
copy_v3_v3(r_pivot_pos, ss->pivot_pos);
return true;
}
else if (ED_object_calc_active_center(ob, false, r_pivot_pos)) {
return true;
}
}
}
[[fallthrough]];
case V3D_AROUND_CENTER_BOUNDS: {
TransformBounds tbounds_stack;
if (tbounds == nullptr) {
TransformCalcParams calc_params{};
calc_params.use_only_center = true;
if (ED_transform_calc_gizmo_stats(C, &calc_params, &tbounds_stack, nullptr)) {
tbounds = &tbounds_stack;
}
}
if (tbounds) {
mid_v3_v3v3(r_pivot_pos, tbounds->min, tbounds->max);
return true;
}
break;
}
case V3D_AROUND_LOCAL_ORIGINS:
case V3D_AROUND_CENTER_MEDIAN:
copy_v3_v3(rv3d->twmat[3], tbounds->center);
break;
case V3D_AROUND_CURSOR:
copy_v3_v3(rv3d->twmat[3], scene->cursor.location);
break;
case V3D_AROUND_CENTER_MEDIAN: {
if (tbounds) {
copy_v3_v3(r_pivot_pos, tbounds->center);
return true;
}
const auto gizmo_3d_calc_center_fn = [&](const blender::float3 &co) {
add_v3_v3(r_pivot_pos, co);
};
const float(*r_mat)[4] = nullptr;
int totsel;
totsel = gizmo_3d_foreach_selected(C,
0,
(pivot_type != V3D_AROUND_LOCAL_ORIGINS),
true,
gizmo_3d_calc_center_fn,
&r_mat,
nullptr);
if (totsel) {
mul_v3_fl(r_pivot_pos, 1.0f / float(totsel));
if (r_mat) {
mul_m4_v3(r_mat, r_pivot_pos);
}
return true;
}
}
}
return false;
}
void gizmo_prepare_mat(const struct bContext *C,
struct RegionView3D *rv3d,
const struct TransformBounds *tbounds)
{
Scene *scene = CTX_data_scene(C);
gizmo_3d_calc_pos(C, scene, tbounds, scene->toolsettings->transform_pivot_point, rv3d->twmat[3]);
}
/**
@ -1598,7 +1670,7 @@ static int gizmo_modal(bContext *C,
TransformCalcParams calc_params{};
calc_params.use_only_center = true;
if (ED_transform_calc_gizmo_stats(C, &calc_params, &tbounds)) {
if (ED_transform_calc_gizmo_stats(C, &calc_params, &tbounds, rv3d)) {
gizmo_prepare_mat(C, rv3d, &tbounds);
for (wmGizmo *gz = static_cast<wmGizmo *>(gzgroup->gizmos.first); gz; gz = gz->next) {
WM_gizmo_set_matrix_location(gz, rv3d->twmat[3]);
@ -1875,11 +1947,12 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
TransformCalcParams calc_params{};
calc_params.use_only_center = true;
calc_params.orientation_index = orient_index + 1;
if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(C, &calc_params, &tbounds) == 0))) {
if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(C, &calc_params, &tbounds, rv3d) == 0))) {
return;
}
gizmo_prepare_mat(C, rv3d, &tbounds);
gizmo_3d_calc_pos(
C, scene, &tbounds, scene->toolsettings->transform_pivot_point, rv3d->twmat[3]);
gizmogroup_refresh_from_matrix(gzgroup, rv3d->twmat, nullptr, false);
}
@ -2378,3 +2451,11 @@ void transform_gizmo_3d_model_from_constraint_and_mode_restore(TransInfo *t)
}
MAN_ITER_AXES_END;
}
bool ED_transform_calc_pivot_pos(const struct bContext *C,
const short pivot_type,
float r_pivot_pos[3])
{
Scene *scene = CTX_data_scene(C);
return gizmo_3d_calc_pos(C, scene, nullptr, pivot_type, r_pivot_pos);
}

View File

@ -111,7 +111,7 @@ static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmGizmoGroup *gzgr
TransformCalcParams calc_params{};
calc_params.use_local_axis = true;
calc_params.orientation_index = orient_index + 1;
if ((ED_transform_calc_gizmo_stats(C, &calc_params, &tbounds) == 0) ||
if ((ED_transform_calc_gizmo_stats(C, &calc_params, &tbounds, rv3d) == 0) ||
equals_v3v3(rv3d->tw_axis_min, rv3d->tw_axis_max)) {
WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
}

View File

@ -120,7 +120,7 @@ static void WIDGETGROUP_xform_shear_refresh(const bContext *C, wmGizmoGroup *gzg
TransformCalcParams calc_params{};
calc_params.use_local_axis = false;
calc_params.orientation_index = orient_index + 1;
if (ED_transform_calc_gizmo_stats(C, &calc_params, &tbounds) == 0) {
if (ED_transform_calc_gizmo_stats(C, &calc_params, &tbounds, rv3d) == 0) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
wmGizmo *gz = xgzgroup->gizmo[i][j];

View File

@ -233,6 +233,7 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
}
Scene *scene = CTX_data_scene(C);
RegionView3D *rv3d = CTX_wm_region_data(C);
int axis_type;
{
@ -255,7 +256,8 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
&(struct TransformCalcParams){
.orientation_index = V3D_ORIENT_NORMAL + 1,
},
&tbounds_normal)) {
&tbounds_normal,
rv3d)) {
unit_m3(tbounds_normal.axis);
}
copy_m3_m3(ggd->data.normal_mat3, tbounds_normal.axis);
@ -266,7 +268,8 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
&(struct TransformCalcParams){
.orientation_index = ggd->data.orientation_index + 1,
},
&tbounds)) {
&tbounds,
rv3d)) {
return;
}

View File

@ -64,15 +64,15 @@ class GeoNodeExecParams {
const bNode &node_;
lf::Params &params_;
const lf::Context &lf_context_;
const Map<StringRef, int> &lf_input_for_output_bsocket_usage_;
const Map<StringRef, int> &lf_input_for_attribute_propagation_to_output_;
const Span<int> lf_input_for_output_bsocket_usage_;
const Span<int> lf_input_for_attribute_propagation_to_output_;
public:
GeoNodeExecParams(const bNode &node,
lf::Params &params,
const lf::Context &lf_context,
const Map<StringRef, int> &lf_input_for_output_bsocket_usage,
const Map<StringRef, int> &lf_input_for_attribute_propagation_to_output)
const Span<int> lf_input_for_output_bsocket_usage,
const Span<int> lf_input_for_attribute_propagation_to_output)
: node_(node),
params_(params),
lf_context_(lf_context),
@ -251,7 +251,9 @@ class GeoNodeExecParams {
*/
bool anonymous_attribute_output_is_required(const StringRef output_identifier)
{
const int lf_index = lf_input_for_output_bsocket_usage_.lookup(output_identifier);
const int lf_index =
lf_input_for_output_bsocket_usage_[node_.output_by_identifier(output_identifier)
.index_in_all_outputs()];
return params_.get_input<bool>(lf_index);
}
@ -282,7 +284,9 @@ class GeoNodeExecParams {
AnonymousAttributePropagationInfo get_output_propagation_info(
const StringRef output_identifier) const
{
const int lf_index = lf_input_for_attribute_propagation_to_output_.lookup(output_identifier);
const int lf_index =
lf_input_for_attribute_propagation_to_output_[node_.output_by_identifier(output_identifier)
.index_in_all_outputs()];
const bke::AnonymousAttributeSet &set = params_.get_input<bke::AnonymousAttributeSet>(
lf_index);
AnonymousAttributePropagationInfo info;

View File

@ -149,6 +149,11 @@ struct GeometryNodeLazyFunctionGraphMapping {
*/
Map<const bNode *, const lf::FunctionNode *> group_node_map;
Map<const bNode *, const lf::FunctionNode *> viewer_node_map;
/* Indexed by #bNodeSocket::index_in_all_outputs. */
Array<int> lf_input_index_for_output_bsocket_usage;
/* Indexed by #bNodeSocket::index_in_all_outputs. */
Array<int> lf_input_index_for_attribute_propagation_to_output;
};
/**

View File

@ -324,6 +324,7 @@ DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Fac
DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "Convert each input geometry into an instance, which can be much faster than the Join Geometry node when the inputs are large")
DefNode(GeometryNode, GEO_NODE_IMAGE_INFO, 0, "IMAGE_INFO", ImageInfo, "Image Info", "Retrieve information about an image")
DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "Sample values from an image texture")
DefNode(GeometryNode, GEO_NODE_INDEX_OF_NEAREST, 0, "INDEX_OF_NEAREST", IndexOfNearest, "Index of Nearest", "Find the nearest element in the a group. Similar to the \"Sample Nearest\" node")
DefNode(GeometryNode, GEO_NODE_IMAGE, def_geo_image, "IMAGE", InputImage, "Image", "Input image")
DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions,"Curve Handle Positions", "Retrieve the position of each Bézier control point's handles")
DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "Retrieve the angle at each control point used to twist the curve's normal around its tangent")
@ -398,7 +399,7 @@ DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateIn
DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "Retrieve data from a point on a curve at a certain distance from its start")
DefNode(GeometryNode, GEO_NODE_SAMPLE_INDEX, def_geo_sample_index, "SAMPLE_INDEX", SampleIndex, "Sample Index", "Retrieve values from specific geometry elements")
DefNode(GeometryNode, GEO_NODE_SAMPLE_NEAREST_SURFACE, def_geo_sample_nearest_surface, "SAMPLE_NEAREST_SURFACE", SampleNearestSurface, "Sample Nearest Surface", "Calculate the interpolated value of a mesh attribute on the closest point of its surface")
DefNode(GeometryNode, GEO_NODE_SAMPLE_NEAREST, def_geo_sample_nearest, "SAMPLE_NEAREST", SampleNearest, "Sample Nearest", "Find the element of a geometry closest to a position")
DefNode(GeometryNode, GEO_NODE_SAMPLE_NEAREST, def_geo_sample_nearest, "SAMPLE_NEAREST", SampleNearest, "Sample Nearest", "Find the element of a geometry closest to a position. Similar to the \"Index of Nearest\" node")
DefNode(GeometryNode, GEO_NODE_SAMPLE_UV_SURFACE, def_geo_sample_uv_surface, "SAMPLE_UV_SURFACE", SampleUVSurface, "Sample UV Surface", "Calculate the interpolated values of a mesh attribute at a UV coordinate")
DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "Scale groups of connected edges and faces")
DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "Scale geometry instances in local or global space")

View File

@ -78,6 +78,7 @@ set(SRC
nodes/node_geo_image.cc
nodes/node_geo_image_info.cc
nodes/node_geo_image_texture.cc
nodes/node_geo_index_of_nearest.cc
nodes/node_geo_input_curve_handles.cc
nodes/node_geo_input_curve_tilt.cc
nodes/node_geo_input_id.cc

View File

@ -62,6 +62,7 @@ void register_geometry_nodes()
register_node_type_geo_image_info();
register_node_type_geo_image_texture();
register_node_type_geo_image();
register_node_type_geo_index_of_nearest();
register_node_type_geo_input_curve_handles();
register_node_type_geo_input_curve_tilt();
register_node_type_geo_input_id();

View File

@ -59,6 +59,7 @@ void register_node_type_geo_geometry_to_instance();
void register_node_type_geo_image_info();
void register_node_type_geo_image_texture();
void register_node_type_geo_image();
void register_node_type_geo_index_of_nearest();
void register_node_type_geo_input_curve_handles();
void register_node_type_geo_input_curve_tilt();
void register_node_type_geo_input_id();

View File

@ -0,0 +1,270 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array.hh"
#include "BLI_kdtree.h"
#include "BLI_map.hh"
#include "BLI_task.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_index_of_nearest_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Vector>(N_("Position")).implicit_field(implicit_field_inputs::position);
b.add_input<decl::Int>(N_("Group ID")).supports_field().hide_value();
b.add_output<decl::Int>(N_("Index")).field_source().description(N_("Index of nearest element"));
b.add_output<decl::Bool>(N_("Has Neighbor")).field_source();
}
static KDTree_3d *build_kdtree(const Span<float3> positions, const IndexMask mask)
{
KDTree_3d *tree = BLI_kdtree_3d_new(mask.size());
for (const int index : mask) {
BLI_kdtree_3d_insert(tree, index, positions[index]);
}
BLI_kdtree_3d_balance(tree);
return tree;
}
static int find_nearest_non_self(const KDTree_3d &tree, const float3 &position, const int index)
{
return BLI_kdtree_3d_find_nearest_cb_cpp(
&tree, position, 0, [index](const int other, const float * /*co*/, const float /*dist_sq*/) {
return index == other ? 0 : 1;
});
}
static void find_neighbors(const KDTree_3d &tree,
const Span<float3> positions,
const IndexMask mask,
MutableSpan<int> r_indices)
{
threading::parallel_for(mask.index_range(), 512, [&](const IndexRange range) {
for (const int index : mask.slice(range)) {
r_indices[index] = find_nearest_non_self(tree, positions[index], index);
}
});
}
class IndexOfNearestFieldInput final : public bke::GeometryFieldInput {
private:
const Field<float3> positions_field_;
const Field<int> group_field_;
public:
IndexOfNearestFieldInput(Field<float3> positions_field, Field<int> group_field)
: bke::GeometryFieldInput(CPPType::get<int>(), "Index of Nearest"),
positions_field_(std::move(positions_field)),
group_field_(std::move(group_field))
{
}
GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
const IndexMask mask) const final
{
if (!context.attributes()) {
return {};
}
const int domain_size = context.attributes()->domain_size(context.domain());
fn::FieldEvaluator evaluator{context, domain_size};
evaluator.add(positions_field_);
evaluator.add(group_field_);
evaluator.evaluate();
const VArraySpan<float3> positions = evaluator.get_evaluated<float3>(0);
const VArray<int> group = evaluator.get_evaluated<int>(1);
Array<int> result;
if (group.is_single()) {
result.reinitialize(mask.min_array_size());
KDTree_3d *tree = build_kdtree(positions, IndexRange(domain_size));
find_neighbors(*tree, positions, mask, result);
BLI_kdtree_3d_free(tree);
return VArray<int>::ForContainer(std::move(result));
}
VectorSet<int> group_indexing;
for (const int index : mask) {
const int group_id = group[index];
group_indexing.add(group_id);
}
/* Each group id has two corresponding index masks. One that contains all the points in the
* group, one that contains all the points in the group that should be looked up (this is the
* intersection of the points in the group and `mask`). In many cases, both of these masks are
* the same or very similar, so there is no benefit two separate masks. */
const bool use_separate_lookup_indices = mask.size() < domain_size / 2;
Array<Vector<int64_t>> all_indices_by_group_id(group_indexing.size());
Array<Vector<int64_t>> lookup_indices_by_group_id;
if (use_separate_lookup_indices) {
result.reinitialize(mask.min_array_size());
lookup_indices_by_group_id.reinitialize(group_indexing.size());
}
else {
result.reinitialize(domain_size);
}
const auto build_group_masks = [&](const IndexMask mask,
MutableSpan<Vector<int64_t>> r_groups) {
for (const int index : mask) {
const int group_id = group[index];
const int index_of_group = group_indexing.index_of_try(group_id);
if (index_of_group != -1) {
r_groups[index_of_group].append(index);
}
}
};
threading::parallel_invoke(
domain_size > 1024 && use_separate_lookup_indices,
[&]() {
if (use_separate_lookup_indices) {
build_group_masks(mask, lookup_indices_by_group_id);
}
},
[&]() { build_group_masks(IndexMask(domain_size), all_indices_by_group_id); });
threading::parallel_for(group_indexing.index_range(), 256, [&](const IndexRange range) {
for (const int index : range) {
const IndexMask tree_mask = all_indices_by_group_id[index].as_span();
const IndexMask lookup_mask = use_separate_lookup_indices ?
IndexMask(lookup_indices_by_group_id[index]) :
tree_mask;
KDTree_3d *tree = build_kdtree(positions, tree_mask);
find_neighbors(*tree, positions, lookup_mask, result);
BLI_kdtree_3d_free(tree);
}
});
return VArray<int>::ForContainer(std::move(result));
}
public:
void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const
{
positions_field_.node().for_each_field_input_recursive(fn);
group_field_.node().for_each_field_input_recursive(fn);
}
uint64_t hash() const final
{
return get_default_hash_2(positions_field_, group_field_);
}
bool is_equal_to(const fn::FieldNode &other) const final
{
if (const auto *other_field = dynamic_cast<const IndexOfNearestFieldInput *>(&other)) {
return positions_field_ == other_field->positions_field_ &&
group_field_ == other_field->group_field_;
}
return false;
}
std::optional<eAttrDomain> preferred_domain(const GeometryComponent &component) const final
{
return bke::try_detect_field_domain(component, positions_field_);
}
};
class HasNeighborFieldInput final : public bke::GeometryFieldInput {
private:
const Field<int> group_field_;
public:
HasNeighborFieldInput(Field<int> group_field)
: bke::GeometryFieldInput(CPPType::get<bool>(), "Has Neighbor"),
group_field_(std::move(group_field))
{
}
GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
const IndexMask mask) const final
{
if (!context.attributes()) {
return {};
}
const int domain_size = context.attributes()->domain_size(context.domain());
if (domain_size == 1) {
return VArray<bool>::ForSingle(false, mask.min_array_size());
}
fn::FieldEvaluator evaluator{context, domain_size};
evaluator.add(group_field_);
evaluator.evaluate();
const VArray<int> group = evaluator.get_evaluated<int>(0);
if (group.is_single()) {
return VArray<bool>::ForSingle(true, mask.min_array_size());
}
Map<int, int> counts;
const VArraySpan<int> group_span(group);
mask.foreach_index([&](const int i) {
counts.add_or_modify(
group_span[i], [](int *count) { *count = 0; }, [](int *count) { (*count)++; });
});
Array<bool> result(mask.min_array_size());
mask.foreach_index([&](const int i) { result[i] = counts.lookup(group_span[i]) > 1; });
return VArray<bool>::ForContainer(std::move(result));
}
public:
void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const
{
group_field_.node().for_each_field_input_recursive(fn);
}
uint64_t hash() const final
{
return get_default_hash_2(3984756934876, group_field_);
}
bool is_equal_to(const fn::FieldNode &other) const final
{
if (const auto *other_field = dynamic_cast<const HasNeighborFieldInput *>(&other)) {
return group_field_ == other_field->group_field_;
}
return false;
}
std::optional<eAttrDomain> preferred_domain(const GeometryComponent &component) const final
{
return bke::try_detect_field_domain(component, group_field_);
}
};
static void node_geo_exec(GeoNodeExecParams params)
{
Field<float3> position_field = params.extract_input<Field<float3>>("Position");
Field<int> group_field = params.extract_input<Field<int>>("Group ID");
if (params.output_is_required("Index")) {
params.set_output("Index",
Field<int>(std::make_shared<IndexOfNearestFieldInput>(
std::move(position_field), group_field)));
}
if (params.output_is_required("Has Neighbor")) {
params.set_output(
"Has Neighbor",
Field<bool>(std::make_shared<HasNeighborFieldInput>(std::move(group_field))));
}
}
} // namespace blender::nodes::node_geo_index_of_nearest_cc
void register_node_type_geo_index_of_nearest()
{
namespace file_ns = blender::nodes::node_geo_index_of_nearest_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_INDEX_OF_NEAREST, "Index of Nearest", NODE_CLASS_CONVERTER);
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}

View File

@ -109,22 +109,26 @@ static void lazy_function_interface_from_node(const bNode &node,
class LazyFunctionForGeometryNode : public LazyFunction {
private:
const bNode &node_;
public:
/**
* Index of a boolean input that indicates whether the output socket is used.
*/
Map<StringRef, int> lf_input_for_output_bsocket_usage_;
const Span<int> lf_input_for_output_bsocket_usage_;
/**
* Index of an attribute set input that indicates which anonymous attributes should be
* propagated to the output.
*/
Map<StringRef, int> lf_input_for_attribute_propagation_to_output_;
const Span<int> lf_input_for_attribute_propagation_to_output_;
public:
LazyFunctionForGeometryNode(const bNode &node,
Vector<const bNodeSocket *> &r_used_inputs,
Vector<const bNodeSocket *> &r_used_outputs)
: node_(node)
Vector<const bNodeSocket *> &r_used_outputs,
MutableSpan<int> r_lf_input_for_output_bsocket_usage,
MutableSpan<int> r_lf_input_for_attribute_propagation_to_output)
: node_(node),
lf_input_for_output_bsocket_usage_(r_lf_input_for_output_bsocket_usage),
lf_input_for_attribute_propagation_to_output_(
r_lf_input_for_attribute_propagation_to_output)
{
BLI_assert(node.typeinfo->geometry_node_execute != nullptr);
debug_name_ = node.name;
@ -141,7 +145,7 @@ class LazyFunctionForGeometryNode : public LazyFunction {
if (output_bsocket.is_available() && !handled_field_outputs.contains(&output_bsocket)) {
handled_field_outputs.append(&output_bsocket);
const int lf_index = inputs_.append_and_get_index_as("Output Used", CPPType::get<bool>());
lf_input_for_output_bsocket_usage_.add(output_bsocket.identifier, lf_index);
r_lf_input_for_output_bsocket_usage[output_bsocket.index_in_all_outputs()] = lf_index;
}
}
@ -152,7 +156,8 @@ class LazyFunctionForGeometryNode : public LazyFunction {
handled_geometry_outputs.append(&output_bsocket);
const int lf_index = inputs_.append_and_get_index_as(
"Propagate to Output", CPPType::get<bke::AnonymousAttributeSet>());
lf_input_for_attribute_propagation_to_output_.add(output_bsocket.identifier, lf_index);
r_lf_input_for_attribute_propagation_to_output[output_bsocket.index_in_all_outputs()] =
lf_index;
}
}
}
@ -181,15 +186,19 @@ class LazyFunctionForGeometryNode : public LazyFunction {
std::string input_name(const int index) const override
{
for (const auto [identifier, lf_index] : lf_input_for_output_bsocket_usage_.items()) {
if (index == lf_index) {
return "Use Output '" + identifier + "'";
for (const bNodeSocket *bsocket : node_.output_sockets()) {
{
const int lf_index = lf_input_for_output_bsocket_usage_[bsocket->index_in_all_outputs()];
if (index == lf_index) {
return StringRef("Use Output '") + bsocket->identifier + "'";
}
}
}
for (const auto [identifier, lf_index] :
lf_input_for_attribute_propagation_to_output_.items()) {
if (index == lf_index) {
return "Propagate to '" + identifier + "'";
{
const int lf_index =
lf_input_for_attribute_propagation_to_output_[bsocket->index_in_all_outputs()];
if (index == lf_index) {
return StringRef("Propagate to '") + bsocket->identifier + "'";
}
}
}
return inputs_[index].debug_name;
@ -1223,6 +1232,12 @@ struct GeometryNodesLazyFunctionGraphBuilder {
socket_is_used_map_.reinitialize(btree_.all_sockets().size());
socket_is_used_map_.fill(nullptr);
mapping_->lf_input_index_for_output_bsocket_usage.reinitialize(
btree_.all_output_sockets().size());
mapping_->lf_input_index_for_output_bsocket_usage.fill(-1);
mapping_->lf_input_index_for_attribute_propagation_to_output.reinitialize(
btree_.all_output_sockets().size());
mapping_->lf_input_index_for_attribute_propagation_to_output.fill(-1);
this->prepare_node_multi_functions();
this->build_group_input_node();
@ -1490,7 +1505,11 @@ struct GeometryNodesLazyFunctionGraphBuilder {
Vector<const bNodeSocket *> used_inputs;
Vector<const bNodeSocket *> used_outputs;
auto lazy_function = std::make_unique<LazyFunctionForGeometryNode>(
bnode, used_inputs, used_outputs);
bnode,
used_inputs,
used_outputs,
mapping_->lf_input_index_for_output_bsocket_usage,
mapping_->lf_input_index_for_attribute_propagation_to_output);
lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
for (const int i : used_inputs.index_range()) {
@ -1521,17 +1540,24 @@ struct GeometryNodesLazyFunctionGraphBuilder {
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
}
for (const auto [identifier, lf_input_index] :
lazy_function->lf_input_for_output_bsocket_usage_.items()) {
output_used_sockets_for_builtin_nodes_.append_as(&bnode.output_by_identifier(identifier),
&lf_node.input(lf_input_index));
socket_usage_inputs_.add_new(&lf_node.input(lf_input_index));
}
/* Keep track of attribute set inputs that need to be populated later. */
for (const auto [identifier, lf_input_index] :
lazy_function->lf_input_for_attribute_propagation_to_output_.items()) {
attribute_set_propagation_map_.add(&bnode.output_by_identifier(identifier),
&lf_node.input(lf_input_index));
for (const bNodeSocket *bsocket : bnode.output_sockets()) {
{
const int lf_input_index =
mapping_->lf_input_index_for_output_bsocket_usage[bsocket->index_in_all_outputs()];
if (lf_input_index != -1) {
output_used_sockets_for_builtin_nodes_.append_as(bsocket,
&lf_node.input(lf_input_index));
socket_usage_inputs_.add_new(&lf_node.input(lf_input_index));
}
}
{
/* Keep track of attribute set inputs that need to be populated later. */
const int lf_input_index = mapping_->lf_input_index_for_attribute_propagation_to_output
[bsocket->index_in_all_outputs()];
if (lf_input_index != -1) {
attribute_set_propagation_map_.add(bsocket, &lf_node.input(lf_input_index));
}
}
}
lf_graph_info_->functions.append(std::move(lazy_function));