BRL-CAD
sh_light.c
Go to the documentation of this file.
1 /* S H _ L I G H T . C
2  * BRL-CAD
3  *
4  * Copyright (c) 1998-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library 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 this file; see the file named COPYING for more
18  * information.
19  */
20 /** @file liboptical/sh_light.c
21  *
22  * Implement simple isotropic light sources as a material property.
23  *
24  */
25 
26 #include "common.h"
27 
28 #include <stddef.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <math.h>
32 
33 #include "bu/parallel.h"
34 #include "vmath.h"
35 #include "raytrace.h"
36 #include "optical.h"
37 #include "plot3.h"
38 #include "light.h"
39 #include "photonmap.h"
40 
41 #ifdef RT_MULTISPECTRAL
42 # include "spectrum.h"
43 #endif
44 
45 
46 #define LIGHT_O(m) bu_offsetof(struct light_specific, m)
47 
48 
49 /** Heads linked list of lights */
50 struct light_specific LightHead;
51 
52 /* local sp_hook functions */
53 /* for light_print_tab and light_parse callbacks */
54 HIDDEN void aim_set(const struct bu_structparse *, const char *, void *, const char *, void *);
55 HIDDEN void light_cvt_visible(const struct bu_structparse *, const char *, void *, const char *, void *);
56 HIDDEN void light_pt_set(const struct bu_structparse *, const char *, void *, const char *, void *);
57 
58 HIDDEN int light_setup(struct region *rp, struct bu_vls *matparm, void **dpp, const struct mfuncs *mfp, struct rt_i *rtip);
59 HIDDEN int light_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp);
60 HIDDEN void light_print(register struct region *rp, void *dp);
61 HIDDEN void light_free(void *cp);
62 
63 
64 /** callback registration table for this shader in optical_shader_init() */
65 struct mfuncs light_mfuncs[] = {
66  {MF_MAGIC, "light", 0, MFI_NORMAL, 0, light_setup, light_render, light_print, light_free },
67  {0, (char *)0, 0, 0, 0, 0, 0, 0, 0 }
68 };
69 
70 
71 /** for printing out light values */
73  {"%f", 1, "bright", LIGHT_O(lt_intensity), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
74  {"%f", 1, "angle", LIGHT_O(lt_angle), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
75  {"%f", 1, "fract", LIGHT_O(lt_fraction), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
76  {"%f", 3, "target", LIGHT_O(lt_target), aim_set, NULL, NULL },
77  {"%d", 1, "shadows", LIGHT_O(lt_shadows), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
78  {"%d", 1, "infinite", LIGHT_O(lt_infinite), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
79  {"%d", 1, "visible", LIGHT_O(lt_visible), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
80  {"%d", 1, "invisible", LIGHT_O(lt_invisible), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
81  {"", 0, (char *)0, 0, BU_STRUCTPARSE_FUNC_NULL, NULL, NULL }
82 };
83 
84 
85 /** for actually parsing light values */
87 
88  {"%f", 1, "bright", LIGHT_O(lt_intensity), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
89  {"%f", 1, "b", LIGHT_O(lt_intensity), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
90  {"%f", 1, "inten", LIGHT_O(lt_intensity), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
91 
92  {"%f", 1, "angle", LIGHT_O(lt_angle), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
93  {"%f", 1, "a", LIGHT_O(lt_angle), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
94 
95  {"%f", 1, "fract", LIGHT_O(lt_fraction), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
96  {"%f", 1, "f", LIGHT_O(lt_fraction), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
97 
98  {"%f", 3, "target", LIGHT_O(lt_target), aim_set, NULL, NULL },
99  {"%f", 3, "t", LIGHT_O(lt_target), aim_set, NULL, NULL },
100  {"%f", 3, "aim", LIGHT_O(lt_target), aim_set, NULL, NULL },
101 
102  {"%d", 1, "shadows", LIGHT_O(lt_shadows), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
103  {"%d", 1, "s", LIGHT_O(lt_shadows), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
104 
105  {"%d", 1, "infinite", LIGHT_O(lt_infinite), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
106  {"%d", 1, "i", LIGHT_O(lt_infinite), BU_STRUCTPARSE_FUNC_NULL, NULL, NULL },
107 
108  {"%d", 1, "visible", LIGHT_O(lt_visible), light_cvt_visible, NULL, NULL },
109  {"%d", 1, "v", LIGHT_O(lt_visible), light_cvt_visible, NULL, NULL },
110 
111  {"%d", 1, "invisible", LIGHT_O(lt_invisible), light_cvt_visible, NULL, NULL },
112 
113  {"%f", 3, "pt", LIGHT_O(lt_parse_pt), light_pt_set, NULL, NULL },
114  {"%f", 6, "pn", LIGHT_O(lt_parse_pt), light_pt_set, NULL, NULL },
115 
116  {"", 0, (char *)0, 0, BU_STRUCTPARSE_FUNC_NULL, NULL, NULL }
117 };
118 
119 
120 /**
121  * This is a container for all the stuff that must be carried around
122  * when doing the light obscuration/visibility calculations.
123  */
125  struct application *ap;
126  struct shadework *swp;
127  struct light_specific *lsp;
128  int *rand_idx;
129 
130 #ifdef RT_MULTISPECTRAL
131  struct bn_tabdata **inten;
132 #else
134 #endif
135  int iter;
136  vect_t to_light_center; /* coordinate system on light */
137  vect_t light_x;
138  vect_t light_y;
139 };
140 
141 
142 /**
143  * This routine is called by bu_struct_parse() if the "aim" qualifier
144  * is encountered, and causes lt_exaim to be set.
145  */
146 HIDDEN void
147 aim_set(const struct bu_structparse *UNUSED(sdp),
148  const char *UNUSED(name),
149  void *base,
150  const char *UNUSED(value),
151  void *UNUSED(data))
152 {
153  register struct light_specific *lsp = (struct light_specific *)base;
154  if (rdebug & RDEBUG_LIGHT) {
155  VPRINT("lt_target: ", lsp->lt_target);
156  }
157  lsp->lt_exaim = 1;
158 }
159 
160 
161 /**
162  * light_cvt_visible()
163  *
164  * Convert "visible" flag to "invisible" variable
165  */
166 HIDDEN void
168  const char *name,
169  void *base,
170  const char *UNUSED(value),
171  void *UNUSED(data))
172 /* structure description */
173 /* struct member name */
174 /* beginning of structure */
175 /* string containing value */
176 {
177  struct light_specific *lsp = (struct light_specific *)base;
178 
179  if (rdebug & RDEBUG_LIGHT) {
180  bu_log("light_cvt_visible(%s, %zu)\n", name, sdp->sp_offset);
181  bu_log("visible: %lu invisible: %lu\n",
182  LIGHT_O(lt_visible),
183  LIGHT_O(lt_invisible));
184  }
185  if (sdp->sp_offset == LIGHT_O(lt_invisible)) {
186  lsp->lt_visible = !lsp->lt_invisible;
187  } else if (sdp->sp_offset == LIGHT_O(lt_visible)) {
188  lsp->lt_invisible = !lsp->lt_visible;
189  }
190 }
191 
192 
193 /**
194  * ensure that there are sufficient light samples, allocate more if
195  * necessary in batches.
196  */
197 HIDDEN void
198 light_pt_allocate(register struct light_specific *lsp)
199 {
200  /* make sure we have enough room, allocate in batches of
201  * SOME_LIGHT_SAMPLES
202  */
203  if (lsp->lt_pt_count % SOME_LIGHT_SAMPLES == 0) {
204  if (lsp->lt_pt_count < 1) {
205  /* assumes initialized to NULL */
206  if (lsp->lt_sample_pts) {
207  bu_free(lsp->lt_sample_pts, "free light samples array");
208  }
209  lsp->lt_sample_pts = (struct light_pt *)bu_calloc(lsp->lt_pt_count + SOME_LIGHT_SAMPLES, sizeof(struct light_pt), "callocate light sample points");
210  } else {
211  lsp->lt_sample_pts = (struct light_pt *)bu_realloc(lsp->lt_sample_pts, (lsp->lt_pt_count + SOME_LIGHT_SAMPLES) * sizeof(struct light_pt), "reallocate light sample points");
212  }
213  }
214 }
215 
216 
217 /**
218  * create a set of light point samples for specified pt/pn arguments
219  * (for points and points with normals respectively)
220  */
221 HIDDEN void
222 light_pt_set(const struct bu_structparse *sdp,
223  const char *name,
224  void *base,
225  const char *UNUSED(value),
226  void *UNUSED(data))
227 /* structure description */
228 /* struct member name */
229 /* beginning of structure */
230 /* string containing value */
231 {
232  struct light_specific *lsp = (struct light_specific *)base;
233  fastf_t *p = (fastf_t *)((char *)base + sdp->sp_offset);
234 
235  if (BU_STR_EQUAL("pt", name)) {
236  /* user just specified point, set normal to zeros */
237  p[3] = p[4] = p[5] = 0.0;
238  } else if (!BU_STR_EQUAL("pn", name)) {
239  bu_log("*********** unknown option in light_pt_set %s:%d\n", __FILE__, __LINE__);
240  return;
241  }
242 
243  light_pt_allocate(lsp);
244  memcpy(&lsp->lt_sample_pts[ lsp->lt_pt_count++ ], p, sizeof(struct light_pt));
245 
246  if (rdebug & RDEBUG_LIGHT) {
247  bu_log("set light point %g %g %g N %g %g %g\n", p[0], p[1], p[2], p[3], p[4], p[5]);
248  }
249 }
250 
251 
252 /**
253  * If we have a direct view of the light, return its color. A cosine
254  * term is needed in the shading of the light source, to make it have
255  * dimension and shape. However, just a simple cosine of the angle
256  * between the normal and the direction vector leads to a pretty dim
257  * looking light. Therefore, a cos/2 + 0.5 term is used when the
258  * viewer is within the beam, and a cos/2 term when the beam points
259  * away.
260  */
261 HIDDEN int
262 light_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp)
263 {
264  register struct light_specific *lsp = (struct light_specific *)dp;
265  register fastf_t f;
266 
267  RT_CK_LIGHT(lsp);
268 
269  /* Provide cosine/2 shading, to make light look round */
270  if ((f = -VDOT(swp->sw_hit.hit_normal, ap->a_ray.r_dir)*0.5) < 0)
271  f = 0;
272 
273  /* See if surface normal falls in light beam direction */
274  if (VDOT(lsp->lt_aim, swp->sw_hit.hit_normal) < lsp->lt_cosangle) {
275  /* dark, outside of light beam area */
276  f *= lsp->lt_fraction;
277  } else {
278  /* within beam area */
279  f = (f+0.5) * lsp->lt_fraction;
280  }
281 #ifdef RT_MULTISPECTRAL
282  /* Support a shader having modified the temperature of the source */
283  if (swp->sw_temperature > 0) {
284  rt_spect_black_body(swp->msw_color, swp->sw_temperature, 5);
285  bn_tabdata_scale(swp->msw_color, swp->msw_color, f);
286  } else {
287  bn_tabdata_scale(swp->msw_color, lsp->lt_spectrum, f);
288  }
289 #else
290  if (!PM_Activated) {
291  VSCALE(swp->sw_color, lsp->lt_color, f);
292  }
293 #endif
294 
295  if (rdebug & RDEBUG_LIGHT) {
296  bu_log("light %s xy=%d, %d temp=%g\n",
297  pp->pt_regionp->reg_name, ap->a_x, ap->a_y,
298  swp->sw_temperature);
299  }
300 
301  return 1;
302 }
303 
304 
305 /**
306  * preparation routine for light_gen_sample_pts() that sets up a
307  * sample point ray.
308  */
309 static void
310 ray_setup(struct application *ap,
311  point_t tree_min,
312  point_t tree_max,
313  point_t span)
314 {
315  int face;
316  point_t pt = VINIT_ZERO;
317  static int idx = 0;
318 
319  /* pick a face of the bounding RPP at which we will start the ray */
320  face = BN_RANDOM(idx) * 2.9999;
321 
322  switch (face) {
323  case 0: /* XMIN */
324  VSET(ap->a_ray.r_pt,
325  tree_min[X] - 10.0,
326  tree_min[Y] + BN_RANDOM(idx) * span[Y],
327  tree_min[Z] + BN_RANDOM(idx) * span[Z]);
328  VSET(pt,
329  tree_max[X],
330  tree_min[Y] + BN_RANDOM(idx) * span[Y],
331  tree_min[Z] + BN_RANDOM(idx) * span[Z]);
332  break;
333 
334  case 1: /* YMIN */
335  VSET(ap->a_ray.r_pt,
336  tree_min[X] + BN_RANDOM(idx) * span[X],
337  tree_min[Y] - 10.0,
338  tree_min[Z] + BN_RANDOM(idx) * span[Z]);
339  VSET(pt,
340  tree_min[X] + BN_RANDOM(idx) * span[X],
341  tree_max[Y],
342  tree_min[Z] + BN_RANDOM(idx) * span[Z]);
343  break;
344 
345  case 2: /* ZMIN */
346  VSET(ap->a_ray.r_pt,
347  tree_min[X] +
348  BN_RANDOM(idx) * span[X],
349 
350  tree_min[Y] +
351  BN_RANDOM(idx) * span[Y],
352 
353  tree_min[Z] - 10.0);
354  VSET(pt,
355  tree_min[X] +
356  BN_RANDOM(idx) * span[X],
357 
358  tree_min[Y] +
359  BN_RANDOM(idx) * span[Y],
360 
361  tree_max[Z]);
362  break;
363  }
364  VSUB2(ap->a_ray.r_dir, pt, ap->a_ray.r_pt);
365  VUNITIZE(ap->a_ray.r_dir);
366 
367 }
368 
369 
370 /**
371  * this is the hit callback function when shooting grids of rays to
372  * generate the points on the light (in light_gen_sample_pts()), we
373  * add the hit point(s) to the list of points on the light.
374  */
375 static int
376 light_gen_sample_pts_hit(register struct application *ap, struct partition *PartHeadp, struct seg *UNUSED(sp))
377 {
378  struct light_specific *lsp = (struct light_specific *)ap->a_uptr;
379  struct soltab *stp;
380  struct light_pt *lpt;
381  struct partition *pp, *prev, *next;
382 
383  RT_CK_LIGHT(lsp);
384 
385  if ((pp=PartHeadp->pt_forw) == PartHeadp) return 0;
386 
387  for (; pp != PartHeadp; pp = pp->pt_forw) {
388 
389  if (pp->pt_regionp != lsp->lt_rp) continue;
390 
391  prev = pp->pt_back;
392  /* check to make sure the light hit point isn't against some
393  * other object
394  */
395  if (prev != PartHeadp) {
396  double delta;
397  delta = prev->pt_outhit->hit_dist -
398  pp->pt_inhit->hit_dist;
399 
400  /* XXX This really should compare to see if adj
401  * object is air
402  */
403  if (delta < 5.0 && delta > -5.0) {
404  continue;
405  }
406  }
407 
408  /* The inbound point is not against another object, so
409  * light will be emitted in this direction
410  */
411  if (&lsp->lt_sample_pts) {
412  light_pt_allocate(lsp);
413  lpt = &lsp->lt_sample_pts[lsp->lt_pt_count++];
414  } else {
415  /* no sample points? */
416  break;
417  }
418 
419  stp = pp->pt_inseg->seg_stp;
420 
421  if (!lpt || !stp) {
422  break;
423  }
424 
425  VJOIN1(lpt->lp_pt, ap->a_ray.r_pt,
426  pp->pt_inhit->hit_dist, ap->a_ray.r_dir);
427 
428  RT_HIT_NORMAL(lpt->lp_norm, pp->pt_inhit, stp,
429  &(ap->a_ray), pp->pt_inflip);
430 
431  /* check to make sure the light out hit point isn't against
432  * some other object
433  */
434  next = pp->pt_forw;
435  if (next != PartHeadp) {
436  double delta;
437  delta = next->pt_inhit->hit_dist -
438  pp->pt_outhit->hit_dist;
439 
440  /* XXX This really should compare to see if adj
441  * object is air
442  */
443  if (delta < 5.0 && delta > -5.0) {
444  continue;
445  }
446  }
447  /* The out point isn't against another object, so light
448  * will be emitted in this direction
449  */
450  if (&lsp->lt_sample_pts) {
451  light_pt_allocate(lsp);
452  lpt = &lsp->lt_sample_pts[lsp->lt_pt_count++];
453  } else {
454  /* no sample points? */
455  break;
456  }
457 
458  stp = pp->pt_outseg->seg_stp;
459 
460  if (!lpt || !stp) {
461  break;
462  }
463 
464  VJOIN1(lpt->lp_pt, ap->a_ray.r_pt,
465  pp->pt_outhit->hit_dist, ap->a_ray.r_dir);
466 
467  RT_HIT_NORMAL(lpt->lp_norm, pp->pt_outhit, stp,
468  &(ap->a_ray), pp->pt_outflip);
469  }
470  return 1;
471 }
472 
473 
474 /**
475  * this is the callback miss function when shooting the grids for
476  * building light pts (in light_gen_sample_pts()). if we miss the
477  * light, then do nothing.
478  */
479 static int
480 light_gen_sample_pts_miss(register struct application *UNUSED(ap))
481 {
482  return 0;
483 }
484 
485 
486 /**
487  * Generate a set of sample points on the surface of the light with
488  * surface normals. calling during shader init to generate samples
489  * for all lights.
490  */
491 void
493  struct light_specific *lsp)
494 {
495  struct application ap;
496  point_t tree_min;
497  point_t tree_max;
498  vect_t span;
499  int total_samples;
500 
501  RT_CK_LIGHT(lsp);
502 
503  if (rdebug & RDEBUG_LIGHT)
504  bu_log("light_gen_sample_pts(%s)\n", lsp->lt_name);
505 
506 
507  memset(&ap, 0, sizeof(ap));
508  ap.a_rt_i = upap->a_rt_i;
509  ap.a_onehit = 0;
510  ap.a_hit = light_gen_sample_pts_hit;
511  ap.a_miss = light_gen_sample_pts_miss;
512  ap.a_logoverlap = upap->a_logoverlap;
513  ap.a_uptr = (void *)lsp;
514 
515  /* get the bounding box of the light source
516  * Return if we can't get the bounding tree dimensions */
517  if (rt_bound_tree(lsp->lt_rp->reg_treetop, tree_min, tree_max) < 0) return;
518 
519  if (rdebug & RDEBUG_LIGHT) {
520  bu_log("\tlight bb (%g %g %g), (%g %g %g)\n",
521  V3ARGS(tree_min), V3ARGS(tree_max));
522  }
523 
524  /* if there is no space occupied by the light source, then
525  * just give up
526  */
527  VSUB2(span, tree_max, tree_min);
528  if (rdebug & RDEBUG_LIGHT) {
529  bu_log("\tspan %g %g %g\n", V3ARGS(span));
530  }
531  if (span[X] <= 0.0 && span[Y] <= 0.0 && span[Z] <= 0.0) {
532  bu_log("\tSmall light. (treating as point source)\n");
533  return;
534  }
535 
536  /* need enough samples points to avoid shadow patterns */
537  total_samples = SOME_LIGHT_SAMPLES * lsp->lt_shadows;
538  while (lsp->lt_pt_count < total_samples) {
539  ray_setup(&ap, tree_min, tree_max, span);
540  (void)rt_shootray(&ap);
541  }
542 
543  /* debugging for the light sample points. output a plot line for
544  * each sample point.
545  */
546  if (rdebug & RDEBUG_LIGHT) {
547  int l;
548  point_t p;
549  struct light_pt *lpt = &lsp->lt_sample_pts[0];
550 
551  bu_log("\t%d light sample points\n", lsp->lt_pt_count);
552 
553  for (l = 0; l < lsp->lt_pt_count; l++, lpt++) {
554 
555  VJOIN1(p, lpt->lp_pt, 100.0, lpt->lp_norm);
556 
557  bu_log("\tV %g %g %g %g %g %g\n",
558  V3ARGS(lpt->lp_pt), V3ARGS(p));
559  }
560  }
561 }
562 
563 
564 HIDDEN void
565 light_print(register struct region *rp, void *dp)
566 {
567  bu_struct_print(rp->reg_name, light_print_tab, (char *)dp);
568 }
569 
570 
571 void
572 light_free(void *cp)
573 {
574  register struct light_specific *lsp = (struct light_specific *)cp;
575 
576  RT_CK_LIGHT(lsp);
577  BU_LIST_DEQUEUE(&(lsp->l));
578  if (lsp->lt_name) {
579  bu_free(lsp->lt_name, "light name");
580  lsp->lt_name = (char *)0;
581  }
582  if (lsp->lt_sample_pts) {
583  bu_free(lsp->lt_sample_pts, "free light samples array");
584  }
585  lsp->l.magic = 0; /* sanity */
586  bu_free(lsp, "struct light_specific");
587 }
588 
589 
590 /**
591  * Called once for each light-emitting region.
592  */
593 HIDDEN int
594 light_setup(register struct region *rp, struct bu_vls *matparm, void **dpp, const struct mfuncs *UNUSED(mfp), struct rt_i *UNUSED(rtip))
595 {
596  register struct light_specific *lsp;
597  register struct soltab *stp;
598  vect_t work;
599  fastf_t f;
600 
601  BU_CK_VLS(matparm);
602 
603  BU_ALLOC(lsp, struct light_specific);
604  BU_LIST_INIT_MAGIC(&(lsp->l), LIGHT_MAGIC);
605 
606  lsp->lt_intensity = 1.0; /* Lumens */
607  lsp->lt_fraction = -1.0; /* Recomputed later */
608  lsp->lt_visible = 1; /* explicitly modeled */
609  lsp->lt_invisible = 0; /* explicitly modeled */
610  lsp->lt_shadows = 1; /* by default, casts shadows */
611  lsp->lt_angle = 180; /* spherical emission by default */
612  lsp->lt_exaim = 0; /* use default aiming mechanism */
613  lsp->lt_infinite = 0;
614  lsp->lt_rp = rp;
615  lsp->lt_pt_count = 0;
616  lsp->lt_sample_pts = (struct light_pt *)NULL;
617  lsp->lt_name = bu_strdup(rp->reg_name);
618 
619  if (bu_struct_parse(matparm, light_parse, (char *)lsp, NULL) < 0) {
620  light_free((void *)lsp);
621  return -1;
622  }
623 
624  if (lsp->lt_angle > 180) lsp->lt_angle = 180;
625  lsp->lt_cosangle = cos((double) lsp->lt_angle * DEG2RAD);
626 
627  /* Determine position and size */
628  if (rp->reg_treetop->tr_op == OP_SOLID) {
629 
630  stp = rp->reg_treetop->tr_a.tu_stp;
631  VMOVE(lsp->lt_pos, stp->st_center);
632  lsp->lt_radius = stp->st_aradius;
633  } else {
634  vect_t min_rpp, max_rpp;
635  vect_t rad;
636  register union tree *tp;
637 
638  if (rt_bound_tree(rp->reg_treetop, min_rpp, max_rpp) < 0)
639  return -1;
640 
641  if (max_rpp[X] >= INFINITY) {
642  bu_log("light_setup(%s) Infinitely large light sources not supported\n",
643  lsp->lt_name);
644  return -1;
645  }
646 
647  VADD2SCALE(lsp->lt_pos, min_rpp, max_rpp, 0.5);
648  VSUB2(rad, max_rpp, lsp->lt_pos);
649  /* Use smallest radius from center to max as light radius */
650  /* Having the radius too large can give very poor lighting */
651  if (rad[X] < rad[Y])
652  lsp->lt_radius = rad[X];
653  else
654  lsp->lt_radius = rad[Y];
655  if (rad[Z] < lsp->lt_radius)
656  lsp->lt_radius = rad[Z];
657 
658  /* Find first leaf node on left of tree */
659  tp = rp->reg_treetop;
660  while (tp->tr_op != OP_SOLID)
661  tp = tp->tr_b.tb_left;
662  stp = tp->tr_a.tu_stp;
663  }
664 
665  /* Light is aimed down -Z in its local coordinate system */
666  {
667  register matp_t matp;
668  if ((matp = stp->st_matp) == (matp_t)0)
669  matp = (matp_t)bn_mat_identity;
670  if (lsp->lt_exaim) {
671  VSUB2 (work, lsp->lt_target, lsp->lt_pos);
672  VUNITIZE (work);
673  } else VSET(work, 0, 0, -1);
674  MAT4X3VEC(lsp->lt_aim, matp, work);
675  VUNITIZE(lsp->lt_aim);
676  }
677 
678 #ifdef RT_MULTISPECTRAL
679  BN_GET_TABDATA(lsp->lt_spectrum, spectrum);
680  if (rp->reg_mater.ma_temperature > 0) {
681  rt_spect_black_body(lsp->lt_spectrum,
682  rp->reg_mater.ma_temperature, 5);
683  if (rdebug & RDEBUG_LIGHT) {
684  bu_log("Light %s temp is %g degK, emission is pure black-body\n",
686  }
687  } else if (rp->reg_mater.ma_color_valid) {
688  rt_spect_reflectance_rgb(lsp->lt_spectrum,
689  rp->reg_mater.ma_color);
690  /* XXX Need to convert units of lumens (candela-sr) to ??
691  * mw/sr? Use any old numbers to get started.
692  */
693  bn_tabdata_scale(lsp->lt_spectrum, lsp->lt_spectrum,
694  lsp->lt_intensity * 0.001); /* XXX */
695  } else {
696  /* Default: Perfectly even emission across whole spectrum */
697  bn_tabdata_constval(lsp->lt_spectrum, 0.001);
698  }
699 #else
700  if (rp->reg_mater.ma_color_valid) {
701  VMOVE(lsp->lt_color, rp->reg_mater.ma_color);
702  } else {
703  VSETALL(lsp->lt_color, 1);
704  }
705 #endif
706 
707  VMOVE(lsp->lt_vec, lsp->lt_pos);
708  f = MAGNITUDE(lsp->lt_vec);
709  if (f < SQRT_SMALL_FASTF) {
710  /* light at the origin, make its direction vector up */
711  VSET(lsp->lt_vec, 0, 0, 1);
712  } else {
713  VSCALE(lsp->lt_vec, lsp->lt_vec, f);
714  }
715 
716  /* Add to linked list of lights */
717  if (!BU_LIST_IS_INITIALIZED(&(LightHead.l))) {
718  BU_LIST_INIT(&(LightHead.l));
719  }
720  BU_LIST_INSERT(&(LightHead.l), &(lsp->l));
721 
722  if (rdebug&RDEBUG_LIGHT) {
723  light_print(rp, (char *)lsp);
724  }
725  if (lsp->lt_invisible) {
726  return 2; /* don't show light, destroy it later */
727  }
728 
729  *dpp = lsp; /* Associate lsp with reg_udata */
730  return 1;
731 }
732 
733 
734 /**
735  * Special routine called by view_2init() to determine the relative
736  * intensities of each light source.
737  *
738  * Because of the limited dynamic range of RGB space (0..255), the
739  * strategy used here is a bit risky. We find the brightest single
740  * light source in the model, and assume that the energy from multiple
741  * lights will not shine on a single location in such a way as to add
742  * up to an overload condition. We then account for the effect of
743  * ambient light, because it always adds its contribution. Even here
744  * we only expect 50% of the ambient intensity, to keep the pictures
745  * reasonably bright.
746  */
747 int
749 {
750  register struct light_specific *lsp;
751  register int nlights = 0;
752  register fastf_t inten = 0.0;
753 
754  if (!BU_LIST_IS_INITIALIZED(&(LightHead.l))) {
755  BU_LIST_INIT(&(LightHead.l));
756  }
757 
758 
759  for (BU_LIST_FOR(lsp, light_specific, &(LightHead.l))) {
760  nlights++;
761  if (lsp->lt_fraction > 0) continue; /* overridden */
762  if (lsp->lt_intensity <= 0)
763  lsp->lt_intensity = 1; /* keep non-neg */
764  if (lsp->lt_intensity > inten)
765  inten = lsp->lt_intensity;
766  }
767 
768  /* Compute total emitted energy, including ambient */
769  /* inten *= (1 + AmbientIntensity); */
770  /* This is non-physical and risky, but gives nicer pictures for now */
771  inten *= (1 + AmbientIntensity*0.5);
772 
773  for (BU_LIST_FOR(lsp, light_specific, &(LightHead.l))) {
774  RT_CK_LIGHT(lsp);
775  if (lsp->lt_fraction > 0) continue; /* overridden */
776 #ifdef RT_MULTISPECTRAL
777  lsp->lt_fraction = 1.0; /* always use honest intensity values */
778 #else
779  lsp->lt_fraction = lsp->lt_intensity / inten;
780 #endif
781  }
782 
783  /*
784  * Make sure we have sample points for all light sources in the scene
785  */
786  for (BU_LIST_FOR(lsp, light_specific, &(LightHead.l))) {
787  RT_CK_LIGHT(lsp);
788  if (lsp->lt_shadows > 1 && ! lsp->lt_infinite && lsp->lt_pt_count < 1)
789  light_gen_sample_pts(ap, lsp);
790  }
791 
792 
793  if (R_DEBUG) {
794  bu_log("Lighting: Ambient = %d%%\n",
795  (int)(AmbientIntensity*100));
796 
797  for (BU_LIST_FOR(lsp, light_specific, &(LightHead.l))) {
798  RT_CK_LIGHT(lsp);
799  bu_log(" %s: (%g, %g, %g), aimed at (%g, %g, %g)\n",
800  lsp->lt_name,
801  lsp->lt_pos[X], lsp->lt_pos[Y], lsp->lt_pos[Z],
802  lsp->lt_aim[X], lsp->lt_aim[Y], lsp->lt_aim[Z]);
803  bu_log(" %s: %s, %s, %g lumens (%d%%), halfang=%g\n",
804  lsp->lt_name,
805  lsp->lt_visible ? "visible":"invisible",
806  lsp->lt_shadows ? "casts shadows":"no shadows",
807  lsp->lt_intensity,
808  (int)(lsp->lt_fraction*100),
809  lsp->lt_angle);
810 
811  if (lsp->lt_pt_count > 0) {
812  int samp;
813 
814  bu_log(" %d sample points\n", lsp->lt_pt_count);
815  for (samp = 0; samp < lsp->lt_pt_count; samp++) {
816  bu_log(" pt %g %g %g N %g %g %g\n",
817  V3ARGS(lsp->lt_sample_pts[samp].lp_pt),
818  V3ARGS(lsp->lt_sample_pts[samp].lp_norm));
819  }
820  }
821  }
822  }
823  if (nlights > SW_NLIGHTS) {
824  bu_log("Number of lights limited to %d\n", SW_NLIGHTS);
825  nlights = SW_NLIGHTS;
826  }
827  return nlights;
828 }
829 
830 
831 /**
832  * Called from view_end(). Take care of releasing storage for any
833  * lights which will not be cleaned up by mlib_free(): implicitly
834  * created lights, because they have no associated region, and
835  * invisible lights, because their region was destroyed.
836  */
837 void
839 {
840  register struct light_specific *lsp, *zaplsp;
841 
842  if (!BU_LIST_IS_INITIALIZED(&(LightHead.l))) {
843  BU_LIST_INIT(&(LightHead.l));
844  return;
845  }
846  for (BU_LIST_FOR(lsp, light_specific, &(LightHead.l))) {
847  RT_CK_LIGHT(lsp);
848  if (lsp->lt_rp != REGION_NULL && lsp->lt_visible) {
849  /* Will be cleaned up by mlib_free() */
850  continue;
851  }
852  zaplsp = lsp;
853  lsp = BU_LIST_PREV(light_specific, &(lsp->l));
854  light_free((void *)zaplsp);
855  }
856 }
857 
858 
859 /**
860  * A light visibility test ray hit something. Determine what this
861  * means.
862  *
863  * Input -
864  *
865  * a_color[] contains the fraction of a the light that will be
866  * propagated back along the ray, so far. If this gets too small,
867  * recursion through lots of glass ought to stop.
868  *
869  * Output -
870  *
871  * a_color[] contains the fraction of light that can be seen. RGB
872  * transmissions are separately indicated, to allow simplistic colored
873  * glass (with apologies to Roy Hall).
874  *
875  * a_spectrum is used in place of a_color for multispectral
876  * renderings.
877  *
878  * These shadow functions return a boolean "light_visible".
879  *
880  * This is a simplified algorithm, and could be improved. Reflected
881  * light can't be dealt with at all.
882  *
883  * Would also be nice to return an actual energy level, rather than a
884  * boolean, which could account for distance, etc.
885  */
886 int
887 light_hit(struct application *ap, struct partition *PartHeadp, struct seg *finished_segs)
888 {
889  register struct partition *pp;
890  register struct region *regp = NULL;
891  struct application sub_ap;
892  struct shadework sw;
893  const struct light_specific *lsp;
894 
895  int light_visible = 0;
896  int air_sols_seen = 0;
897  int is_proc;
898  char *reason = "???";
899 
900 #ifdef RT_MULTISPECTRAL
901  struct bn_tabdata *ms_filter_color = BN_TABDATA_NULL;
902 #else
903  vect_t filter_color;
904 #endif
905 
906  RT_CK_PT_HD(PartHeadp);
907 
908  memset(&sw, 0, sizeof(sw)); /* make sure nothing nasty on the stack */
909  if (rdebug&RDEBUG_LIGHT)
910  bu_log("light_hit level %d %d\n", ap->a_level, __LINE__);
911 
912 #ifdef RT_MULTISPECTRAL
913  sub_ap.a_spectrum = BN_TABDATA_NULL; /* sanity */
915 #endif
916 
917  BU_CK_LIST_HEAD(&finished_segs->l);
918 
919  lsp = (struct light_specific *)(ap->a_uptr);
920  RT_CK_LIGHT(lsp);
921 
922 #ifdef RT_MULTISPECTRAL
923  ms_filter_color = bn_tabdata_get_constval(1.0, spectrum);
924  BN_GET_TABDATA(sw.msw_color, spectrum);
925  BN_GET_TABDATA(sw.msw_basecolor, spectrum);
926 #else
927  VSETALL(filter_color, 1);
928 #endif
929 
930  /* anything to do? */
931  if (PartHeadp->pt_forw == PartHeadp) {
932  bu_log("light_hit: ERROR, EMPTY PARTITION sxy=(%d, %d)\n", ap->a_x, ap->a_y);
933  light_visible = 0;
934  reason = "ERROR: EMPTY PARTITION";
935  goto out;
936  }
937 
938  /**
939  * FIXME: Bogus with Air. We should check to see if it is the
940  * same surface.
941  *
942  * Since the light visibility ray started at the surface of a
943  * solid, it is likely that the solid will be the first partition
944  * on the list, with pt_outhit->hit_dist being roughly zero.
945  * Don't start using partitions until pt_inhit->hit_dist is
946  * slightly larger than zero, i.e., that the partition is not
947  * including the start point.
948  *
949  * The outhit distance needs to be checked too, so that if the
950  * partition is heading through the solid toward the light
951  * e.g. (-1, +50), then the fact that the light is obscured will
952  * not be missed.
953  */
954  for (pp=PartHeadp->pt_forw; pp != PartHeadp; pp = pp->pt_forw) {
955  if (pp->pt_regionp->reg_aircode != 0) {
956  /* Accumulate transmission through each air lump */
957  air_sols_seen++;
958 
959  /* Obtain opacity of this region, multiply */
960  sw.sw_inputs = 0;
961  sw.sw_transmit = sw.sw_reflect = 0.0;
962  sw.sw_refrac_index = 1.0;
963  sw.sw_xmitonly = 1; /* only want sw_transmit */
964  sw.sw_segs = finished_segs;
965 #ifdef RT_MULTISPECTRAL
966  bn_tabdata_constval(sw.msw_color, 1.0);
967  bn_tabdata_constval(sw.msw_basecolor, 1.0);
968 #else
969  VSETALL(sw.sw_color, 1);
970  VSETALL(sw.sw_basecolor, 1);
971 #endif
972  if (rdebug&RDEBUG_LIGHT) bu_log("calling viewshade\n");
973  (void)viewshade(ap, pp, &sw);
974  if (rdebug&RDEBUG_LIGHT) bu_log("viewshade returns\n");
975  /* sw_transmit is only return */
976 
977  /* XXX Clouds don't yet attenuate differently based on freq */
978 #ifdef RT_MULTISPECTRAL
979  bn_tabdata_scale(ms_filter_color, ms_filter_color,
980  sw.sw_transmit);
981 #else
982  VSCALE(filter_color, filter_color, sw.sw_transmit);
983 #endif
984  continue;
985  }
986  if (pp->pt_inhit->hit_dist >= ap->a_rt_i->rti_tol.dist)
987  break;
988  if (pp->pt_outhit->hit_dist >= ap->a_rt_i->rti_tol.dist*10)
989  break;
990  }
991 
992 
993  if (pp == PartHeadp) {
994  if (rdebug&RDEBUG_LIGHT) bu_log("pp == PartHeadp\n");
995 
996  pp=PartHeadp->pt_forw;
997  RT_CK_PT(pp);
998 
999  if (lsp->lt_invisible || lsp->lt_infinite) {
1000  light_visible = 1;
1001 #ifdef RT_MULTISPECTRAL
1002  bn_tabdata_copy(ap->a_spectrum, ms_filter_color);
1003 #else
1004  VMOVE(ap->a_color, filter_color);
1005 #endif
1006  reason = "Unobstructed invisible/infinite light";
1007  goto out;
1008  }
1009 
1010  if (air_sols_seen > 0) {
1011  light_visible = 1;
1012 #ifdef RT_MULTISPECTRAL
1013  bn_tabdata_copy(ap->a_spectrum, ms_filter_color);
1014 #else
1015  VMOVE(ap->a_color, filter_color);
1016 #endif
1017  /* XXXXXXX This seems to happen with *every*
1018  * light vis ray through air
1019  */
1020  reason = "Off end of partition list, air was seen";
1021  goto out;
1022  }
1023 
1024  if (pp->pt_inhit->hit_dist <= ap->a_rt_i->rti_tol.dist) {
1025  int retval;
1026  /* XXX This is bogus if air is being used */
1027 
1028  /* What has probably happened is that the shadow ray has
1029  * produced an Out-hit from the current solid which looks
1030  * valid, but is in fact an intersection with the current
1031  * hit point.
1032  */
1033 
1034  sub_ap = *ap; /* struct copy */
1035  sub_ap.a_level++;
1036 #ifdef RT_MULTISPECTRAL
1037  sub_ap.a_spectrum = bn_tabdata_dup(ap->a_spectrum);
1038 #endif
1039  /* pt_outhit->hit_point has not been calculated */
1040  VJOIN1(sub_ap.a_ray.r_pt, ap->a_ray.r_pt,
1041  pp->pt_outhit->hit_dist, ap->a_ray.r_dir);
1042 
1043  if (rdebug&RDEBUG_LIGHT) bu_log("hit_dist < tol\n");
1044  retval = rt_shootray(&sub_ap);
1045 
1046  ap->a_user = sub_ap.a_user;
1047  ap->a_uptr = sub_ap.a_uptr;
1048 #ifdef RT_MULTISPECTRAL
1049  bn_tabdata_copy(ap->a_spectrum, sub_ap.a_spectrum);
1050 #else
1051  ap->a_color[0] = sub_ap.a_color[0];
1052  ap->a_color[1] = sub_ap.a_color[1];
1053  ap->a_color[2] = sub_ap.a_color[2];
1054 #endif
1055  VMOVE(ap->a_uvec, sub_ap.a_uvec);
1056  VMOVE(ap->a_vvec, sub_ap.a_vvec);
1057  ap->a_refrac_index = sub_ap.a_refrac_index;
1058  ap->a_cumlen = sub_ap.a_cumlen;
1059  ap->a_return = sub_ap.a_return;
1060 
1061  light_visible = retval;
1062  reason = "pressed on past start point";
1063  goto out;
1064  }
1065 
1066 
1067  bu_log("light_hit: ERROR, nothing hit, sxy=%d, %d, dtol=%e\n",
1068  ap->a_x, ap->a_y,
1069  ap->a_rt_i->rti_tol.dist);
1070  rt_pr_partitions(ap->a_rt_i, PartHeadp, "light_hit pt list");
1071  light_visible = 0;
1072  reason = "error, nothing hit";
1073  goto out;
1074  }
1075 
1076  regp = pp->pt_regionp;
1077 
1078  /* Check to see if we hit the light source */
1079  if (lsp->lt_rp == regp) {
1080 #ifdef RT_MULTISPECTRAL
1081  bn_tabdata_copy(ap->a_spectrum, ms_filter_color);
1082 #else
1083  VMOVE(ap->a_color, filter_color);
1084 #endif
1085  light_visible = 1;
1086  reason = "hit light";
1087  goto out;
1088  }
1089 
1090  /* if the region we hit is a light source be generous */
1091  {
1092  struct light_specific *lspi;
1093  for (BU_LIST_FOR(lspi, light_specific, &(LightHead.l))) {
1094  if (lspi->lt_rp == regp) {
1095 #ifdef RT_MULTISPECTRAL
1096  bn_tabdata_copy(ap->a_spectrum, ms_filter_color);
1097 #else
1098  VMOVE(ap->a_color, filter_color);
1099 #endif
1100  light_visible = 1;
1101  reason = "hit light";
1102  goto out;
1103 
1104  }
1105  }
1106  }
1107 
1108  /* or something further away than a finite invisible light */
1109  if (lsp->lt_invisible && !(lsp->lt_infinite)) {
1110  vect_t tolight;
1111  VSUB2(tolight, lsp->lt_pos, ap->a_ray.r_pt);
1112  if (pp->pt_inhit->hit_dist >= MAGNITUDE(tolight)) {
1113 #ifdef RT_MULTISPECTRAL
1114  bn_tabdata_copy(ap->a_spectrum, ms_filter_color);
1115 #else
1116  VMOVE(ap->a_color, filter_color);
1117 #endif
1118  light_visible = 1;
1119  reason = "hit behind invisible light ==> hit light";
1120  goto out;
1121  }
1122  }
1123 
1124  /* If we hit an entirely opaque object, this light is invisible */
1125  is_proc = ((struct mfuncs *)regp->reg_mfuncs)->mf_flags|MFF_PROC;
1126 
1127 
1128  if (pp->pt_outhit->hit_dist >= INFINITY ||
1129  (regp->reg_transmit == 0 &&
1130  ! is_proc /* procedural shader */)) {
1131 
1132 #ifdef RT_MULTISPECTRAL
1133  bn_tabdata_constval(ap->a_spectrum, 0.0);
1134 #else
1135  VSETALL(ap->a_color, 0);
1136 #endif
1137  light_visible = 0;
1138  reason = "hit opaque object";
1139  goto out;
1140  }
1141 
1142 #ifdef RT_MULTISPECTRAL
1143  /* XXX Check area under spectral curve? What power level for
1144  * thresh?
1145  */
1146 #else
1147  /* See if any further contributions will mater */
1148  if (ap->a_color[0] + ap->a_color[1] + ap->a_color[2] < 0.01) {
1149  /* Any light energy is "fully" attenuated by here */
1150  VSETALL(ap->a_color, 0);
1151  light_visible = 0;
1152  reason = "light fully attenuated before shading";
1153  goto out;
1154  }
1155 #endif
1156 
1157  /*
1158  * Determine transparency parameters of this object.
1159  * All we really need here is the opacity information;
1160  * full shading is not required.
1161  */
1162  sw.sw_inputs = 0;
1163  sw.sw_transmit = sw.sw_reflect = 0.0;
1164  sw.sw_refrac_index = 1.0;
1165  sw.sw_xmitonly = 1; /* only want sw_transmit */
1166  sw.sw_segs = finished_segs;
1167 #ifdef RT_MULTISPECTRAL
1168  bn_tabdata_constval(sw.msw_color, 1.0);
1169  bn_tabdata_constval(sw.msw_basecolor, 1.0);
1170 #else
1171  VSETALL(sw.sw_color, 1);
1172  VSETALL(sw.sw_basecolor, 1);
1173 #endif
1174 
1175  if (rdebug&RDEBUG_LIGHT) bu_log("calling viewshade\n");
1176  (void)viewshade(ap, pp, &sw);
1177  if (rdebug&RDEBUG_LIGHT) bu_log("viewshade back\n");
1178  /* sw_transmit is output */
1179 
1180 #ifdef RT_MULTISPECTRAL
1181  bn_tabdata_scale(ms_filter_color, ms_filter_color, sw.sw_transmit);
1182  /* XXX Power level check again? */
1183 #else
1184  VSCALE(filter_color, filter_color, sw.sw_transmit);
1185  if (filter_color[0] + filter_color[1] + filter_color[2] < 0.01) {
1186  /* Any recursion won't be significant */
1187  VSETALL(ap->a_color, 0);
1188  light_visible = 0;
1189  reason = "light fully attenuated after shading";
1190  goto out;
1191  }
1192 #endif
1193  /*
1194  * Push on to exit point, and trace on from there.
1195  * Transmission so far is passed along in sub_ap.a_color[];
1196  * Don't even think of trying to refract, or we will miss the light!
1197  */
1198  sub_ap = *ap; /* struct copy */
1199  sub_ap.a_level = ap->a_level+1;
1200 #ifdef RT_MULTISPECTRAL
1201  sub_ap.a_spectrum = bn_tabdata_dup(ap->a_spectrum);
1202 #endif
1203  {
1204  register fastf_t f;
1205  f = pp->pt_outhit->hit_dist + ap->a_rt_i->rti_tol.dist;
1206  VJOIN1(sub_ap.a_ray.r_pt, ap->a_ray.r_pt, f, ap->a_ray.r_dir);
1207  }
1208  sub_ap.a_purpose = "light transmission after filtering";
1209  if (rdebug&RDEBUG_LIGHT)
1210  bu_log("shooting level %d from %d\n",
1211  sub_ap.a_level, __LINE__);
1212  light_visible = rt_shootray(&sub_ap);
1213  if (rdebug&RDEBUG_LIGHT)
1214  if (light_visible < 0)
1215  bu_log("%s:%d\n", __FILE__, __LINE__);
1216 
1217 #ifdef RT_MULTISPECTRAL
1218  bn_tabdata_mul(ap->a_spectrum, sub_ap.a_spectrum, ms_filter_color);
1219 #else
1220  VELMUL(ap->a_color, sub_ap.a_color, filter_color);
1221 #endif
1222  reason = "after filtering";
1223 out:
1224 
1225 #ifdef RT_MULTISPECTRAL
1226  if (ms_filter_color) bn_tabdata_free(ms_filter_color);
1227  if (sw.msw_color) bn_tabdata_free(sw.msw_color);
1228  if (sw.msw_basecolor) bn_tabdata_free(sw.msw_basecolor);
1229  if (sub_ap.a_spectrum) bn_tabdata_free(sub_ap.a_spectrum);
1230  if (rdebug & RDEBUG_LIGHT) {
1231  bu_log("light vis=%d %s %s %s ",
1232  light_visible,
1233  lsp->lt_name,
1234  reason,
1235  regp ? regp->reg_name : "");
1236  bn_pr_tabdata("light spectrum", ap->a_spectrum);
1237  }
1238 #else
1239  if (rdebug & RDEBUG_LIGHT) bu_log("light vis=%d %s (%4.2f, %4.2f, %4.2f) %s %s\n",
1240  light_visible,
1241  lsp->lt_name,
1242  V3ARGS(ap->a_color), reason,
1243  regp ? regp->reg_name : "");
1244 #endif
1245  return light_visible;
1246 }
1247 
1248 
1249 /**
1250  * If there is no explicit light solid in the model, we will always
1251  * "miss" the light, so return light_visible = TRUE.
1252  */
1253 int
1254 light_miss(register struct application *ap)
1255 {
1256  struct light_specific *lsp = (struct light_specific *)(ap->a_uptr);
1257 
1258  RT_CK_LIGHT(lsp);
1259  if (lsp->lt_invisible || lsp->lt_infinite) {
1260  VSETALL(ap->a_color, 1);
1261  if (rdebug & RDEBUG_LIGHT) bu_log("light_miss vis=1\n");
1262  return 1; /* light_visible = 1 */
1263  }
1264 
1265  if (rdebug & RDEBUG_LIGHT) {
1266  bu_log("light ray missed non-infinite, visible light source\n");
1267  bu_log("on pixel: %d %d\n", ap->a_x, ap->a_y);
1268  bu_log("ray: (%g %g %g) -> %g %g %g\n", V3ARGS(ap->a_ray.r_pt), V3ARGS(ap->a_ray.r_dir));
1269  bu_log("a_level: %d\n", ap->a_level);
1270  }
1271 
1272  /* Missed light, either via blockage or dither. Return black */
1273  VSETALL(ap->a_color, 0);
1274  if (rdebug & RDEBUG_LIGHT) bu_log("light_miss vis=0\n");
1275  return -1; /* light_visible = 0 */
1276 }
1277 
1278 
1279 #define VF_SEEN 1
1280 #define VF_BACKFACE 2
1281 
1282 /**
1283  * Compute 1 light visibility ray from a hit point to the light.
1284  * Called by light_obs() to determine light visibility.
1285  */
1286 static int
1287 light_vis(struct light_obs_stuff *los, char *flags)
1288 {
1289  const double cosine89_99deg = 0.0001745329;
1290  struct application sub_ap;
1291  double radius = 0.0;
1292  double angle = 0.0;
1293  double cos_angle, x, y;
1294  point_t shoot_pt;
1295  vect_t shoot_dir;
1296  int shot_status;
1297  vect_t dir, rdir;
1298  int idx;
1299  int k = 0;
1300  struct light_pt *lpt;
1301  int tryagain = 0;
1302  double VisRayvsLightN;
1303  double VisRayvsSurfN;
1304 
1305  if (rdebug & RDEBUG_LIGHT) bu_log("light_vis\n");
1306 
1307  /* compute the light direction */
1308  if (los->lsp->lt_infinite) {
1309  /* Infinite lights are point sources, no fuzzy penumbra */
1310  VMOVE(shoot_dir, los->lsp->lt_vec);
1311 
1312  } else if (los->lsp->lt_pt_count > 0) {
1313  /* pick a point at random from the list of points on
1314  * the surface of the light. If the normals indicate
1315  * inter-visibility, then shoot at that point
1316  */
1317 
1318  idx = los->lsp->lt_pt_count *
1319  fabs(bn_rand_half(los->ap->a_resource->re_randptr)) *
1320  2.0;
1321  if (idx == los->lsp->lt_pt_count) idx--;
1322 
1323  reusept:
1324 
1325  for (k=idx; ((k+1) % los->lsp->lt_pt_count) != idx;
1326  k = (k+1) % los->lsp->lt_pt_count) {
1327  if (rdebug & RDEBUG_LIGHT)
1328  bu_log("checking sample pt %d\n", k);
1329 
1330  if (flags[k] & VF_SEEN) continue;
1331  if (flags[k] & VF_BACKFACE) continue;
1332 
1333  /* we've got a candidate, check for backfacing */
1334  if (rdebug & RDEBUG_LIGHT)
1335  bu_log("\tpossible sample pt %d\n", k);
1336 
1337  lpt = &los->lsp->lt_sample_pts[k];
1338 
1339  VSUB2(dir, lpt->lp_pt, los->swp->sw_hit.hit_point);
1340  VUNITIZE(dir);
1341  VREVERSE(rdir, dir);
1342 
1343 
1344  /* if the surface normals of the light and hit point
1345  * indicate that light could pass between the two
1346  * points, then we have a good choice
1347  *
1348  * If the light point has no surface normal, then
1349  * this is a general point usable from any angle
1350  * so again we can shoot at this point
1351  *
1352  * We tolerance this down so that light points which
1353  * are in the plane of the hit point are not candidates
1354  * (since the light on the surface from such would be
1355  * very small). We also tolerance the normal on the
1356  * light to the visibility ray so that points on the
1357  * perimeter of the presented area of the light source
1358  * are not chosen. This helps avoid shooting at points
1359  * on the light source which machine floating-point
1360  * inaccuracies would cause the ray to miss.
1361  */
1362 
1363  VisRayvsSurfN
1364  = VDOT(los->swp->sw_hit.hit_normal, dir);
1365 
1366  if (VNEAR_ZERO(lpt->lp_norm, SMALL_FASTF)) {
1367  VisRayvsLightN = 1.0;
1368  } else {
1369  VisRayvsLightN = VDOT(lpt->lp_norm, rdir);
1370  }
1371 
1372 
1373  if (VisRayvsLightN > cosine89_99deg &&
1374  VisRayvsSurfN > cosine89_99deg) {
1375 
1376  /* ok, we can shoot at this sample point */
1377  if (rdebug & RDEBUG_LIGHT)
1378  bu_log("\tPt %d selected... OK normal %g %g %g\n",
1379  k, V3ARGS(lpt->lp_norm));
1380 
1381  flags[k] |= VF_SEEN;
1382 
1383  goto done;
1384  }
1385 
1386  if (rdebug & RDEBUG_LIGHT) {
1387  bu_log("\tbackfacing\n");
1388  bu_log("VisRayvsLightN %g\n", VisRayvsLightN);
1389  bu_log("VisRayvsSurfN %g\n", VisRayvsSurfN);
1390  VPRINT("norm", lpt->lp_norm);
1391  }
1392  /* the sample point is backfacing to the location
1393  * we want to test from
1394  */
1395  flags[k] |= VF_BACKFACE;
1396  }
1397 
1398  /* if we get here, then everything is used or backfacing */
1399 
1400  if (rdebug & RDEBUG_LIGHT) {
1401  bu_log("all light sample pts used. trying to recycle\n");
1402  }
1403 
1404  tryagain = 0;
1405  for (k = 0; k < los->lsp->lt_pt_count; k++) {
1406  if (flags[k] & VF_SEEN) {
1407  /* this one was used, we can re-use it */
1408  tryagain = 1;
1409  flags[k] &= VF_BACKFACE;
1410  }
1411  }
1412  if (tryagain) {
1413  if (rdebug & RDEBUG_LIGHT) {
1414  bu_log("recycling\n");
1415  }
1416  goto reusept;
1417  }
1418  /* at this point, we have no candidate points available to
1419  * shoot at
1420  */
1421  if (rdebug & RDEBUG_LIGHT) {
1422  bu_log("can't find point to shoot at\n");
1423  }
1424  return 0;
1425  done:
1426  /* we've got a point on the surface of the light to shoot at */
1427  VMOVE(shoot_pt, lpt->lp_pt);
1428  VSUB2(shoot_dir, shoot_pt, los->swp->sw_hit.hit_point);
1429 
1430  } else {
1431 
1432  if (rdebug & RDEBUG_LIGHT)
1433  bu_log("shooting at approximating sphere\n");
1434 
1435  /* We're going to shoot at a point on the approximating
1436  * sphere for the light source. We pick a point on the
1437  * circle (presented area) for the light source from this
1438  * angle. This is done by picking random radius and angle
1439  * values on the disc.
1440  */
1441  radius = los->lsp->lt_radius *
1442  /* drand48(); */
1443  fabs(bn_rand_half(los->ap->a_resource->re_randptr)
1444  * 2.0);
1445  angle = M_2PI *
1446  /* drand48(); */
1447  (bn_rand_half(los->ap->a_resource->re_randptr) + 0.5);
1448 
1449  y = radius * bn_tab_sin(angle);
1450 
1451  /* by adding 90 degrees to the angle, the sin of the new
1452  * angle becomes the cosine of the old angle. Thus we
1453  * can use the sine table to compute the value, and avoid
1454  * the expensive actual computation. So the next 3 lines
1455  * replace:
1456  * x = radius * cos(angle);
1457  */
1458  cos_angle = M_PI_2 + angle;
1459  if (cos_angle > M_2PI)
1460  cos_angle -= M_2PI;
1461 
1462  x = radius * bn_tab_sin(cos_angle);
1463 
1464  VJOIN2(shoot_pt, los->lsp->lt_pos,
1465  x, los->light_x,
1466  y, los->light_y);
1467 
1468  if (rdebug & RDEBUG_LIGHT) {
1469  bu_log("light at (%g %g %g) radius %g\n",
1470  V3ARGS(los->lsp->lt_pos),
1471  los->lsp->lt_radius);
1472 
1473  bu_log("\tshooting at radius %g\n", radius);
1474 
1475  bu_log("\ttarget light point %g %g %g\n",
1476  V3ARGS(shoot_pt));
1477  }
1478  VSUB2(shoot_dir, shoot_pt, los->swp->sw_hit.hit_point);
1479  }
1480 
1481  if (rdebug & RDEBUG_LIGHT) {
1482  VPRINT("shoot_dir", shoot_dir);
1483  }
1484 
1485  if (rdebug& RDEBUG_RAYPLOT) {
1486  point_t ray_endpt;
1487 
1488  /* Yellow -- light visibility ray */
1489  VADD2(ray_endpt, los->swp->sw_hit.hit_point, shoot_dir);
1491  pl_color(stdout, 200, 200, 0);
1492  pdv_3line(stdout, los->swp->sw_hit.hit_point, ray_endpt);
1494  }
1495 
1496  VUNITIZE(shoot_dir);
1497 
1498  /*
1499  * See if ray from hit point to light lies within light beam
1500  * Note: this is should always be true for infinite lights!
1501  */
1502  if (-VDOT(shoot_dir, los->lsp->lt_aim) < los->lsp->lt_cosangle) {
1503  /* dark (outside of light beam) */
1504  if (rdebug & RDEBUG_LIGHT)
1505  bu_log("point outside beam, obscured: %s\n",
1506  los->lsp->lt_name);
1507  return 0;
1508  }
1509 
1510 
1511  if (!(los->lsp->lt_shadows)) {
1512  /* "fill light" in beam, don't care about shadows */
1513  if (rdebug & RDEBUG_LIGHT)
1514  bu_log("fill light, no shadow, visible: %s\n",
1515  los->lsp->lt_name);
1516 
1517 #ifdef RT_MULTISPECTRAL
1518  /* XXX Need a power level for this! */
1519  bn_tabdata_constval(((struct bn_tabdata *)los->inten), 1.0);
1520 #else
1521  VSETALL(((vectp_t)los->inten), 1);
1522 #endif
1523 
1524  return -1;
1525  }
1526 
1527 
1528  /*
1529  * Fire ray at light source to check for shadowing.
1530  * (This SHOULD actually return an energy spectrum).
1531  * Advance start point slightly off surface.
1532  */
1533  sub_ap = *los->ap; /* struct copy */
1534  RT_CK_AP(&sub_ap);
1535 
1536  VMOVE(sub_ap.a_ray.r_dir, shoot_dir);
1537  {
1538  register fastf_t f;
1539  f = los->ap->a_rt_i->rti_tol.dist;
1540  VJOIN1(sub_ap.a_ray.r_pt, los->swp->sw_hit.hit_point, f,
1541  shoot_dir);
1542  }
1543  sub_ap.a_rbeam = los->ap->a_rbeam +
1544  los->swp->sw_hit.hit_dist *
1545  los->ap->a_diverge;
1546  sub_ap.a_diverge = los->ap->a_diverge;
1547 
1548  sub_ap.a_hit = light_hit;
1549  sub_ap.a_miss = light_miss;
1550  sub_ap.a_logoverlap = los->ap->a_logoverlap;
1551  sub_ap.a_user = -1; /* sanity */
1552  sub_ap.a_uptr = (void *)los->lsp; /* so we can tell.. */
1553  sub_ap.a_level = 0;
1554  /* Will need entry & exit pts, for filter glass ==> 2 */
1555  /* Continue going through air ==> negative */
1556  sub_ap.a_onehit = -2;
1557 
1558  VSETALL(sub_ap.a_color, 1); /* vis intens so far */
1559  sub_ap.a_purpose = los->lsp->lt_name; /* name of light shot at */
1560 
1561  RT_CK_LIGHT((struct light_specific *)(sub_ap.a_uptr));
1562  RT_CK_AP(&sub_ap);
1563 
1564  if (rdebug & RDEBUG_LIGHT)
1565  bu_log("shooting level %d from %d\n", sub_ap.a_level, __LINE__);
1566 
1567  /* see if we are in the dark. */
1568  shot_status = rt_shootray(&sub_ap);
1569 
1570  if (shot_status > 0) {
1571  /* light visible */
1572  if (rdebug & RDEBUG_LIGHT)
1573  bu_log("light visible: %s\n", los->lsp->lt_name);
1574 
1575 #ifdef RT_MULTISPECTRAL
1576  BN_CK_TABDATA(sub_ap.a_spectrum);
1577  if (*(los->inten) == BN_TABDATA_NULL) {
1578  *(los->inten) = sub_ap.a_spectrum;
1579  } else {
1580  BN_CK_TABDATA(*(los->inten));
1581  bn_tabdata_add(*(los->inten),
1582  *(los->inten),
1583  sub_ap.a_spectrum);
1584 
1585  bn_tabdata_free(sub_ap.a_spectrum);
1586  }
1587  sub_ap.a_spectrum = BN_TABDATA_NULL;
1588 #else
1589  VMOVE(los->inten, sub_ap.a_color);
1590 #endif
1591 
1592  return 1;
1593  }
1594 
1595  /* dark (light obscured) */
1596  if (rdebug & RDEBUG_LIGHT)
1597  bu_log("light obscured: %s\n", los->lsp->lt_name);
1598 
1599  return 0;
1600 }
1601 
1602 
1603 /**
1604  * Determine the visibility of each light source in the scene from a
1605  * particular location. It is up to the caller to apply
1606  * sw_lightfract[] to lp_color, etc.
1607  *
1608  * Sets swp:
1609  * sw_tolight[]
1610  * sw_intensity[] or msw_intensity[]
1611  * sw_visible[]
1612  * sw_lightfract[]
1613  *
1614  * References ap:
1615  * a_resource
1616  * a_rti_i->rti_tol
1617  * a_rbeam
1618  * a_diverge
1619  */
1620 void
1621 light_obs(struct application *ap, struct shadework *swp, int have)
1622 {
1623  register struct light_specific *lsp;
1624  register int i;
1625  register fastf_t *tl_p;
1626  int vis_ray;
1627  int tot_vis_rays;
1628  int visibility;
1629  struct light_obs_stuff los;
1630  static int rand_idx;
1631  int flag_size = 0;
1632 
1633  /* use a constant buffer to minimize number of malloc/free calls per ray */
1634  char static_flags[SOME_LIGHT_SAMPLES] = {0};
1635  char *flags = static_flags;
1636 
1637  if (rdebug & RDEBUG_LIGHT) {
1638  bu_log("computing Light obscuration: start\n");
1639  }
1640 
1641  RT_CK_AP(ap);
1642  los.rand_idx = &rand_idx;
1643  los.ap = ap;
1644  los.swp = swp;
1645 
1646  /* find largest sampled light */
1647  for (BU_LIST_FOR(lsp, light_specific, &(LightHead.l))) {
1648  if (lsp->lt_pt_count > flag_size) {
1649  flag_size = lsp->lt_pt_count;
1650  }
1651  }
1652  if (flag_size > SOME_LIGHT_SAMPLES) {
1653  flags = (char *)bu_calloc(flag_size, sizeof(char), "callocate flags array");
1654  }
1655 
1656  /*
1657  * Determine light visibility
1658  *
1659  * The sw_intensity field does NOT include the light's
1660  * emission spectrum (color), only path attenuation.
1661  * sw_intensity=(1, 1, 1) for no attenuation.
1662  */
1663  tl_p = swp->sw_tolight;
1664 
1665  i = 0;
1666  for (BU_LIST_FOR(lsp, light_specific, &(LightHead.l))) {
1667  RT_CK_LIGHT(lsp);
1668 
1669  if (rdebug & RDEBUG_LIGHT)
1670  bu_log("computing for light %d\n", i);
1671  swp->sw_lightfract[i] = 0.0;
1672 
1673  if (lsp->lt_infinite || lsp->lt_shadows == 0) tot_vis_rays = 1;
1674  else tot_vis_rays = lsp->lt_shadows;
1675 
1676  los.lsp = lsp;
1677 #ifdef RT_MULTISPECTRAL
1678  if (swp->msw_intensity[i]) BN_CK_TABDATA(swp->msw_intensity[i]);
1679  los.inten = &swp->msw_intensity[i];
1680 #else
1681  los.inten = &swp->sw_intensity[3*i];
1682 #endif
1683 
1684  /* create a coordinate system about the light center with the
1685  * hitpoint->light ray as one of the axes
1686  */
1687  if (lsp->lt_infinite) {
1688  VMOVE(los.to_light_center, lsp->lt_vec);
1689  } else {
1690  VSUB2(los.to_light_center, lsp->lt_pos, swp->sw_hit.hit_point);
1691  }
1692  VUNITIZE(los.to_light_center);
1694  VCROSS(los.light_y, los.to_light_center, los.light_x);
1695 
1696  /*
1697  * If we have a normal, test against light direction
1698  */
1699  if ((have & MFI_NORMAL) && (swp->sw_transmit <= 0)) {
1700  if (VDOT(swp->sw_hit.hit_normal,
1701  los.to_light_center) < 0) {
1702  /* backfacing, opaque */
1703  if (rdebug & RDEBUG_LIGHT)
1704  bu_log("norm backfacing, opaque surf:%s\n",
1705  lsp->lt_name);
1706  continue;
1707  }
1708  }
1709 
1710  visibility = 0;
1711  if (flag_size > 0) {
1712  memset(flags, 0, flag_size * sizeof(char));
1713  }
1714  for (vis_ray = 0; vis_ray < tot_vis_rays; vis_ray ++) {
1715  int lv;
1716  los.iter = vis_ray;
1717 
1718  if (rdebug & RDEBUG_LIGHT)
1719  bu_log("----------vis_ray %d---------\n",
1720  vis_ray);
1721 
1722  switch (lv = light_vis(&los, flags)) {
1723  case 1:
1724  /* remember the last ray that hit */
1725  VMOVE(tl_p, los.to_light_center);
1726  visibility++;
1727  break;
1728  case -1:
1729  /* this is our clue to give up on
1730  * this light source.
1731  */
1732  VMOVE(tl_p, los.to_light_center);
1733  visibility = vis_ray = tot_vis_rays;
1734  break;
1735  case 0: /* light not visible */
1736  if (rdebug & RDEBUG_LIGHT)
1737  bu_log("light not visible\n");
1738  break;
1739  default:
1740  bu_log("light_vis = %d\n", lv);
1741  }
1742  }
1743  if (visibility) {
1744  swp->sw_visible[i] = lsp;
1745  swp->sw_lightfract[i] =
1746  (fastf_t)visibility / (fastf_t)tot_vis_rays;
1747  } else {
1748  swp->sw_visible[i] = (struct light_specific *)NULL;
1749  }
1750 
1751  /* Advance to next light */
1752  tl_p += 3;
1753  i++;
1754  }
1755  if (flags && flags != static_flags) {
1756  bu_free(flags, "free flags array");
1757  }
1758 
1759  if (rdebug & RDEBUG_LIGHT) bu_log("computing Light obscuration: end\n");
1760 }
1761 
1762 
1763 /**
1764  * Special hook called by view_2init to build 1 or 3 debugging lights.
1765  */
1766 void
1767 light_maker(int num, mat_t v2m)
1768 {
1769  register struct light_specific *lsp;
1770  register int i;
1771  vect_t temp;
1772  vect_t color;
1773  char name[64];
1774 #ifdef RT_MULTISPECTRAL
1775  float fcolor[3];
1776 #endif
1777 
1778  /* Determine the Light location(s) in view space */
1779  for (i = 0; i < num; i++) {
1780  switch (i) {
1781  case 0:
1782  /* 0: At left edge, 1/2 high */
1783  VSET(color, 1, 1, 1); /* White */
1784  VSET(temp, -1, 0, 1);
1785  break;
1786 
1787  case 1:
1788  /* 1: At right edge, 1/2 high */
1789  VSET(color, 1, 1, 1);
1790  VSET(temp, 1, 0, 1);
1791  break;
1792 
1793  case 2:
1794  /* 2: Behind, and overhead */
1795  VSET(color, 1, 1, 1);
1796  VSET(temp, 0, 1, -0.5);
1797  break;
1798 
1799  default:
1800  return;
1801  }
1802 
1803  if (rdebug & RDEBUG_LIGHT) {
1804  /* debugging ascii plot commands drawing from point
1805  * location to origin
1806  */
1807  vect_t tmp;
1808  static vect_t pt = {0.0, 0.0, 0.0};
1809  MAT4X3PNT(tmp, v2m, temp); bu_log("C 0 255 255\nO %g %g %g\n", V3ARGS(tmp));
1810  MAT4X3PNT(tmp, v2m, pt); bu_log("Q %g %g %g\n", V3ARGS(tmp));
1811  }
1812 
1813  BU_GET(lsp, struct light_specific);
1814  BU_LIST_INIT_MAGIC(&(lsp->l), LIGHT_MAGIC);
1815 
1816 #ifdef RT_MULTISPECTRAL
1817  BN_GET_TABDATA(lsp->lt_spectrum, spectrum);
1818  VMOVE(fcolor, color);
1819  rt_spect_reflectance_rgb(lsp->lt_spectrum, fcolor);
1820  bn_tabdata_scale(lsp->lt_spectrum, lsp->lt_spectrum, 1000.0);
1821 #else
1822  VMOVE(lsp->lt_color, color);
1823 #endif
1824 
1825  MAT4X3PNT(lsp->lt_pos, v2m, temp);
1826  VMOVE(lsp->lt_vec, lsp->lt_pos);
1827  VUNITIZE(lsp->lt_vec);
1828 
1829  sprintf(name, "Implicit light %d", i);
1830  lsp->lt_name = bu_strdup(name);
1831 
1832  /* XXX Is it bogus to set lt_aim? */
1833  VSET(lsp->lt_aim, 0, 0, -1); /* any direction: spherical */
1834  lsp->lt_intensity = 1.0;
1835  lsp->lt_radius = 0.1; /* mm, "point" source */
1836  lsp->lt_visible = 0; /* NOT explicitly modeled */
1837  lsp->lt_invisible = 1; /* NOT explicitly modeled */
1838  lsp->lt_shadows = 0; /* no shadows for speed */
1839  lsp->lt_angle = 180; /* spherical emission */
1840  lsp->lt_cosangle = -1; /* cos(180) */
1841  lsp->lt_infinite = 0;
1842  lsp->lt_rp = REGION_NULL;
1843  if (!BU_LIST_IS_INITIALIZED(&(LightHead.l))) {
1844  BU_LIST_INIT(&(LightHead.l));
1845  }
1846  BU_LIST_INSERT(&(LightHead.l), &(lsp->l));
1847  }
1848 }
1849 
1850 
1851 /*
1852  * Local Variables:
1853  * mode: C
1854  * tab-width: 8
1855  * indent-tabs-mode: t
1856  * c-file-style: "stroustrup"
1857  * End:
1858  * ex: shiftwidth=4 tabstop=8
1859  */
#define BN_TABDATA_NULL
Definition: tabdata.h:107
struct xray a_ray
Actual ray to be shot.
Definition: raytrace.h:1583
#define BU_LIST_FOR(p, structure, hp)
Definition: list.h:365
#define RDEBUG_LIGHT
Definition: optical.h:132
HIDDEN void light_print(register struct region *rp, void *dp)
Definition: sh_light.c:565
fastf_t a_cumlen
cumulative length of ray
Definition: raytrace.h:1625
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
#define BU_LIST_INSERT(old, new)
Definition: list.h:183
struct shadework * swp
Definition: sh_light.c:126
struct region * pt_regionp
ptr to containing region
Definition: raytrace.h:580
struct hit * pt_outhit
OUT hit ptr.
Definition: raytrace.h:579
#define bn_rand_half(_p)
Definition: rand.h:97
#define bn_tab_sin(_a)
Definition: rand.h:127
vect_t light_y
Definition: sh_light.c:138
HIDDEN void light_pt_set(const struct bu_structparse *, const char *, void *, const char *, void *)
Definition: sh_light.c:222
#define MF_MAGIC
Definition: magic.h:205
#define BN_GET_TABDATA(_data, _table)
Definition: tabdata.h:114
void bn_tabdata_free(struct bn_tabdata *data)
Definition: tabdata.c:67
float ma_temperature
positive ==> degrees Kelvin
Definition: raytrace.h:523
int viewshade(struct application *app, const struct partition *pp, struct shadework *swp)
Definition: shade.c:259
double dist
>= 0
Definition: tol.h:73
void rt_spect_black_body(struct bn_tabdata *data, double temp, unsigned int n)
Definition: spectrum.c:238
const mat_t bn_mat_identity
Matrix and vector functionality.
Definition: mat.c:46
#define R_DEBUG
Definition: optical.h:115
Definition: clone.c:90
int rt_bound_tree(const union tree *tp, vect_t tree_min, vect_t tree_max)
#define VSET(a, b, c, d)
Definition: color.c:53
#define VSETALL(a, s)
Definition: color.c:54
void bu_semaphore_acquire(unsigned int i)
Definition: semaphore.c:180
Definition: raytrace.h:368
#define BU_LIST_IS_INITIALIZED(_hp)
Definition: list.h:175
struct mfuncs light_mfuncs[]
Definition: sh_light.c:65
#define SMALL_FASTF
Definition: defines.h:342
fastf_t st_aradius
Radius of APPROXIMATING sphere.
Definition: raytrace.h:433
HIDDEN void light_cvt_visible(const struct bu_structparse *, const char *, void *, const char *, void *)
Definition: sh_light.c:167
Header file for the BRL-CAD common definitions.
matp_t st_matp
solid coords to model space, NULL=identity
Definition: raytrace.h:441
const char * reg_name
Identifying string.
Definition: raytrace.h:539
char ma_color_valid
non-0 ==> ma_color is non-default
Definition: raytrace.h:524
struct resource * a_resource
dynamic memory resources
Definition: raytrace.h:1591
int a_onehit
flag to stop on first hit
Definition: raytrace.h:1586
#define RDEBUG_RAYPLOT
Definition: optical.h:141
void light_cleanup(void)
Definition: sh_light.c:838
HIDDEN void light_pt_allocate(register struct light_specific *lsp)
Definition: sh_light.c:198
int(* a_hit)(struct application *, struct partition *, struct seg *)
called when shot hits model
Definition: raytrace.h:1584
struct hit * pt_inhit
IN hit pointer.
Definition: raytrace.h:577
#define HIDDEN
Definition: common.h:86
vect_t light_x
Definition: sh_light.c:137
#define BN_CK_TABDATA(_p)
Definition: tabdata.h:106
void light_obs(struct application *ap, struct shadework *swp, int have)
Definition: sh_light.c:1621
union tree * tb_left
Definition: raytrace.h:1149
struct bu_list l
Definition: raytrace.h:369
#define VF_BACKFACE
Definition: sh_light.c:1280
struct soltab * tu_stp
Definition: raytrace.h:1156
void bu_struct_print(const char *title, const struct bu_structparse *parsetab, const char *base)
Definition: parse.c:1221
void bn_tabdata_copy(struct bn_tabdata *out, const struct bn_tabdata *in)
Definition: tabdata.c:1025
fastf_t a_color[3]
application-specific color
Definition: raytrace.h:1620
HIDDEN int light_setup(struct region *rp, struct bu_vls *matparm, void **dpp, const struct mfuncs *mfp, struct rt_i *rtip)
Definition: color.c:49
COMPLEX data[64]
Definition: fftest.c:34
struct rt_i * a_rt_i
this librt instance
Definition: raytrace.h:1588
#define BU_CK_VLS(_vp)
Definition: vls.h:69
void * memset(void *s, int c, size_t n)
int PM_Activated
Definition: photonmap.c:43
struct application * ap
Definition: sh_light.c:125
#define BU_ALLOC(_ptr, _type)
Definition: malloc.h:223
void bn_tabdata_scale(struct bn_tabdata *out, const struct bn_tabdata *in1, double scale)
void * bu_calloc(size_t nelem, size_t elsize, const char *str)
Definition: malloc.c:321
fastf_t a_diverge
slope of beam divergence/mm
Definition: raytrace.h:1600
#define RT_CK_AP(_p)
Definition: raytrace.h:1674
int a_user
application-specific value
Definition: raytrace.h:1617
#define V3ARGS(a)
Definition: color.c:56
int a_x
Screen X of ray, if applicable.
Definition: raytrace.h:1596
void bn_tabdata_mul(struct bn_tabdata *out, const struct bn_tabdata *in1, const struct bn_tabdata *in2)
Definition: tabdata.c:149
#define BU_GET(_ptr, _type)
Definition: malloc.h:201
#define SQRT_SMALL_FASTF
Definition: defines.h:346
#define BU_LIST_INIT_MAGIC(_hp, _magic)
Definition: list.h:156
struct bn_tabdata * a_spectrum
application-specific bn_tabdata pointer
Definition: raytrace.h:1619
fastf_t * inten
Definition: sh_light.c:133
int reg_transmit
flag: material transmits light
Definition: raytrace.h:549
#define LIGHT_MAGIC
Definition: magic.h:204
int a_level
recursion level (for printing)
Definition: raytrace.h:1595
struct tree::tree_node tr_b
int light_init(struct application *ap)
Definition: sh_light.c:748
void * bu_realloc(void *ptr, size_t siz, const char *str)
const char * a_purpose
Debug string: purpose of ray.
Definition: raytrace.h:1598
struct bn_tabdata * bn_tabdata_get_constval(double val, const struct bn_table *tabp)
Definition: tabdata.c:1054
#define UNUSED(parameter)
Definition: common.h:239
vect_t a_vvec
application-specific vector
Definition: raytrace.h:1623
void bn_tabdata_add(struct bn_tabdata *out, const struct bn_tabdata *in1, const struct bn_tabdata *in2)
Definition: tabdata.c:123
void rt_pr_partitions(const struct rt_i *rtip, const struct partition *phead, const char *title)
HIDDEN void aim_set(const struct bu_structparse *, const char *, void *, const char *, void *)
Definition: sh_light.c:147
goto out
Definition: nmg_mod.c:3846
struct light_specific * lsp
Definition: sh_light.c:127
size_t sp_offset
Definition: parse.h:141
struct partition * pt_back
backwards link
Definition: raytrace.h:575
void rt_spect_reflectance_rgb(struct bn_tabdata *curve, const float *rgb)
Definition: spectrum.c:191
void bn_tabdata_constval(struct bn_tabdata *data, double val)
Definition: tabdata.c:1072
#define BU_STRUCTPARSE_FUNC_NULL
Definition: parse.h:153
void bu_semaphore_release(unsigned int i)
Definition: semaphore.c:218
void pl_color(register FILE *plotfp, int r, int g, int b)
Definition: plot3.c:325
vect_t r_dir
Direction of ray (UNIT Length)
Definition: raytrace.h:219
void pdv_3line(register FILE *plotfp, const fastf_t *a, const fastf_t *b)
Definition: plot3.c:642
void bn_pr_tabdata(const char *title, const struct bn_tabdata *data)
Definition: tabdata.c:816
struct bn_tol rti_tol
Math tolerances for this model.
Definition: raytrace.h:1765
float ma_color[3]
explicit color: 0..1
Definition: raytrace.h:522
vect_t to_light_center
Definition: sh_light.c:136
int a_return
Return of a_hit()/a_miss()
Definition: raytrace.h:1601
struct mater_info reg_mater
Real material information.
Definition: raytrace.h:546
#define BU_LIST_INIT(_hp)
Definition: list.h:148
void(* a_logoverlap)(struct application *, const struct partition *, const struct bu_ptbl *, const struct partition *)
called to log overlaps
Definition: raytrace.h:1594
point_t r_pt
Point at which ray starts.
Definition: raytrace.h:218
vect_t a_uvec
application-specific vector
Definition: raytrace.h:1622
int reg_aircode
Region ID AIR code.
Definition: raytrace.h:543
struct bu_structparse light_parse[]
Definition: sh_light.c:86
int light_miss(register struct application *ap)
Definition: sh_light.c:1254
#define BU_SEM_SYSCALL
Definition: parallel.h:178
void bn_vec_ortho(vect_t out, const vect_t in)
#define RT_CK_PT(_p)
Definition: raytrace.h:589
int bu_struct_parse(const struct bu_vls *in_vls, const struct bu_structparse *desc, const char *base, void *data)
Definition: parse.c:878
int a_y
Screen Y of ray, if applicable.
Definition: raytrace.h:1597
#define RT_CK_PT_HD(_p)
Definition: raytrace.h:591
#define VF_SEEN
Definition: sh_light.c:1279
#define BN_RANDOM(_i)
Definition: rand.h:73
#define REGION_NULL
Definition: raytrace.h:558
int light_hit(struct application *ap, struct partition *PartHeadp, struct seg *finished_segs)
Definition: sh_light.c:887
void light_gen_sample_pts(struct application *upap, struct light_specific *lsp)
Definition: sh_light.c:492
union tree * reg_treetop
Pointer to boolean tree.
Definition: raytrace.h:540
struct bn_tabdata * bn_tabdata_dup(const struct bn_tabdata *in)
Definition: tabdata.c:1041
Definition: color.c:51
#define LIGHT_O(m)
Definition: sh_light.c:46
int rt_shootray(struct application *ap)
void * a_uptr
application-specific pointer
Definition: raytrace.h:1618
HIDDEN int light_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp)
Definition: sh_light.c:262
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
fastf_t a_rbeam
initial beam radius (mm)
Definition: raytrace.h:1599
#define BU_CK_LIST_HEAD(_p)
Definition: list.h:142
struct bn_table * spectrum
Definition: init.c:41
int(* a_miss)(struct application *)
called when shot misses
Definition: raytrace.h:1585
#define OP_SOLID
Leaf: tr_stp -> solid.
Definition: raytrace.h:1126
struct bu_structparse light_print_tab[]
Definition: sh_light.c:72
HIDDEN void light_free(void *cp)
Definition: sh_light.c:572
#define RT_HIT_NORMAL(_normal, _hitp, _stp, _unused, _flipflag)
Definition: raytrace.h:273
int * rand_idx
Definition: sh_light.c:128
float * re_randptr
ptr into random number table
Definition: raytrace.h:1457
#define BU_LIST_DEQUEUE(cur)
Definition: list.h:209
struct light_specific LightHead
Definition: sh_light.c:50
struct partition * pt_forw
forwards link
Definition: raytrace.h:574
void light_maker(int num, mat_t v2m)
Definition: sh_light.c:1767
fastf_t hit_dist
dist from r_pt to hit_point
Definition: raytrace.h:250
fastf_t a_refrac_index
current index of refraction
Definition: raytrace.h:1624
Definition: vls.h:56
HIDDEN const point_t delta
Definition: sh_prj.c:618
double fastf_t
Definition: defines.h:300
#define VPRINT(a, b)
Definition: raytrace.h:1881
Header file for the BRL-CAD Optical Library, LIBOPTICAL.
void * reg_mfuncs
User appl. funcs for material.
Definition: raytrace.h:547
struct tree::tree_leaf tr_a
int rdebug
Definition: init.c:39
#define BU_LIST_PREV(structure, hp)
Definition: list.h:310
double AmbientIntensity
Definition: init.c:40
Definition: color.c:50
#define bu_strdup(s)
Definition: str.h:71
point_t st_center
Centroid of solid.
Definition: raytrace.h:432
#define BU_STR_EQUAL(s1, s2)
Definition: str.h:126