/* create intersection coordinates in view Z direction at mouse coordinates */ void ED_view3d_win_to_segment_clip(ARegion *ar, View3D *v3d, const float mval[2], float ray_start[3], float ray_end[3]) { RegionView3D *rv3d= ar->regiondata; if(rv3d->is_persp) { float vec[3]; ED_view3d_win_to_vector(ar, mval, vec); copy_v3_v3(ray_start, rv3d->viewinv[3]); madd_v3_v3v3fl(ray_start, rv3d->viewinv[3], vec, v3d->near); madd_v3_v3v3fl(ray_end, rv3d->viewinv[3], vec, v3d->far); } else { float vec[4]; vec[0] = 2.0f * mval[0] / ar->winx - 1; vec[1] = 2.0f * mval[1] / ar->winy - 1; vec[2] = 0.0f; vec[3] = 1.0f; mul_m4_v4(rv3d->persinv, vec); madd_v3_v3v3fl(ray_start, vec, rv3d->viewinv[2], 1000.0f); madd_v3_v3v3fl(ray_end, vec, rv3d->viewinv[2], -1000.0f); } /* clipping */ if(rv3d->rflag & RV3D_CLIPPING) { int a; for(a=0; a<4; a++) { clip_line_plane(ray_start, ray_end, rv3d->clip[a]); } } }
void ED_view3d_win_to_segment(ARegion *ar, View3D *v3d, const float mval[2], float ray_start[3], float ray_end[3]) { RegionView3D *rv3d = ar->regiondata; if (rv3d->is_persp) { float vec[3]; ED_view3d_win_to_vector(ar, mval, vec); copy_v3_v3(ray_start, rv3d->viewinv[3]); madd_v3_v3v3fl(ray_start, rv3d->viewinv[3], vec, v3d->near); madd_v3_v3v3fl(ray_end, rv3d->viewinv[3], vec, v3d->far); } else { float vec[4]; vec[0] = 2.0f * mval[0] / ar->winx - 1; vec[1] = 2.0f * mval[1] / ar->winy - 1; vec[2] = 0.0f; vec[3] = 1.0f; mul_m4_v4(rv3d->persinv, vec); madd_v3_v3v3fl(ray_start, vec, rv3d->viewinv[2], 1000.0f); madd_v3_v3v3fl(ray_end, vec, rv3d->viewinv[2], -1000.0f); } }
static int bake_intersect_tree(RayObject *raytree, Isect *isect, float *start, float *dir, float sign, float *hitco, float *dist) { float maxdist; int hit; /* might be useful to make a user setting for maxsize*/ if (R.r.bake_maxdist > 0.0f) maxdist = R.r.bake_maxdist; else maxdist = RE_RAYTRACE_MAXDIST + R.r.bake_biasdist; /* 'dir' is always normalized */ madd_v3_v3v3fl(isect->start, start, dir, -R.r.bake_biasdist); mul_v3_v3fl(isect->dir, dir, sign); isect->dist = maxdist; hit = RE_rayobject_raycast(raytree, isect); if (hit) { madd_v3_v3v3fl(hitco, isect->start, isect->dir, isect->dist); *dist = isect->dist; } return hit; }
static void bmbvh_find_face_segment_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) { struct SegmentUserData *bmcb_data = userdata; const BMLoop **ltri = bmcb_data->looptris[index]; float dist, uv[2]; const float *tri_cos[3]; bmbvh_tri_from_face(tri_cos, ltri, bmcb_data->cos_cage); if (equals_v3v3(bmcb_data->co_a, tri_cos[0]) || equals_v3v3(bmcb_data->co_a, tri_cos[1]) || equals_v3v3(bmcb_data->co_a, tri_cos[2]) || equals_v3v3(bmcb_data->co_b, tri_cos[0]) || equals_v3v3(bmcb_data->co_b, tri_cos[1]) || equals_v3v3(bmcb_data->co_b, tri_cos[2])) { return; } if (isect_ray_tri_v3(ray->origin, ray->direction, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv) && (dist < hit->dist)) { hit->dist = dist; hit->index = index; copy_v3_v3(hit->no, ltri[0]->f->no); madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); copy_v2_v2(bmcb_data->uv, uv); } }
static void bmbvh_ray_cast_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) { struct RayCastUserData *bmcb_data = userdata; const BMLoop **ltri = bmcb_data->looptris[index]; float dist, uv[2]; const float *tri_cos[3]; bool isect; bmbvh_tri_from_face(tri_cos, ltri, bmcb_data->cos_cage); isect = (ray->radius > 0.0f ? isect_ray_tri_epsilon_v3(ray->origin, ray->direction, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv, ray->radius) : isect_ray_tri_v3(ray->origin, ray->direction, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv)); if (isect && dist < hit->dist) { hit->dist = dist; hit->index = index; copy_v3_v3(hit->no, ltri[0]->f->no); madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); copy_v2_v2(bmcb_data->uv, uv); } }
/* copy of function above */ static void mesh_looptri_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) { const BVHTreeFromMesh *data = (BVHTreeFromMesh *) userdata; const MVert *vert = data->vert; const MLoopTri *lt = &data->looptri[index]; const float *vtri_co[3] = { vert[data->loop[lt->tri[0]].v].co, vert[data->loop[lt->tri[1]].v].co, vert[data->loop[lt->tri[2]].v].co, }; float dist; if (data->sphere_radius == 0.0f) dist = bvhtree_ray_tri_intersection(ray, hit->dist, UNPACK3(vtri_co)); else dist = bvhtree_sphereray_tri_intersection(ray, data->sphere_radius, hit->dist, UNPACK3(vtri_co)); if (dist >= 0 && dist < hit->dist) { hit->index = index; hit->dist = dist; madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); normal_tri_v3(hit->no, UNPACK3(vtri_co)); } }
/* copy of function above (warning, should de-duplicate with editmesh_bvh.c) */ static void editmesh_faces_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) { const BVHTreeFromMesh *data = (BVHTreeFromMesh *) userdata; BMEditMesh *em = data->em_evil; const BMLoop **ltri = (const BMLoop **)em->looptris[index]; const float *t0, *t1, *t2; t0 = ltri[0]->v->co; t1 = ltri[1]->v->co; t2 = ltri[2]->v->co; { float dist; if (data->sphere_radius == 0.0f) dist = bvhtree_ray_tri_intersection(ray, hit->dist, t0, t1, t2); else dist = bvhtree_sphereray_tri_intersection(ray, data->sphere_radius, hit->dist, t0, t1, t2); if (dist >= 0 && dist < hit->dist) { hit->index = index; hit->dist = dist; madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); normal_tri_v3(hit->no, t0, t1, t2); } } }
float BLI_bvhtree_bb_raycast(float *bv, const float light_start[3], const float light_end[3], float pos[3]) { BVHRayCastData data; float dist = 0.0; data.hit.dist = FLT_MAX; // get light direction data.ray.direction[0] = light_end[0] - light_start[0]; data.ray.direction[1] = light_end[1] - light_start[1]; data.ray.direction[2] = light_end[2] - light_start[2]; data.ray.radius = 0.0; data.ray.origin[0] = light_start[0]; data.ray.origin[1] = light_start[1]; data.ray.origin[2] = light_start[2]; normalize_v3(data.ray.direction); copy_v3_v3(data.ray_dot_axis, data.ray.direction); dist = ray_nearest_hit(&data, bv); if (dist > 0.0f) { madd_v3_v3v3fl(pos, light_start, data.ray.direction, dist); } return dist; }
static void dfs_raycast(BVHRayCastData *data, BVHNode *node) { int i; //ray-bv is really fast.. and simple tests revealed its worth to test it //before calling the ray-primitive functions /* XXX: temporary solution for particles until fast_ray_nearest_hit supports ray.radius */ float dist = (data->ray.radius > 0.0f) ? ray_nearest_hit(data, node->bv) : fast_ray_nearest_hit(data, node); if (dist >= data->hit.dist) return; if (node->totnode == 0) { if (data->callback) { data->callback(data->userdata, node->index, &data->ray, &data->hit); } else { data->hit.index = node->index; data->hit.dist = dist; madd_v3_v3v3fl(data->hit.co, data->ray.origin, data->ray.direction, dist); } } else { //pick loop direction to dive into the tree (based on ray direction and split axis) if (data->ray_dot_axis[ (int)node->main_axis ] > 0.0f) { for (i=0; i != node->totnode; i++) { dfs_raycast(data, node->children[i]); } } else { for (i=node->totnode-1; i >= 0; i--) { dfs_raycast(data, node->children[i]); } } } }
/* Callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_faces. * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ static void mesh_faces_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) { const BVHTreeFromMesh *data = (BVHTreeFromMesh *) userdata; const MVert *vert = data->vert; const MFace *face = &data->face[index]; const float *t0, *t1, *t2, *t3; t0 = vert[face->v1].co; t1 = vert[face->v2].co; t2 = vert[face->v3].co; t3 = face->v4 ? vert[face->v4].co : NULL; do { float dist; if (data->sphere_radius == 0.0f) dist = bvhtree_ray_tri_intersection(ray, hit->dist, t0, t1, t2); else dist = bvhtree_sphereray_tri_intersection(ray, data->sphere_radius, hit->dist, t0, t1, t2); if (dist >= 0 && dist < hit->dist) { hit->index = index; hit->dist = dist; madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); normal_tri_v3(hit->no, t0, t1, t2); } t1 = t2; t2 = t3; t3 = NULL; } while (t2); }
static void iterative_raycast(BVHRayCastData *data, BVHNode *node) { while (node) { float dist = fast_ray_nearest_hit(data, node); if (dist >= data->hit.dist) { node = node->skip[1]; continue; } if (node->totnode == 0) { if (data->callback) data->callback(data->userdata, node->index, &data->ray, &data->hit); else { data->hit.index = node->index; data->hit.dist = dist; madd_v3_v3v3fl(data->hit.co, data->ray.origin, data->ray.direction, dist); } node = node->skip[1]; } else { node = node->children[0]; } } }
static int heat_ray_source_visible(LaplacianSystem *sys, int vertex, int source) { BVHTreeRayHit hit; BVHCallbackUserData data; const MLoopTri *lt; float end[3]; int visible; lt = sys->heat.vltree[vertex]; if (lt == NULL) return 1; data.sys = sys; copy_v3_v3(data.start, sys->heat.verts[vertex]); closest_to_line_segment_v3(end, data.start, sys->heat.root[source], sys->heat.tip[source]); sub_v3_v3v3(data.vec, end, data.start); madd_v3_v3v3fl(data.start, data.start, data.vec, 1e-5); mul_v3_fl(data.vec, 1.0f - 2e-5f); /* pass normalized vec + distance to bvh */ hit.index = -1; hit.dist = normalize_v3(data.vec); visible = BLI_bvhtree_ray_cast(sys->heat.bvhtree, data.start, data.vec, 0.0f, &hit, bvh_callback, (void *)&data) == -1; return visible; }
static void view3d_win_to_ray_segment( const ARegion *ar, const View3D *v3d, const float mval[2], float r_ray_co[3], float r_ray_dir[3], float r_ray_start[3], float r_ray_end[3]) { RegionView3D *rv3d = ar->regiondata; float _ray_co[3], _ray_dir[3], start_offset, end_offset; if (!r_ray_co) r_ray_co = _ray_co; if (!r_ray_dir) r_ray_dir = _ray_dir; ED_view3d_win_to_vector(ar, mval, r_ray_dir); if (rv3d->is_persp) { copy_v3_v3(r_ray_co, rv3d->viewinv[3]); } else { r_ray_co[0] = 2.0f * mval[0] / ar->winx - 1.0f; r_ray_co[1] = 2.0f * mval[1] / ar->winy - 1.0f; if (rv3d->persp == RV3D_CAMOB) { r_ray_co[2] = -1.0f; } else { r_ray_co[2] = 0.0f; } mul_project_m4_v3(rv3d->persinv, r_ray_co); } if ((rv3d->is_persp == false) && (rv3d->persp != RV3D_CAMOB)) { end_offset = v3d->far / 2.0f; start_offset = -end_offset; } else { ED_view3d_clip_range_get(v3d, rv3d, &start_offset, &end_offset, false); } if (r_ray_start) { madd_v3_v3v3fl(r_ray_start, r_ray_co, r_ray_dir, start_offset); } if (r_ray_end) { madd_v3_v3v3fl(r_ray_end, r_ray_co, r_ray_dir, end_offset); } }
/** ensure \a v1 is \a dist from \a v2 */ void dist_ensure_v3_v3fl(float v1[3], const float v2[3], const float dist) { if (!equals_v3v3(v2, v1)) { float nor[3]; sub_v3_v3v3(nor, v1, v2); normalize_v3(nor); madd_v3_v3v3fl(v1, v2, nor, dist); } }
void ED_armature_ebone_from_mat3(EditBone *ebone, float mat[3][3]) { float vec[3], roll; const float len = len_v3v3(ebone->head, ebone->tail); mat3_to_vec_roll(mat, vec, &roll); madd_v3_v3v3fl(ebone->tail, ebone->head, vec, len); ebone->roll = roll; }
/* use for mousemove events */ static bool view3d_ruler_item_mousemove(bContext *C, RulerInfo *ruler_info, const int mval[2], const bool do_thickness, const bool do_snap) { const float eps_bias = 0.0002f; RulerItem *ruler_item = ruler_item_active_get(ruler_info); ruler_info->snap_flag &= ~RULER_SNAP_OK; if (ruler_item) { float *co = ruler_item->co[ruler_item->co_index]; /* restore the initial depth */ copy_v3_v3(co, ruler_info->drag_start_co); view3d_ruler_item_project(ruler_info, co, mval); if (do_thickness && ruler_item->co_index != 1) { const float mval_fl[2] = {UNPACK2(mval)}; float ray_normal[3]; float ray_start[3]; float *co_other; co_other = ruler_item->co[ruler_item->co_index == 0 ? 2 : 0]; if (ED_view3d_snap_co(C, co, ray_normal, mval_fl, true, false, false, false, true)) { negate_v3(ray_normal); /* add some bias */ madd_v3_v3v3fl(ray_start, co, ray_normal, eps_bias); ED_view3d_snap_ray(C, co_other, ray_start, ray_normal); } } else if (do_snap) { const float mval_fl[2] = {UNPACK2(mval)}; View3D *v3d = CTX_wm_view3d(C); bool use_depth = (v3d->drawtype >= OB_SOLID); bool is_hit = ED_view3d_snap_co(C, co, NULL, mval_fl, use_depth, false, true, true, use_depth); if (is_hit) { ruler_info->snap_flag |= RULER_SNAP_OK; } } return true; } else { return false; } }
float bvhtree_sphereray_tri_intersection( const BVHTreeRay *ray, float radius, const float m_dist, const float v0[3], const float v1[3], const float v2[3]) { float idist; float p1[3]; float hit_point[3]; madd_v3_v3v3fl(p1, ray->origin, ray->direction, m_dist); if (isect_sweeping_sphere_tri_v3(ray->origin, p1, radius, v0, v1, v2, &idist, hit_point)) { return idist * m_dist; } return FLT_MAX; }
float effector_falloff(EffectorCache *eff, EffectorData *efd, EffectedPoint *UNUSED(point), EffectorWeights *weights) { float temp[3]; float falloff = weights ? weights->weight[0] * weights->weight[eff->pd->forcefield] : 1.0f; float fac, r_fac; fac = dot_v3v3(efd->nor, efd->vec_to_point2); if (eff->pd->zdir == PFIELD_Z_POS && fac < 0.0f) falloff=0.0f; else if (eff->pd->zdir == PFIELD_Z_NEG && fac > 0.0f) falloff=0.0f; else { switch (eff->pd->falloff) { case PFIELD_FALL_SPHERE: falloff*= falloff_func_dist(eff->pd, efd->distance); break; case PFIELD_FALL_TUBE: falloff*= falloff_func_dist(eff->pd, ABS(fac)); if (falloff == 0.0f) break; madd_v3_v3v3fl(temp, efd->vec_to_point, efd->nor, -fac); r_fac= len_v3(temp); falloff*= falloff_func_rad(eff->pd, r_fac); break; case PFIELD_FALL_CONE: falloff*= falloff_func_dist(eff->pd, ABS(fac)); if (falloff == 0.0f) break; r_fac= RAD2DEGF(saacos(fac/len_v3(efd->vec_to_point))); falloff*= falloff_func_rad(eff->pd, r_fac); break; } } return falloff; }
/** * Calculate a 3d location from 2d window coordinates. * \param ar The region (used for the window width and height). * \param depth_pt The reference location used to calculate the Z depth. * \param mval The area relative location (such as event->mval converted to floats). * \param out The resulting world-space location. */ void ED_view3d_win_to_3d(const ARegion *ar, const float depth_pt[3], const float mval[2], float out[3]) { RegionView3D *rv3d = ar->regiondata; float ray_origin[3]; float ray_direction[3]; float lambda; if (rv3d->is_persp) { float plane[4]; copy_v3_v3(ray_origin, rv3d->viewinv[3]); ED_view3d_win_to_vector(ar, mval, ray_direction); /* note, we could use isect_line_plane_v3() however we want the intersection to be infront of the * view no matter what, so apply the unsigned factor instead */ plane_from_point_normal_v3(plane, depth_pt, rv3d->viewinv[2]); isect_ray_plane_v3(ray_origin, ray_direction, plane, &lambda, false); lambda = fabsf(lambda); } else { float dx = (2.0f * mval[0] / (float)ar->winx) - 1.0f; float dy = (2.0f * mval[1] / (float)ar->winy) - 1.0f; if (rv3d->persp == RV3D_CAMOB) { /* ortho camera needs offset applied */ const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 4.0f; dx += rv3d->camdx * zoomfac; dy += rv3d->camdy * zoomfac; } ray_origin[0] = (rv3d->persinv[0][0] * dx) + (rv3d->persinv[1][0] * dy) + rv3d->viewinv[3][0]; ray_origin[1] = (rv3d->persinv[0][1] * dx) + (rv3d->persinv[1][1] * dy) + rv3d->viewinv[3][1]; ray_origin[2] = (rv3d->persinv[0][2] * dx) + (rv3d->persinv[1][2] * dy) + rv3d->viewinv[3][2]; copy_v3_v3(ray_direction, rv3d->viewinv[2]); lambda = ray_point_factor_v3(depth_pt, ray_origin, ray_direction); } madd_v3_v3v3fl(out, ray_origin, ray_direction, lambda); }
static void cloth_continuum_step(ClothModifierData *clmd, float dt) { ClothSimSettings *parms = clmd->sim_parms; Cloth *cloth = clmd->clothObject; Implicit_Data *data = cloth->implicit; int mvert_num = cloth->mvert_num; ClothVertex *vert; const float fluid_factor = 0.95f; /* blend between PIC and FLIP methods */ float smoothfac = parms->velocity_smooth; /* XXX FIXME arbitrary factor!!! this should be based on some intuitive value instead, * like number of hairs per cell and time decay instead of "strength" */ float density_target = parms->density_target; float density_strength = parms->density_strength; float gmin[3], gmax[3]; int i; /* clear grid info */ zero_v3_int(clmd->hair_grid_res); zero_v3(clmd->hair_grid_min); zero_v3(clmd->hair_grid_max); clmd->hair_grid_cellsize = 0.0f; hair_get_boundbox(clmd, gmin, gmax); /* gather velocities & density */ if (smoothfac > 0.0f || density_strength > 0.0f) { HairGrid *grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax); cloth_continuum_fill_grid(grid, cloth); /* main hair continuum solver */ BPH_hair_volume_solve_divergence(grid, dt, density_target, density_strength); for (i = 0, vert = cloth->verts; i < mvert_num; i++, vert++) { float x[3], v[3], nv[3]; /* calculate volumetric velocity influence */ BPH_mass_spring_get_position(data, i, x); BPH_mass_spring_get_new_velocity(data, i, v); BPH_hair_volume_grid_velocity(grid, x, v, fluid_factor, nv); interp_v3_v3v3(nv, v, nv, smoothfac); /* apply on hair data */ BPH_mass_spring_set_new_velocity(data, i, nv); } /* store basic grid info in the modifier data */ BPH_hair_volume_grid_geometry(grid, &clmd->hair_grid_cellsize, clmd->hair_grid_res, clmd->hair_grid_min, clmd->hair_grid_max); #if 0 /* DEBUG hair velocity vector field */ { const int size = 64; int i, j; float offset[3], a[3], b[3]; const int axis = 0; const float shift = 0.0f; copy_v3_v3(offset, clmd->hair_grid_min); zero_v3(a); zero_v3(b); offset[axis] = shift * clmd->hair_grid_cellsize; a[(axis+1) % 3] = clmd->hair_grid_max[(axis+1) % 3] - clmd->hair_grid_min[(axis+1) % 3]; b[(axis+2) % 3] = clmd->hair_grid_max[(axis+2) % 3] - clmd->hair_grid_min[(axis+2) % 3]; BKE_sim_debug_data_clear_category(clmd->debug_data, "grid velocity"); for (j = 0; j < size; ++j) { for (i = 0; i < size; ++i) { float x[3], v[3], gvel[3], gvel_smooth[3], gdensity; madd_v3_v3v3fl(x, offset, a, (float)i / (float)(size-1)); madd_v3_v3fl(x, b, (float)j / (float)(size-1)); zero_v3(v); BPH_hair_volume_grid_interpolate(grid, x, &gdensity, gvel, gvel_smooth, NULL, NULL); // BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity, 0.7, 0.3, 1, "grid density", i, j, 3111); if (!is_zero_v3(gvel) || !is_zero_v3(gvel_smooth)) { float dvel[3]; sub_v3_v3v3(dvel, gvel_smooth, gvel); // BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel, 0.4, 0, 1, "grid velocity", i, j, 3112); // BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel_smooth, 0.6, 1, 1, "grid velocity", i, j, 3113); BKE_sim_debug_data_add_vector(clmd->debug_data, x, dvel, 0.4, 1, 0.7, "grid velocity", i, j, 3114); #if 0 if (gdensity > 0.0f) { float col0[3] = {0.0, 0.0, 0.0}; float col1[3] = {0.0, 1.0, 0.0}; float col[3]; interp_v3_v3v3(col, col0, col1, CLAMPIS(gdensity * clmd->sim_parms->density_strength, 0.0, 1.0)); // BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity * clmd->sim_parms->density_strength, 0, 1, 0.4, "grid velocity", i, j, 3115); // BKE_sim_debug_data_add_dot(clmd->debug_data, x, col[0], col[1], col[2], "grid velocity", i, j, 3115); BKE_sim_debug_data_add_circle(clmd->debug_data, x, 0.01f, col[0], col[1], col[2], "grid velocity", i, j, 3115); } #endif } } } } #endif BPH_hair_volume_free_vertex_grid(grid); } }
static bool collision_response(ClothModifierData *clmd, CollisionModifierData *collmd, CollPair *collpair, float dt, float restitution, float r_impulse[3]) { Cloth *cloth = clmd->clothObject; int index = collpair->ap1; bool result = false; float v1[3], v2_old[3], v2_new[3], v_rel_old[3], v_rel_new[3]; float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree); float margin_distance = (float)collpair->distance - epsilon2; float mag_v_rel; zero_v3(r_impulse); if (margin_distance > 0.0f) return false; /* XXX tested before already? */ /* only handle static collisions here */ if ( collpair->flag & COLLISION_IN_FUTURE ) return false; /* velocity */ copy_v3_v3(v1, cloth->verts[index].v); collision_get_collider_velocity(v2_old, v2_new, collmd, collpair); /* relative velocity = velocity of the cloth point relative to the collider */ sub_v3_v3v3(v_rel_old, v1, v2_old); sub_v3_v3v3(v_rel_new, v1, v2_new); /* normal component of the relative velocity */ mag_v_rel = dot_v3v3(v_rel_old, collpair->normal); /* only valid when moving toward the collider */ if (mag_v_rel < -ALMOST_ZERO) { float v_nor_old, v_nor_new; float v_tan_old[3], v_tan_new[3]; float bounce, repulse; /* Collision response based on * "Simulating Complex Hair with Robust Collision Handling" (Choe, Choi, Ko, ACM SIGGRAPH 2005) * http://graphics.snu.ac.kr/publications/2005-choe-HairSim/Choe_2005_SCA.pdf */ v_nor_old = mag_v_rel; v_nor_new = dot_v3v3(v_rel_new, collpair->normal); madd_v3_v3v3fl(v_tan_old, v_rel_old, collpair->normal, -v_nor_old); madd_v3_v3v3fl(v_tan_new, v_rel_new, collpair->normal, -v_nor_new); bounce = -v_nor_old * restitution; repulse = -margin_distance / dt; /* base repulsion velocity in normal direction */ /* XXX this clamping factor is quite arbitrary ... * not sure if there is a more scientific approach, but seems to give good results */ CLAMP(repulse, 0.0f, 4.0f * bounce); if (margin_distance < -epsilon2) { mul_v3_v3fl(r_impulse, collpair->normal, max_ff(repulse, bounce) - v_nor_new); } else { bounce = 0.0f; mul_v3_v3fl(r_impulse, collpair->normal, repulse - v_nor_new); } result = true; } return result; }
/* tries to realize the wanted velocity taking all constraints into account */ void boid_body(BoidBrainData *bbd, ParticleData *pa) { BoidSettings *boids = bbd->part->boids; BoidParticle *bpa = pa->boid; BoidValues val; EffectedPoint epoint; float acc[3] = {0.0f, 0.0f, 0.0f}, tan_acc[3], nor_acc[3]; float dvec[3], bvec[3]; float new_dir[3], new_speed; float old_dir[3], old_speed; float wanted_dir[3]; float q[4], mat[3][3]; /* rotation */ float ground_co[3] = {0.0f, 0.0f, 0.0f}, ground_nor[3] = {0.0f, 0.0f, 1.0f}; float force[3] = {0.0f, 0.0f, 0.0f}; float pa_mass=bbd->part->mass, dtime=bbd->dfra*bbd->timestep; set_boid_values(&val, boids, pa); /* make sure there's something in new velocity, location & rotation */ copy_particle_key(&pa->state, &pa->prev_state, 0); if (bbd->part->flag & PART_SIZEMASS) pa_mass*=pa->size; /* if boids can't fly they fall to the ground */ if ((boids->options & BOID_ALLOW_FLIGHT)==0 && ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)==0 && psys_uses_gravity(bbd->sim)) bpa->data.mode = eBoidMode_Falling; if (bpa->data.mode == eBoidMode_Falling) { /* Falling boids are only effected by gravity. */ acc[2] = bbd->sim->scene->physics_settings.gravity[2]; } else { /* figure out acceleration */ float landing_level = 2.0f; float level = landing_level + 1.0f; float new_vel[3]; if (bpa->data.mode == eBoidMode_Liftoff) { bpa->data.mode = eBoidMode_InAir; bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor); } else if (bpa->data.mode == eBoidMode_InAir && boids->options & BOID_ALLOW_LAND) { /* auto-leveling & landing if close to ground */ bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor); /* level = how many particle sizes above ground */ level = (pa->prev_state.co[2] - ground_co[2])/(2.0f * pa->size) - 0.5f; landing_level = - boids->landing_smoothness * pa->prev_state.vel[2] * pa_mass; if (pa->prev_state.vel[2] < 0.0f) { if (level < 1.0f) { bbd->wanted_co[0] = bbd->wanted_co[1] = bbd->wanted_co[2] = 0.0f; bbd->wanted_speed = 0.0f; bpa->data.mode = eBoidMode_Falling; } else if (level < landing_level) { bbd->wanted_speed *= (level - 1.0f)/landing_level; bbd->wanted_co[2] *= (level - 1.0f)/landing_level; } } } copy_v3_v3(old_dir, pa->prev_state.ave); new_speed = normalize_v3_v3(wanted_dir, bbd->wanted_co); /* first check if we have valid direction we want to go towards */ if (new_speed == 0.0f) { copy_v3_v3(new_dir, old_dir); } else { float old_dir2[2], wanted_dir2[2], nor[3], angle; copy_v2_v2(old_dir2, old_dir); normalize_v2(old_dir2); copy_v2_v2(wanted_dir2, wanted_dir); normalize_v2(wanted_dir2); /* choose random direction to turn if wanted velocity */ /* is directly behind regardless of z-coordinate */ if (dot_v2v2(old_dir2, wanted_dir2) < -0.99f) { wanted_dir[0] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng)); wanted_dir[1] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng)); wanted_dir[2] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng)); normalize_v3(wanted_dir); } /* constrain direction with maximum angular velocity */ angle = saacos(dot_v3v3(old_dir, wanted_dir)); angle = min_ff(angle, val.max_ave); cross_v3_v3v3(nor, old_dir, wanted_dir); axis_angle_to_quat(q, nor, angle); copy_v3_v3(new_dir, old_dir); mul_qt_v3(q, new_dir); normalize_v3(new_dir); /* save direction in case resulting velocity too small */ axis_angle_to_quat(q, nor, angle*dtime); copy_v3_v3(pa->state.ave, old_dir); mul_qt_v3(q, pa->state.ave); normalize_v3(pa->state.ave); } /* constrain speed with maximum acceleration */ old_speed = len_v3(pa->prev_state.vel); if (bbd->wanted_speed < old_speed) new_speed = MAX2(bbd->wanted_speed, old_speed - val.max_acc); else new_speed = MIN2(bbd->wanted_speed, old_speed + val.max_acc); /* combine direction and speed */ copy_v3_v3(new_vel, new_dir); mul_v3_fl(new_vel, new_speed); /* maintain minimum flying velocity if not landing */ if (level >= landing_level) { float len2 = dot_v2v2(new_vel, new_vel); float root; len2 = MAX2(len2, val.min_speed*val.min_speed); root = sasqrt(new_speed*new_speed - len2); new_vel[2] = new_vel[2] < 0.0f ? -root : root; normalize_v2(new_vel); mul_v2_fl(new_vel, sasqrt(len2)); } /* finally constrain speed to max speed */ new_speed = normalize_v3(new_vel); mul_v3_fl(new_vel, MIN2(new_speed, val.max_speed)); /* get acceleration from difference of velocities */ sub_v3_v3v3(acc, new_vel, pa->prev_state.vel); /* break acceleration to components */ project_v3_v3v3(tan_acc, acc, pa->prev_state.ave); sub_v3_v3v3(nor_acc, acc, tan_acc); } /* account for effectors */ pd_point_from_particle(bbd->sim, pa, &pa->state, &epoint); pdDoEffectors(bbd->sim->psys->effectors, bbd->sim->colliders, bbd->part->effector_weights, &epoint, force, NULL); if (ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) { float length = normalize_v3(force); length = MAX2(0.0f, length - boids->land_stick_force); mul_v3_fl(force, length); } add_v3_v3(acc, force); /* store smoothed acceleration for nice banking etc. */ madd_v3_v3fl(bpa->data.acc, acc, dtime); mul_v3_fl(bpa->data.acc, 1.0f / (1.0f + dtime)); /* integrate new location & velocity */ /* by regarding the acceleration as a force at this stage we*/ /* can get better control allthough it's a bit unphysical */ mul_v3_fl(acc, 1.0f/pa_mass); copy_v3_v3(dvec, acc); mul_v3_fl(dvec, dtime*dtime*0.5f); copy_v3_v3(bvec, pa->prev_state.vel); mul_v3_fl(bvec, dtime); add_v3_v3(dvec, bvec); add_v3_v3(pa->state.co, dvec); madd_v3_v3fl(pa->state.vel, acc, dtime); //if (bpa->data.mode != eBoidMode_InAir) bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor); /* change modes, constrain movement & keep track of down vector */ switch (bpa->data.mode) { case eBoidMode_InAir: { float grav[3]; grav[0] = 0.0f; grav[1] = 0.0f; grav[2] = bbd->sim->scene->physics_settings.gravity[2] < 0.0f ? -1.0f : 0.0f; /* don't take forward acceleration into account (better banking) */ if (dot_v3v3(bpa->data.acc, pa->state.vel) > 0.0f) { project_v3_v3v3(dvec, bpa->data.acc, pa->state.vel); sub_v3_v3v3(dvec, bpa->data.acc, dvec); } else { copy_v3_v3(dvec, bpa->data.acc); } /* gather apparent gravity */ madd_v3_v3v3fl(bpa->gravity, grav, dvec, -boids->banking); normalize_v3(bpa->gravity); /* stick boid on goal when close enough */ if (bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { bpa->data.mode = eBoidMode_Climbing; bpa->ground = bbd->goal_ob; boid_find_ground(bbd, pa, ground_co, ground_nor); boid_climb(boids, pa, ground_co, ground_nor); } else if (pa->state.co[2] <= ground_co[2] + pa->size * boids->height) { /* land boid when below ground */ if (boids->options & BOID_ALLOW_LAND) { pa->state.co[2] = ground_co[2] + pa->size * boids->height; pa->state.vel[2] = 0.0f; bpa->data.mode = eBoidMode_OnLand; } /* fly above ground */ else if (bpa->ground) { pa->state.co[2] = ground_co[2] + pa->size * boids->height; pa->state.vel[2] = 0.0f; } } break; } case eBoidMode_Falling: { float grav[3]; grav[0] = 0.0f; grav[1] = 0.0f; grav[2] = bbd->sim->scene->physics_settings.gravity[2] < 0.0f ? -1.0f : 0.0f; /* gather apparent gravity */ madd_v3_v3fl(bpa->gravity, grav, dtime); normalize_v3(bpa->gravity); if (boids->options & BOID_ALLOW_LAND) { /* stick boid on goal when close enough */ if (bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { bpa->data.mode = eBoidMode_Climbing; bpa->ground = bbd->goal_ob; boid_find_ground(bbd, pa, ground_co, ground_nor); boid_climb(boids, pa, ground_co, ground_nor); } /* land boid when really near ground */ else if (pa->state.co[2] <= ground_co[2] + 1.01f * pa->size * boids->height) { pa->state.co[2] = ground_co[2] + pa->size * boids->height; pa->state.vel[2] = 0.0f; bpa->data.mode = eBoidMode_OnLand; } /* if we're falling, can fly and want to go upwards lets fly */ else if (boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) bpa->data.mode = eBoidMode_InAir; } else bpa->data.mode = eBoidMode_InAir; break; } case eBoidMode_Climbing: { boid_climb(boids, pa, ground_co, ground_nor); //float nor[3]; //copy_v3_v3(nor, ground_nor); ///* gather apparent gravity to r_ve */ //madd_v3_v3fl(pa->r_ve, ground_nor, -1.0); //normalize_v3(pa->r_ve); ///* raise boid it's size from surface */ //mul_v3_fl(nor, pa->size * boids->height); //add_v3_v3v3(pa->state.co, ground_co, nor); ///* remove normal component from velocity */ //project_v3_v3v3(v, pa->state.vel, ground_nor); //sub_v3_v3v3(pa->state.vel, pa->state.vel, v); break; } case eBoidMode_OnLand: { /* stick boid on goal when close enough */ if (bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { bpa->data.mode = eBoidMode_Climbing; bpa->ground = bbd->goal_ob; boid_find_ground(bbd, pa, ground_co, ground_nor); boid_climb(boids, pa, ground_co, ground_nor); } /* ground is too far away so boid falls */ else if (pa->state.co[2]-ground_co[2] > 1.1f * pa->size * boids->height) bpa->data.mode = eBoidMode_Falling; else { /* constrain to surface */ pa->state.co[2] = ground_co[2] + pa->size * boids->height; pa->state.vel[2] = 0.0f; } if (boids->banking > 0.0f) { float grav[3]; /* Don't take gravity's strength in to account, */ /* otherwise amount of banking is hard to control. */ negate_v3_v3(grav, ground_nor); project_v3_v3v3(dvec, bpa->data.acc, pa->state.vel); sub_v3_v3v3(dvec, bpa->data.acc, dvec); /* gather apparent gravity */ madd_v3_v3v3fl(bpa->gravity, grav, dvec, -boids->banking); normalize_v3(bpa->gravity); } else { /* gather negative surface normal */ madd_v3_v3fl(bpa->gravity, ground_nor, -1.0f); normalize_v3(bpa->gravity); } break; } } /* save direction to state.ave unless the boid is falling */ /* (boids can't effect their direction when falling) */ if (bpa->data.mode!=eBoidMode_Falling && len_v3(pa->state.vel) > 0.1f*pa->size) { copy_v3_v3(pa->state.ave, pa->state.vel); pa->state.ave[2] *= bbd->part->boids->pitch; normalize_v3(pa->state.ave); } /* apply damping */ if (ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) mul_v3_fl(pa->state.vel, 1.0f - 0.2f*bbd->part->dampfac); /* calculate rotation matrix based on forward & down vectors */ if (bpa->data.mode == eBoidMode_InAir) { copy_v3_v3(mat[0], pa->state.ave); project_v3_v3v3(dvec, bpa->gravity, pa->state.ave); sub_v3_v3v3(mat[2], bpa->gravity, dvec); normalize_v3(mat[2]); } else { project_v3_v3v3(dvec, pa->state.ave, bpa->gravity); sub_v3_v3v3(mat[0], pa->state.ave, dvec); normalize_v3(mat[0]); copy_v3_v3(mat[2], bpa->gravity); } negate_v3(mat[2]); cross_v3_v3v3(mat[1], mat[2], mat[0]); /* apply rotation */ mat3_to_quat_is_ok(q, mat); copy_qt_qt(pa->state.rot, q); }
/* Code adapted from: * "GPU-based Volume Rendering, Real-time Volume Graphics", AK Peters/CRC Press */ static int create_view_aligned_slices(VolumeSlicer *slicer, const int num_slices, const float view_dir[3]) { const int indices[] = { 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5 }; const float vertices[8][3] = { { slicer->min[0], slicer->min[1], slicer->min[2] }, { slicer->max[0], slicer->min[1], slicer->min[2] }, { slicer->max[0], slicer->max[1], slicer->min[2] }, { slicer->min[0], slicer->max[1], slicer->min[2] }, { slicer->min[0], slicer->min[1], slicer->max[2] }, { slicer->max[0], slicer->min[1], slicer->max[2] }, { slicer->max[0], slicer->max[1], slicer->max[2] }, { slicer->min[0], slicer->max[1], slicer->max[2] } }; const int edges[12][2] = { { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 }, { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 }, { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 4 } }; const int edge_list[8][12] = { { 0, 1, 5, 6, 4, 8, 11, 9, 3, 7, 2, 10 }, { 0, 4, 3, 11, 1, 2, 6, 7, 5, 9, 8, 10 }, { 1, 5, 0, 8, 2, 3, 7, 4, 6, 10, 9, 11 }, { 7, 11, 10, 8, 2, 6, 1, 9, 3, 0, 4, 5 }, { 8, 5, 9, 1, 11, 10, 7, 6, 4, 3, 0, 2 }, { 9, 6, 10, 2, 8, 11, 4, 7, 5, 0, 1, 3 }, { 9, 8, 5, 4, 6, 1, 2, 0, 10, 7, 11, 3 }, { 10, 9, 6, 5, 7, 2, 3, 1, 11, 4, 8, 0 } }; /* find vertex that is the furthest from the view plane */ int max_index = 0; float max_dist, min_dist; min_dist = max_dist = dot_v3v3(view_dir, vertices[0]); for (int i = 1; i < 8; i++) { float dist = dot_v3v3(view_dir, vertices[i]); if (dist > max_dist) { max_dist = dist; max_index = i; } if (dist < min_dist) { min_dist = dist; } } max_dist -= FLT_EPSILON; min_dist += FLT_EPSILON; /* start and direction vectors */ float vec_start[12][3], vec_dir[12][3]; /* lambda intersection values */ float lambda[12], lambda_inc[12]; float denom = 0.0f; float plane_dist = min_dist; float plane_dist_inc = (max_dist - min_dist) / (float)num_slices; /* for all egdes */ for (int i = 0; i < 12; i++) { copy_v3_v3(vec_start[i], vertices[edges[edge_list[max_index][i]][0]]); copy_v3_v3(vec_dir[i], vertices[edges[edge_list[max_index][i]][1]]); sub_v3_v3(vec_dir[i], vec_start[i]); denom = dot_v3v3(vec_dir[i], view_dir); if (1.0f + denom != 1.0f) { lambda_inc[i] = plane_dist_inc / denom; lambda[i] = (plane_dist - dot_v3v3(vec_start[i], view_dir)) / denom; } else { lambda[i] = -1.0f; lambda_inc[i] = 0.0f; } } float intersections[6][3]; float dL[12]; int num_points = 0; /* find intersections for each slice, process them in back to front order */ for (int i = 0; i < num_slices; i++) { for (int e = 0; e < 12; e++) { dL[e] = lambda[e] + i * lambda_inc[e]; } if ((dL[0] >= 0.0f) && (dL[0] < 1.0f)) { madd_v3_v3v3fl(intersections[0], vec_start[0], vec_dir[0], dL[0]); } else if ((dL[1] >= 0.0f) && (dL[1] < 1.0f)) { madd_v3_v3v3fl(intersections[0], vec_start[1], vec_dir[1], dL[1]); } else if ((dL[3] >= 0.0f) && (dL[3] < 1.0f)) { madd_v3_v3v3fl(intersections[0], vec_start[3], vec_dir[3], dL[3]); } else continue; if ((dL[2] >= 0.0f) && (dL[2] < 1.0f)) { madd_v3_v3v3fl(intersections[1], vec_start[2], vec_dir[2], dL[2]); } else if ((dL[0] >= 0.0f) && (dL[0] < 1.0f)) { madd_v3_v3v3fl(intersections[1], vec_start[0], vec_dir[0], dL[0]); } else if ((dL[1] >= 0.0f) && (dL[1] < 1.0f)) { madd_v3_v3v3fl(intersections[1], vec_start[1], vec_dir[1], dL[1]); } else { madd_v3_v3v3fl(intersections[1], vec_start[3], vec_dir[3], dL[3]); } if ((dL[4] >= 0.0f) && (dL[4] < 1.0f)) { madd_v3_v3v3fl(intersections[2], vec_start[4], vec_dir[4], dL[4]); } else if ((dL[5] >= 0.0f) && (dL[5] < 1.0f)) { madd_v3_v3v3fl(intersections[2], vec_start[5], vec_dir[5], dL[5]); } else { madd_v3_v3v3fl(intersections[2], vec_start[7], vec_dir[7], dL[7]); } if ((dL[6] >= 0.0f) && (dL[6] < 1.0f)) { madd_v3_v3v3fl(intersections[3], vec_start[6], vec_dir[6], dL[6]); } else if ((dL[4] >= 0.0f) && (dL[4] < 1.0f)) { madd_v3_v3v3fl(intersections[3], vec_start[4], vec_dir[4], dL[4]); } else if ((dL[5] >= 0.0f) && (dL[5] < 1.0f)) { madd_v3_v3v3fl(intersections[3], vec_start[5], vec_dir[5], dL[5]); } else { madd_v3_v3v3fl(intersections[3], vec_start[7], vec_dir[7], dL[7]); } if ((dL[8] >= 0.0f) && (dL[8] < 1.0f)) { madd_v3_v3v3fl(intersections[4], vec_start[8], vec_dir[8], dL[8]); } else if ((dL[9] >= 0.0f) && (dL[9] < 1.0f)) { madd_v3_v3v3fl(intersections[4], vec_start[9], vec_dir[9], dL[9]); } else { madd_v3_v3v3fl(intersections[4], vec_start[11], vec_dir[11], dL[11]); } if ((dL[10] >= 0.0f) && (dL[10] < 1.0f)) { madd_v3_v3v3fl(intersections[5], vec_start[10], vec_dir[10], dL[10]); } else if ((dL[8] >= 0.0f) && (dL[8] < 1.0f)) { madd_v3_v3v3fl(intersections[5], vec_start[8], vec_dir[8], dL[8]); } else if ((dL[9] >= 0.0f) && (dL[9] < 1.0f)) { madd_v3_v3v3fl(intersections[5], vec_start[9], vec_dir[9], dL[9]); } else { madd_v3_v3v3fl(intersections[5], vec_start[11], vec_dir[11], dL[11]); } for (int e = 0; e < 12; e++) { copy_v3_v3(slicer->verts[num_points++], intersections[indices[e]]); } } return num_points; }
static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for_render) { int i; /* Options about projection direction */ const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit; float proj_axis[3] = {0.0f, 0.0f, 0.0f}; /* Raycast and tree stuff */ /** \note 'hit.dist' is kept in the targets space, this is only used * for finding the best hit, to get the real dist, * measure the len_v3v3() from the input coord to hit.co */ BVHTreeRayHit hit; BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; /* auxiliary target */ DerivedMesh *auxMesh = NULL; BVHTreeFromMesh auxData = NULL_BVHTreeFromMesh; SpaceTransform local2aux; /* If the user doesn't allows to project in any direction of projection axis * then theres nothing todo. */ if ((calc->smd->shrinkOpts & (MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 0) return; /* Prepare data to retrieve the direction in which we should project each vertex */ if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { if (calc->vert == NULL) return; } else { /* The code supports any axis that is a combination of X,Y,Z * although currently UI only allows to set the 3 different axis */ if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) proj_axis[0] = 1.0f; if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) proj_axis[1] = 1.0f; if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) proj_axis[2] = 1.0f; normalize_v3(proj_axis); /* Invalid projection direction */ if (len_squared_v3(proj_axis) < FLT_EPSILON) { return; } } if (calc->smd->auxTarget) { auxMesh = object_get_derived_final(calc->smd->auxTarget, for_render); if (!auxMesh) return; SPACE_TRANSFORM_SETUP(&local2aux, calc->ob, calc->smd->auxTarget); } /* After sucessufuly build the trees, start projection vertexs */ if (bvhtree_from_mesh_faces(&treeData, calc->target, 0.0, 4, 6) && (auxMesh == NULL || bvhtree_from_mesh_faces(&auxData, auxMesh, 0.0, 4, 6))) { #ifndef __APPLE__ #pragma omp parallel for private(i, hit) schedule(static) #endif for (i = 0; i < calc->numVerts; ++i) { float *co = calc->vertexCos[i]; float tmp_co[3], tmp_no[3]; const float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); if (weight == 0.0f) { continue; } if (calc->vert) { /* calc->vert contains verts from derivedMesh */ /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { copy_v3_v3(tmp_co, calc->vert[i].co); normal_short_to_float_v3(tmp_no, calc->vert[i].no); } else { copy_v3_v3(tmp_co, co); copy_v3_v3(tmp_no, proj_axis); } } else { copy_v3_v3(tmp_co, co); copy_v3_v3(tmp_no, proj_axis); } hit.index = -1; hit.dist = 10000.0f; /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ /* Project over positive direction of axis */ if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { if (auxData.tree) { BKE_shrinkwrap_project_normal(0, tmp_co, tmp_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); } BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, tmp_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); } /* Project over negative direction of axis */ if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) { float inv_no[3]; negate_v3_v3(inv_no, tmp_no); if (auxData.tree) { BKE_shrinkwrap_project_normal(0, tmp_co, inv_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); } BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, inv_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); } /* don't set the initial dist (which is more efficient), * because its calculated in the targets space, we want the dist in our own space */ if (proj_limit_squared != 0.0f) { if (len_squared_v3v3(hit.co, co) > proj_limit_squared) { hit.index = -1; } } if (hit.index != -1) { madd_v3_v3v3fl(hit.co, hit.co, tmp_no, calc->keepDist); interp_v3_v3v3(co, co, hit.co, weight); } } } /* free data structures */ free_bvhtree_from_mesh(&treeData); free_bvhtree_from_mesh(&auxData); }
static void ruler_info_draw_pixel(const struct bContext *C, ARegion *ar, void *arg) { Scene *scene = CTX_data_scene(C); UnitSettings *unit = &scene->unit; RulerItem *ruler_item; RulerInfo *ruler_info = arg; RegionView3D *rv3d = ruler_info->ar->regiondata; // ARegion *ar = ruler_info->ar; const float cap_size = 4.0f; const float bg_margin = 4.0f * U.pixelsize; const float bg_radius = 4.0f * U.pixelsize; const float arc_size = 64.0f * U.pixelsize; #define ARC_STEPS 24 const int arc_steps = ARC_STEPS; int i; //unsigned int color_act = 0x666600; unsigned int color_act = 0xffffff; unsigned int color_base = 0x0; unsigned char color_back[4] = {0xff, 0xff, 0xff, 0x80}; unsigned char color_text[3]; unsigned char color_wire[3]; /* anti-aliased lines for more consistent appearance */ glEnable(GL_LINE_SMOOTH); BLF_enable(blf_mono_font, BLF_ROTATION); BLF_size(blf_mono_font, 14 * U.pixelsize, U.dpi); BLF_rotation(blf_mono_font, 0.0f); UI_GetThemeColor3ubv(TH_TEXT, color_text); UI_GetThemeColor3ubv(TH_WIRE, color_wire); for (ruler_item = ruler_info->items.first, i = 0; ruler_item; ruler_item = ruler_item->next, i++) { const bool is_act = (i == ruler_info->item_active); float dir_ruler[2]; float co_ss[3][2]; int j; /* should these be checked? - ok for now not to */ for (j = 0; j < 3; j++) { ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP); } glEnable(GL_BLEND); cpack(is_act ? color_act : color_base); if (ruler_item->flag & RULERITEM_USE_ANGLE) { glBegin(GL_LINE_STRIP); for (j = 0; j < 3; j++) { glVertex2fv(co_ss[j]); } glEnd(); cpack(0xaaaaaa); setlinestyle(3); glBegin(GL_LINE_STRIP); for (j = 0; j < 3; j++) { glVertex2fv(co_ss[j]); } glEnd(); setlinestyle(0); /* arc */ { float dir_tmp[3]; float co_tmp[3]; float arc_ss_coords[ARC_STEPS + 1][2]; float dir_a[3]; float dir_b[3]; float quat[4]; float axis[3]; float angle; const float px_scale = (ED_view3d_pixel_size(rv3d, ruler_item->co[1]) * min_fff(arc_size, len_v2v2(co_ss[0], co_ss[1]) / 2.0f, len_v2v2(co_ss[2], co_ss[1]) / 2.0f)); sub_v3_v3v3(dir_a, ruler_item->co[0], ruler_item->co[1]); sub_v3_v3v3(dir_b, ruler_item->co[2], ruler_item->co[1]); normalize_v3(dir_a); normalize_v3(dir_b); cross_v3_v3v3(axis, dir_a, dir_b); angle = angle_normalized_v3v3(dir_a, dir_b); axis_angle_to_quat(quat, axis, angle / arc_steps); copy_v3_v3(dir_tmp, dir_a); glColor3ubv(color_wire); for (j = 0; j <= arc_steps; j++) { madd_v3_v3v3fl(co_tmp, ruler_item->co[1], dir_tmp, px_scale); ED_view3d_project_float_global(ar, co_tmp, arc_ss_coords[j], V3D_PROJ_TEST_NOP); mul_qt_v3(quat, dir_tmp); } glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, arc_ss_coords); glDrawArrays(GL_LINE_STRIP, 0, arc_steps + 1); glDisableClientState(GL_VERTEX_ARRAY); } /* text */ { char numstr[256]; float numstr_size[2]; float pos[2]; const int prec = 2; /* XXX, todo, make optional */ ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec); BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]); pos[0] = co_ss[1][0] + (cap_size * 2.0f); pos[1] = co_ss[1][1] - (numstr_size[1] / 2.0f); /* draw text (bg) */ glColor4ubv(color_back); uiSetRoundBox(UI_CNR_ALL); uiRoundBox(pos[0] - bg_margin, pos[1] - bg_margin, pos[0] + bg_margin + numstr_size[0], pos[1] + bg_margin + numstr_size[1], bg_radius); /* draw text */ glColor3ubv(color_text); BLF_position(blf_mono_font, pos[0], pos[1], 0.0f); BLF_rotation(blf_mono_font, 0.0f); BLF_draw(blf_mono_font, numstr, sizeof(numstr)); } /* capping */ { float rot_90_vec_a[2]; float rot_90_vec_b[2]; float cap[2]; sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[1]); rot_90_vec_a[0] = -dir_ruler[1]; rot_90_vec_a[1] = dir_ruler[0]; normalize_v2(rot_90_vec_a); sub_v2_v2v2(dir_ruler, co_ss[1], co_ss[2]); rot_90_vec_b[0] = -dir_ruler[1]; rot_90_vec_b[1] = dir_ruler[0]; normalize_v2(rot_90_vec_b); glEnable(GL_BLEND); glColor3ubv(color_wire); glBegin(GL_LINES); madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, -cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, -cap_size); glVertex2fv(cap); /* angle vertex */ glVertex2f(co_ss[1][0] - cap_size, co_ss[1][1] - cap_size); glVertex2f(co_ss[1][0] + cap_size, co_ss[1][1] + cap_size); glVertex2f(co_ss[1][0] - cap_size, co_ss[1][1] + cap_size); glVertex2f(co_ss[1][0] + cap_size, co_ss[1][1] - cap_size); glEnd(); glDisable(GL_BLEND); } } else { glBegin(GL_LINE_STRIP); for (j = 0; j < 3; j += 2) { glVertex2fv(co_ss[j]); } glEnd(); cpack(0xaaaaaa); setlinestyle(3); glBegin(GL_LINE_STRIP); for (j = 0; j < 3; j += 2) { glVertex2fv(co_ss[j]); } glEnd(); setlinestyle(0); sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[2]); /* text */ { char numstr[256]; float numstr_size[2]; const int prec = 6; /* XXX, todo, make optional */ float pos[2]; ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec); BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]); mid_v2_v2v2(pos, co_ss[0], co_ss[2]); /* center text */ pos[0] -= numstr_size[0] / 2.0f; pos[1] -= numstr_size[1] / 2.0f; /* draw text (bg) */ glColor4ubv(color_back); uiSetRoundBox(UI_CNR_ALL); uiRoundBox(pos[0] - bg_margin, pos[1] - bg_margin, pos[0] + bg_margin + numstr_size[0], pos[1] + bg_margin + numstr_size[1], bg_radius); /* draw text */ glColor3ubv(color_text); BLF_position(blf_mono_font, pos[0], pos[1], 0.0f); BLF_draw(blf_mono_font, numstr, sizeof(numstr)); } /* capping */ { float rot_90_vec[2] = {-dir_ruler[1], dir_ruler[0]}; float cap[2]; normalize_v2(rot_90_vec); glEnable(GL_BLEND); glColor3ubv(color_wire); glBegin(GL_LINES); madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, -cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, -cap_size); glVertex2fv(cap); glEnd(); glDisable(GL_BLEND); } } } glDisable(GL_LINE_SMOOTH); BLF_disable(blf_mono_font, BLF_ROTATION); #undef ARC_STEPS /* draw snap */ if ((ruler_info->snap_flag & RULER_SNAP_OK) && (ruler_info->state == RULER_STATE_DRAG)) { ruler_item = ruler_item_active_get(ruler_info); if (ruler_item) { /* size from drawSnapping */ const float size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE); float co_ss[3]; ED_view3d_project_float_global(ar, ruler_item->co[ruler_item->co_index], co_ss, V3D_PROJ_TEST_NOP); cpack(color_act); circ(co_ss[0], co_ss[1], size * U.pixelsize); } } }
/** * Sets the depth from #StrokeElem.mval */ static bool stroke_elem_project( const struct CurveDrawData *cdd, const int mval_i[2], const float mval_fl[2], float surface_offset, const float radius, float r_location_world[3], float r_normal_world[3]) { View3D *v3d = cdd->vc.v3d; ARegion *ar = cdd->vc.ar; RegionView3D *rv3d = cdd->vc.rv3d; bool is_location_world_set = false; /* project to 'location_world' */ if (cdd->project.use_plane) { /* get the view vector to 'location' */ float ray_origin[3], ray_direction[3]; ED_view3d_win_to_ray(cdd->vc.ar, v3d, mval_fl, ray_origin, ray_direction, false); float lambda; if (isect_ray_plane_v3(ray_origin, ray_direction, cdd->project.plane, &lambda, true)) { madd_v3_v3v3fl(r_location_world, ray_origin, ray_direction, lambda); if (r_normal_world) { zero_v3(r_normal_world); } is_location_world_set = true; } } else { const ViewDepths *depths = rv3d->depths; if (depths && ((unsigned int)mval_i[0] < depths->w) && ((unsigned int)mval_i[1] < depths->h)) { const double depth = (double)depth_read_zbuf(&cdd->vc, mval_i[0], mval_i[1]); if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { if (depth_unproject(ar, &cdd->mats, mval_i, depth, r_location_world)) { is_location_world_set = true; if (r_normal_world) { zero_v3(r_normal_world); } if (surface_offset != 0.0f) { const float offset = cdd->project.use_surface_offset_absolute ? 1.0f : radius; float normal[3]; if (depth_read_normal(&cdd->vc, &cdd->mats, mval_i, normal)) { madd_v3_v3fl(r_location_world, normal, offset * surface_offset); if (r_normal_world) { copy_v3_v3(r_normal_world, normal); } } } } } } } if (is_location_world_set) { if (cdd->project.use_offset) { add_v3_v3(r_location_world, cdd->project.offset); } } return is_location_world_set; }
static MDefBoundIsect *meshdeform_ray_tree_intersect(MeshDeformBind *mdb, const float co1[3], const float co2[3]) { BVHTreeRayHit hit; MeshDeformIsect isect_mdef; struct MeshRayCallbackData data = { mdb, &isect_mdef, }; float end[3], vec_normal[3]; /* happens binding when a cage has no faces */ if (UNLIKELY(mdb->bvhtree == NULL)) return NULL; /* setup isec */ memset(&isect_mdef, 0, sizeof(isect_mdef)); isect_mdef.lambda = 1e10f; copy_v3_v3(isect_mdef.start, co1); copy_v3_v3(end, co2); sub_v3_v3v3(isect_mdef.vec, end, isect_mdef.start); isect_mdef.vec_length = normalize_v3_v3(vec_normal, isect_mdef.vec); hit.index = -1; hit.dist = BVH_RAYCAST_DIST_MAX; if (BLI_bvhtree_ray_cast(mdb->bvhtree, isect_mdef.start, vec_normal, 0.0, &hit, harmonic_ray_callback, &data) != -1) { const MLoop *mloop = mdb->cagedm_cache.mloop; const MLoopTri *lt = &mdb->cagedm_cache.looptri[hit.index]; const MPoly *mp = &mdb->cagedm_cache.mpoly[lt->poly]; const float (*cagecos)[3] = mdb->cagecos; const float len = isect_mdef.lambda; MDefBoundIsect *isect; float (*mp_cagecos)[3] = BLI_array_alloca(mp_cagecos, mp->totloop); int i; /* create MDefBoundIsect, and extra for 'poly_weights[]' */ isect = BLI_memarena_alloc(mdb->memarena, sizeof(*isect) + (sizeof(float) * mp->totloop)); /* compute intersection coordinate */ madd_v3_v3v3fl(isect->co, co1, isect_mdef.vec, len); isect->facing = isect_mdef.isect; isect->poly_index = lt->poly; isect->len = max_ff(len_v3v3(co1, isect->co), MESHDEFORM_LEN_THRESHOLD); /* compute mean value coordinates for interpolation */ for (i = 0; i < mp->totloop; i++) { copy_v3_v3(mp_cagecos[i], cagecos[mloop[mp->loopstart + i].v]); } interp_weights_poly_v3(isect->poly_weights, mp_cagecos, mp->totloop, isect->co); return isect; } return NULL; }
/** * Specialized slerp that uses a sphere defined by each points normal. */ static void interp_slerp_co_no_v3( const float co_a[3], const float no_a[3], const float co_b[3], const float no_b[3], const float no_dir[3], /* caller already knows, avoid normalize */ float fac, float r_co[3]) { /* center of the sphere defined by both normals */ float center[3]; BLI_assert(len_squared_v3v3(no_a, no_b) != 0); /* calculate sphere 'center' */ { /* use point on plane to */ float plane_a[4], plane_b[4], plane_c[4]; float no_mid[3], no_ortho[3]; /* pass this as an arg instead */ #if 0 float no_dir[3]; #endif float v_a_no_ortho[3], v_b_no_ortho[3]; add_v3_v3v3(no_mid, no_a, no_b); normalize_v3(no_mid); #if 0 sub_v3_v3v3(no_dir, co_a, co_b); normalize_v3(no_dir); #endif /* axis of slerp */ cross_v3_v3v3(no_ortho, no_mid, no_dir); normalize_v3(no_ortho); /* create planes */ cross_v3_v3v3(v_a_no_ortho, no_ortho, no_a); cross_v3_v3v3(v_b_no_ortho, no_ortho, no_b); project_v3_plane(v_a_no_ortho, no_ortho, v_a_no_ortho); project_v3_plane(v_b_no_ortho, no_ortho, v_b_no_ortho); plane_from_point_normal_v3(plane_a, co_a, v_a_no_ortho); plane_from_point_normal_v3(plane_b, co_b, v_b_no_ortho); plane_from_point_normal_v3(plane_c, co_b, no_ortho); /* find the sphere center from 3 planes */ if (isect_plane_plane_plane_v3(plane_a, plane_b, plane_c, center)) { /* pass */ } else { mid_v3_v3v3(center, co_a, co_b); } } /* calculate the final output 'r_co' */ { float ofs_a[3], ofs_b[3], ofs_slerp[3]; float dist_a, dist_b; sub_v3_v3v3(ofs_a, co_a, center); sub_v3_v3v3(ofs_b, co_b, center); dist_a = normalize_v3(ofs_a); dist_b = normalize_v3(ofs_b); if (interp_v3_v3v3_slerp(ofs_slerp, ofs_a, ofs_b, fac)) { madd_v3_v3v3fl(r_co, center, ofs_slerp, interpf(dist_b, dist_a, fac)); } else { interp_v3_v3v3(r_co, co_a, co_b, fac); } } }
/* * Shrinkwrap moving vertexs to the nearest surface point on the target * * it builds a BVHTree from the target mesh and then performs a * NN matches for each vertex */ static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) { int i; BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; BVHTreeNearest nearest = NULL_BVHTreeNearest; /* Create a bvh-tree of the given target */ bvhtree_from_mesh_faces(&treeData, calc->target, 0.0, 2, 6); if (treeData.tree == NULL) { OUT_OF_MEMORY(); return; } /* Setup nearest */ nearest.index = -1; nearest.dist_sq = FLT_MAX; /* Find the nearest vertex */ #ifndef __APPLE__ #pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(calc, treeData) schedule(static) #endif for (i = 0; i < calc->numVerts; ++i) { float *co = calc->vertexCos[i]; float tmp_co[3]; float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); if (weight == 0.0f) continue; /* Convert the vertex to tree coordinates */ if (calc->vert) { copy_v3_v3(tmp_co, calc->vert[i].co); } else { copy_v3_v3(tmp_co, co); } space_transform_apply(&calc->local2target, tmp_co); /* Use local proximity heuristics (to reduce the nearest search) * * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex * so we can initiate the "nearest.dist" with the expected value to that last hit. * This will lead in pruning of the search tree. */ if (nearest.index != -1) nearest.dist_sq = len_squared_v3v3(tmp_co, nearest.co); else nearest.dist_sq = FLT_MAX; BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); /* Found the nearest vertex */ if (nearest.index != -1) { if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) { /* Make the vertex stay on the front side of the face */ madd_v3_v3v3fl(tmp_co, nearest.co, nearest.no, calc->keepDist); } else { /* Adjusting the vertex weight, * so that after interpolating it keeps a certain distance from the nearest position */ const float dist = sasqrt(nearest.dist_sq); if (dist > FLT_EPSILON) { /* linear interpolation */ interp_v3_v3v3(tmp_co, tmp_co, nearest.co, (dist - calc->keepDist) / dist); } else { copy_v3_v3(tmp_co, nearest.co); } } /* Convert the coordinates back to mesh coordinates */ space_transform_invert(&calc->local2target, tmp_co); interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ } } free_bvhtree_from_mesh(&treeData); }
static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc) { int i; //Options about projection direction const char use_normal = calc->smd->shrinkOpts; float proj_axis[3] = {0.0f, 0.0f, 0.0f}; //Raycast and tree stuff BVHTreeRayHit hit; BVHTreeFromMesh treeData= NULL_BVHTreeFromMesh; //auxiliary target DerivedMesh *auxMesh = NULL; BVHTreeFromMesh auxData = NULL_BVHTreeFromMesh; SpaceTransform local2aux; //If the user doesn't allows to project in any direction of projection axis //then theres nothing todo. if ((use_normal & (MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 0) return; //Prepare data to retrieve the direction in which we should project each vertex if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { if (calc->vert == NULL) return; } else { //The code supports any axis that is a combination of X,Y,Z //although currently UI only allows to set the 3 different axis if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) proj_axis[0] = 1.0f; if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) proj_axis[1] = 1.0f; if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) proj_axis[2] = 1.0f; normalize_v3(proj_axis); //Invalid projection direction if (dot_v3v3(proj_axis, proj_axis) < FLT_EPSILON) return; } if (calc->smd->auxTarget) { auxMesh = object_get_derived_final(calc->smd->auxTarget); if (!auxMesh) return; space_transform_setup(&local2aux, calc->ob, calc->smd->auxTarget); } //After sucessufuly build the trees, start projection vertexs if (bvhtree_from_mesh_faces(&treeData, calc->target, 0.0, 4, 6) && (auxMesh == NULL || bvhtree_from_mesh_faces(&auxData, auxMesh, 0.0, 4, 6))) { #ifndef __APPLE__ #pragma omp parallel for private(i,hit) schedule(static) #endif for (i = 0; i<calc->numVerts; ++i) { float *co = calc->vertexCos[i]; float tmp_co[3], tmp_no[3]; float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); if (weight == 0.0f) continue; if (calc->vert) { /* calc->vert contains verts from derivedMesh */ /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { copy_v3_v3(tmp_co, calc->vert[i].co); normal_short_to_float_v3(tmp_no, calc->vert[i].no); } else { copy_v3_v3(tmp_co, co); copy_v3_v3(tmp_no, proj_axis); } } else { copy_v3_v3(tmp_co, co); copy_v3_v3(tmp_no, proj_axis); } hit.index = -1; hit.dist = 10000.0f; //TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that //Project over positive direction of axis if (use_normal & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { if (auxData.tree) normal_projection_project_vertex(0, tmp_co, tmp_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); normal_projection_project_vertex(calc->smd->shrinkOpts, tmp_co, tmp_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); } //Project over negative direction of axis if (use_normal & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR && hit.index == -1) { float inv_no[3]; negate_v3_v3(inv_no, tmp_no); if (auxData.tree) normal_projection_project_vertex(0, tmp_co, inv_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); normal_projection_project_vertex(calc->smd->shrinkOpts, tmp_co, inv_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); } if (hit.index != -1) { madd_v3_v3v3fl(hit.co, hit.co, tmp_no, calc->keepDist); interp_v3_v3v3(co, co, hit.co, weight); } } } //free data structures free_bvhtree_from_mesh(&treeData); free_bvhtree_from_mesh(&auxData); }