Refactoring: Corrections and unifications in mathematics vfont gizmos #107193
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,15 +64,15 @@ class GeoNodeExecParams {
|
|||
const bNode &node_;
|
||||
lf::Params ¶ms_;
|
||||
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 ¶ms,
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue