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