## Chapter 10. Creating and Maintaining Surface Topology

Most objects in a large model are made of many parametric surfaces. The OpenGL Performer classes that describe the connectivity of parametric surfaces—that is, their topology—allow you to “ stitch” surfaces together by defining shared boundary curves, and to propagate surface contact information.

The main purpose for shared-boundary information is to generate tessellations of adjacent surfaces that are consistent, that is, no cracks develop between any pair of rendered surfaces. Tessellations are discrete approximations of surfaces in terms of renderable geometric primitives, typically triangles (see Chapter 11, “Rendering Higher-Order Primitives: Tessellators”).

These topics are covered in this chapter:

The topology classes provide definitions of boundary curves shared by adjacent parametric surfaces. Discrete versions of these curves are used by tessellators to prevent cracks. A rendered image can have artificial cracks due to the following:

• Difficulty sampling enough points on the boundary between two surfaces so that mismatches of the tessellations are imperceptible

• Finite-precision mismatches between coordinates of ideally identical points, for example at triple junctions where the edges of three surfaces meet at a point

Propagating surface contact information is useful for other tasks, such as

• Maintaining consistent normal vectors for adjacent surfaces

• Deforming a surface and consistently deform an adjacent surface

• Determining whether an edge of a surface is in fact a shared boundary

• Creating a mirror image of a compound surface (you can use topological information to reorient the surface)

## Summary of Scene Graph Topology: pfTopo

The class pfTopo holds data that indicates whether, and how, two pfParaSurfaces are in contact. You can create several pfTopos for a particular scene: for example, one each for subassemblies. A static member of pfTopo lists all the pfTopos that you create.

pfTopo maintains lists of surfaces and boundaries (pfBoundarys) that are shared by an arbitrary number of surfaces. Figure 10-1 illustrates how these data structures define relations between pfParaSurfaces.

When an edge has been tessellated, the associated pfBoundary holds a discrete version of the curve. This discrete version is needed for consistent tessellations because it specifies one set of boundary vertices for tessellating all the surfaces that share the boundary. The role of pfBoundary in determining a consistent tessellation is illustrated in Figure 10-2.

The classes pfTopo and pfBoundary are examples of b-reps, which identify objects in terms of their bounding objects. pfBoundary is also winged data structures, a particular form of b-rep.

### Building Topology: Computing and Using Connectivity Information

Given a set of pfParaSurfaces in a scene graph, there are several ways to develop a set of shared vertices to be held in pfBoundarys. The following sections describe the topology construction strategies (beyond the low-fidelity alternative of ignoring topology):

#### Building Topology Incrementally: A Single-Traversal Build

As each surface is tessellated during a traversal, the tessellator checks for previously tessellated adjacent surfaces, uses existing vertices when it can, and adds necessary data to topology data structures.

Although OpenGL Performer's incremental topology building tools attempt to avoid cracks, they can, in principle, appear: When a surface is added, a new junction on the boundary of an existing, tessellated surface may occur and the junction point may not be in the existing tessellation. The tessellation of the added surface introduces the junction point, necessarily at a finite distance from the existing tessellation, and a crack appears between the newly and previously tessellated surfaces.

#### Building Topology From All Scene Graph Surfaces: A Two-Traversal Build

Topology built with two passes is very clean; unlike a single-pass build, in principle no cracks due to unforeseen junctions can occur. The added cost of performing a two-traversal build is slight; it is the recommended way to build topology and perform tessellations if you want high-quality images. When building topology in two traversals, the following steps occur:

1. Connectivity of all surfaces is calculated during a topology building traversal of the scene graph, before a tessellation traversal.

2. The surfaces in the scene are tessellated during a second traversal.

#### Building Topology From a List of Surfaces

You can explicitly accumulate a list of surfaces for which to build topology and then tessellate the surfaces. The result is clean tessellations of the surfaces on the list. Cracks may appear if an adjacent surface was not included in the list.

#### Building Topology “by Hand”: Imported Surfaces

If you have a set of surfaces for which you know connectivity, you can explicitly develop the appropriate topological data structures and develop consistent tessellations.

The presence of cracks will depend on how good your input trim curves are. If three surfaces meet at a junction point that is not the shared endpoint of trim curves, a crack may appear.

#### Summary of Topology Building Strategies

Table 10-1 lists the methods required for each of the topology building strategies. See “Base Class pfTessellateAction” in Chapter 11 for more information about the tessellation methods listed.

Table 10-1. Topology Building Methods

Topology Building Strategy

Methods

Ignore topology information and let cracks appear as they will.

1. Do not create an pfTopo or build topology.
2. pfTessellateAction::setBuildTopoWhileTess(FALSE).
3. pfdTessellateGeometry( root , tessAction )

Build topology incrementally.

1. Create an pfTopo.
2. pfTessellateAction::setBuildTopoWhileTess(TRUE).
3. pfTessellateAction::setTopo(topo).
4. pfdTessellateAction( root, tessAction ).

Two-traversal build.

1. Create an pfTopo.
2. pfTopo::buildTopologyTraverse( root ).
3. pfTessellateAction::setBuildTopoWhileTess(FALSE).
4. pfdTessellateAction( root, tessAction ).

Assemble a list of surfaces, build the topology, and then tessellate.

1. Create an pfTopo.
2. Assemble list of surfaces: pfTopo::addSurface( surf ).
3. pfTopo::buildTopology().
4. pfTessellateAction::setBuildTopoWhileTess(FALSE).
5. pfdTessellateGeometry( shape, tessAction ).

Build the topology “by hand.”

See the file /usr/share/Performer/src/pgu ide/libpf/C++/topoTest.C
(IRIX and Linux)

%PFROOT\Src\pguide\libpf\C++ \topoTest.cxx
(Microsoft Windows)

S
tep 7 does not appear in the code because FALSE is the default.

1. Create an pfTopo.
2. Assemble list of surfaces: pfTopo::addSurface().
3. Create pfBoundarys.
6. Set boundary orientation: pfEdge::setBoundaryDir().
7. pfTessellateAction::setBuildTopoWhileTess(FALSE).
8. pfdTesselateGeometry( shape, tessAction ).

You can add topological information to an existing set of connected, higher-order surfaces in a file—for example, NURBS in an .iv or .csb file—and save the information for future, crack-free surface rendering. As a result, you do not have to repeat the topology build. The function pfdLoadFile() reads the topological information in a .pfb file.

Before you save the scene graph data, you can also add tessellations that use the topology to give crack-free images (see Chapter 11, “Rendering Higher-Order Primitives: Tessellators”).

Table 10-2 shows three possible file conversions that you can apply to .iv or .csb files that contain reps but no topology or tessellation; they are listed with example pfconv command lines, which demonstrate how to use both the pfctol and pfttol pseudo loaders.

Table 10-2. Adding Topology and Tessellations to .iv and .csb Files

Conversion

Example Command Line

Format change only.

pfconv sur.csb sur.pfb

Add topology information to scene graph: save reps and topology information but not tessellations.

pfconv sur.iv.topoTol.ttol surTopo.pfb

or

pfconv sur.csb.topoTol.ttol surTopo.pfb

Add topology information and tessellations to scene graph: save reps, topology, and tessellations.

pfconv sur.iv.topoTol.ttol.tessTol.ctol surTopoTess.pfb

or

pfconv sur.csb.topoTol.ttol.tessTol.ctol surTopoTess.pfb

Add topology, tesselate, but do not save reps.

pfconv sur.csb.topoTol.ttol.tessTol.ctol geodes.pfb.~.ctol

If you perform conversion, you may have files with or without tessellations. Depending on the type of file you read, use one of the command lines in Table 10-3.

Table 10-3. Reading and Writing .pfb Files: with and without Tessellations

 To read a .pfb file and perform tessellation (without having to build topology): perfly surTopo.pfb.tessTol.ctol To read a .pfb file that already has tessellations perfly surTopoTess.pfb To read a .pfb file that already has tessellations and force retesselation (thus, removing any existing geometry associated with the higher order primitives) perfly surTopoTess.pfb.+tessTol.ctol To read a .pfb file that already has tessellations and store it without reps perfly surTopoTess.pfb geodes.pfb.~.ctol

To delete the tessellation date, use the method clearTessellation().

### Class Declaration for pfTopo

The class has the following main methods:

 ```class pfTopo : public pfObject { public: // Creating and destroying pfTopo( ); virtual ~pfTopo(); // Accessor functions void setDistanceTol( pfReal tol, pfLengthUnits u ) pfReal getDistanceTol( ) const; pfLengthUnits getLengthUnits() const; static pfTopo* getGlobalTopo(int n); static int getNumTopos(); pfParaSurface* getSurface( int i ); int getSurfaceCount( ) const; pfBoundary* getBoundary( int i ); int getBoundaryCount( ) const; int getSolidCount() const; pfSolid* getSolid( int i ) //Adding topological elements int addSurface( pfParaSurface *sur ); int addBoundary( pfBoundary *bnd ); //Topology construction void buildTopology(); int buildSolids(); }; ```

### Main Features of the Methods in pfTopo

buildSolids()

Collects connected surfaces in the pfTopo into pfSolids (see “Collecting Connected Surfaces: pfSolid”).

buildTopology()

Builds consistent set of boundaries from the list of surfaces accumulated by calls to addSurface(). Previously developed boundaries are deleted.

pfTopo(tol,u,sizeEstimate)

Construct a topological data structure.

tol specifies a tolerance for calculating when points are close enough together to be considered the same. Default is 1 millimeter.

u specifies the system of units for tol. Default is meters.

getLengthUnits() and setLengthUnits()

Gets and sets the measurement units in object space for this pfTopo.

getNumTopos()

Returns the number of pfTopo structures in the global array of pfTopos.

getGlobalTopo()

Returns the specified pfTopo ID from the global array of pfTopos.

 Note: In addition, it is possible to traverse the scene graph and build a consistent set of boundaries for all the surfaces under a specified node by using the pfdBuildTopologyTraverse(pfTopo *topo,pfNode *root) function.

The static member topology is an array of all topologies that have been created.

## Collecting Connected Surfaces: pfSolid

To maintain consistent normals or propagate deformation information, organize connected pfParaSurfaces in an pfSolid. With an pfSolid, you can collect connected surface patches in one object for convenient access and manipulation.

Despite the name of the class, the set of surfaces need not form a closed surface, that is the boundary of a volume. They can be a set of patches joined to form a surface, for example, you might generate a hood of a car from two pfParaSurafaces that are mirror images of each other.

To create solids, collect them in an pfTopo and then call pfTopo::buildSolid() (see “Summary of Scene Graph Topology: pfTopo”).

### Class Declaration for pfSolid

The class has the following main methods:

 ```class pfSolid : public pfObject { public: // Creating and destroying pfSolid() virtual ~pfSolid() // Accessor functions int addSurface( pfParaSurface *sur ); pfParaSurface* getSurface( int i); int getSurfaceCount( ) const; int getSolidId() const; }; ```

### Main Features of the Methods in pfSolid

Use the methods only after you have created an pfSolid with pfTopo::buildSolid().

Treat the method setSolidId() that appears in pfSolid.h as private: it is used by pfTopo::buildSolid() when building the solid.