Transient analysis-mesh topology for an existing CRhinoBrepObject?

Hi McNeel team,

We are implementing a Rhino 8 Windows C++ visual analysis mode. The presentation requires discrete classification bands with exact boundaries, so triangles crossing thresholds must be locally split; vertex colors alone produce interpolation.

We need:

  • An existing document CRhinoBrepObject to retain its UUID, runtime class, geometry, attributes and history.
  • A transient per-face ON::analysis_mesh with custom topology, used in Wireframe, Shaded and Rendered display.
  • Rhino-native depth, highlight, preselection, selection, face-interior picking, grips and Gumball.
  • No document Modified/Undo/event effects and no 3dm serialization.
  • Atomic replacement and clearing when the source geometry changes.

Our current findings:

  • CRhinoVisualAnalysisMode::UpdateVertexColors receives existing mesh pointers but exposes no topology provider/setter.
  • CRhinoObject::GetMeshes returns object-owned const meshes.
  • ON_BrepFace::SetMesh(ON::analysis_mesh, ...) appears to modify the source Brep’s analysis-mesh storage.
  • IRenderMeshProvider can provide custom render primitives for an ObjectId, but we cannot find an API that augments native picking for that source object.
  • A derived CRhinoBrepObject can override GetMeshes and Pick, but installing it through CRhinoDoc::ReplaceObject introduces document replacement/Undo/history/event behavior.

Is there a supported public Rhino 8 C++ SDK path for publishing a transient, source-associated analysis mesh with custom topology to an existing CRhinoBrepObject, without replacing the document object or mutating its persistent mesh cache?

If not, which extension point should a plug-in use for this combination of VAM display and native source-object picking? Is temporarily using ON_BrepFace::SetMesh(ON::analysis_mesh, ...) on the source supported, and can that mesh be guaranteed runtime-only and excluded from 3dm serialization?

Hi @mh_eos,

Can you provide an example, perhaps even a screen capture, of what type of analysis you are trying to display?

Thanks,

– Dale

Hi Dale,

Thank you. This is a draft-angle analysis on an existing CRhinoBrepObject.

For a pull direction and surface normal, we evaluate a signed draft value and classify it into three discrete regions:

  • red: value < 0 degrees
  • yellow: 0 degrees <= value < alpha
  • green: value >= alpha

The important requirement is that the 0-degree and alpha boundaries are exact discrete boundaries. With ordinary visual-analysis vertex colors, a triangle crossing a threshold interpolates between its vertex colors, so the boundary is rendered as a gradient. The first image shows that continuous interpolation with Rhino’s built-in Draft Angle Analysis.

To obtain the second result, we keep the source Brep unchanged, locally split only the threshold-crossing triangles in a transient mesh, and assign pure band colors to the resulting topology. This gives a single visible strict hard-band surface without replacing the document object.

Our remaining problem is ownership and interaction:

  • the original Brep must keep its UUID, selection state, grips, Gumball, Undo/history, and document lifecycle;
  • the refined transient mesh must be used for display and face-interior picking;
  • no persistent render mesh or document meshing setting may be modified.

We can currently display the refined mesh with an IRenderMeshProvider and associate it with the source object. However, in Wireframe Rhino does not appear to ask that provider for face-interior picking. The source can still be selected through its Brep edges/seams, but clicking the visible interior of the strict analysis surface does not select the source.

Is there a supported Rhino 8 C++ SDK route to publish a transient, topology-refined analysis mesh as the existing CRhinoBrepObject’s analysis/display-and-picking mesh, while leaving the document object and its persistent render meshes unchanged?

Attached:

  1. 01-rhino-native-gradient.png — Rhino built-in Draft Angle Analysis; the threshold-crossing area is continuously interpolated.
  2. 02-coshape-strict-hard-bands.png — the desired locally refined strict bands on the same source Brep.
  3. 03-wireframe-selected-source-seam.png — Wireframe strict surface with source selection visible through the Brep seam/edge path; face-interior picking is the missing part.

@dale hi?

Hi @mh_eos,

Thanks for the detailed writeup and the images — that makes the goal clear.

Short answer: there’s no supported public Rhino 8 C++ SDK path for publishing a transient, topology-refined analysis mesh onto an existing CRhinoBrepObject. Your findings are all accurate. Here’s what’s going on and what I’d do instead.

Don’t put the refined mesh in the source Brep

ON_BrepFace::SetMesh(ON::analysis_mesh, ...) is not a runtime-only slot:

  • Brep face analysis meshes are written to the 3dm. Rhino’s writer only has a switch for render meshes — analysis meshes are always saved when present. So your mesh ends up in your users’ files.
  • It wouldn’t survive anyway. When the display needs analysis meshes it compares the cached mesh’s ON_MeshParameters against the document’s analysis mesh settings. Yours won’t match, so Rhino treats the face as unmeshed, re-meshes it, and replaces your mesh.

So no, that route can’t be made runtime-only, and I wouldn’t recommend it even temporarily.

UpdateVertexColors will never do what you need

Correct — it hands you the object’s meshes as const, and the only thing the pipeline picks up afterwards is m_C. Rhino does have an internal hook that lets a visual analysis mode take over an object’s shaded draw completely, which is exactly what you’d want, but it isn’t part of the public SDK. I’ve made a note to look at exposing it.

What I’d do: a command plus a custom display conduit

Draw the refined mesh yourself from a CRhinoDisplayConduit:

  • Support SC_DRAWOBJECT. When the object being drawn is one you’re analyzing, set m_pChannelAttrs->m_bDrawObject = false and draw your locally refined, hard-banded mesh instead.
  • The document object is never touched — UUID, runtime class, attributes, history, grips, Gumball, selection and Undo all behave exactly as before, and nothing is serialized.
  • You own the cache. Key it on object id plus a geometry hash, rebuild from a CRhinoDoc replace/modify event watcher, and clear it when your mode ends. Atomic replacement and clearing are then entirely under your control.
  • A command that toggles the conduit on and off (or a modeless panel) is the natural wrapper.

Face-interior picking — you don’t need the refined mesh for it

This is the part I’d change in your design. CRhinoBrepObject::Pick switches to mesh (face-interior) picking whenever the object has a shaded visual analysis mode enabled, even in a Wireframe viewport — that behavior exists specifically so zebra and draft-angle objects pick sensibly in wireframe. And the pick runs against the object’s ordinary render mesh; Rhino’s own Draft Angle Analysis does exactly the same thing.

So:

  1. Register a CRhinoVisualAnalysisMode for your plug-in with false_color_style (or texture_style) so IsShaded() returns true.
  2. Enable it on the source object with CRhinoObject::EnableAnalysisMode(your_id, true). That call is const and only touches the object’s runtime analysis-mode list plus a redraw hint — no document Modified flag, no Undo record, no events, nothing written to the 3dm.

You then get native face-interior picking, preselection, highlighting and the What/Properties naming for free. The triangulation used for the pick differs slightly from your refined display mesh, but it’s the same underlying surface, so hits land where the user expects.

One thing to watch out for

Your current combination can’t work as-is: when a visual analysis mode is active on an object, Rhino skips custom render meshes entirely for the shaded draw, and a mode’s DrawBrepObject/DrawMeshObject overrides are only called for wireframe-style modes. So IRenderMeshProvider and a VAM will fight each other. Use the conduit for viewport drawing, and keep IRenderMeshProvider for Rendered/Raytraced, which goes through the RDK.

Hope this helps — let me know how it goes.

– Dale

Hi @dale,

Thank you very much — your explanation was exactly what we needed.

We followed your recommendation and changed the interactive viewport presentation to a CRhinoDisplayConduit: the source draw is suppressed with m_bDrawObject = false, and the conduit draws our topology-refined hard-band mesh. We keep a shaded visual analysis mode attached for Rhino’s native interaction path, while IRenderMeshProvider no longer owns the interactive viewport display.

The strict analysis colors now display consistently in Wireframe, Shaded, and Rendered modes, and the source-object selection highlight is working again, without replacing or modifying the document Brep.

Your warning about ON_BrepFace::SetMesh() and the explanation of Rhino’s picking behavior saved us from continuing down the wrong path. Thanks again for the detailed guidance!

Best regards,
Mh