- Home /
Finding Viewport Coordinates of Vertices of Meshes that are in View
Hi, I have an array of Tree meshes called myTrees. I am trying to get each mesh's viewport coordinates and write to files so I can later post process in Python. There is only one camera in the project and the screenshot script takes exactly the correct thing on the viewport. But the files written come out to be totally different (example: say I have one tree mesh in view, the screenshot would capture it correctly but the file writes like 41 tree mesh's vertices' coordinates. I suspect I am not understanding some kind of Viewport/World/Scene point transformations. Any help would be amazing. Thanks. The relevant code snippet is below. Also, I am checking if x and y are between 0 and 1 to make sure they are inside the viewport. FYI. my viewport and target size is fixed at 832x832 and I have set up the scene like so. Any advice would be great. You can ignore everything after line 20.
for (int i = 0; i < myTrees.Count; i++) {
Mesh meshT = myTrees[i].GetComponent<MeshFilter>().sharedMesh;
Vector3[] verticesT = meshT.vertices;
int[] triangles = meshT.triangles;
string treeId = myTrees[i].GetInstanceID().ToString();
int tid=0;
for (int j = 0; j < triangles.Length; j++) {
if (j % 3 == 0) {
int triangleId = tid++;
//Converting to World Point
Vector3 world_t = transform.TransformPoint(meshT.vertices[j]);
//Converting World Point to ViewPort Point
Vector3 world_v = Camera.main.WorldToViewportPoint(world_t);
float x = (float) world_v.x ;
float y = (float) world_v.y;
float z = (float) world_v.z;
float r = meshT.colors32 [j].r;
float g = meshT.colors32 [j].g;
float b = meshT.colors32 [j].b;
//Check to see if inside viewport i.e., inside [0,1]
if (x >= 0f && y >= 0f && x<=1f && y<=1f ) {
string col = "" + r.ToString () + "," + g.ToString () + "," + b.ToString ();
string position = x.ToString ("0.0000") + "," + y.ToString ("0.0000") + "," + z.ToString ("0.0000");
string fullId = treeId + "-" + triangleId.ToString ();
line = fullId + "," + treeId + "," + triangleId.ToString () + "," + col + "," + position;// + "," +cam.pixelWidth.ToString()+","+cam.pixelHeight.ToString()+","+cam.pixelRect.ToString();
writer.WriteLine (line);
}
}
}
}
Please feel free to ignore anything after line 20. I think the issue is between line 10 and 20 (the point transformation). Please advice with any suggestion. Thank you.
Answer by Eno-Khaon · Mar 05 at 07:06 AM
Well, there's a bit going on here that looks either confusing or potentially incorrect, so let's break down the process:
-- // For each tree...
for (int i = 0; i < myTrees.Count; i++)
{
-- // Get its mesh, vertices, and triangles...
Mesh meshT = myTrees[i].GetComponent<MeshFilter>().sharedMesh;
Vector3[] verticesT = meshT.vertices;
int[] triangles = meshT.triangles;
-- // Get two unique identifiers for the tree
string treeId = myTrees[i].GetInstanceID().ToString();
int tid=0;
-- // for every triangle in the mesh (which come in groups of 3)
for (int j = 0; j < triangles.Length; j++)
{
-- // So, only check the first vertex per triangle?
-- // The for loop could be changed to better facilitate that process:
-- // for(int j = 0; j < triangles.Length; j+=3)
if (j % 3 == 0)
{
-- // Assign, then increment for output string(s)
int triangleId = tid++;
//Converting to World Point
-- // How are you not going out of bounds of the array?
-- // Also, you already made a copy of that array, yet you
-- // make a new copy with every pass.
-- // -> verticesT[triangles[j]] <-
-- // ----------------------------------------
-- // ALSO, you're not using the correct Transform data.
-- // You're positioning the vertices based on the current
-- // Transform's position/rotation/scale rather than the
-- // tree itself. You should use:
-- // myTrees[i].transform.TransformPoint(verticesT[triangles[j]]);
Vector3 world_t = transform.TransformPoint(meshT.vertices[j]);
//Converting World Point to ViewPort Point
-- // Then you take the world-space position of the tree
-- // (or whatever it currently is) and get the main camera's
-- // viewport position of that.
Vector3 world_v = Camera.main.WorldToViewportPoint(world_t);
-- // Redundant casts to "float"?
float x = (float) world_v.x ;
float y = (float) world_v.y;
float z = (float) world_v.z;
-- // Like up above, if you're going to access these
-- // arrays, and especially multiple times per pass,
-- // you should probably cache the array data.
float r = meshT.colors32 [j].r;
float g = meshT.colors32 [j].g;
float b = meshT.colors32 [j].b;
So, essentially, it's mainly just unclear what the intent is with respect to using only the first vertex of each triangle, and your logic for transforming local to world positions simply isn't viable at all.
To reiterate a few notes I inserted into the script, elements that could be cleaned up a bit for more efficient usage could include:
Reading Mesh data was given improvements over time, so using...
List<Vector3> treeVerts = new List<Vector3>();
Mesh.GetVertices(treeVerts);
List<int> treeTris = new List<int>();
Mesh.GetTriangles(treeTris);
List<Color32> treeColors = new List<Color32>();
Mesh.GetColors(treeColors);
... should be a viable replacement. Note that this isn't *universally* true, especially since you're not currently reusing them later, but it's generally just a good point of reference anyway.
With the idea of using List<>(s) in mind, this would also change the syntax for adapting the for loop (as necessary), as well as skipping the modulo operation altogether:
for(int j = 0; j < triangles.Count; j+=3)
The purpose of using TransformPoint() is to test which trees are within view of the camera, so the vertices of each tree need to be translated to the positions of, well, the trees. Doing this means making use of each tree's Transform data as the vertices of its Mesh are being tested. I'm not sure which GameObject this script is currently attached to, but it is almost definitely NOT the one you're intending to use in this situation:
// Assuming "myTrees" IS NOT an array of Transforms...
// If it is, then the ".transform" step is obviously unnecessary.
myTrees[i].transform.TransformPoint(verticesT[triangles[j]]);
Fundamentally, the rest looks like it's probably in reasonable shape at this point. Apart from some redundancy (it's not necessary to read each of the X/Y/Z-axis values out of "world_v" to their own floats, as well as re-casting those floats as floats), there probably aren't any other crippling problems in this situation.
Thank you so much. You are a lifesaver. Really appreciate how you annotated the code and additionally provided workflow directions. The "transform" part got my thing working.
Really grateful for the suggestions. Now I need to figure out how to make it more efficient, meaning, every time I do this (bound to a keypress), the whole myTrees array needs to be searched which has hundreds of trees - each with thousands of triangles. Having lists like you suggested would still have the same asymptotic complexity (loops' iterations) as far as I understand; if you have any suggestions about this, I'll be extremely grateful.
Your answer
Follow this Question
Related Questions
Alternative Ways of Finding Vertices 2 Answers
Determine neighbor boundary vertices order 0 Answers
mask one gameObject mesh by another mesh vertex transparency 1 Answer
Mesh SetVertices() vs vertices 2 Answers