The easiest way to get a random point on the surface of an irregular mesh in Unity would be to cast a random ray against the mesh. Every collider have a dedicated Raycast function to test only against this collider.
Both functions are not tested yet
function GetPointOnMesh() : RaycastHit{
var length : float = 100.0;
var direction : Vector3 = Random.onUnitSphere;
var ray : Ray = Ray(transform.position + direction*length,-direction);
var hit : RaycastHit;
collider.Raycast (ray, hit, length*2);
return hit;
}
The function returns a RaycastHit with all information on the point.
Another way would be to calculate a random triangle index and random barycentric coordinates and calculate the world position from this information.
function GetPointOnMesh() : RaycastHit{
var hit : RaycastHit;
var mesh : Mesh = GetComponent.<MeshFilter>().mesh;
hit.triangleIndex = Random.Range(0,mesh.triangles.Length/3);
var BC : Vector3;
BC.x = Random.Range(0.0,1.0);
BC.y = Random.Range(0.0,1.0-BC.x);
BC.z = Random.Range(0.0,1.0-BC.x-BC.y);
hit.barycentricCoordinate = BC;
var P1 : Vector3 = mesh.vertices[mesh.triangles[hit.triangleIndex + 0]];
var P2 : Vector3 = mesh.vertices[mesh.triangles[hit.triangleIndex + 1]];
var P3 : Vector3 = mesh.vertices[mesh.triangles[hit.triangleIndex + 2]];
hit.point = transform.TransformPoint(P1*BC.x + P2*BC.y + P3*BC.z);
// Interpolated vertex normal
var N1 : Vector3 = mesh.normals[mesh.triangles[hit.triangleIndex + 0]];
var N2 : Vector3 = mesh.normals[mesh.triangles[hit.triangleIndex + 1]];
var N3 : Vector3 = mesh.normals[mesh.triangles[hit.triangleIndex + 2]];
hit.normal = N1*BC.x + N2*BC.y + N3*BC.z;
return hit;
}
If you need the face normal instead of the interpolated vertex normal you have to calculate it.
[...]
var P1 : Vector3 = mesh.vertices[mesh.triangles[hit.triangleIndex + 0]];
var P2 : Vector3 = mesh.vertices[mesh.triangles[hit.triangleIndex + 1]];
var P3 : Vector3 = mesh.vertices[mesh.triangles[hit.triangleIndex + 2]];
hit.point = P1*BC.x + P2*BC.y + P3*BC.z;
// Face normal
hit.normal = Vector3.Cross((P1-P2),(P3-P2)).normalized;
[...]
Both functions returns a RaycastHit structure. Keep in mind that the second method doesn't set all fields of `hit`.
If your mesh is more or less convex but with very different triangle sizes, the better point distribution you will get with the first function.
If the mesh have almost equal sized triangles but a very custom non convex shape the second would be better.
The first one is like a spherical projection onto the mesh. The second will covers all mesh triangles, even when they are hidden be other parts of the mesh.
To use this function do something like that:
var prefab : GameObject;
function spawn() {
var randomPoint = GetPointOnMesh();
var spawnPreferences = Instantiate(prefab, randomPoint.point, Quaternion.identity);
spawnPreferences.transform.eulerAngles.y = Random.Range(0, 360);
}
You can use hit.normal with Quaternion.LookRotation to align the spawned object to the surface if you want but the axis that will point away from the surface have to be z in this case.