# Check if object is on surface, remove those that aren't with raycast?

I’ve been looking for ways to detect objects that are not on a surface, & remove them. I am developing an addon that works with blender rigid body & some objects fall through sometimes, It is just a clean up option.

Thanks!

Easiest way would be to calculate a bounding box for each object and raycast downward from various points below it (the more raycasts, the more accurate your results will be).

I wrote some code to check if objects are intersecting in world space. If by “on a surface”, you mean that the geometry is crashing, then you could loop through your objects and anything that returns an empty tuple (no intersections), you delete.

``````from collections import namedtuple
import bpy
from mathutils import Matrix, Vector

Hit = namedtuple('Hit', ('obj', 'location', 'normal', 'face_index'))

def get_intersections(obj1, obj2, get_all=False):
"""
Cast rays between all edges of `obj1` on `obj2` an return the hit info. Then do the same
in reverse.
:param Object obj1: First object to cast rays with.
:param Object obj2: Second object to cast rays with.
:param bool get_all: If False, will return the moment a ray hits, instead of collecting all hits.
"""
def a_to_b(_obj1, _obj2):
_result = []
for edge in _obj1.data.edges:
# Convert spatial relationship from _obj1 local space to _obj2 local space for ray cast.
v1, v2 = edge.vertices
inv_mat = _obj2.matrix_world.inverted()
start = inv_mat @ _obj1.matrix_world @ _obj1.data.vertices[v1].co
end = inv_mat @ _obj1.matrix_world @ _obj1.data.vertices[v2].co

# Cast ray
delta = end - start
norm_delta = Vector(delta)
norm_delta.normalize()
hit, loc, normal, face_index = _obj2.ray_cast(start, norm_delta, distance=delta.magnitude)
if hit:
_result.append(Hit(_obj2, loc, normal, face_index))
if not get_all:
return _result
return _result

result = a_to_b(obj1, obj2)
if result and not get_all:
return result

result += a_to_b(obj2, obj1)
return tuple(result)
``````

Note: This won’t be very efficient for a lot of dense meshes that may not be near each other. I intend to update this when I have time to first check if the bounding boxes are intersecting and only do a full geometry check if they are. That will speed this up for geometry not close to the collider considerably.

1 Like

Nice! How would I make use of this? Do I select the objects and then the surface(plane)?

I think you can start with bounding box or bounding sphere tests to see if that is generally enough for you.

if you have a list of your objects (e.g. a selection), you can just loop through and run this.

E.g.
if objects is a list of objects you want to check and surface is the surface you want to check against:

``````intersecting = [obj for obj in objects if get_intersections(obj, surface)]
``````

Then intersecting will be a list of just the objects that intersect with the surface.

I’m sorry, im confused. Where would I include this & how could I execute it?. I have my objects & then my surface selected.

If you select everything and your surface last, then you could put this below the code I posted originally:

``````surface = bpy.context.active_object
objects = [x for x in bpy.context.selected_objects if x is not surface]
not_intersecting = [obj for obj in objects if not get_intersections(obj, surface)]
bpy.ops.object.delete({'selected_objects': not_intersecting})   # Delete non-intersecting objects.
``````

You can try it out by opening the text editor and pasting this all in there and running the script.

2 Likes

Awesome!, for some reason, after running the script it removes some extra objects. I was wondering if this can be avoided?

As I mentioned, this works if you define an object as being “on the surface” if it is intersecting the surface. I suspect the extra objects being deleted in your case are not actually intersecting the surface. Hard to tell from this distance though. I would be interested to know if you have a case where an object that is intersecting is being deleted.

For a case like that, you could just cast a single ray down in the Z axis to see if it is “above” the surface.