Jump to content
Sign in to follow this  
Nakor

List faces on collision

Recommended Posts

Hi, I am searching through the API docs right now and I'm having trouble finding a solution to my problem. I have a surface which is a whole bunch of planes combines (to create a plane with many faces). When an object with collider (and rigid body) hits the plane I want to obtain a list of the faces that were hit by the collider so that I can manipulate those faces. Is there a way to do this or should I work out a way to do it myself?

I'll keep looking and trying to figure it out but if anyone has a solution for me that would be great :D

Share this post


Link to post
Share on other sites

Hey Nakor,

Welcome to the ProBuilder community :) I'm not too sure about this process to be honest but Gabriel or Karl may have an answer for you. I'll be sure to link them to this and see if there's a way!

Regards,

Mike

Share this post


Link to post
Share on other sites

That's an interesting problem. There isn't a built in method for this specifically, but you may find

pb_Object::QuadWithTriangle(int[] tri) : pb_Face

useful.

How accurate does the collision have to be? If an approximation would work, using a raycast to find the hit triangle and extrapolating neighbor triangles would probably be the easiest.

Share this post


Link to post
Share on other sites

Yeah that is what I was just looking into. I was looking at Collider.raycast but the documentation says something odd so I guess I'll just have to try it and see how it works.

Unity documentation says: "Casts a Ray that ignores all Colliders except this one." The way I read that it doesn't sound very useful lol but I'll see. The rays only have to go straight down on the Y axis though so I could just set up a point for every square of my rigid body colliders and have them fire to get the faces.

So that QuadWithTriangle function takes a int array (of the hit triangle) and gives me the face? That's what I'm guessing by your documentation as it returns a pb_Face.

Share this post


Link to post
Share on other sites

Ok so I decided to drop the collision idea and go with doing a raycast each move (each move is basically a single grid move). Instead of checking for collision or trigger I am casting a ray only 1.0f and when it hits I am having it raise the face. Oddly though, it has totally fubar'd my prefab lol and I will have to make a new one. I'm not sure why it is writing changes to the prefab as I am instantiating it and making changes in game. I will have to find a work around for that.

Share this post


Link to post
Share on other sites

Quick sample code for this - sorry I'm on my way out the door atm, but I can answer any questions with this in a few hours.

using UnityEngine;
using System.Collections;

public class CollisionListener : MonoBehaviour
{
public void OnCollisionEnter(Collision hit)
{		
	Vector3[] points = new Vector3[hit.contacts.Length];
	for(int i = 0; i 			points[i] = hit.contacts[i].point;

	pb_Object pb;
	pb_Face face;

	FaceCheck(transform.position, -transform.up, out pb, out face);

	Debug.Log(pb.ToString() + "\nFace: " + face + "\nCollision Points: " + points.ToFormattedString("\n"));
}

public bool FaceCheck(Vector3 origin, Vector3 direction, out pb_Object pb, out pb_Face face)
{
	RaycastHit hit;

	if( Physics.Raycast(origin, direction, out hit))
	{
		pb = hit.transform.gameObject.GetComponent();

		if(pb == null)
		{
			face = null;
			return false;
		}

		Mesh m = pb.msh;

		int[] tri = new int[3] {
			m.triangles[hit.triangleIndex * 3 + 0], 
			m.triangles[hit.triangleIndex * 3 + 1], 
			m.triangles[hit.triangleIndex * 3 + 2] 
		};

		face = pb.QuadWithTriangle(tri);
		return true;
	}

	pb = null;
	face = null;
	return false;
}
}

Share this post


Link to post
Share on other sites

I just read your earlier post about prefabs screwing things up. Good news! This is fixed internally, and will be included in the next release. Glad to hear you've got it working otherwise though.

Share this post


Link to post
Share on other sites

Well to be clear I wasn't sure it wasn't your intention. It is more likely that a game would need to save changes made by the player than not. Still, glad to see it is fixed :)

Share this post


Link to post
Share on other sites

Just wanted to say that I noticed the mesh colliders do not update when the base mesh changes. I am working around this by recalculating but I wanted to make sure you were aware of it.

I'm making progress :)

Share this post


Link to post
Share on other sites

That's strange. Any call to `TranslateVertices` should result in a call to `Refresh` as well, which will recalculate the mesh collisions. If you wouldn't mind, could you post a snippet of code to reproduce this bug?

Share this post


Link to post
Share on other sites

Sorry for the delay, I had to go to the day job today. Here is the code that makes the changes. I wrote it pretty quickly to test things so it isn't exactly the way I want it but I am rewriting it now anyway.

do{
		pb = colInfScript.PBObject;

		pb_Face currentFace = colInfScript.ListPop();

		Vector3 nrml = pbUtil.PlaneNormal(pb.VerticesInWorldSpace(currentFace));
		if(Input.GetKey(KeyCode.LeftShift))
			pb.TranslateVertices(currentFace.DistinctIndices(), nrml.normalized * -.5f);
		else
			pb.TranslateVertices(currentFace.DistinctIndices(), nrml.normalized * .5f);


		i++;
		Debug.Log("i:  " + i);


		if(!colInfScript.ListContainsFaceItems())
			exitTheLoop = true;

	}while(!exitTheLoop);

	Mesh newMesh = new Mesh();
	newMesh = pb.gameObject.GetComponent().mesh;
	newMesh.RecalculateBounds();
	pb.gameObject.GetComponent().sharedMesh.Clear();
	pb.gameObject.GetComponent().sharedMesh = newMesh;

I am working on spreading out these translations over a few frames. I haven't used coroutines in C# yet so I'm reading up on them. I have a problem right now where changing all of the faces at once seems to error my game out. My test piece has 5 face pieces (changes 5 faces). I am fairly certain that this is due to the amount of time it takes to update the mesh.

I was thinking it might work better to change all of the faces at once but it doesn't seem to work as I had hoped. You may know of a better way to do this though. I am not using collisions because the mesh has to be convex and I find that the collider mesh does not update and therefore just raises up one point and stays like that. I don't really need collisions though so that is not a big deal. I am using a trigger collider on the ground.

Share this post


Link to post
Share on other sites

How does your runtime API handle it's meshes? My script seems to bottleneck on GC and I am seeing a large number of meshes in profiler. Is it me doing something stupid in my script or is it just because the API is still very early stage?

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class OnPieceContact : MonoBehaviour 
{
private bool _done = false;

private List colBlocks;

public LayerMask objectLayersToHit;

private pb_Face _face;
private pb_Object _pb;

public GameObject meshObject;


void Start () 
{
	colBlocks = new List();

	foreach(Transform child in transform)
	{
		if(child.tag == "pieceCollider")
		{
			colBlocks.Add(child.gameObject);
		}
	}
}

void Update () 
{

}

public void ContactDetected()
{
	StartCoroutine(IterateChildren());
}


public IEnumerator IterateChildren()
{
	bool exitTheLoop = false;

	meshObject.renderer.enabled = false;

	int i = 0;

	do{
		GameObject currentBlock = colBlocks[0];

		if(currentBlock == null)
		{
			return false;
		}

		if(FaceCheck(currentBlock.transform.position))
		{
			colBlocks.Remove(currentBlock);

			Vector3 nrml = pbUtil.PlaneNormal(_pb.VerticesInWorldSpace(_face));
			if(Input.GetKey(KeyCode.LeftShift))
				_pb.TranslateVertices(_face.DistinctIndices(), nrml.normalized * -.5f);
			else
				_pb.TranslateVertices(_face.DistinctIndices(), nrml.normalized * .5f);

			i++;
			Debug.Log(i);
			currentBlock.SetActive(false);

			Object.DestroyImmediate(currentBlock);
		}
		else
		{
			colBlocks.Remove(currentBlock);
		}


		if(colBlocks.Count <= 0)
			exitTheLoop = true;

		if(!exitTheLoop)
			yield return null;

	}while(!exitTheLoop);

	_done = true;

	ResetForRemainingPieces();

	yield return null;
}

public bool OperationsComplete()
{
	return _done;
}

public void ResetForRemainingPieces()
{
	_done = false;
	_pb = null;
	_face = null;

	colBlocks = new List();

	meshObject.renderer.enabled = true;

	foreach(Transform child in transform)
	{
		if(child.tag == "pieceCollider")
		{

			if(child.gameObject.activeSelf)
			{
				Debug.Log("Adding:  " + child.name);
				colBlocks.Add(child.gameObject);
			}
		}
	}

	if(colBlocks.Count <= 0)
	{
		transform.GetComponent().Fsm.Event("dieNow");
	}
	else
	{
		transform.GetComponent().Fsm.Event("childOpsComplete");
	}
}

public bool FaceCheck(Vector3 pos)
{

	Ray ray = new Ray(pos, Vector3.down);

	RaycastHit hit;

	if( Physics.Raycast(ray.origin, ray.direction, out hit, 1.0f, objectLayersToHit))
	{
		pb_Object hitpb = hit.transform.gameObject.GetComponent();

		if(hitpb == null)
			return false;

		Mesh m = hitpb.msh;


		int[] tri = new int[3] 
		{
			m.triangles[hit.triangleIndex * 3 + 0], 
			m.triangles[hit.triangleIndex * 3 + 1], 
			m.triangles[hit.triangleIndex * 3 + 2] 
		};


		_pb = hitpb;
		_face = hitpb.QuadWithTriangle(tri);
		return true;
	}
	return false;
}
}

Share this post


Link to post
Share on other sites

The API won't create new meshes unless you explicitly tell it to do so. Calling TranslateVertices() only recalculates the vertices and reassigns it to the existing mesh. As far as I can tell, that's the only API call in your code that could feasibly leak.

Share this post


Link to post
Share on other sites

Yeah that's pretty much what I thought. I'll have to dig deeper to see what is happening. Is there any way I can tell the API I want to change 5 faces so it can do it in one operation?

Share this post


Link to post
Share on other sites

Yup, TranslateVertices() takes an int array containing the triangle indexes of every face you need to move. It's a little tricky in that it requires that no two indices point to the same point in world space though (a limitation of the method that I probably will address in the future).

It is easy to grab unique triangle indices though. Sample code follows:

pb_Face[] facesToMove = GetFaces();    // All the faces you'd like to translate

int[] tris = pb.DistinctTriangles(facesToMove); 

pb.TranslateVertices(tris);

Share this post


Link to post
Share on other sites

Hey I just ran this profile to try to figure out why it still bottlenecks and this is what I found. Any insight?

I tested in a stand alone build too so I doubt it is editor related.

Share this post


Link to post
Share on other sites

Whoa, that's lot of garbage :)

This is all from one call to 'TranslateVertices' eh? I'm not sure why UpdateQuadUV() is accessing the mesh at all - it should draw from the vertex cache. Sounds like a bug in the API. I'll look into getting this fixed.

Share this post


Link to post
Share on other sites

Yeah just one call passing in the array like you explained. I tested this by creating a much smaller prefab for my ground and it is fairly fast but the amount of time it takes is still visible without looking for it.

Share this post


Link to post
Share on other sites

Hey Nakor! Just thought I'd throw my two cents in. if you are still having trouble I'd take a look at SetPivot.cs it is a pretty good example of translating vertices with the ProBuilder API.

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×