Ins and Outs of drawing to screen space

I have quite a number of GH components that render geometry (text, curves, meshes, etc) to fixed viewport coordinates to create a sort of “heads-up display.” These have always worked okay-but-not-great - the strategy I employ has some drawbacks. What I’m doing generally is to use some of the methods on RhinoViewport like GetWorldToScreenScale and GetFrustum(near/far)Plane in order to figure out where to draw. I am typically picking some depth parameter along the frustum and positioning a screen-parallel plane there, and using ClientToWorld/WorldToClient to convert coordinates.

Some of the limitations of this approach:

  • Depending on what “depth” parameter is chosen, it is hard to control whether the geometry I draw goes “behind” real scene objects. I have tried Push/Pop Depth Testing, Enable/Disable Depth Writing, setting the DepthMode, etc, but have never been successful - perhaps I am just not understanding.

  • I also struggle with clipping issues. I can manipulate the component’s clipping box, but this is unfortunately designed for use with world-oriented geometry and not calculated on a per-viewport basis. Because the geometry I’m drawing may be very far from the origin (and the scene) it seems like a mistake to add it to the scene bounding box. is there anything else I can do to prevent clipping, or a better strategy for working with the component’s ClippingBox?

  • With these components in use, right-click orbit behavior in the viewport behaves unexpectedly (setting the center of the orbit far away from what one would expect, requiring a constant “recentering” with zoom-selected or some other method.) Is there a way to exclude certain drawn geometry from this calculation?

Would greatly appreciate any guidance DisplayPipeline gurus have for me!

Quick notes, you should use the Frustum (aka camera near plane) to draw things on if you want them to be in front of other geometry. You can disable depth testing and depth writing (DisplayPipeline.EnableDepthWriting/Testing). If you disable testing then your geometry will just be on top of everything that was drawn before it. If you disable writing then your geometry won’t appear in the depth buffer. For drawing HUD like stuff I recommend disabling testing but keeping writing.

If you can prototype some functions for drawing that would make your life easier, I can see what I can do about adding these to the SDK. All I need are declarations of functions; I can fill in the implementation.

I would assume that you would be doing all of this drawing in the DrawForeground stage of drawing and that you would be using a traditional 2D painters algorithm for drawing (i.e. no depth testing/writing).

@stevebaer @DavidRutten I wonder if the “stage” of drawing is part of the problem - In the context of grasshopper drawing (via DrawViewportWires method override) I am only drawing via DisplayPipeline.DrawXXX methods, and I’m not clear exactly which channel/stage those are drawing in (apologies if I’m misunderstanding here, my DisplayPipeline/Conduit understanding is shaky at best.)

What I would love, if we’re making wishes, is essentially “2d” versions of those draw methods. Draw2DText already exists but looks terrible in the viewport (no anti-aliasing, lack of justification options, etc). I would find especially useful:

Draw2DCurve(Curve curve, Color color, Int32 thickness)
Draw2DFilledRegion(Curve[] curve, Color color) - just a way to draw filled regions with a curve or list of curves, like a 2D solid hatch.
Draw2DText(String text, Color color, Point2d ScreenCoordinate, TextJustification textJustification, int height, string fontFace) (But with anti-aliased screen text)
DrawScreenMesh(Mesh mesh, Point2d screenCoordinate, Color color, bool fixed=true) A means of drawing a mesh in front of everything else at a screen location - mapping from its world position to a center "orbit point" at the screen coordinate.

See the behavior of the 3d geometry in this video: https://vimeo.com/89824822

These are all just ideas. I am not opposed to continuing to use the current methods I am using to do this if I could simply figure out how to avoid the clipping / zoom center / occlusion issues I mentioned above.

So when you draw a 2D curve, how does the coordinate system of the curve translate to the coordinate system of the viewport?

the way I’ve handled it to date maps directly from world XY to screen XY, and I do some mirroring over the X-axis at the object level so that “up” stays up. I would be OK leaving this mirroring portion to the user to avoid ambiguity.

I would imagine this would just be the window coordinate system (0,0 is top,left and width,height is the client area of the window).

DrawViewportWires is probably not quite the correct spot for drawing HUD 2D effects. DrawForeground or maybe even some new location would be better.

Yes this sounds logical - but correct me if I’m wrong @DavidRutten but I don’t think GH exposes any way to supply code to DrawForeground.

The two Grasshopper channels (DrawViewportWires and DrawViewportMeshes) both happen in PostDrawObjects. In future releases of GH I can add more channels including one for HUD drawing. At any rate the display needs to be rethought from scratch for GH2, so may as well make allowances for this sort of stuff.

Having greater control in GH2 would be great.

With all this said though - is there anything (even something hacky) I can do to “always draw in the front” from drawViewportWires? I’d like to see if I can get this working in rh5/gh1. Just drawing right at the cameraNearPlane results in really extreme clipping and graphic issues.

Also - a specific detail question I had - is there a difference between calling PushDepthTesting(false) and then PopDepthTesting() at the end vs just calling EnableDepthTesting(false) and then EnableDepthTesting(whateverThePreviousSettingWas) at the end? My assumption is that these would (basically) have the same effect.

Sure; just add a callback to the DisplayPipeline.DrawForeground event. This is always called after DrawViewportWires.

This is essentially the same

ok sweet! I think that’s the bit I was missing. Will give it a try and come whining when I can’t figure it out :stuck_out_tongue_closed_eyes:

Thanks for your help guys!

Are we talking about V6? The text drawing in V6 is completely different

oh haven’t tried in v6.

at initial glance Draw2DText no longer works at all in V6. the same code that works in v5 doesn’t work at all - maybe I’m missing some new method I should be using instead.

  public override void DrawViewportWires(IGH_PreviewArgs args)
  {
    args.Display.Draw2dText("Hello", Color.Black, new Point2d(100, 100), false, 40);
  }
1 Like

Sounds like a bug to me. You shouldn’t have to adjust your code to get things to work in V6

it’s working for me now - because it was the only thing previewing nothing was showing up - as soon as there was other geometry previewing in GH the text appeared. And yes, it looks much better. Would still love to be able to pass a proper TextJustification rather than just a bool for left/center.

Drawing from a DrawForeground callback is working great - thanks for the tip @stevebaer! I’m getting some funny behavior in Rh6 but I’m not gonna worry too much because I’m still experiencing general display weirdness from GH in rh6.

Please report any weirdness you see. Most likely I don’t already know about it

the weirdness I was referring to was this. The current weirdness I’m experiencing is this:

the red tags/arrows are what I’m drawing in a DrawForeground callback. It’s completely smooth in rhino 5, no cutting out - in 6 things cut in and out as shown. Would be happy to share the source privately if it helps.

3 Likes