1 /* Copyright (C) 1992-1998 The Geometry Center
2  * Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips
3  *
4  * This file is part of Geomview.
5  *
6  * Geomview is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * Geomview is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with Geomview; see the file COPYING.  If not, write
18  * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
19  * USA, or visit http://www.gnu.org.
20  */
21 
22 #if HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25 
26 #if 0
27 static char copyright[] = "Copyright (C) 1992-1998 The Geometry Center\n\
28 Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips";
29 #endif
30 
31 
32 /* Authors: Stuart Levy, Tamara Munzner, Mark Phillips */
33 
34 #include "lights.h"
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <math.h>
38 #include <string.h>
39 #include "geom.h"
40 #include "list.h"
41 #include "geomclass.h"
42 #include "3d.h"
43 #include "pick.h"
44 #include "common.h"
45 #include "drawer.h"
46 #include "event.h"
47 #include "ui.h"
48 #include "motion.h"
49 #include "mouse.h"
50 
51 #include "inst.h"
52 
53 static Transform Tincr;
54 static int updateproc(Geom *g);
55 static int lightedit(Event *event);
56 static void normalize_color( Color *color, float *coeff );
57 static void set_ambient(Color *color, float intensity);
58 static void build_light_rack(LmLighting *lgt);
59 static void pick_light(Event *ev);
60 
61 static void mouse2world(Point *axis, int objid);
62 void Rotation(double dx, double dy, Transform rot, int *target);
63 
64 static void lxform(Transform t, Point3 axis);
65 
66 typedef struct LightRack {
67   Geom *list;
68   Geom **lightgeoms;
69   LtLight **lights;
70   int lightcount;
71   LmLighting *lgt;
72 } LightRack;
73 
74 Geom *make_light(Point3 axis, Color *color);
75 static int countlights(LmLighting *lm);
76 static Geom *ray = NULL;
77 /*static int inited = 0; currently unused */
78 static LightRack lr;
79 static int lr_id = NOID;
80 static int apseq = -1;
81 
82 static char raytxt[] = "VECT \
83  10 20 0 \
84  2 2 2 2 2 2 2 2 2 2 \
85  0 0 0 0 0 0 0 0 0 0 \
86  .050 .000 0	 .160 .000 10 \
87  .040 .029 0	 .129 .094 10 \
88  .015 .047 0	 .049 .152 10 \
89 -.015 .047 0	 -.049 .152 10 \
90 -.040 .029 0	 -.129 .094 10 \
91 -.050 .000 0	 -.160 .000 10 \
92 -.040 -.029 0	 -.129 -.094 10 \
93 -.015 -.047 0	 -.049 -.152 10 \
94  .015 -.047 0	 .049 -.152 10 \
95  .040 -.029 0	 .129 -.094 10";
96 
97 
clight_init()98 void clight_init()
99 {
100   Appearance *ap = drawerstate.ap;
101   LmLighting *lgt;
102   IOBFILE *f;
103 
104   f = iobfileopen(fmemopen(raytxt, strlen(raytxt), "rb"));
105   if (f) {
106     ray = GeomFLoad(f, "built-in light ray");
107     if (! ray)
108       printf("can't construct built-in light ray\n");
109   }
110   else
111     printf("can't fstropen\n");
112   iobfclose(f);
113 
114   lr.lights = NULL;
115   lr.lightgeoms = NULL;
116   lr.lightcount = 0;
117   lr.list = NULL;
118 
119   ApGet(ap, AP_LGT, &lgt);
120   if (lgt == NULL) {
121     printf("current appearance has no lighting !!!\n");
122   }
123   apseq = drawerstate.apseq;
124   build_light_rack(lgt);
125 
126 }
127 
128 
colmult(c,scalar)129 void colmult( c, scalar)
130      Color *c;
131      float scalar;
132 {
133   c->r = c->r * scalar;
134   c->g = c->g * scalar;
135   c->b = c->b * scalar;
136 }
137 
138 
139 
140 Color *
light_color()141 light_color()
142 {
143   return &( lr.lights[uistate.current_light]->color );
144 }
145 
146 float
light_intensity()147 light_intensity()
148 {
149   return lr.lights[uistate.current_light]->intensity;
150 }
151 
set_light_intensity(float intens)152 void set_light_intensity(float intens)
153 {
154   lr.lights[uistate.current_light]->intensity = intens;
155   if (uistate.current_light == 0)
156     set_ambient( &(lr.lights[0]->color), lr.lights[0]->intensity );
157   else {
158     set_beam_color();
159   }
160   apseq = ++drawerstate.apseq;
161   drawerstate.changed = true;
162 }
163 
set_beam_color()164 void set_beam_color()
165 {
166   Color scaledcol;
167 
168   CoCopy( &lr.lights[uistate.current_light]->color, &scaledcol);
169   colmult( &scaledcol, light_intensity());
170   ApSet(lr.lightgeoms[uistate.current_light]->ap,
171 	AP_MtSet, MT_EDGECOLOR, &scaledcol, MT_END, AP_END);
172 }
173 
set_light_color(Color * color)174 void set_light_color(Color *color)
175 {
176   lr.lights[uistate.current_light]->color = *color;
177   if (uistate.current_light == 0)
178     /* ambient */
179     set_ambient( &(lr.lights[0]->color), lr.lights[0]->intensity );
180   else {
181     /* not ambient --- also set color of light beam */
182     set_beam_color();
183   }
184   apseq = ++drawerstate.apseq;
185   drawerstate.changed = true;
186 }
187 
set_light(int lightno)188 void set_light(int lightno)
189 {
190   uistate.current_light = lightno;
191   set_light_display(lightno);
192 }
193 
light_count()194 int light_count()
195 {
196   return lr.lightcount;
197 }
198 
199 static void
set_ambient(Color * color,float intensity)200 set_ambient(Color *color, float intensity)
201 {
202   Color scaledcolor;
203 
204   CoCopy(color, &scaledcolor);
205   colmult(&scaledcolor, intensity);
206   ApSet(drawerstate.ap, AP_LmSet, LM_AMBIENT, &scaledcolor, LM_END, AP_END);
207 }
208 
209 /*-----------------------------------------------------------------------
210  * Function:	light_edit_mode
211  * Description:	enter/exit light editing mode
212  * Args:	edit: 1 means enter, 0 means exit, 2 means toggle
213  * Returns:
214  * Author:	mbp
215  * Date:	Thu Dec 26 21:12:52 1991
216  * Notes:
217  */
light_edit_mode(int edit)218 void light_edit_mode(int edit)
219 {
220 
221  if (edit == 2)
222    edit = uistate.lights_shown ? 0 : 1;
223 
224  if (edit) {
225    show_lights();
226  }
227  else {
228    unshow_lights();
229  }
230  ui_light_button();
231 }
232 
unshow_lights()233 void unshow_lights()
234 {
235 /*  int i; currently unused */
236 
237   if (lr_id != NOID) {
238     gv_delete( lr_id );
239     lr_id = NOID;
240   }
241   ui_remove_mode(LIGHTEDIT);
242   uistate.lights_shown = 0;
243 }
244 
show_lights()245 void show_lights()
246 {
247   static char *name = "light rack";
248   GeomStruct gs;
249 
250   lights_changed_check(); /* rebuild lightrack if lights changed */
251   RefIncr((Ref *)lr.list);
252   gs.geom = lr.list;
253   gs.h = NULL;
254   lr_id = gv_new_alien( name, &gs );
255   ui_add_mode(LIGHTEDIT, lightedit, T_NONE);
256   uistate.lights_shown = 1;
257   gv_event_mode( LIGHTEDIT );
258 }
259 
build_light_rack(LmLighting * lgt)260 static void build_light_rack(LmLighting *lgt)
261 {
262   Point3 axis;
263   int lightno;
264   int i;
265   LtLight *light, **lp;
266   static LtLight ambient;
267 
268   if (lr.lightgeoms) OOGLFree(lr.lightgeoms);
269   if (lr.lights) OOGLFree(lr.lights);
270   if (lr.list) GeomDelete(lr.list);
271 
272   lr.list = GeomCreate("list", CR_END);
273   lr.lightcount = countlights(lgt)+1;
274   lr.lightgeoms = OOGLNewNE(Geom *, lr.lightcount, "lightgeoms array");
275   lr.lights = OOGLNewNE(LtLight *, lr.lightcount, "lights array");
276 
277   lr.lightgeoms[0] = NULL;
278   lr.lights[0] = &ambient;
279   LmGet(lgt, LM_AMBIENT, &(ambient.color));
280   normalize_color( &(ambient.color), &(ambient.intensity) );
281 
282   lightno = 1;
283   LM_FOR_ALL_LIGHTS(lgt, i,lp) {
284     light = *lp;
285     axis.x = light->position.x;
286     axis.y = light->position.y;
287     axis.z = light->position.z;
288     lr.lights[lightno] = light;
289     lr.lightgeoms[lightno] = make_light(axis, &(light->color));
290     lr.list = ListAppend(lr.list, lr.lightgeoms[lightno]);
291     ++lightno;
292   }
293   if(lr_id != NOID) {
294     RefIncr((Ref *)lr.list);
295     {
296       GeomStruct gs;
297       gs.geom = lr.list;
298       gs.h = NULL;
299       gv_geometry( drawer_id2name(lr_id), &gs );
300     }
301   }
302   ui_lights_changed();
303 }
304 
305 #define max(a,b) (a)>(b)?(a):(b)
306 
307 static void
normalize_color(color,coeff)308 normalize_color( color, coeff )
309     Color *color;
310     float *coeff;
311 {
312     *coeff = max(color->r, color->g);
313     *coeff = max(color->b, *coeff);
314 
315     if( *coeff != 0.0 ) {
316 	color->r /= *coeff;
317 	color->g /= *coeff;
318 	color->b /= *coeff;
319     }
320 }
321 
countlights(LmLighting * lm)322 static int countlights(LmLighting *lm)
323 {
324   int n = 0, i;
325   LtLight **lp;
326   LM_FOR_ALL_LIGHTS(lm, i,lp) {
327     n++;
328   }
329   return n;
330 }
331 
make_light(Point3 axis,Color * color)332 Geom* make_light(Point3 axis, Color *color)
333 {
334   Geom *g;
335   Transform t;
336   Appearance *ap;
337 
338   ap = ApCreate(AP_MtSet, MT_EDGECOLOR, color, MT_END, AP_END);
339   lxform(t, axis);
340   g = GeomCreate("inst", CR_GEOM, ray, CR_NOCOPY, CR_APPEAR, ap, CR_END);
341   if (g == NULL) {
342     fprintf(stderr, "Couldn't create inst\n");
343     return NULL;
344   }
345   InstTransformTo((Inst *)g, t, NULL);
346   return g;
347 }
348 
lxform(Transform t,Point3 axis)349 static void lxform(Transform t, Point3 axis)
350 {
351   Point3 x, y;
352   static Point3 other1 = {0,0,1};
353   static Point3 other2 = {0,1,0};
354   Point3 *other;
355 
356   Pt3Unit(&axis);
357   other = (Pt3Dot(&axis, &other1) < .5) ? &other1 : &other2;
358 
359   Pt3Cross(other, &axis, &x);
360   Pt3Unit(&x);
361   Pt3Cross(&axis, &x, &y);
362 
363   t[0][0] = x.x;
364   t[0][1] = x.y;
365   t[0][2] = x.z;
366   t[0][3] = 0;
367 
368   t[1][0] = y.x;
369   t[1][1] = y.y;
370   t[1][2] = y.z;
371   t[1][3] = 0;
372 
373   t[2][0] = axis.x;
374   t[2][1] = axis.y;
375   t[2][2] = axis.z;
376   t[2][3] = 0;
377 
378   t[3][0] = 0;
379   t[3][1] = 0;
380   t[3][2] = 0;
381   t[3][3] = 1;
382 }
383 
384 #if LIGHTDEBUG
385 static char *
Point3String(Point3 * p)386 Point3String(Point3 *p)
387 {
388   static char buf[80];
389   sprintf(buf,"%f  %f  %f", p->x, p->y, p->z);
390   return buf;
391 }
392 #endif
393 
394 static int
lightedit(Event * event)395 lightedit(Event *event)
396 {
397   float dx,dy;
398   unsigned long int dt;
399   Transform T;
400   int target=WORLDGEOM;
401 
402   lights_changed_check(); /* rebuild lightrack if lights have changed */
403 
404   if (!uistate.lights_shown)
405     show_lights();
406 
407   /* don't do anything with ambient light */
408   if (uistate.current_light == 0) return 0; /* should this return 0 or 1 ? */
409 
410   switch (event->dev) {
411 
412   case ELEFTMOUSE:
413     if (event->val > 0) {
414       /* button went down */
415       mousezero(event,NULL);
416       TmIdentity(Tincr);
417       drawer_updateproc( lr_id, NULL );
418     }
419     else if (event->val) {
420       /* dragging while button down */
421       mousedisp(event, &dx, &dy, &dt, &drawerstate.winpos);
422       Rotation(dx, dy, T, &target);
423       InstTransform((Inst *)lr.lightgeoms[uistate.current_light], T, NULL);
424       PtTransform(T, &(lr.lights[uistate.current_light]->position),
425 		  &(lr.lights[uistate.current_light]->position));
426 #if LIGHTDEBUG
427 printf("light moved to %s\n", Point3String(&(lr.lights[uistate.current_light]->position)));
428 #endif
429       apseq = ++drawerstate.apseq;
430       drawerstate.changed = true;
431     }
432     else {
433       /* button went up */
434       mousedisp(event, &dx, &dy, &dt, &drawerstate.winpos);
435       if ( (dx != 0) || (dy != 0) ) {
436 	Rotation(dx, dy, Tincr, &target);
437 	drawer_updateproc( lr_id, updateproc );
438       }
439       else
440 	drawer_updateproc( lr_id, NULL );
441     }
442     break;
443 
444   case EMIDDLEMOUSE:
445   case ERIGHTMOUSE:
446     pick_light(event);
447     break;
448 
449   default:
450     return 0;
451     break;
452   }
453   return 1;
454 }
455 
updateproc(Geom * g)456 static int updateproc(Geom *g)
457 {
458   /* don't do anything with ambient light */
459   if (uistate.current_light == 0) return 42;
460 
461   InstTransform((Inst *)lr.lightgeoms[uistate.current_light], Tincr, NULL);
462   PtTransform(Tincr, &(lr.lights[uistate.current_light]->position),
463 	      &(lr.lights[uistate.current_light]->position));
464   apseq = ++drawerstate.apseq;
465   drawerstate.changed = true;
466   return 42;
467 }
468 
469 
pick_light(Event * ev)470 static void pick_light(Event *ev)
471 {
472   int i;
473   DView *dv = dview[uistate.mousefocus];
474   DGeom *dg = dgeom[INDEXOF(lr_id)];
475   Transform V, T, Tnorm, Tmodel, Tnet, Tt;
476   float xpick, ypick;
477   Pick *pick = NULL;
478 
479   CamView(dv->cam, V);
480   /* Top-level modelling transform */
481   GeomPosition( dv->Item, T );
482   TmConcat( T,V, T );
483   pick = PickSet(NULL, PA_THRESH, .02, PA_END);
484   for (i=1; i<lr.lightcount; ++i) {
485     TmIdentity(T);
486     GeomPosition( dg->Item, Tmodel );
487     GeomPosition( dg->Inorm, Tnorm );
488     TmConcat( Tnorm,Tmodel, Tt );
489     TmConcat( Tt,T, Tnet );	/* Now Tnet = complete geom-to-screen proj'n */
490     mousemap(ev->x, ev->y, &xpick, &ypick, &drawerstate.winpos);
491     if(GeomMousePick( lr.lightgeoms[i], pick, (Appearance *)NULL,
492 		      Tnet, NULL, NULL, xpick, ypick )) {
493       uistate.current_light = i;
494       set_light(i);
495     }
496   }
497 }
498 
add_light()499 void add_light()
500 {
501   LmLighting *lgt;
502 
503   ApGet(drawerstate.ap, AP_LGT, &lgt);
504   LmAddLight( lgt, LtCreate(LT_END) );
505   ++drawerstate.apseq;
506   uistate.current_light = lr.lightcount;
507   lights_changed_check();
508 }
509 
510 
delete_light()511 void delete_light()
512 {
513   LmLighting *lgt;
514 
515   ApGet(drawerstate.ap, AP_LGT, &lgt);
516   LmRemoveLight( lgt, lr.lights[uistate.current_light] );
517   ++drawerstate.apseq;
518   if ( uistate.current_light > (lr.lightcount - 2) ) {
519     uistate.current_light--;
520   }
521   lights_changed_check();
522 }
523 
lights_changed_check()524 void lights_changed_check()
525 {
526   LmLighting *lgt;
527 /*  Appearance *ap; currently unused */
528 
529   if (drawerstate.apseq != apseq) {
530     ApGet( drawerstate.ap, AP_LGT, &lgt );
531     build_light_rack( lgt );
532     apseq = drawerstate.apseq;
533     drawerstate.changed = true;
534 /*
535     if (uistate.lights_shown) {
536       GeomStruct gs;
537       gs.geom = lr.list;
538       gs.h = NULL;
539       gv_geometry( lr_id, &gs );
540     }
541 
542       Not just unnecessary (happens in build_light_rack) but wrong:
543       this is a hidden GeomDelete!
544 */
545   }
546 }
547 
548 /* This is a complete hack.  This module should use the drawer_transform stuff,
549  * but I'm too lazy to fix it now --njt
550  */
551 static void
mouse2world(Point * axis,int objid)552 mouse2world(Point *axis, int objid)
553 {
554     Transform C2W, T, Tinv;
555     DObject *dobj;
556 
557     if((dobj = drawer_get_object(objid)) == NULL)
558 	return;
559 
560     CamGet( dview[uistate.mousefocus]->cam, CAM_C2W, C2W );
561     PtTransform( C2W, axis, axis );
562 
563     if(ISCAM(objid)) {
564 	CamGet( ((DView *)dobj)->cam, CAM_W2C, Tinv );
565 	PtTransform( Tinv, axis, axis );
566     } else if(((DGeom *)dobj)->citizenship == ORDINARY) {
567 	GeomGet(dgeom[0]->Item, CR_AXIS, T);
568 	/* Assert: dgeom[0]->Inorm uses the identity matrix */
569 	TmInvert(T, Tinv);
570 	PtTransform( Tinv, axis, axis );
571     }
572 }
573 void
Rotation(double dx,double dy,Transform rot,int * target)574 Rotation(double dx, double dy, Transform rot, int *target)
575 {
576   double ang;
577   Point axis;
578 
579   if (dx == 0 && dy == 0) {
580     TmIdentity(rot);
581     return;
582   }
583 
584   axis.x = -dy;  axis.y = dx;  axis.z = 0;
585   axis.w = 0;
586   ang = atan( sqrt( dx*dx + dy*dy ) );
587 
588   mouse2world(&axis, *target);
589 
590   TmRotate( rot, ang, (Point3 *)(void *)&axis );
591 }
592