- Home /
Detecting how many times a raycast collides with a single concave meshcollider
Hello all,
I am attempting to create an anti-mesh destructible terrain engine similar to that of the original Red Faction. The system uses meshes to cut into other meshes by taking all vertices of the original mesh that intersect with an "eraser mesh" or anti-mesh deleting said vertices. Afterwards, it takes all of the vertices of the anti-mesh and add them to the vertices of the original mesh, essentially taking a "bite" out of it.
Here is a picture that demonstrates this concept. Where the red is the original mesh, and the green is the anti-mesh, and cyan is the vertices of interest.
The system, however, requires the ability to see if an individual point is within a collision mesh. I have researched the topic a bit and found that the best way to do this in the traditional sense is to use a raycast algorithm to look and see how many intersects a raycast has with a mesh. If you have an odd number of intersects, you know for certain that the point is within the mesh; an even number of intersects implies the point lies outside of the mesh (where zero is also even).
Here is another image to demonstrate this concept. Here green is the mesh, and the line running through it is red and blue. Cyan are the intersects with the polygon "skin".
Problem is, using a simple RaycastAll only gives ONE intersect per meshCollider. I was wondering what the function would be in unity c# in order to see how many intersects a ray has with a given mesh.
Thanks in advance.
I cannot find the API link where I learned how to do this, but for an array of colliders that SphereCastAll hit, this is the method. I don't know if it works for RaycastAll, but worth a try hey :
var collidersInRange : RaycastHit[];
collidersInRange = Physics.SphereCastAll( transform.position, 1.0, transform.forward, 10.0, hitmeLayermask );
for ( i = 0; i < collidersInRange.Length; i ++ )
{
Debug.Log( "collidersInRange[" + i + "] " + collidersInRange[i].collider.gameObject.name + " : " + collidersInRange[i].collider.transform.position );
Debug.Log( "collidersInRange[" + i + "] hitpoint = " + collidersInRange[i].point );
}
Thank you for the quick response, but sadly this does not answer my question. I not only need to know the colliders the raycast hits, I need to know each and every intersect it has on that collider. $$anonymous$$eaning that I not only need to know when it hits it, I need to know when the raycast leaves it too. And if need be re-enters it. And re-leaves it. And re-enters it. etc etc.
Sure, no worries, that's why it was just a suggestion/comment. I don't know how you are going to manage when the raycast exits the collider, this would be the same as casting from within a convex collider ie the collision won't detect from the backface. $$anonymous$$aybe have to cast a ray in both directions (from start to finish, then fom finish to start).
There must be a way to find the contact points :
http://docs.unity3d.com/Documentation/ScriptReference/Collision-contacts.html
http://docs.unity3d.com/Documentation/ScriptReference/ContactPoint-point.html
Edit : I just did a quick test with this script and some primitives, see if it works for your mesh. As I stated above, it won't register hits on the backface of a collider mesh :
#pragma strict
function Start()
{
CheckCollisionPoints();
}
function Update()
{
// so you can move the raycasting object around and get new results
if ( Input.Get$$anonymous$$ouseButtonDown(0) )
{
CheckCollisionPoints();
}
}
function CheckCollisionPoints()
{
var collidersInRange : RaycastHit[];
collidersInRange = Physics.RaycastAll( transform.position, transform.forward, 1000.0 );
for ( var i : int = 0; i < collidersInRange.Length; i ++ )
{
Debug.Log( "collidersInRange[" + i + "] " + collidersInRange[i].collider.gameObject.name + " : hitpoint = " + collidersInRange[i].point );
}
Debug.DrawRay( transform.position, transform.forward * 1000.0, Color.red, 5.0 );
}
That line of code would in theory work perfectly. The problem is that unity seems to only return a max of 1 hit per collider. So even if you hit two faces you only get 1 result.
I'm going to try creating a new ray at the point where the last one hit, going in the same direction as the last. To create a "chain" of rays. Hopefully this will work better.
Answer by MirrorIrorriM · Jan 24, 2013 at 02:41 AM
Thanks to alucardj for helping with the logic.
Note that this solution doesn't work if you have one sided 2 dimensional objects such as the default unity plane. It will be unable to tell if the object is actually inside anything or not.
How you solve the problem is you have to make multiple a new raycast every time you collide with the object. You repeat this until you reach your target, at which point you reverse direction to try and return from an arbitrary point in world space which is outside the collider in question. You must do the return check to get all the "backfaces" which the first raycast direction will miss. Here is the final code.
using UnityEngine;
using System.Collections;
public class CollisionTest : MonoBehaviour {
void Update() {
Vector3 Point;
Vector3 Start = new Vector3(0,100,0); // This is defined to be some arbitrary point far away from the collider.
Vector3 Goal = transform.position; // This is the point we want to determine whether or not is inside or outside the collider.
Vector3 Direction = Goal-Start; // This is the direction from start to goal.
Direction.Normalize();
int Itterations = 0; // If we know how many times the raycast has hit faces on its way to the target and back, we can tell through logic whether or not it is inside.
Point = Start;
while(Point != Goal) // Try to reach the point starting from the far off point. This will pass through faces to reach its objective.
{
RaycastHit hit;
if( Physics.Linecast(Point, Goal, out hit)) // Progressively move the point forward, stopping everytime we see a new plane in the way.
{
Itterations ++;
Point = hit.point + (Direction/100.0f); // Move the Point to hit.point and push it forward just a touch to move it through the skin of the mesh (if you don't push it, it will read that same point indefinately).
}
else
{
Point = Goal; // If there is no obstruction to our goal, then we can reach it in one step.
}
}
while(Point != Start) // Try to return to where we came from, this will make sure we see all the back faces too.
{
RaycastHit hit;
if( Physics.Linecast(Point, Start, out hit))
{
Itterations ++;
Point = hit.point + (-Direction/100.0f);
}
else
{
Point = Start;
}
}
if(Itterations % 2 == 0)
{
print("Point is Outside");
}
if(Itterations % 2 == 1)
{
print("Point is Inside");
}
}
}
It is actually remarkably similar to the original picture I posted.
$$anonymous$$irror$$anonymous$$irror,
I'm trying to do something very similar. I have a ray casting on user mouse click going through an object, then casting a second ray perpendicular to the first (offset to ensure the second ray starts from outside the collider) back towards the origin point (where the user clicked).
Unfortunately I am running into the same situation you did in which I would like to be able to deter$$anonymous$$e if there are multiple walls to the collider.
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
//obtain the vector where the ray hit the collider.
hitPoint = hit.point; //origin point
//offset the ray, keeping it along the XZ plane of the hit
Vector3 offsetDirection = -1 * hit.normal;
//offset a long way, $$anonymous$$imum thickness of the object
ray.origin = hit.point + offsetDirection * 100;
//point the ray back at the first hit point
ray.direction = (hit.point - ray.origin).normalized;
//raycast all, because there might be other objects in the way
RaycastHit[] hits = Physics.RaycastAll(ray);
foreach (RaycastHit h in hits)
{
if (h.collider == hit.collider)
{
hitBack = h.point; //destination point
}
}
}
hitPoint is the origin point (the first ray cast from the mouse where the user clicks) hitBack is the destination point (second ray cast perpendicularly to the first ray back towards it)
As you said before, RaycastAll only gives one intercept per mesh collider. There in lies the problem. I am trying to implement your while loop to continuously cast rays until a ray reaches the origin point but am co$$anonymous$$g up short on the math.
//offset the ray, keeping it along the XZ plane of the hit
Vector3 offsetDirection = -hit.normal;
//offset a long way, $$anonymous$$imum thickness of the object
ray.origin = hit.point + offsetDirection * 100;
//point the ray back at the first hit point
ray.direction = (hit.point - ray.origin).normalized;
Vector3 Point, Goal, Direction;
Point = ray.origin;
Goal = hit.point;
Direction = ray.direction;
Debug.Log("Point: " + Point);
Debug.Log("Goal: " + Goal);
Debug.Log("Point+: " + (Point + (Direction * 100.0f)));
while (Point != Goal)
{
RaycastHit hit2;
if (Physics.Linecast(Point, Goal, out hit2))
{
counter_one++;
Point = hit2.point;
Debug.Log("Point-hit2: " + Point);
}
else
{
Point = Goal;
hitBack = Point;
}
}
I'm running through Debug printout statements to try and diagnose how to do the math properly to ensure the rays co$$anonymous$$g back toward the origin point eventually reach it and the while loop stops, but as of now, it deadlocks and crashes Unity.
I as well am unsure if it would be possible to add to my existing RaycastAll loop and do the casting their, or if Linecasting is the better option.
*Double commenting as it wouldn't allow me to post this all in one comment
Figured it out! Thanks $$anonymous$$irror$$anonymous$$irror for posting this.
Answer by SofiaFeist · Feb 09, 2020 at 12:43 AM
Hello, I've been having a similar problem recently and this thread helped me a lot to solve it. I just wanted to share my code, which is a bit smaller than MirrorIrorriM's but uses the exact same logic, in case anyone is interested. Let me know if anyone has any feedback.
bool InsideCollider(Vector3 point)
{
int count = 0;
RaycastHit hit;
Vector3 rayStart = new Vector3(100, 100, 100);
Vector3 direction = point - rayStart;
Vector3 hitPoint = rayStart;
while (Physics.Raycast(hitPoint, direction, out hit, direction.magnitude) && count < 100) // count < 100 Just in case you accidentally enter an infinite loop
{
hitPoint = hit.point + (direction.normalized / 100.0f);
count++;
}
hitPoint = point;
while (Physics.Raycast(hitPoint, -direction, out hit, direction.magnitude) && count < 100)
{
hitPoint = hit.point + (-direction.normalized / 100.0f);
count++;
}
// Checking how many intersections there are
if (count % 2 == 0) // if count is even
return false;
else // if count is odd
return true;
}
Answer by Dreamer-of-dreams · Mar 29, 2020 at 12:01 PM
https://gist.github.com/tsubaki/6438870 This Japanese commented code solves all problems, it was recently released
Your answer
Follow this Question
Related Questions
Cutting a Sprite 0 Answers
Trigger and raycast error? 2 Answers
Mesh polygon of a hit surface on a gameobject 1 Answer
Detecting game object under UI 2 Answers