Procedural geometry - pipes

The concept of generating geometry in Unity is not complicated. You have a list of vertices, and you tell Unity how those vertices should be connected to form triangles. Additionally you can provide information about UV layout for texturing and lighting / generating smooth normals. In the end you have three lists, vertices, triangles and UVs. You assign these to a mesh component and you get a mesh out of it. One thing to note is that vertices for each triangle must be assigned clockwise.

This tells Unity that the triangle is facing outwards and you will see it's surface. If you assign them counterclockwise the triangle will be facing inwards and will be invisible. For a much more in depth and eloquent explanation, see the tutorial on mesh generation from Catlike Coding

 Derpy clockwise vertex assignment illustration.

Derpy clockwise vertex assignment illustration.

For my purposes I need to generate pipes, to illustrate the backbone of a protein. A few things have to be done:

  1. Take a set of world space coordinates (location of amino acids) and use them as points along the pipe.
  2. Create a circle of vertices around each point, to define the profile of the pipe
  3. Rotate these circles so they form nice corners at each turn.

One is easy, I already have those coordinates for my space filling model.

Two is relatively simple as well. I can get the x and z (or y) coordinates using the parametric equation of an ellipse, with the radius value equal on each axis.

x = r cos t
z = r sin t

x and z are my coordinates I'm trying to find. r is the radius, which could be different for each axis. t is my angle in radians. 360° = 2π radians, so if I divide by n (the number of sides I want), I get a step size which I can add to t, n times, to get my circle of points. 

 

Three was the difficult step for me conceptually. My end solution was as follows:

Given three points, A, B, and C, get the vector AB, BC, and AC. Take the average vector of AB and BC. Get the cross product of AB and AC. Average vector tells me where the circle should be facing and the cross product gives me the normal of triangle ABC. I use the cross product to define "Up" and the average vector to define the "Look" value in Quaternion.SetLookRotation(crossVector, averageVector).

 
Figure: AB (red), BC (blue), Average vector (Green), Cross product / normal (Orange)
Pipe cross section

This gives me a quaternion that defines the plane and start point of each circle. I was getting weird twists in my pipes, where the start point for the circle would flip. When I connected my vertices they would cross to the opposites side of the next joint, bascially 180 degress around the the next circle. To keep everything lined up, I took the absolute value of each XYZ value in the cross product vector, and made this my up direction. No more twists! 

The end result is that for each point P in my backbone I use the previous point (P-1) and the next point (P+1) to calculate the orientation of the cross section at P. I could have used more cross sections to create smoother angles but as this is for VR purposes, poly counts must be kept low.