BMFace *BKE_bmbvh_ray_cast_filter( BMBVHTree *bmtree, const float co[3], const float dir[3], const float radius, float *r_dist, float r_hitout[3], float r_cagehit[3], BMBVHTree_FaceFilter filter_cb, void *filter_userdata) { BVHTreeRayHit hit; struct RayCastUserData_Filter bmcb_data_filter; struct RayCastUserData *bmcb_data = &bmcb_data_filter.bmcb_data; const float dist = r_dist ? *r_dist : FLT_MAX; bmcb_data_filter.filter_cb = filter_cb; bmcb_data_filter.filter_userdata = filter_userdata; if (bmtree->cos_cage) BLI_assert(!(bmtree->bm->elem_index_dirty & BM_VERT)); hit.dist = dist; hit.index = -1; /* ok to leave 'uv' uninitialized */ bmcb_data->looptris = (const BMLoop *(*)[3])bmtree->looptris; bmcb_data->cos_cage = (const float (*)[3])bmtree->cos_cage; BLI_bvhtree_ray_cast(bmtree->tree, co, dir, radius, &hit, bmbvh_ray_cast_cb_filter, &bmcb_data_filter); if (hit.index != -1 && hit.dist != dist) { return bmbvh_ray_cast_handle_hit(bmtree, bmcb_data, &hit, r_dist, r_hitout, r_cagehit); } return NULL; }
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; }
/* * This function raycast a single vertex and updates the hit if the "hit" is considered valid. * Returns TRUE if "hit" was updated. * Opts control whether an hit is valid or not * Supported options are: * MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE (front faces hits are ignored) * MOD_SHRINKWRAP_CULL_TARGET_BACKFACE (back faces hits are ignored) */ int normal_projection_project_vertex(char options, const float *vert, const float *dir, const SpaceTransform *transf, BVHTree *tree, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata) { float tmp_co[3], tmp_no[3]; const float *co, *no; BVHTreeRayHit hit_tmp; //Copy from hit (we need to convert hit rays from one space coordinates to the other memcpy(&hit_tmp, hit, sizeof(hit_tmp)); //Apply space transform (TODO readjust dist) if (transf) { copy_v3_v3(tmp_co, vert); space_transform_apply(transf, tmp_co); co = tmp_co; copy_v3_v3(tmp_no, dir); space_transform_apply_normal(transf, tmp_no); no = tmp_no; hit_tmp.dist *= mat4_to_scale(((SpaceTransform*)transf)->local2target); } else { co = vert; no = dir; } hit_tmp.index = -1; BLI_bvhtree_ray_cast(tree, co, no, 0.0f, &hit_tmp, callback, userdata); if (hit_tmp.index != -1) { /* invert the normal first so face culling works on rotated objects */ if (transf) { space_transform_invert_normal(transf, hit_tmp.no); } if (options & (MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE|MOD_SHRINKWRAP_CULL_TARGET_BACKFACE)) { /* apply backface */ const float dot= dot_v3v3(dir, hit_tmp.no); if ( ((options & MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE) && dot <= 0.0f) || ((options & MOD_SHRINKWRAP_CULL_TARGET_BACKFACE) && dot >= 0.0f) ) { return FALSE; /* Ignore hit */ } } if (transf) { /* Inverting space transform (TODO make coeherent with the initial dist readjust) */ space_transform_invert(transf, hit_tmp.co); hit_tmp.dist = len_v3v3((float *)vert, hit_tmp.co); } memcpy(hit, &hit_tmp, sizeof(hit_tmp)); return TRUE; } return FALSE; }
static void rna_Object_ray_cast( Object *ob, ReportList *reports, float origin[3], float direction[3], float distance, int *r_success, float r_location[3], float r_normal[3], int *r_index) { bool success = false; if (ob->derivedFinal == NULL) { BKE_reportf(reports, RPT_ERROR, "Object '%s' has no mesh data to be used for ray casting", ob->id.name + 2); return; } /* Test BoundBox first (efficiency) */ BoundBox *bb = BKE_object_boundbox_get(ob); float distmin; if (!bb || (isect_ray_aabb_v3_simple(origin, direction, bb->vec[0], bb->vec[6], &distmin, NULL) && distmin <= distance)) { BVHTreeFromMesh treeData = {NULL}; /* no need to managing allocation or freeing of the BVH data. this is generated and freed as needed */ bvhtree_from_mesh_looptri(&treeData, ob->derivedFinal, 0.0f, 4, 6); /* may fail if the mesh has no faces, in that case the ray-cast misses */ if (treeData.tree != NULL) { BVHTreeRayHit hit; hit.index = -1; hit.dist = distance; normalize_v3(direction); if (BLI_bvhtree_ray_cast(treeData.tree, origin, direction, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) { if (hit.dist <= distance) { *r_success = success = true; copy_v3_v3(r_location, hit.co); copy_v3_v3(r_normal, hit.no); *r_index = dm_looptri_to_poly_index(ob->derivedFinal, &treeData.looptri[hit.index]); } } free_bvhtree_from_mesh(&treeData); } } if (success == false) { *r_success = false; zero_v3(r_location); zero_v3(r_normal); *r_index = -1; } }
// get visibility of a wind ray static float eff_calc_visibility(ListBase *colliders, EffectorCache *eff, EffectorData *efd, EffectedPoint *point) { ListBase *colls = colliders; ColliderCache *col; float norm[3], len = 0.0; float visibility = 1.0, absorption = 0.0; if(!(eff->pd->flag & PFIELD_VISIBILITY)) return visibility; if(!colls) colls = get_collider_cache(eff->scene, eff->ob, NULL); if(!colls) return visibility; negate_v3_v3(norm, efd->vec_to_point); len = normalize_v3(norm); // check all collision objects for(col = colls->first; col; col = col->next) { CollisionModifierData *collmd = col->collmd; if(col->ob == eff->ob) continue; if(collmd->bvhtree) { BVHTreeRayHit hit; hit.index = -1; hit.dist = len + FLT_EPSILON; // check if the way is blocked if(BLI_bvhtree_ray_cast(collmd->bvhtree, point->loc, norm, 0.0f, &hit, eff_tri_ray_hit, NULL)>=0) { absorption= col->ob->pd->absorption; // visibility is only between 0 and 1, calculated from 1-absorption visibility *= CLAMPIS(1.0f-absorption, 0.0f, 1.0f); if(visibility <= 0.0f) break; } } } if(!colliders) free_collider_cache(&colls); return visibility; }
BMFace *BKE_bmbvh_find_face_segment(BMBVHTree *bmtree, const float co_a[3], const float co_b[3], float *r_fac, float r_hitout[3], float r_cagehit[3]) { BVHTreeRayHit hit; struct SegmentUserData bmcb_data; const float dist = len_v3v3(co_a, co_b); float dir[3]; if (bmtree->cos_cage) BLI_assert(!(bmtree->bm->elem_index_dirty & BM_VERT)); sub_v3_v3v3(dir, co_b, co_a); hit.dist = dist; hit.index = -1; /* ok to leave 'uv' uninitialized */ bmcb_data.looptris = (const BMLoop *(*)[3])bmtree->looptris; bmcb_data.cos_cage = (const float (*)[3])bmtree->cos_cage; bmcb_data.co_a = co_a; bmcb_data.co_b = co_b; BLI_bvhtree_ray_cast(bmtree->tree, co_a, dir, 0.0f, &hit, bmbvh_find_face_segment_cb, &bmcb_data); if (hit.index != -1 && hit.dist != dist) { /* duplicate of BKE_bmbvh_ray_cast() */ if (r_hitout) { if (bmtree->flag & BMBVH_RETURN_ORIG) { BMLoop **ltri = bmtree->looptris[hit.index]; interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, bmcb_data.uv); } else { copy_v3_v3(r_hitout, hit.co); } if (r_cagehit) { copy_v3_v3(r_cagehit, hit.co); } } /* end duplicate */ if (r_fac) { *r_fac = hit.dist / dist; } return bmtree->looptris[hit.index][0]->f; } return NULL; }
static int RE_rayobject_blibvh_intersect(RayObject *o, Isect *isec) { BVHObject *obj = (BVHObject*)o; BVHTreeRayHit hit; float dir[3]; struct BVHCallbackUserData data; data.isec = isec; data.leafs = obj->leafs; copy_v3_v3(dir, isec->dir); hit.index = 0; hit.dist = isec->dist; return BLI_bvhtree_ray_cast(obj->bvh, isec->start, dir, 0.0, &hit, bvh_callback, (void*)&data); }
BMFace *BKE_bmbvh_ray_cast(BMBVHTree *bmtree, const float co[3], const float dir[3], const float radius, float *r_dist, float r_hitout[3], float r_cagehit[3]) { BVHTreeRayHit hit; struct RayCastUserData bmcb_data; const float dist = r_dist ? *r_dist : FLT_MAX; if (bmtree->cos_cage) BLI_assert(!(bmtree->bm->elem_index_dirty & BM_VERT)); hit.dist = dist; hit.index = -1; /* ok to leave 'uv' uninitialized */ bmcb_data.looptris = (const BMLoop *(*)[3])bmtree->looptris; bmcb_data.cos_cage = (const float (*)[3])bmtree->cos_cage; BLI_bvhtree_ray_cast(bmtree->tree, co, dir, radius, &hit, bmbvh_ray_cast_cb, &bmcb_data); if (hit.index != -1 && hit.dist != dist) { if (r_hitout) { if (bmtree->flag & BMBVH_RETURN_ORIG) { BMLoop **ltri = bmtree->looptris[hit.index]; interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, bmcb_data.uv); } else { copy_v3_v3(r_hitout, hit.co); } if (r_cagehit) { copy_v3_v3(r_cagehit, hit.co); } } if (r_dist) { *r_dist = hit.dist; } return bmtree->looptris[hit.index][0]->f; } return NULL; }
static void rna_Object_ray_cast(Object *ob, ReportList *reports, float ray_start[3], float ray_end[3], float r_location[3], float r_normal[3], int *index) { BVHTreeFromMesh treeData = {NULL}; if (ob->derivedFinal == NULL) { BKE_reportf(reports, RPT_ERROR, "Object '%s' has no mesh data to be used for ray casting", ob->id.name + 2); return; } /* no need to managing allocation or freeing of the BVH data. this is generated and freed as needed */ bvhtree_from_mesh_faces(&treeData, ob->derivedFinal, 0.0f, 4, 6); /* may fail if the mesh has no faces, in that case the ray-cast misses */ if (treeData.tree != NULL) { BVHTreeRayHit hit; float ray_nor[3], dist; sub_v3_v3v3(ray_nor, ray_end, ray_start); dist = hit.dist = normalize_v3(ray_nor); hit.index = -1; if (BLI_bvhtree_ray_cast(treeData.tree, ray_start, ray_nor, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) { if (hit.dist <= dist) { copy_v3_v3(r_location, hit.co); copy_v3_v3(r_normal, hit.no); *index = dm_tessface_to_poly_index(ob->derivedFinal, hit.index); free_bvhtree_from_mesh(&treeData); return; } } } zero_v3(r_location); zero_v3(r_normal); *index = -1; free_bvhtree_from_mesh(&treeData); }
void rna_Object_ray_cast(Object *ob, ReportList *reports, float ray_start[3], float ray_end[3], float r_location[3], float r_normal[3], int *index) { BVHTreeFromMesh treeData= {NULL}; if(ob->derivedFinal==NULL) { BKE_reportf(reports, RPT_ERROR, "object \"%s\" has no mesh data to be used for ray casting", ob->id.name+2); return; } /* no need to managing allocation or freeing of the BVH data. this is generated and freed as needed */ bvhtree_from_mesh_faces(&treeData, ob->derivedFinal, 0.0f, 4, 6); if(treeData.tree==NULL) { BKE_reportf(reports, RPT_ERROR, "object \"%s\" could not create internal data for ray casting", ob->id.name+2); return; } else { BVHTreeRayHit hit; float ray_nor[3], dist; sub_v3_v3v3(ray_nor, ray_end, ray_start); dist= hit.dist = normalize_v3(ray_nor); hit.index = -1; if(BLI_bvhtree_ray_cast(treeData.tree, ray_start, ray_nor, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) { if(hit.dist<=dist) { copy_v3_v3(r_location, hit.co); copy_v3_v3(r_normal, hit.no); *index= hit.index; return; } } } zero_v3(r_location); zero_v3(r_normal); *index= -1; }
/** * This function populates pixel_array and returns TRUE if things are correct */ static bool cast_ray_highpoly( BVHTreeFromMesh *treeData, TriTessFace *triangles[], BakePixel *pixel_array, BakeHighPolyData *highpoly, const float co[3], const float dir[3], const int pixel_id, const int tot_highpoly, const float du_dx, const float du_dy, const float dv_dx, const float dv_dy) { int i; int primitive_id = -1; float uv[2]; int hit_mesh = -1; float hit_distance = FLT_MAX; BVHTreeRayHit *hits; hits = MEM_mallocN(sizeof(BVHTreeRayHit) * tot_highpoly, "Bake Highpoly to Lowpoly: BVH Rays"); for (i = 0; i < tot_highpoly; i++) { float co_high[3], dir_high[3]; hits[i].index = -1; /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ hits[i].dist = 10000.0f; /* transform the ray from the world space to the highpoly space */ mul_v3_m4v3(co_high, highpoly[i].imat, co); /* rotates */ mul_v3_mat3_m4v3(dir_high, highpoly[i].imat, dir); normalize_v3(dir_high); /* cast ray */ if (treeData[i].tree) { BLI_bvhtree_ray_cast(treeData[i].tree, co_high, dir_high, 0.0f, &hits[i], treeData[i].raycast_callback, &treeData[i]); } if (hits[i].index != -1) { /* cull backface */ const float dot = dot_v3v3(dir_high, hits[i].no); if (dot < 0.0f) { float distance; float hit_world[3]; /* distance comparison in world space */ mul_v3_m4v3(hit_world, highpoly[i].obmat, hits[i].co); distance = len_squared_v3v3(hit_world, co); if (distance < hit_distance) { hit_mesh = i; hit_distance = distance; } } } } if (hit_mesh != -1) { calc_barycentric_from_point(triangles[hit_mesh], hits[hit_mesh].index, hits[hit_mesh].co, &primitive_id, uv); pixel_array[pixel_id].primitive_id = primitive_id; pixel_array[pixel_id].object_id = hit_mesh; copy_v2_v2(pixel_array[pixel_id].uv, uv); /* the differentials are relative to the UV/image space, so the highpoly differentials * are the same as the low poly differentials */ pixel_array[pixel_id].du_dx = du_dx; pixel_array[pixel_id].du_dy = du_dy; pixel_array[pixel_id].dv_dx = dv_dx; pixel_array[pixel_id].dv_dy = dv_dy; } else { pixel_array[pixel_id].primitive_id = -1; pixel_array[pixel_id].object_id = -1; } MEM_freeN(hits); return hit_mesh != -1; }
/* * This function raycast a single vertex and updates the hit if the "hit" is considered valid. * Returns true if "hit" was updated. * Opts control whether an hit is valid or not * Supported options are: * MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE (front faces hits are ignored) * MOD_SHRINKWRAP_CULL_TARGET_BACKFACE (back faces hits are ignored) */ bool BKE_shrinkwrap_project_normal( char options, const float vert[3], const float dir[3], const SpaceTransform *transf, BVHTree *tree, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata) { /* don't use this because this dist value could be incompatible * this value used by the callback for comparing prev/new dist values. * also, at the moment there is no need to have a corrected 'dist' value */ // #define USE_DIST_CORRECT float tmp_co[3], tmp_no[3]; const float *co, *no; BVHTreeRayHit hit_tmp; /* Copy from hit (we need to convert hit rays from one space coordinates to the other */ memcpy(&hit_tmp, hit, sizeof(hit_tmp)); /* Apply space transform (TODO readjust dist) */ if (transf) { copy_v3_v3(tmp_co, vert); space_transform_apply(transf, tmp_co); co = tmp_co; copy_v3_v3(tmp_no, dir); space_transform_apply_normal(transf, tmp_no); no = tmp_no; #ifdef USE_DIST_CORRECT hit_tmp.dist *= mat4_to_scale(((SpaceTransform *)transf)->local2target); #endif } else { co = vert; no = dir; } hit_tmp.index = -1; BLI_bvhtree_ray_cast(tree, co, no, 0.0f, &hit_tmp, callback, userdata); if (hit_tmp.index != -1) { /* invert the normal first so face culling works on rotated objects */ if (transf) { space_transform_invert_normal(transf, hit_tmp.no); } if (options & (MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE | MOD_SHRINKWRAP_CULL_TARGET_BACKFACE)) { /* apply backface */ const float dot = dot_v3v3(dir, hit_tmp.no); if (((options & MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE) && dot <= 0.0f) || ((options & MOD_SHRINKWRAP_CULL_TARGET_BACKFACE) && dot >= 0.0f)) { return false; /* Ignore hit */ } } if (transf) { /* Inverting space transform (TODO make coeherent with the initial dist readjust) */ space_transform_invert(transf, hit_tmp.co); #ifdef USE_DIST_CORRECT hit_tmp.dist = len_v3v3(vert, hit_tmp.co); #endif } BLI_assert(hit_tmp.dist <= hit->dist); memcpy(hit, &hit_tmp, sizeof(hit_tmp)); return true; } return false; }
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; }
static void rna_Object_ray_cast(Object *ob, bContext *C, ReportList *reports, float origin[3], float direction[3], float distance, PointerRNA *rnaptr_depsgraph, bool *r_success, float r_location[3], float r_normal[3], int *r_index) { bool success = false; if (ob->runtime.mesh_eval == NULL && (ob = eval_object_ensure(ob, C, reports, rnaptr_depsgraph)) == NULL) { return; } /* Test BoundBox first (efficiency) */ BoundBox *bb = BKE_object_boundbox_get(ob); float distmin; normalize_v3( direction); /* Needed for valid distance check from isect_ray_aabb_v3_simple() call. */ if (!bb || (isect_ray_aabb_v3_simple(origin, direction, bb->vec[0], bb->vec[6], &distmin, NULL) && distmin <= distance)) { BVHTreeFromMesh treeData = {NULL}; /* No need to managing allocation or freeing of the BVH data. * This is generated and freed as needed. */ BKE_bvhtree_from_mesh_get(&treeData, ob->runtime.mesh_eval, BVHTREE_FROM_LOOPTRI, 4); /* may fail if the mesh has no faces, in that case the ray-cast misses */ if (treeData.tree != NULL) { BVHTreeRayHit hit; hit.index = -1; hit.dist = distance; if (BLI_bvhtree_ray_cast(treeData.tree, origin, direction, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) { if (hit.dist <= distance) { *r_success = success = true; copy_v3_v3(r_location, hit.co); copy_v3_v3(r_normal, hit.no); *r_index = mesh_looptri_to_poly_index(ob->runtime.mesh_eval, &treeData.looptri[hit.index]); } } free_bvhtree_from_mesh(&treeData); } } if (success == false) { *r_success = false; zero_v3(r_location); zero_v3(r_normal); *r_index = -1; } }
static Object *boid_find_ground(BoidBrainData *bbd, ParticleData *pa, float ground_co[3], float ground_nor[3]) { BoidParticle *bpa = pa->boid; if (bpa->data.mode == eBoidMode_Climbing) { SurfaceModifierData *surmd = NULL; float x[3], v[3]; surmd = (SurfaceModifierData *)modifiers_findByType(bpa->ground, eModifierType_Surface ); /* take surface velocity into account */ closest_point_on_surface(surmd, pa->state.co, x, NULL, v); add_v3_v3(x, v); /* get actual position on surface */ closest_point_on_surface(surmd, x, ground_co, ground_nor, NULL); return bpa->ground; } else { float zvec[3] = {0.0f, 0.0f, 2000.0f}; ParticleCollision col; ColliderCache *coll; BVHTreeRayHit hit; float radius = 0.0f, t, ray_dir[3]; if (!bbd->sim->colliders) return NULL; memset(&col, 0, sizeof(ParticleCollision)); /* first try to find below boid */ copy_v3_v3(col.co1, pa->state.co); sub_v3_v3v3(col.co2, pa->state.co, zvec); sub_v3_v3v3(ray_dir, col.co2, col.co1); col.f = 0.0f; hit.index = -1; hit.dist = col.original_ray_length = len_v3(ray_dir); col.pce.inside = 0; for (coll = bbd->sim->colliders->first; coll; coll = coll->next) { col.current = coll->ob; col.md = coll->collmd; col.fac1 = col.fac2 = 0.f; if (col.md && col.md->bvhtree) BLI_bvhtree_ray_cast(col.md->bvhtree, col.co1, ray_dir, radius, &hit, BKE_psys_collision_neartest_cb, &col); } /* then use that object */ if (hit.index>=0) { t = hit.dist/col.original_ray_length; interp_v3_v3v3(ground_co, col.co1, col.co2, t); normalize_v3_v3(ground_nor, col.pce.nor); return col.hit; } /* couldn't find below, so find upmost deflector object */ add_v3_v3v3(col.co1, pa->state.co, zvec); sub_v3_v3v3(col.co2, pa->state.co, zvec); sub_v3_v3(col.co2, zvec); sub_v3_v3v3(ray_dir, col.co2, col.co1); col.f = 0.0f; hit.index = -1; hit.dist = col.original_ray_length = len_v3(ray_dir); for (coll = bbd->sim->colliders->first; coll; coll = coll->next) { col.current = coll->ob; col.md = coll->collmd; if (col.md && col.md->bvhtree) BLI_bvhtree_ray_cast(col.md->bvhtree, col.co1, ray_dir, radius, &hit, BKE_psys_collision_neartest_cb, &col); } /* then use that object */ if (hit.index>=0) { t = hit.dist/col.original_ray_length; interp_v3_v3v3(ground_co, col.co1, col.co2, t); normalize_v3_v3(ground_nor, col.pce.nor); return col.hit; } /* default to z=0 */ copy_v3_v3(ground_co, pa->state.co); ground_co[2] = 0; ground_nor[0] = ground_nor[1] = 0.0f; ground_nor[2] = 1.0f; return NULL; } }
static int rule_avoid_collision(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) { BoidRuleAvoidCollision *acbr = (BoidRuleAvoidCollision*) rule; KDTreeNearest *ptn = NULL; ParticleTarget *pt; BoidParticle *bpa = pa->boid; ColliderCache *coll; float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; float co1[3], vel1[3], co2[3], vel2[3]; float len, t, inp, t_min = 2.0f; int n, neighbors = 0, nearest = 0; int ret = 0; //check deflector objects first if (acbr->options & BRULE_ACOLL_WITH_DEFLECTORS && bbd->sim->colliders) { ParticleCollision col; BVHTreeRayHit hit; float radius = val->personal_space * pa->size, ray_dir[3]; memset(&col, 0, sizeof(ParticleCollision)); copy_v3_v3(col.co1, pa->prev_state.co); add_v3_v3v3(col.co2, pa->prev_state.co, pa->prev_state.vel); sub_v3_v3v3(ray_dir, col.co2, col.co1); mul_v3_fl(ray_dir, acbr->look_ahead); col.f = 0.0f; hit.index = -1; hit.dist = col.original_ray_length = len_v3(ray_dir); /* find out closest deflector object */ for (coll = bbd->sim->colliders->first; coll; coll=coll->next) { /* don't check with current ground object */ if (coll->ob == bpa->ground) continue; col.current = coll->ob; col.md = coll->collmd; if (col.md && col.md->bvhtree) BLI_bvhtree_ray_cast(col.md->bvhtree, col.co1, ray_dir, radius, &hit, BKE_psys_collision_neartest_cb, &col); } /* then avoid that object */ if (hit.index>=0) { t = hit.dist/col.original_ray_length; /* avoid head-on collision */ if (dot_v3v3(col.pce.nor, pa->prev_state.ave) < -0.99f) { /* don't know why, but uneven range [0.0, 1.0] */ /* works much better than even [-1.0, 1.0] */ bbd->wanted_co[0] = BLI_rng_get_float(bbd->rng); bbd->wanted_co[1] = BLI_rng_get_float(bbd->rng); bbd->wanted_co[2] = BLI_rng_get_float(bbd->rng); } else { copy_v3_v3(bbd->wanted_co, col.pce.nor); } mul_v3_fl(bbd->wanted_co, (1.0f - t) * val->personal_space * pa->size); bbd->wanted_speed = sqrtf(t) * len_v3(pa->prev_state.vel); bbd->wanted_speed = MAX2(bbd->wanted_speed, val->min_speed); return 1; } } //check boids in own system if (acbr->options & BRULE_ACOLL_WITH_BOIDS) { neighbors = BLI_kdtree_range_search__normal( bbd->sim->psys->tree, pa->prev_state.co, pa->prev_state.ave, &ptn, acbr->look_ahead * len_v3(pa->prev_state.vel)); if (neighbors > 1) for (n=1; n<neighbors; n++) { copy_v3_v3(co1, pa->prev_state.co); copy_v3_v3(vel1, pa->prev_state.vel); copy_v3_v3(co2, (bbd->sim->psys->particles + ptn[n].index)->prev_state.co); copy_v3_v3(vel2, (bbd->sim->psys->particles + ptn[n].index)->prev_state.vel); sub_v3_v3v3(loc, co1, co2); sub_v3_v3v3(vec, vel1, vel2); inp = dot_v3v3(vec, vec); /* velocities not parallel */ if (inp != 0.0f) { t = -dot_v3v3(loc, vec)/inp; /* cpa is not too far in the future so investigate further */ if (t > 0.0f && t < t_min) { madd_v3_v3fl(co1, vel1, t); madd_v3_v3fl(co2, vel2, t); sub_v3_v3v3(vec, co2, co1); len = normalize_v3(vec); /* distance of cpa is close enough */ if (len < 2.0f * val->personal_space * pa->size) { t_min = t; mul_v3_fl(vec, len_v3(vel1)); mul_v3_fl(vec, (2.0f - t)/2.0f); sub_v3_v3v3(bbd->wanted_co, vel1, vec); bbd->wanted_speed = len_v3(bbd->wanted_co); ret = 1; } } } } } if (ptn) { MEM_freeN(ptn); ptn=NULL; } /* check boids in other systems */ for (pt=bbd->sim->psys->targets.first; pt; pt=pt->next) { ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt); if (epsys) { neighbors = BLI_kdtree_range_search__normal( epsys->tree, pa->prev_state.co, pa->prev_state.ave, &ptn, acbr->look_ahead * len_v3(pa->prev_state.vel)); if (neighbors > 0) for (n=0; n<neighbors; n++) { copy_v3_v3(co1, pa->prev_state.co); copy_v3_v3(vel1, pa->prev_state.vel); copy_v3_v3(co2, (epsys->particles + ptn[n].index)->prev_state.co); copy_v3_v3(vel2, (epsys->particles + ptn[n].index)->prev_state.vel); sub_v3_v3v3(loc, co1, co2); sub_v3_v3v3(vec, vel1, vel2); inp = dot_v3v3(vec, vec); /* velocities not parallel */ if (inp != 0.0f) { t = -dot_v3v3(loc, vec)/inp; /* cpa is not too far in the future so investigate further */ if (t > 0.0f && t < t_min) { madd_v3_v3fl(co1, vel1, t); madd_v3_v3fl(co2, vel2, t); sub_v3_v3v3(vec, co2, co1); len = normalize_v3(vec); /* distance of cpa is close enough */ if (len < 2.0f * val->personal_space * pa->size) { t_min = t; mul_v3_fl(vec, len_v3(vel1)); mul_v3_fl(vec, (2.0f - t)/2.0f); sub_v3_v3v3(bbd->wanted_co, vel1, vec); bbd->wanted_speed = len_v3(bbd->wanted_co); ret = 1; } } } } if (ptn) { MEM_freeN(ptn); ptn=NULL; } } } if (ptn && nearest==0) MEM_freeN(ptn); return ret; }