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

This is light.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 */

/*
 *	light -
 *		Support for describing light sources and lists of 
 *		light sources.
 *
 *	Pat Hanrahan 1989
 */
#include "appearance.h"
#include "ooglutil.h"

static void norm();

/*
 * Default light is full white and position along the z-axis.
 */

static Color black = { 0.0, 0.0, 0.0 };

LtLight *
_LtSet(LtLight *light, int a1, va_list *alist)
{
    
    int attr;
    Color *co;
    Point *pt;
    char **ablock = NULL;

#define NEXT(type) OOGL_VA_ARG(type,alist,ablock)
    
    if (light == NULL) {
      /*
       * New LtLight created here.
       */
      light = OOGLNewE(LtLight, "LtCreate LtLight");
      LtDefault(light);
    }
    
    for(attr = a1; attr != LT_END; attr = NEXT(int)) {
	switch (attr) { /* parse argument list */
	  case LT_ABLOCK:
	    ablock = NEXT(char **);
	    break;
	  case LT_AMBIENT:
	    co = NEXT(Color *);
	    CoCopy(co, &light->ambient);
	    light->changed = 1;
	    break;
	  case LT_COLOR:
	    co = NEXT(Color *);
	    CoCopy(co, &light->color);
	    light->changed = 1;
	    break;
	  case LT_POSITION:
	    pt = NEXT(Point *);
	    PtCopy(pt, &light->position);
	    light->changed = 1;
	    break;
	  case LT_INTENSITY:
	    light->intensity = NEXT(double);
	    light->changed = 1;
	    break;
	  case LT_LOCATION:
	    light->location = NEXT(int);
	    light->changed = 1;
	    break;
	  default:
	    OOGLError (0, "_LtSet: undefined option: %d\n", attr);
	    return NULL;
	    break;
	}
    }
    return light;
#undef NEXT
}

LtLight *
LtCreate(int a1, ... )
{
    va_list alist;
    LtLight *light;
    
    va_start(alist,a1);
    light = _LtSet(NULL, a1, &alist);
    va_end(alist);
    return light;
}

LtLight *
LtSet(LtLight *light, int attr, ...)
{
    va_list alist;
    
    va_start(alist,attr);
    light = _LtSet(light,attr,&alist);
    va_end(alist);
    return light;
}

int
LtGet(LtLight *light, int attr, void * value)
{
    if (!light) return NULL;

    switch (attr) { 
       case LT_AMBIENT:
	*(Color *) value = light->ambient;
	break;
      case LT_COLOR:
	*(Color *) value = light->color;
	break;
      case LT_POSITION:
	*(Point *) value = light->position;
	break;
      case LT_INTENSITY:
	*(double *) value = light->intensity;
	break;
      case LT_LOCATION:
	*(int *)value = light->location;
	break;
      default:
	OOGLError (0, "LtGet: undefined option: %d\n", attr);
	return -1;
	break;
      }
    return 1;
}

void
LtDelete(LtLight *l)
{
    free(l);
}

LtLight *
LtCopy( register LtLight *l1, register LtLight *l2 )
{
    if(l2 == NULL)
	l2 = OOGLNewE(LtLight, "LtCopy LtLight");
    *l2 = *l1;		/* Don't reset the 'changed' & 'Private' fields */
    l2->next = NULL;
    /* Reset private and changed flags */
    l2->Private = 0;
    l2->changed = 1;
    return l2;
}


LtLight *
LtMerge( register LtLight *l1, register LtLight *l2 )
{
    if(l2 == NULL)
	l2 = OOGLNewE(LtLight, "LtMerge LtLight");
    *l2 = *l1;		
    /* Don't reset the 'changed' & 'Private' fields */
    l2->next = NULL;
    return l2;
}

LtLight *
LtDefault( LtLight *light ) 
{
  static HPoint3 defposition = { 0.0, 0.0, 1.0, 0.0 };
  static Color deflight = { 1.0, 1.0, 1.0 };

  light->next = NULL;
  light->intensity = 1.0;
  light->ambient = black;
  light->color = deflight;
  light->position = defposition;
  light->location = LTF_GLOBAL;
  light->Private = 0;
  light->changed = 1;
}

/*
 * Set the intensity, color and position of a light.
 */
void
LtProperties( LtLight *light, float intensity, Color *color, Point *position )
{
    light->intensity = intensity;
    light->color = *color;
    light->position = *position;
    light->location = LTF_GLOBAL;

    light->changed = 1;
}

/*
 * Load a lighting description from a file.
 */
LtLight *
  LtLoad(LtLight *li, char *name)
{
  FILE *f;
  
  if(name == NULL || (f = fopen(name, "r")) == NULL) {
    OOGLError(1, "Can't find light file %s: %s", name, sperror());
    return NULL;
  }
  li = LtFLoad(li, f, name);
  fclose(f);
  return li;
}

/*
 * Load Light from file.
 * Syntax:
 *	< "filename_containing_material"	[or]
 *    {   keyword  value   keyword  value   ...  }
 *
 */

LtLight *
LtFLoad(lite, f, fname)
    LtLight *lite;
    FILE *f;
    char *fname;	/* Used for error msgs, may be NULL */
{
  char *w;
  register int i,j;
  float v[4];
  int brack = 0;
  static char *lkeys[] = {
    "ambient", "color", "position", "location", "global", "camera", "local"
    };
  static short largs[] = { 3, 3, 4, 0, ~LTF_GLOBAL, ~LTF_CAMERA, ~LTF_LOCAL };
  int got;
  LtLight l;
  
  LtDefault(&l);
  
  for(;;) {
    switch(fnextc(f, 0)) {
    case '<':
      fgetc(f);
      if(LtLoad(&l, fdelimtok("(){}", f, 0)) == NULL) return NULL;
      if(!brack) goto done;
      break;
    case '{': brack++; fgetc(f); break;
    case '}': if(brack) { fgetc(f); } goto done;
    default:
      w = ftoken(f, 0);
      if(w == NULL)
	goto done;
      
      for(i = sizeof(lkeys)/sizeof(lkeys[0]); --i >= 0; )
	if(!strcmp(w, lkeys[i]))
	  break;
      
      if( i < 0) {
	OOGLSyntax(f, "Reading light from %s: unknown keyword %s",fname,w);
	return NULL;
      } else if( largs[i] > 0 && (got=fgetnf(f, largs[i], v, 0)) != largs[i] ) {
	OOGLSyntax(f, "Reading light from %s: \"%s\" expects %d values, got %d",
		  fname, w, largs[i], got);
	return NULL;
      }
      switch(i) {
      case 0: l.ambient = *(Color *)v; break;
      case 1: l.color = *(Color *)v; 
	norm( &l.color, &l.intensity ); break;
      case 2: l.position = *(Point *)v; break;
      case 3: break;
      default: l.location = ~largs[i]; break;
      }
    }
  }
 done:
  lite = LtCopy(&l, lite);
  return lite;
}

void
LtFSave( LtLight *l, FILE *f )
{
    fprintf(f,"\t\tambient %f %f %f\n",
	l->ambient.r,
	l->ambient.g,
	l->ambient.b);
    fprintf(f,"\t\tcolor %f %f %f\n",
	l->intensity*l->color.r,
	l->intensity*l->color.g,
	l->intensity*l->color.b);
    fprintf(f,"\t\tposition %f %f %f %f\n",
	l->position.x,
	l->position.y,
	l->position.z,
	l->position.w);
    if(l->location != LTF_GLOBAL)
	fprintf(f, "\t\tlocation %s\n",
		l->location == LTF_CAMERA ? "camera" : "local");
    /*
    fprintf(f,"intensity %f\n", la->intensity);
    */
}

/*
 * Remove a light from a light source list. However,
 * this routine doesn't actually delete it.
 */
void
LtRemove( LmLighting *lighting, LtLight *light )
{
    LtLight *l;

    if( lighting->lights == light ) {
	lighting->lights = lighting->lights->next;
    }
    else {
	for( l=lighting->lights; l->next; l=l->next )
	    if( l->next == light ) {
		l->next = l->next->next;
		break;
	    }
    }
}

void
LtAppend( LmLighting *lighting, LtLight *light )
{
    LtLight *l;

    if( lighting->lights ) {
	for( l=lighting->lights; l->next; l=l->next )
	    ;
	l->next = light;
    }
    else
	lighting->lights = light;
}

/*
 * Create a list of lights and a lighting model.
 */
LmLighting *
_LmSet(LmLighting *lgt, int a1, register va_list *alist)
{
    int attr;
    Color *co;
    LtLight *la;
    int v, mask;
    char **ablock = NULL;

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

    if (!alist) return lgt;
    if (lgt == NULL) {
      /*
       * New Lighting created here.
       */
      lgt =  OOGLNewE(LmLighting, "LmCreate Lighting");
      LmDefault(lgt);
    }

    for(attr = a1; attr != LM_END; attr = NEXT(int)) {
      switch (attr) { /* parse argument list */
      case LM_ABLOCK:
	ablock = NEXT(char **);
	break;
      case LM_LtSet:
	la = ablock ? LtSet(NULL, LT_ABLOCK, ablock)
	  : _LtSet(NULL, va_arg(*alist, int), alist);
	la->next = lgt->lights;
	lgt->lights = la;
	break;
      case LM_LIGHT:
	lgt->lights = NEXT(LtLight *);
	break;
      case LM_REPLACELIGHTS:
	if (NEXT(int))
	  lgt->valid |= LMF_REPLACELIGHTS;
	else
	  lgt->valid &= ~LMF_REPLACELIGHTS;
	break;
      case LM_AMBIENT:
	co = NEXT(Color *);
	CoCopy(co, &lgt->ambient);
	lgt->valid |= LMF_AMBIENT;
	break;
      case LM_LOCALVIEWER:
	lgt->localviewer = NEXT(double);
	lgt->valid |= LMF_LOCALVIEWER;
	break;
      case LM_ATTENC:
	lgt->attenconst = NEXT(double);
	lgt->valid |= LMF_ATTENC;
	break;
      case LM_ATTENM:
	lgt->attenmult = NEXT(double);
	lgt->valid |= LMF_ATTENM;
	break;
      case LM_OVERRIDE:
	lgt->override |= NEXT(int);
	break;
      case LM_NOOVERRIDE:
	lgt->override &= ~NEXT(int);
	break;
      case LM_INVALID:
	lgt->valid &= ~NEXT(int);
	break;
      default:
	OOGLError (0, "_LmSet: undefined option: %d\n", attr);
	return NULL;
	break;
      }
    }

    return lgt;

#undef NEXT
}

LmLighting *
LmCreate(int attr, ... )
{
    va_list alist;
    LmLighting *lgt;
     
    va_start(alist,attr);
    lgt = _LmSet(NULL, attr, &alist);
    va_end(alist);
    return lgt;
}


LmLighting *
LmSet(LmLighting *lgt, int a1, ... )
{
    va_list alist;
    va_start(alist,a1);
    lgt = _LmSet(lgt, a1, &alist);
    va_end(alist);
    return lgt;
}


int
LmGet(LmLighting *lgt, int attr, void *value)
{
    if (!lgt) return NULL;

    switch (attr) {
      case LM_LIGHT:
	*(LtLight **) value = lgt->lights;
	break;
      case LM_REPLACELIGHTS:
	*(int*)value = lgt->valid & LMF_REPLACELIGHTS;
	break;
      case LM_AMBIENT:
	*(Color *) value = lgt->ambient;
	break;
      case LM_LOCALVIEWER:
	*(double *) value = lgt->localviewer;
	break;
      case LM_ATTENC:
	*(double *) value = lgt->attenconst;
	break;
      case LM_ATTENM:
	*(double *) value = lgt->attenmult;
	break;
      case LM_ATTEN2:
	*(double *) value = lgt->attenmult2;
	break;
      case LM_OVERRIDE:
      case LM_NOOVERRIDE:
	*(int *) value = lgt->override;
	break;
      case LM_VALID:
      case LM_INVALID:
	*(int *) value = lgt->valid;
	break;
      default:
	OOGLError (0, "LmGet: undefined option: %d\n", attr);
	return -1;
	break;
      }
    return 1;
}

LmLighting *
LmMerge(LmLighting *src, LmLighting *dst, int mergeflags)
{
    unsigned int mask;

    if(dst == NULL)
	return LmCopy(src, NULL);

    mask = src ?
	(mergeflags & APF_OVEROVERRIDE) ?
		src->valid : src->valid & ~(dst->override &~ src->override)
	: 0;

    if(src == NULL || (mask == 0 && src->lights == NULL)) {
	RefIncr((Ref *)dst);
	return dst;
    }

    if(mask && !(mergeflags & APF_INPLACE))
	dst = LmCopy(dst, NULL);
    dst->changed |= src->changed;
    dst->valid = (src->valid & mask) | (dst->valid & ~mask);
    dst->override = (src->override & mask) | (dst->override & ~mask);
    if(mask & LMF_LOCALVIEWER) dst->localviewer = src->localviewer;
    if(mask & LMF_AMBIENT) dst->ambient = src->ambient;
    if(mask & LMF_ATTENC) dst->attenconst = src->attenconst;
    if(mask & LMF_ATTENM) dst->attenmult = src->attenmult;
    if(mask & LMF_ATTEN2) dst->attenmult2 = src->attenmult2;
    if(src->lights != dst->lights) {
	if((mask & LMF_REPLACELIGHTS) && dst->lights) {
	    /* LMF_REPLACELIGHTS: replace lights rather than merging with them */
	    LtDeletelist(dst->lights);
	    dst->lights = NULL;
	}
	if(src->lights) {
	    LtLight *sl;
	    register LtLight *hl;

	    hl = sl = LtCopylist(src->lights, 1); /* merge not copy */
	    while(sl->next)
		sl = sl->next;
	    sl->next = dst->lights;
	    dst->lights = hl;
	}
    }

    RefIncr((Ref *)dst);
    return dst;
}

LmLighting *
LmCopy(register LmLighting *from, register LmLighting *to)
{
  if (!from) return NULL;
    if(to == NULL) {
	to = OOGLNewE(LmLighting, "LmCopy LmLighting");
	*to = *from;
	RefInit((Ref *)to, LIGHTINGMAGIC);
	to->Private = 0;
	to->lights = LtCopylist(from->lights, 0);
    } else if (from != to) {
	Ref r;
	r = *(Ref *)to;
	LtDeletelist(to->lights);
	*to = *from;
	to->lights = LtCopylist(from->lights, 0);
	*(Ref *)to = r;
    }

    return to;
}

void
LmDefault( LmLighting *l ) 
{
  RefInit((Ref *)l, LIGHTINGMAGIC);
  l->valid = l->override = 0;
  l->ambient = black;
  l->localviewer = 1;
  l->attenconst = 0.0;
  l->attenmult = 0.0;
  l->lights = NULL;
  l->changed = 1;
  l->Private = 0;
}

/*
 * Delete a list of lights and all the lights in the list.
 */
void
LmDelete(LmLighting *lm)
{
    register LtLight *l, *nl;

    if(lm == NULL || RefDecr((Ref *)lm) > 0)
	return;
    LtDeletelist(lm->lights);
    free(lm);
}

void
LtDeletelist(register LtLight *l)
{
    register LtLight *nl;

    while(l) {
	nl = l->next;
	LtDelete(l);
	l = nl;
    }
}

LtLight *
LtCopylist(LtLight *l, int mergeflag)
{
    register LtLight **tailp;
    LtLight *new;

    for(tailp = &new; l != NULL; l = l->next) {
        if (mergeflag & APF_INPLACE) 
	  *tailp = LtMerge(l, NULL);
        else 
	  *tailp = LtCopy(l, NULL);
	tailp = &(*tailp)->next;
    }
    *tailp = NULL;
    return new;
}

#define max(a,b) (a)>(b)?(a):(b)

static void
norm( color, coeff )
    Color *color;
    float *coeff;
{
    *coeff = max(color->r, color->g);
    *coeff = max(color->b, *coeff);

    if( *coeff != 0.0 ) {
	color->r /= *coeff;
	color->g /= *coeff;
	color->b /= *coeff;
    }
}

/*
 * Load a lighting description from a file.
 */
LmLighting *
LmLoad(LmLighting *li, char *name)
{
    FILE *f;
    LmLighting *ls;

    f = fopen(name,"r");
    if(!f) {
	perror(name);
	return 0;
    }
    ls = LmFLoad(li, f, name);
    fclose(f);
    return ls;
}


/*
 * Load Lighting from file.
 * Syntax:
 *	< "filename_containing_material"	[or]
 *    {   keyword  value   keyword  value   ...  }
 *
 *   Each keyword may be prefixed by "*", indicating that its value should
 *   override corresponding settings in child objects.  [By default,
 *   children's appearance values supercede those of their parents.]
 *
 */

LmLighting *
  LmFLoad(lgt, f, fname)
LmLighting *lgt;
FILE *f;
char *fname;	/* Used for error msgs, may be NULL */
{
  char *w;
  register int i;
  float v[3];
  int brack = 0;
  int over, not;
  static char *lkeys[] = {
    "ambient", "localviewer", "attenconst", "attenmult", "attenmult2", "light",
    "replacelights" 
    };
  static char largs[] = { 3, 1, 1, 1, 1, 0, 0};
  static unsigned short lbits[] = {
    LMF_AMBIENT, LMF_LOCALVIEWER, LMF_ATTENC, LMF_ATTENM, LMF_ATTEN2, 0, LMF_REPLACELIGHTS
    };
  int got;
  LmLighting l;
  LtLight lite;
  LtLight *last, *new;
  
  LmDefault(&l);
  
  over = not = 0;
  new = l.lights;
  last = NULL;

  for(;;) {
    switch(fnextc(f, 0)) {
    case '<':
      fgetc(f);
      if(LmLoad(&l, ftoken(f, 0)) == NULL) return NULL;
      if(!brack) goto done;
      break;
    case '{': brack++; fgetc(f); break;
    case '}': if(brack) { fgetc(f); } goto done;
    case '*': over = 1; fgetc(f); break;		/* 'override' prefix */
    case '!': not = 1; fgetc(f); break;
    default:
      w = ftoken(f, 0);
      if(w == NULL)
	return LmCopy(&l, lgt);
      /* break;	*/				/* done */
      
      for(i = sizeof(lkeys)/sizeof(lkeys[0]); --i >= 0; )
	if(!strcmp(w, lkeys[i]))
	  break;
      
      if( i < 0) {
	OOGLError(1, "LmFLoad: %s: unknown lighting keyword %s",fname,w);
	return NULL;
      } else if( !not && (got=fgetnf(f, largs[i], v, 0)) != largs[i] ) {
	OOGLError(1, "LmFLoad: %s: \"%s\" expects %d values, got %d",
		  fname, w, largs[i], got);
	return NULL;
      }
      
      if(not) {
	if(!over) l.valid &= ~lbits[i];
	l.override &= ~lbits[i];
      } else {
	l.valid |= lbits[i];
	if(over) l.override |= lbits[i];
	switch(i) {
	case 0: l.ambient = *(Color *)v; break;
	case 1: l.localviewer = v[0]; break;
	case 2: l.attenconst = v[0]; break;
	case 3: l.attenmult = v[0]; break;
	case 4: l.attenmult2 = v[0]; break;
	case 5:
	  LtFLoad( &lite, f, fname );
	  new = LtCreate(LT_END);
	  if (!last)
	    l.lights = new;
	  else
	    last->next = new;
	  LtCopy( &lite, new);
	  last = new;
	  new = new->next;
	  break;
	}
      }
      over = not = 0;
    }
  }
 done:
  return LmCopy(&l, lgt);
}


/*
 * Save a light description in a file.
 */

LmFSave(LmLighting *li, FILE *f, char *fname)
{
    register LtLight *la;

    fprintf(f,"\tambient %g %g %g\n", 
	li->ambient.r,
	li->ambient.g,
	li->ambient.b);
    fprintf(f,"\tlocalviewer %d\n",li->localviewer);
    fprintf(f,"\tattenconst %g\n",li->attenconst);
    fprintf(f,"\tattenmult %g\n",li->attenmult);
    if(li->valid & LMF_ATTEN2) fprintf(f,"\tattenmult2 %g\n",li->attenmult2);
    if (li->valid & LMF_REPLACELIGHTS) fprintf(f,"\treplacelights\n");
    la = li->lights;
    while(la) {
	fprintf(f, "\tlight {\n");
	LtFSave( la, f );
	fprintf(f, "\t}\n");
	la = la->next;
    }
}

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