- Home /
How do i find the top of a mesh?
I have a dynamically generated mesh, of a theoretically arbitrary shape. But it's definitely convex, not smooth and manifold. There is no guarantee of how much of its bounds are filled
What i want to do is find a vector which sits on top of the mesh. Basically, a line starting from the centre of the mesh and going in the world direction <0,1,0> until it exits the mesh.
Note world direction, this is important. I need to find the top of the mesh right now, however it happens to be rotated. And i'm doing this on dynamic objects which are going to roll/slide around on the ground a fair bit
Answer by Bunny83 · Dec 09, 2014 at 05:36 AM
Well it's a bit vague what you really want. Having a vector starting at the center and going into world-y-axis doesn't need to exit the mesh at the top most point. So do you need the top most vertex or the exit point of your "ray" from the origin up?
To find the topmost vertex in world space there are basically two ways:
Use Transform.TransformPoint on each vertex to get the worldspace position and do a max-search similar to what Cherno did.
- get the worldspace up orientation into localspace and do some magic there.
Either project each local space point onto the normalized worldspace up vector which you first transform into localspace. You'll end up with direction vectors which length represent the distance from the origin in world up direction. You can use the sqrMagnitude to find the largest value.
Another way is to use Unity's Plane struct (which represents a mathematical plane) and use again the up vector transformed into localspace as normal and 0,0,0 as origin. Now you can simply use Plane.GetDistanceToPoint. Just again search for the largest value.
To get the worldspace up direction in local space just use InverseTransformDirection
transform.InverseTransformDirection(Vector3.up);
If you want to find the exit point of the vector starting at the origin along the y axis it's the easiest way to use a MeshCollider and doing a Raycast from outside to the center. You can use Collider.Raycast in this case.
edit
To get the highest vertex you would do something like:
Vector3[] verts = GetComponent<MeshFilter>().sharedMesh.vertices;
Vector3 topVertex = new Vector3(0,float.NegativeInfinity,0);
for(int i = 0; i < verts.Length; i++)
{
Vector3 vert = transform.TransformPoint(verts[i]);
if(vert.y > topVertex.y)
{
topVertex = vert;
}
}
For this you need to convert every vertex into worldspace. The second way, to project the worldspace up vector into local space and use the Plane object would look like this:
Vector3 worldUp = transform.InverseTransformDirection(Vector3.up).normalized;
Plane p = new Plane(worldUp, Vector3.zero);
Vector3[] verts = GetComponent<MeshFilter>().sharedMesh.vertices;
Vector3 topVertex = Vector3.zero;
float maxDist = float.NegativeInfinity;
for(int i = 0; i < verts.Length; i++)
{
float dist = p.GetDistanceToPoint(verts[i]);
if (dist > maxDist)
{
maxDist = dist;
topVertex = verts[i];
}
}
topVertex = transform.TransformPoint( topVertex );
thanks for fixing the vertex list method bunny, testing this out now, it's running a lot faster than i expected. i'll benchmark it a bit to see if it's suitable for my needs.
do you think this is faster than raycasting down from above, though? my thought is not.
@Nanako: Not necessarily. I guess that Raycasting is probably faster but only when the vertices don't change. Updating a $$anonymous$$eshCollider is horrible for performance. However no matter which is actually faster you get two completely different results as the top most point doesn't need to be above the center in the world up axis. That's why i initially asked what exactly you want.
Again the point where the up-axis leaves your mesh doesn't need to be the top most point as the top most point could be offset from the objects center.
Just imagine a default cube rotated by 10° on the x axis. There are two vertices which are at the topmost position (actually 6 vertices as the two corners are splitted in 3 vertices each). However a vector starting at the cubes center going up would leave the mesh earlier. So it's not the topmost position. The point is on the exact surface at this point. If the cube is rotated 45° then the exit point will be exactly between the two top most vertices at the center of the edge.
You asked an vague question since you didn't specify what exact point you're searching. You have given two different descriptions. Be more specific and you can expect a more specific answers ;) However i'm off, i need some sleep, good luck.
I agree, the question is intentionally vague. I guess what i want is, whichever of the two is faster to retrieve. Any point that is approximately "at the top" of the mesh will do for my purposes.
so far the vertex list combing method gives me about 0.13ms per object, i'll see how the raycasting goes.
Answer by Cherno · Dec 09, 2014 at 03:18 AM
Coincidentally, I just did something similar for my own proc. generated mesh. I did it like this:
Vector3 topVertex = GetComponent<MeshFilter>().sharedMesh.vertices[0];
for(int i = 0; i < GetComponent<MeshFilter>().sharedMesh.vertices.Length - 1; i++) {
Vector3 vertex = GetComponent<MeshFilter>().sharedMesh.vertices[i];
Vector3 nextVertex = GetComponent<MeshFilter>().sharedMesh.vertices[i + 1];
if(vertex.y > nextVertex.y) {
topVertex = vertex;
}
else if(vertex.y < nextVertex.y) {
topVertex = nextVertex;
}
}
}
This will get you the highest vertex position of the mesh.
wait a $$anonymous$$ute, this would imply that the vertices list is already rotated to the object's rotation. is that correct?
for that matter, is it also scaled to the scale of the object?
I am not 100% sure but I think that the .vertices array of a mesh is just a collection of Vector3s that are relative to world space. So this function would always give the highest vertex position in world space, no matter the scale and rotation.
I guess you could also use Linq to do it with a proper sorting function, comparing each y value.
yeah, sorry, this solution is useless. The vertex array is in strictly local coordinates. The vertices are not scaled or rotated, you'd have to do a whole load of math on the entire array BEFORE finding the top vertex. I don't have the time to do that in my code, unless there's absolutely no faster way.
I suggest you go back and re-evaluate your own code, this method is not going to work for you either without more effort.
@Cherno: You don't seriously use that code do you? You know that everytime you use shared$$anonymous$$esh.vertices Unity will create a new array and copy the vertices into that array. You have to store the array in a local variable once, before the loop.
Also like @Nanako said vertices are in local space.
@Nanako: First of all you still have a shared$$anonymous$$esh.vertices in your for-loop's header ^^
Second, you're doing some strange things with "topVertex" and "vertex".
I just realized it's just what Cherno did ^^ I'll edit my answer and add the correct sorting
edit
@Cherno:
You only compare two neighboring vertices in the vertices list. This won't get the the vertex with the largest y value. Also your two conditions are almost the opposite from each other. That means you would set a new topVertex each iteration. So you actually end up just comparing the last two vertices in the array. You have to compare the vertex with the current topVertex.
Answer by zainjer · Jul 16, 2020 at 01:57 PM
I recently encounter a situation where I had to find the Highest Vertex within a given Mesh.
I found the following to be the most efficient way,
Vector3 FindHighestVert(MeshFilter targetMesh)
{
var maxBounds = targetMesh.sharedMesh.bounds.max;
Matrix4x4 localToWorld = targetMesh.transform.localToWorldMatrix;
Vector3 hi = localToWorld.MultiplyPoint3x4(maxBounds);
return hi;
}
Your answer
Follow this Question
Related Questions
Mesh Colliders as triggers not working... Work-arounds (raycast)? 4 Answers
issues with raycasts 0 Answers
How to get the pixel depth values of the camera view? 0 Answers
Possible to use sprite mesh for UI.Image GraphicRaycaster culling? 1 Answer
nav mesh agent stop ScreenPointToRay after a while 0 Answers