Transform Volume rewrite/refactor

Take advantage of the efficiency provided by the snap_context.

Also fixes errors:
- volume snap fails based on view angle (T48394).
- multiple instances of dupli-objects break volume calculation.
This commit is contained in:
Germano Cavalcante
2016-05-11 19:59:02 +10:00
committed by Campbell Barton
parent 4a135ad2b7
commit 67d2de8828
7 changed files with 466 additions and 613 deletions

View File

@@ -82,7 +82,6 @@ typedef struct SK_Intersection {
typedef struct SK_Sketch {
ListBase strokes;
ListBase depth_peels;
SK_Stroke *active_stroke;
SK_Stroke *gesture;
SK_Point next_point;

View File

@@ -50,8 +50,6 @@ void freeSketch(SK_Sketch *sketch)
sk_freeStroke(stk);
}
BLI_freelistN(&sketch->depth_peels);
MEM_freeN(sketch);
}

View File

@@ -970,100 +970,30 @@ static int sk_getStrokeSnapPoint(bContext *C, SK_Point *pt, SK_Sketch *sketch, S
ToolSettings *ts = CTX_data_tool_settings(C);
int point_added = 0;
struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d(
CTX_data_main(C), CTX_data_scene(C), 0,
CTX_wm_region(C), CTX_wm_view3d(C));
float mvalf[2] = {UNPACK2(dd->mval)};
float r_loc[3], dummy_no[3];
if (ts->snap_mode == SCE_SNAP_MODE_VOLUME) {
DepthPeel *p1, *p2;
float *last_p = NULL;
float dist = FLT_MAX;
float p[3] = {0};
float size = 0;
float mvalf[2];
BLI_freelistN(&sketch->depth_peels);
BLI_listbase_clear(&sketch->depth_peels);
mvalf[0] = dd->mval[0];
mvalf[1] = dd->mval[1];
peelObjectsContext(C, mvalf, SNAP_ALL, &sketch->depth_peels);
if (stk->nb_points > 0 && stk->points[stk->nb_points - 1].type == PT_CONTINUOUS) {
last_p = stk->points[stk->nb_points - 1].p;
}
else if (LAST_SNAP_POINT_VALID) {
last_p = LAST_SNAP_POINT;
}
for (p1 = sketch->depth_peels.first; p1; p1 = p1->next) {
if (p1->flag == 0) {
float vec[3];
float new_dist;
float new_size = 0;
p2 = NULL;
p1->flag = 1;
/* if peeling objects, take the first and last from each object */
if (ts->snap_flag & SCE_SNAP_PEEL_OBJECT) {
DepthPeel *peel;
for (peel = p1->next; peel; peel = peel->next) {
if (peel->ob == p1->ob) {
peel->flag = 1;
p2 = peel;
}
}
}
/* otherwise, pair first with second and so on */
else {
for (p2 = p1->next; p2 && p2->ob != p1->ob; p2 = p2->next) {
/* nothing to do here */
}
}
if (p2) {
p2->flag = 1;
add_v3_v3v3(vec, p1->p, p2->p);
mul_v3_fl(vec, 0.5f);
new_size = len_v3v3(p1->p, p2->p);
}
else {
copy_v3_v3(vec, p1->p);
}
if (last_p == NULL) {
copy_v3_v3(p, vec);
size = new_size;
dist = 0;
break;
}
new_dist = len_v3v3(last_p, vec);
if (new_dist < dist) {
copy_v3_v3(p, vec);
dist = new_dist;
size = new_size;
}
}
}
if (dist != FLT_MAX) {
float size;
if (peelObjectsSnapContext(
snap_context, mvalf, SNAP_ALL,
(ts->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0,
r_loc, dummy_no, &size))
{
pt->type = dd->type;
pt->mode = PT_SNAP;
pt->size = size / 2;
copy_v3_v3(pt->p, p);
copy_v3_v3(pt->p, r_loc);
point_added = 1;
}
//BLI_freelistN(&depth_peels);
}
else {
SK_Stroke *snap_stk;
float vec[3];
float no[3];
float mval[2];
int found = 0;
float dist_px = SNAP_MIN_DISTANCE; // Use a user defined value here
/* snap to strokes */
@@ -1082,37 +1012,28 @@ static int sk_getStrokeSnapPoint(bContext *C, SK_Point *pt, SK_Sketch *sketch, S
point_added = 1;
}
}
mval[0] = dd->mval[0];
mval[1] = dd->mval[1];
/* try to snap to closer object */
{
struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d(
CTX_data_main(C), CTX_data_scene(C), 0,
CTX_wm_region(C), CTX_wm_view3d(C));
found = ED_transform_snap_object_project_view3d(
if (ED_transform_snap_object_project_view3d(
snap_context,
&(const struct SnapObjectParams){
.snap_select = SNAP_NOT_SELECTED,
.snap_to = ts->snap_mode,
},
mval, &dist_px, NULL,
vec, no);
mvalf, &dist_px, NULL,
r_loc, dummy_no))
{
pt->type = dd->type;
pt->mode = PT_SNAP;
copy_v3_v3(pt->p, r_loc);
ED_transform_snap_object_context_destroy(snap_context);
}
if (found == 1) {
pt->type = dd->type;
pt->mode = PT_SNAP;
copy_v3_v3(pt->p, vec);
point_added = 1;
point_added = 1;
}
}
}
ED_transform_snap_object_context_destroy(snap_context);
return point_added;
}

View File

@@ -158,19 +158,6 @@ void BIF_draw_manipulator(const struct bContext *C);
/* Snapping */
typedef struct DepthPeel {
struct DepthPeel *next, *prev;
float depth;
float p[3];
float no[3];
struct Object *ob;
int flag;
} DepthPeel;
struct ListBase;
typedef enum SnapSelect {
SNAP_ALL = 0,
SNAP_NOT_SELECTED = 1,
@@ -179,14 +166,18 @@ typedef enum SnapSelect {
#define SNAP_MIN_DISTANCE 30
bool peelObjectsTransForm(
struct TransInfo *t, const float mval[2], SnapSelect snap_select,
bool peelObjectsTransform(
struct TransInfo *t, const float mval[2],
SnapSelect snap_select, bool use_peel_object,
/* return args */
struct ListBase *r_depth_peels);
bool peelObjectsContext(
struct bContext *C, const float mval[2], SnapSelect snap_select,
float r_loc[3], float r_no[3], float *r_thickness);
bool peelObjectsSnapContext(
struct SnapObjectContext *sctx,
const float mval[2],
SnapSelect snap_select, bool use_peel_object,
/* return args */
struct ListBase *r_depth_peels);
float r_loc[3], float r_no[3], float *r_thickness);
bool snapObjectsTransform(
struct TransInfo *t, const float mval[2], SnapSelect snap_select,
float *dist_px,

View File

@@ -29,6 +29,7 @@ struct BMVert;
struct BMEdge;
struct BMFace;
struct ListBase;
struct Scene;
struct Main;
struct Object;
@@ -38,6 +39,24 @@ struct View3D;
/* transform_snap_object.c */
/* ED_transform_snap_object_*** API */
/** used for storing multiple hits */
struct SnapObjectHitDepth {
struct SnapObjectHitDepth *next, *prev;
float depth;
float co[3];
float no[3];
int index;
struct Object *ob;
float obmat[4][4];
/* needed to tell which ray-cast this was part of,
* the same object may be part of many ray-casts when dupli's are used. */
unsigned int ob_uuid;
};
struct SnapObjectParams {
int snap_select; /* SnapSelect */
union {
@@ -81,9 +100,16 @@ bool ED_transform_snap_object_project_ray_ex(
struct Object **r_ob, float r_obmat[4][4]);
bool ED_transform_snap_object_project_ray(
SnapObjectContext *sctx,
const float ray_origin[3], const float ray_direction[3], float *ray_dist,
const float ray_origin[3], const float ray_direction[3], float *ray_depth,
float r_co[3], float r_no[3]);
bool ED_transform_snap_object_project_ray_all(
SnapObjectContext *sctx,
const struct SnapObjectParams *params,
const float ray_start[3], const float ray_normal[3],
float ray_depth, bool sort,
struct ListBase *r_hit_list);
bool ED_transform_snap_object_project_view3d_ex(
struct SnapObjectContext *sctx,
const struct SnapObjectParams *params,
@@ -104,4 +130,11 @@ bool ED_transform_snap_object_project_view3d_mixed(
bool use_depth,
float r_co[3], float r_no[3]);
bool ED_transform_snap_object_project_all_view3d_ex(
SnapObjectContext *sctx,
const struct SnapObjectParams *params,
const float mval[2],
float ray_depth, bool sort,
ListBase *r_hit_list);
#endif /* __ED_TRANSFORM_SNAP_OBJECT_CONTEXT_H__ */

View File

@@ -963,86 +963,10 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec))
mval[1] = t->mval[1];
if (t->tsnap.mode == SCE_SNAP_MODE_VOLUME) {
ListBase depth_peels;
DepthPeel *p1, *p2;
const float *last_p = NULL;
float max_dist = FLT_MAX;
float p[3] = {0.0f, 0.0f, 0.0f};
BLI_listbase_clear(&depth_peels);
peelObjectsTransForm(t, mval, t->tsnap.modeSelect, &depth_peels);
// if (LAST_SNAP_POINT_VALID)
// {
// last_p = LAST_SNAP_POINT;
// }
// else
{
last_p = t->tsnap.snapPoint;
}
for (p1 = depth_peels.first; p1; p1 = p1->next) {
if (p1->flag == 0) {
float vec[3];
float new_dist;
p2 = NULL;
p1->flag = 1;
/* if peeling objects, take the first and last from each object */
if (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) {
DepthPeel *peel;
for (peel = p1->next; peel; peel = peel->next) {
if (peel->ob == p1->ob) {
peel->flag = 1;
p2 = peel;
}
}
}
/* otherwise, pair first with second and so on */
else {
for (p2 = p1->next; p2 && p2->ob != p1->ob; p2 = p2->next) {
/* nothing to do here */
}
}
if (p2) {
p2->flag = 1;
add_v3_v3v3(vec, p1->p, p2->p);
mul_v3_fl(vec, 0.5f);
}
else {
copy_v3_v3(vec, p1->p);
}
if (last_p == NULL) {
copy_v3_v3(p, vec);
max_dist = 0;
break;
}
new_dist = len_squared_v3v3(last_p, vec);
if (new_dist < max_dist) {
copy_v3_v3(p, vec);
max_dist = new_dist;
}
}
}
if (max_dist != FLT_MAX) {
copy_v3_v3(loc, p);
/* XXX, is there a correct normal in this case ???, for now just z up */
no[0] = 0.0;
no[1] = 0.0;
no[2] = 1.0;
found = true;
}
BLI_freelistN(&depth_peels);
found = peelObjectsTransform(
t, mval, t->tsnap.modeSelect,
(t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0,
loc, no, NULL);
}
else {
zero_v3(no); /* objects won't set this */
@@ -1302,347 +1226,87 @@ bool snapObjectsTransform(
/******************** PEELING *********************************/
static int cmpPeel(const void *arg1, const void *arg2)
bool peelObjectsSnapContext(
SnapObjectContext *sctx,
const float mval[2], SnapSelect snap_select, bool use_peel_object,
/* return args */
float r_loc[3], float r_no[3], float *r_thickness)
{
const DepthPeel *p1 = arg1;
const DepthPeel *p2 = arg2;
int val = 0;
if (p1->depth < p2->depth) {
val = -1;
}
else if (p1->depth > p2->depth) {
val = 1;
}
return val;
}
ListBase depths_peel = {0};
ED_transform_snap_object_project_all_view3d_ex(
sctx,
&(const struct SnapObjectParams){
.snap_to = SCE_SNAP_MODE_FACE,
.snap_select = snap_select,
.use_object_edit = true,
},
mval, -1.0f, false,
&depths_peel);
static void removeDoublesPeel(ListBase *depth_peels)
{
DepthPeel *peel;
for (peel = depth_peels->first; peel; peel = peel->next) {
DepthPeel *next_peel = peel->next;
if (next_peel && fabsf(peel->depth - next_peel->depth) < 0.0015f) {
peel->next = next_peel->next;
if (next_peel->next) {
next_peel->next->prev = peel;
}
MEM_freeN(next_peel);
}
}
}
static void addDepthPeel(ListBase *depth_peels, float depth, float p[3], float no[3], Object *ob)
{
DepthPeel *peel = MEM_callocN(sizeof(DepthPeel), "DepthPeel");
peel->depth = depth;
peel->ob = ob;
copy_v3_v3(peel->p, p);
copy_v3_v3(peel->no, no);
BLI_addtail(depth_peels, peel);
peel->flag = 0;
}
struct PeelRayCast_Data {
BVHTreeFromMesh bvhdata;
/* internal vars for adding peel */
Object *ob;
const float (*obmat)[4];
const float (*timat)[3];
const float *ray_start; /* globalspace */
const MLoopTri *looptri;
const float (*polynors)[3]; /* optional, can be NULL */
/* output list */
ListBase *depth_peels;
};
struct PeelEditMeshRayCast_Data {
BVHTreeFromEditMesh bvhdata;
/* internal vars for adding peel */
Object *ob;
const float(*obmat)[4];
const float(*timat)[3];
const float *ray_start; /* globalspace */
/* output list */
ListBase *depth_peels;
};
static void peelRayCast_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
{
struct PeelRayCast_Data *data = userdata;
data->bvhdata.raycast_callback(&data->bvhdata, index, ray, hit);
if (hit->index != -1) {
/* get all values in worldspace */
float location[3], normal[3];
float depth;
/* worldspace location */
mul_v3_m4v3(location, (float (*)[4])data->obmat, hit->co);
depth = len_v3v3(location, data->ray_start);
/* worldspace normal */
copy_v3_v3(normal, data->polynors ? data->polynors[data->looptri[hit->index].poly] : hit->no);
mul_m3_v3((float (*)[3])data->timat, normal);
normalize_v3(normal);
addDepthPeel(data->depth_peels, depth, location, normal, data->ob);
}
}
static void peelEditMeshRayCast_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
{
struct PeelEditMeshRayCast_Data *data = userdata;
data->bvhdata.raycast_callback(&data->bvhdata, index, ray, hit);
if (hit->index != -1) {
/* get all values in worldspace */
float location[3], normal[3];
float depth;
/* worldspace location */
mul_v3_m4v3(location, (float(*)[4])data->obmat, hit->co);
depth = len_v3v3(location, data->ray_start);
/* worldspace normal */
copy_v3_v3(normal, hit->no);
mul_m3_v3((float(*)[3])data->timat, normal);
normalize_v3(normal);
addDepthPeel(data->depth_peels, depth, location, normal, data->ob);
}
}
static bool peelDerivedMesh(
Object *ob, DerivedMesh *dm, float obmat[4][4],
const float ray_start[3], const float ray_normal[3], const float UNUSED(mval[2]),
ListBase *depth_peels)
{
bool retval = false;
int totvert = dm->getNumVerts(dm);
if (totvert > 0) {
const MLoopTri *looptri = dm->getLoopTriArray(dm);
const int looptri_num = dm->getNumLoopTri(dm);
float imat[4][4];
float timat[3][3]; /* transpose inverse matrix for normals */
float ray_start_local[3], ray_normal_local[3];
bool test = true;
invert_m4_m4(imat, obmat);
transpose_m3_m4(timat, imat);
mul_v3_m4v3(ray_start_local, imat, ray_start);
mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal);
/* If number of vert is more than an arbitrary limit,
* test against boundbox first
* */
if (looptri_num > 16) {
BoundBox *bb = BKE_object_boundbox_get(ob);
if (bb) {
BoundBox bb_temp;
/* We cannot aford a bbox with some null dimension, which may happen in some cases...
* Threshold is rather high, but seems to be needed to get good behavior, see T46099. */
bb = BKE_boundbox_ensure_minimum_dimensions(bb, &bb_temp, 1e-1f);
test = BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, NULL);
if (!BLI_listbase_is_empty(&depths_peel)) {
/* At the moment we only use the hits of the first object */
struct SnapObjectHitDepth *hit_min = depths_peel.first;
for (struct SnapObjectHitDepth *iter = hit_min->next; iter; iter = iter->next) {
if (iter->depth < hit_min->depth) {
hit_min = iter;
}
}
if (test == true) {
struct PeelRayCast_Data data;
struct SnapObjectHitDepth *hit_max = NULL;
bvhtree_from_mesh_looptri(&data.bvhdata, dm, 0.0f, 4, 6);
if (data.bvhdata.tree != NULL) {
data.ob = ob;
data.obmat = (const float (*)[4])obmat;
data.timat = (const float (*)[3])timat;
data.ray_start = ray_start;
data.looptri = looptri;
data.polynors = dm->getPolyDataArray(dm, CD_NORMAL); /* can be NULL */
data.depth_peels = depth_peels;
BLI_bvhtree_ray_cast_all(
data.bvhdata.tree, ray_start_local, ray_normal_local, 0.0f, BVH_RAYCAST_DIST_MAX,
peelRayCast_cb, &data);
if (use_peel_object) {
/* if peeling objects, take the first and last from each object */
hit_max = hit_min;
for (struct SnapObjectHitDepth *iter = depths_peel.first; iter; iter = iter->next) {
if ((iter->depth > hit_max->depth) && (iter->ob_uuid == hit_min->ob_uuid)) {
hit_max = iter;
}
}
free_bvhtree_from_mesh(&data.bvhdata);
}
}
return retval;
}
static bool peelEditMesh(
Object *ob, BMEditMesh *em, float obmat[4][4],
const float ray_start[3], const float ray_normal[3],
ListBase *depth_peels)
{
bool retval = false;
int totvert = em->bm->totvert;
if (totvert > 0) {
float imat[4][4];
float timat[3][3]; /* transpose inverse matrix for normals */
float ray_start_local[3], ray_normal_local[3];
bool test = true;
invert_m4_m4(imat, obmat);
transpose_m3_m4(timat, imat);
mul_v3_m4v3(ray_start_local, imat, ray_start);
mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal);
if (test == true) {
struct PeelEditMeshRayCast_Data data;
BLI_bitmap *looptri_mask = BLI_BITMAP_NEW(em->tottri, __func__);
int looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface(
em->bm, looptri_mask,
BM_elem_cb_check_hflag_enabled_simple(BMFace *, (BM_ELEM_SELECT | BM_ELEM_HIDDEN)));
bvhtree_from_editmesh_looptri_ex(&data.bvhdata, em, looptri_mask, looptri_num_active, 0.0f, 4, 6);
MEM_freeN(looptri_mask);
if (data.bvhdata.tree != NULL) {
data.ob = ob;
data.obmat = (const float(*)[4])obmat;
data.timat = (const float(*)[3])timat;
data.ray_start = ray_start;
data.depth_peels = depth_peels;
BLI_bvhtree_ray_cast_all(
data.bvhdata.tree, ray_start_local, ray_normal_local, 0.0f, BVH_RAYCAST_DIST_MAX,
peelEditMeshRayCast_cb, &data);
}
free_bvhtree_from_editmesh(&data.bvhdata);
}
}
return retval;
}
static bool peelObjects(
Scene *scene, View3D *v3d, ARegion *ar, Object *obedit,
const float mval[2], SnapSelect snap_select,
ListBase *r_depth_peels)
{
Base *base;
bool retval = false;
float ray_start[3], ray_normal[3];
if (ED_view3d_win_to_ray(ar, v3d, mval, ray_start, ray_normal, true) == false) {
return false;
}
for (base = scene->base.first; base != NULL; base = base->next) {
if (BASE_SELECTABLE(v3d, base)) {
Object *ob = base->object;
if (ob->transflag & OB_DUPLI) {
DupliObject *dupli_ob;
ListBase *lb = object_duplilist(G.main->eval_ctx, scene, ob);
for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) {
Object *dob = dupli_ob->ob;
if (dob->type == OB_MESH) {
BMEditMesh *em;
bool val;
if (dob != obedit) {
DerivedMesh *dm;
dm = mesh_get_derived_final(scene, dob, CD_MASK_BAREMESH);
val = peelDerivedMesh(dob, dm, dob->obmat, ray_start, ray_normal, mval, r_depth_peels);
dm->release(dm);
}
else {
em = BKE_editmesh_from_object(dob);
val = peelEditMesh(dob, em, dob->obmat, ray_start, ray_normal, r_depth_peels);
}
retval = retval || val;
else {
/* otherwise, pair first with second and so on */
for (struct SnapObjectHitDepth *iter = depths_peel.first; iter; iter = iter->next) {
if ((iter != hit_min) && (iter->ob_uuid == hit_min->ob_uuid)) {
if (hit_max == NULL) {
hit_max = iter;
}
else if (iter->depth < hit_max->depth) {
hit_max = iter;
}
}
free_object_duplilist(lb);
}
if (ob->type == OB_MESH) {
bool val = false;
if (ob != obedit && ((snap_select == SNAP_NOT_SELECTED && (base->flag & (SELECT | BA_WAS_SEL)) == 0) || ELEM(snap_select, SNAP_ALL, SNAP_NOT_OBEDIT))) {
DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
val = peelDerivedMesh(ob, dm, ob->obmat, ray_start, ray_normal, mval, r_depth_peels);
dm->release(dm);
}
else if (ob == obedit && snap_select != SNAP_NOT_OBEDIT) {
BMEditMesh *em = BKE_editmesh_from_object(ob);
DerivedMesh *dm = editbmesh_get_derived_cage(scene, obedit, em, CD_MASK_BAREMESH);
val = peelDerivedMesh(ob, dm, ob->obmat, ray_start, ray_normal, mval, r_depth_peels);
dm->release(dm);
}
retval = retval || val;
/* in this case has only one hit. treat as raycast */
if (hit_max == NULL) {
hit_max = hit_min;
}
}
mid_v3_v3v3(r_loc, hit_min->co, hit_max->co);
if (r_thickness) {
*r_thickness = hit_max->depth - hit_min->depth;
}
/* XXX, is there a correct normal in this case ???, for now just z up */
r_no[0] = 0.0;
r_no[1] = 0.0;
r_no[2] = 1.0;
BLI_freelistN(&depths_peel);
return true;
}
BLI_listbase_sort(r_depth_peels, cmpPeel);
removeDoublesPeel(r_depth_peels);
return retval;
return false;
}
bool peelObjectsTransForm(
TransInfo *t, const float mval[2], SnapSelect snap_select,
ListBase *r_depth_peels)
bool peelObjectsTransform(
TransInfo *t,
const float mval[2], SnapSelect snap_select, bool use_peel_object,
/* return args */
float r_loc[3], float r_no[3], float *r_thickness)
{
return peelObjects(t->scene, t->view, t->ar, t->obedit, mval, snap_select, r_depth_peels);
}
bool peelObjectsContext(
bContext *C, const float mval[2], SnapSelect snap_select,
ListBase *r_depth_peels)
{
Scene *scene = CTX_data_scene(C);
ScrArea *sa = CTX_wm_area(C);
View3D *v3d = sa->spacedata.first;
ARegion *ar = CTX_wm_region(C);
Object *obedit = CTX_data_edit_object(C);
return peelObjects(scene, v3d, ar, obedit, mval, snap_select, r_depth_peels);
return peelObjectsSnapContext(
t->tsnap.object_context,
mval, snap_select, use_peel_object,
r_loc, r_no, r_thickness);
}
/******************** NODES ***********************************/

View File

@@ -34,6 +34,7 @@
#include "BLI_memarena.h"
#include "BLI_ghash.h"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "DNA_armature_types.h"
@@ -109,6 +110,111 @@ struct SnapObjectContext {
};
static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt);
/* -------------------------------------------------------------------- */
/** \name Support for storing all depths, not just the first (raycast 'all')
*
* This uses a list of #SnapObjectHitDepth structs.
*
* \{ */
/* Store all ray-hits */
struct RayCastAll_Data {
void *bvhdata;
/* internal vars for adding depths */
BVHTree_RayCastCallback raycast_callback;
const float(*obmat)[4];
const float(*timat)[3];
float len_diff;
float local_scale;
Object *ob;
unsigned int ob_uuid;
/* DerivedMesh only */
DerivedMesh *dm;
const struct MLoopTri *dm_looptri;
/* output data */
ListBase *hit_list;
bool retval;
};
static struct SnapObjectHitDepth *hit_depth_create(
const float depth, const float co[3], const float no[3], int index,
Object *ob, const float obmat[4][4], unsigned int ob_uuid)
{
struct SnapObjectHitDepth *hit = MEM_mallocN(sizeof(*hit), __func__);
hit->depth = depth;
copy_v3_v3(hit->co, co);
copy_v3_v3(hit->no, no);
hit->index = index;
hit->ob = ob;
copy_m4_m4(hit->obmat, (float(*)[4])obmat);
hit->ob_uuid = ob_uuid;
return hit;
}
static int hit_depth_cmp(const void *arg1, const void *arg2)
{
const struct SnapObjectHitDepth *h1 = arg1;
const struct SnapObjectHitDepth *h2 = arg2;
int val = 0;
if (h1->depth < h2->depth) {
val = -1;
}
else if (h1->depth > h2->depth) {
val = 1;
}
return val;
}
static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
{
struct RayCastAll_Data *data = userdata;
data->raycast_callback(data->bvhdata, index, ray, hit);
if (hit->index != -1) {
/* get all values in worldspace */
float location[3], normal[3];
float depth;
/* worldspace location */
mul_v3_m4v3(location, (float(*)[4])data->obmat, hit->co);
depth = (hit->dist + data->len_diff) / data->local_scale;
/* worldspace normal */
copy_v3_v3(normal, hit->no);
mul_m3_v3((float(*)[3])data->timat, normal);
normalize_v3(normal);
/* currently unused, and causes issues when looptri's havn't been calculated.
* since theres some overhead in ensuring this data is valid, it may need to be optional. */
#if 0
if (data->dm) {
hit->index = dm_looptri_to_poly_index(data->dm, &data->dm_looptri[hit->index]);
}
#endif
struct SnapObjectHitDepth *hit_item = hit_depth_create(
depth, location, normal, hit->index,
data->ob, data->obmat, data->ob_uuid);
BLI_addtail(data->hit_list, hit_item);
}
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -572,8 +678,10 @@ static bool snapDerivedMesh(
SnapObjectContext *sctx,
Object *ob, DerivedMesh *dm, float obmat[4][4],
const float mval[2], float *dist_px, const short snap_to, bool do_bb,
const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth,
float r_loc[3], float r_no[3], int *r_index)
const float ray_start[3], const float ray_normal[3], const float ray_origin[3],
float *ray_depth, unsigned int ob_index,
float r_loc[3], float r_no[3], int *r_index,
ListBase *r_hit_list)
{
ARegion *ar = sctx->v3d_data.ar;
bool retval = false;
@@ -729,31 +837,55 @@ static bool snapDerivedMesh(
switch (snap_to) {
case SCE_SNAP_MODE_FACE:
{
BVHTreeRayHit hit;
if (r_hit_list) {
struct RayCastAll_Data data;
hit.index = -1;
hit.dist = local_depth;
data.bvhdata = treedata;
data.raycast_callback = treedata->raycast_callback;
data.obmat = obmat;
data.timat = timat;
data.len_diff = len_diff;
data.local_scale = local_scale;
data.ob = ob;
data.ob_uuid = ob_index,
data.dm = dm;
data.hit_list = r_hit_list;
data.retval = retval;
if (treedata->tree &&
BLI_bvhtree_ray_cast(treedata->tree, ray_start_local, ray_normal_local, 0.0f,
&hit, treedata->raycast_callback, treedata) != -1)
{
hit.dist += len_diff;
hit.dist /= local_scale;
if (hit.dist <= *ray_depth) {
*ray_depth = hit.dist;
copy_v3_v3(r_loc, hit.co);
copy_v3_v3(r_no, hit.no);
BLI_bvhtree_ray_cast_all(
treedata->tree, ray_start_local, ray_normal_local, 0.0f,
*ray_depth, raycast_all_cb, &data);
/* back to worldspace */
mul_m4_v3(obmat, r_loc);
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
retval = data.retval;
}
else {
BVHTreeRayHit hit;
retval = true;
hit.index = -1;
hit.dist = local_depth;
if (r_index) {
*r_index = dm_looptri_to_poly_index(dm, &treedata->looptri[hit.index]);
if (treedata->tree &&
BLI_bvhtree_ray_cast(
treedata->tree, ray_start_local, ray_normal_local, 0.0f,
&hit, treedata->raycast_callback, treedata) != -1)
{
hit.dist += len_diff;
hit.dist /= local_scale;
if (hit.dist <= *ray_depth) {
*ray_depth = hit.dist;
copy_v3_v3(r_loc, hit.co);
copy_v3_v3(r_no, hit.no);
/* back to worldspace */
mul_m4_v3(obmat, r_loc);
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
retval = true;
if (r_index) {
*r_index = dm_looptri_to_poly_index(dm, &treedata->looptri[hit.index]);
}
}
}
}
@@ -814,8 +946,10 @@ static bool snapEditMesh(
SnapObjectContext *sctx,
Object *ob, BMEditMesh *em, float obmat[4][4],
const float mval[2], float *dist_px, const short snap_to,
const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth,
float r_loc[3], float r_no[3], int *r_index)
const float ray_start[3], const float ray_normal[3], const float ray_origin[3],
float *ray_depth, const unsigned int ob_index,
float r_loc[3], float r_no[3], int *r_index,
ListBase *r_hit_list)
{
ARegion *ar = sctx->v3d_data.ar;
bool retval = false;
@@ -951,7 +1085,7 @@ static bool snapEditMesh(
*/
len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */
madd_v3_v3v3fl(ray_start_local, ray_org_local, ray_normal_local,
len_diff - len_v3v3(ray_start_local, ray_org_local));
len_diff - len_v3v3(ray_start_local, ray_org_local));
local_depth -= len_diff;
}
else {
@@ -961,32 +1095,55 @@ static bool snapEditMesh(
switch (snap_to) {
case SCE_SNAP_MODE_FACE:
{
BVHTreeRayHit hit;
if (r_hit_list) {
struct RayCastAll_Data data;
hit.index = -1;
hit.dist = local_depth;
data.bvhdata = treedata;
data.raycast_callback = treedata->raycast_callback;
data.obmat = obmat;
data.timat = timat;
data.len_diff = len_diff;
data.local_scale = local_scale;
data.ob = ob;
data.ob_uuid = ob_index;
data.dm = NULL;
data.hit_list = r_hit_list;
data.retval = retval;
if (treedata->tree &&
BLI_bvhtree_ray_cast(
treedata->tree, ray_start_local, ray_normal_local, 0.0f,
&hit, treedata->raycast_callback, treedata) != -1)
{
hit.dist += len_diff;
hit.dist /= local_scale;
if (hit.dist <= *ray_depth) {
*ray_depth = hit.dist;
copy_v3_v3(r_loc, hit.co);
copy_v3_v3(r_no, hit.no);
BLI_bvhtree_ray_cast_all(
treedata->tree, ray_start_local, ray_normal_local, 0.0f,
*ray_depth, raycast_all_cb, &data);
/* back to worldspace */
mul_m4_v3(obmat, r_loc);
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
retval = data.retval;
}
else {
BVHTreeRayHit hit;
retval = true;
hit.index = -1;
hit.dist = local_depth;
if (r_index) {
*r_index = hit.index;
if (treedata->tree &&
BLI_bvhtree_ray_cast(
treedata->tree, ray_start_local, ray_normal_local, 0.0f,
&hit, treedata->raycast_callback, treedata) != -1)
{
hit.dist += len_diff;
hit.dist /= local_scale;
if (hit.dist <= *ray_depth) {
*ray_depth = hit.dist;
copy_v3_v3(r_loc, hit.co);
copy_v3_v3(r_no, hit.no);
/* back to worldspace */
mul_m4_v3(obmat, r_loc);
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
retval = true;
if (r_index) {
*r_index = hit.index;
}
}
}
}
@@ -1050,11 +1207,13 @@ static bool snapEditMesh(
static bool snapObject(
SnapObjectContext *sctx,
Object *ob, float obmat[4][4], bool use_obedit, const short snap_to,
const float mval[2], float *dist_px,
const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth,
const float mval[2], float *dist_px, const unsigned int ob_index,
const float ray_start[3], const float ray_normal[3], const float ray_origin[3],
float *ray_depth,
/* return args */
float r_loc[3], float r_no[3], int *r_index,
Object **r_ob, float r_obmat[4][4])
Object **r_ob, float r_obmat[4][4],
ListBase *r_hit_list)
{
ARegion *ar = sctx->v3d_data.ar;
bool retval = false;
@@ -1066,8 +1225,10 @@ static bool snapObject(
em = BKE_editmesh_from_object(ob);
retval = snapEditMesh(
sctx, ob, em, obmat, mval, dist_px, snap_to,
ray_start, ray_normal, ray_origin, ray_depth,
r_loc, r_no, r_index);
ray_start, ray_normal, ray_origin,
ray_depth, ob_index,
r_loc, r_no, r_index,
r_hit_list);
}
else {
/* in this case we want the mesh from the editmesh, avoids stale data. see: T45978.
@@ -1082,8 +1243,9 @@ static bool snapObject(
}
retval = snapDerivedMesh(
sctx, ob, dm, obmat, mval, dist_px, snap_to, true,
ray_start, ray_normal, ray_origin, ray_depth,
r_loc, r_no, r_index);
ray_start, ray_normal, ray_origin,
ray_depth, ob_index,
r_loc, r_no, r_index, r_hit_list);
dm->release(dm);
}
@@ -1132,19 +1294,22 @@ static bool snapObjectsRay(
const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth,
/* return args */
float r_loc[3], float r_no[3], int *r_index,
Object **r_ob, float r_obmat[4][4])
Object **r_ob, float r_obmat[4][4],
ListBase *r_hit_list)
{
Base *base;
bool retval = false;
bool snap_obedit_first = snap_select == SNAP_ALL && obedit;
unsigned int ob_index = 0;
if (snap_select == SNAP_ALL && obedit) {
if (snap_obedit_first) {
Object *ob = obedit;
retval |= snapObject(
sctx, ob, ob->obmat, true, snap_to,
mval, dist_px,
mval, dist_px, ob_index++,
ray_start, ray_normal, ray_origin, ray_depth,
r_loc, r_no, r_index, r_ob, r_obmat);
r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list);
}
/* Need an exception for particle edit because the base is flagged with BA_HAS_RECALC_DATA
@@ -1157,9 +1322,9 @@ static bool snapObjectsRay(
Object *ob = base->object;
retval |= snapObject(
sctx, ob, ob->obmat, false, snap_to,
mval, dist_px,
mval, dist_px, ob_index++,
ray_start, ray_normal, ray_origin, ray_depth,
r_loc, r_no, r_index, r_ob, r_obmat);
r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list);
}
for (base = sctx->scene->base.first; base != NULL; base = base->next) {
@@ -1173,12 +1338,6 @@ static bool snapObjectsRay(
Object *ob_snap = ob;
bool use_obedit = false;
/* for linked objects, use the same object but a different matrix */
if (obedit && ob->data == obedit->data) {
use_obedit = true;
ob_snap = obedit;
}
if (ob->transflag & OB_DUPLI) {
DupliObject *dupli_ob;
ListBase *lb = object_duplilist(sctx->bmain->eval_ctx, sctx->scene, ob);
@@ -1189,19 +1348,33 @@ static bool snapObjectsRay(
retval |= snapObject(
sctx, dupli_snap, dupli_ob->mat, use_obedit_dupli, snap_to,
mval, dist_px,
mval, dist_px, ob_index++,
ray_start, ray_normal, ray_origin, ray_depth,
r_loc, r_no, r_index, r_ob, r_obmat);
r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list);
}
free_object_duplilist(lb);
}
if (obedit) {
if ((ob == obedit) &&
(snap_obedit_first || (snap_select == SNAP_NOT_OBEDIT)))
{
continue;
}
if (ob->data == obedit->data) {
/* for linked objects, use the same object but a different matrix */
use_obedit = true;
ob_snap = obedit;
}
}
retval |= snapObject(
sctx, ob_snap, ob->obmat, use_obedit, snap_to,
mval, dist_px,
mval, dist_px, ob_index++,
ray_start, ray_normal, ray_origin, ray_depth,
r_loc, r_no, r_index, r_ob, r_obmat);
r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list);
}
}
@@ -1315,7 +1488,53 @@ bool ED_transform_snap_object_project_ray_ex(
base_act, obedit,
ray_start, ray_normal, ray_start, ray_depth,
r_loc, r_no, r_index,
r_ob, r_obmat);
r_ob, r_obmat, NULL);
}
/**
* Fill in a list of all hits.
*
* \param ray_depth: Only depths in this range are considered, -1.0 for maximum.
* \param sort: Optionally sort the hits by depth.
* \param r_hit_list: List of #SnapObjectHitDepth (caller must free).
*/
bool ED_transform_snap_object_project_ray_all(
SnapObjectContext *sctx,
const struct SnapObjectParams *params,
const float ray_start[3], const float ray_normal[3],
float ray_depth, bool sort,
ListBase *r_hit_list)
{
Base *base_act = params->use_object_active ? sctx->scene->basact : NULL;
Object *obedit = params->use_object_edit ? sctx->scene->obedit : NULL;
if (ray_depth == -1.0f) {
ray_depth = BVH_RAYCAST_DIST_MAX;
}
#ifdef DEBUG
float ray_depth_prev = ray_depth;
#endif
bool retval = snapObjectsRay(
sctx,
params->snap_select, params->snap_to,
NULL, NULL,
base_act, obedit,
ray_start, ray_normal, ray_start, &ray_depth,
NULL, NULL, NULL, NULL, NULL,
r_hit_list);
/* meant to be readonly for 'all' hits, ensure it is */
#ifdef DEBUG
BLI_assert(ray_depth_prev == ray_depth);
#endif
if (sort) {
BLI_listbase_sort(r_hit_list, hit_depth_cmp);
}
return retval;
}
/**
@@ -1327,7 +1546,7 @@ bool ED_transform_snap_object_project_ray_ex(
*/
static bool transform_snap_context_project_ray_impl(
SnapObjectContext *sctx,
const float ray_start[3], const float ray_normal[3], float *ray_dist,
const float ray_start[3], const float ray_normal[3], float *ray_depth,
float r_co[3], float r_no[3])
{
bool ret;
@@ -1340,7 +1559,7 @@ static bool transform_snap_context_project_ray_impl(
.snap_to = SCE_SNAP_MODE_FACE,
.use_object_edit = (sctx->scene->obedit != NULL),
},
ray_start, ray_normal, ray_dist,
ray_start, ray_normal, ray_depth,
r_co, r_no, NULL,
NULL, NULL);
@@ -1349,13 +1568,13 @@ static bool transform_snap_context_project_ray_impl(
bool ED_transform_snap_object_project_ray(
SnapObjectContext *sctx,
const float ray_origin[3], const float ray_direction[3], float *ray_dist,
const float ray_origin[3], const float ray_direction[3], float *ray_depth,
float r_co[3], float r_no[3])
{
float ray_dist_fallback;
if (ray_dist == NULL) {
ray_dist_fallback = BVH_RAYCAST_DIST_MAX;
ray_dist = &ray_dist_fallback;
float ray_depth_fallback;
if (ray_depth == NULL) {
ray_depth_fallback = BVH_RAYCAST_DIST_MAX;
ray_depth = &ray_depth_fallback;
}
float no_fallback[3];
@@ -1365,7 +1584,7 @@ bool ED_transform_snap_object_project_ray(
return transform_snap_context_project_ray_impl(
sctx,
ray_origin, ray_direction, ray_dist,
ray_origin, ray_direction, ray_depth,
r_co, r_no);
}
@@ -1376,7 +1595,7 @@ static bool transform_snap_context_project_view3d_mixed_impl(
bool use_depth,
float r_co[3], float r_no[3])
{
float ray_dist = BVH_RAYCAST_DIST_MAX;
float ray_depth = BVH_RAYCAST_DIST_MAX;
bool is_hit = false;
float r_no_dummy[3];
@@ -1394,7 +1613,7 @@ static bool transform_snap_context_project_view3d_mixed_impl(
for (int i = 0; i < 3; i++) {
if ((params->snap_to_flag & (1 << i)) && (is_hit == false || use_depth)) {
if (use_depth == false) {
ray_dist = BVH_RAYCAST_DIST_MAX;
ray_depth = BVH_RAYCAST_DIST_MAX;
}
params_temp.snap_to = elem_type[i];
@@ -1402,7 +1621,7 @@ static bool transform_snap_context_project_view3d_mixed_impl(
if (ED_transform_snap_object_project_view3d(
sctx,
&params_temp,
mval, dist_px, &ray_dist,
mval, dist_px, &ray_depth,
r_co, r_no))
{
is_hit = true;
@@ -1470,7 +1689,7 @@ bool ED_transform_snap_object_project_view3d_ex(
mval, dist_px,
base_act, obedit,
ray_start, ray_normal, ray_orgigin, ray_depth,
r_loc, r_no, r_index, NULL, NULL);
r_loc, r_no, r_index, NULL, NULL, NULL);
}
bool ED_transform_snap_object_project_view3d(
@@ -1488,4 +1707,32 @@ bool ED_transform_snap_object_project_view3d(
r_loc, r_no, NULL);
}
/**
* see: #ED_transform_snap_object_project_ray_all
*/
bool ED_transform_snap_object_project_all_view3d_ex(
SnapObjectContext *sctx,
const struct SnapObjectParams *params,
const float mval[2],
float ray_depth, bool sort,
ListBase *r_hit_list)
{
float ray_start[3], ray_normal[3];
BLI_assert(params->snap_to == SCE_SNAP_MODE_FACE);
if (!ED_view3d_win_to_ray_ex(
sctx->v3d_data.ar, sctx->v3d_data.v3d,
mval, NULL, ray_normal, ray_start, true))
{
return false;
}
return ED_transform_snap_object_project_ray_all(
sctx,
params,
ray_start, ray_normal, ray_depth, sort,
r_hit_list);
}
/** \} */