Is it possible to create, in .NET, some graphic objects which are not Rhino objects?
Yes. The solution is to use a display conduit to draw. For an overview of display conduits, See Display Conduit Introduction
In the following we will dynamically draw a mesh. The first thing we need a display conduit to draw the mesh for us. Considering the following class:
public class DrawBlueMeshConduit : MRhinoDisplayConduit { private OnMesh m_mesh = null; private OnColor m_drawing_color = null; private MDisplayPipelineMaterial m_material = null; private OnBoundingBox m_mesh_bbox = null; public DrawBlueMeshConduit(OnMesh mesh) : base(new MSupportChannels(MSupportChannels.SC_CALCBOUNDINGBOX | MSupportChannels.SC_PREDRAWOBJECTS), false) { // set up as much data as possible so we do the minimum amount of work possible inside the // actual display code m_mesh = mesh; m_drawing_color = OnColor.FromColor(System.Drawing.Color.Blue); m_material = new MDisplayPipelineMaterial(); m_material.m_FrontMaterial.m_diffuse = m_drawing_color; if (m_mesh != null && m_mesh.IsValid()) m_mesh_bbox = m_mesh.BoundingBox(); } public override bool ExecConduit(ref MRhinoDisplayPipeline dp, uint nChannel, ref bool bTerminate) { if (m_mesh_bbox == null) return true; // this is called every frame inside the drawing code so try to do as little as possible // in order to not degrade display speed. Don't create new objects if you don't have to as this // will incur an overhead on the heap and garbage collection. if (nChannel == MSupportChannels.SC_CALCBOUNDINGBOX) { // Since we are dynamically drawing geometry, we needed to override // SC_CALCBOUNDINGBOX. Otherwise, there is a good chance that our // dynamically drawing geometry would get clipped. // Union the mesh's bbox with the scene's bounding box m_pChannelAttrs.m_BoundingBox.Union(m_mesh_bbox); } else if (nChannel == MSupportChannels.SC_PREDRAWOBJECTS) { // Time to draw our mesh // Get the active viewport MRhinoViewport vp = dp.GetRhinoVP(); if (vp.DisplayMode() == IOn.display_mode.wireframe_display) { // If the viewport is in wireframe, draw a wireframe mesh dp.DrawWireframeMesh(m_mesh, m_drawing_color); } else { // Otherwise, draw a shaded mesh dp.DrawShadedMesh(m_mesh, m_material); } } return true; } }
Public Class DrawBlueMeshConduit Inherits MRhinoDisplayConduit Private m_mesh As OnMesh = Nothing Private m_drawing_color As OnColor = Nothing Private m_material As MDisplayPipelineMaterial = Nothing Private m_mesh_bbox As OnBoundingBox = Nothing Public Sub New(ByVal mesh As OnMesh) MyBase.New(New MSupportChannels(MSupportChannels.SC_CALCBOUNDINGBOX Or MSupportChannels.SC_PREDRAWOBJECTS), False) ' set up as much data as possible so we do the minimum amount of work possible inside the ' actual display code m_mesh = mesh m_drawing_color = OnColor.FromColor(System.Drawing.Color.Blue) m_material = New MDisplayPipelineMaterial() m_material.m_FrontMaterial.m_diffuse = m_drawing_color If (m_mesh IsNot Nothing AndAlso m_mesh.IsValid()) Then m_mesh_bbox = m_mesh.BoundingBox() End If End Sub Public Overrides Function ExecConduit(ByRef pipeline As RMA.Rhino.MRhinoDisplayPipeline, ByVal channel As UInteger, ByRef terminate As Boolean) As Boolean If (m_mesh_bbox Is Nothing) Then Return True ' this is called every frame inside the drawing code so try to do as little as possible ' in order to not degrade display speed. Don't create new objects if you don't have to as this ' will incur an overhead on the heap and garbage collection. If (channel = MSupportChannels.SC_CALCBOUNDINGBOX) Then ' Since we are dynamically drawing geometry, we needed to override ' SC_CALCBOUNDINGBOX. Otherwise, there is a good chance that our ' dynamically drawing geometry would get clipped. ' Union the mesh's bbox with the scene's bounding box m_pChannelAttrs.m_BoundingBox.Union(m_mesh_bbox) ElseIf (channel = MSupportChannels.SC_PREDRAWOBJECTS) Then ' Time to draw our mesh ' Get the active viewport Dim vp As MRhinoViewport = pipeline.GetRhinoVP() If (vp.DisplayMode() = IOn.display_mode.wireframe_display) Then ' If the viewport is in wireframe, draw a wireframe mesh pipeline.DrawWireframeMesh(m_mesh, m_drawing_color) Else ' Otherwise, draw a shaded mesh pipeline.DrawShadedMesh(m_mesh, m_material) End If End If Return True End Function End Class
Here we have an MRhinoDisplayConduit derived class. The constructor of the class requires that we define when we want Rhino to call our display conduit. In this case, we want to be called to be called to ensure the drawing bounding box is the correct size (MSupportChannels.SC_CALCBOUNDINGBOX) and when Rhino is drawing visible, non-highlighted objects (MSupportChannels.SC_PREDRAWOBJECTS). All MRhinoDisplayConduit derived class are required to override the ExecConduit abstract member function. This is were all of the action will take place.
To use the above class, we can write a simple command, such as the following that creates a meshed sphere and then draws it with our conduit:
public override IRhinoCommand.result RunCommand(IRhinoCommandContext context) { // Define the sphere OnSphere sphere = new OnSphere(); MArgsRhinoGetSphere args = new MArgsRhinoGetSphere(); IRhinoCommand.result rc = RhUtil.RhinoGetSphere(args, ref sphere); if (rc != IRhinoCommand.result.success) return rc; // Create a mesh sphere OnMesh mesh = RhUtil.RhinoMeshSphere( sphere, 10, 10 ); if( mesh == null ) return IRhinoCommand.result.failure; // Create an instance of our conduit DrawBlueMeshConduit conduit = new DrawBlueMeshConduit(mesh); // Enable the conduit - don't for get to regen conduit.Enable(); context.m_doc.Regen(); // Pause so the user can see our work MRhinoGetString gs = new MRhinoGetString(); gs.SetCommandPrompt("Press <Enter> to continue"); gs.AcceptNothing(); gs.GetString(); // Disable the conduit - don't for get to regen conduit.Disable(); context.m_doc.Regen(); return IRhinoCommand.result.success; }
Public Overrides Function RunCommand(ByVal context As RMA.Rhino.IRhinoCommandContext) _ As RMA.Rhino.IRhinoCommand.result ' Define the sphere Dim sphere As New OnSphere() Dim args As New MArgsRhinoGetSphere() Dim rc As IRhinoCommand.result = RhUtil.RhinoGetSphere(args, sphere) If (rc <> IRhinoCommand.result.success) Then Return rc ' Create a mesh sphere Dim mesh As OnMesh = RhUtil.RhinoMeshSphere(sphere, 10, 10) If (mesh Is Nothing) Then Return IRhinoCommand.result.failure ' Create an instance of our conduit Dim conduit As New DrawBlueMeshConduit(mesh) ' Enable the conduit - don't for get to regen conduit.Enable() context.m_doc.Regen() ' Pause so the user can see our work Dim gs As New MRhinoGetString() gs.SetCommandPrompt("Press <Enter> to continue") gs.AcceptNothing() gs.GetString() ' Disable the conduit - don't for get to regen conduit.Disable() context.m_doc.Regen() conduit.Dispose() Return IRhinoCommand.result.success End Function