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:
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)
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.
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):
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.
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:
Connectivity of all surfaces is calculated during a topology building traversal of the scene graph, before a tessellation traversal.
The surfaces in the scene are tessellated during a second traversal.
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.
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.
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
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
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().
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(); }; |
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. |
The static member topology is an array of all topologies that have been created.
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”).