Get length of intersection of ray intersecting part?

I’m trying to get the length of the intersection of ray that is intersecting part, for destruction physics for bullets.
Getting the beginning point of the intersection is simple, using FindPartOnRay, but getting the end point is a bit difficult.
Here’s a “great” microsoft paint demonstration of what I’m talking about if my explanation is a bit subpar.

A solution I tried is to make another ray in the opposite direction of the original ray facing the opposite surface, but that would not account for convave parts or custom shapes.

.

UPDATE: I’ve used GollyGreg’s Negative Ray Loop solution, because, for me, rays that originate from inside will not register the surface of the part for some reason. The solution is accurate to some extent, which is fine for me, but is a bit slow. It solved the problem of concave parts, and overall I think this is a better solution than the one I had before.

1 Like

You could try determining the length of the penetration by getting the size of the hit part based on the surface that is hit. As for accounting for angles (which can affect penetration length, since not all penetrations will be perfectly along one axis) I don’t know. However, I am 99.999 percent sure there is some trigonometry hack that can solve for this.

Also, it would be tricky to determine penetration accounting for unions and weirdly shaped objects since unions do not always have exact collision boxes.

One important thing to know when attempting to solve this problem is that a ray that originates from inside a part will not collide with the part using the workspace ray methods.

One way to solve this problem is to use long rays until we get our first hit. Once we have our first hit, we should use small rays (at a resolution of your choosing, but 1 stud is a good starting point) incrementing forward “slowly” (moving the origin of these small rays forward by the amount of your resolution), but with a negative direction.

When your negative direction rays have found a hit (or reached your max depth), stop looping and compare the two positions of your initial positive ray and final negative ray.

This wont work too well if you have parts inside of parts however, but will work for basic geometry.

If you have more complicated geometry, you may have better luck using the surface normal returned from the raycast method and using a solution similar to @ndrwcswll’s assuming all parts are rectangular prisms

Edit: The above solution is a bit more complicated than it needs to be. When originally wrote, I had forgotten that a ray that originates from inside a part will still intersect with the same part if collides with the surface of the part when it exists. You can use the same method as above, but instead of taking the negative direction for the smaller rays, you may simply use the positive direction for the same effect

6 Likes

That might be a good solution, I’m gonna try it out and see if it can replace my current solution. Thanks!

Different games will likely require different solutions depending on how their geometry is built. If neither of our proposed solutions work, it may be worth sharing a small section of your level geometry to see what constraints your solution needs to work with.

If you can get the position of your circled points(origin, first hit, last hit, final point)
Maybe something like this would work.

Unless I’m mistaken he already said that isn’t going to work because it doesn’t account for concave shapes. In this case, I think what he really wants is this distance:image

1 Like

To go along with that solution, if you use a ray with a whitelist then you won’t have to worry about having parts inside of parts

1 Like

The problem with making this work with non-convex parts is that we have no way to read the geometry from Lua. The mesh would have to be split into convex parts or additional information about each mesh would need to be pasted into the script.

On another note, I’ve already written and posted code to figure out part-ray intersection length here:

Here is the code I posted:

local axes = {
	X = true;
	Y = true;
	Z = true;
}

local min = math.min
local max = math.max

local function getIntersectionLength(cframe, size, origin, destination)
	local cframeInv = cframe:inverse()
	origin = cframeInv * origin
	local direction =  (cframeInv * destination - origin).unit

	local maxEnterTime
	local minExitTime
	for axis in next, axes do
		local enterTime = (size[axis] - origin[axis])/direction[axis]
		local exitTime = -(size[axis] + origin[axis])/direction[axis]

		if exitTime < enterTime then
			exitTime, enterTime = enterTime, exitTime
		end

		minExitTime = min(exitTime, minExitTime)
		maxEnterTime = max(enterTime, maxEnterTime)
	end

	if maxEnterTime > minExitTime then
		-- We missed the part
		return 0
	else
		-- Since direction is a unit vector, the distance is really pretty
		return minEnterTime - maxEnterTime
	end
end
3 Likes