- Home /
How can I loop through every 3D point on a plane's surface in C#?
I'm making a model editor for Unity 5 that will allow the developer to draw an image, and then inflate the image into a 3D object. Of course, it would not inflate transparent points. In order to do this, I've created a script that's connected to a plane with particles/alpha blended enabled in order to ensure the plane shows both sides. I've also created a custom editor script that links to the plane's script. I decided it would be a good idea to disable the mesh renderer when the plane is inflated to ensure people can't see the deflated object. What I want to do is to loop through each Vector3 on the plane's surface and create a new object with new points based on the pixels in the object.
So the question is: How can I loop through every 3D point on a plane's surface in C#?
Bonus points if you can help me find the color of each point, but I think I have that figured out.
UPDATE:
Thanks for all the support! I actually thought of a way to do this that I'm suprised I never thought of. My idea only works well with planes, but that's all that I need. I also figured out how to filter colors! You can view the code below.
public static IEnumerable<Vector3> GetPoints(GameObject obj, float res)
{
Vector3[] verts = obj.GetComponent<MeshFilter>().sharedMesh.vertices;
float x1 = float.MaxValue;
float x2 = 0;
float z1 = float.MaxValue;
float z2 = 0;
foreach (Vector3 vert in verts)
{
if (vert.x < x1)
{
x1 = vert.x;
}
if (vert.x > x2)
{
x2 = vert.x;
}
if (vert.z < z1)
{
z1 = vert.z;
}
if (vert.z > z2)
{
z2 = vert.z;
}
}
Vector2 size = new Vector2(Mathf.Abs(x1 - x2), Mathf.Abs(z1 - z2));
Texture2D tex = ((Texture2D)obj.GetComponent<Renderer>().sharedMaterial.mainTexture);
for (float x = -(size.x / 2); x <= size.x / 2; x += res)
{
for (float z = -(size.y / 2); z <= size.y / 2; z += res)
{
Vector3 V3 = obj.transform.TransformPoint(new Vector3(x, 1, z));
if (tex.GetPixel(tex.width - (int)(((x + (size.x / 2)) / size.x) * tex.width),
tex.height - (int)(((z + (size.y / 2)) / size.y) * tex.height)).a > 0.5f)
yield return V3;
}
}
}
If anyone is wondering how to use the code above, here's a snipplet (0.1f is the increment):
foreach (Vector3 V3 in GetPoints(gameObject, 0.1f))
{
GameObject t = Instantiate(gameObject2);
t.transform.position = V3;
}
Here's a couple screenshots of what you can do:
So now I know how to loop through each vertice. All I had to do is get the $$anonymous$$eshFilter component to get the mesh, and from that I got the vertices. The problem is that the vertices aren't on a pixel level, and they don't match up in the game. The distance between the vertices is 10 x 10, and it ranges from -5 to 5. I need to know how to get all the vertices in between based on the actual scale on the plane.
Will raycasting over the entire X/Y size of the object do it? If you know the world position of the plane then you could deter$$anonymous$$e height by measuring the distance from the ray cast hit to that. I expect it would be slow though.
If the texture will be a 2D material then try
var material = theTarget.renderer.material;
var texture2D = (Texture2D)material.mainTexture;
var color = texture2D.GetPixel(..................)
I like your idea, but I wonder if there's a way to do it with vertices. This idea sounds great, but I'm wondering if the complexity is called for. $$anonymous$$aybe it could be a bit more optimized.
Answer by Glurth · Jul 26, 2016 at 04:55 PM
You mentioned that you now know how to get each vertex of the MeshFilter's mesh. These verticies are defined in model-space. In order to convert these to world-space coordinates use the MeshFilter's transform. e.g.
worldVertexPoint=meshfilter.gameobject.transform.TransformPoint(modelVertexPoint);
You could now put an object at that particular worldVertexPoint like so:
someObject.transform.position= worldVertexPoint;
https://docs.unity3d.com/ScriptReference/Transform.TransformPoint.html
Regarding getting the color of the texture, at that vertex: Lets go back a step- lets say you got your vertex using
modelVertexPoint= mesh.vertices[vertexNumber];
We can also get the Texture coordinate (UV), of that vertexNumber
Vector2 vertexUV = mesh.uv[vertexNumber];
You can then feed this UV coordinate, into the GetPixel function that MrPMorris mentioned.
The advantage to this method is, it eliminates the need to do any raycasting. It does NOT account for any shadows or lighting though.
note: Colored verticies are optional in a Mesh, and they require a vertex-color-shader to work. Fairly rare nowadays. If you import the mesh it might not have vertex colors defined. If you ARE using colored verticies , rather than a texture, it's even simpler:
Color vertexColors= mesh.color[vertexNumber];
No additional points needed, thanks tho! Accepting it as the answer will grant me points anyway :) What about the color stuff? Get that figured out? Are you using vertex colors(in which case, you now have the answer) or textures?
No problem! Unfortunately, mesh.color32 is returning an empty list. It would be great if you could help me figure that out :)
Thanks to you, I was able to retrieve some of the pixels, but is it possible to get the same number of vertices as the number of pixels in the image?
Thanks!
Not with any of the methods I've described, no. It's starting to sound like what you want is a surface shader. A surface-shader performs processing on each pixel of a drawn object. If you are just looking for a random pixel, here and there, then $$anonymous$$rP$$anonymous$$orris's raycast suggestion might be suitable.
Thanks for the reply. Could a surface shader send information back to c# so that it may be processed later?
EDIT: I just found out that what I just asked is totally possible. Thank you SO $$anonymous$$UCH! I now know what to do. :)