Fastest way to display Mesh Wires

Thanks.
The best explanation of the relationship between a topological and a mesh vertex I’ve found so far comes from @RIL in this post:

For anyone who is interested, here is a recursive method, I ended up using to find all mesh vertices and faces which fall within a radius of a given point:

public static void FindConnectedVertices(int mVertex)
        {
            Stack<int> verticesToCheck = new Stack<int>();
            HashSet<int> visitedTVertices = new HashSet<int>();

            // Local variables
            var _topologyVertices = Brush.initialMesh.TopologyVertices;
            var _meshVertices = Brush.mesh.Vertices;

            Point3d hitPoint = Brush.mesh.Vertices[mVertex];
            var tVertex = Brush.initialMesh.TopologyVertices.TopologyVertexIndex(mVertex);

            verticesToCheck.Push(tVertex);

            while (verticesToCheck.Count > 0)
            {
                var currentTVertex = verticesToCheck.Pop();
                if (visitedTVertices.Contains(currentTVertex))
                    continue;
                visitedTVertices.Add(currentTVertex);

                var mVertices = _topologyVertices.MeshVertexIndices(currentTVertex);
                var _point = _meshVertices[mVertices[0]];
                double distance = hitPoint.DistanceToSquared(_point);


                if (distance <= Brush.radiusSquared)
                {
                    foreach (var mV in mVertices) // One topology vertex can contain multiple mesh vertices. 
                    {
                        Brush.cursorVertices.Add(mV, _meshVertices[mV]);
                        Brush.cursorFaces.Add(mV, _topologyVertices.ConnectedFaces(currentTVertex));
                    }
                    var connectedTVertices = _topologyVertices.ConnectedTopologyVertices(currentTVertex);

                    foreach (var tV in connectedTVertices) // Mark connected topology vertices to check later
                        verticesToCheck.Push(tV);
                }
            }
        }

This way I can update normals of selected faces and vertices without recalculating these for the entire mesh. Now, I’m down to 15ms for each draw call of the 450k quad test mesh. That’s 66 FPS - more than enough to keep things interactive and so much faster than what I initially had.

EDIT: Replaced the recursive method with a while loop to prevent stack overflow exceptions.

3 Likes

To answer the original question: in my testing the by far fastest way to display Mesh Wires is to use OpenGL. We can reuse buffers used for drawing the shaded triangles and switch to line display. It’s also a good idea to slightly offset the wires along vertex normals to prevent Z-fighting.

Here is how the final result looks like:

6 Likes
public static void ReplaceVertex(int location, Rhino.Geometry.Point3f vertex)         {             IntPtr _location = new IntPtr(3 * sizeof(float) * location);             IntPtr _size = new IntPtr(3 * sizeof(float));              var handle = System.Runtime.InteropServices.GCHandle.Alloc(vertex, System.Runtime.InteropServices.GCHandleType.Pinned);             IntPtr _dataPointer = handle.AddrOfPinnedObject();              OpenGL.glBindBuffer(OpenGL.GL_ARRAY_BUFFER, _model.GetUniformsAndAttributes(0)._meshes[0].VertexVbo);             OpenGL.glBufferSubData(OpenGL.GL_ARRAY_BUFFER, _location, _size, _dataPointer);             handle.Free();         }

Is it also possible to apply paint locally to VertexColors and change the Faces color?

The main idea is to apply the mask. I am currently implementing this through e.Display.DrawMeshShaded and then e.Display.DrawMeshFalseColors Because I draw two versions at the same time. The rendering time increases almost twice. :pensive_face: I am considering the possibility of using ghgl, I would appreciate it if you would share your decision. @stevebaer @mrhe @kitjmv

1 Like

Did you succeed?

In fact, using **ghgl** mainly serves as an example of how to implement it in Rhino with C#. But your exact question would actually be “how to implement it with **OpenGL**”, because that’s what ghgl is really about.

And on that point, it’s essential first and foremost to know how to code and to understand how **OpenGL** and **GLSL shaders** work. From there, it becomes fairly straightforward to use OpenGL shaders to draw a mesh with a different color assigned to each vertex of the mesh.

I have already solved this problem. And I managed to achieve a mask rendering speed on a mesh with 1 million vertices in 5 milliseconds.:blush: used DrawMeshShaded(Mesh mesh, DisplayMaterial material, int faceIndices); but there are still some issues with the process of transferring the Transform mesh to replace the starting mesh. for the GetPointOnMesh function

ChangeMesh 43,9928 ms //OnMouseMove+m_down = true;
Final 40,2366 ms //OnMouseUp
GetPoint 1611,069 ms //OnMouseMove+m_down = false; :woozy_face:
GetPoint 0,04 ms //OnMouseMove+m_down = false;

I spent a couple of days trying to figure out how to start the process draw Mesh in ghgl, but I couldn’t figure it out. :sweat_smile:

Perhaps the problem is that when I start using the new Mesh instead of the old one, a complete recalculation occurs for use. Rhino.Geometry.Intersect.Intersection ? @kitjmv @mrhe @stevebaer

Yes, each time you update the mesh, Rhino will rebuild its acceleration sctructure used for raycasting which is used by Rhino.Geometry.Intersect.Intersection. This is relatively slow. Doing this on every mouse move will kill your perfomance.

Can you recommend an alternative?

It’s difficult to help you and know exactly what’s going on, but here’s what I can state with some confidence:

  • Your CPU is fast enough (even if, like mine, it’s a 6-year-old computer).
  • Looping (foreach) over 1,000,000 vertices in C# is slow, but not nearly as catastrophic as one might imagine.
  • Transferring 1,000,000 vertices from Rhino’s native C++ library to C# (or from C# back to C++) is often slower than just storing the 1,000,000 vertices in a C# variable and performing the loop (foreach).
  • Transferring 1,000,000 vertices from C++ or C# to GPU memory is very often slower than all the above.

Have a look at:

I hope this can help you

Currently, my main problem is that I am updating the mesh for use. Intersection.MeshRay, which takes a lot of time to update the calculation Intersection . GetPoint 1611,069 ms, :sweat_smile:

If you want to implement truly interactive mesh sculpting, you’d need to move away from Intersection.MeshRay and Mesh.PointAt.

As mentioned in my previous post - each time you update the Rhino.Geometry.Mesh it will be marked as dirty and any subsequent call to these methods will force a rebuild of the underlying acceleration structure which will kill performance.

An alternative I used with quite some success was porting the whole logic to a custom mesh library, and using OpenGL to display the underlying vertex and index buffers which is what Rhino does under the hood anyway.

While it works, this approach requires quite a bit of low-level hacks into the DisplayPipeline. It is only truly supported on Windows, and Rhino 9 is moving to D3D so I’m not even sure whether it will still be possible.

Having said that, if you want to explore this path, your best bet is to dissect the ghgl repo. It has a lot of unnecessary complexity for your needs, but it is open source and - when simplified - does exactly what you need: display triangles on screen regardles of where they came from. Once you’ll get that set up, you can then experiment with various raycasting libraries (Embree is great, but there are others too).

I will try to make a function for determining Mesh PointAt using OctreeCell IntersectRay

MeshPicker.cs (15.0 KB) managed to reduce the delay to 400 ms from 1000, The code still needs some work, but it’s working fine for now.

1 Like

Is this discussion still relevant if we’re switching away from OpenGL in next Rhino version?

AFAIK, R9 will still support OGL, similarly to how R8 supports .NET Core8 & .NET45

But fundamentally it is about bypassing the DisplayPipeline and updating the underlying geometry buffers directly on the GPU. We will be able to do this with D3D albeit the exact code will have to change.