Tubes Tutorial

From GMLwiki

Jump to: navigation, search

[edit] Procedural Tubes in GenerativeML

dict dup begin
 /self          exch        def
 /curverad      0.4         def
 /curveseg      4           def
 /tubetypeid    0           def
 /piperad       0.1         def
 /trasserad     (0.08,0.05) def
 /openangle     45          def
 /tubethick     0.02        def
 /tubeseg       6           def
 /ringlength    0.1         def
 /ringthick     0.03        def
 /ground-offset (0,0)       def
 /ground-z      -1.0        def
 /model      { Tubes.Tools
       .model-tube-object } def
 model
end

Fig. 1: High-level GML code to create a tube


a) image:curverad-1.jpg b) image:curverad-2.jpg c) image:curverad-3.jpg d) image:curverad-4.jpg
e) image:pipeclosed-1.jpg f) image:pipeclosed-2.jpg g) image:pipeclosed-3.jpg h) image:pipeclosed-1-4.jpg
Fig. 2: Curve geometry. To achieve nice rounded edges, a circle segment is computed to connect subsequent tubes. The circle segment is offset to either side and sampled to obtain the radial line segments in (d). The circular profiles (n-gons) in (f) are converted to double-sided faces (g) that are then connected using a make-tunnel operation (h).


a) image:pipedetail-1.jpg b) image:pipedetail-2.jpg c) image:pipedetail-3.jpg d) image:pipedetail-4.jpg
Fig. 3: A higher density of the control mesh (green) of the subdivision surface improves the "roundness" of the tube (parameter tubeseg). The highest level of subdivision supported by our system, 4, is shown here.

The high-level tube parameters are shown in the GML code in Fig. 1. This code also creates the actual geometry by calling model that in turn calls the library function Tubes.Tools.model-tube. Fig 2 shows how this function proceeds and illustrates the geometric calculations. The basic problem is to connect two straight tube segments with a curved tube. Its curve is a circle segment starting at the dashed line before the actual joint in a distance determined by parameter curverad. The circle midpoint is computed as the intersection of the two dashed lines. Then the circle segment is sampled and radial line segments are produced (1d). These line segments are used to orient vertical profiles. The creation process is the same for all differnet types of tubes, namely by creating a sweep mesh from the vertical profiles (Fig. 2e-h). Note that the first and last faces have sharp (red) edges to produce a crease, resulting in flat faces with a B-spline boundary (Fig. 3a,b).

a) image:pipeopen-1.jpg b) image:pipeopen-2.jpg c) image:pipeopen-3.jpg d) image:pipeopen-4.jpg
e) image:trasse-1.jpg f) image:trasse-2.jpg g) image:trasse-3.jpg
i) image:trasseopenpoly-009.jpg j) image:trasseopenpoly-072.jpg k) image:trasseopenpoly-120.jpg
Fig. 4: Different tube types through different profiles. The open profiles are also created procedurally, the opening angle can vary smoothly and may even be animated. g) shows the lowest resolution, 0 times subdivided.

The great power of GenerativeML is that it supports process chains quite efficiently. The stack is a flexible way for passing data produced by one function as input parameters to the next. This is exploited to create different types of pipes simply by passing different profiles to a connecting function (Fig. 4). To open up the rounded pipes (openangle parameter), two sampled circle segments with different radii are connected (4b) and connected as before (4c,d). The same way pipes with rectangular profile can be created, so-called "trasse" style (2D parameter trasserad for x/y-dimensions), and these "trasse" tubes can also be opened up. Note that the profile for open tubes depends on the opening angle, so the open "trasse" profile can also have 10 points (4j).

a) image:z-ring-pipeopen.jpg b) image:z-pipes-and-trassen-1.jpg c) image:z-pipes-and-trassen-2.jpg
d) image:tubehierarchy-1.jpg e) image:tubehierarchy-2.jpg f) image:tubehierarchy-3.jpg
Fig. 5: Rings are added to better distinguish the curved joints between straight tube segments. b) shows both tube types, pipe and trasse, in both flavors, open and closed. c) shows offset tubes, (d-f) are hierarchical tubes.

Rings around the tube endings are important clues that help understanding the spatial relationships (Fig. 5a), so that also tubes of different types can be joined without creating too much confusion (5b). They are controlled using the parameters ringlength and ringthick.

A minor complication comes from the fact that tubes often run nexts to each other (offset tubes). They seem to run in parallel, but the truth is that in curves, offset tubes share the same curve midpoint but have different radii depending on whether the curve goes to the left or right (5c). This problem can elegantly be solved by re-using the procedure that was used for creating the tubes in the first place, as can be seen by the similarity to Figs. 2a-d). Once it is built in, all tube types immediately inherit the ground-offset parameter. This feature literally opens up a great perspective, because it permits hierarchical groups of pipes that all follow the same route that can be opened up at the same time using simple parameter propagation of the opening angle (5d-f).

a) image:multijoint-1.jpg b) image:multijoint-2.jpg c) image:multijoint-3.jpg
Fig. 6: Multi-way joints. For an n-joint, first a face of degree 2n is created at the bottom that becomes extruded. Then the midpoint is inserted and connected star-wise, once for the bottom and for the top.

The final feature that we have added to the procedural tubes are multi-way joints (Fig. 6). They are needed in few, but very distinct places in our models. At present they can not be opened up and they do not support offsets. But as all the required sorts of geometric calculations have already been introduced we consider this a straightforward extension that we might carry out as future work if really needed.

[edit] The Interface GenerativeML offers to the scene graph

a) image:tubegraph-1.jpg b) image:tubegraph-2.jpg
Fig. 7: Two graphs of tubes. Their only difference is the tubetypeid.

We consider as a major contribution of our work the simplicity of the interface between the scene graph and the model generation using GenerativeML. This claim shall be illustrated by the script code that produces the graph in Fig. 7. This graph is composed of two 3-way joints that are connected by three tubes. One of them is a 1-segment tube, the other two are 3-segment tubes.

One scene graph node early in the traversal order is only used to create a parameter dictionary. It fills it first with default values and then overwrites some of them, e.g., to create the opening of 70 degrees and a thickness of the tube wall of 4 centimeters:

Tubes.tube-create
Tubes.tube-defaults /MyTemplate exch def
MyTemplate begin
 /tubetypeid  0     def
 /openangle   70    def
 /tubethick   0.04  def
end

This parameter dictionary is not directly used to create geometry, but only serves as template to be copied by script code in subsequent nodes later on in the traversal. The type of the object (tube or n-way joint) is determined by the model function defined there, and of course the tube polygon has to be defined. This works like this for one of the 3-segment tubes:

My Tempelate copy dup begin
 /self          exch             def
 /ground-poly   [ (2,3) (3,2)  
                  (4,3) (4,4) ]  def
 /model         { Tubes.Tools.model-tube-object
                }                def
 model
end

The first and last coordinates, (2,3) and (4,4), are the positions of the two 3-way joints. These joints are created in much the same way. The difference is only that they use the function model-joint-object, and that the center point of the joint is explicitly mentioned. The ground-poly has now the function of a list of (arbitrary many) neighbours. Very useful in practice is that this list can be given in any order, as the neighbours are sorted in clockwise order anyways by the model function. Note that ground-poly contains one point from the tube above, and also the position of the other joint:

MyTempelate copy dup begin
 /self          exch             def
 /ground-center (2,3)            def
 /ground-poly   [ (3,2) (4,4) 
                  (2,4)       ]  def
 /model         { Tubes.Tools.model-joint-object
                }                def
 model
end

The other tubes and joints are created exacly the same way. -- Note that creating tubes with a "trasse" instead of a pipe style is as easy as changing the tubetypeid from 0 to 1. In that case the scene graph will take care of managing a "dirty" flag that will lead to a re-evaluation of all affected nodes, so that within a fraction of a second the model in Fig. 7a) looks as the one in 7b). This again illustrates the complementarity of both concepts.

Personal tools