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 Troubles with Clipper2 integration in Unity: Seeking assistance and insights

Discussion in 'Scripting' started by WTBA_Engineer, May 22, 2023.

  1. WTBA_Engineer

    WTBA_Engineer

    Joined:
    May 22, 2023
    Posts:
    13
    Hello everyone,

    I'm currently working on a project that requires the use of Clipper2 (http://www.angusj.com/clipper2/Docs/Overview.htm). I downloaded the files from GitHub and extracted them into my Assets folder. However, I encountered several errors.



    I searched for the error messages, and it seems that the issue is related to the target framework. I have installed the SDK provided by the official website (https://dotnet.microsoft.com/zh-cn/download/visual-studio-sdks?cid=getdotnetsdk), but the errors have only increased. I'm not sure where I went wrong, so I was wondering if anyone who has experience using Clipper2 in Unity could share some insights.

    Thank you for your guidance and advice!
     

    Attached Files:

    • E1.PNG
      E1.PNG
      File size:
      86.8 KB
      Views:
      53
    • E2.PNG
      E2.PNG
      File size:
      84.4 KB
      Views:
      53
  2. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Clipper isn't a library that has been made with Unity in mind.

    Due to this it relies on many libraries and workflows that belong to the .Net ecosystem, most of which are either unnecessary or incompatible with Unity's environment.

    I have integrated Clipper1 for my needs a couple of years ago, and what you need to do is to be very selective at removing the chaff from the wheat, nail down the actual heart of the library and refit it to a service which you can wrap up with an expandable proxy. I've done it like this, because it can be a messy process with a lot of loose ends, and so the proxy provides an airlock between my actual projects and whatever is going on behind the curtains. It is where I do conversion of all kinds of intermediate products, like paths, polygons, enum options etc. so I can keep my own standards protected without having to touch that in the Clipper itself. I also do my own triangulation, so this is all part of a bundle. Honestly, it's one of the best and most powerful tools I have at my disposal.

    I'm absolutely sure that Clipper2 is even bigger and much more ambitious project, and that's one of the reasons why I decided against it. Sadly, Clipper1 has one slight bug, namely you can't configure the resolution of the rounded offset path, though only on the inward joints, so it's pretty specific.

    Anyway, I don't think there is anyone who can help you with that, mostly because well I can't exactly name what I did to integrate Clipper1. It was just natural work, I knew what I was doing, and so it was one thing after another. The whole package including a full suite of really powerful API with named path registry, layer support, two operand banks, smart selection, constrained mesh triangulation etc. I've done in a week plus. And thanks to decoupling I can easily break off this upper logic, and re-integrate something else.
     
  3. WTBA_Engineer

    WTBA_Engineer

    Joined:
    May 22, 2023
    Posts:
    13
    I'm sorry, but since I am using translation software, I would like to confirm with you what you mean. Based on what you said, do I need to check the source code, extract the parts I want, and make it work in Unity?

    Indeed, I may not necessarily need to use Clipper. However, I saw the author using Clipper for Mesh Cutting and Optimization in this article, and it seems similar to the functionality I want to achieve. I am currently struggling with the mesh after performing CSG, so I want to use this library to simulate the cutting function of a machine tool.
     

    Attached Files:

  4. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    If you are to integrate Clipper manually, then yes. This is no small amount of work, can take days and that's when you know what your doing and you're also relying on a good source of know-how.

    Mesh manipulation can be incredibly hard. Many advanced algorithms and tools are completely absent from Unity.

    This is why I integrated Clipper in the first place, it's an incredible tool which raises the bar of overall productivity, and lets me produce visuals programmatically. But what it does is basically only half the story, because it works with the polygons, i.e. compounding polygon operations as well as turning polylines into polygons, much like vector illustration programs (i.e. Adobe Illustrator). However, you still need to create meshes from the resulting polygons, which isn't trivial.

    There are several so-called Delaunay triangulation algorithms out there, differing not only in performance but also in feature set. To make Clipper work well, you need the edge-constrained triangulation specifically, which preserves set geometry. All of this requires you to have a lot of experience with proper OOP, memory management, and data structures to make for an optimal library that is applicable to a wide variety of cases. Not to mention prior experience with Unity meshes, Clipper and Delaunay triangulation, I've had a lot of practice and made a lot of prototypes before I was ready to do this in one go.

    If you're not up to such programming tasks, you can a) try looking for other people's Clipper integrations on Github, b) ask @Kurt-Dekker about his makegeo library, maybe you can learn more from it, c) look for something useful and ready-to-use on the asset store.
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
  6. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Yes I've seen the thing they've done with the SpriteShaper, I think that's how it's called. It's quite good! And uses Clipper.

    Yes this is it (after checking the link). Well, it should be usable, but I have zero experience with it, don't know if it's, like you said, only Sprite-related, which is quite possibly the case. But I'm now seeing that Clipper-related API is also exposed, which is cool. Maybe it's easier for someone to hook it up with something other than sprites.

    Good thing you've mentioned this btw, I completely forgot about it.
     
    Kurt-Dekker likes this.
  7. WTBA_Engineer

    WTBA_Engineer

    Joined:
    May 22, 2023
    Posts:
    13
    I lack confidence in my programming skills, and there is some time pressure on this project, so currently I will try to find ready-made solutions as much as possible. However, in the future, I do want to improve my abilities, so I really appreciate you providing me with the knowledge of Delaunay triangulation, which is something I haven't come across in my search for related resources.

    After reading many articles, I also believe that mesh manipulation requires more specialized knowledge. Are there any other functionalities that can achieve geometric removal?


    As for @Kurt-Dekker's makegeo library, I have downloaded and looked into it. I have implemented some functionalities myself, such as generating cubes, but there are still some functionalities that I need to further study. It is indeed a great resource to learn a lot from!
     
  8. WTBA_Engineer

    WTBA_Engineer

    Joined:
    May 22, 2023
    Posts:
    13

    I have reviewed the content in the link, and it appears to be specifically for sprite shapes (as far as I understand, it is for U2D), so it doesn't quite meet my needs.

    The current result of my project, as shown in the image, requires using cylinders (or other shapes) to slice through cubes (or other shapes).
     

    Attached Files:

  9. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    For layer-based milling as you're showing on that picture, Clipper is the best you could hope for. But you need to find something to triangulate the results into meshes, and you need to be a decent programmer.

    I know Unity strikes many people as a tool that makes gaming and rendering approachable, but this is still after all, predominantly a programming business, and the topic is majestically deep and complex, as ever.

    If time is of the essence, I bet there is something useful on asset store, but you have to see for yourself.

    You could also try and think outside of the box: maybe you could think of something with 3D textures? Or maybe a shader that does ray marching and true constructive solid geometry? (That's tbh fairly easy to do, but lighting is not.) I don't know, it's worth checking out.
     
  10. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    This worked fine for me...

    1. Clone https://github.com/AngusJohnson/Clipper2
    2. Open the sln in JetBrains Rider or Visual Studio
    3. Remove the FileIO, benchmark, example, and test projects. Just keep the Clipper2Lib main project:

    upload_2023-5-24_14-11-44.png

    4. Change configuration to release.
    5. Build it
    6. Take the compiled DLL and move it into the Unity project
     
    Last edited: May 24, 2023
  11. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    My asset store product (shameless spamming!) does cutouts using Clipper2 and Triangle.NET. It only works for convex shapes (and isn't even perfectly exact there because it does a lot of merging of verts and what not). Even with the libraries, though, this is a very non-trivial task. Here's the basic skeleton for how I generate the cuts needed to insert a window into a wall:

    1. Extract a vertex+index soup from the input meshes.
    2. Adjust input positions slightly inwards from the edges to help with floating point error/quantization artifacts
    3. Quantize all points to an integer grid.
    4. Merge coincident verts and do some other misc cleanup on the input data.
    5. Get the points of intersection between the cutting plane and the mesh.
    6. Take the convex hull of these points.*
    7. Clip the convex hull polygon against the plane (using clipper)
    8. Triangulate the resulting polygon (using traingle net)
    9. Rescale output vertices
    10. Convert all this back into a format Unity can use.

    Part (7) and (8) are handled by libraries. The rest is stuff you need to figure out for yourself.

    (* Getting only the edge loops is an EXTRODINARILY difficult task, which is why I decided eventually to just take the convex hull. I spent a long time trying to figure that out for arbitrary concave meshes, read several papers about it, and ported a C++ library which claimed to do it but still had bugs in very simple cases. When I found out Blender couldn't do it right, I decided "**** it; if they can't figure it out I'm not going to bother". Autodesk Maya can do it perfectly for concave meshes. If you go down this route, note that following connected triangles is not enough for nonmanifold/intersecting meshes)
     
    orionsyndrome likes this.
  12. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    thumbs up

    Can I ask which type of data structure you ended up using? Is it the winged edge?
    I rolled my own weird half-edge thing, from which I managed to build an ultraoptimal "petal sorter". It's essentially a fan sorter that works from a vertex, and can detect edge discontinuities (i.e fan holes), I'm now wondering if this would help with edge loops somehow.
     
  13. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    That would be an interesting idea. Was the original goal to find and fill holes?

    Basically, I started by trying to port this library: https://github.com/intents-software/mesh-plane-intersection/blob/master/src/MeshPlaneIntersect.hpp . That code depends a lot on the nature of the C++
    std::map
    , which allows you to do things C#
    Dictionary<>
    can't do. I eventually made an edge adjacency matrix with some gaps to align rows to 64-bit segments allowing for fast traversal/iteration

    Code (csharp):
    1.  
    2.    private struct EdgeAdjacencyMatrix
    3.     {
    4.         public readonly int vertexCount;
    5.         private readonly int rowLength;
    6.         private ulong[] data;
    7.  
    8.         public EdgeAdjacencyMatrix(int2[] edges, int vertexCount)
    9.         {
    10.             this.vertexCount = vertexCount;
    11.             rowLength = (vertexCount+63)/64;
    12.             data = new ulong[vertexCount*rowLength];
    13.             foreach(int2 e in edges)
    14.             {
    15.                 data[e.x * rowLength + e.y/64] |= 1UL << (e.y%64);
    16.                 data[e.y * rowLength + e.x/64] |= 1UL << (e.x%64);
    17.             }
    18.         }
    19.      
    20.         public IEnumerable<int> enumerateRow(int x)
    21.         {
    22.             int ofs = x * rowLength;
    23.             for(int i = 0; i < rowLength; ++i)
    24.             {
    25.                 ulong v = data[ofs + i];
    26.                 for(int j = 0; j < 64; ++j)
    27.                     if((v & (1UL << j)) != 0)
    28.                         yield return i * 64 + j;
    29.             }
    30.         }
    31.  
    32.         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    33.         // ReSharper disable once UnusedMember.Local
    34.         public bool findFirstEdge(out int2 e)
    35.         {
    36.             for(int x = 0; x < vertexCount; ++x)
    37.             {
    38.                 if(findFirstColumn(x, out int y))
    39.                 {
    40.                     e = new int2(x, y);
    41.                     return true;
    42.                 }
    43.             }
    44.             e = default;
    45.             return false;
    46.         }
    47.      
    48.         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    49.         // ReSharper disable once MemberCanBePrivate.Local
    50.         public bool findFirstColumn(int x, out int y)
    51.         {
    52.             int ofs = x * rowLength;
    53.             for(int i = 0; i < rowLength; ++i)
    54.             {
    55.                 ulong v = data[ofs + i];
    56.                 if(v != 0UL)
    57.                 {
    58.                     y = i*64 + bsf(v);
    59.                     return true;
    60.                 }
    61.             }
    62.             y = -1;
    63.             return false;
    64.         }
    65.      
    66.         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    67.         // ReSharper disable once UnusedMember.Local
    68.         public void unset(int2 e)
    69.         {
    70.             data[e.x * rowLength + e.y/64] &= ~(1UL << (e.y%64));
    71.             data[e.y * rowLength + e.x/64] &= ~(1UL << (e.x%64));
    72.         }
    73.      
    74.         // bit scan function -- finds the least significant bit that's set for a nonzero ulong
    75.         private static readonly int[] _deBrujinBitPositions = { 0, 1, 17, 2, 18, 50, 3, 57, 47, 19, 22,
    76.             51, 29, 4, 33, 58, 15, 48, 20, 27, 25, 23, 52, 41, 54, 30, 38, 5, 43, 34, 59, 8, 63, 16, 49, 56, 46, 21,
    77.             28, 32, 14, 26, 24, 40, 53, 37, 42, 7, 62, 55, 45, 31, 13, 39, 36, 6, 61, 44, 12, 35, 60, 11, 10, 9 };
    78.         [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int bsf(ulong v)
    79.             => _deBrujinBitPositions[((ulong)((long)v & -(long)v) * 0x37E84A99DAE458F) >> 58];
    80.     }
    81.  
    82. // before putting edges into the matrix, you probably want to run this...
    83.  
    84. /// <summary>
    85. /// Modifies the input array (it gets the new numbering), and also returns the mappings.
    86. /// </summary>
    87. private static (Dictionary<int, int> oldToNew, int[] newToOld) renumberVerticesOnEdges(int2[] edges)
    88. {
    89.     Dictionary<int, int> oldToNew = new();
    90.     List<int> newToOld = new();
    91.     for(int i = 0; i < edges.Length; ++i)
    92.         edges[i] = new int2(getOrAdd(edges[i].x, oldToNew, newToOld), getOrAdd(edges[i].y, oldToNew, newToOld));
    93.     return (oldToNew, newToOld.ToArray());
    94.      
    95.     static int getOrAdd(int oldIndex, Dictionary<int, int> oldToNew, List<int> newToOld)
    96.     {
    97.         if(!oldToNew.TryGetValue(oldIndex, out int newIndex))
    98.         {
    99.             newIndex = newToOld.Count;
    100.             oldToNew.Add(oldIndex, newIndex);
    101.             newToOld.Add(oldIndex);
    102.         }
    103.         return newIndex;
    104.     }
    105. }
    106.  

    However, after re-implementing the port several times, then writing a C++ testbed to see if it behaved differently, I realized the algorithm itself has some flaws if you want to translate the results into polygons. It basically works by tracing edges going back and forth across the cutting plane, then joining as many edge chains as it can, greedily. It's sort of a best effort to give you some edge chains that will hopefully result in loops.*

    Here's a simple example with a stool that shows exactly why it doesn't work:



    The C++ library will give the following result (the different colors are each separate edge chains that it fails to connect):



    Notice the yellow and cyan parts are just lines, so you can't use them to create a polygon. The parts that would connect the yellow and cyan lines have been "swallowed" by the blue and red lines. As long as you're doing the greedy/dynamic-programming approach and marking visited edges as ineligible for other polygons, this solution won't ever work for a lot of "real world" (nonmanifold) meshes.**

    HOWEVER (if you're still reading this), there is another solution, which is just to do what a "real" 3D application or CAD program does... boolean geometry defined by the planes of every triangle (a BSP tree). That still doesn't get you the 2D polygon(s) on the plane - at least not directly - but there's some light at the end of that tunnel.

    By that point, I just wanted to get the damn asset out the door so decided to use the convex hull and be done with it. But if there is a robust solution that works for nonmanifold concave meshes, it probably requires that (so... yay CGAL), not tracing edges.

    * It's also
    O(N!)
    in the worst case, although since Hamiltonian cycle is NP-hard and this problem seems equivalent, that might just be the way things are.

    ** (It's worth noting the greedy algorithm that the C++ library used is similar to the "fixing" one proposed in the end of this paper: https://www.scitepress.org/papers/2018/65384/65384.pdf . I'm not sure if they figured out a better solution, or if the polygons generated in the first half didn't ever have that sort of issue so the "repair" method works for those, but not for arbitrary meshes.)
     
    Last edited: May 25, 2023
  14. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    It was actually a subset of my (vastly overdone) mesh experiments, that I called MicroMesh. It was something I've built specifically for 2D triangulation at some point. MicroMesh is basically a loose collection of triangles similar to Unity's Mesh, but one that would build a much more powerful underlying data structure on the fly, letting you rotate edges, find loops, disconnect a triangle, fill in a hole, that sort of thing. One of the deeper concerns was detecting a boundary conditions (both vertices and edges), so it was built from ground up to support all of this.

    It's a vertex-centric model, where each vertex would be correlated with a triangle using it, and in this context, the triangle is called a petal. Upon querying, at some later time, a lazy algorithm would sweep over the emanating edges and optimally sort the petals in a counter-clockwise order and/or would notice a discontinuity. So effectively it would build an adjacency matrix. Having a petal discontinuity is a clear sign of a being at a mesh boundary, so it would mark the vertex accordingly.

    I was looking hard for an optimal algorithm to do this, and stumbled on many academic papers, many of which do this in such a complicated and clumsy way, that made me believe this algorithm was perhaps paper-worthy. Mainly because it wasn't exactly trivial -- I treat edges as half-edge structs, find correlated vertex pairs, and have some clever ways of traversing through the edges, and this is done just once, then baked in the model, through swapping in place -- but also it's very fast, an order of magnitude faster than already speedy triangulation that was used to build the mesh in the first place.

    You could use it for that, yes. But mostly this would let me relax the mesh by using Laplacian relaxation, but without relaxing the boundaries, among other things. And I was able to make this work in runtime for some pretty sizable meshes, i.e. 1K vertices, hundreds of triangles.

    Code (csharp):
    1.     private class Vertex {
    2.  
    3.       public int Id { get; }
    4.  
    5.       public Vector2 Position { get; set; }
    6.       public float Height { get; set; }
    7.  
    8.       public bool Fixed { get; set; }
    9.       public Vector2 LateOffset { get; set; }
    10.  
    11.       List<Triangle> _petals;
    12.       private bool _disorderedPetals;
    13.       private int _discontinuity;
    14.  
    15.       int[] _edges;
    16.  
    17.       public Vector3 FinalPoint(Vector3 scale)
    18.         => (Position + LateOffset).ToVector3_XY(Height).ScaledBy(scale);
    19.  
    20.       public Vertex(int id, Vector2 position) {
    21.         Id = id;
    22.         Position = position;
    23.         _disorderedPetals = true;
    24.         _discontinuity = -1; // no discontinuities by default
    25.       }
    26.  
    27.       public bool HasDiscontinuities() {
    28.         if(!IsConnected()) return true;
    29.         if(_disorderedPetals) _ = Petals; // TODO simply do petalSorting(), why the funky business
    30.         return _discontinuity >= 0;
    31.       }
    32.  
    33.       public bool IsConnected() => _petals?.Count > 0;
    34.  
    35.       internal void Attach(Triangle tri) {
    36.         if(_petals == null) _petals = new List<Triangle>();
    37.         _petals.Add(tri);
    38.         _disorderedPetals = true;
    39.       }
    40.  
    41.       internal void Detach(Triangle tri) {
    42.         for(int i = 0; i < _petals.Count; i++) {
    43.           if(_petals[i] == tri) {
    44.             _petals.RemoveAt(i);
    45.             _disorderedPetals = true;
    46.             return;
    47.           }
    48.         }
    49.         throw new InvalidOperationException();
    50.       }
    51.  
    52.       public List<Triangle> Petals {
    53.         get {
    54.           if(!IsConnected()) return null;
    55.           if(_disorderedPetals) petalSorting();
    56.           return _petals;
    57.         }
    58.       }
    59.  
    60.       // exact order not guaranteed
    61.       public int[] Edges {
    62.         get {
    63.           if(_edges == null || _disorderedPetals) {
    64.             var petals = Petals;
    65.             if(!IsConnected()) return _edges = null;
    66.  
    67.             var partners = new HashSet<int>();
    68.             for(int i = 0, r; i < petals.Count; i++) {
    69.               r = petals[i].NextFrom(Id);
    70.               if(!partners.Contains(r)) partners.Add(r);
    71.               r = petals[i].PrevFrom(Id);
    72.               if(!partners.Contains(r)) partners.Add(r);
    73.             }
    74.  
    75.             _edges = new int[partners.Count];
    76.             partners.CopyTo(_edges);
    77.           }
    78.  
    79.           return _edges;
    80.         }
    81.       }
    82.  
    83.       // petal CCW in-place sorting algorithm
    84.       // + discontinuity detection (allows quick per-vertex boundary checks)
    85.       void petalSorting() {
    86.         _discontinuity = -1;
    87.  
    88.         if(_petals.Count < 3) {
    89.           _discontinuity = 0;
    90.  
    91.         } else {
    92.           for(int i = 0; i < _petals.Count; i++) {
    93.             bool foundDiscontinuity = true;
    94.             int r = _petals[i].NextFrom(Id);
    95.  
    96.             if(i == _petals.Count - 1) {
    97.               int l = _petals[0].PrevFrom(Id);
    98.               if(l == r) foundDiscontinuity = false;
    99.  
    100.             } else {
    101.               for(int j = i + 1; j < _petals.Count; j++) {
    102.                 int l = _petals[j].PrevFrom(Id);
    103.  
    104.                 if(l == r) {
    105.                   swapItems(_petals, i + 1, j);
    106.                   foundDiscontinuity = false;
    107.                   break;
    108.                 }
    109.               }
    110.  
    111.             }
    112.  
    113.             if(foundDiscontinuity) _discontinuity = i;
    114.           }
    115.         }
    116.  
    117.         _edges = null;
    118.         _disorderedPetals = false;
    119.  
    120.         void swapItems<T>(List<T> lst, int i1, int i2) {
    121.           var tmp = lst[i1]; lst[i1] = lst[i2]; lst[i2] = tmp;
    122.         }
    123.       }
    124.  
    125.     }

    Anyway, I'd say your example is much harder! Bruh I've done some wild stuff but that looks like NP-hard right from the get go! What can I say, any progress with that is quite a success.

    I've seen slicing done with stencil shaders before, even done some, but you have zero control over (regular UV) texturing, you can only really improvise with projections and world space and hope for the best, or do it with a plain color. But I haven't seen an attempt to actually build a cross-section polygon.

    Yep the problem with that stool (and with man-made meshes in general) is that it lacks any proper volume. Everything is made to satisfy the external surface, but if you would to look closely at the pieces that came out wrongly in your results, these parts probably contain holes hidden by legs, so I think there's nothing you can do. There is no cheap or easy computational method to discover what the cross-section should look like, so you either do what rendering does (probe it on a fragment basis, which is absurd) or you need to find complex topological relationships and hope it's not O(n!).

    This should be doable however, if you would impose some mesh constraints, namely if it was constructed strictly from convex primitives. The cross-section should work similarly to how slicing a polygon works, and I see you went for the same idea.

    Btw, I've seen this demo of yours before, it's pretty solid work (no pun intended lol), very nice.
     
  15. WTBA_Engineer

    WTBA_Engineer

    Joined:
    May 22, 2023
    Posts:
    13

    I'm sorry, I'm having a bit of trouble understanding what you mean by "layer-based milling." Are you referring to I can see it from a 2D perspective? However, my original goal is to achieve five-axis milling (including rotational axes, so it's not limited to just the surface). Therefore, my current understanding is that I need to sweep out my toolpath, generate a swept volume, and then perform a difference operation using it, ultimately optimizing the mesh.

    However, as seen in the previous image, I'm stuck on the mesh optimization part. I've spent nearly eight hours today researching information on Delaunay triangulation, but with no success. It's been quite discouraging... QAQ


    Hahaha, yes, most of the time, I start by searching the Asset Store for the functionality I want. However, I usually have to look for free resources, so the options are limited.

    Indeed, I have considered and searched for other methods, such as mesh deformation and Marching Cubes. Mesh deformation has been the most successful approach I've tried so far, as it can resemble milling. The drawback is that I haven't figured out how to handle the placement of the cylindrical shape if it doesn't align with the mesh vertices. As for Marching Cubes, I'm still learning about it and unsure if it can truly achieve the functionality I desire, so I can't decide whether to invest time in it.
     
  16. WTBA_Engineer

    WTBA_Engineer

    Joined:
    May 22, 2023
    Posts:
    13
    Thank you very much, now I can use it in my Unity project.



    Regarding "generate the cuts needed to insert a window into a wall," does it refer to this image?

    Having these processes clarified has made my thoughts much clearer, but I still need time to try them out. Currently, I'm unaware of the potential issues I may encounter. In any case, thank you for sharing your insights.
     

    Attached Files:

  17. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    But then I don't understand how you intend to use Clipper. It's a 2D library intended for polygons. It can be used however if you intend to apply both triangulation and extrusion. That's why I said multi-layer milling.

    Yes yes and yes, but this is deeply in the domain of volumetric boolean algebra. The closest you could ever hope to achieve in real time (assuming the full freedom and not tricks, animations, or hand-made intermediate products) would be something based on voxels. At least in my opinion. I've never seen anything like it, so it must be immensely hard to pull off.

    And if you're not doing this in real time, why do it in Unity, when you can use Blender or Houdini?
     
  18. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Yes. Exactly that feature.

    Taking another look at your example image, the milling might be a bit different. Is it always cylinders and cubes, instead of actual real meshes? If so, that can be handled fairly simply via constructive solid geometry. For example, there's a free tool that does it (don't know if works at runtime). If you start with solid objects instead of user-defined meshes, you have a lot more power.
     
  19. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Ah, I see. Basically the focus was around operations and optimizations of 2D tri meshes? Like removing redundant vertices, simplifying/decimating, etc?

    Stencils and what not rely on rasterization. Turning that back into a polygon (eg detecting lines and angles in a rasterized image) is a huge problem of its own ;-P.

    Just building a filled cross-section can use a rather simpler algorithm by finding midpoints of vertex loops (respecting normal direction) and then connecting the surrounding vertices to the midpoint, without ever needing to get the 2D plane projection. I know of at least one asset store asset that does that. If the source mesh "looks right" the result mesh will also likely "look right" without ever needing to get the actual polygons. Getting the polygons for arbitrary meshes is where **** hits the fan.

    Thanks!!! :)
     
    orionsyndrome likes this.
  20. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    I don't think MC is what you're looking for. I have a lot of experience with it, and it's very... aesthetically limited. The cubic grid is pretty jarring, even when you interpolate the vertices. I've tried marching tetrahedrons as well, and while these are slightly better in some situations, I still find this approach heavily stylized and definitely in a need of a 2nd pass of something, sadly beveling or some such operation is very hard to implement.

    All of that pushed me into direction of ray marching and clay rendering, but this is also quite limited, however on the other end of the spectrum. So in the end I've been experimenting with a combined approach. But all of this is very artistic, it's hard to apply any of it for hard surface models or milling.
     
  21. WTBA_Engineer

    WTBA_Engineer

    Joined:
    May 22, 2023
    Posts:
    13
    I'm ashamed to admit that initially, I focused my search on "Boolean operations" and tried various plugins related to unions, differences, and intersections. I didn't realize that using CSG (Constructive Solid Geometry) would be more appropriate.
    When I first searched, I saw that Clipper2 had this feature (BooleanOp), and there was a paper about it, so I mistakenly thought that Clipper2 was also applicable to my project, but it turns out I was wrong.


    Hahaha, it's always easy to explain the general flow of a process, but it's always more challenging to figure out how to actually implement and plan each step.


    In the past few days, I found an image of software that has a similar functionality. Unfortunately, I can't publicly upload it due to certain reasons. However, from what I saw, it appears to use voxels, marching cubes, and octrees, which is why I started considering researching in that direction instead of CSG. Indeed, when zooming in on the objects after milling in that software, you can see many jagged edges. However, the surface mesh looks quite beautiful and doesn't resemble the previous image I had. There's a video of another software where I believe they are using a similar method. (
    )


    Due to company requirements, real-time operations are necessary. Basically, I need to implement a functionality similar to CAM (Computer-Aided Manufacturing) using Unity, as it doesn't seem feasible for me to pre-create everything using software like Blender.
     
  22. WTBA_Engineer

    WTBA_Engineer

    Joined:
    May 22, 2023
    Posts:
    13

    It's possible that it could be a real mesh, as different users may upload their own milled objects. I started with cylinders and cubes because these shapes are commonly used to represent milling simulations.

    I know this tool, and I have tried it. It can be used at runtime. However, according to the developer, it's not suitable for meshes other than those generated by the tool itself.

    Yes, perhaps I shouldn't have overcomplicated things initially. The statement "If you start with solid objects instead of user-defined meshes, you have a lot more power" has given me strength. Thank you!