ftp.nice.ch/pub/next/graphics/3d/geomview.1.4.1.s.tar.gz#/Geomview/src/lib/mg/gl/mggl.c

This is mggl.c in view mode; [Download] [Up]

/* Copyright (c) 1992 The Geometry Center; University of Minnesota
   1300 South Second Street;  Minneapolis, MN  55454, USA;

This file is part of geomview/OOGL. geomview/OOGL is free software;
you can redistribute it and/or modify it only under the terms given in
the file COPYING, which you should have received along with this file.
This and other related software may be obtained via anonymous ftp from
geom.umn.edu; email: software@geom.umn.edu. */

/* Authors: Charlie Gunn, Stuart Levy, Tamara Munzner, Mark Phillips */

#include "mgP.h"
#include "mgglP.h"
#include "windowP.h"
#include "mgglshade.h"
#include <gl/gl.h>
#include <stdio.h>

mgcontext * mggl_ctxcreate(int a1, ...);
void	    mggl_ctxset( int a1, ...  );
int	    mggl_feature( int feature );
void	    mggl_ctxdelete( mgcontext *ctx );
int	    mggl_ctxget( int attr, void* valueptr );
int	    mggl_ctxselect( mgcontext *ctx );
void	    mggl_sync( void );
void	    mggl_worldbegin( void );
void	    mggl_worldend( void );
void	    mggl_reshapeviewport( void );
void	    mggl_identity( void );
void	    mggl_transform( Transform T );
int	    mggl_pushtransform( void );
int	    mggl_poptransform( void );
void	    mggl_gettransform( Transform T );
void	    mggl_settransform( Transform T );
int	    mggl_pushappearance( void );
int	    mggl_popappearance( void );
Appearance *mggl_setappearance( Appearance* app, int merge );
Appearance *mggl_getappearance( void );
int	    mggl_setcamera( Camera* cam );
int	    mggl_setwindow( WnWindow *win, int final );
mgglcontext *mggl_newcontext( mgglcontext *ctx );

extern void mggl_polygon();
extern void mggl_mesh();
extern void mggl_line();
extern void mggl_polyline();
extern void mggl_polylist();
extern void mggl_quads();



void _mggl_ctxset(int a1, va_list *alist);

WnWindow *mgglwindow(WnWindow *win);

static unsigned long PackColorA(ColorA *c);

struct mgfuncs mgglfuncs = {
  MGD_GL,
  mgdevice_GL,
  mggl_feature,
  (mgcontext *(*)())mggl_ctxcreate,
  mggl_ctxdelete,
  (void (*)())mggl_ctxset,
  mggl_ctxget,
  mggl_ctxselect,
  mggl_sync,
  mggl_worldbegin,
  mggl_worldend,
  mggl_reshapeviewport,
  mggl_settransform,
  mggl_gettransform,
  mggl_identity,
  mggl_transform,
  mggl_pushtransform,
  mggl_poptransform,
  mggl_pushappearance,
  mggl_popappearance,
  mggl_setappearance,
  mggl_getappearance,
  mggl_setcamera,
  mggl_polygon,
  mggl_polylist,
  mggl_mesh,
  mggl_line,
  mggl_polyline,
  mggl_quads,
  mg_bezier
  };

int
  mgdevice_GL()
{
  _mgf = mgglfuncs;
  if (_mgc != NULL && _mgc->devno != MGD_GL)
    _mgc = NULL;
  return(0);
}

/*-----------------------------------------------------------------------
 * Function:	mggl_ctxcreate
 * Date:	Thu Jul 18 18:55:18 1991
 * Author:	mbp
 * Notes:	see mg.doc for rest of spec
 */
mgcontext *
mggl_ctxcreate(int a1, ...)
{
  va_list alist;


  _mgc =
    (mgcontext*)mggl_newcontext( OOGLNewE(mgglcontext, "mggl_ctxcreate") );

  va_start(alist, a1);
  _mggl_ctxset(a1, &alist);
  va_end(alist);
  return _mgc;
}

/*-----------------------------------------------------------------------
 * Function:	_mggl_ctxset
 * Description:	internal ctxset routine
 * Args:	a1: first attribute
 *		*alist: rest of attribute-value list
 * Returns:	nothing
 * Author:	mbp
 * Date:	Fri Sep 20 11:08:13 1991
 * Notes:	mggl_ctxcreate() and mggl_ctxset() call this to actually
 *		parse and interpret the attribute list.
 */
void
_mggl_ctxset(int a1, va_list *alist)
{
  int attr;
  WnWindow *owin;
  char **ablock = NULL;

#define NEXT(type) OOGL_VA_ARG(type,alist,ablock)

  for (attr = a1; attr != MG_END; attr = NEXT(int)) {
    switch (attr) {
    case MG_ABLOCK:
      ablock = NEXT(char**);
      break;
    case MG_ApSet:
      {
	Appearance *ap;

	if (ablock) {
	  ap = ApSet(NULL, AP_ABLOCK, ablock);
	} else {
	  ap = _ApSet(NULL, va_arg(*alist, int), alist);
	}

	mggl_setappearance(ap, MG_MERGE);
	ApDelete(ap);
      }
      break;
    case MG_WnSet:
      if (ablock) {
	WnSet( _mgc->win, WN_ABLOCK, ablock);
      } else {
	_WnSet( _mgc->win, va_arg(*alist, int), alist);
      }
      mggl_setwindow( _mgc->win, 0 );
      break;
    case MG_CamSet:
      if (ablock) {
	CamSet( _mgc->cam, CAM_ABLOCK, ablock);
      } else {
	_CamSet( _mgc->cam, va_arg(*alist, int), alist); break;
      }
    case MG_APPEAR: mgsetappearance(NEXT(Appearance *), MG_SET);
      break;
    case MG_WINDOW: mggl_setwindow( NEXT(WnWindow *), 0 ); break;
    case MG_CAMERA: mggl_setcamera( NEXT(Camera*) ); break;
    case MG_SETOPTIONS: _mgc->opts |= NEXT(int); break;
    case MG_UNSETOPTIONS: _mgc->opts &= ~NEXT(int); break;
    case MG_SHOW: _mgc->shown = NEXT(int); break;
    case MG_PARENT: _mgc->parent = NEXT(mgcontext*); break;
    case MG_BACKGROUND: _mgc->background = *NEXT(ColorA*); break;

    case MG_SHADER: mggl_setshader( NEXT(mgshadefunc) ); break;
    case MG_SHADERDATA: _mgc->astk->shaderdata = NEXT(void*); break;

    case MG_SPACE:
      {
	int space = NEXT(int);
	switch (TM_SPACE(space)) {
	case TM_EUCLIDEAN:
	case TM_SPHERICAL:
	  _mgc->space = space;
	  break;
	case TM_HYPERBOLIC:
	  switch (TM_MODEL(space)) {
	  case TM_VIRTUAL:
	  case TM_PROJECTIVE:
	  case TM_CONFORMAL_BALL:
	    _mgc->space = space;
	    break;
	  default:
	    fprintf(stderr, "_mggl_ctxset: Illegal space value %1d\n", space);
	    break;
	  }
	  break;
	default:
	  fprintf(stderr, "_mggl_ctxset: Illegal space value %1d\n", space);
	  break;
	}
      }
      break;

    case MG_NDINFO: _mgc->NDinfo = NEXT(void *); break;
    case MG_NDMAP:  _mgc->NDmap = NEXT(mgmapfunc); break;

    /* GL-specific */
    case MG_ZNUDGE:
	_mgc->zfnudge = NEXT(double);
	if(_mgglc->born) mggl_init_zrange();
	break;

    case MG_GLWINID:
	_mgglc->win = NEXT(int);
	break;

    case MG_GLXDISPLAY:
	_mgglc->GLXdisplay = NEXT(void *);
	break;

    default:
      OOGLError (0, "_mggl_ctxset: undefined option: %d", attr);
      return;
    }
  }

  if (_mgc->shown && !_mgglc->born) {

    /* open the window */
    mgglwindow(_mgc->win);

    /* bring gl state into accordance with appearance state */
    {
      Appearance *ap = ApCopy( &(_mgc->astk->ap), NULL );
      mggl_setappearance( ap, MG_SET );
      ApDelete(ap);
    }

  }

#undef NEXT

}


/*-----------------------------------------------------------------------
 * Function:	mggl_ctxget
 * Description:	get a context attribute value
 * Args:	attr: the attribute to get
 *		value: place to write attr's value
 * Returns:	1 for success; -1 if attr is invalid
 * Author:	mbp
 * Date:	Fri Sep 20 11:50:25 1991
 * Notes:
 */
int
mggl_ctxget(int attr, void* value)
{
#define VALUE(type) ((type*)value)

  switch (attr) {

  /* Attributes common to all MG contexts: */
  case MG_APPEAR: *VALUE(Appearance*) = &(_mgc->astk->ap); break;
  case MG_CAMERA: *VALUE(Camera*) = _mgc->cam; break;
  case MG_WINDOW:
	if(_mgglc->born) {
	    long x0, y0, xsize, ysize;
	    WnPosition wp;
	    getorigin(&x0, &y0);
	    getsize(&xsize, &ysize);
	    wp.xmin = x0; wp.xmax = x0+xsize-1;
	    wp.ymin = y0; wp.ymax = y0+ysize-1;
	    WnSet(_mgc->win, WN_CURPOS, &wp, WN_END);
	}
	*VALUE(WnWindow*) = _mgc->win;
	break;
  case MG_PARENT:	*VALUE(mgcontext*) = _mgc->parent; break;

  case MG_SETOPTIONS:
  case MG_UNSETOPTIONS:	*VALUE(int) = _mgc->opts; break;

  case MG_BACKGROUND:	*VALUE(ColorA) = _mgc->background; break;

  case MG_SHADER:	*VALUE(mgshadefunc) = _mgc->astk->shader; break;
  case MG_SHADERDATA:	*VALUE(void *) = _mgc->astk->shaderdata; break;
  case MG_SPACE:	*VALUE(int) = _mgc->space; break;
  case MG_NDINFO:	*VALUE(void *) = _mgc->NDinfo; break;
  case MG_NDMAP:	*VALUE(mgmapfunc) = _mgc->NDmap; break;

  /* Attributes specific to GL contexts: */
  case MG_GLWINID: *VALUE(int) = _mgglc->win; break;
  case MG_GLBORN: *VALUE(int) = _mgglc->born; break;
  case MG_GLZMAX: *VALUE(long) = _mgglc->zmax; break;
  case MG_ZNUDGE: *VALUE(float) = _mgc->zfnudge; break;

  default:
    OOGLError (0, "mggl_ctxget: undefined option: %d", attr);
    return -1;

  }
  return 1;

#undef VALUE
}

/*-----------------------------------------------------------------------
 * Function:	mgglwindow
 * Description:	create a GL window
 * Args:	*win: the WnWindow structure to realize
 * Returns:	win if success, NULL if not
 * Author:	mbp, slevy
 * Date:	Fri Sep 20 11:56:31 1991
 * Notes:	makes the GL calls necessary to create a GL window
 *		  corresponding to *win.  This involves the call to
 *		  winopen(), as well as various configuration things
 *		  like RGBmode(), zbuffer(), gconfig(), etc.
 */
WnWindow *
mgglwindow(WnWindow *win)
{
  WnPosition pos;
  int xsize, ysize, flag;
  int zmin;
  char *name;
  char gver[80];

  mggl_setwindow(win, 1);

  RGBmode();
  zbuffer(TRUE);
  subpixel(TRUE);

  mmode(MPROJECTION);
  loadmatrix( TM_IDENTITY );
  mmode(MVIEWING);

  if(_mgglc->GLXdisplay == NULL) {
      if(_mgc->opts & MGO_DOUBLEBUFFER)
	doublebuffer();
      gconfig();
  }
  _mgglc->oldopts = _mgc->opts;
  _mgglc->born = 1;
  _mgglc->zmax = getgdesc(GD_ZMAX);
  _mgglc->zmin = getgdesc(GD_ZMIN);
  mggl_init_zrange();

  _mgglc->cantwoside = getgdesc(GD_LIGHTING_TWOSIDE);

  gversion(gver);
  _mgglc->is_PI = (strncmp(gver, "GL4DPI", 6) == 0);
  _mgglc->turbo = (strncmp(gver, "GL4DPIT", 7) == 0);

  czclear(PackColorA(&(_mgc->background)), _mgglc->zmax);
  if((_mgc->opts&MGO_DOUBLEBUFFER) && !(_mgc->opts&MGO_INHIBITSWAP) )
    swapbuffers();

  return(win);
}

/*-----------------------------------------------------------------------
 * Function:	mggl_ctxset
 * Description:	set some context attributes
 * Args:	a1, ...: list of attribute-value pairs
 * Returns:	nothing
 * Author:	mbp
 * Date:	Fri Sep 20 12:00:18 1991
 */
void mggl_ctxset( int a1, ...  )
{
  va_list alist;

  va_start( alist, a1 );
  _mggl_ctxset(a1, &alist);
  va_end(alist);
}


/*-----------------------------------------------------------------------
 * Function:	mggl_feature
 * Description:	report whether the GL device has a particular feature
 * Args:	feature: the feature to report on
 * Returns:	an int giving info about feature
 * Author:	mbp
 * Date:	Fri Sep 20 12:00:58 1991
 * Notes:	-1 means the feature is not present.
 *
 *		NO OPTIONAL FEATURES SUPPORTED YET.  ALWAYS RETURNS -1.
 */
int mggl_feature( int feature )
{
  return(-1);
}

/*-----------------------------------------------------------------------
 * Function:	mggl_ctxdelete
 * Description:	delete a GL context
 * Args:	*ctx: context to delete
 * Returns:	nothing
 * Author:	slevy
 * Date:	Tue Nov 12 10:29:04 CST 1991
 * Notes:	Deleting the current context leaves the current-context
 *		pointer set to NULL.
 */
void mggl_ctxdelete( mgcontext *ctx )
{
  if(ctx->devno != MGD_GL) {
    mgcontext *was = _mgc;
    mgctxselect(ctx);
    mgctxdelete(ctx);
    if(was != ctx)
	mgctxselect(was);
  } else {
    if(((mgglcontext *)ctx)->born)
	winclose(((mgglcontext *)ctx)->win);
    mg_ctxdelete(ctx);
    if(ctx == _mgc)
	_mgc = NULL;
  }
}

/*-----------------------------------------------------------------------
 * Function:	mggl_ctxselect
 * Description:	select an MG context --- make it current
 * Args:	*ctx: the context to become current
 * Returns:	0 (why ???)
 * Author:	mbp
 * Date:	Fri Sep 20 12:04:41 1991
 */
int
mggl_ctxselect( mgcontext *ctx )
{
  if(ctx == NULL || ctx->devno != MGD_GL) {
    return mg_ctxselect(ctx);
  }
  /* Yes, it's a GL context.  Do something useful. */
  _mgc = ctx;
  if(_mgglc->win) {
    if(_mgglc->GLXdisplay != NULL)
      GLXwinset(_mgglc->GLXdisplay, _mgglc->win);
    else
      winset(_mgglc->win);
  }
  return(0);
}

/*-----------------------------------------------------------------------
 * Function:	mggl_sync
 * Description:	flush buffered GL commands
 * Returns:	nothing
 * Author:	mbp
 * Date:	Fri Sep 20 12:06:09 1991
 * Notes:	Just flushes the GL buffer -- needed for remote displays.
 */
void
mggl_sync( void )
{ gflush(); }

/*-----------------------------------------------------------------------
 * Function:	mggl_worldbegin
 * Description:	prepare to draw a frame
 * Returns:	nothing
 * Author:	mbp
 * Date:	Fri Sep 20 12:06:58 1991
 */
void
mggl_worldbegin( void )
{
  Transform V, S;

  if((_mgglc->oldopts ^ _mgc->opts) & MGO_DOUBLEBUFFER) {
    if(_mgglc->GLXdisplay == NULL) {
	if(_mgc->opts & MGO_DOUBLEBUFFER) doublebuffer();
	else singlebuffer();
	gconfig();
    } else {
	OOGLError(1, "Note: Can't change single/doublebuffer config of GLX window");
    }
    _mgglc->oldopts = _mgc->opts;
  }

  RGBwritemask(_mgc->opts & MGO_NORED ? 0 : 255,
		_mgc->opts & MGO_NOGREEN ? 0 : 255,
		_mgc->opts & MGO_NOBLUE ? 0 : 255);

  /* Erase to background color & initialize z-buffer */
  if(_mgc->opts & MGO_INHIBITCLEAR) zclear();
  else czclear(PackColorA(&(_mgc->background)), _mgglc->zmax);

  mg_worldbegin();	/* Initialize W2C, C2W, W2S, S2W, etc. */

  _mgc->has = 0;

  /* Interpret the camera: load the proper matrices onto the GL matrix
     stacks.  */
  if(!(_mgc->opts & MGO_INHIBITCAM)) {
      mmode(MPROJECTION);
      CamViewProjection( _mgc->cam, V );
      loadmatrix( V );
      mmode(MVIEWING);
      loadmatrix( _mgc->W2C );
  }

  /* Bind the lights; do this here so we get lights in world coords. */
  /* Only do this if we're supposed to do lighting */

  mg_globallights(_mgc->astk->lighting.lights, 1);

  if ( (_mgc->astk->lighting.lights != NULL) &&
      (_mgc->astk->ap.shading != APF_CONSTANT))
    mggl_lights(_mgc->astk->lighting.lights, _mgc->astk);

  /* Allow concave polygons.  This could be an Appearance attribute. */
  concave(TRUE);
}

/*-----------------------------------------------------------------------
 * Function:	mggl_worldend
 * Description:	finish drawing a frame
 * Returns:	nothing
 * Author:	mbp
 * Date:	Fri Sep 20 12:08:02 1991
 */
void
mggl_worldend( void )
{
  if((_mgc->opts&MGO_DOUBLEBUFFER) && !(_mgc->opts&MGO_INHIBITSWAP))
    swapbuffers();
  gflush();
}

/*-----------------------------------------------------------------------
 * Function:	mggl_reshapeviewport
 * Description:	adjust to a new window size
 * Returns:	nothing
 * Author:	mbp
 * Date:	Fri Sep 20 12:08:30 1991
 * Notes:	adjusts both GL's internal viewport setting, as well as
 *		MG context WnWindow's current position and camera's
 *		aspect ratio.
 */
void
mggl_reshapeviewport( void )
{
  long w, h;
  float pixasp = 1;
  WnPosition vp;

  WnGet(_mgc->win, WN_PIXELASPECT, &pixasp);
  if(WnGet(_mgc->win, WN_VIEWPORT, &vp) <= 0) {
    getsize(&w, &h);
    reshapeviewport();
  } else {
    w = vp.xmax - vp.xmin + 1;
    h = vp.ymax - vp.ymin + 1;
    viewport(vp.xmin, vp.xmax, vp.ymin, vp.ymax);
  }
  CamSet(_mgc->cam, CAM_ASPECT, pixasp * (double)w/(double)h, CAM_END);
}

/*-----------------------------------------------------------------------
 * Function:	mggl_identity
 * Description:	set the current object xform to identity
 * Returns:	nothing
 * Author:	mbp
 * Date:	Fri Sep 20 12:23:48 1991
 * Notes:	We use the GL ModelView matrix stack, not the mgcontext's
 *		stack.
 *
 *		This assumes we're already in MVIEWING mode.
 */
void
mggl_identity( void )
{
  /* [ obj xform ] = identity corresponds to having current W2C on
     ModelView stack */
  mggl_settransform( TM3_IDENTITY );
}

/*-----------------------------------------------------------------------
 * Function:	mggl_transform
 * Description:	premultiply the object xform by T
 * Args:	T
 * Returns:	nothing
 * Author:	mbp
 * Date:	Fri Sep 20 12:24:57 1991
 * Notes:	We use the GL ModelView matrix stack, not the mgcontext's
 *		stack.
 *
 *		This assumes we're already in MVIEWING mode.
 */
void
mggl_transform( Transform T )
{
  multmatrix(T);
  TmConcat(T, _mgc->xstk->T, _mgc->xstk->T);
  _mgc->has = _mgc->xstk->hasinv = 0;
}

/*-----------------------------------------------------------------------
 * Function:	mggl_pushtransform
 * Description:	push the object xform stack
 * Returns:	nothing (???)
 * Author:	mbp
 * Date:	Fri Sep 20 12:25:43 1991
 * Notes:	We use the GL ModelView matrix stack, not the mgcontext's
 *		stack.
 *
 *		This assumes we're already in MVIEWING mode.
 */
int
mggl_pushtransform( void )
{
  pushmatrix();
  mg_pushtransform();
}

/*-----------------------------------------------------------------------
 * Function:	mggl_popransform
 * Description:	pop the object xform stack
 * Returns:	nothing (???)
 * Author:	mbp
 * Date:	Fri Sep 20 12:25:43 1991
 * Notes:	We use the GL ModelView matrix stack, not the mgcontext's
 *		stack.
 *
 *		This assumes we're already in MVIEWING mode.
 */
mggl_poptransform( void )
{
  popmatrix();
  mg_poptransform();
}

/*-----------------------------------------------------------------------
 * Function:	mggl_gettransform
 * Description:	get the current object xform
 * Args:	T: place to write the current object xform
 * Returns:	nothing
 * Author:	mbp
 * Date:	Fri Sep 20 12:29:43 1991
 * Notes:	We use the GL ModelView matrix stack, not the mgcontext's
 *		stack.  This means we must multiply on the right by
 *		the current C2W matrix after reading the GL ModelView
 *		matrix.
 *
 *		This assumes we're already in MVIEWING mode.
 */
void
mggl_gettransform( Transform T )
{
  TmCopy(_mgc->xstk->T, T);
}

/*-----------------------------------------------------------------------
 * Function:	mggl_settransform
 * Description:	set the current object xform to T
 * Args:	T
 * Returns:	nothing
 * Author:	mbp
 * Date:	Fri Sep 20 12:29:43 1991
 * Notes:	We use the GL ModelView matrix stack, not the mgcontext's
 *		stack.  This means we must first load W2C onto the
 *		modelview stact, then premult by T.
 *
 *		This assumes we're already in MVIEWING mode.
 */
void
mggl_settransform( Transform T )
{
  loadmatrix( _mgc->W2C );
  multmatrix( T );
  TmCopy(T, _mgc->xstk->T);
  _mgc->has = _mgc->xstk->hasinv = 0;
}

/*-----------------------------------------------------------------------
 * Function:	mggl_pushappearance
 * Description:	push the MG context appearance stack
 * Returns:	nothing
 * Author:	mbp
 * Date:	Fri Sep 20 12:54:19 1991
 */
int
mggl_pushappearance( void )
{
  mg_pushappearance();
}

/*-----------------------------------------------------------------------
 * Function:	mggl_popappearance
 * Description:	pop the MG context appearance stack
 * Returns:	nothing
 * Author:	mbp, munzner
 * Date:	Fri Sep 20 12:54:19 1991
 * Note:        We call mggl_lighting and mggl_material instead of
 *              just doing lmbinds here because those procedures make the
 *		proper existence checks and reset the GL drawing state.
 */
int
mggl_popappearance( void )
{
  register struct mgastk *mastk = _mgc->astk;
  register struct mgastk *mastk_next;

  if (! (mastk_next=mastk->next)) {
    OOGLError(0, "mggl_popappearance: appearance stack has only 1 entry.");
    return;
  }

  if ((mastk->light_seq != mastk_next->light_seq) && /* changed */
		IS_SHADED(mastk->next->ap.shading))    /* lighting on */
      mggl_lighting(mastk_next, mastk_next->lighting.valid);
  mggl_appearance(mastk_next, mastk_next->ap.valid);

  mg_popappearance();
}

/*-----------------------------------------------------------------------
 * Function:	mggl_setappearance
 * Author:	munzner, mbp
 * Date:	Wed Aug  7 01:08:07 1991
 * Notes:	when app=NULL, mergeflag = MG_MERGE is assumed
 *		  (regardless of the actual value of mergeflag).
 *		  In this case, we make the GL calls to bring
 *		  the GL state into agreement with the current
 *		  appearance.
 *
 *		The above isn't true any more; update these comments
 *		soon.  -- mbp Mon Sep 23 19:07:39 1991
 *
 *		things set here: material, lights, shading,
 *		  linewidth, transparency
 *		things not set here: normals (drawing, scaling,
 *		  everting), drawing faces vs. edges
 */
Appearance *
mggl_setappearance( Appearance* ap, int mergeflag )
{
  int changed, mat_changed, lng_changed;
  struct mgastk *mastk = _mgc->astk;
  Appearance *ma;
  static float nullarray[] = { LMNULL };

  ma = &(mastk->ap);

  /* Decide what changes */
  if (mergeflag == MG_MERGE) {
    changed = ap->valid &~ (ma->override &~ ap->override);
    mat_changed =
      ap->mat ? ap->mat->valid &~ (ma->mat->override &~ ap->mat->override) : 0;
    lng_changed =
      ap->lighting ? ap->lighting->valid &~ 
	(ma->lighting->override &~ ap->lighting->override) : 0;
  }
  else {
    changed = ap->valid;
    mat_changed = ap->mat ? ap->mat->valid : 0; 
    lng_changed = ap->lighting ? ap->lighting->valid : 0; 
  }

  /*
   * Update current appearance; this needs to be done before making GL
   * calls because it is conceivable that we might need to make a GL call
   * corresponding to something in the current appearance for which the
   * valid bit in *ap isn't set. (???)  By updating the current
   * appearance before making GL calls, our GL calls can always take data
   * from the current appearance, rather than worrying about whether to
   * read *ap or the current appearance.
   */
  mg_setappearance( ap, mergeflag );

  /* Bring GL device into accord with new appearance */
  if (_mgglc->born) {

    /*
     * No bit in "changed" corresponds to {lighting,mat}.  We think of
     * ap->{lighting,mat} as an extension to *ap which is interpreted to
     * have all valid bits 0 if the {lighting,ap} pointer is NULL.  Note
     * that this means there is no way for the parent to override the
     * entire {lighting,mat} structure as a whole.  It can, however, set
     * the individual override bits in the {lighting,mat} structure.
     */
    if ((ap->lighting) && (mastk->next)) {
      if (mastk->light_seq == mastk->next->light_seq) {
	mastk->light_seq++;
	/*
	 * We need a new lighting model.
	 * To ensure we don't have any leftover garbage in GL's copy of this
	 * lighting model, we force GL to reset to defaults, then
	 * reinitialize everything.
	 */
	lmdef(DEFLMODEL, mastk->light_seq, 0, nullarray);
	lng_changed |= ma->lighting->valid;	/* "All fields changed" */
      }
    }
    if (ma->shading != APF_CONSTANT && ap->lighting != NULL) {
      mggl_lighting( mastk, lng_changed );
    }

	/* Let mggl_material() decide if we need a new material */
    if (ap->mat)
      mggl_material( mastk, mat_changed );

    mggl_appearance( mastk, changed );

  }

}


/*-----------------------------------------------------------------------
 * Function:	mggl_getappearance
 * Description:	return a ptr to current appearance
 * Returns:	ptr to current appearance
 * Author:	mbp
 * Date:	Fri Sep 20 13:00:41 1991
 * Notes:	Applications should not modify the returned appearance
 *		in any way.
 */
Appearance *
mggl_getappearance()
{
    return &(_mgc->astk->ap);
}


/*-----------------------------------------------------------------------
 * Function:	mggl_setcamera
 * Description:	set the context's camera (pointer)
 * Args:	*cam: the camera to use
 * Returns:	nothing
 * Author:	mbp
 * Date:	Fri Sep 20 13:07:31 1991
 * Notes:	The context stores a pointer to the camera, not a copy
 *		of it.
 */
int
mggl_setcamera( Camera* cam )
{
  if (_mgc->cam) CamDelete(_mgc->cam);
  _mgc->cam = cam;
  RefIncr((Ref*) cam);
}

/*
 * Change current Window structure.
 * If 'final' and otherwise appropriate, actually open the window.
 * Apply relevant changes to window, if needed.
 */
int
mggl_setwindow( WnWindow *win, int final )
{
  WnPosition pos, vp;
  int xsize, ysize, flag, reconstrain;
  int positioned = 0;
  int zmin;
  char *name, *oname;

  if(win == NULL)
    return 0;

  reconstrain = 0;
  if (WnGet(win, WN_PREFPOS, (void*)&pos) == 1 && (win->changed&WNF_HASPREF)) {
    if(_mgglc->born) {
	winposition(pos.xmin, pos.xmax, pos.ymin, pos.ymax);
	win->changed &= ~(WNF_HASPREF|WNF_HASSIZE);
    } else if(final) {
	prefposition(pos.xmin, pos.xmax, pos.ymin, pos.ymax);
	win->changed &= ~(WNF_HASPREF|WNF_HASSIZE);
	reconstrain = positioned = 1;
    }
  } else if ((WnGet(win, WN_XSIZE, (void*)&xsize) == 1
    && (WnGet(win, WN_YSIZE, (void*)&ysize) == 1
    && (win->changed&WNF_HASSIZE))) ) {
	prefsize(xsize, ysize);
	reconstrain = 1;
	win->changed &= ~WNF_HASSIZE;
  }


  if(reconstrain && _mgglc->born) {
    WnGet(win, WN_NOBORDER, &flag);
    if (flag) noborder();
    winconstraints();
    reconstrain = 0;
  }

  if(_mgc->shown) {
    WnGet(win, WN_NAME, &name);
    if(_mgglc->born) {
	if(name && (win->changed & WNF_HASNAME)) {
	    wintitle(name);
	    win->changed &= ~WNF_HASNAME;
	}
	if(WnGet(win, WN_VIEWPORT, &vp) > 0 && win->changed & WNF_HASVP) {
	    viewport(vp.xmin, vp.xmax, vp.ymin, vp.ymax);
	    win->changed &= ~WNF_HASVP;
	}
    } else if(final) {
	foreground();
	if(_mgglc->win <= 0)
	    _mgglc->win = winopen(name);
	if(_mgglc->win == 0) {
	  OOGLError(0,"mgglwindow: bad return from winopen: %s",sperror());
	  return 0;
	}
	if(positioned)	/* 4.0 kludge: ensure the window is where we want it! */
	  winposition(pos.xmin, pos.xmax, pos.ymin, pos.ymax);
	_mgglc->born = 1;
	reconstrain = positioned;
    }
  }

  if((reconstrain && _mgglc->born)
		|| (win->changed & (WNF_NOBORDER|WNF_ENLARGE|WNF_SHRINK))) {
	WnGet(win, WN_NOBORDER, &flag);
	if (flag) noborder();

	WnGet(win, WN_ENLARGE, &flag);
	if (flag) maxsize(getgdesc(GD_XPMAX), getgdesc(GD_YPMAX));

	WnGet(win, WN_SHRINK, &flag);
	if (flag) minsize((long)40, (long)30);
	reconstrain = 1;
  }

  if (reconstrain && _mgglc->born) {
    winconstraints();
    win->changed &= ~(WNF_NOBORDER | WNF_ENLARGE | WNF_SHRINK);
  }

  if(win != _mgc->win) {
    RefIncr((Ref *)win);
    WnDelete(_mgc->win);
    _mgc->win = win;
  }
  return 1;
}



/*-----------------------------------------------------------------------
 * Function:	mggl_newcontext
 * Description:	initialize a new mgglcontext structure
 * Args:	*ctx: the struct to initialize
 * Returns:	ctx
 * Author:	mbp
 * Date:	Fri Sep 20 13:11:03 1991
 */
mgglcontext *
mggl_newcontext( mgglcontext *ctx )
{
  mg_newcontext(&(ctx->mgctx));
  ctx->mgctx.devfuncs = &mgglfuncs;
  ctx->mgctx.devno = MGD_GL;
  ctx->mgctx.astk->ap_seq = 1;
  ctx->mgctx.astk->mat_seq = 1;
  ctx->mgctx.astk->light_seq = 1;
  ctx->mgctx.zfnudge = 40.e-6;
  ctx->born = 0;
  ctx->win = 0;
  ctx->d4f = c4f;
  ctx->lmcolor = LMC_COLOR;
  ctx->n3f = n3f;
  VVINIT(ctx->room, char, 180);
  ctx->GLXdisplay = NULL;
  return ctx;
}

/*-----------------------------------------------------------------------
 * Function:	mggl_findctx
 * Description: Given a GL window ID, returns the associated mg context.
 * Returns:	mgcontext * for success, NULL if none exists.
 * Author:	slevy
 * Date:	Mon Nov 11 18:33:53 CST 1991
 * Notes:	This is a public routine.
 */
mgcontext *
mggl_findctx( int winid )
{
  register struct mgcontext *mgc;

  for(mgc = _mgclist; mgc != NULL; mgc = mgc->next) {
    if(mgc->devno == MGD_GL && ((mgglcontext *)mgc)->win == winid)
	return mgc;
  }
  return NULL;
}

/*-----------------------------------------------------------------------
 * Function:	PackColorA
 * Description:	Pack a ColorA struct into an unsigned long int for
 *		  use by cpack() etc.
 * Args:	*c: the ColorA to pack
 * Returns:	the unsigned long corresponding to *c
 * Author:	mbp
 * Date:	Fri Sep 20 13:37:02 1991
 */
static unsigned long
PackColorA(ColorA *c)
{
  unsigned long val,r,g,b,a;

  r = c->r * 255;
  g = c->g * 255;
  b = c->b * 255;
  a = c->a * 255;

  val = (a << 24) | (b << 16) | (g << 8) | r;
  return(val);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.