Jump to content United States-English
HP.com Home Products and Services Support and Drivers Solutions How to Buy
» Contact HP
More options
HP.com home
Starbase Display List Programmer's Manual: HP 9000 Series 700 Computers > Chapter 6 Segment Networks

Conditional Execution

» 

Technical documentation

» Feedback
Content starts here

 » Table of Contents

 » Glossary

 » Index

The call_segment and execute_segment routines cause a display list segment to be traversed unconditionally; that is, if the call is there, it will always pass control to the specified segment. While this is definitely useful, there are some cases in which you might want a segment to be traversed only if a certain condition is met. Display List offers this capability in the three routines cond_call_segment, cond_execute_segment and cond_return. As their names imply, the former two traverse a subordinate segment if its condition is met, and the latter returns control to the calling segment if its condition is met.

All the conditional commands supported by Display List have similarities in syntax: in addition to the parameters required by their unconditional versions, there are two other parameters:

cond_index_select

This parameter — the condition index selector — specifies the condition to be tested. There are three available conditions: their numeric indexes are made mnemonic by the tokens CI_FALSE, CI_PRUNE, and CI_CULL. These are explained below.

comp_flag

This parameter — the comparison flag — contains the value with which the specified condition is to be compared. The value of 〈comp_flag〉 can be either FALSE or TRUE.

If the boolean value resulting from the condition specified by 〈cond_index_select〉 is equal to the boolean value specified by 〈comp_flag〉, the appropriate action is taken; if the values are different, no action is taken as a result of that element in the segment, and traversal in the current segment continues at the following element.

What Are the Conditions?

There are three conditions — three valid values for 〈cond_index_select〉 — supported by Display List. One can be used for temporarily making the conditional statement unconditional (sometimes used in debugging), and the other two allow you to make decisions based on conditions in Display List itself ("CI" stands for "condition index"):

CI_FALSE

The CI_FALSE condition can be useful during debugging; you can cause a segment to be always traversed or never traversed, depending on the value you compare with it. In effect, by specifying 〈comp_flag〉 as FALSE, you can temporarily change a conditional traversal into an unconditional traversal; and by specifying 〈comp_flag〉 as TRUE, you can temporarily "remove" a segment-traversing call without actually removing the line from your source code.

CI_PRUNE

Suppose you have a segment in which all its primitives draw their output within a certain volume of Modeling Coordinate (MC) space. Display List allows you to specify such a three-dimensional bounding box and if, during segment traversal, the entire bounding box is outside of the currently active clip limits, the "prune" condition is set. You can test whether this prune condition is set, and, if so, avoid calling the whole segment. Pruning segments off the display list hierarchy can result in a large performance increase.

CI_CULL

Suppose you have a display list that draws a large object that is very elaborately defined, down to the smallest details. These details are necessary when zooming in for a close look, but the level of detail is such that when you are not zoomed in, the fineness of detail in the representation merely slows the rendering time, and the end result is that many objects are drawn so small that you can't see them anyway. Display List addresses this problem by allowing you to specify a certain size (in pixels) that the bounding box must exceed. If the rendered size of the objects in the segment is smaller than the size of the bounding box, the "cull" condition is set. Testing the cull condition allows you, for example, to draw a detailed version of a small object when zoomed in close, but a very simple, quickly rendered version of the object when zoomed out. Changing the representation of objects, based on their ultimate size on the screen, can increase performance significantly.

What are the Actions?

There are three different actions that can be taken conditionally, according to the above conditions. They are:

cond_call_segment

This statement does a call_segment if the associated condition comparison evaluates to TRUE. As with the unconditional call_segment routine, the Starbase state is neither saved before nor restored after traversal of the specified segment.

cond_execute_segment

This statement does an execute_segment if the associated condition comparison evaluates to TRUE. As with the unconditional execute_segment routine, the Starbase state is saved before traversal of the specified segment, and it is restored afterwards.

cond_return

This statement returns control to the calling segment if the associated condition comparison evaluates to TRUE. This is similar to the unconditional set_disp_traversal_control with a value of TRAVERSAL_RETURN.

The syntax for the three routines above are as follows:

  • cond_call_segment(〈fildes〉, 〈cond_index_select〉, 〈comp_flag〉, 〈segno〉);

  • cond_execute_segment(〈fildes〉, 〈cond_index_select〉, 〈comp_flag〉, 〈segno〉);

  • cond_return(〈fildes〉, 〈cond_index_select〉,〈comp_flag〉);

where 〈cond_index_select〉 and 〈comp_flag〉 are as described above, and 〈segno〉 is the segment number to call.

Setting the Conditions

There are two different conditions mentioned above: pruning and culling. The values needed for these conditions are set by the routines set_extent and set_cull_size. These are discussed below.

Setting the Bounding Box

The concept of "setting the bounding box" was referred to above, but no mention was made of just how to do that. The routine that sets the size of the bounding box is set_extent, and its syntax is:

  • set_extent(〈fildes〉, 〈mc_extent〉);

where:

  • mc_extent〉 is a 2×3 array of floating-point numbers. The top row contains the X, Y, and Z minima, respectively, and the bottom row contains the X, Y, and Z maxima. Note that Fortran uses column-major order instead of row-major order when defining its arrays; thus, Fortran users must declare a 3×2 array.

When CI_PRUNE is specified as a condition, the volume described by the 〈mc_extent〉 matrix is checked against the currently active clip limits. If the volume is entirely outside the clip limits, the prune condition is set to TRUE, and execution proceeds accordingly. When CI_CULL is specified as a condition, the 3D bounding box is projected onto the display. The length of the diagonal of the resulting rectangle is checked against the size specified in the set_cull_size routine, and execution continues according to whether the diagonal is larger than the specified cull size or not.

Two examples follow shortly, both of which show the use of the set_extent routine. The first shows it in relation to pruning; the second, to culling.

Setting the Culling Size

When you want your program to do culling, you need to set the size of the bounding box, as above, but you also need to set the size limit against which the diagonal of the projected bounding box is compared. This size limit is set with the set_cull_size routine:

  • set_cull_size(〈fildes〉, 〈cull_size〉);

where:

  • cull_size〉 is a floating-point value that specifies the length, in DCs, of the diagonal of the bounding box when it is projected onto the display. Below this size, the CI_CULL condition is set; above this size, it is not set.

The culling example later in this chapter illustrates the use of both set_extent and set_cull_size.

A Pruning Example

As described above, "pruning" is the ability to determine if the objects drawn by a segment are entirely outside the clip limits and, if so, to circumvent all the segment's Starbase primitives. This typically results in a greater rendering speed. It is sort of a "segment clipping" functionality.

Note that pruning can be implemented in two slightly different ways, with virtually identical results. The method you choose depends on your application and your personal preference. The two methods are:

  • Define the bounding box in the parent segment, and traverse (via cond_call_segment or cond_execute_segment) the child segment only if CI_PRUNE is FALSE.

  • Unconditionally call the child segment. Once there, define the bounding box and, if CI_PRUNE is TRUE, return to the parent segment (via cond_return).

The approach you use would probably depend on whether you want the dimensions of the bounding box to be contained in the parent segment or the child segment.

In the following example program, Display List's pruning capability is illustrated. The program defines sixteen objects, appropriately named TimeSink, whose sole purpose in life is take substantial amounts of time to render. The objects are 1000-vector polylines, contained in unit cubes that are arranged in a circular pattern. The camera aims at the edge of the circle, such that only about three of the cubes are within the clip limits at any one time. When pruning is not done, Starbase must process and clip 16,000 vectors, about 13,000 of which are always invisible — outside the clip limits. When pruning is on, however, those 13,000 vectors, contained in their 13 cubes, are avoided altogether. This results in a substantial reduction in rendering time for each frame.

The camera flies around the ring twice; one revolution with pruning off, and one revolution with pruning on. The increase in rendering speed should be obvious.

Here is the program:

#include <starbase.c.h>                 /* get Starbase definitions */
#include <dl.c.h> /* get Display List definitions */
#include <stdio.h> /* get standard I/O functions */
#include <math.h> /* link with library "-lm" */
#define AppendOff FALSE /* sent to "open_segment" */
#define AppendOn TRUE /* sent to "open_segment" */
#define DisplayOff FALSE /* sent to "open_segment" */
#define WholeRingPrune 100 /* segment no. picked arbitrarily */
#define WholeRingNoPrune 101 /* segment no. picked arbitrarily */
#define DoubleBufferOn TRUE /* sent to "double_buffer" */
#define ZbufferOn TRUE /* sent to "hidden_surface" */
#define CullOff FALSE /* sent to "hidden_surface" */
#define FrontOn TRUE /* sent to "depth_indicator" */
#define BackOn TRUE /* sent to "depth_indicator" */
#define NoFlags FALSE /* sent to "polyline2d" */
#define Max 16 /* the number of "time sinks" */
#define Lines 1000 /* number of lines per "time sink" */
#define ObjRadius 7 /* of circular pattern of objects */
#define CamRadius 9 /* of orbit of camera */
#define SemiSize 0.5 /* used in random number generator */
#define deg *M_PI/180 /* convert degrees to radians */
#define randomno(x) ((rand() % 200 - 100) * 0.01 * x) /* range: -x to +x */

main() /* program "Pruning.c" */
{
int fildes; /* file descriptor */
float TimeSink[Max][Lines][3];/* time-consuming things to draw */
float Xoffset, Zoffset; /* temporary variables */
float MCextent[2][3]; /* for prune-condition testing */
int Buffer = 0; /* for double buffering */
camera_arg Camera; /* for camera model */
int I, J; /* loop control variables */
float Theta; /* loop control variable */

/*- set up Starbase */
if ((fildes = gopen(getenv("SB_OUTDEV"), OUTDEV, NULL,
INIT | THREE_D)) == -1) {
fprintf(stderr, "%s %s\\n", "Error: gopen failed using environment",
"variable SB_OUTDEV.");
exit(-1);
}
vdc_extent(fildes, 0.0, 0.0, 0.0, 1.25, 1.0, 1.0);
double_buffer(fildes, DoubleBufferOn | INIT, 12);
hidden_surface(fildes, ZbufferOn, CullOff);
clear_control(fildes, CLEAR_DISPLAY_SURFACE | CLEAR_ZBUFFER);
depth_indicator(fildes, FrontOn, BackOn);
background_color(fildes, 0.4, 0.4, 0.0);
/*- define the objects to render -*/
for (I = 0; I < Max; I++) {
Xoffset = ObjRadius * cos((I * (360.0 / Max)) deg);
Zoffset = ObjRadius * sin((I * (360.0 / Max)) deg);
for (J = 0; J < Lines; J++) {
TimeSink[I][J][0] = Xoffset + randomno(SemiSize);
TimeSink[I][J][1] = randomno(SemiSize);
TimeSink[I][J][2] = Zoffset + randomno(SemiSize);
}
}
/*- put the objects into low-level segments */
for (I = 0; I < Max; I++) {
open_segment(fildes, I, AppendOff, DisplayOff);
line_color_index(fildes, I);
polyline3d(fildes, &TimeSink[I][0][0], Lines, NoFlags);
close_segment(fildes);
}
/*- define the high-level segments -*/
for (I = 0; I < Max; I++) {
/*- append call of segment "I" to non-pruning segment -*/
open_segment(fildes, WholeRingNoPrune, AppendOn, DisplayOff);
execute_segment(fildes, I);
close_segment(fildes);
/*- append conditional call of segment "I" to pruning segment -*/
Xoffset = ObjRadius * cos((I * (360.0 / Max)) deg);
Zoffset = ObjRadius * sin((I * (360.0 / Max)) deg);
MCextent[0][0] = Xoffset - SemiSize;
MCextent[0][1] = -SemiSize;
MCextent[0][2] = Zoffset - SemiSize;
MCextent[1][0] = Xoffset + SemiSize;
MCextent[1][1] = SemiSize;
MCextent[1][2] = Zoffset + SemiSize;
open_segment(fildes, WholeRingPrune, AppendOn, DisplayOff);
set_extent(fildes, MCextent);
cond_execute_segment(fildes, CI_PRUNE, FALSE, I);
close_segment(fildes);
}
/*- set up the camera and draw the image -*/
Camera.upx = 0.0, Camera.upy = 1.0, Camera.upz = 0.0;
Camera.projection = CAM_PERSPECTIVE;
Camera.field_of_view = 30.0;
Camera.front = -CamRadius; Camera.back = CamRadius;
printf("Pruning not now active.\\n");
for (Theta = 0.0; Theta < 360.0; Theta += 1.0) {
dbuffer_switch(fildes, Buffer = !Buffer);
Camera.refx = ObjRadius * 1.8 * cos((Theta + 90) deg);
Camera.refy = 0.0;
Camera.refz = ObjRadius * 1.8 * sin((Theta + 90) deg);
Camera.camx = CamRadius * cos(Theta deg);
Camera.camy = 1.0;
Camera.camz = CamRadius * sin(Theta deg);
view_camera(fildes, &Camera);
refresh_segment(fildes, WholeRingNoPrune);
}
printf("Pruning now active.\\n");
for (Theta = 0.0; Theta < 360.0; Theta += 1.0) {
dbuffer_switch(fildes, Buffer = !Buffer);
Camera.refx = ObjRadius * 1.8 * cos((Theta + 90) deg);
Camera.refy = 0.0;
Camera.refz = ObjRadius * 1.8 * sin((Theta + 90) deg);
Camera.camx = CamRadius * cos(Theta deg);
Camera.camy = 1.0;
Camera.camz = CamRadius * sin(Theta deg);
view_camera(fildes, &Camera);
refresh_segment(fildes, WholeRingPrune);
}
gclose(fildes);
}

Here is one typical frame during the execution of the program. You can see that the circular arrangement of the line-filled cubes extends off the screen to the left.

Figure 6-1 Pruning Display List Segments

Pruning Display List Segments

Your application's performance may also benefit by using adaptive clipping — where you can temporarily expand the clip limits to encompass the entire VDC extent. See adapt_clip_to_extent(3G) in the Reference section for further information.

A Culling Example

The second method of conditional traversal of display list segments has to do with "culling." This is basically specifying a certain size in pixels, below which, the CI_CULL flag is set. This is typically used as follows: when an object would be rendered smaller than a certain size, a simpler, more quickly executed version is drawn instead. Or, if you prefer, nothing at all is drawn.

In the following example, a telephone keypad is drawn. There are two versions of it:

  • A detailed version, for close-ups. This uses the bold sans serif font (the spline-edged, filled-polygon font) for accuracy of detail.

  • A rougher version, for more distant shots. This uses the simplex sans serif font for speed of rendering.

At the beginning of the program, the keypad is drawn in the detailed version. It gradually recedes from you, and at a certain threshold, the simpler version is rendered in its place. It recedes still further, and then starts coming back. When it again crosses the threshold, the detailed version is once again rendered. The threshold point, set by the routine set_cull_size, is defined to be 500 pixels[16].

Culling, like pruning, is a performance enhancer; when the keypad is being drawn, notice the increase in speed when the simpler version is used.

Here is the program:

#include <starbase.c.h>                 /* get Starbase definitions */
#include <dl.c.h> /* get Display List definitions */
#include <stdio.h> /* get standard I/O functions */
#include <math.h> /* link with library "-lm" */
#define AppendOff FALSE /* sent to "open_segment" */
#define DisplayOff FALSE /* sent to "open_segment" */
#define DetailedKeypad 1 /* for close-ups */
#define RoughKeypad 2 /* for far shots */
#define KeySize 10.0 /* millimeters */
#define KeySemiSize (KeySize / 2)
#define KeyEdge 1.0 /* millimeter */
#define KeyExtent (KeySize + 2 * KeyEdge)
#define KeyGap 3.0 /* millimeters */
#define KeypadWidth (3 * KeyExtent + 2 * KeyGap)
#define KeypadHeight (4 * KeyExtent + 3 * KeyGap)
#define WholeKeypad 3 /* the parent segment */
#define DoubleBufferOn TRUE /* sent to "double_buffer" */
#define BoldSansSerif 6 /* sent to "text_font_index" */
#define SimplexSansSerif 4 /* sent to "text_font_index" */
#define NoEdges FALSE /* sent to "interior_style" */
#define Edges TRUE /* sent to "interior_style" */
#define EndOfLine FALSE /* sent to "text2d" */
#define FrontOn TRUE /* sent to "depth_indicator" */
#define BackOn TRUE /* sent to "depth_indicator" */
#define deg *M_PI/180 /* convert degrees to radians */

int fildes; /* file descriptor */
main() /* program "Culling.c" */
{
static struct Key { /* the text on telephone keypads */
char Letters[4];
char Label[2];
} Keypad[12] = {
" ", "1", "ABC", "2", "DEF", "3", "GHI", "4",
"JKL", "5", "MNO", "6", "PRS", "7", "TUV", "8",
"WXY", "9", " ", "*", " ", "0", " ", "#"};
float MCextent[2][3]; /* for prune-condition testing */
int Buffer = 0; /* for double buffering */
camera_arg Camera; /* for camera model */
int I, Row, Col; /* loop control variables */
float X, Y, Theta; /* loop control variables */ if ((fildes = gopen(getenv("SB_OUTDEV"), OUTDEV, NULL,
INIT | THREE_D)) == -1) {
fprintf(stderr, "%s %s\\n", "Error: gopen failed using environment",
"variable SB_OUTDEV.");
exit(-1);
}
vdc_extent(fildes, 0.0, 0.0, 0.0, 1.25, 1.0, 0.0);
depth_indicator(fildes, FrontOn, BackOn);
double_buffer(fildes, DoubleBufferOn | INIT, 12);
background_color(fildes, 0.4, 0.4, 0.0);
/*- put the object into low-level segments */
open_segment(fildes, DetailedKeypad, AppendOff, DisplayOff);
text_font_index(fildes, BoldSansSerif);
for (I = 0; I < 12; I++) {
Row = I / 3, Col = I % 3;
X = -KeypadWidth / 2 + (KeyExtent + KeyGap) * Col + KeyExtent / 2;
Y = KeypadHeight / 2 - (KeyExtent + KeyGap) * Row - KeyExtent / 2;
DrawDetailedKey(X, Y, Keypad[I].Letters, Keypad[I].Label,
(I == 9 || I == 11));
}
close_segment(fildes);

open_segment(fildes, RoughKeypad, AppendOff, DisplayOff);
text_font_index(fildes, SimplexSansSerif);
for (I = 0; I < 12; I++) {
Row = I / 3, Col = I % 3;
X = -KeypadWidth / 2 + (KeyExtent + KeyGap) * Col + KeyExtent / 2;
Y = KeypadHeight / 2 - (KeyExtent + KeyGap) * Row - KeyExtent / 2;
DrawRoughKey(X, Y, Keypad[I].Letters, Keypad[I].Label,
(I == 9 || I == 11));
}
close_segment(fildes);
/*- define the high-level segment */
MCextent[0][0] = -KeypadWidth / 2;
MCextent[0][1] = -KeypadHeight / 2;
MCextent[0][2] = 0.0;
MCextent[1][0] = KeypadWidth / 2;
MCextent[1][1] = KeypadHeight / 2;
MCextent[1][2] = 0.0;
open_segment(fildes, WholeKeypad, AppendOff, DisplayOff);
set_extent(fildes, MCextent);
set_cull_size(fildes, 500.0);
cond_execute_segment(fildes, CI_CULL, FALSE, DetailedKeypad);
cond_execute_segment(fildes, CI_CULL, TRUE, RoughKeypad);
close_segment(fildes); /*- set up the camera and draw the image -*/
Camera.refx = Camera.refy = Camera.refz = 0.0;
Camera.upx = 0.0, Camera.upy = 1.0, Camera.upz = 0.0;
Camera.field_of_view = 45.0;
Camera.back = 1.0, Camera.front = -1.0;
Camera.projection = CAM_PERSPECTIVE;
for (Theta = 0.0; Theta < 360.0 * 3; Theta += 5.0) {
dbuffer_switch(fildes, Buffer = !Buffer);
Camera.camx = Camera.camy = 0.0;
Camera.camz = -((sin(Theta deg) / 2 + 0.5) * 150.0 + 100.0);
view_camera(fildes, &Camera);
refresh_segment(fildes, WholeKeypad);
}
gclose(fildes);
}
/**********************************************************************/
DrawDetailedKey(X, Y, Letters, Label, Special)
float X, Y; /* location of CENTER of key */
char *Letters; /* the letters above main key label */
char *Label; /* the main label of the key */
int Special; /* a "special" keys ("*" or "#")? */
{
/*- draw the key itself */
interior_style(fildes, INT_HOLLOW, Edges);
rectangle(fildes, X - KeySemiSize, Y + KeySemiSize,
X + KeySemiSize, Y - KeySemiSize);
rectangle(fildes, X - KeySemiSize - KeyEdge, Y + KeySemiSize + KeyEdge,
X + KeySemiSize + KeyEdge, Y - KeySemiSize - KeyEdge);
/*- draw the key labels */
interior_style(fildes, INT_SOLID, NoEdges);
if (Special) {
text_alignment(fildes, TA_CENTER, TA_HALF, 0.0, 0.0);
character_height(fildes, 9.0);
text2d(fildes, X, Y, Label, WORLD_COORDINATE_TEXT, EndOfLine);
}
else {
Y = Y - KeySemiSize + KeySize * 0.6;
text_alignment(fildes, TA_CENTER, TA_BOTTOM, 0.0, 0.0);
character_height(fildes, 4.0);
text2d(fildes, X, Y, Letters, WORLD_COORDINATE_TEXT, EndOfLine);
text_alignment(fildes, TA_CENTER, TA_CAP, 0.0, 0.0);
character_height(fildes, 9.0);
text2d(fildes, X, Y, Label, WORLD_COORDINATE_TEXT, EndOfLine);
}
}/*****************************************************************/
DrawRoughKey(X, Y, Letters, Label, Special)
float X, Y; /* location of CENTER of key */
char *Letters; /* the letters above main key label */
char *Label; /* the main label of the key */
int Special; /* a "special" keys ("*" or "#")? */
{
/*- draw the key itself */
interior_style(fildes, INT_HOLLOW, Edges);
rectangle(fildes, X - KeySemiSize, Y + KeySemiSize,
X + KeySemiSize, Y - KeySemiSize);
rectangle(fildes, X - KeySemiSize - KeyEdge, Y + KeySemiSize + KeyEdge,
X + KeySemiSize + KeyEdge, Y - KeySemiSize - KeyEdge);
/*- draw the key labels */
if (Special) {
text_alignment(fildes, TA_CENTER, TA_HALF, 0.0, 0.0);
character_height(fildes, 9.0);
text2d(fildes, X, Y, Label, WORLD_COORDINATE_TEXT, EndOfLine);
}
else {
Y = Y - KeySemiSize + KeySize * 0.6;
text_alignment(fildes, TA_CENTER, TA_BOTTOM, 0.0, 0.0);
character_height(fildes, 4.0);
text2d(fildes, X, Y, Letters, WORLD_COORDINATE_TEXT, EndOfLine);
text_alignment(fildes, TA_CENTER, TA_CAP, 0.0, 0.0);
character_height(fildes, 9.0);
text2d(fildes, X, Y, Label, WORLD_COORDINATE_TEXT, EndOfLine);
}
}

Here is one typical frame during the execution of the program. The text shown here is the bold sans serif font — the more detailed, and therefore slower, font.

Figure 6-2 Culling Example

Culling Example


[16] In real life, a 500-pixel limit is probably a bit large; perhaps 50 pixels, or even 10, would be better for your application. This example uses 500 pixels so you can easily see when the representation changes.

Printable version
Privacy statement Using this site means you accept its terms Feedback to webmaster
© 1995 Hewlett-Packard Development Company, L.P.