Closest point on triangle

I am thinking about splat decals and I need to be able to check if any part of a triangle (represented by 3 Vector3s) is inside a sphere (represented by a point and radius).

Talking on the IRC channel it looks like the best way to check is to get the point on the triangle that is closest to the center of the sphere, and then check if the distance between it and the center on the sphere is < radius.

What I have thought up to do for starters is this: Is the distance between the center of the triangle and any of it’s vertices greater than the distance between the center of the triangle and the center of the sphere minus the radius.

Here is my code as it is right now.

//TextureSplat.js

var material : Material;
var rotationAcuracy = 10;
var radius = 0.00;
var splatSize = 0.00;

class Root
{
	var meshes = new Array();
	var transform : Transform;
}

function Start () 
{
	var hit : RaycastHit;
	var rays = 0;
	var hits = 0;
	var direction : Vector3;
	var explosionDirection : Vector3;
	while(rays < rotationAcuracy)
	{
		direction = (-direction / 2) + Random.onUnitSphere;
		if(Physics.Raycast(transform.position, direction, hit, radius))
		{
			explosionDirection += ((-direction.normalized / 2) + hit.normal.normalized) * (1 - (hit.distance / radius));
			hits ++;
		}
		rays ++;
	}
	
	if(hits == 0)
	{
		return;
	}
	else
	{
		transform.rotation = Quaternion.LookRotation(explosionDirection);
	}
	

	var roots = new Array();
	colliders = Physics.OverlapSphere (transform.position, radius);
	for(collider in colliders)
	{
		m = collider.gameObject.GetComponent(MeshFilter);
		if(m)
		{
			found = null;
			for(r in roots)
			{
				if(collider.transform.root == r.transform) found = r;
			}
			
			if(found)
			{
				found.meshes.Add(m);
			}
			else
			{
				r = new Root();
				r.transform = collider.transform.root;
				r.meshes.Add(m);
				roots.Add(r);
			}

		}
	}
	
	for(r in roots)
	{
		newVertices = new Array();
		newNormals = new Array();
		newTriangles = new Array();
		
		totalVerts = 0;
		for(var m : MeshFilter in r.meshes)
		{
			totalVerts += m.mesh.vertices.length;
		}
		usedVertices = new int[totalVerts];
		usedCount = 0;
	
		otherMeshVerts = 0;
		for(var envMesh : MeshFilter in r.meshes)
		{
			i = 0;
			while(i < envMesh.mesh.triangles.length)
			{
				p1 = envMesh.transform.TransformPoint(envMesh.mesh.vertices[envMesh.mesh.triangles[i]]);
				p2 = envMesh.transform.TransformPoint(envMesh.mesh.vertices[envMesh.mesh.triangles[i + 1]]);
				p3 = envMesh.transform.TransformPoint(envMesh.mesh.vertices[envMesh.mesh.triangles[i + 2]]);
				center = (p1 / 3) + (p2 / 3) + (p3 / 3);
				toHereFromFace = transform.position - center;
				distanceFromCenter = toHereFromFace.magnitude - radius;
				
				if(distanceFromCenter < (center - p1).magnitude || distanceFromCenter < (center - p2).magnitude || distanceFromCenter < (center - p3).magnitude)
				{
					faceNormal = Vector3.Cross(envMesh.mesh.vertices[envMesh.mesh.triangles[i + 2]] - envMesh.mesh.vertices[envMesh.mesh.triangles[i + 1]], envMesh.mesh.vertices[envMesh.mesh.triangles[i + 1]] - envMesh.mesh.vertices[envMesh.mesh.triangles[i]]);
					if((-envMesh.transform.TransformDirection(faceNormal).normalized - toHereFromFace.normalized).magnitude < 1.6)
					{	
						if(!usedVertices[envMesh.mesh.triangles[i] + otherMeshVerts])
						{
							usedVertices[envMesh.mesh.triangles[i] + otherMeshVerts] = usedCount;
							newVertices.Add(p1);
							newNormals.Add(envMesh.transform.TransformDirection(envMesh.mesh.normals[envMesh.mesh.triangles[i]]));
							newTriangles.Add(usedCount);
							usedCount ++;
						}
						else
						{
							newTriangles.Add(usedVertices[envMesh.mesh.triangles[i] + otherMeshVerts]);
						}
		
						if(!usedVertices[envMesh.mesh.triangles[i + 1] + otherMeshVerts])
						{
							usedVertices[envMesh.mesh.triangles[i + 1] + otherMeshVerts] = usedCount;
							newVertices.Add(p2);
							newNormals.Add(envMesh.transform.TransformDirection(envMesh.mesh.normals[envMesh.mesh.triangles[i + 1]]));
							newTriangles.Add(usedCount);
							usedCount ++;
						}
						else
						{
							newTriangles.Add(usedVertices[envMesh.mesh.triangles[i + 1] + otherMeshVerts]);
						}
		
						if(!usedVertices[envMesh.mesh.triangles[i + 2] + otherMeshVerts])
						{
							usedVertices[envMesh.mesh.triangles[i + 2] + otherMeshVerts] = usedCount;
							newVertices.Add(p3);
							newNormals.Add(envMesh.transform.TransformDirection(envMesh.mesh.normals[envMesh.mesh.triangles[i + 2]]));
							newTriangles.Add(usedCount);
							usedCount ++;
						}
						else
						{
							newTriangles.Add(usedVertices[envMesh.mesh.triangles[i + 2] + otherMeshVerts]);
						}
					}
				}
				
				i += 3;
			}
			otherMeshVerts += envMesh.mesh.vertices.length;
		}
			
		if(newVertices.length > 0)
		{
			decal = new GameObject("Decal");
			decal.transform.position = transform.position;
			decal.transform.rotation = transform.rotation;
			decal.transform.parent = r.transform;
			decal.transform.localScale = Vector3(1, 1, 1);
		
			newUV = new Vector2[newVertices.length];
			newColors = new Color[newVertices.length];
			
			i = 0;
			for(v in newVertices)
			{
				v = decal.transform.InverseTransformPoint(v);
				newUV[i].x = (v.x / splatSize) + 0.5;
				newUV[i].y = (v.y / splatSize) + 0.5;
				
				a = Mathf.Abs(v.z) / radius;
				newColors[i] = Color.Lerp(Color.white, Color.clear, a * a * a);
				
				newNormals[i] = decal.transform.InverseTransformDirection(newNormals[i]).normalized;
				i ++;
			}
			
			dMesh = decal.AddComponent(MeshFilter);
			ren = decal.AddComponent(MeshRenderer);
			ren.material = material;
			
			var mesh = new Mesh ();
			
			mesh.vertices = newVertices;
			mesh.normals = newNormals;
			mesh.uv = newUV;
			mesh.colors = newColors;
			mesh.triangles	= newTriangles;

			dMesh.mesh = mesh;
		}
	}
}

Alright so it works ok with that script (I updated it) but I still would like to improve my triangle picking and work on smoothing out the uv maps.

In action: www.yoggy.bluegillweb.com/site/webplayers/splat

can’t help you with the code but just wanted to say that’s pretty sweet. i was wondering if something like that could be done.

nice one! wikiwikiwiki… plz :smile:

[forgot to mention… rockets affected by gravity is a nice touch too!]

Very cool Yogster
AC

Thanks!
This helps me a lot!

Heh, what happened to the domain?

A lot can change in 2.5 years…

–Eric

looks like

http://www.unifycommunity.com/wiki/index.php?title=TextureSplat

could use an update!