Animating Surfaces using C#

I’m trying to make a method in my plugin that can take a List of Surfaces and generate an “animation” from them, like each surface is a different frame. Here’s what I have so far:

EscapeKeyEventHandler handler = new EscapeKeyEventHandler(“Press to stop visualization.”);
bool pressed = false;
List<System.Guid> guids = new List<System.Guid>();

        for (int i = 0; i < surfaces.Count; i++)
        {
        	guids.Add(doc.Objects.AddSurface(surfaces[i]));
        }
        for (int i = 0; i < surfaces.Count; i++)
        {
        	doc.Objects.Hide(doc.Objects.Find(guids[i]), true);
        }
        //Here();
        for (int i = 0; !pressed; i++)
        {
        	if (handler.EscapeKeyPressed)
            {
            	pressed = true;
            }
        	
        	doc.Objects.Hide(doc.Objects.Find(guids[i]), true);
        	doc.Objects.Show(doc.Objects.Find(guids[i]), true);
        	doc.Views.Redraw();
        	for(int e = 0; e < 9999999; e++) {}
        	
        	if (i + 1 == surfaces.Count)
        	{
        		i = -1;
        	}
        }

In Wireframe mode, the visualization is beautiful. But I have two problems: for bigger surfaces, it takes longer to hide and show, which makes the animation a bit choppy, and in Rendered mode you can see each individual hide and show, which almost makes the visualization look transparent and choppy. Are there any ways to improve this method WITHOUT using other plugins, like Bongo?

Make sure that all surfaces have their render meshes generated before you start the animation. Otherwise, at each frame the meshes need to be generated first, which can take some time.

Also

for (int e = 0; e < 9999999; e++) {}

I assume this is some sort of pause? You’re better off with

RhinoApp.Wait(10); // in milliseconds

Another option would be to do this drawing in a display conduit, instead of by hiding and showing. This would involve a “director” that does this:

static int frame = 0;
while(!escapePressed && frame >= surfaces.Count)
{
  doc.Views.Redraw();
  frame++;
  RhinoApp.Wait(40); // wait 40 ms to get to 25 Hz frame rate  
}

And a display conduit that draws the corresponding frame

override void PostDrawObjects(DrawEventArgs a)
{
  // to draw wireframe
  Surface toDraw = surfaces[frame];
  a.DisplayPipeline.DrawSurface(toDraw);
  // to draw shaded:
  Mesh toDrawShaded = meshes[frame]
  a.DisplayPipeline.DrawMeshShaded(toDrawShaded, material);
}

Note that to draw in shaded mode, you need a Mesh, not a Surface. So make meshes

for(int i = 0; i < surfaces.Length; ++i) 
{ 
  using(Brep b = surfaces[i].ToBrep())
    meshes[i] = Mesh.FromBrep(b, MeshingParameters.Default);
}
1 Like

Wow, that’s a lot of good strategies I didn’t think of. I do have a couple questions though:

  1. RhinoApp.Wait() doesn’t take any arguments, at least when I copied it from your code snippets. Do I have to do more than just copy and paste what you have?

  2. I’ve never used conduits before, how do I implement them into my code? In your PostDrawObjects method, where is “surfaces” initialized?

  3. You said “make sure that all surfaces have their render meshes generated before you start the animation”, how do I do that?

  4. Is the method for Rendered the same for Wireframe?

Thanks for your help!

  1. RhinoApp.Wait() indeed does not take arguments, my mistake.

  2. See http://developer.rhino3d.com/guides/rhinocommon/display-conduits/

  3. You need to coordinate the surfaces between what I called “the director”, the code that is calling doc.RedrawViews() (probably inside a command) and the display conduit that is drawing to the screen. One way to do this is to generate the surfaces in the command, then pass them in the constructor to the display conduit, enable the conduit, then start the animation.

  4. This is for the way you do it now. After adding the Surface to the document, retrieve it and call CreateMeshes on it and CommitChanges(), like so:

Guid g = doc.Objects.AddSurface(surfaces[i]);
RhinoObject obj = doc.Objects.Find(g);
MeshingParameters current = doc.GetMeshingParameters(MeshingParameterStyle.Fast); 
obj.CreateMeshes(MeshType.Render, current, false);
obj.CommitChanges();
  1. The code for Rendered is not the same as for Wireframe; I have given both (see PostDrawObjects). You need to decide, based on the viewport you are drawing to, which code to use. To find out if a viewport is drawing in Shaded mode, you use:
void PostDrawObjects(DrawEventArgs a)
{
  bool drawShaded = a.View.DisplayMode.DisplayAttributes.ShadingEnabled;
}