/* * B R D F _ R E N D E R * Color pixel based on the energy of a point light source (Eps) plus some diffuse illumination (Epd) reflected from the point <x, y> : E = Epd + Eps (1) The energy reflected from diffuse illumination is the product of the reflectance coefficient at point P (Rp) and the diffuse illumination (Id) : Epd = Rp * Id (2) The energy reflected from the point light source is calculated by the sum of the diffuse reflectance (Rd) and the specular reflectance (Rs), multiplied by the intensity of the light source (Ips) : Eps = (Rd + Rs) * Ips (3) The diffuse reflectance is calculated by the product of the reflectance coefficient (Rp) and the cosine of the angle of incidence (I) and normalized by PI : Rd = Rp * cos(I) / PI (4) The specular reflectance is calculated by the product of the specular reflectance coeffient and a term dependent on the surface roughness : Rs = W(I, O) * R(I, O, r) (5) Where, I is the angle of incidence. O is the angle to the observer. r is the standard deviation (RMS) of the surface slope. W returns the specular reflection coefficient as a function of the angle of incidence, and the viewer angle. R is a surface roughness term. */ HIDDEN int brdf_render(register struct application *ap, struct partition *pp, struct shadework *swp, char *dp) { register struct light_specific *lp; register fastf_t *intensity, *to_light; register int i; register fastf_t cosi, cosr; register fastf_t refl; vect_t h_dir; vect_t to_eye; vect_t work; vect_t cprod; /* color product */ point_t matcolor; /* Material color */ struct brdf_specific *ps = (struct brdf_specific *)dp; if (ps->magic != BRDF_MAGIC ) bu_log("brdf_render: bad magic\n"); if (rdebug&RDEBUG_SHADE) bu_struct_print( "brdf_render", brdf_parse, (char *)ps ); swp->sw_transmit = ps->transmit; swp->sw_reflect = ps->reflect; swp->sw_refrac_index = ps->refrac_index; swp->sw_extinction = ps->extinction; if (swp->sw_xmitonly ) { if (swp->sw_reflect > 0 || swp->sw_transmit > 0 ) (void)rr_render( ap, pp, swp ); return(1); /* done */ } VMOVE( matcolor, swp->sw_color ); /* Diffuse reflectance from "Ambient" light source (at eye) */ if ((cosr = -VDOT( swp->sw_hit.hit_normal, ap->a_ray.r_dir )) > 0.0 ) { if (cosr > 1.00001 ) { bu_log("cosAmb=1+%g (x%d, y%d, lvl%d)\n", cosr-1, ap->a_x, ap->a_y, ap->a_level); cosr = 1; } refl = cosr * AmbientIntensity; VSCALE( swp->sw_color, matcolor, refl ); } else { VSETALL( swp->sw_color, 0 ); } VREVERSE( to_eye, ap->a_ray.r_dir ); /* Consider effects of each light source */ for ( i=ap->a_rt_i->rti_nlights-1; i>=0; i-- ) { fastf_t cos_tmp; fastf_t tan_sq; double exponent; if ((lp = (struct light_specific *)swp->sw_visible[i]) == LIGHT_NULL ) continue; /* Light is not shadowed -- add this contribution */ intensity = swp->sw_intensity+3*i; to_light = swp->sw_tolight+3*i; if ((cosi = VDOT( swp->sw_hit.hit_normal, to_light )) > 0.0 ) { if (cosi > 1.00001 ) { bu_log("cosI=1+%g (x%d, y%d, lvl%d)\n", cosi-1, ap->a_x, ap->a_y, ap->a_level); cosi = 1; } /* Diffuse reflectance from this light source. */ refl = cosi * lp->lt_fraction * ps->diffuse_refl; VELMUL( work, lp->lt_color, intensity ); VELMUL( cprod, matcolor, work ); VJOIN1( swp->sw_color, swp->sw_color, refl, cprod ); /* Calculate specular reflectance. */ if (NEAR_ZERO( ps->rms_sq, SMALL_FASTF ) ) continue; VADD2( h_dir, to_eye, to_light ) VUNITIZE( h_dir ); cos_tmp = VDOT( h_dir, swp->sw_hit.hit_normal ); if (cos_tmp <= 0.0 ) continue; cos_tmp *= cos_tmp; if (NEAR_ZERO( cos_tmp, SMALL_FASTF ) ) continue; tan_sq = (1.0-cos_tmp)/cos_tmp; exponent = (-tan_sq/ps->rms_sq ); refl = ps->specular_refl * lp->lt_fraction * exp( exponent ) / sqrt( cosi * cosr ) / ps->denom; if (refl > 1.0 ) refl = 1.0; VELMUL( work, lp->lt_color, intensity ); VJOIN1( swp->sw_color, swp->sw_color, refl, work ); } } if (swp->sw_reflect > 0 || swp->sw_transmit > 0 ) (void)rr_render( ap, pp, swp ); return(1); }
int read_arbn(char *name) { int npt; /* # vertex pts to be read in */ int npe; /* # planes from 3 vertex points */ int neq; /* # planes from equation */ int nae; /* # planes from az, el & vertex index */ int nface; /* total number of faces */ double *input_points = (double *)0; double *vertex = (double *)0; /* vertex list of final solid */ int last_vertex; /* index of first unused vertex */ int max_vertex; /* size of vertex array */ int *used = (int *)0; /* plane eqn use count */ plane_t *eqn = (plane_t *)0; /* plane equations */ int cur_eq = 0; /* current (free) equation number */ int symm = 0; /* symmetry about Y used */ register int i; int j; int k; register int m; point_t cent; /* centroid of arbn */ struct bn_tol tol; /* XXX The tolerance here is sheer guesswork */ tol.magic = BN_TOL_MAGIC; tol.dist = 0.005; tol.dist_sq = tol.dist * tol.dist; tol.perp = 1e-6; tol.para = 1 - tol.perp; npt = getint( scard, 10+0*10, 10 ); npe = getint( scard, 10+1*10, 10 ); neq = getint( scard, 10+2*10, 10 ); nae = getint( scard, 10+3*10, 10 ); nface = npe + neq + nae; if ( npt < 1 ) { /* Having one point is necessary to compute centroid */ printf("arbn defined without at least one point\n"); bad: if (npt>0) eat( (npt+1)/2 ); /* vertex input_points */ if (npe>0) eat( (npe+5)/6 ); /* vertex pt index numbers */ if (neq>0) eat( neq ); /* plane eqns? */ if (nae>0) eat( (nae+1)/2 ); /* az el & vertex index? */ return(-1); } /* Allocate storage for plane equations */ eqn = (plane_t *)bu_malloc(nface*sizeof(plane_t), "eqn"); /* Allocate storage for per-plane use count */ used = (int *)bu_malloc(nface*sizeof(int), "used"); if ( npt >= 1 ) { /* Obtain vertex input_points */ input_points = (double *)bu_malloc(npt*3*sizeof(double), "input_points"); if ( getxsoldata( input_points, npt*3, sol_work ) < 0 ) goto bad; } /* Get planes defined by three points, 6 per card */ for ( i=0; i<npe; i += 6 ) { if ( get_line( scard, sizeof(scard), "arbn vertex point indices" ) == EOF ) { printf("too few cards for arbn %d\n", sol_work); return(-1); } for ( j=0; j<6; j++ ) { int q, r, s; point_t a, b, c; q = getint( scard, 10+j*10+0, 4 ); r = getint( scard, 10+j*10+4, 3 ); s = getint( scard, 10+j*10+7, 3 ); if ( q == 0 || r == 0 || s == 0 ) continue; if ( q < 0 ) { VMOVE( a, &input_points[((-q)-1)*3] ); a[Y] = -a[Y]; symm = 1; } else { VMOVE( a, &input_points[((q)-1)*3] ); } if ( r < 0 ) { VMOVE( b, &input_points[((-r)-1)*3] ); b[Y] = -b[Y]; symm = 1; } else { VMOVE( b, &input_points[((r)-1)*3] ); } if ( s < 0 ) { VMOVE( c, &input_points[((-s)-1)*3] ); c[Y] = -c[Y]; symm = 1; } else { VMOVE( c, &input_points[((s)-1)*3] ); } if ( bn_mk_plane_3pts( eqn[cur_eq], a, b, c, &tol ) < 0 ) { printf("arbn degenerate plane\n"); VPRINT("a", a); VPRINT("b", b); VPRINT("c", c); continue; } cur_eq++; } } /* Get planes defined by their equation */ for ( i=0; i < neq; i++ ) { register double scale; if ( get_line( scard, sizeof(scard), "arbn plane equation card" ) == EOF ) { printf("too few cards for arbn %d\n", sol_work); return(-1); } eqn[cur_eq][0] = getdouble( scard, 10+0*10, 10 ); eqn[cur_eq][1] = getdouble( scard, 10+1*10, 10 ); eqn[cur_eq][2] = getdouble( scard, 10+2*10, 10 ); eqn[cur_eq][3] = getdouble( scard, 10+3*10, 10 ); scale = MAGNITUDE(eqn[cur_eq]); if ( scale < SMALL ) { printf("arbn plane normal too small\n"); continue; } scale = 1/scale; VSCALE( eqn[cur_eq], eqn[cur_eq], scale ); eqn[cur_eq][3] *= scale; cur_eq++; } /* Get planes defined by azimuth, elevation, and pt, 2 per card */ for ( i=0; i < nae; i += 2 ) { if ( get_line( scard, sizeof(scard), "arbn az/el card" ) == EOF ) { printf("too few cards for arbn %d\n", sol_work); return(-1); } for ( j=0; j<2; j++ ) { double az, el; int vert_no; double cos_el; point_t pt; az = getdouble( scard, 10+j*30+0*10, 10 ) * bn_degtorad; el = getdouble( scard, 10+j*30+1*10, 10 ) * bn_degtorad; vert_no = getint( scard, 10+j*30+2*10, 10 ); if ( vert_no == 0 ) break; cos_el = cos(el); eqn[cur_eq][X] = cos(az)*cos_el; eqn[cur_eq][Y] = sin(az)*cos_el; eqn[cur_eq][Z] = sin(el); if ( vert_no < 0 ) { VMOVE( pt, &input_points[((-vert_no)-1)*3] ); pt[Y] = -pt[Y]; } else { VMOVE( pt, &input_points[((vert_no)-1)*3] ); } eqn[cur_eq][3] = VDOT(pt, eqn[cur_eq]); cur_eq++; } } if ( nface != cur_eq ) { printf("arbn expected %d faces, got %d\n", nface, cur_eq); return(-1); } /* Average all given points together to find centroid */ /* This is why there must be at least one (two?) point given */ VSETALL(cent, 0); for ( i=0; i<npt; i++ ) { VADD2( cent, cent, &input_points[i*3] ); } VSCALE( cent, cent, 1.0/npt ); if ( symm ) cent[Y] = 0; /* Point normals away from centroid */ for ( i=0; i<nface; i++ ) { double dist; dist = VDOT( eqn[i], cent ) - eqn[i][3]; /* If dist is negative, 'cent' is inside halfspace */ #define DIST_TOL (1.0e-8) #define DIST_TOL_SQ (1.0e-10) if ( dist < -DIST_TOL ) continue; if ( dist > DIST_TOL ) { /* Flip halfspace over */ VREVERSE( eqn[i], eqn[i] ); eqn[i][3] = -eqn[i][3]; } else { /* Centroid lies on this face */ printf("arbn centroid lies on face\n"); return(-1); } } /* Release storage for input points */ bu_free( (char *)input_points, "input_points" ); input_points = (double *)0; /* * ARBN must be convex. Test for concavity. * Byproduct is an enumeration of all the verticies. */ last_vertex = max_vertex = 0; /* Zero face use counts */ for ( i=0; i<nface; i++ ) { used[i] = 0; } for ( i=0; i<nface-2; i++ ) { for ( j=i+1; j<nface-1; j++ ) { double dot; int point_count; /* # points on this line */ /* If normals are parallel, no intersection */ dot = VDOT( eqn[i], eqn[j] ); if ( !NEAR_ZERO( dot, 0.999999 ) ) continue; point_count = 0; for ( k=j+1; k<nface; k++ ) { point_t pt; if ( bn_mkpoint_3planes( pt, eqn[i], eqn[j], eqn[k] ) < 0 ) continue; /* See if point is outside arb */ for ( m=0; m<nface; m++ ) { if ( i==m || j==m || k==m ) continue; if ( VDOT(pt, eqn[m])-eqn[m][3] > DIST_TOL ) goto next_k; } /* See if vertex already was found */ for ( m=0; m<last_vertex; m++ ) { vect_t dist; VSUB2( dist, pt, &vertex[m*3] ); if ( MAGSQ(dist) < DIST_TOL_SQ ) goto next_k; } /* * Add point to vertex array. * If more room needed, realloc. */ if ( last_vertex >= max_vertex ) { if ( max_vertex == 0 ) { max_vertex = 3; vertex = (double *)bu_malloc( max_vertex*3*sizeof(double), "vertex" ); } else { max_vertex *= 10; vertex = (double *)bu_realloc( (char *)vertex, max_vertex*3*sizeof(double), "vertex" ); } } VMOVE( &vertex[last_vertex*3], pt ); last_vertex++; point_count++; /* Increment "face used" counts */ used[i]++; used[j]++; used[k]++; next_k: ; } if ( point_count > 2 ) { printf("arbn: warning, point_count on line=%d\n", point_count); } } } /* If any planes were not used, then arbn is not convex */ for ( i=0; i<nface; i++ ) { if ( used[i] != 0 ) continue; /* face was used */ printf("arbn face %d unused, solid is not convex\n", i); return(-1); } /* Write out the solid ! */ i = mk_arbn( outfp, name, nface, eqn ); if ( input_points ) bu_free( (char *)input_points, "input_points" ); if ( vertex ) bu_free( (char *)vertex, "vertex" ); if ( eqn ) bu_free( (char *)eqn, "eqn" ); if ( used ) bu_free( (char *)used, "used" ); return(i); }
/* * This is called (from viewshade() in shade.c) once for each hit point * to be shaded. The purpose here is to fill in values in the shadework * structure. * * dp is a pointer to the shader-specific struct */ int bbd_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) { register struct bbd_specific *bbd_sp = (struct bbd_specific *)dp; union tree *tp; struct bbd_img *bi; struct imgdist id[MAX_IMAGES]; size_t i; /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); CK_bbd_SP(bbd_sp); if (rdebug&RDEBUG_SHADE) { bu_struct_print("bbd_render Parameters:", bbd_print_tab, (char *)bbd_sp); bu_log("pixel %d %d\n", ap->a_x, ap->a_y); bu_log("bbd region: %s\n", pp->pt_regionp->reg_name); } tp = pp->pt_regionp->reg_treetop; if (tp->tr_a.tu_op != OP_SOLID) { bu_log("%s:%d region %s rendering bbd should have found OP_SOLID, not %d\n", __FILE__, __LINE__, pp->pt_regionp->reg_name, tp->tr_a.tu_op); bu_bomb("\n"); } swp->sw_transmit = 1.0; VSETALL(swp->sw_color, 0.0); VSETALL(swp->sw_basecolor, 1.0); i = 0; for (BU_LIST_FOR(bi, bbd_img, &bbd_sp->imgs)) { /* find out if the ray hits the plane */ id[i].index = i; id[i].bi = bi; id[i].status = bn_isect_line3_plane(&id[i].dist, ap->a_ray.r_pt, ap->a_ray.r_dir, bi->img_plane, &ap->a_rt_i->rti_tol); i++; } bu_sort(id, bbd_sp->img_count, sizeof(id[0]), &imgdist_compare, NULL); for (i = 0; i < bbd_sp->img_count && swp->sw_transmit > 0.0; i++) { if (id[i].status > 0) do_ray_image(ap, pp, swp, bbd_sp, id[i].bi, id[i].dist); } if (rdebug&RDEBUG_SHADE) { bu_log("color %g %g %g\n", V3ARGS(swp->sw_color)); } /* shader must perform transmission/reflection calculations * * 0 < swp->sw_transmit <= 1 causes transmission computations * 0 < swp->sw_reflect <= 1 causes reflection computations */ if (swp->sw_reflect > 0 || swp->sw_transmit > 0) { int level = ap->a_level; ap->a_level = 0; /* Bogus hack to keep rr_render from giving up */ (void)rr_render(ap, pp, swp); ap->a_level = level; } if (rdebug&RDEBUG_SHADE) { bu_log("color %g %g %g\n", V3ARGS(swp->sw_color)); } return 1; }
/* compute the bounding sphere for a metaball cluster. center is * filled, and the radius is returned. */ fastf_t rt_metaball_get_bounding_sphere(point_t *center, fastf_t threshold, struct rt_metaball_internal *mb) { struct wdb_metaballpt *mbpt, *mbpt2; point_t min, max, d; fastf_t r = 0.0, dist, mag; int i; struct bu_list *points; points = &mb->metaball_ctrl_head; /* find a bounding box for the POINTS (NOT the surface) */ VSETALL(min, +INFINITY); VSETALL(max, -INFINITY); for (BU_LIST_FOR(mbpt, wdb_metaballpt, points)) for (i=0;i<3;i++) { if (mbpt->coord[i] < min[i]) min[i] = mbpt->coord[i]; if (mbpt->coord[i] > max[i]) max[i] = mbpt->coord[i]; } /* return -1 if no points are defined. */ if (ZERO(min[X] - INFINITY) || min[X] > INFINITY) return -1; /* compute the center of the generated box, call that the center */ VADD2(*center, min, max); VSCALE(*center, *center, 0.5); /* start looking for the radius... */ for (BU_LIST_FOR(mbpt, wdb_metaballpt, points)) { VSUB2(d, mbpt->coord, *center); /* since the surface is where threshold=fldstr/mag, mag=fldstr/threshold, so make that the initial value */ dist = MAGNITUDE(d) + mbpt->fldstr/threshold; /* and add all the contribution */ for (BU_LIST_FOR(mbpt2, wdb_metaballpt, points)) if (mbpt2 != mbpt) { fastf_t additive = 0.0; VSUB2(d, mbpt2->coord, mbpt->coord); mag = MAGNITUDE(d) + dist; switch(mb->method) { case METABALL_METABALL: break; case METABALL_ISOPOTENTIAL: additive = fabs(mbpt2->fldstr) * mbpt2->fldstr / mag; break; case METABALL_BLOB: additive = 1.0/exp((mbpt2->sweat / (mbpt2->fldstr * mbpt2->fldstr)) * mag * mag - mbpt2->sweat); break; } dist += additive; } /* then see if this is a 'defining' point */ if (dist > r) r = dist; } return r; }
/* * R R _ H I T * * This routine is called when an internal reflection ray hits something * (which is ordinarily the case). * * Generally, there will be one or two partitions on the hit list. * The values for pt_outhit for the second partition should not be used, * as a_onehit was set to 3, getting a maximum of 3 valid hit points. * * Explicit Returns - * 0 dreadful internal error * 1 treat as escaping ray & reshoot * 2 Proper exit point determined, with Implicit Returns: * a_uvec exit Point * a_vvec exit Normal (inward pointing) * a_refrac_index RI of *next* material */ HIDDEN int rr_hit(struct application *ap, struct partition *PartHeadp, struct seg *UNUSED(segp)) { register struct partition *pp; register struct hit *hitp; register struct soltab *stp; struct partition *psave = (struct partition *)NULL; struct shadework sw; struct application appl; int ret; RT_AP_CHECK(ap); RT_APPLICATION_INIT(&appl); for (pp=PartHeadp->pt_forw; pp != PartHeadp; pp = pp->pt_forw) if (pp->pt_outhit->hit_dist > 0.0) break; if (pp == PartHeadp) { if (R_DEBUG&(RDEBUG_SHOWERR|RDEBUG_REFRACT)) { bu_log("rr_hit: %d, %d no hit out front?\n", ap->a_x, ap->a_y); ret = 0; /* error */ goto out; } ret = 1; /* treat as escaping ray */ goto out; } /* * Ensure that the partition we are given is part of the same * region that we started in. When the internal reflection * is happening very near an edge or corner, this is not always * the case, and either (a) a small sliver of some other region * is found to be in the way, or (b) the ray completely misses the * region that it started in, although not by much. */ psave = pp; if (R_DEBUG&RDEBUG_REFRACT) bu_log("rr_hit(%s)\n", psave->pt_regionp->reg_name); for (; pp != PartHeadp; pp = pp->pt_forw) if (pp->pt_regionp == (struct region *)(ap->a_uptr)) break; if (pp == PartHeadp) { if (R_DEBUG&(RDEBUG_SHOWERR|RDEBUG_REFRACT)) { bu_log("rr_hit: %d, %d Ray internal to %s landed unexpectedly in %s\n", ap->a_x, ap->a_y, ((struct region *)(ap->a_uptr))->reg_name, psave->pt_regionp->reg_name); ret = 0; /* error */ goto out; } ret = 1; /* treat as escaping ray */ goto out; } /* * At one time, this was a check for pp->pt_inhit->hit_dist * being NEAR zero. That was a mistake, because we may have * been at the edge of a subtracted out center piece when * internal reflection happened, except that floating point * error (being right on the surface of the interior solid) * prevented us from "seeing" that solid on the next ray, * causing our ray endpoints to be quite far from the starting * point, yet with the ray still validly inside the glass region. * * There is a major problem if the entry point * is further ahead than the firing point, i.e., >0. * * Because this error has not yet been encountered, it is * considered dreadful. Some recovery may be possible. * * For now, this seems to happen when a reflected ray starts outside * the glass and doesn't even intersect the glass, so treat it as * an escaping ray. */ if (pp->pt_inhit->hit_dist > 10) { stp = pp->pt_inseg->seg_stp; if (R_DEBUG&RDEBUG_REFRACT) bu_log("rr_hit: %d, %d %s inhit %g > 10.0! (treating as escaping ray)\n", ap->a_x, ap->a_y, pp->pt_regionp->reg_name, pp->pt_inhit->hit_dist); ret = 1; /* treat as escaping ray */ goto out; } /* * If there is a very small crack in the glass, perhaps formed * by a small error when taking the Union of two solids, * attempt to find the real exit point. * NOTE that this is usually taken care of inside librt * in the bool_weave code, but it is inexpensive to check for it * here. If this case is detected, push on, and log it. * This code is not expected to be needed. */ while (pp->pt_forw != PartHeadp) { register fastf_t d; d = pp->pt_forw->pt_inhit->hit_dist - pp->pt_outhit->hit_dist; if (!NEAR_ZERO(d, AIR_GAP_TOL)) break; if (pp->pt_forw->pt_regionp != pp->pt_regionp) break; if (R_DEBUG&(RDEBUG_SHOWERR|RDEBUG_REFRACT)) bu_log( "rr_hit: %d, %d fusing small crack in glass %s\n", ap->a_x, ap->a_y, pp->pt_regionp->reg_name); pp = pp->pt_forw; } hitp = pp->pt_outhit; stp = pp->pt_outseg->seg_stp; if (hitp->hit_dist >= INFINITY) { bu_log("rr_hit: %d, %d infinite glass (%g, %g) %s\n", ap->a_x, ap->a_y, pp->pt_inhit->hit_dist, hitp->hit_dist, pp->pt_regionp->reg_name); ret = 0; /* dreadful error */ goto out; } VJOIN1(hitp->hit_point, ap->a_ray.r_pt, hitp->hit_dist, ap->a_ray.r_dir); RT_HIT_NORMAL(ap->a_vvec, hitp, stp, &(ap->a_ray), pp->pt_outflip); /* For refraction, want exit normal to point inward. */ VREVERSE(ap->a_vvec, ap->a_vvec); VMOVE(ap->a_uvec, hitp->hit_point); ap->a_cumlen += (hitp->hit_dist - pp->pt_inhit->hit_dist); ap->a_refrac_index = RI_AIR; /* Default medium: air */ /* * Look ahead, and see if there is more glass to come. * If so, obtain its refractive index, to enable correct * calculation of the departing refraction angle. */ if (pp->pt_forw != PartHeadp) { register fastf_t d; d = pp->pt_forw->pt_inhit->hit_dist - hitp->hit_dist; if (NEAR_ZERO(d, AIR_GAP_TOL)) { /* * Make a private copy of the application struct, * because viewshade() may change various fields. */ appl = *ap; /* struct copy */ memset((char *)&sw, 0, sizeof(sw)); sw.sw_transmit = sw.sw_reflect = 0.0; /* Set default in case shader doesn't fill this in. */ sw.sw_refrac_index = RI_AIR; /* Set special flag so that we get only shader * parameters (refractive index, in this case). * We don't even care about transmitted energy. */ sw.sw_xmitonly = 2; sw.sw_inputs = 0; /* no fields filled yet */ #ifdef RT_MULTISPECTRAL sw.msw_color = bn_tabdata_get_constval(1.0, spectrum); sw.msw_basecolor = bn_tabdata_get_constval(1.0, spectrum); #else VSETALL(sw.sw_color, 1); VSETALL(sw.sw_basecolor, 1); #endif if (R_DEBUG&(RDEBUG_SHADE|RDEBUG_REFRACT)) bu_log("rr_hit calling viewshade to discover refractive index\n"); (void)viewshade(&appl, pp->pt_forw, &sw); #ifdef RT_MULTISPECTRAL bu_free(sw.msw_color, "sw.msw_color"); bu_free(sw.msw_basecolor, "sw.msw_basecolor"); #endif if (R_DEBUG&(RDEBUG_SHADE|RDEBUG_REFRACT)) bu_log("rr_hit refractive index = %g\n", sw.sw_refrac_index); if (sw.sw_transmit > 0) { ap->a_refrac_index = sw.sw_refrac_index; if (R_DEBUG&RDEBUG_SHADE) { bu_log("rr_hit a_refrac_index=%g (trans=%g)\n", ap->a_refrac_index, sw.sw_transmit); } ret= 3; /* OK -- more glass follows */ goto out; } } } ret = 2; /* OK -- no more glass */ out: if (R_DEBUG&RDEBUG_REFRACT) bu_log("rr_hit(%s) return=%d\n", psave ? psave->pt_regionp->reg_name : "", ret); return ret; }
/* * Handle ray interaction with 1 image */ static void do_ray_image(struct application *ap, const struct partition *pp, struct shadework *swp, /* defined in ../h/shadework.h */ struct bbd_specific *bbd_sp, struct bbd_img *bi, double dist) { double x, y, dx, dy, xlo, xhi, ylo, yhi; int u, v, ulo, uhi, vlo, vhi; unsigned char *pixels, *color; int val; static const double rgb255 = 1.0 / 256.0; vect_t cum_color; point_t pt; vect_t vpt; double radius; int tot; int color_count; double t, opacity; if (rdebug&RDEBUG_SHADE) { bu_log("do_ray_image\n"); plot_ray_img(ap, pp, dist, bi); } VJOIN1(pt, ap->a_ray.r_pt, dist, ap->a_ray.r_dir); /* point on plane */ VSUB2(vpt, pt, bi->img_origin); /* vect: origin to pt */ x = VDOT(vpt, bi->img_x); y = VDOT(vpt, bi->img_y); if (x < 0.0 || x > bi->img_xlen || y < 0.0 || y > bi->img_ylen) { if (rdebug&RDEBUG_SHADE) { bu_log("hit outside bounds, leaving color %g %g %g\n", V3ARGS(swp->sw_color)); } return; } /* get the radius of the beam at the image plane */ radius = ap->a_rbeam + dist * ap->a_diverge; /* dx = radius * VDOT(ap->a_ray.r_dir, bi->img_x); dy = radius * VDOT(ap->a_ray.r_dir, bi->img_y); */ dx = radius; dy = radius; xlo = x - dx; xhi = x + dx; ylo = y - dy; yhi = y + dy; CLAMP(xlo, 0.0, bi->img_xlen); CLAMP(xhi, 0.0, bi->img_xlen); CLAMP(ylo, 0.0, bi->img_ylen); CLAMP(yhi, 0.0, bi->img_ylen); vlo = (ylo / bi->img_ylen) * (bi->img_height -1); vhi = (yhi / bi->img_ylen) * (bi->img_height -1); ulo = (xlo / bi->img_xlen) * (bi->img_width -1); uhi = (xhi / bi->img_xlen) * (bi->img_width -1); if (ulo > uhi) { int i = ulo; ulo = uhi; uhi = i; } if (vlo > vhi) { int i = vlo; vlo = vhi; vhi = i; } pixels = (unsigned char*)bi->img_mf->buf; if (rdebug&RDEBUG_SHADE) { bu_log("u:%d..%d v:%d..%d\n", ulo, uhi, vlo, vhi); } tot = (uhi - ulo + 1) * (vhi - vlo + 1); /* total # of pixels */ color_count = 0; /* */ VSETALL(cum_color, 0.0); for (v = vlo; v <= vhi; v++) { for (u = ulo; u <= uhi; u++) { color = &pixels[v*bi->img_width*3 + u*3]; val = color[0]+color[1]+color[2]; if (val > bbd_sp->img_threshold) { color_count++; VJOIN1(cum_color, cum_color, rgb255, color); if (rdebug&RDEBUG_SHADE) { bu_log("%d %d %d\n", V3ARGS(color)); VPRINT("cum_color", cum_color); } } } } if (rdebug&RDEBUG_SHADE) bu_log("tot:%d color_count: %d\n", tot, color_count); if (color_count == 0) { if (rdebug&RDEBUG_SHADE) bu_log("no color contribution, leaving color as %g %g %g\n", V3ARGS(swp->sw_color)); return; } /* get average color: scale color by the # of contributions */ t = 1.0 / (double)color_count; VSCALE(cum_color, cum_color, t); if (rdebug&RDEBUG_SHADE) { int c[3]; VSCALE(c, cum_color, 256); bu_log("average color: %d %d %d\n", V3ARGS(c)); } /* compute residual transmission */ opacity = ((double)color_count / tot); /* opacity */ t = swp->sw_transmit*opacity; VJOIN1(swp->sw_color, swp->sw_color, t, cum_color); swp->sw_transmit -= opacity; if (swp->sw_transmit < 0.0) swp->sw_transmit = 0.0; }
void nmg_2_vrml(FILE *fp, const struct db_full_path *pathp, struct model *m, struct mater_info *mater) { struct nmgregion *reg; struct bu_ptbl verts; struct vrml_mat mat; struct bu_vls vls = BU_VLS_INIT_ZERO; char *tok; int i; int first=1; int is_light=0; float r, g, b; point_t ave_pt; char *full_path; /*There may be a better way to capture the region_id, than getting the rt_comb_internal structure, * (and may be a better way to capture the rt_comb_internal struct), but for now I just copied the * method used in select_lights/select_non_lights above, could have used a global variable but I noticed * none other were used, so I didn't want to be the first */ struct directory *dp; struct rt_db_internal intern; struct rt_comb_internal *comb; int id; NMG_CK_MODEL( m ); BARRIER_CHECK; full_path = db_path_to_string( pathp ); /* replace all occurrences of '.' with '_' */ char_replace(full_path, '.', '_'); RT_CK_FULL_PATH( pathp ); dp = DB_FULL_PATH_CUR_DIR( pathp ); if ( !(dp->d_flags & RT_DIR_COMB) ) return; id = rt_db_get_internal( &intern, dp, dbip, (matp_t)NULL, &rt_uniresource ); if ( id < 0 ) { bu_log( "Cannot internal form of %s\n", dp->d_namep ); return; } if ( id != ID_COMBINATION ) { bu_log( "Directory/database mismatch!\n\t is '%s' a combination or not?\n", dp->d_namep ); return; } comb = (struct rt_comb_internal *)intern.idb_ptr; RT_CK_COMB( comb ); if ( mater->ma_color_valid ) { r = mater->ma_color[0]; g = mater->ma_color[1]; b = mater->ma_color[2]; } else { r = g = b = 0.5; } if ( mater->ma_shader ) { tok = strtok( mater->ma_shader, tok_sep ); bu_strlcpy( mat.shader, tok, TXT_NAME_SIZE ); } else mat.shader[0] = '\0'; mat.shininess = -1; mat.transparency = -1.0; mat.lt_fraction = -1.0; VSETALL( mat.lt_dir, 0.0 ); mat.lt_angle = -1.0; mat.tx_file[0] = '\0'; mat.tx_w = -1; mat.tx_n = -1; bu_vls_strcpy( &vls, &mater->ma_shader[strlen(mat.shader)] ); (void)bu_struct_parse( &vls, vrml_mat_parse, (char *)&mat ); if ( bu_strncmp( "light", mat.shader, 5 ) == 0 ) { /* this is a light source */ is_light = 1; } else { fprintf( fp, "\t<Shape DEF=\"%s\">\n", full_path); fprintf( fp, "\t\t<Appearance>\n"); if ( bu_strncmp( "plastic", mat.shader, 7 ) == 0 ) { if ( mat.shininess < 0 ) mat.shininess = 10; if ( mat.transparency < 0.0 ) mat.transparency = 0.0; fprintf( fp, "\t\t\t<Material diffuseColor=\"%g %g %g\" shininess=\"%g\" transparency=\"%g\" specularColor=\"%g %g %g\"/>\n", r, g, b, 1.0-exp(-(double)mat.shininess/20.0), mat.transparency, 1.0, 1.0, 1.0); } else if ( bu_strncmp( "glass", mat.shader, 5 ) == 0 ) { if ( mat.shininess < 0 ) mat.shininess = 4; if ( mat.transparency < 0.0 ) mat.transparency = 0.8; fprintf( fp, "\t\t\t<Material diffuseColor=\"%g %g %g\" shininess=\"%g\" transparency=\"%g\" specularColor=\"%g %g %g\"/>\n", r, g, b, 1.0-exp(-(double)mat.shininess/20.0), mat.transparency, 1.0, 1.0, 1.0); } else if ( mater->ma_color_valid ) { fprintf( fp, "\t\t\t<Material diffuseColor=\"%g %g %g\"/>\n", r, g, b); } else { /* If no color was defined set the colors according to the thousands groups */ int thou = comb->region_id/1000; thou == 0 ? fprintf( fp, "\t\t\t<Material USE=\"Material_999\"/>\n") : thou == 1 ? fprintf( fp, "\t\t\t<Material USE=\"Material_1999\"/>\n") : thou == 2 ? fprintf( fp, "\t\t\t<Material USE=\"Material_2999\"/>\n") : thou == 3 ? fprintf( fp, "\t\t\t<Material USE=\"Material_3999\"/>\n") : thou == 4 ? fprintf( fp, "\t\t\t<Material USE=\"Material_4999\"/>\n") : thou == 5 ? fprintf( fp, "\t\t\t<Material USE=\"Material_5999\"/>\n") : thou == 6 ? fprintf( fp, "\t\t\t<Material USE=\"Material_6999\"/>\n") : thou == 7 ? fprintf( fp, "\t\t\t<Material USE=\"Material_7999\"/>\n") : thou == 8 ? fprintf( fp, "\t\t\t<Material USE=\"Material_8999\"/>\n") : fprintf( fp, "\t\t\t<Material USE=\"Material_9999\"/>\n"); } } if ( !is_light ) { process_non_light(m); fprintf( fp, "\t\t</Appearance>\n"); } /* FIXME: need code to handle light */ /* get list of vertices */ nmg_vertex_tabulate( &verts, &m->magic ); fprintf( fp, "\t\t<IndexedFaceSet coordIndex=\"\n"); first = 1; if ( !is_light ) { for ( BU_LIST_FOR( reg, nmgregion, &m->r_hd ) ) { struct shell *s; NMG_CK_REGION( reg ); for ( BU_LIST_FOR( s, shell, ®->s_hd ) ) { struct faceuse *fu; NMG_CK_SHELL( s ); for ( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) ) { struct loopuse *lu; NMG_CK_FACEUSE( fu ); if ( fu->orientation != OT_SAME ) continue; for ( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) ) { struct edgeuse *eu; NMG_CK_LOOPUSE( lu ); if ( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC ) continue; if ( !first ) fprintf( fp, ",\n" ); else first = 0; fprintf( fp, "\t\t\t\t" ); for ( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) ) { struct vertex *v; NMG_CK_EDGEUSE( eu ); v = eu->vu_p->v_p; NMG_CK_VERTEX( v ); fprintf( fp, " %d,", bu_ptbl_locate( &verts, (long *)v ) ); } fprintf( fp, "-1" ); } } } } /* close coordIndex */ fprintf( fp, "\" "); fprintf( fp, "normalPerVertex=\"false\" "); fprintf( fp, "convex=\"false\" "); fprintf( fp, "creaseAngle=\"0.5\" "); /* close IndexedFaceSet open tag */ fprintf( fp, ">\n"); } fprintf( fp, "\t\t\t<Coordinate point=\""); for ( i=0; i<BU_PTBL_END( &verts ); i++ ) { struct vertex *v; struct vertex_g *vg; point_t pt_meters; v = (struct vertex *)BU_PTBL_GET( &verts, i ); NMG_CK_VERTEX( v ); vg = v->vg_p; NMG_CK_VERTEX_G( vg ); /* convert to desired units */ VSCALE( pt_meters, vg->coord, scale_factor ); if ( is_light ) VADD2( ave_pt, ave_pt, pt_meters ); if ( first ) { if ( !is_light ) fprintf( fp, " %10.10e %10.10e %10.10e, ", V3ARGS(pt_meters)); first = 0; } else if ( !is_light ) fprintf( fp, "%10.10e %10.10e %10.10e, ", V3ARGS( pt_meters )); } /* close point */ fprintf(fp, "\""); /* close Coordinate */ fprintf(fp, "/>\n"); /* IndexedFaceSet end tag */ fprintf( fp, "\t\t</IndexedFaceSet>\n"); /* Shape end tag */ fprintf( fp, "\t</Shape>\n"); BARRIER_CHECK; }
/* * R R _ R E N D E R */ int rr_render(register struct application *ap, const struct partition *pp, struct shadework *swp) { struct application sub_ap; vect_t work; vect_t incident_dir; fastf_t shader_fract; fastf_t reflect; fastf_t transmit; #ifdef RT_MULTISPECTRAL struct bn_tabdata *ms_filter_color = BN_TABDATA_NULL; struct bn_tabdata *ms_shader_color = BN_TABDATA_NULL; struct bn_tabdata *ms_reflect_color = BN_TABDATA_NULL; struct bn_tabdata *ms_transmit_color = BN_TABDATA_NULL; #else vect_t filter_color; vect_t shader_color; vect_t reflect_color; vect_t transmit_color; #endif fastf_t attenuation; vect_t to_eye; int code; RT_AP_CHECK(ap); RT_APPLICATION_INIT(&sub_ap); #ifdef RT_MULTISPECTRAL sub_ap.a_spectrum = BN_TABDATA_NULL; ms_reflect_color = bn_tabdata_get_constval(0.0, spectrum); #endif /* * sw_xmitonly is set primarily for light visibility rays. * Need to compute (partial) transmission through to the light, * or procedural shaders won't be able to cast shadows * and light won't be able to get through glass * (including "stained glass" and "filter glass"). * * On the other hand, light visibility rays shouldn't be refracted, * it is pointless to shoot at where the light isn't. */ if (swp->sw_xmitonly) { /* Caller wants transmission term only, don't fire reflected rays */ transmit = swp->sw_transmit + swp->sw_reflect; /* Don't loose energy */ reflect = 0; } else { reflect = swp->sw_reflect; transmit = swp->sw_transmit; } if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render(%s) START: lvl=%d reflect=%g, transmit=%g, xmitonly=%d\n", pp->pt_regionp->reg_name, ap->a_level, reflect, transmit, swp->sw_xmitonly); } if (reflect <= 0 && transmit <= 0) goto out; if (ap->a_level > max_bounces) { /* Nothing more to do for this ray */ static long count = 0; /* Not PARALLEL, should be OK */ if ((R_DEBUG&(RDEBUG_SHOWERR|RDEBUG_REFRACT)) && ( count++ < MSG_PROLOGUE || (count%MSG_INTERVAL) == 3 )) { bu_log("rr_render: %d, %d MAX BOUNCES=%d: %s\n", ap->a_x, ap->a_y, ap->a_level, pp->pt_regionp->reg_name); } /* * Return the basic color of the object, ignoring the * the fact that it is supposed to be * filtering or reflecting light here. * This is much better than returning just black, * but something better might be done. */ #ifdef RT_MULTISPECTRAL BN_CK_TABDATA(swp->msw_color); BN_CK_TABDATA(swp->msw_basecolor); bn_tabdata_copy(swp->msw_color, swp->msw_basecolor); #else VMOVE(swp->sw_color, swp->sw_basecolor); #endif ap->a_cumlen += pp->pt_inhit->hit_dist; goto out; } #ifdef RT_MULTISPECTRAL BN_CK_TABDATA(swp->msw_basecolor); ms_filter_color = bn_tabdata_dup(swp->msw_basecolor); #else VMOVE(filter_color, swp->sw_basecolor); #endif if ((swp->sw_inputs & (MFI_HIT|MFI_NORMAL)) != (MFI_HIT|MFI_NORMAL)) shade_inputs(ap, pp, swp, MFI_HIT|MFI_NORMAL); /* * If this ray is being fired from the exit point of * an object, and is directly entering another object, * (i.e., there is no intervening air-gap), and * the two refractive indices match, then do not fire a * reflected ray -- just take the transmission contribution. * This is important, e.g., for glass gun tubes projecting * through a glass armor plate. :-) */ if (NEAR_ZERO(pp->pt_inhit->hit_dist, AIR_GAP_TOL) && ZERO(ap->a_refrac_index - swp->sw_refrac_index)) { transmit += reflect; reflect = 0; } /* * Diminish base color appropriately, and add in * contributions from mirror reflection & transparency */ shader_fract = 1 - (reflect + transmit); if (shader_fract < 0) { shader_fract = 0; } else if (shader_fract >= 1) { goto out; } if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render: lvl=%d start shader=%g, reflect=%g, transmit=%g %s\n", ap->a_level, shader_fract, reflect, transmit, pp->pt_regionp->reg_name); } #ifdef RT_MULTISPECTRAL BN_GET_TABDATA(ms_shader_color, swp->msw_color->table); bn_tabdata_scale(ms_shader_color, swp->msw_color, shader_fract); #else VSCALE(shader_color, swp->sw_color, shader_fract); #endif /* * Compute transmission through an object. * There may be a mirror reflection, which will be handled * by the reflection code later */ if (transmit > 0) { if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render: lvl=%d transmit=%g. Calculate refraction at entrance to %s.\n", ap->a_level, transmit, pp->pt_regionp->reg_name); } /* * Calculate refraction at entrance. */ sub_ap = *ap; /* struct copy */ #ifdef RT_MULTISPECTRAL sub_ap.a_spectrum = bn_tabdata_dup((struct bn_tabdata *)ap->a_spectrum); #endif sub_ap.a_level = 0; /* # of internal reflections */ sub_ap.a_cumlen = 0; /* distance through the glass */ sub_ap.a_user = -1; /* sanity */ sub_ap.a_rbeam = ap->a_rbeam + swp->sw_hit.hit_dist * ap->a_diverge; sub_ap.a_diverge = 0.0; sub_ap.a_uptr = (genptr_t)(pp->pt_regionp); VMOVE(sub_ap.a_ray.r_pt, swp->sw_hit.hit_point); VMOVE(incident_dir, ap->a_ray.r_dir); /* If there is an air gap, reset ray's RI to air */ if (pp->pt_inhit->hit_dist > AIR_GAP_TOL) sub_ap.a_refrac_index = RI_AIR; if (!ZERO(sub_ap.a_refrac_index - swp->sw_refrac_index) && !rr_refract(incident_dir, /* input direction */ swp->sw_hit.hit_normal, /* exit normal */ sub_ap.a_refrac_index, /* current RI */ swp->sw_refrac_index, /* next RI */ sub_ap.a_ray.r_dir /* output direction */ )) { /* * Ray was mirror reflected back outside solid. * Just add contribution to reflection, * and quit. */ reflect += transmit; transmit = 0; #ifdef RT_MULTISPECTRAL ms_transmit_color = bn_tabdata_get_constval(0.0, spectrum); #else VSETALL(transmit_color, 0); #endif if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render: lvl=%d change xmit into reflection %s\n", ap->a_level, pp->pt_regionp->reg_name); } goto do_reflection; } if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render: lvl=%d begin transmission through %s.\n", ap->a_level, pp->pt_regionp->reg_name); } /* * Find new exit point from the inside. * We will iterate, but not recurse, due to the special * (non-recursing) hit and miss routines used here for * internal reflection. * * a_onehit is set to 3, so that where possible, * rr_hit() will be given three accurate hit points: * the entry and exit points of this glass region, * and the entry point into the next region. * This permits calculation of the departing * refraction angle based on the RI of the current and * *next* regions along the ray. */ sub_ap.a_purpose = "rr first glass transmission ray"; sub_ap.a_flag = 0; do_inside: sub_ap.a_hit = rr_hit; sub_ap.a_miss = rr_miss; sub_ap.a_logoverlap = ap->a_logoverlap; sub_ap.a_onehit = 3; sub_ap.a_rbeam = ap->a_rbeam + swp->sw_hit.hit_dist * ap->a_diverge; sub_ap.a_diverge = 0.0; switch (code = rt_shootray(&sub_ap)) { case 3: /* More glass to come. * uvec=exit_pt, vvec=N, a_refrac_index = next RI. */ break; case 2: /* No more glass to come. * uvec=exit_pt, vvec=N, a_refrac_index = next RI. */ break; case 1: /* Treat as escaping ray */ if (R_DEBUG&RDEBUG_REFRACT) bu_log("rr_refract: Treating as escaping ray\n"); goto do_exit; case 0: default: /* Dreadful error */ #ifdef RT_MULTISPECTRAL bu_bomb("rr_refract: Stuck in glass. Very green pixel, unsupported in multi-spectral mode\n"); #else VSET(swp->sw_color, 0, 99, 0); /* very green */ #endif goto out; /* abandon hope */ } if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render: calculating refraction @ exit from %s (green)\n", pp->pt_regionp->reg_name); bu_log("Start point to exit point:\n\ vdraw open rr;vdraw params c 00ff00; vdraw write n 0 %g %g %g; vdraw wwrite n 1 %g %g %g; vdraw send\n", V3ARGS(sub_ap.a_ray.r_pt), V3ARGS(sub_ap.a_uvec)); } /* NOTE: rr_hit returns EXIT Point in sub_ap.a_uvec, * and returns EXIT Normal in sub_ap.a_vvec, * and returns next RI in sub_ap.a_refrac_index */ if (R_DEBUG&RDEBUG_RAYWRITE) { wraypts(sub_ap.a_ray.r_pt, sub_ap.a_ray.r_dir, sub_ap.a_uvec, 2, ap, stdout); /* 2 = ?? */ } if (R_DEBUG&RDEBUG_RAYPLOT) { /* plotfp */ bu_semaphore_acquire(BU_SEM_SYSCALL); pl_color(stdout, 0, 255, 0); pdv_3line(stdout, sub_ap.a_ray.r_pt, sub_ap.a_uvec); bu_semaphore_release(BU_SEM_SYSCALL); } /* Advance. Exit point becomes new start point */ VMOVE(sub_ap.a_ray.r_pt, sub_ap.a_uvec); VMOVE(incident_dir, sub_ap.a_ray.r_dir); /* * Calculate refraction at exit point. * Use "look ahead" RI value from rr_hit. */ if (!ZERO(sub_ap.a_refrac_index - swp->sw_refrac_index) && !rr_refract(incident_dir, /* input direction */ sub_ap.a_vvec, /* exit normal */ swp->sw_refrac_index, /* current RI */ sub_ap.a_refrac_index, /* next RI */ sub_ap.a_ray.r_dir /* output direction */ )) { static long count = 0; /* not PARALLEL, should be OK */ /* Reflected internally -- keep going */ if ((++sub_ap.a_level) <= max_ireflect) { sub_ap.a_purpose = "rr reflected internal ray, probing for glass exit point"; sub_ap.a_flag = 0; goto do_inside; } /* * Internal Reflection limit exceeded -- just let * the ray escape, continuing on current course. * This will cause some energy from somewhere in the * scene to be received through this glass, * which is much better than just returning * grey or black, as before. */ if ((R_DEBUG&(RDEBUG_SHOWERR|RDEBUG_REFRACT)) && ( count++ < MSG_PROLOGUE || (count%MSG_INTERVAL) == 3 )) { bu_log("rr_render: %d, %d Int.reflect=%d: %s lvl=%d\n", sub_ap.a_x, sub_ap.a_y, sub_ap.a_level, pp->pt_regionp->reg_name, ap->a_level); } VMOVE(sub_ap.a_ray.r_dir, incident_dir); goto do_exit; } do_exit: /* * Compute internal spectral transmittance. * Bouger's law. pg 30 of "color science" * * Apply attenuation factor due to thickness of the glass. * sw_extinction is in terms of fraction of light absorbed * per linear meter of glass. a_cumlen is in mm. */ /* XXX extinction should be a spectral curve, not scalor */ if (swp->sw_extinction > 0 && sub_ap.a_cumlen > 0) { attenuation = pow(10.0, -1.0e-3 * sub_ap.a_cumlen * swp->sw_extinction); } else { attenuation = 1; } /* * Process the escaping refracted ray. * This is the only place we might recurse dangerously, * so we are careful to use our caller's recursion level+1. * NOTE: point & direction already filled in */ sub_ap.a_hit = ap->a_hit; sub_ap.a_miss = ap->a_miss; sub_ap.a_logoverlap = ap->a_logoverlap; sub_ap.a_onehit = ap->a_onehit; sub_ap.a_level = ap->a_level+1; sub_ap.a_uptr = ap->a_uptr; sub_ap.a_rbeam = ap->a_rbeam + swp->sw_hit.hit_dist * ap->a_diverge; sub_ap.a_diverge = 0.0; if (code == 3) { sub_ap.a_purpose = "rr recurse on next glass"; sub_ap.a_flag = 0; } else { sub_ap.a_purpose = "rr recurse on escaping internal ray"; sub_ap.a_flag = 1; sub_ap.a_onehit = sub_ap.a_onehit > -3 ? -3 : sub_ap.a_onehit; } /* sub_ap.a_refrac_index was set to RI of next material by rr_hit(). */ sub_ap.a_cumlen = 0; (void) rt_shootray(&sub_ap); /* a_user has hit/miss flag! */ if (sub_ap.a_user == 0) { #ifdef RT_MULTISPECTRAL ms_transmit_color = bn_tabdata_dup(background); #else VMOVE(transmit_color, background); #endif sub_ap.a_cumlen = 0; } else { #ifdef RT_MULTISPECTRAL ms_transmit_color = bn_tabdata_dup(sub_ap.a_spectrum); #else VMOVE(transmit_color, sub_ap.a_color); #endif } transmit *= attenuation; #ifdef RT_MULTISPECTRAL bn_tabdata_mul(ms_transmit_color, ms_filter_color, ms_transmit_color); #else VELMUL(transmit_color, filter_color, transmit_color); #endif if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render: lvl=%d end of xmit through %s\n", ap->a_level, pp->pt_regionp->reg_name); } } else {
/* * This routine is called (at prep time) * once for each region which uses this shader. * Any shader-specific initialization should be done here. */ HIDDEN int fire_setup(register struct region *rp, struct bu_vls *matparm, void **dpp, const struct mfuncs *UNUSED(mfp), struct rt_i *rtip) /* pointer to reg_udata in *rp */ /* New since 4.4 release */ { register struct fire_specific *fire_sp; /* check the arguments */ RT_CHECK_RTI(rtip); BU_CK_VLS(matparm); RT_CK_REGION(rp); if (rdebug&RDEBUG_SHADE) bu_log("fire_setup(%s)\n", rp->reg_name); /* Get memory for the shader parameters and shader-specific data */ BU_GET(fire_sp, struct fire_specific); *dpp = fire_sp; /* initialize the default values for the shader */ memcpy(fire_sp, &fire_defaults, sizeof(struct fire_specific)); /* parse the user's arguments for this use of the shader. */ if (bu_struct_parse(matparm, fire_parse_tab, (char *)fire_sp) < 0) return -1; if (!EQUAL(fire_sp->noise_size, -1.0)) { VSETALL(fire_sp->noise_vscale, fire_sp->noise_size); } /* * The shader needs to operate in a coordinate system which stays * fixed on the region when the region is moved (as in animation). * We need to get a matrix to perform the appropriate transform(s). */ rt_shader_mat(fire_sp->fire_m_to_sh, rtip, rp, fire_sp->fire_min, fire_sp->fire_max, &rt_uniresource); /* Build matrix to map shader space to noise space. * XXX If only we could get the frametime at this point * we could factor the flicker of flames into this matrix * rather than having to recompute it on a pixel-by-pixel basis. */ MAT_IDN(fire_sp->fire_sh_to_noise); MAT_DELTAS_VEC(fire_sp->fire_sh_to_noise, fire_sp->noise_delta); MAT_SCALE_VEC(fire_sp->fire_sh_to_noise, fire_sp->noise_vscale); /* get matrix for performing spline of fire colors */ rt_dspline_matrix(fire_sp->fire_colorspline_mat, "Catmull", 0.5, 0.0); if (rdebug&RDEBUG_SHADE || fire_sp->fire_debug) { bu_struct_print(" FIRE Parameters:", fire_print_tab, (char *)fire_sp); bn_mat_print("m_to_sh", fire_sp->fire_m_to_sh); bn_mat_print("sh_to_noise", fire_sp->fire_sh_to_noise); bn_mat_print("colorspline", fire_sp->fire_colorspline_mat); } return 1; }
/** * Brute force through all possible plane intersections. * Generate all edge lines, then intersect the line with all * the other faces to find the vertices on that line. * If the geometry is correct, there will be no more than two. * While not the fastest strategy, this will produce an accurate * plot without requiring extra bookkeeping. * Note that the vectors will be drawn in no special order. */ int rt_arbn_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct rt_tess_tol *UNUSED(ttol), const struct bn_tol *tol, const struct rt_view_info *UNUSED(info)) { struct rt_arbn_internal *aip; size_t i; size_t j; size_t k; BU_CK_LIST_HEAD(vhead); RT_CK_DB_INTERNAL(ip); aip = (struct rt_arbn_internal *)ip->idb_ptr; RT_ARBN_CK_MAGIC(aip); for (i = 0; i < aip->neqn - 1; i++) { for (j = i + 1; j < aip->neqn; j++) { double dot; int point_count; /* # points on this line */ point_t a, b; /* start and end points */ vect_t dist; VSETALL(a, 0); VSETALL(b, 0); /* If normals are parallel, no intersection */ dot = VDOT(aip->eqn[i], aip->eqn[j]); if (BN_VECT_ARE_PARALLEL(dot, tol)) continue; /* Have an edge line, isect with all other planes */ point_count = 0; for (k = 0; k < aip->neqn; k++) { size_t m; point_t pt; size_t next_k; next_k = 0; if (k == i || k == j) continue; if (bn_mkpoint_3planes(pt, aip->eqn[i], aip->eqn[j], aip->eqn[k]) < 0) continue; /* See if point is outside arb */ for (m = 0; m < aip->neqn; m++) { if (i == m || j == m || k == m) continue; if (VDOT(pt, aip->eqn[m])-aip->eqn[m][3] > tol->dist) { next_k = 1; break; } } if (next_k != 0) continue; if (point_count <= 0) { RT_ADD_VLIST(vhead, pt, BN_VLIST_LINE_MOVE); VMOVE(a, pt); } else if (point_count == 1) { VSUB2(dist, pt, a); if (MAGSQ(dist) < tol->dist_sq) continue; RT_ADD_VLIST(vhead, pt, BN_VLIST_LINE_DRAW); VMOVE(b, pt); } else { VSUB2(dist, pt, a); if (MAGSQ(dist) < tol->dist_sq) continue; VSUB2(dist, pt, b); if (MAGSQ(dist) < tol->dist_sq) continue; bu_log("rt_arbn_plot() error, point_count=%d (>2) on edge %zu/%zu, non-convex\n", point_count+1, i, j); VPRINT(" a", a); VPRINT(" b", b); VPRINT("pt", pt); RT_ADD_VLIST(vhead, pt, BN_VLIST_LINE_DRAW); /* draw it */ } point_count++; } /* Point counts of 1 are (generally) not harmful, * occurring on pyramid peaks and the like. */ } } return 0; }
/* T R E E T H E R M _ S E T U P * * This routine is called (at prep time) * once for each region which uses this shader. * Any shader-specific initialization should be done here. */ HIDDEN int tthrm_setup(register struct region *rp, struct bu_vls *matparm, genptr_t *dpp, const struct mfuncs *UNUSED(mfp), struct rt_i *rtip) /* pointer to reg_udata in *rp */ /* New since 4.4 release */ { register struct tthrm_specific *tthrm_sp; struct bu_mapped_file *tt_file; char *tt_data; long cyl_tot = 0; long tseg; float *fp; float fv[4]; double min_temp; double max_temp; point_t center; point_t pt; vect_t dir; static const double inv_nodes = 1.0/8.0; int node; int i; int long_size = 0; size_t file_size_long; size_t file_size_int; /* check the arguments */ RT_CHECK_RTI(rtip); BU_CK_VLS(matparm); RT_CK_REGION(rp); if (rdebug&RDEBUG_SHADE) bu_log("tthrm_setup(Region:\"%s\", tthrm(%s))\n", rp->reg_name, bu_vls_addr(matparm)); /* Get memory for the shader parameters and shader-specific data */ BU_GET(tthrm_sp, struct tthrm_specific); *dpp = tthrm_sp; tthrm_sp->magic = tthrm_MAGIC; tthrm_sp->tt_name[0] = '\0'; tthrm_sp->tt_min_temp = tthrm_sp->tt_max_temp = 0.0; if (rdebug&RDEBUG_SHADE) bu_log("Parsing: (%s)\n", bu_vls_addr(matparm)); if (bu_struct_parse(matparm, tthrm_parse, (char *)tthrm_sp) < 0) { bu_bomb(__FILE__); } if (tthrm_sp->tt_name[0] == '\0') { bu_log("Must specify file for tthrm shader on %s (got \"%s\"\n", rp->reg_name, bu_vls_addr(matparm)); bu_bomb(__FILE__); } tt_file = bu_open_mapped_file(tthrm_sp->tt_name, (char *)NULL); if (!tt_file) { bu_log("Error mapping \"%s\"\n", tthrm_sp->tt_name); bu_bomb("shader tthrm: can't get thermal data"); } tt_data = tt_file->buf; if (rdebug&RDEBUG_SHADE) bu_log("tthrm_setup() data: %p total\n", (void *)tt_data); /* Compute how big the file should be, so that we can guess * at the size of the integer at the front of the file */ file_size_int = sizeof(int) + *((int *)tt_data) * (sizeof(short) + sizeof(float) * 4 * NUM_NODES); file_size_long = sizeof(long) + *((long *)tt_data) * (sizeof(short) + sizeof(float) * 4 * NUM_NODES); switch (sizeof(long)) { case 8: if (tt_file->buflen == file_size_long) { /* 64bit data on 64bit host */ long_size = sizeof(long); tthrm_sp->tt_max_seg = cyl_tot = *((long *)tt_data); } else if (tt_file->buflen == file_size_int) { /* 32bit data on 32bit host */ long_size = sizeof(int); tthrm_sp->tt_max_seg = cyl_tot = *((int *)tt_data); } break; case 4: if (tt_file->buflen == file_size_long) { /* 32bit data on 32bit host */ long_size = sizeof(long); tthrm_sp->tt_max_seg = cyl_tot = *((long *)tt_data); } else if (tt_file->buflen == (file_size_long+4)) { /* 64bit data on 32bit host */ cyl_tot = *((int *)tt_data); if (cyl_tot != 0) { bu_log("%s:%d thermal data written on 64bit machine with more that 2^32 segs\n", __FILE__, __LINE__); bu_bomb(""); } long_size = sizeof(long) + 4; tthrm_sp->tt_max_seg = cyl_tot = ((int *)tt_data)[1]; } break; default: bu_log("a long int is %d bytes on this machine\n", sizeof(long)); bu_bomb("I can only handle 4 or 8 byte longs\n"); break; } if (rdebug&RDEBUG_SHADE) bu_log("cyl_tot = %ld\n", cyl_tot); tthrm_sp->tt_segs = (struct thrm_seg *) bu_calloc(cyl_tot, sizeof(struct thrm_seg), "thermal segs"); min_temp = MAX_FASTF; max_temp = -MAX_FASTF; #define CYL_DATA(_n) ((float *) (&tt_data[ \ long_size + \ (_n) * (sizeof(short) + sizeof(float) * 4 * NUM_NODES) + \ sizeof(short) \ ])) for (tseg = 0; tseg < cyl_tot; tseg++) { /* compute centerpoint, min/max temperature values */ fp = CYL_DATA(tseg); VSETALL(center, 0.0); for (node=0; node < NUM_NODES; node++, fp+=4) { /* this is necessary to assure that all float * values are aligned on 4-byte boundaries */ memcpy(fv, fp, sizeof(float)*4); if (rdebug&RDEBUG_SHADE) bu_log("tthrm_setup() node %d (%g %g %g) %g\n", node, fv[0], fv[1], fv[2], fv[3]); /* make sure we don't have any "infinity" values */ for (i=0; i < 4; i++) { if (fv[i] > MAX_FASTF || fv[i] < -MAX_FASTF) { bu_log("%s:%d seg %ld node %d coord %d out of bounds: %g\n", __FILE__, __LINE__, tseg, node, i, fv[i]); bu_bomb("choke, gasp, *croak*\n"); } } /* copy the values to the segment list, converting * from Meters to Millimeters in the process */ VSCALE(tthrm_sp->tt_segs[tseg].node[node], fv, 1000.0); tthrm_sp->tt_segs[tseg].temperature[node] = fv[3]; VADD2(center, center, fv); if (fv[3] > max_temp) max_temp = fv[3]; if (fv[3] < min_temp) min_temp = fv[3]; } VSCALE(center, center, 1000.0); VSCALE(tthrm_sp->tt_segs[tseg].pt, center, inv_nodes); if (rdebug&RDEBUG_SHADE) { bu_log("Center: (%g %g %g) (now in mm, not m)\n", V3ARGS(tthrm_sp->tt_segs[tseg].pt)); } /* compute vectors from center pt for each node */ fp = CYL_DATA(tseg); for (node=0; node < NUM_NODES; node++, fp+=4) { /* this is necessary to assure that all float * values are aligned on 4-byte boundaries */ memcpy(fv, fp, sizeof(float)*4); VSCALE(pt, fv, 1000.0); VSUB2(tthrm_sp->tt_segs[tseg].vect[node], pt, tthrm_sp->tt_segs[tseg].pt ); } /* compute a direction vector for the thermal segment */ VCROSS(dir, tthrm_sp->tt_segs[tseg].vect[0], tthrm_sp->tt_segs[tseg].vect[2]); VUNITIZE(dir); VMOVE(tthrm_sp->tt_segs[tseg].dir, dir); tthrm_sp->tt_segs[tseg].magic = THRM_SEG_MAGIC; } bu_close_mapped_file(tt_file); if (ZERO(tthrm_sp->tt_min_temp) && EQUAL(tthrm_sp->tt_max_temp, SMALL_FASTF)) { tthrm_sp->tt_min_temp = min_temp; tthrm_sp->tt_max_temp = max_temp; bu_log("computed temp min/max on %s: %g/%g\n", rp->reg_name, min_temp, max_temp); } else { min_temp =tthrm_sp->tt_min_temp; max_temp = tthrm_sp->tt_max_temp; bu_log("taking user specified on %s: min/max %g/%g\n", rp->reg_name, min_temp, max_temp); } if (!EQUAL(max_temp, min_temp)) { tthrm_sp->tt_temp_scale = 1.0 / (max_temp - min_temp); } else { /* min and max are equal, maybe zero */ if (ZERO(max_temp)) tthrm_sp->tt_temp_scale = 0.0; else tthrm_sp->tt_temp_scale = 255.0/max_temp; } /* The shader needs to operate in a coordinate system which stays * fixed on the region when the region is moved (as in animation) * we need to get a matrix to perform the appropriate transform(s). * * Shading is done in "region coordinates": */ db_region_mat(tthrm_sp->tthrm_m_to_sh, rtip->rti_dbip, rp->reg_name, &rt_uniresource); if (rdebug&RDEBUG_SHADE) { bu_log("min_temp: %17.14e max_temp %17.14e temp_scale: %17.14e\n", tthrm_sp->tt_min_temp, tthrm_sp->tt_max_temp, tthrm_sp->tt_temp_scale); bu_log("tthrm_setup(%s, %s)done\n", rp->reg_name, bu_vls_addr(matparm)); tthrm_print(rp, *dpp); } return 1; }
/** * R T _ G E T T R E E _ L E A F * * This routine must be prepared to run in parallel. */ HIDDEN union tree *rt_gettree_leaf(struct db_tree_state *tsp, struct db_full_path *pathp, struct rt_db_internal *ip, genptr_t client_data) /*const*/ /*const*/ { register struct soltab *stp; union tree *curtree; struct directory *dp; register matp_t mat; int i; struct rt_i *rtip; RT_CK_DBTS(tsp); RT_CK_DBI(tsp->ts_dbip); RT_CK_FULL_PATH(pathp); RT_CK_DB_INTERNAL(ip); rtip = tsp->ts_rtip; RT_CK_RTI(rtip); RT_CK_RESOURCE(tsp->ts_resp); dp = DB_FULL_PATH_CUR_DIR(pathp); /* Determine if this matrix is an identity matrix */ if ( !bn_mat_is_equal(tsp->ts_mat, bn_mat_identity, &rtip->rti_tol)) { /* Not identity matrix */ mat = (matp_t)tsp->ts_mat; } else { /* Identity matrix */ mat = (matp_t)0; } /* * Check to see if this exact solid has already been processed. * Match on leaf name and matrix. Note that there is a race here * between having st_id filled in a few lines below (which is * necessary for calling ft_prep), and ft_prep filling in * st_aradius. Fortunately, st_aradius starts out as zero, and * will never go down to -1 unless this soltab structure has * become a dead solid, so by testing against -1 (instead of <= 0, * like before, oops), it isn't a problem. */ stp = rt_find_identical_solid( mat, dp, rtip ); if ( stp->st_id != 0 ) { /* stp is an instance of a pre-existing solid */ if ( stp->st_aradius <= -1 ) { /* It's dead, Jim. st_uses was not incremented. */ return( TREE_NULL ); /* BAD: instance of dead solid */ } goto found_it; } if ( rtip->rti_add_to_new_solids_list ) { bu_ptbl_ins( &rtip->rti_new_solids, (long *)stp ); } stp->st_id = ip->idb_type; stp->st_meth = &rt_functab[ip->idb_type]; if ( mat ) { mat = stp->st_matp; } else { mat = (matp_t)bn_mat_identity; } RT_CK_DB_INTERNAL( ip ); /* init solid's maxima and minima */ VSETALL( stp->st_max, -INFINITY ); VSETALL( stp->st_min, INFINITY ); /* * If the ft_prep routine wants to keep the internal structure, * that is OK, as long as idb_ptr is set to null. Note that the * prep routine may have changed st_id. */ if ( stp->st_meth->ft_prep( stp, ip, rtip ) ) { int hash; /* Error, solid no good */ bu_log("rt_gettree_leaf(%s): prep failure\n", dp->d_namep ); /* Too late to delete soltab entry; mark it as "dead" */ hash = db_dirhash( dp->d_namep ); ACQUIRE_SEMAPHORE_TREE(hash); stp->st_aradius = -1; stp->st_uses--; RELEASE_SEMAPHORE_TREE(hash); return( TREE_NULL ); /* BAD */ } if ( rtip->rti_dont_instance ) { /* * If instanced solid refs are not being compressed, then * memory isn't an issue, and the application (such as * solids_on_ray) probably cares about the full path of this * solid, from root to leaf. So make it available here. * (stp->st_dp->d_uses could be the way to discriminate * references uniquely, if the path isn't enough. To locate * given dp and d_uses, search dp->d_use_hd list. Question * is, where to stash current val of d_uses?) */ db_full_path_init( &stp->st_path ); db_dup_full_path( &stp->st_path, pathp ); } else { /* * If there is more than just a direct reference to this leaf * from it's containing region, copy that below-region path * into st_path. Otherwise, leave st_path's magic number 0. * * XXX nothing depends on this behavior yet, and this whole * XXX 'else' clause might well be deleted. -Mike */ i = pathp->fp_len-1; if ( i > 0 && !(pathp->fp_names[i-1]->d_flags & DIR_REGION) ) { /* Search backwards for region. If no region, use whole path */ for ( --i; i > 0; i-- ) { if ( pathp->fp_names[i-1]->d_flags & DIR_REGION ) break; } if ( i < 0 ) i = 0; db_full_path_init( &stp->st_path ); db_dup_path_tail( &stp->st_path, pathp, i ); } } if (RT_G_DEBUG&DEBUG_TREEWALK && stp->st_path.magic == DB_FULL_PATH_MAGIC) { char *sofar = db_path_to_string(&stp->st_path); bu_log("rt_gettree_leaf() st_path=%s\n", sofar ); bu_free(sofar, "path string"); } if (RT_G_DEBUG&DEBUG_SOLIDS) { struct bu_vls str; bu_log("\n---Primitive %d: %s\n", stp->st_bit, dp->d_namep); bu_vls_init( &str ); /* verbose=1, mm2local=1.0 */ if ( stp->st_meth->ft_describe( &str, ip, 1, 1.0, tsp->ts_resp, tsp->ts_dbip ) < 0 ) { bu_log("rt_gettree_leaf(%s): solid describe failure\n", dp->d_namep ); } bu_log( "%s: %s", dp->d_namep, bu_vls_addr( &str ) ); bu_vls_free( &str ); } found_it: RT_GET_TREE( curtree, tsp->ts_resp ); curtree->magic = RT_TREE_MAGIC; curtree->tr_op = OP_SOLID; curtree->tr_a.tu_stp = stp; /* regionp will be filled in later by rt_tree_region_assign() */ curtree->tr_a.tu_regionp = (struct region *)0; if (RT_G_DEBUG&DEBUG_TREEWALK) { char *sofar = db_path_to_string(pathp); bu_log("rt_gettree_leaf() %s\n", sofar ); bu_free(sofar, "path string"); } return(curtree); }
HIDDEN int wood_setup(register struct region *rp, struct bu_vls *matparm, void **dpp, const struct mfuncs *UNUSED(mfp), struct rt_i *UNUSED(rtip)) /* New since 4.4 release */ { register int i; register struct wood_specific *wd; register struct resource *resp = &rt_uniresource; /* * Get the impure storage for the control block */ BU_CK_VLS(matparm); BU_GET(wd, struct wood_specific); *dpp = wd; /* * Load the default values */ if (rp->reg_mater.ma_color_valid) { VSCALE(wd->lt_rgb, rp->reg_mater.ma_color, 255); } else { wd->lt_rgb[0] = 255; /* Light yellow */ wd->lt_rgb[1] = 255; wd->lt_rgb[2] = 224; } wd->dk_rgb[0] = 191; /* Brownish-red */ wd->dk_rgb[1] = 97; wd->dk_rgb[2] = 0; wd->ident = 0; wd->forw = WOOD_NULL; wd->rp = rp; wd->flags = 0; wd->overlay = 0; /* Draw only one ring */ wd->ns = 10; wd->jitter = 0.0; wd->scale = 1.0; wd->spacing = 5; /* 5mm space between rings */ wd->dd = 0.0; /* no dither of vertex */ wd->dz = 0.0; /* nor of Z-axis */ wd->qd = 0; wd->qp = 0; wd->phase = 5; wd->depth = 0; wd->dither[0] = bn_rand0to1(resp->re_randptr); wd->dither[1] = bn_rand0to1(resp->re_randptr); wd->dither[2] = bn_rand0to1(resp->re_randptr); VSETALL(wd->rot, 0); VSETALL(wd->vertex, 0); VSETALL(wd->D, 0); VSETALL(wd->V, 0); /* * Parse the MATPARM field */ if (bu_struct_parse(matparm, wood_parse, (char *)wd) < 0) { BU_PUT(wd, struct wood_specific); return -1; } /* * Do some sundry range and misc. checking */ for (i = 0; i < 3; i++) { if (wd->dither[i] < 0 || wd->dither[i] > 1.0) { bu_log("wood_setup(%s): dither is out of range.\n", rp->reg_name); return -1; } } if (wd->flags == EXPLICIT_VERTEX) { bu_log("wood_setup(%s): Explicit vertex specified without direction\n", rp->reg_name); return -1; } if (wd->flags == EXPLICIT_DIRECTION) { bu_log("wood_setup(%s): Explicit direction specified without vertex\n", rp->reg_name); return -1; } /* * Get the bounding RPP */ if (rt_bound_tree(rp->reg_treetop, wd->b_min, wd->b_max) < 0) return -1; /* * Add it to the wood chain */ wd->forw = Wood_Chain; Wood_Chain = wd; /* * See if the user has flagged this region as a member of a larger * combination. If so, go ahead and process it */ if (wd->ident == 0) wood_setup_2(wd); else { register struct wood_specific *wc; vect_t c_min, c_max; /* * First, process the accumulated chain of wood regions and * process all regions which have the specified ident field. */ VSETALL(c_min, 0); VSETALL(c_max, 0); for (wc = Wood_Chain; wc != WOOD_NULL; wc = wc->forw) { if (wc->ident == wd->ident) { VMIN(c_min, wc->b_min); VMAX(c_max, wc->b_max); } } /* * Now, loop through the chain again this time updating the * regions' min/max fields with the new values */ for (wc = Wood_Chain; wc != WOOD_NULL; wc = wc->forw) { if (wc->ident == wd->ident) { VMOVE(wc->b_min, c_min); VMOVE(wc->b_max, c_max); wood_setup_2(wc); } } /* * End of multi-region processing loop */ } /* * Normalize the RGB colors */ for (i = 0; i < 3; i++) { wd->lt_rgb[i] /= 255.0; wd->dk_rgb[i] /= 255.0; } /* * Return to the caller */ return 1; }