/* light_tool.c 1.1 */ /* Copyright 1992, 1993 O'Reilly and Associates, Inc. Permission to use, copy, and modify this program is hereby granted, as long as this copyright notice appears in each copy of the program source code. */ /* Use this program to explore PEX rendering. There are three objects, * each centered at the center of the unit cube, (0.5, 0.5, 0.5). Four * light sources are defined, one of each type: ambient, directional, * positional, and spot. The direction of the directional light is * <-1, 0, 0>. The positional light is directly above the objects, at * position (0.5, 2.0, 0.5). The spot light is at the same position; its * direction is <0, -1, 0>. All other light parameters can be set * interactively, as can the reflectance properties. To see the effect * of modifying any one light's parameters, you usually need to turn all * the other lights off, except maybe the ambient light. * * Moving the cursor while holding the left mouse button pans * around the object (view orientation is changed). Pressing the * middle mouse button zooms in; pressing that button while also * pressing the shift key zooms out. * * This program is very limited. Feel free to modify it to suit your * taste. Please send bugs or suggestions to bookquestions@ora.com. * * The raw PEX-SI from the X Consortium does not peform lighting, shading, * and some other rendering operations, such as HLHSR. This program, * then, will not be very helpful when used with that code. * * This program uses the Motif toolkit. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "book_utils.h" /* PHIGS Identifiers */ #define WS_ID (int) 1 #define OBJECT_LABEL (long) 1 #define LIGHT_STATE_LABEL (long) 2 #define SHADING_METHOD_LABEL (long) 3 #define REFL_PROPS_LABEL (long) 4 #define REFL_EQN_LABEL (long) 5 #define INTERIOR_STYLE_LABEL (long) 6 /* Window layout information */ #define WS_WIDTH 400 #define WS_HEIGHT 400 /* Light source parameters */ #define RED_COORD 1 #define GREEN_COORD 2 #define BLUE_COORD 3 #define ATTENUATION_C1 4 #define ATTENUATION_C2 5 #define SPREAD_ANGLE 6 #define CONCENTRATION 7 #define AMBIENT_COEF 8 #define DIFFUSE_COEF 9 #define SPECULAR_COEF 10 #define SPECULAR_EXP 11 /* Light types */ #define AMBIENT 0 #define DIRECTIONAL 1 #define POSITIONAL 2 #define SPOT 3 static Display *dpy; static Window window; static PEXRenderer renderer; static PEXRendererAttributes rattrs; static PEXStructure top_struct, cube_quad, cube, cylinder, sphere; static PEXStructure cur_object; static Atom delete_window_atom; static Widget top, canvas, object_menu, lights_menu, shading_menu, refl_menu, depth_cue_menu, hlhsr_menu, cur_light_menu, light_red, light_blue, light_green, refl_panel, sep_1, scale_C1, scale_C2, sep_2, scale_spread, scale_conc, scale_Ka, scale_Kd, scale_Ks, scale_Sr, scale_Sg, scale_Sb, scale_Se; static PEXTableIndex light_switches[4] = {1,1,1,1}; static PEXLightEntry lights[4]; static PEXDepthCueEntry depth_cue_rep; static PEXReflectionAttributes refl_props; static int cur_light = SPOT; static int cur_shading = PEXSurfaceInterpColor; static int cur_refl = PEXReflectionSpecular; static int cur_hlhsr_mode = PEXHLHSRZBuffer; static int cur_interior_style = PEXInteriorStyleSolid; /* The viewing parameters. */ static PEXCoord view_ref_pt = {0.5,0.5,0.5}; static PEXVector view_up_vec = {0,1,0}; static PEXVector view_plane_normal = {1,1,1}; static PEXCoord2D view_window[2]; static PEXNPCSubVolume viewport; static double view_plane, front_plane, back_plane; static PEXCoord prp; static int perspective; static PEXViewEntry view; static void initialize_light_table(), initialize_depth_cue_table(), initialize_view(), build_cube(), build_cylinder(), build_sphere(); /* Callback function to quit the program. */ static void quit( w, client_data, call_data ) Widget w; caddr_t client_data; XmAnyCallbackStruct *call_data; { XClientMessageEvent *cm = &call_data->event->xclient; if ( cm->data.l[0] == delete_window_atom ) { XtDestroyWidget( top ); exit( 0 ); } } static void redraw() { /* Redraw the window, after first erasing (clearing) it. */ XClearWindow( dpy, window ); PEXRenderNetwork( dpy, window, renderer, top_struct ); XFlush( dpy ); } /* Callback for Expose events. */ static void expose( w, client_data, event ) Widget w; XtPointer client_data; XEvent *event; { /* Redraw when last Expose event received. */ if ( event->xexpose.count == 0 ) redraw(); } static void set_object( widget, choice, cbs ) Widget widget; int choice; { PEXStructure object; switch ( choice ) { case 0: object = cube; break; case 1: object = cylinder; break; case 2: object = sphere; break; } if ( object != cur_object ) { cur_object = object; PEXSetEditingMode( dpy, top_struct, PEXStructureReplace ); PEXSetElementPtr( dpy, top_struct, PEXBeginning, 0 ); PEXSetElementPtrAtLabel( dpy, top_struct, OBJECT_LABEL, 1 ); PEXExecuteStructure( dpy, top_struct, PEXOCStore, cur_object ); redraw(); } } static void set_interior_style( widget, choice, cbs ) Widget widget; int choice; { int interior_style; switch ( choice ) { case 0: interior_style = PEXInteriorStyleHollow; break; case 1: interior_style = PEXInteriorStyleSolid; break; case 2: interior_style = PEXInteriorStylePattern; break; case 3: interior_style = PEXInteriorStyleHatch; break; case 4: interior_style = PEXInteriorStyleEmpty; break; } if ( interior_style != cur_interior_style ) { cur_interior_style = interior_style; PEXSetEditingMode( dpy, top_struct, PEXStructureReplace ); PEXSetElementPtr( dpy, top_struct, PEXBeginning, 0 ); PEXSetElementPtrAtLabel( dpy, top_struct, INTERIOR_STYLE_LABEL, 1 ); PEXSetInteriorStyle( dpy, top_struct, PEXOCStore, cur_interior_style ); PEXSetElementPtr( dpy, top_struct, PEXCurrent, 1 ); PEXSetBFInteriorStyle( dpy, top_struct, PEXOCStore, cur_interior_style ); redraw(); } } static void set_lights( widget, choice ) Widget widget; int choice; { int i, on_off, state, num_on; PEXTableIndex light_sw[4]; Widget button; if ( choice == 0 || choice == 5 ) { /* Set all lights. */ if ( choice == 0 ) on_off = 1, state = True; else on_off = 0, state = False; for ( i = 0; i < 4 ; i++ ) light_switches[i] = on_off; if ( button = XtNameToWidget( lights_menu, "button_1" ) ) XtVaSetValues( button, XmNset, state, NULL ); if ( button = XtNameToWidget( lights_menu, "button_2" ) ) XtVaSetValues( button, XmNset, state, NULL ); if ( button = XtNameToWidget( lights_menu, "button_3" ) ) XtVaSetValues( button, XmNset, state, NULL ); if ( button = XtNameToWidget( lights_menu, "button_4" ) ) XtVaSetValues( button, XmNset, state, NULL ); } else { light_switches[choice - 1] = XmToggleButtonGetState( widget ); } num_on = 0; for ( i = 0; i < 4; i++ ) { if ( light_switches[i] ) light_sw[num_on++] = i + 1; } PEXSetEditingMode( dpy, top_struct, PEXStructureReplace ); PEXSetElementPtr( dpy, top_struct, PEXBeginning, 0 ); PEXSetElementPtrAtLabel( dpy, top_struct, LIGHT_STATE_LABEL, 1 ); PEXSetLightSourceState( dpy, top_struct, PEXOCStore, num_on, light_sw, 0, (PEXTableIndex *)NULL ); redraw(); } static void set_shading( widget, choice ) Widget widget; int choice; { int shading_method; switch ( choice ) { case 0: shading_method = PEXSurfaceInterpNone; break; case 1: shading_method = PEXSurfaceInterpColor; break; case 2: shading_method = PEXSurfaceInterpDotProduct; break; case 3: shading_method = PEXSurfaceInterpNormal; break; } if ( shading_method != cur_shading ) { cur_shading = shading_method; PEXSetEditingMode( dpy, top_struct, PEXStructureReplace ); PEXSetElementPtr( dpy, top_struct, PEXBeginning, 0 ); PEXSetElementPtrAtLabel( dpy, top_struct, SHADING_METHOD_LABEL, 1 ); PEXSetSurfaceInterpMethod( dpy, top_struct, PEXOCStore, cur_shading ); PEXSetElementPtr( dpy, top_struct, PEXCurrent, 1 ); PEXSetBFSurfaceInterpMethod( dpy, top_struct, PEXOCStore, cur_shading ); redraw(); } } static void set_reflectance( widget, choice ) Widget widget; int choice; { int refl; XSync( XtDisplay(top), False ); switch ( choice ) { case 0: refl = PEXReflectionNone; break; case 1: refl = PEXReflectionAmbient; break; case 2: refl = PEXReflectionDiffuse; break; case 3: refl = PEXReflectionSpecular; break; } if ( refl != cur_refl ) { cur_refl = refl; PEXSetEditingMode( dpy, top_struct, PEXStructureReplace ); PEXSetElementPtr( dpy, top_struct, PEXBeginning, 0 ); PEXSetElementPtrAtLabel( dpy, top_struct, REFL_EQN_LABEL, 1 ); PEXSetReflectionModel( dpy, top_struct, PEXOCStore, cur_refl ); PEXSetElementPtr( dpy, top_struct, PEXCurrent, 1 ); PEXSetBFReflectionModel( dpy, top_struct, PEXOCStore, cur_refl ); redraw(); } } static void set_depth_cue( widget, choice ) Widget widget; int choice; { PEXSwitch mode; mode = choice == 1 ? PEXOn : PEXOff; if ( mode != depth_cue_rep.mode ) { depth_cue_rep.mode = mode; PEXSetTableEntries( dpy, rattrs.depth_cue_table, 1, 1, PEXLUTDepthCue, &depth_cue_rep ); redraw(); } } static void set_hlhsr( widget, choice ) Widget widget; int choice; { int mode; mode = choice == 1 ? PEXHLHSRZBuffer : PEXHLHSROff; if ( mode != cur_hlhsr_mode ) { cur_hlhsr_mode = mode; rattrs.hlhsr_mode = mode; PEXChangeRenderer( dpy, renderer, PEXRAHLHSRMode, &rattrs ); redraw(); } } static void set_light_source( widget, parameter, cbs ) Widget widget; caddr_t parameter; XmScaleCallbackStruct *cbs; { PEXColor *color; float *atten1, *atten2; int set_light = 1; switch ( cur_light ) { case AMBIENT: color = &lights[AMBIENT].color.value; break; case DIRECTIONAL: color = &lights[DIRECTIONAL].color.value; break; case POSITIONAL: color = &lights[POSITIONAL].color.value; atten1 = &lights[POSITIONAL].attenuation1; atten2 = &lights[POSITIONAL].attenuation2; break; case SPOT: color = &lights[SPOT].color.value; atten1 = &lights[SPOT].attenuation1; atten2 = &lights[SPOT].attenuation2; break; } switch ( (int)parameter ) { case RED_COORD: color->rgb.red = 0.1 * cbs->value; break; case GREEN_COORD: color->rgb.green = 0.1 * cbs->value; break; case BLUE_COORD: color->rgb.blue = 0.1 * cbs->value; break; case ATTENUATION_C1: if ( cur_light == POSITIONAL || cur_light == SPOT ) *atten1 = 0.1 * cbs->value; else set_light = 0; break; case ATTENUATION_C2: if ( cur_light == POSITIONAL || cur_light == SPOT ) *atten2 = 0.1 * cbs->value; else set_light = 0; break; case SPREAD_ANGLE: if ( cur_light == SPOT ) lights[SPOT].spread_angle = cbs->value * M_PI/180; else set_light = 0; break; case CONCENTRATION: if ( cur_light == SPOT ) lights[SPOT].concentration = cbs->value; else set_light = 0; break; } if ( set_light ) { PEXSetTableEntries( dpy, rattrs.light_table, cur_light + 1, 1, PEXLUTLight, &lights[cur_light] ); redraw(); } } static void set_refl_props( widget, parameter, cbs ) Widget widget; caddr_t parameter; XmScaleCallbackStruct *cbs; { switch ( (int)parameter ) { case RED_COORD: refl_props.specular_color.value.rgb.red = 0.1 * cbs->value; break; case GREEN_COORD: refl_props.specular_color.value.rgb.green = 0.1 * cbs->value; break; case BLUE_COORD: refl_props.specular_color.value.rgb.blue = 0.1 * cbs->value; break; case AMBIENT_COEF: refl_props.ambient = 0.1 * cbs->value; break; case DIFFUSE_COEF: refl_props.diffuse = 0.1 * cbs->value; break; case SPECULAR_COEF: refl_props.specular = 0.1 * cbs->value; break; case SPECULAR_EXP: refl_props.specular_conc = cbs->value; break; } PEXSetEditingMode( dpy, top_struct, PEXStructureReplace ); PEXSetElementPtr( dpy, top_struct, PEXBeginning, 0 ); PEXSetElementPtrAtLabel( dpy, top_struct, REFL_PROPS_LABEL, 1 ); PEXSetReflectionAttributes( dpy, top_struct, PEXOCStore, &refl_props ); PEXSetElementPtr( dpy, top_struct, PEXCurrent, 1 ); PEXSetBFReflectionAttributes( dpy, top_struct, PEXOCStore, &refl_props ); redraw(); } static void select_cur_light( widget, choice ) Widget widget; int choice; { PEXColor *color; switch ( choice ) { case 0: cur_light = AMBIENT; XtUnmanageChild( scale_C1 ); XtUnmanageChild( scale_C2 ); XtUnmanageChild( sep_2 ); XtUnmanageChild( scale_spread ); XtUnmanageChild( scale_conc ); color = &lights[AMBIENT].color.value; break; case 1: cur_light = DIRECTIONAL; XtUnmanageChild( scale_C1 ); XtUnmanageChild( scale_C2 ); XtUnmanageChild( sep_2 ); XtUnmanageChild( scale_spread ); XtUnmanageChild( scale_conc ); color = &lights[DIRECTIONAL].color.value; break; case 2: cur_light = POSITIONAL; XmScaleSetValue( scale_C1, (int) (10 * lights[POSITIONAL].attenuation1) ); XmScaleSetValue( scale_C2, (int) (10 * lights[POSITIONAL].attenuation2) ); color = &lights[POSITIONAL].color.value; XtManageChild( scale_C1 ); XtManageChild( scale_C2 ); XtManageChild( sep_2 ); XtUnmanageChild( scale_spread ); XtUnmanageChild( scale_conc ); break; case 3: cur_light = SPOT; XmScaleSetValue( scale_C1, (int) (10 * lights[SPOT].attenuation1) ); XmScaleSetValue( scale_C2, (int) (10 * lights[SPOT].attenuation2) ); XmScaleSetValue( scale_spread, (int) ((180/M_PI) * lights[SPOT].spread_angle) ); XmScaleSetValue( scale_conc, (int)lights[SPOT].concentration ); color = &lights[SPOT].color.value; XtManageChild( scale_C1 ); XtManageChild( scale_C2 ); XtManageChild( sep_2 ); XtManageChild( scale_spread ); XtManageChild( scale_conc ); break; } /* Have the sliders show the current light color. */ XmScaleSetValue( light_red, (int)(10 * color->rgb.red) ); XmScaleSetValue( light_green, (int)(10 * color->rgb.green) ); XmScaleSetValue( light_blue, (int)(10 * color->rgb.blue) ); } main( argc, argv ) int argc; char *argv[]; { XtAppContext app; PEXColor color; static PEXTableIndex list[4] = {1, 2, 3, 4}; static void create_appl_windows(); /* Initialize the toolkit. */ top = XtVaAppInitialize( &app, "Light Tool", NULL, 0, &argc, argv, NULL, NULL ); create_appl_windows(); build_cube(); build_cylinder(); build_sphere(); top_struct = PEXCreateStructure( dpy ); PEXSetViewIndex( dpy, top_struct, PEXOCStore, 1 ); PEXSetFacetDistinguishFlag( dpy, top_struct, PEXOCStore, True ); PEXLabel( dpy, top_struct, PEXOCStore, INTERIOR_STYLE_LABEL ); PEXSetInteriorStyle( dpy, top_struct, PEXOCStore, cur_interior_style ); PEXSetBFInteriorStyle( dpy, top_struct, PEXOCStore, cur_interior_style ); SET_COLOR( 1, 0.2, 0.5, color ); PEXSetSurfaceColor( dpy, top_struct, PEXOCStore, PEXColorTypeRGB, &color ); SET_COLOR( 0.5, 0.5, 0.5, color ); PEXSetBFSurfaceColor( dpy, top_struct, PEXOCStore, PEXColorTypeRGB, &color ); PEXLabel( dpy, top_struct, PEXOCStore, LIGHT_STATE_LABEL ); PEXSetLightSourceState( dpy, top_struct, PEXOCStore, 4, list, 0, (PEXTableIndex *)NULL ); PEXLabel( dpy, top_struct, PEXOCStore, REFL_EQN_LABEL ); PEXSetReflectionModel( dpy, top_struct, PEXOCStore, cur_refl ); PEXSetBFReflectionModel( dpy, top_struct, PEXOCStore, cur_refl ); refl_props.ambient = 0.4; refl_props.diffuse = 0.4; refl_props.specular = 0.5; refl_props.specular_conc = 3.0; refl_props.transmission = 0; SET_COLOR( 1, 1, 1, refl_props.specular_color.value ); PEXLabel( dpy, top_struct, PEXOCStore, REFL_PROPS_LABEL ); PEXSetReflectionAttributes( dpy, top_struct, PEXOCStore, &refl_props ); PEXSetBFReflectionAttributes( dpy, top_struct, PEXOCStore, &refl_props ); PEXLabel( dpy, top_struct, PEXOCStore, SHADING_METHOD_LABEL ); PEXSetSurfaceInterpMethod( dpy, top_struct, PEXOCStore, cur_shading ); PEXSetBFSurfaceInterpMethod( dpy, top_struct, PEXOCStore, cur_shading ); PEXSetDepthCueIndex( dpy, top_struct, PEXOCStore, 1 ); PEXLabel( dpy, top_struct, PEXOCStore, OBJECT_LABEL ); cur_object = cube; PEXExecuteStructure( dpy, top_struct, PEXOCStore, cur_object ); /* Initialize view 1. */ initialize_view(); /* Initialize light table and depth cueing table. */ initialize_light_table(); initialize_depth_cue_table(); /* Set the renderer's HLHSR mode. */ rattrs.hlhsr_mode = PEXHLHSRZBuffer; PEXChangeRenderer( dpy, renderer, PEXRAHLHSRMode, &rattrs ); XtAppMainLoop( app ); } static void initialize_light_table() { lights[AMBIENT].type = PEXLightAmbient; SET_COLOR( 1, 1, 1, lights[AMBIENT].color.value ); PEXSetTableEntries( dpy, rattrs.light_table, 1, 1, PEXLUTLight, &lights[AMBIENT] ); lights[DIRECTIONAL].type = PEXLightWCVector; lights[DIRECTIONAL].direction.x = -1; lights[DIRECTIONAL].direction.y = 0; lights[DIRECTIONAL].direction.z = 0; SET_COLOR( 1, 1, 1, lights[DIRECTIONAL].color.value ); PEXSetTableEntries( dpy, rattrs.light_table, 2, 1, PEXLUTLight, &lights[DIRECTIONAL] ); lights[POSITIONAL].type = PEXLightWCPoint; lights[POSITIONAL].point.x = 0.5; lights[POSITIONAL].point.y = 2.0; lights[POSITIONAL].point.z = 0.5; lights[POSITIONAL].attenuation1 = 0; lights[POSITIONAL].attenuation2 = 1; SET_COLOR( 1, 1, 1, lights[POSITIONAL].color.value ); PEXSetTableEntries( dpy, rattrs.light_table, 3, 1, PEXLUTLight, &lights[POSITIONAL] ); lights[SPOT].type = PEXLightWCSpot; lights[SPOT].point.x = 0.5; lights[SPOT].point.y = 2.0; lights[SPOT].point.z = 0.5; lights[SPOT].direction.x = 0; lights[SPOT].direction.y = -1; lights[SPOT].direction.z = 0; lights[SPOT].attenuation1 = 0; lights[SPOT].attenuation2 = 1; lights[SPOT].concentration = 2; lights[SPOT].spread_angle = M_PI/2; SET_COLOR( 1, 1, 1, lights[SPOT].color.value ); PEXSetTableEntries( dpy, rattrs.light_table, 4, 1, PEXLUTLight, &lights[SPOT] ); } static void initialize_depth_cue_table() { depth_cue_rep.mode = PEXOff; depth_cue_rep.front_plane = 1; depth_cue_rep.back_plane = 0; depth_cue_rep.front_scaling = 1; depth_cue_rep.back_scaling = 0.5; SET_COLOR( 0, 0, 0, depth_cue_rep.color.value ); PEXSetTableEntries( dpy, rattrs.depth_cue_table, 1, 1, PEXLUTDepthCue, &depth_cue_rep ); } static void initialize_view() { int error; /* Compute the view orientation matrix. */ error = PEXViewOrientationMatrix( &view_ref_pt, &view_plane_normal, &view_up_vec, view.orientation ); /* Set the view mapping parameters. */ perspective = True; prp.x = 0; prp.y = 0; prp.z = 10.0; view_window[0].x = -1.5; view_window[1].x = 1.5; view_window[0].y = -1.5; view_window[1].y = 1.5; back_plane = -1.5; front_plane = 1.5; view_plane = 0.0; viewport.min.x = 0.0; viewport.max.x = 1.0; viewport.min.y = 0.0; viewport.max.y = 1.0; viewport.min.z = 0.0; viewport.max.z = 1.0; error = PEXViewMappingMatrix( view_window, &viewport, perspective, &prp, view_plane, back_plane, front_plane, view.mapping ); /* Set the view representation. */ view.clip_flags = PEXClippingAll; view.clip_limits = viewport; PEXSetTableEntries( dpy, rattrs.view_table, 1, 1, PEXLUTView, &view ); } #define PAN_SCALE (M_PI/500) #define PAN_OVER_SCALE 2.0/400 static void pan( event ) XEvent *event; { int i, j, err; PEXCoord movement, shift; static int lastx, lasty; static double theta, phi, length; static PEXMatrix inv_ori; /* so that it's init'ed to 0 */ switch ( event->type ) { case ButtonPress: /* Initialize. */ lastx = event->xbutton.x; lasty = event->xbutton.y; /* Compute the current view plane normal angles. */ theta = atan2( view_plane_normal.x, view_plane_normal.z ); length = hypot(view_plane_normal.x, view_plane_normal.z); phi = atan2( view_plane_normal.y, length ); break; case MotionNotify: if ( event->xbutton.state & ShiftMask ) { /* Pan over the view plane. */ inv_ori[3][3] = 1; for ( i = 0; i < 3; i++ ) for ( j = 0; j < 3; j++ ) inv_ori[i][j] = view.orientation[j][i]; movement.x = PAN_OVER_SCALE * (event->xmotion.x - lastx); movement.y = PAN_OVER_SCALE * (lasty - event->xmotion.y); movement.z = 0; PEXTransformPoints( inv_ori, 1, &movement, &shift ); view_ref_pt.x += shift.x; view_ref_pt.y += shift.y; view_ref_pt.z += shift.z; } else { /* Pan around by moving the view plane normal. */ /* Increment the longitude and latitude. */ theta += PAN_SCALE * (event->xmotion.x - lastx); phi += PAN_SCALE * (lasty - event->xmotion.y); /* Compute the new view plane normal. */ view_plane_normal.x = cos( phi ) * sin( theta ); view_plane_normal.y = sin( phi ); view_plane_normal.z = cos( phi ) * cos( theta ); } /* Compute the new view orientation transform. */ err = PEXViewOrientationMatrix( &view_ref_pt, &view_plane_normal, &view_up_vec, view.orientation ); /* Set the view if orientation is okay. */ if ( !err ) { PEXSetTableEntries( dpy, rattrs.view_table, 1, 1, PEXLUTView, &view ); lastx = event->xmotion.x; lasty = event->xmotion.y; /* Update the display. */ redraw(); } break; } } #define ZOOM_FACTOR 0.1 static void zoom( event ) XEvent *event; { int err; float old_width, old_height, zoom_factor; /* Compute the new view window. */ old_width = view_window[1].x - view_window[0].x; old_height = view_window[1].y - view_window[0].y; /* Divide the change evenly between sides. */ zoom_factor = 0.5 * ZOOM_FACTOR; if ( event->xbutton.state & ShiftMask ) { /* Zoom out. */ view_window[0].x -= zoom_factor * old_width; view_window[1].x += zoom_factor * old_width; view_window[0].y -= zoom_factor * old_height; view_window[1].y += zoom_factor * old_height; } else { /* Zoom in. */ view_window[0].x += zoom_factor * old_width; view_window[1].x -= zoom_factor * old_width; view_window[0].y += zoom_factor * old_height; view_window[1].y -= zoom_factor * old_height; } /* Calculate the new mapping matrix. */ err = PEXViewMappingMatrix( view_window, &viewport, False, &prp, view_plane, back_plane, front_plane, view.mapping ); /* Set the view if mapping is okay. */ if ( !err ) { PEXSetTableEntries( dpy, rattrs.view_table, 1, 1, PEXLUTView, &view ); redraw(); } } static void handle_input( w, client_data, event ) Widget w; caddr_t client_data; XEvent *event; { switch ( event->type ) { case ButtonPress: switch ( event->xbutton.button ) { case Button1: pan( event ); break; case Button2: zoom( event ); break; } break; case MotionNotify: pan( event ); break; } } #define FREE_STRINGS( _s, _n ) \ {int i = (_n); while (i--) XmStringFree( (_s)[i] );} static void create_appl_windows() { Widget form, bar, button, panel, scale, sep, refl_label, frame; XmString strings[10]; XVisualInfo vis_info; XStandardColormap cmap_info; PEXColorApproxEntry capx_info; XSetWindowAttributes wattrs; XtAppContext app; PEXExtensionInfo *pexinfo; char err[80]; /* See what display is being used. */ dpy = XtDisplay( top ); /* See if a PEX extension exists and initialize PEXlib. */ if ( PEXInitialize( dpy, &pexinfo, 80, err ) != 0 ) { fprintf( stderr, "No PEX extension.\n" ); fprintf( stderr, "%s\n", err ); exit(1); } /* Determine the best visual to use. */ ora_find_best_visual( dpy, &vis_info ); /* Get a standard colormap for the visual. */ if ( !ora_get_standard_colormap( dpy, &vis_info, &cmap_info ) ) { fprintf ( stderr, "Cannot find a standard colormap\n" ); exit(1); } ora_set_stdcmap_approx( &vis_info, &cmap_info, &capx_info ); /* Set the visual, depth, and colormap for the shell. */ XtVaSetValues( top, XtNvisual, vis_info.visual, XtNdepth, vis_info.depth, XtNcolormap, cmap_info.colormap, NULL ); /* Create the widgets. */ form = XtVaCreateManagedWidget( NULL, xmFormWidgetClass, top, NULL ); strings[0] = XmStringCreateSimple( "Object" ); strings[1] = XmStringCreateSimple( "Style" ); strings[2] = XmStringCreateSimple( "Lights" ); strings[3] = XmStringCreateSimple( "Shading" ); strings[4] = XmStringCreateSimple( "Reflectance" ); strings[5] = XmStringCreateSimple( "Depth-cue" ); strings[6] = XmStringCreateSimple( "Hlhsr" ); bar = XmVaCreateSimpleMenuBar( form, "menubar", XmVaCASCADEBUTTON, strings[0], 0, XmVaCASCADEBUTTON, strings[1], 0, XmVaCASCADEBUTTON, strings[2], 0, XmVaCASCADEBUTTON, strings[3], 0, XmVaCASCADEBUTTON, strings[4], 0, XmVaCASCADEBUTTON, strings[5], 0, XmVaCASCADEBUTTON, strings[6], 0, XtNwidth, WS_WIDTH, XmNrightAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, NULL ); FREE_STRINGS( strings, 7 ) strings[0] = XmStringCreateSimple( "cube" ); strings[1] = XmStringCreateSimple( "cylinder" ); strings[2] = XmStringCreateSimple( "sphere" ); object_menu = XmVaCreateSimplePulldownMenu( bar, "object_menu", 0, set_object, XmVaRADIOBUTTON, strings[0], 0, NULL, NULL, XmVaRADIOBUTTON, strings[1], 0, NULL, NULL, XmVaRADIOBUTTON, strings[2], 0, NULL, NULL, XmNradioBehavior, True, XmNradioAlwaysOne, True, NULL ); FREE_STRINGS( strings, 3 ) if ( button = XtNameToWidget( object_menu, "button_0" ) ) XtVaSetValues( button, XmNset, True, NULL ); strings[0] = XmStringCreateSimple( "hollow" ); strings[1] = XmStringCreateSimple( "solid" ); strings[2] = XmStringCreateSimple( "pattern" ); strings[3] = XmStringCreateSimple( "hatch" ); strings[4] = XmStringCreateSimple( "empty" ); object_menu = XmVaCreateSimplePulldownMenu( bar, "style_menu", 1, set_interior_style, XmVaRADIOBUTTON, strings[0], 0, NULL, NULL, XmVaRADIOBUTTON, strings[1], 0, NULL, NULL, XmVaRADIOBUTTON, strings[2], 0, NULL, NULL, XmVaRADIOBUTTON, strings[3], 0, NULL, NULL, XmVaRADIOBUTTON, strings[4], 0, NULL, NULL, XmNradioBehavior, True, XmNradioAlwaysOne, True, NULL ); FREE_STRINGS( strings, 5 ) if ( button = XtNameToWidget( object_menu, "button_1" ) ) XtVaSetValues( button, XmNset, True, NULL ); strings[0] = XmStringCreateSimple( "all on" ); strings[1] = XmStringCreateSimple( "ambient" ); strings[2] = XmStringCreateSimple( "directional" ); strings[3] = XmStringCreateSimple( "positional" ); strings[4] = XmStringCreateSimple( "spot" ); strings[5] = XmStringCreateSimple( "all off" ); lights_menu = XmVaCreateSimplePulldownMenu( bar, "lights_menu", 2, set_lights, XmVaPUSHBUTTON, strings[0], 0, NULL, NULL, XmVaSEPARATOR, XmVaCHECKBUTTON, strings[1], 0, NULL, NULL, XmVaCHECKBUTTON, strings[2], 0, NULL, NULL, XmVaCHECKBUTTON, strings[3], 0, NULL, NULL, XmVaCHECKBUTTON, strings[4], 0, NULL, NULL, XmVaSEPARATOR, XmVaPUSHBUTTON, strings[5], 0, NULL, NULL, NULL ); FREE_STRINGS( strings, 6 ) if ( button = XtNameToWidget( lights_menu, "button_1" ) ) XtVaSetValues( button, XmNset, True, NULL ); if ( button = XtNameToWidget( lights_menu, "button_2" ) ) XtVaSetValues( button, XmNset, True, NULL ); if ( button = XtNameToWidget( lights_menu, "button_3" ) ) XtVaSetValues( button, XmNset, True, NULL ); if ( button = XtNameToWidget( lights_menu, "button_4" ) ) XtVaSetValues( button, XmNset, True, NULL ); strings[0] = XmStringCreateSimple( "none" ); strings[1] = XmStringCreateSimple( "color" ); strings[2] = XmStringCreateSimple( "dot" ); strings[3] = XmStringCreateSimple( "normal" ); shading_menu = XmVaCreateSimplePulldownMenu( bar, "shading_menu", 3, set_shading, XmVaRADIOBUTTON, strings[0], 0, NULL, NULL, XmVaRADIOBUTTON, strings[1], 0, NULL, NULL, XmVaRADIOBUTTON, strings[2], 0, NULL, NULL, XmVaRADIOBUTTON, strings[3], 0, NULL, NULL, XmNradioBehavior, True, XmNradioAlwaysOne, True, NULL ); FREE_STRINGS( strings, 4 ) if ( button = XtNameToWidget( shading_menu, "button_1" ) ) XtVaSetValues( button, XmNset, True, NULL ); strings[0] = XmStringCreateSimple( "none" ); strings[1] = XmStringCreateSimple( "ambient" ); strings[2] = XmStringCreateSimple( "amb-diff" ); strings[3] = XmStringCreateSimple( "amb-diff-spec" ); refl_menu = XmVaCreateSimplePulldownMenu( bar, "reflectance_menu", 4, set_reflectance, XmVaRADIOBUTTON, strings[0], 0, NULL, NULL, XmVaRADIOBUTTON, strings[1], 0, NULL, NULL, XmVaRADIOBUTTON, strings[2], 0, NULL, NULL, XmVaRADIOBUTTON, strings[3], 0, NULL, NULL, XmNradioBehavior, True, XmNradioAlwaysOne, True, NULL ); FREE_STRINGS( strings, 4 ) if ( button = XtNameToWidget( refl_menu, "button_3" ) ) XtVaSetValues( button, XmNset, True, NULL ); strings[0] = XmStringCreateSimple( "suppressed" ); strings[1] = XmStringCreateSimple( "allowed" ); depth_cue_menu = XmVaCreateSimplePulldownMenu( bar, "depth_cue_menu", 5, set_depth_cue, XmVaRADIOBUTTON, strings[0], 0, NULL, NULL, XmVaRADIOBUTTON, strings[1], 0, NULL, NULL, XmNradioBehavior, True, XmNradioAlwaysOne, True, NULL ); FREE_STRINGS( strings, 2 ) if ( button = XtNameToWidget( depth_cue_menu, "button_0" ) ) XtVaSetValues( button, XmNset, True, NULL ); strings[0] = XmStringCreateSimple( "off" ); strings[1] = XmStringCreateSimple( "on" ); hlhsr_menu = XmVaCreateSimplePulldownMenu( bar, "hlhsr_menu", 6, set_hlhsr, XmVaRADIOBUTTON, strings[0], 0, NULL, NULL, XmVaRADIOBUTTON, strings[1], 0, NULL, NULL, XmNradioBehavior, True, XmNradioAlwaysOne, True, NULL ); FREE_STRINGS( strings, 2 ) if ( button = XtNameToWidget( hlhsr_menu, "button_1" ) ) XtVaSetValues( button, XmNset, True, NULL ); canvas = XtVaCreateManagedWidget( "canvas", xmDrawingAreaWidgetClass, form, XtNwidth, WS_WIDTH, XtNheight, WS_HEIGHT, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, bar, XmNleftAttachment, XmATTACH_FORM, NULL ); /* Register an event handler for Expose events. */ XtAddEventHandler( canvas, ExposureMask, FALSE, expose, NULL ); XtAddEventHandler( canvas, ButtonPressMask, FALSE, handle_input, NULL ); XtAddEventHandler( canvas, Button1MotionMask, FALSE, handle_input, NULL ); frame = XtVaCreateManagedWidget( "frame", xmFrameWidgetClass, form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, bar, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, canvas, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL ); panel = XtVaCreateManagedWidget( "panel", xmRowColumnWidgetClass, frame, NULL ); strings[0] = XmStringCreateSimple( "Lights:" ); strings[1] = XmStringCreateSimple( "ambient" ); strings[2] = XmStringCreateSimple( "directional" ); strings[3] = XmStringCreateSimple( "positional" ); strings[4] = XmStringCreateSimple( "spot" ); cur_light_menu = XmVaCreateSimpleOptionMenu( panel, "light_control", strings[0], 0, 3, select_cur_light, XmVaPUSHBUTTON, strings[1], 0, NULL, NULL, XmVaPUSHBUTTON, strings[2], 0, NULL, NULL, XmVaPUSHBUTTON, strings[3], 0, NULL, NULL, XmVaPUSHBUTTON, strings[4], 0, NULL, NULL, NULL ); FREE_STRINGS( strings, 5 ) light_red = XtVaCreateManagedWidget( "red", xmScaleWidgetClass, panel, XtVaTypedArg, XmNtitleString, XmRString, "Red", 3, XmNmaximum, 10, XmNminimum, 0, XmNvalue, 10, XmNdecimalPoints, 1, XmNshowValue, True, XmNorientation, XmHORIZONTAL, NULL ); XtAddCallback( light_red, XmNvalueChangedCallback, set_light_source, RED_COORD ); light_green = XtVaCreateManagedWidget( "green", xmScaleWidgetClass, panel, XtVaTypedArg, XmNtitleString, XmRString, "Green", 5, XmNmaximum, 10, XmNminimum, 0, XmNvalue, 10, XmNdecimalPoints, 1, XmNshowValue, True, XmNorientation, XmHORIZONTAL, NULL ); XtAddCallback( light_green, XmNvalueChangedCallback, set_light_source, GREEN_COORD ); light_blue = XtVaCreateManagedWidget( "blue", xmScaleWidgetClass, panel, XtVaTypedArg, XmNtitleString, XmRString, "Blue", 4, XmNmaximum, 10, XmNminimum, 0, XmNvalue, 10, XmNdecimalPoints, 1, XmNshowValue, True, XmNorientation, XmHORIZONTAL, NULL ); XtAddCallback( light_blue, XmNvalueChangedCallback, set_light_source, BLUE_COORD ); sep_1 = XtVaCreateManagedWidget( "separator", xmSeparatorWidgetClass, panel, XmNorientation, XmHORIZONTAL, NULL ); scale_C1 = XtVaCreateManagedWidget( "C1", xmScaleWidgetClass, panel, XtVaTypedArg, XmNtitleString, XmRString, "C1 Attenuation", 14, XmNmaximum, 10, XmNminimum, 0, XmNvalue, 0, XmNdecimalPoints, 1, XmNshowValue, True, XmNorientation, XmHORIZONTAL, XmNhighlightOnEnter, True, NULL ); XtAddCallback( scale_C1, XmNvalueChangedCallback, set_light_source, ATTENUATION_C1 ); scale_C2 = XtVaCreateManagedWidget( "C2", xmScaleWidgetClass, panel, XtVaTypedArg, XmNtitleString, XmRString, "C2 Attenuation", 14, XmNmaximum, 10, XmNminimum, 0, XmNvalue, 10, XmNdecimalPoints, 1, XmNshowValue, True, XmNorientation, XmHORIZONTAL, NULL ); XtAddCallback( scale_C2, XmNvalueChangedCallback, set_light_source, ATTENUATION_C2 ); sep_2 = XtVaCreateManagedWidget( "separator", xmSeparatorWidgetClass, panel, XmNorientation, XmHORIZONTAL, NULL ); scale_spread = XtVaCreateManagedWidget( "spread", xmScaleWidgetClass, panel, XtVaTypedArg, XmNtitleString, XmRString, "Spread Angle", 12, XmNmaximum, 90, XmNminimum, 0, XmNvalue, 90, XmNdecimalPoints, 0, XmNshowValue, True, XmNorientation, XmHORIZONTAL, NULL ); XtAddCallback( scale_spread, XmNvalueChangedCallback, set_light_source, SPREAD_ANGLE ); scale_conc = XtVaCreateManagedWidget( "concentration", xmScaleWidgetClass, panel, XtVaTypedArg, XmNtitleString, XmRString, "Concentration", 13, XmNmaximum, 200, XmNminimum, 0, XmNvalue, 2, XmNdecimalPoints, 0, XmNshowValue, True, XmNorientation, XmHORIZONTAL, NULL ); XtAddCallback( scale_conc, XmNvalueChangedCallback, set_light_source, CONCENTRATION ); /* The reflectance properties panel. */ frame = XtVaCreateManagedWidget( "frame", xmFrameWidgetClass, form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, canvas, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET, XmNrightWidget, canvas, XmNbottomAttachment, XmATTACH_FORM, NULL ); refl_panel = XtVaCreateManagedWidget( "refl_panel", xmFormWidgetClass, frame, XmNorientation, XmVERTICAL, NULL ); refl_label = XtVaCreateManagedWidget( "Reflectance Properties", xmLabelWidgetClass, refl_panel, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL ); scale_Ka = XtVaCreateManagedWidget( "Ka", xmScaleWidgetClass, refl_panel, XtVaTypedArg, XmNtitleString, XmRString, "Ambient Coef", 12, XmNmaximum, 10, XmNminimum, 0, XmNvalue, 4, XmNdecimalPoints, 1, XmNshowValue, True, XmNorientation, XmHORIZONTAL, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, refl_label, XmNleftAttachment, XmATTACH_FORM, XmNtopOffset, 10, XmNbottomOffset, 10, XmNleftOffset, 10, XmNrightOffset, 10, NULL ); XtAddCallback( scale_Ka, XmNvalueChangedCallback, set_refl_props, AMBIENT_COEF ); scale_Kd = XtVaCreateManagedWidget( "Kd", xmScaleWidgetClass, refl_panel, XtVaTypedArg, XmNtitleString, XmRString, "Diffuse Coef", 12, XmNmaximum, 10, XmNminimum, 0, XmNvalue, 4, XmNdecimalPoints, 1, XmNshowValue, True, XmNorientation, XmHORIZONTAL, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, scale_Ka, XmNleftAttachment, XmATTACH_FORM, XmNtopOffset, 10, XmNbottomOffset, 10, XmNleftOffset, 10, XmNrightOffset, 10, NULL ); XtAddCallback( scale_Kd, XmNvalueChangedCallback, set_refl_props, DIFFUSE_COEF ); scale_Ks = XtVaCreateManagedWidget( "Ks", xmScaleWidgetClass, refl_panel, XtVaTypedArg, XmNtitleString, XmRString, "Specular Coef", 13, XmNmaximum, 10, XmNminimum, 0, XmNvalue, 5, XmNdecimalPoints, 1, XmNshowValue, True, XmNorientation, XmHORIZONTAL, XmNleftAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, scale_Kd, XmNtopOffset, 10, XmNbottomOffset, 10, XmNleftOffset, 10, XmNrightOffset, 10, NULL ); XtAddCallback( scale_Ks, XmNvalueChangedCallback, set_refl_props, SPECULAR_COEF ); sep = XtVaCreateManagedWidget( "separator", xmSeparatorWidgetClass, refl_panel, XmNorientation, XmVERTICAL, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, scale_Ka, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, refl_label, XmNbottomAttachment, XmATTACH_FORM, XmNtopOffset, 10, XmNbottomOffset, 10, XmNleftOffset, 10, XmNrightOffset, 10, NULL ); scale_Sr = XtVaCreateManagedWidget( "r_red", xmScaleWidgetClass, refl_panel, XtVaTypedArg, XmNtitleString, XmRString, "Specular Red", 12, XmNmaximum, 10, XmNminimum, 0, XmNvalue, 10, XmNdecimalPoints, 1, XmNshowValue, True, XmNorientation, XmHORIZONTAL, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, sep, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, refl_label, XmNtopOffset, 10, XmNbottomOffset, 10, XmNleftOffset, 10, XmNrightOffset, 10, NULL ); XtAddCallback( scale_Sr, XmNvalueChangedCallback, set_refl_props, RED_COORD ); scale_Sg = XtVaCreateManagedWidget( "r_green", xmScaleWidgetClass, refl_panel, XtVaTypedArg, XmNtitleString, XmRString, "Specular Green", 14, XmNmaximum, 10, XmNminimum, 0, XmNvalue, 10, XmNdecimalPoints, 1, XmNshowValue, True, XmNorientation, XmHORIZONTAL, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, sep, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, scale_Sr, XmNtopOffset, 10, XmNbottomOffset, 10, XmNleftOffset, 10, XmNrightOffset, 10, NULL ); XtAddCallback( scale_Sg, XmNvalueChangedCallback, set_refl_props, GREEN_COORD ); scale_Sb = XtVaCreateManagedWidget( "r_blue", xmScaleWidgetClass, refl_panel, XtVaTypedArg, XmNtitleString, XmRString, "Specular Blue", 13, XmNmaximum, 10, XmNminimum, 0, XmNvalue, 10, XmNdecimalPoints, 1, XmNshowValue, True, XmNorientation, XmHORIZONTAL, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, sep, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, scale_Sg, XmNtopOffset, 10, XmNbottomOffset, 10, XmNleftOffset, 10, XmNrightOffset, 10, NULL ); XtAddCallback( scale_Sb, XmNvalueChangedCallback, set_refl_props, BLUE_COORD ); sep = XtVaCreateManagedWidget( "separator", xmSeparatorWidgetClass, refl_panel, XmNorientation, XmVERTICAL, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, scale_Sr, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, refl_label, XmNbottomAttachment, XmATTACH_FORM, XmNtopOffset, 10, XmNbottomOffset, 10, XmNleftOffset, 10, XmNrightOffset, 10, NULL ); scale_Se = XtVaCreateManagedWidget( "spec_exp", xmScaleWidgetClass, refl_panel, XtVaTypedArg, XmNtitleString, XmRString, "Specular Exponent", 17, XmNmaximum, 100, XmNminimum, 0, XmNvalue, 3, XmNdecimalPoints, 0, XmNshowValue, True, XmNorientation, XmHORIZONTAL, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, sep, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, refl_label, XmNtopOffset, 10, XmNbottomOffset, 10, XmNleftOffset, 10, XmNrightOffset, 10, NULL ); XtAddCallback( scale_Se, XmNvalueChangedCallback, set_refl_props, SPECULAR_EXP ); /* Manage and realize the widgets. */ XtManageChild( bar ); XtManageChild( cur_light_menu ); XtRealizeWidget( top ); /* Set up to use window manager DELETE_WINDOW protocol. */ delete_window_atom = XInternAtom( XtDisplay(top), "WM_DELETE_WINDOW", TRUE ); XmAddWMProtocols( top, &delete_window_atom, 1 ); XmSetWMProtocolHooks( top, delete_window_atom, NULL, NULL, quit, NULL ); /* Set the backing store attribute. */ window = XtWindow( canvas ); wattrs.backing_store = NotUseful; XChangeWindowAttributes( dpy, window, CWBackingStore, &wattrs ); /* Create the PEX renderer. */ if ( IMMED_MODE_SPT( pexinfo ) ) { renderer = ora_setup_renderer( dpy, window, &capx_info, &rattrs ); } else { fprintf( stderr, "Immediate mode not supported.\n" ); exit(1); } } #define NORMALIZE( _v ) \ {double mag; \ mag = 1.0 / sqrt((_v).x * (_v).x \ + (_v).y * (_v).y \ + (_v).z * (_v).z); \ (_v).x *= mag, (_v).y *= mag, (_v).z *= mag;} #define NUM_FACETS 16 #define NUM_POINTS (NUM_FACETS + 1) #define CYL_RADIUS 0.5 static void build_cylinder() { PEXVertexNormal vertex_data[2 * NUM_POINTS]; PEXArrayOfVertex vdata; PEXArrayOfFacetData facet_data; PEXVector facet_normals[NUM_FACETS]; unsigned int facet_data_flag, vertex_data_flag; double theta = 0; int i; facet_data_flag = PEXGANormal; vertex_data_flag = PEXGANormal; /* Compute the top row of points. */ for ( i = 0; i < NUM_FACETS; i++ ) { vertex_data[i].point.x = CYL_RADIUS * (1 + cos( theta )); vertex_data[i].point.z = CYL_RADIUS * (1 + sin( theta )); vertex_data[i].point.y = 1; vertex_data[i].normal.x = cos( theta ); vertex_data[i].normal.y = 0; vertex_data[i].normal.z = sin( theta ); NORMALIZE(vertex_data[i].normal); theta += (2 * M_PI) / NUM_FACETS; } /* Close the cylinder. */ vertex_data[NUM_FACETS].point = vertex_data[0].point; vertex_data[NUM_FACETS].normal = vertex_data[0].normal; /* Compute the bottom row of points. */ for ( i = 0; i < NUM_POINTS; i++ ) { vertex_data[NUM_POINTS + i].point.x = vertex_data[i].point.x; vertex_data[NUM_POINTS + i].point.y = 0; vertex_data[NUM_POINTS + i].point.z = vertex_data[i].point.z; vertex_data[NUM_POINTS + i].normal = vertex_data[i].normal; } facet_data.normal = facet_normals; theta = M_PI / NUM_FACETS; for ( i = 0; i < NUM_FACETS; i++ ) { facet_normals[i].x = cos( theta ); facet_normals[i].y = 0; facet_normals[i].z = sin( theta ); NORMALIZE(facet_normals[i]); theta += (2 * M_PI) / NUM_FACETS; } /* Create the structure and store the quad mesh. */ cylinder = PEXCreateStructure( dpy ); vdata.normal = vertex_data; PEXQuadrilateralMesh( dpy, cylinder, PEXOCStore, PEXShapeUnknown, facet_data_flag, vertex_data_flag, PEXColorTypeRGB, facet_data, NUM_POINTS, 2, vdata ); } #define NUM_LONG_FACETS 32 #define NUM_LONG_POINTS (NUM_LONG_FACETS + 1) #define NUM_LAT_FACETS 16 #define NUM_LAT_POINTS (NUM_LAT_FACETS + 1) #define RADIUS 0.6 static void build_sphere() { PEXVertexNormal vertex_data[NUM_LONG_POINTS * NUM_LAT_POINTS]; PEXArrayOfVertex vdata; PEXArrayOfFacetData facet_data; PEXVector facet_normals[NUM_LONG_FACETS * NUM_LAT_FACETS]; unsigned int facet_data_flag, vertex_data_flag; double theta = 0, phi = 0; int i, j, index; facet_data_flag = PEXGANormal; vertex_data_flag = PEXGANormal; for ( i = 0; i < NUM_LAT_POINTS; i++ ) { for ( j = 0; j < NUM_LONG_FACETS; j++ ) { index = i * NUM_LONG_POINTS + j; vertex_data[index].point.x = RADIUS * (1 + sin(phi) * cos(theta)); vertex_data[index].point.y = RADIUS * (1 + cos(phi)); vertex_data[index].point.z = RADIUS * (1 + sin(phi) * sin(theta)); vertex_data[index].normal.x = sin(phi) * cos(theta); vertex_data[index].normal.y = cos(phi); vertex_data[index].normal.z = sin(phi) * sin(theta); NORMALIZE(vertex_data[index].normal); theta += (2 * M_PI) / NUM_LONG_FACETS; } phi += M_PI / NUM_LAT_FACETS; /* Close the circle. */ index = i * NUM_LONG_POINTS + NUM_LONG_FACETS; vertex_data[index].point = vertex_data[index - NUM_LONG_FACETS].point; vertex_data[index].normal = vertex_data[index - NUM_LONG_FACETS].normal; } theta = M_PI / NUM_LONG_FACETS; phi = 0.5 * M_PI / NUM_LAT_FACETS; index = 0; for ( i = 0; i < NUM_LAT_FACETS; i++ ) { for ( j = 0; j < NUM_LONG_FACETS; j++ ) { facet_normals[index].x = sin(phi) * cos(theta); facet_normals[index].y = cos(phi); facet_normals[index].z = sin(phi) * sin(theta); ++index; theta += (2 * M_PI) / NUM_LONG_FACETS; } phi += M_PI / NUM_LAT_FACETS; } sphere = PEXCreateStructure( dpy ); PEXSetFacetCullingMode( dpy, sphere, PEXOCStore, PEXBackFaces ); vdata.normal = vertex_data; facet_data.normal = facet_normals; PEXQuadrilateralMesh( dpy, sphere, PEXOCStore, PEXShapeUnknown, facet_data_flag, vertex_data_flag, PEXColorTypeRGB, facet_data, NUM_LONG_POINTS, NUM_LAT_POINTS, vdata ); } #define NUM_Q_POINTS 10 static void build_cube() { PEXCoord vertex_data[NUM_Q_POINTS * NUM_Q_POINTS]; PEXArrayOfVertex vdata; PEXArrayOfFacetData facet_data; PEXVector facet_normals[(NUM_Q_POINTS-1) * (NUM_Q_POINTS-1)]; unsigned int facet_data_flag, vertex_data_flag; PEXCoord fixed_pt; PEXVector trans, scale; PEXMatrix xform; int i, j, index; for ( j = 0; j < NUM_Q_POINTS; j++ ) { for ( i = 0; i < NUM_Q_POINTS; i++ ) { index = j * NUM_Q_POINTS + i; vertex_data[index].x = i * (1.0/(NUM_Q_POINTS - 1)); vertex_data[index].y = j * (1.0/(NUM_Q_POINTS - 1)); vertex_data[index].z = 0; } } for ( i = 0; i < sizeof(facet_normals)/sizeof(PEXVector); i++ ) { facet_normals[i].x = facet_normals[i].y = 0; facet_normals[i].z = 1; } facet_data.normal = facet_normals; /* Create the structure for one face of the cube. */ cube_quad = PEXCreateStructure( dpy ); /* Create the quadrilateral mesh. */ facet_data_flag = PEXGANormal; vertex_data_flag = PEXGANone; vdata.no_data = vertex_data; PEXQuadrilateralMesh( dpy, cube_quad, PEXOCStore, PEXShapeConvex, facet_data_flag, vertex_data_flag, PEXColorTypeRGB, facet_data, NUM_Q_POINTS, NUM_Q_POINTS, vdata ); /* Create the structure for the cube. */ cube = PEXCreateStructure( dpy ); /* Initialize the fixed point and scale vector. */ fixed_pt.x = fixed_pt.y = 0.5; fixed_pt.z = 0; scale.x = scale.y = scale.z = 1; /* The front face. */ trans.x = trans.y = 0; trans.z = 1; PEXBuildTransform( &fixed_pt, &trans, 0.0, 0.0, 0.0, &scale, xform ); PEXSetLocalTransform( dpy, cube, PEXOCStore, PEXReplace, xform ); PEXExecuteStructure( dpy, cube, PEXOCStore, cube_quad ); /* The back face. */ trans.x = trans.y = 0; trans.z = 0; PEXBuildTransform( &fixed_pt, &trans, 0.0, M_PI, 0.0, &scale, xform ); PEXSetLocalTransform( dpy, cube, PEXOCStore, PEXReplace, xform ); PEXExecuteStructure( dpy, cube, PEXOCStore, cube_quad ); /* The bottom face. */ trans.x = 0; trans.y = -0.5; trans.z = 0.5; PEXBuildTransform( &fixed_pt, &trans, M_PI/2, 0.0, 0.0, &scale, xform ); PEXSetLocalTransform( dpy, cube, PEXOCStore, PEXReplace, xform ); PEXExecuteStructure( dpy, cube, PEXOCStore, cube_quad ); /* The top face. */ trans.x = 0; trans.y = 0.5; trans.z = 0.5; PEXBuildTransform( &fixed_pt, &trans, -M_PI/2, 0.0, 0.0, &scale, xform ); PEXSetLocalTransform( dpy, cube, PEXOCStore, PEXReplace, xform ); PEXExecuteStructure( dpy, cube, PEXOCStore, cube_quad ); #ifdef CLOSE_CUBE /* The left face. */ trans.x = -0.5; trans.y = 0; trans.z = 0.5; PEXBuildTransform( &fixed_pt, &trans, 0.0, -M_PI/2, 0.0, &scale, xform ); PEXSetLocalTransform( dpy, cube, PEXOCStore, PEXReplace, xform ); PEXExecuteStructure( dpy, cube, PEXOCStore, cube_quad ); /* The right face. */ trans.x = 0.5; trans.y = 0; trans.z = 0.5; PEXBuildTransform( &fixed_pt, &trans, 0.0, M_PI/2, 0.0, &scale, xform ); PEXSetLocalTransform( dpy, cube, PEXOCStore, PEXReplace, xform ); PEXExecuteStructure( dpy, cube, PEXOCStore, cube_quad ); #endif }