Selection (Selection and Feedback) (OpenGL Programming) Part 2

Picking with Multiple Names and a Hierarchical Model

Multiple names can also be used to choose parts of a hierarchical object in a scene. For example, if you were rendering an assembly line of automobiles, you might want the user to move the mouse to pick the third bolt on the left front wheel of the third car in line. A different name can be used to identify each level of hierarchy: which car, which wheel, and finally which bolt. As another example, one name can be used to describe a single molecule among other molecules, and additional names can differentiate individual atoms within that molecule.

Example 13-4 is a modification of Example 3-4, which draws an automobile with four identical wheels, each of which has five identical bolts. Code has been added to manipulate the name stack with the object hierarchy.

Example 13-4 Creating Multiple Names

Creating Multiple Names

 

 

 

Creating Multiple Names


Example 13-5 uses the routines in Example 13-4 to draw three different cars, numbered 1, 2, and 3.

Example 13-5 Using Multiple Names

Using Multiple Names

Assuming that picking is performed, the following are some possible name-stack return values and their interpretations. In these examples, at most one hit record is returned; also, dl and d2 are depth values.

2 dl d2 2 1

Car 2, wheel 1

ldld2 3

Car 3 body

3 dl d2 1 1 0

Bolt 0 on wheel 1 on car 1

empty

The pick was outside all cars

The last interpretation assumes that the bolt and wheel don’t occupy the same picking region. A user might well pick both the wheel and the bolt, yielding two hits. If you receive multiple hits, you have to decide which hit to process, perhaps by using the depth values to determine which picked object is closest to the viewpoint. The use of depth values is explored further in the next subsection.

Picking and Depth Values

Example 13-6 demonstrates how to use depth values when picking to determine which object is picked. This program draws three overlapping rectangles in normal rendering mode. When the left mouse button is pressed, the pickRects() routine is called. This routine returns the cursor position, enters selection mode, initializes the name stack, and multiplies the picking matrix with the current orthographic projection matrix. A selection hit occurs for each rectangle the cursor is over when the left mouse button is clicked. Finally, the contents of the selection buffer are examined to identify which named objects were within the picking region near the cursor.

The rectangles in this program are drawn at different depth, or z-, values. Since only one name is used to identify all three rectangles, only one hit can be recorded. However, if more than one rectangle is picked, that single hit has different minimum and maximum z-values.

Example 13-6 Picking with Depth Values: pickdepth.c

Picking with Depth Values: pickdepth.c

 

 

Picking with Depth Values: pickdepth.c

 

 

 

Picking with Depth Values: pickdepth.c

 

 

Picking with Depth Values: pickdepth.c

Try This

•    Modify Example 13-6 to add additional calls to glPushName() so that multiple names are on the stack when the selection hit occurs. What will the contents of the selection buffer be?

•    By default, glDepthRange() sets the mapping of the z-values to [0.0, 1.0]. Try modifying the glDepthRange() values and see how these modifications affect the z-values that are returned in the selection array.

Hints for Writing a Program That Uses Selection

Most programs that allow a user to edit geometric objects interactively provide a mechanism for the user to pick items or groups of items for editing.Often, it’s easy to find bounding boxes for two-dimensional objects and to organize them in some hierarchical data structure to speed up searches. For example, picking that uses the OpenGL style in a VLSI layout program containing millions of rectangles can be relatively slow. However, using simple bounding-box information when rectangles are typically aligned with the screen could make picking in such a program extremely fast. The code is probably simpler to write, too.

As another example, since only geometric objects cause hits, you might want to create your own method for picking text. Setting the current raster position is a geometric operation, but it effectively creates only a single pickable point at the current raster position, which is typically at the lower left corner of the text. If your editor needs to manipulate individual characters within a text string, some other picking mechanism must be used. You could draw little rectangles around each character during picking mode, but it’s almost certainly easier to handle text as a special case.

If you decide to use OpenGL picking, organize your program and its data structures so that it’s easy to draw appropriate lists of objects in either selection or normal drawing mode. This way, when the user picks something, you can use the same data structures for the pick operation that you use to display the items on the screen. Also, consider whether you want to allow the user to select multiple objects. One way to do this is to store a bit for each item, indicating whether it’s selected (this method, however, requires you to traverse your entire list of items to find the selected items). You might find it useful to maintain a list of pointers to selected items to speed up this search. It’s probably a good idea to keep the selection bit for each item as well, because when you’re drawing the entire picture, you might want to draw selected items differently (for example, in a different color or within a selection box). Finally, consider the selection user interface. You might want to allow the user to

• Select an item

• Sweep-select a group of items

• Add an item to the selection

• Add a sweep selection to the current selections

• Delete an item from a selection

• Choose a single item from a group of overlapping items

A typical solution for a two-dimensional drawing program might work as follows:

1. All selection is done by pointing with the mouse cursor and using the left mouse button. In the steps that follow, cursor means the cursor tied to the mouse, and button means the left mouse button.

2. Clicking on an item selects it and deselects all other    currently    selected items. If the cursor is on top of multiple items, the smallest    is    selected.

3.Clicking down where there is no item, holding the button down while dragging the cursor, and then releasing the button selects all the items in a screen-aligned rectangle whose corners are determined by the cursor positions when the button went down and where it came up. This is called a sweep selection. All items not in the swept-out region are deselected. (You must decide whether an item is selected only if it’s completely within the sweep region, or if any part of it falls within the region. The "completely within" strategy usually works best.)

4. If the Shift key is held down and the user clicks on an item that isn’t currently selected, that item is added to the selected list. If the clicked-on item is selected, it’s deleted from the selection list.

5. If a sweep selection is performed with the Shift key pressed, the items swept out are added to the current selection.

6. In an extremely cluttered region, it’s often hard to do a sweep selection. When the button goes down, the cursor might lie on top of some item, and normally that item would be selected. You can make any operation a sweep selection, but a typical user interface interprets a button down on an item plus a mouse motion as a select-plus-drag operation. To solve this problem, you can have an enforced sweep selection by holding down, say, the Alt key. With this procedure, the following set of operations constitutes a sweep selection: Alt button down, sweep, button up. Items under the cursor when the button goes down are ignored.

7. If the Shift key is held during this sweep selection, the items enclosed in the sweep region are added to the current selection.

8. Finally, if the user clicks on multiple items, select just one of them. If the cursor isn’t moved (or maybe not moved more than a pixel), and the user clicks again in the same place, deselect the item originally selected, and select a different item under the cursor. Use repeated clicks at the same point to cycle through all the possibilities.

Different rules can apply in particular situations. In a text editor, you probably don’t have to worry about characters on top of each other, and selections of multiple characters are always contiguous characters in the document. Thus, you need to mark only the first and last selected characters to identify the complete selection. With text, often the best way to handle selection is to identify the positions between characters, rather than the characters themselves. This allows you to have an empty selection when the beginning and end of the selection are between the same pair of characters; it also allows you to put the cursor before the first chaiacter in the document or after the final one with no special-case code.

In a three-dimensional editor, you might provide ways to rotate and zoom between selections, so sophisticated schemes for cycling through the possible selections might be unnecessary. On the other hand, selection in three dimensions is difficult because the cursor’s position on the screen usually gives no indication of its depth.

Next post:

Previous post: