Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question LibTressDotNet Example

Discussion in 'Scripting' started by Artpen, Mar 9, 2022.

  1. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291
    Hi guys,

    I have started using Clipper library in my project. And I would like to use LibTress lib for tessellation.
    But I can’t figure out how it works?
    Do you have an example ? Say I have a list of 5 Vector 3. How to take them and make a mesh GamePbject using LibTress?
    I should be able to hook up Clipper paths later after execute function.

    Thank you
     
  2. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291
    For mesh to be constructed in Unity I need at list two arrays of points and tri indexes. Just wonder how to get to this point using LibTress
     
  3. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291
  4. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,112
    The lib only generates tesselation data (tris) for you from one or more polygons. You will be in charge of actually constructing a mesh from it.

    Docs: https://github.com/speps/LibTessDotNet

    Here is the method I used in some of my code:

    Code (CSharp):
    1. /// <summary>
    2. /// Generate Mesh using LibTess.
    3. /// </summary>
    4. /// <param name="pointLists"></param>
    5. /// <param name="textureScale"></param>
    6. public void GenerateGeometryWithLibTess(List<List<Vector3>> pointLists, float textureScale)
    7. {
    8.     // Libtess stuff
    9.     var triangulatedShape = new TriangulatedShape2D(pointLists);
    10.  
    11.     // Your own mesh generation code (this one generates a mesh and applies it to a MeshFilter component)
    12.     int triCount = triangulatedShape.TriangleCount;
    13.     int[] tris = triangulatedShape.Triangles;
    14.     ContourVertex[] vertices = triangulatedShape.Vertices;
    15.     Vec3 p0, p1, p2;
    16.  
    17.     Vector3[] meshVertices = new Vector3[triCount * 3];
    18.     Vector2[] meshUVs = new Vector2[triCount * 3];
    19.     int[] meshTriangles = new int[triCount * 3];
    20.  
    21.     // uv start point
    22.     Vector3 uvPoint03D, uvPoint13D, uvPoint23D;
    23.  
    24.     for (int i = 0; i < triCount; i++)
    25.     {
    26.         p0 = vertices[tris[i * 3]].Position;
    27.         p1 = vertices[tris[i * 3 + 1]].Position;
    28.         p2 = vertices[tris[i * 3 + 2]].Position;
    29.  
    30.         meshVertices[i * 3] = new Vector3(p0.X, p0.Y, p0.Z);
    31.         meshVertices[i * 3 + 1] = new Vector3(p1.X, p1.Y, p1.Z);
    32.         meshVertices[i * 3 + 2] = new Vector3(p2.X, p2.Y, p2.Z);
    33.  
    34.         uvPoint03D = this.transform.TransformPoint(new Vector3(p0.X, p0.Y, 0));
    35.         uvPoint13D = this.transform.TransformPoint(new Vector3(p1.X, p1.Y, 0));
    36.         uvPoint23D = this.transform.TransformPoint(new Vector3(p2.X, p2.Y, 0));
    37.         meshUVs[i * 3] = new Vector2(uvPoint03D.x / textureScale, uvPoint03D.y / textureScale);
    38.         meshUVs[i * 3 + 1] = new Vector2(uvPoint13D.x / textureScale, uvPoint13D.y / textureScale);
    39.         meshUVs[i * 3 + 2] = new Vector2(uvPoint23D.x / textureScale, uvPoint23D.y / textureScale);
    40.  
    41.         meshTriangles[i * 3] = i * 3;
    42.         meshTriangles[i * 3 + 1] = i * 3 + 1;
    43.         meshTriangles[i * 3 + 2] = i * 3 + 2;
    44.     }
    45.  
    46.     Mesh mesh;
    47.     if (MeshFilter.sharedMesh == null)
    48.     {
    49.         mesh = new Mesh();
    50.     }
    51.     else
    52.     {
    53.         mesh = MeshFilter.sharedMesh;
    54.         mesh.Clear();
    55.     }
    56.     mesh.vertices = meshVertices;
    57.     mesh.uv = meshUVs;
    58.     mesh.triangles = meshTriangles;
    59.     mesh.RecalculateNormals();
    60.     //mesh.RecalculateBounds();
    61.     MeshFilter.sharedMesh = mesh;
    62. }
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. namespace Kamgam.SBR2.LibTessDotNet
    5. {
    6.     public class TriangulatedShape2D
    7.     {
    8.         protected Tess _tess;
    9.  
    10.         public int[] Triangles
    11.         {
    12.             get { return _tess.Elements; }
    13.             set { }
    14.         }
    15.  
    16.         public int TriangleCount
    17.         {
    18.             get { return _tess.ElementCount; }
    19.             set { }
    20.         }
    21.  
    22.         public ContourVertex[] Vertices
    23.         {
    24.             get { return _tess.Vertices; }
    25.             set { }
    26.         }
    27.  
    28.         public int VertexCount
    29.         {
    30.             get { return _tess.VertexCount; }
    31.             set { }
    32.         }
    33.  
    34.         public TriangulatedShape2D(List<Vector2> pointList)
    35.         {
    36.             tesselate(new List<List<Vector2>>() { pointList });
    37.         }
    38.  
    39.         public TriangulatedShape2D(List<List<Vector2>> pointLists)
    40.         {
    41.             tesselate(pointLists);
    42.         }
    43.  
    44.         public TriangulatedShape2D(List<Vector3> pointList)
    45.         {
    46.             tesselate(new List<List<Vector3>>() { pointList });
    47.         }
    48.  
    49.         public TriangulatedShape2D(List<List<Vector3>> pointLists)
    50.         {
    51.             tesselate(pointLists);
    52.         }
    53.  
    54.         // First list is the contour. Second, third, ... are holes iirc.
    55.         protected void tesselate(List<List<Vector3>> pointLists)
    56.         {
    57.             _tess = new LibTessDotNet.Tess();
    58.             for (int p = 0; p < pointLists.Count; p++)
    59.             {
    60.                 var contour = new LibTessDotNet.ContourVertex[pointLists[p].Count];
    61.                 for (int i = 0; i < pointLists[p].Count; i++)
    62.                 {
    63.                     contour[i].Position = new LibTessDotNet.Vec3(pointLists[p][i].x, pointLists[p][i].y, pointLists[p][i].z);
    64.                 }
    65.                 _tess.AddContour(contour, LibTessDotNet.ContourOrientation.Original);
    66.             }
    67.             _tess.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3);
    68.         }
    69.  
    70.         protected void tesselate(List<List<Vector2>> pointLists)
    71.         {
    72.             _tess = new LibTessDotNet.Tess();
    73.             for (int p = 0; p < pointLists.Count; p++)
    74.             {
    75.                 var contour = new LibTessDotNet.ContourVertex[pointLists[p].Count];
    76.                 for (int i = 0; i < pointLists[p].Count; i++)
    77.                 {
    78.                     contour[i].Position = new LibTessDotNet.Vec3(pointLists[p][i].x, pointLists[p][i].y, 0);
    79.                 }
    80.                 _tess.AddContour(contour, LibTessDotNet.ContourOrientation.Original);
    81.             }
    82.             _tess.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3);
    83.         }
    84.     }
    85. }
    The code is just an excerpt, don't expect it to work out of the box. It should help you to get started.

    You will have to write your own mesh generation code based on your needs anyhow. I think I only used tri-planar shaders on those mehses, so I am not sure if the generated UVs are any good.
     
    PhaneV, scrawk and Artpen like this.
  5. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291
    Thank you @_geo__ this is a good start for me.
    Ok I see... Now it is clear. Thank you
    Code (CSharp):
    1. int triCount = triangulatedShape.TriangleCount;
    2.     int[] tris = triangulatedShape.Triangles;
     
  6. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291
    Hi @_geo__ I think I got the hang of it.
    What I can't understand is why the order of indexes in triangles is wrong. It creates a mesh with normals pointing down for me?

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using LibTessDotNet;
    5.  
    6. [RequireComponent(typeof(MeshFilter))]
    7. [RequireComponent(typeof(MeshRenderer))]
    8.  
    9. public class PlanarMeshGenerator : MonoBehaviour
    10. {
    11.     [SerializeField] private SiteDataSO data;
    12.     private Mesh mesh;
    13.     private Vector3[] vertices;
    14.     private int[] triangles;
    15.  
    16.     // Start is called before the first frame update
    17.     void Start()
    18.     {
    19.         mesh = GetComponent<MeshFilter>().mesh;
    20.         MakeMeshData();
    21.         CreateMesh();  
    22.     }
    23.  
    24.     private void MakeMeshData()
    25.     {
    26.         // 1. Create an instance of tessellator
    27.         Tess tess = new LibTessDotNet.Tess();
    28.  
    29.         // 2. Construct the contour from data
    30.         ContourVertex[] contour = new LibTessDotNet.ContourVertex[data.Points.Count];
    31.         for (int i = 0; i < data.Points.Count; i++)
    32.         {
    33.             contour[i].Position = new LibTessDotNet.Vec3(data.Points[i].x, data.Points[i].y, data.Points[i].z);
    34.         }
    35.  
    36.         // 3. Add contour
    37.         tess.AddContour(contour, LibTessDotNet.ContourOrientation.Original);
    38.  
    39.         // 4. Tessellate
    40.         tess.Tessellate(WindingRule.EvenOdd, ElementType.Polygons, 3);
    41.  
    42.         // 5. Add mesh data
    43.         vertices = new Vector3[tess.ElementCount * 3];
    44.         triangles = new int[tess.ElementCount * 3];
    45.  
    46.         for (int i = 0; i < tess.ElementCount; i++)
    47.         {
    48.             var v0 = tess.Vertices[tess.Elements[i * 3]].Position;
    49.             var v1 = tess.Vertices[tess.Elements[i * 3 + 1]].Position;
    50.             var v2 = tess.Vertices[tess.Elements[i * 3 + 2]].Position;
    51.  
    52.             vertices[i * 3] = new Vector3(v0.X, v0.Y, v0.Z);
    53.             vertices[i * 3 + 1] = new Vector3(v1.X, v1.Y, v1.Z);
    54.             vertices[i * 3 + 2] = new Vector3(v2.X, v2.Y, v2.Z);
    55.  
    56.             triangles[i * 3] = i * 3;
    57.             triangles[i * 3 + 1] = i * 3 + 1;
    58.             triangles[i * 3 + 2] = i * 3 + 2;
    59.         }
    60.     }
    61.  
    62.     private void CreateMesh()
    63.     {
    64.         mesh.Clear();
    65.         mesh.vertices = vertices;
    66.         mesh.triangles = triangles;
    67.     }
    Also to update the tessellation at game play should I just run Tessellate() again and update my mesh?
     
  7. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291
    So if I am using a simple ordered list of points it works
    Screenshot 2022-03-11 at 14.51.02.png

    But if I feed a different list, it creates a mesh upside down (with the wrong winding)

    Screenshot 2022-03-11 at 14.51.44.png
     
  8. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291
    Ok Fixed it with a normal parameter...
    Code (CSharp):
    1. tess.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons,3, null, new Vec3(0,1,0));
     
  9. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291
    Hi @_geo__
    Apologies, I have another quick question for you. I am working with Clipper LIb now, And as far as I can tell it works only with ints.
    I created float to int conversion functions, but I'm not sure they are correct, I have to use up to 1000 scale to get results closer to reality.

    Have you got one of your examples? Thank you

    Code (CSharp):
    1. // Convert input Vecto3 List to intPoints. Including float precision
    2.     // http://www.angusj.com/delphi/clipper/documentation/Docs/Overview/FAQ.htm
    3.     private void ConvertToIntPoints()
    4.     {
    5.         foreach (var point in PathPoints)
    6.         {
    7.             float scaledX = point.x * scale; // 10000
    8.             //Debug.Log("Original float: " + point.x);
    9.             //Debug.Log("Converted int: " + scaledX);
    10.             float scaledY = point.z * scale;
    11.             IntPoints.Add(new IntPoint(scaledX, scaledY));
    12.         }
    13.     }
    14.  
    15.     // Convert paths intPoints back to Vecto3s. Including float precision
    16.     private void ConvertToFloat()
    17.     {
    18.         foreach (var path in offsetPaths)
    19.         {
    20.             for (int i = 0; i < path.Count; i++)
    21.             {
    22.                 float scaledX = path[i].X / scale; // 10000
    23.                 float scaledZ = path[i].Y / scale;
    24.                 //Debug.Log("Converted int back to float: " + scaledX);
    25.                 OffsetPoints.Add(new Vector3(scaledX, 0.0f, scaledZ));
    26.             }  
    27.         }
    28.     }
     
  10. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,112
    Can't help you much except asking whether "scale" is an int or a float. If it's an int then use a float instead to avoid int division.
     
    Artpen likes this.
  11. LapidistCubed

    LapidistCubed

    Joined:
    Dec 22, 2015
    Posts:
    9
    Just in case anyone is looking at this in the future as I was, this can be simplified down quite a bit.
    tess.Elements already contains the order of the vertices to create triangles. All that is required is to convert the array of Vec3 to Vector3 and use tess.Elements as the triangles indices.

    Code (CSharp):
    1. private Mesh CreateTriangulatedMesh()
    2.     {
    3.         tess.Tessellate(WindingRule.EvenOdd, ElementType.Polygons, 3);
    4.         Vector3[] verts = new Vector3[tess.VertexCount];
    5.         for (int i = 0; i < tess.VertexCount; i++)
    6.         {
    7.             Vec3 v = tess.Vertices[i].Position;
    8.             verts[i] = new Vector3(v.X, v.Y, v.Z);
    9.         }
    10.  
    11.         Mesh mesh = new Mesh();
    12.         mesh.vertices = verts;
    13.         mesh.triangles = tess.Elements;
    14.         mesh.RecalculateBounds();
    15.         mesh.RecalculateNormals();
    16.  
    17.         mesh.name = GENERATED_MESH_NAME;
    18.         return mesh;
    19.     }
     
    PhaneV, Artpen and _geo__ like this.
  12. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291
    Cool Thank you @LapidistCubed
    btw any advice regarding Clipper Lib conversion from int to float and back ?
     
  13. LapidistCubed

    LapidistCubed

    Joined:
    Dec 22, 2015
    Posts:
    9
    I didn't use Clipper at all in my project, so I'm afraid not! Best of luck!
     
    Artpen likes this.