Project one mesh onto another mesh

I can’t believe nobody knows how to solve this issue

@Alfredo_Peña We recently fixed a bug with the Curve.ProjectToMesh method. This was reported about a week ago and the fix should be in the next Rhino 8.12 build. You will need to update your version of Rhino to 8.12 in order for this to work in Hops.

3 Likes

Hello,

I’m encountering a new issue.

When I work with meshes that have “holes” in them, I find it very difficult to project them.

I’m adding a point to each mesh to identify the one I need. However, when a mesh has a hole, it creates a new curve that splits the original mesh, and none the resulting portions are needed for me. Ideally, it should split the “larger” mesh instead.

I’m not sure if I’ve explained this clearly enough. I’m attaching a file for reference.

MeshSplitHoles.gh (618.1 KB)

Edit for clarifying:

  1. I want to project the green Mesh (list with many meshes) onto the white Mesh (terrain)

  2. I generate vertical meshes with the naked edges of the green Mehs and intersect them with the terrain

  3. I find problems when meshes have holes inside. For example:

  4. After mesh splitting, I receive 2 meshes in each branch, and I use a point to identify which mesh I want to preserve. However it doesn’t work with holes, because I don’t want to preserve none of them, but their naked edges are considered for mesh split…

Does anybody know how to project the meshes with their holes?

would this work?

MeshSplitHoles_Re.gh (617.8 KB)

by the way, this does maintain whatever resolution the original projected meshes had, very same number of vertexes, just projected on the destination surface along Z… not sure if you want this or if you want to trim the destination mesh based on projected boundaries

Thanks @inno,

It’s not a bad idea, but the result will be similar as the one provided by @PeterFotiadis. The output mesh will cross the target mesh multiple times:

Hi @Alfredo_Peña ,
Does the following work for you? It’s a C# version that builds on what @AndersDeleuran made, but uses all the naked edges, instead of just the outer edge for projecting and splitting the terrain mesh. The resulting meshes of the splitting are filtered based on if a ray cast from the center of the first face hits the original projected mesh, you add it to the final mesh.

20240831_MoveToTopo_BW.gh (618.7 KB)

C#
    List<Curve> curves = new List<Curve>();
    List<Mesh> meshes = new List<Mesh>(){terrainMesh};
    
    ///Get naked edges of the project mesh
    var nakedEdges = projectMesh.GetNakedEdges();

    ///Assume projecting vertically for the terrain scenario
    var vector = new Vector3d(0,0,1);

    ///Project naked edges to terrain mesh
    var projectedCurves = Curve.ProjectToMesh(nakedEdges.Select(n => n.ToNurbsCurve()), meshes, vector, tol);

    ///Split the terrain mesh with the projected naked edges
    var results = terrainMesh.SplitWithProjectedPolylines(projectedCurves.Cast<PolylineCurve>(), tol);
    
    ///Flip the projection direction if first direction fails
    if (results.Count() < 2)
    {
        projectedCurves = Curve.ProjectToMesh(nakedEdges.Select(n => n.ToNurbsCurve()), meshes, -vector, tol);
        results = terrainMesh.SplitWithProjectedPolylines(projectedCurves.Cast<PolylineCurve>(), tol);
    }

    ///Remove mesh results outside of the projection mesh and combine remaining into one mesh
    ///Use first face center in the mesh to check inside/outside relationship
    Mesh finalMesh = new Mesh();
    foreach (Mesh m in results)
    {
        Point3d pt = m.Faces.GetFaceCenter(0);
        Ray3d ray = new Ray3d(pt, vector); 
        double t = Rhino.Geometry.Intersect.Intersection.MeshRay(projectMesh, ray);
        if (t > 0)
        {
            finalMesh.Append(m);
        }
        else
        {
            Ray3d rayOpp = new Ray3d(pt,-vector);
            double tOpp = Rhino.Geometry.Intersect.Intersection.MeshRay(projectMesh, rayOpp);
            if (tOpp > 0)
            {
                finalMesh.Append(m);
            }
        }
    }

    a = finalMesh;

Note, the splitting sometimes fails with small, acute faces in the projected mesh, so I’ve used the RefineMesh component to help mitigate this.

Edit:
Here is a version that expands the logic from C# into (mostly) native components:
20240901_MoveToTopoNativeOnly_BW.gh (621.6 KB)

-Brian

1 Like

Well … think the whole thing as a 2 step process: (1) get the projected Mesh (2) further refine it (i.e. subdivide and/or maybe move vertices) if the goal is a “match” as close as some var dictates.

BTW: For the general case of this (Any target, any source and with NO post - subdivision/adjustments) is highly unlikely that you can optain a projected result as you want (i.e: with no “Faces cross” - so to speak).

Using Extrude component delays a lot my script:

I’ve slightly modified your code, but it sometimes returns empty holes inside:

If I use Refine component, the number of holes decreases, but other holes appear:

Because of these problems I split the mesh with the naked edges.

20240902_MeshSplitHoles.gh (624.6 KB)

Edit. For these other inputs there is one region that is duplicated.


image
However, the loft surface for mesh splitting is generated properly:
image

20240905_MeshSplitHoles.gh (1016.6 KB)

Edit 2. With another inputs, after transforming the result mesh by moving it along a vector, I receive 2 invalid meshes:


Does anybody know how to avoid this? I’ve seen this thread: BUG: Transform produces invalid Mesh, but I see that more as a temporary fix than anything else

20240905_MeshSplitHoles_InvalidMesh.gh (1.1 MB)

I keep encountering issues…

When I try to project some small and thin meshes like roads:

First, I have to adjust the domain of the mesh, because, if I don’t, it returns null meshes for all of them:

And, even after doing that, it still doesn’t perform well:

20240930_MeshSplitHoles_InvalidMesh.gh (1.1 MB)

I refer you to this video that shows a projection of one mesh onto another mesh made in ZBrush. It explains well the concept of why a low-resolution mesh, for example with 100 faces, when projected onto a mesh with a resolution of 10,000 faces, cannot be perfectly identical once projected. It’s also quite intuitive to understand why… So, if you want to achieve something very similar to the target surface on which you want to project, you need to subdivide the source mesh

@AndersDeleuran & @Brian_Washburn have exposed a way of doing that by splitting the mesh, so it’s definitely not impossible.

The method they discovered is the one shown in the video I linked, although done with different software… the concept is the same. The ‘input mesh’ must be subdivided multiple times to resemble the ‘target mesh’ as closely as possible. The process done in ZBrush can be performed in other mesh modeling programs or even in Rhino, but the concept remains the same.
In Rhino, you use different processes to achieve the same result; the important thing is to understand the theory. :grinning: :wink:

I’ve found another issue…

For some reason, the success of the script depends on the loft extension:

For example, for an extension of approximately 500 meters it doesn’t perform the split well, and it returns only one mesh.

However, for an extension of 450 it does it properly:

Does anybody know why this occurs?

MeshSplitProblem08102024.gh (480.0 KB)