- Home /
Odd Duplicate when selecting Triangle from Mesh (with Barycentric Selection.)
What I'm trying to do is select a specific triangle on any given mesh, so I'm using the Barycentric algorithm. However, as seen here, there is some strange behaviors and certain locations and always an Unwanted Triangle. Best guess, it has something to do with inaccurate float precision of the Vector3.
Here is the Script I'm Currently Using:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpecificTriangleIndex : MonoBehaviour
{
public Camera m_camera;
public LayerMask layermask;
public Vector3[] vertices;
public Vector3[] normals;
public int[] triangles;
public List<int> triangleIndices = new List<int>(); ///Returned Triangle Indices -- What it thinks is selected.
public List<Vector3> baryAllens = new List<Vector3>(); ///Position of Verticies of Returned Triangles
private RaycastHit hit;
private Transform objectHit;
private Vector3 bary;
void Update()
{
if (Input.GetMouseButton(0))
{
EobardThawne();
Raycast();
}
}
void Raycast()
{
Ray ray = m_camera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, layermask))
{
objectHit = hit.transform;
MeshFilter meshFilter = hit.transform.gameObject.GetComponent<MeshFilter>();
Mesh mesh = meshFilter.sharedMesh;
vertices = mesh.vertices;
normals = mesh.normals;
triangles = mesh.triangles;
Vector3 p = transform.InverseTransformPoint(hit.point);
for (int i = 0; i < triangles.Length; i += 3)
{
Vector3 a = vertices[triangles[i]];
Vector3 b = vertices[triangles[i + 1]];
Vector3 c = vertices[triangles[i + 2]];
bary = GetBarycentric(a, b, c, p);
if (InTriangle(bary))
{
triangleIndices.Add(i / 3);
baryAllens.Add(a + objectHit.position);
baryAllens.Add(b + objectHit.position);
baryAllens.Add(c + objectHit.position);
}
}
for (int i = 0; i < baryAllens.Count; i += 3)
{
Vector3 v0 = baryAllens[i];
Vector3 v1 = baryAllens[i + 1];
Vector3 v2 = baryAllens[i + 2];
Debug.DrawLine(v0, v1, Color.green);
Debug.DrawLine(v1, v2, Color.green);
Debug.DrawLine(v2, v0, Color.green);
}
}
}
Vector3 GetBarycentric(Vector2 v1, Vector2 v2, Vector2 v3, Vector2 p)
{
Vector3 B = new Vector3();
B.x = ((v2.y - v3.y) * (p.x - v3.x) + (v3.x - v2.x) * (p.y - v3.y)) /
((v2.y - v3.y) * (v1.x - v3.x) + (v3.x - v2.x) * (v1.y - v3.y));
B.y = ((v3.y - v1.y) * (p.x - v3.x) + (v1.x - v3.x) * (p.y - v3.y)) /
((v3.y - v1.y) * (v2.x - v3.x) + (v1.x - v3.x) * (v2.y - v3.y));
B.z = 1 - B.x - B.y;
return B;
}
bool InTriangle(Vector3 barycentric)
{
return (barycentric.x >= 0.0f) && (barycentric.x <= 1.0f)
&& (barycentric.y >= 0.0f) && (barycentric.y <= 1.0f)
&& (barycentric.z >= 0.0f); //(barycentric.z <= 1.0f)
}
private void OnDrawGizmos()
{
Gizmos.color = Color.yellow;
Gizmos.DrawSphere(hit.point, .01f);
Gizmos.color = Color.cyan;
foreach (int i in triangleIndices)
{
Gizmos.DrawSphere(FindCenter(i * 3), .01f);
Debug.DrawLine(hit.point, FindCenter(i * 3), Color.red);
}
}
public void EobardThawne()
{
//Kill Barry
triangleIndices.Clear();
baryAllens.Clear();
}
public Vector3 FindCenter(int i)
{
Vector3 v0 = transform.TransformPoint(vertices[triangles[i]]);
Vector3 v1 = transform.TransformPoint(vertices[triangles[i + 1]]);
Vector3 v2 = transform.TransformPoint(vertices[triangles[i + 2]]);
Vector3 center = (v0 + v1 + v2) / 3;
return center;
}
}
Any Help or Insight would be Appreciated.
Also, RaycastHit.triangleIndex doesn't work in all cases, so thats why I am pursuing this.
Answer by Bunny83 · Mar 01 at 09:06 PM
You seem to use the methods I had posted over here or in the other linked post. However those methods takes "Vector2" values since they were meant to be used to look up uv coordinates, not spatial position data which is of course 3d and not 2d. You pass in your 3d coordinates into the method where the z component is essentially dropped.
Later in the comments below my answer I've also created the Barycentric struct which I posted to the unity wiki. Unfortunately the wiki does no longer exist. However we have the wayback machine ^^. This struct has 5 different constructors. So you can use Vector2, 3, 4 values as well as a version that takes Color values. So you should be able to create baricentric coordinates from any vertex attribute, given you do have the interpolated value (i.e. the uv coordinate or the local position).
Likewise the struct has several "Interpolate" methods in order to interpolate any other vertex attribute across the triangle based on the barycentric coordinate.
While your wiki and past answers have been an enormous help, I'm still a bit confused on how your "Interpolate" method does, and how to use it
Sorry, I wasn't home for the last couple of days ^^. Barycentric coordinates can be seen as interpolation factors in 2d space based on the triangle they belong to. They work similar to how the interpolation factors t
and (1-t)
work for a normal 1d lerp interpolation. You can simply interpolate any vertex attribute across the triangle. Vertex attributes are any attributes that are associated with vertices. This includes the position in 3d space, the uv coordinates in 2d space, the associated vertex color and even the vertex normal. Each vertex has one distinct value of those. Our barycentric coordinate represents a point in between those vertices. So by providing the 3 values of a certain vertex attribute we can calculate the interpolated value between the vertices according to the barycentric coordinate.
So you can use the different constructores of the Barycentric struct to calculate the barycentric coordinates based on a known value (like the position of a point inside a triangle and the 3 vertex positions) and then use the resulting struct to calculate any other interpolated vertex attribute at that point.
The original question I've linked asked about calculating the barycentric coordinate of a known 2d UV coordinate and compare that to the 3 UV coordinates of a triangle. If the requested point is inside a triangle we can then use the 3 vertex positions and the barycentric coordinates to calculate the position in 3d space. However you have a position (the hit.point value) so you have to use the vertex positions to calculate the barycentric coordinate of the hit point in relation to a certain triangle.
An important property of a barycentric coordinate is that the sum of the 3 factors will always add up to 1.0. A barycentric point is inside the triangle when all 3 factors are within a range of 0 <= v <=1
. Though for exampe (-5, 9, -3) would be a valid barycentric coordinate (it sums to 1.0), but it would lay outside the triangle but in the same plane as the triangle.
Keep in mind that when calculating the barycentric coordinate from a point in 3d space and the vertex positions, the point has to be on the surface / plane of the triangle. passing in a point that is above / below the triangle would give you a wrong coordinate. You also have to be careful with degenerate triangles (triangles with no area, so at least two of the vertices have the same position) as it's not possible to calculate a meaningful coordinate for such triangles.
Your answer
![](https://koobas.hobune.stream/wayback/20220613053649im_/https://answers.unity.com/themes/thub/images/avi.jpg)