1 /* Copyright (C) 1992-1998 The Geometry Center
2  * Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips
3  * Copyright (C) 2006-2007 Claus-Justus Heine
4  *
5  * This file is part of Geomview.
6  *
7  * Geomview is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published
9  * by the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * Geomview is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with Geomview; see the file COPYING.  If not, write
19  * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
20  * USA, or visit http://www.gnu.org.
21  */
22 
23 #if HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26 
27 #include "appearance.h"
28 #include "transobj.h"
29 #include "handleP.h"
30 #include <string.h>
31 #include <sys/stat.h>
32 
33 extern int Apsavepfx(int valid, int override, int mask, char *keyword,
34 		     FILE *f, Pool *p);
35 
36 Material *
_MtSet(Material * mat,int attr1,va_list * alist)37 _MtSet(Material *mat, int attr1, va_list *alist)
38 {
39   int attr;
40 
41 #define NEXT(type) va_arg(*alist, type)
42 
43   if (mat == NULL) {
44     /*
45      * New Material created here.
46      */
47     mat = OOGLNewE(Material, "new Material");
48     MtDefault(mat);
49   }
50 
51   for (attr = attr1; attr != MT_END; attr = NEXT(int)) {
52     switch (attr) { /* parse argument list */
53     case MT_AMBIENT:
54       mat->ambient = *NEXT(Color *);
55       mat->valid |= MTF_AMBIENT;
56       break;
57     case MT_DIFFUSE:
58     {
59       Color tmp;
60       tmp = *NEXT(Color *);
61       mat->diffuse.r = tmp.r;
62       mat->diffuse.g = tmp.g;
63       mat->diffuse.b = tmp.b;
64       mat->valid |= MTF_DIFFUSE;
65       break;
66     }
67     case MT_SPECULAR:
68       mat->specular = *NEXT(Color *);
69       mat->valid |= MTF_SPECULAR;
70       break;
71     case MT_EMISSION:
72       mat->emission = *NEXT(Color *);
73       mat->valid |= MTF_EMISSION;
74       break;
75     case MT_ALPHA:
76       mat->diffuse.a = NEXT(double);
77       mat->valid |= MTF_ALPHA;
78       break;
79     case MT_Ka:
80       mat->ka = NEXT(double);
81       mat->valid |= MTF_Ka;
82       break;
83     case MT_Kd:
84       mat->kd = NEXT(double);
85       mat->valid |= MTF_Kd;
86       break;
87     case MT_Ks:
88       mat->ks = NEXT(double);
89       mat->valid |= MTF_Ks;
90       break;
91     case MT_SHININESS:
92       mat->shininess = NEXT(double);
93       mat->valid |= MTF_SHININESS;
94       break;
95     case MT_EDGECOLOR:
96       mat->edgecolor = *NEXT(Color *);
97       mat->valid |= MTF_EDGECOLOR;
98       break;
99     case MT_NORMALCOLOR:
100       mat->normalcolor = *NEXT(Color *);
101       mat->valid |= MTF_NORMALCOLOR;
102       break;
103     case MT_INVALID:
104       mat->valid &= ~NEXT(int);
105       break;
106     case MT_OVERRIDE:
107       mat->override |= NEXT(int);
108       break;
109     case MT_NOOVERRIDE:
110       mat->override &= ~NEXT(int);
111       break;
112     default:
113       OOGLError (0, "_MtSet: undefined option: %d\n", attr);
114       return NULL;
115       break;
116     }
117   }
118 
119   return mat;
120 
121 #undef NEXT
122 }
123 
124 Material *
MtCreate(int a1,...)125 MtCreate(int a1, ... )
126 {
127   va_list alist;
128   Material *mat;
129 
130   va_start(alist,a1);
131   mat = _MtSet(NULL, a1, &alist);
132   va_end(alist);
133   return mat;
134 }
135 
136 Material *
MtSet(Material * mat,int attr,...)137 MtSet(Material *mat, int attr, ... )
138 {
139   va_list alist;
140 
141   va_start(alist,attr);
142   mat = _MtSet(mat,attr,&alist);
143   va_end(alist);
144   return mat;
145 }
146 
147 int
MtGet(Material * mat,int attr,void * value)148 MtGet(Material *mat, int attr, void * value)
149 {
150   if (mat == NULL)
151     return -1;
152 
153   switch (attr) {
154   case MT_AMBIENT:
155     *(Color *)value = mat->ambient;
156     break;
157   case MT_DIFFUSE:
158   {
159     ((Color *)value)->r = mat->diffuse.r;
160     ((Color *)value)->g = mat->diffuse.g;
161     ((Color *)value)->b = mat->diffuse.b;
162     break;
163   }
164   case MT_SPECULAR:
165     *(Color *)value = mat->specular;
166     break;
167   case MT_EMISSION:
168     *(Color *) value = mat->emission;
169     break;
170   case MT_ALPHA:
171     *(double *) value = mat->diffuse.a;
172     break;
173   case MT_Ka:
174     *(double *) value = mat->ka;
175     break;
176   case MT_Kd:
177     *(double *) value = mat->kd;
178     break;
179   case MT_Ks:
180     *(double *) value = mat->ks;
181     break;
182   case MT_SHININESS:
183     *(double *) value = mat->shininess;
184     break;
185   case MT_EDGECOLOR:
186     *(Color *)value = mat->edgecolor;
187     break;
188   case MT_NORMALCOLOR:
189     *(Color *)value = mat->normalcolor;
190     break;
191   case MT_OVERRIDE:
192   case MT_NOOVERRIDE:
193     *(int *) value = mat->override;
194     break;
195   case MT_VALID:
196   case MT_INVALID:
197     *(int *) value = mat->valid;
198     break;
199   default:
200     OOGLError (0, "MtGet: undefined option: %d\n", attr);
201     return -1;
202     break;
203   }
204 
205   return 1;
206 }
207 
208 void
MtDelete(Material * mat)209 MtDelete(Material *mat)
210 {
211   if (mat && RefDecr((Ref *)mat) <= 0) {
212     if (mat->magic != MATMAGIC) {
213       OOGLError(1, "MtDelete(%x) of non-Material: magic %x != %x",
214 		mat, mat->magic, MATMAGIC);
215       return;
216     }
217     mat->magic = MATMAGIC ^ 0x80000000;
218     OOGLFree(mat);
219   }
220 }
221 
222 Material *
MtDefault(Material * mat)223 MtDefault( Material *mat )
224 {
225   memset(mat, 0, sizeof(Material));
226   RefInit((Ref *)mat, MATMAGIC);
227   mat->valid = mat->override = 0;
228   mat->diffuse.a = 1.0;
229   mat->Private = 0;
230   mat->changed = 1;
231   return mat;
232 }
233 
234 
235 Material *
MtCopy(Material * src,Material * dst)236 MtCopy( Material *src, Material *dst )
237 {
238   if (!src) return NULL;
239   if (dst == NULL)
240     dst = OOGLNewE(Material, "MtCopy: Material");
241   *dst = *src;
242   dst->Private = 0;
243   RefInit((Ref *)dst, MATMAGIC);
244   dst->changed = 1;
245   return dst;
246 }
247 
248 #ifndef max
249 # define max(a,b) ((a) > (b) ? (a) : (b))
250 #endif
251 
252 #if 0
253 static void
254 norm( color, coeff )
255      Color *color;
256      float *coeff;
257 {
258   *coeff = max(color->r, color->g);
259   *coeff = max(color->b, *coeff);
260 
261   if ( *coeff != 0.0 ) {
262     color->r /= *coeff;
263     color->g /= *coeff;
264     color->b /= *coeff;
265   }
266 }
267 #endif
268 
269 /*
270  * MtMerge(src, dst, mergeflags)
271  * Merge Material values:  src into dst, controlled by flag.
272  * If "inplace" is true, changes are made in dst itself; otherwise,
273  * the dst material is copied if any changes need be made to it.
274  * The returned Material's reference count is incremented as appropriate;
275  * thus the caller should MtDelete() the returned Material when done.
276  */
277 Material *
MtMerge(Material * src,Material * dst,int mergeflags)278 MtMerge(Material *src, Material *dst, int mergeflags)
279 {
280   int mask;
281 
282   if (dst == NULL)
283     return MtCopy(src, NULL);
284 
285   /* Fields to merge in */
286   mask = src ?
287     (mergeflags & APF_OVEROVERRIDE) ?
288     src->valid : src->valid & ~(dst->override &~ src->override)
289     : 0;
290 
291   if (mergeflags & APF_INPLACE)
292     RefIncr((Ref *)dst);
293   else
294     dst = MtCopy(dst, NULL);
295 
296   if (mask == 0)			/* No changes to dst */
297     return dst;
298 
299   dst->changed |= src->changed;
300   dst->valid = (src->valid & mask) | (dst->valid & ~mask);
301   dst->override = (src->override & mask) | (dst->override & ~mask);
302   if (mask & MTF_EMISSION) dst->emission = src->emission;
303   if (mask & MTF_AMBIENT) dst->ambient = src->ambient;
304   if (mask & MTF_DIFFUSE) {
305     dst->diffuse.r = src->diffuse.r;
306     dst->diffuse.g = src->diffuse.g;
307     dst->diffuse.b = src->diffuse.b;
308   }
309   if (mask & MTF_SPECULAR) dst->specular = src->specular;
310   if (mask & MTF_Ka) dst->ka = src->ka;
311   if (mask & MTF_Kd) dst->kd = src->kd;
312   if (mask & MTF_Ks) dst->ks = src->ks;
313   if (mask & MTF_ALPHA) dst->diffuse.a = src->diffuse.a;
314   if (mask & MTF_SHININESS) dst->shininess = src->shininess;
315   if (mask & MTF_EDGECOLOR) dst->edgecolor = src->edgecolor;
316   if (mask & MTF_NORMALCOLOR) dst->normalcolor = src->normalcolor;
317   return dst;
318 }
319 
MtSave(Material * mat,char * name)320 int MtSave(Material *mat, char *name)
321 {
322   FILE *f;
323   int ok;
324 
325   f = fopen(name,"w");
326   if (!f) {
327     perror(name);
328     return -1;
329   }
330   ok = MtFSave(mat, f, NULL);
331   fclose(f);
332   return ok;
333 }
334 
MtLoad(Material * mat,char * name)335 Material *MtLoad(Material *mat, char *name)
336 {
337     IOBFILE *f = iobfopen(name,"rb");
338 
339     if (f == NULL) {
340 	OOGLError(0, "MtLoad: can't open %s: %s", name, sperror());
341 	return NULL;
342     }
343 
344     mat = MtFLoad(mat, f, name);
345     iobfclose(f);
346     return mat;
347 }
348 
349 /*
350  * Load Material from file.
351  * Syntax:
352  *	< "filename_containing_material"	[or]
353  *    {   keyword  value   keyword  value   ...  }
354  *
355  *   Each keyword may be prefixed by "*", indicating that its value should
356  *   override corresponding settings in child objects.  [By default,
357  *   children's appearance values supercede those of their parents.]
358  *
359  *  Note: don't overwrite ka, kd, ks if they're already set when we read in
360  *        the corresponding color.
361  */
362 static char *mt_kw[] = {
363   "shininess",	"ka",		"kd",		"ks",		"alpha",
364   "backdiffuse", "emission",	"ambient",	"diffuse",	"specular",
365   "edgecolor",	"normalcolor",	"material"
366 };
367 static unsigned short mt_flags[] = {
368   MTF_SHININESS,  MTF_Ka,	MTF_Kd,		MTF_Ks,		MTF_ALPHA,
369   MTF_EMISSION,   MTF_EMISSION, MTF_AMBIENT, MTF_DIFFUSE, MTF_SPECULAR,
370   MTF_EDGECOLOR, MTF_NORMALCOLOR, 0
371 };
372 static char mt_args[] = { 1,1,1,1,1,  3,3,3,3,3,3,3, 0 };
373 
374 /* fname used for error msgs, may be NULL */
375 Material *
MtFLoad(Material * mat,IOBFILE * f,char * fname)376 MtFLoad(Material *mat, IOBFILE *f, char *fname)
377 {
378   char *w;
379   int i;
380   float v[3];
381   int brack = 0;
382   int over, not;
383   int got;
384   Material m;
385 
386   MtDefault(&m);
387 
388   over = not = 0;
389   for (;;) {
390     switch(iobfnextc(f, 0)) {
391     case '<':
392       iobfgetc(f);
393       if (MtLoad(&m, iobfdelimtok("{}()", f, 0)) == NULL) return NULL;
394       if (!brack) goto done;
395       break;
396     case '{': brack++; iobfgetc(f); break;
397     case '}': if (brack) { iobfgetc(f); } goto done;
398     case '*': over = 1; iobfgetc(f); break;		/* 'override' prefix */
399     case '!': not = 1; iobfgetc(f); break;
400     default:
401       w = iobfdelimtok("{}()", f, 0);
402       if (w == NULL)
403 	return MtCopy(&m, mat);
404       /* break;	*/				/* done */
405 
406       for (i = sizeof(mt_kw)/sizeof(mt_kw[0]); --i >= 0; )
407 	if (!strcmp(w, mt_kw[i]))
408 	  break;
409 
410       if ( i < 0) {
411 	OOGLError(1, "MtFLoad: %s: unknown material keyword %s",fname,w);
412 	return NULL;
413       } else if ( !not && (got=iobfgetnf(f, mt_args[i], v, 0)) != mt_args[i] ) {
414 	OOGLError(1, "MtFLoad: %s: \"%s\" expects %d values, got %d",
415 		  fname, w, mt_args[i], got);
416 	return NULL;
417       }
418 
419       if (not) {
420 	if (!over) m.valid &= ~mt_flags[i];
421 	m.override &= ~mt_flags[i];
422       } else {
423 	switch(i) {
424 	case 0: m.shininess = v[0]; break;
425 	case 1: m.ka = v[0]; break;
426 	case 2: m.kd = v[0]; break;
427 	case 3: m.ks = v[0]; break;
428 	case 4: m.diffuse.a = v[0]; break;
429 	case 5: case 6: memcpy(&m.emission, v, sizeof(Color)); break;
430 	case 7: memcpy(&m.ambient, v, sizeof(Color)); break;
431 	case 8: memcpy(&m.diffuse, v, sizeof(Color)); break;
432 	case 9: memcpy(&m.specular, v, sizeof(Color)); break;
433 	case 10: memcpy(&m.edgecolor, v, sizeof(Color)); break;
434 	case 11: memcpy(&m.normalcolor, v, sizeof(Color)); break;
435 	}
436 	m.valid |= mt_flags[i];
437 	if (over) m.override |= mt_flags[i];
438       }
439       over = not = 0;
440     }
441   }
442  done:
443   return MtCopy(&m, mat);
444 }
445 
MtFSave(Material * mat,FILE * f,Pool * p)446 int MtFSave(Material *mat, FILE *f, Pool *p)
447 {
448   int i;
449   float v;
450   Color *c;
451 
452   for (i = 0; i < (int)(sizeof(mt_kw)/sizeof(mt_kw[0])); i++) {
453     if (Apsavepfx(mat->valid, mat->override, mt_flags[i], mt_kw[i], f, p)) {
454       switch(mt_flags[i]) {
455       case MTF_Ka: v = mat->ka; goto pfloat;
456       case MTF_Kd: v = mat->kd; goto pfloat;
457       case MTF_Ks: v = mat->ks; goto pfloat;
458       case MTF_SHININESS: v = mat->shininess; goto pfloat;
459       case MTF_ALPHA: v = mat->diffuse.a; goto pfloat;
460       pfloat:
461 	fprintf(f, "%f\n", v);
462 	break;
463 
464       case MTF_DIFFUSE: c = (Color *)(void *)&mat->diffuse; goto pcolor;
465       case MTF_AMBIENT: c = &mat->ambient; goto pcolor;
466       case MTF_EMISSION: c = &mat->emission; goto pcolor;
467       case MTF_SPECULAR: c = &mat->specular; goto pcolor;
468       case MTF_EDGECOLOR: c = &mat->edgecolor; goto pcolor;
469       case MTF_NORMALCOLOR: c = &mat->normalcolor; goto pcolor;
470       pcolor:
471 	fprintf(f, "%f %f %f\n", c->r, c->g, c->b);
472 	break;
473       }
474     }
475   }
476   return ferror(f);
477 }
478 
479 /*
480  * Local Variables: ***
481  * mode: c ***
482  * c-basic-offset: 2 ***
483  * End: ***
484  */
485