/* * Sort of a surface spot transparency shader. Picks transparency * based upon noise value of surface spot. */ int tsplat_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) { register struct scloud_specific *scloud_sp = (struct scloud_specific *)dp; point_t in_pt; /* point where ray enters scloud solid */ double val; RT_CHECK_PT(pp); RT_AP_CHECK(ap); RT_CK_REGION(pp->pt_regionp); /* just shade the surface with a transparency */ MAT4X3PNT(in_pt, scloud_sp->mtos, swp->sw_hit.hit_point); val = bn_noise_fbm(in_pt, scloud_sp->h_val, scloud_sp->lacunarity, scloud_sp->octaves); CLAMP(val, 0.0, 1.0); swp->sw_transmit = 1.0 - val; if (swp->sw_reflect > 0 || swp->sw_transmit > 0) (void)rr_render(ap, pp, swp); return 1; }
/* * 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. */ int toon_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) { int i; struct toon_specific *toon_sp = (struct toon_specific *)dp; struct light_specific *lp; fastf_t cosi, scale; /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); CK_TOON_SP(toon_sp); if (rdebug&RDEBUG_SHADE) bu_struct_print("toon_render Parameters:", toon_print_tab, (char *)toon_sp); /* if surface normal is nearly orthogonal to the ray, make a black line */ if (VDOT(swp->sw_hit.hit_normal, ap->a_inv_dir) >= 0.8) { VSETALL(swp->sw_color, 0); return 1; } /* probably need to set some swp values here to avoid the infinite recursion * if specified lights exist. */ light_obs(ap, swp, MFI_HIT); /* Consider effects of each light source */ for (i=ap->a_rt_i->rti_nlights-1; i>=0; i--) { if ((lp = (struct light_specific *)swp->sw_visible[i]) == LIGHT_NULL) continue; cosi = VDOT(swp->sw_hit.hit_normal, swp->sw_tolight); if (cosi <= 0.0) scale = 0.0; else if (cosi <= 0.5) scale = 0.5; else if (cosi <= 0.8) scale = 0.8; else scale = 1.0; VSCALE(swp->sw_color, swp->sw_color, scale); return 1; } /* no paths to light source, so just paint it black */ VSETALL(swp->sw_color, 0); if (swp->sw_reflect > 0 || swp->sw_transmit > 0) (void)rr_render(ap, pp, swp); return 1; }
/* * N U L L _ R E N D E R * * 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. This is, of course, not necessary when setup returns 0. * * The null shader actually does "something", though it is not called. * It has to at least pass the ray through so that it can actually * raytrace what is visible behind the invisible object. Otherwise, * an empty black void would be rendered. this is not really important * though, since it shouldn't normally be called. */ HIDDEN int sh_null_render( struct application *ap, struct partition *pp, struct shadework *swp, char *dp ) { /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); /* shadework structures do not have magic numbers or other means to test * their validity */ bu_log("Who called sh_null_render explicitly?"); /* here is what actually makes the object invisible/null instead of being a * black void (if render ever is called). */ (void)rr_render(ap, pp, swp); return(1); }
/* * 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. * * The flat shader is probably the second most simple shader (second to * the null/invisible shader -- it does nothing). It shades an object * a constant color. The only complexity comes into play when a * transparency value is set. Then we get the color value behind the * one we are shading and blend accordingly with the flat color. */ int flat_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) { register struct flat_specific *flat_sp = (struct flat_specific *)dp; const point_t unit = {1.0, 1.0, 1.0}; point_t intensity; /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); CK_FLAT_SP(flat_sp); if (rdebug&RDEBUG_SHADE) bu_struct_print("flat_render Parameters:", flat_parse_tab, (char *)flat_sp); /* do the actual flat color shading for the flat object. if the object is * not transparent, just put the color. if the object is transparent, do * a little more work determining the background pixel, and then blend with * the flat foreground object. */ if (VNEAR_ZERO(flat_sp->transparency, SMALL_FASTF)) { /* just put the flat value */ VMOVE(swp->sw_color, flat_sp->color); } else { /* this gets the background pixel value, if the transparency is not 0 */ swp->sw_transmit = 1.0; /*!!! try to remove */ VMOVE(swp->sw_basecolor, flat_sp->transparency); (void)rr_render(ap, pp, swp); /* now blend with the foreground object being shaded */ VSUB2(intensity, unit, flat_sp->transparency); /* inverse transparency is how much we want */ VELMUL(intensity, intensity, flat_sp->color); /* ??? is there a way to merge this mul->add step? */ VADD2(swp->sw_color, swp->sw_color, intensity); } return 1; }
/** * Given a ray, shoot it at all the relevant parts of the model, * (building the HeadSeg chain), and then call rt_boolregions() to * build and evaluate the partition chain. If the ray actually hit * anything, call the application's a_hit() routine with a pointer to * the partition chain, otherwise, call the application's a_miss() * routine. * * It is important to note that rays extend infinitely only in the * positive direction. The ray is composed of all points P, where * * P = r_pt + K * r_dir * * for K ranging from 0 to +infinity. There is no looking backwards. * * It is also important to note that the direction vector r_dir must * have unit length; this is mandatory, and is not ordinarily checked, * in the name of efficiency. * * Input: Pointer to an application structure, with these mandatory fields: * a_ray.r_pt Starting point of ray to be fired * a_ray.r_dir UNIT VECTOR with direction to fire in (dir cosines) * a_hit Routine to call when something is hit * a_miss Routine to call when ray misses everything * * Calls user's a_miss() or a_hit() routine as appropriate. Passes * a_hit() routine list of partitions, with only hit_dist fields * valid. Normal computation deferred to user code, to avoid needless * computation here. * * Returns: whatever the application function returns (an int). * * NOTE: The application functions may call rt_shootray() recursively. * Thus, none of the local variables may be static. * * An open issue for execution in a PARALLEL environment is locking of * the statistics variables. */ int rt_vshootray(struct application *ap) { struct seg *HeadSeg; int ret; vect_t inv_dir; /* inverses of ap->a_ray.r_dir */ struct bu_bitv *solidbits; /* bits for all solids shot so far */ struct bu_ptbl *regionbits; /* bits for all involved regions */ char *status; struct partition InitialPart; /* Head of Initial Partitions */ struct partition FinalPart; /* Head of Final Partitions */ int nrays = 1; /* for now */ int vlen; int id; int i; struct soltab **ary_stp; /* array of pointers */ struct xray **ary_rp; /* array of pointers */ struct seg *ary_seg; /* array of structures */ struct rt_i *rtip; int done; #define BACKING_DIST (-2.0) /* mm to look behind start point */ rtip = ap->a_rt_i; RT_AP_CHECK(ap); if (!ap->a_resource) { ap->a_resource = &rt_uniresource; } RT_CK_RESOURCE(ap->a_resource); if (RT_G_DEBUG&(DEBUG_ALLRAYS|DEBUG_SHOOT|DEBUG_PARTITION)) { bu_log("\n**********mshootray cpu=%d %d, %d lvl=%d (%s)\n", ap->a_resource->re_cpu, ap->a_x, ap->a_y, ap->a_level, ap->a_purpose != (char *)0 ? ap->a_purpose : "?"); VPRINT("Pnt", ap->a_ray.r_pt); VPRINT("Dir", ap->a_ray.r_dir); } rtip->rti_nrays++; if (rtip->needprep) rt_prep(rtip); /* Allocate dynamic memory */ vlen = nrays * rtip->rti_maxsol_by_type; ary_stp = (struct soltab **)bu_calloc(vlen, sizeof(struct soltab *), "*ary_stp[]"); ary_rp = (struct xray **)bu_calloc(vlen, sizeof(struct xray *), "*ary_rp[]"); ary_seg = (struct seg *)bu_calloc(vlen, sizeof(struct seg), "ary_seg[]"); /**** for each ray, do this ****/ InitialPart.pt_forw = InitialPart.pt_back = &InitialPart; FinalPart.pt_forw = FinalPart.pt_back = &FinalPart; HeadSeg = RT_SEG_NULL; solidbits = rt_get_solidbitv(rtip->nsolids, ap->a_resource); if (BU_LIST_IS_EMPTY(&ap->a_resource->re_region_ptbl)) { BU_ALLOC(regionbits, struct bu_ptbl); bu_ptbl_init(regionbits, 7, "rt_shootray() regionbits ptbl"); } else { regionbits = BU_LIST_FIRST(bu_ptbl, &ap->a_resource->re_region_ptbl); BU_LIST_DEQUEUE(®ionbits->l); BU_CK_PTBL(regionbits); } /* Compute the inverse of the direction cosines */ if (!ZERO(ap->a_ray.r_dir[X])) { inv_dir[X]=1.0/ap->a_ray.r_dir[X]; } else { inv_dir[X] = INFINITY; ap->a_ray.r_dir[X] = 0.0; } if (!ZERO(ap->a_ray.r_dir[Y])) { inv_dir[Y]=1.0/ap->a_ray.r_dir[Y]; } else { inv_dir[Y] = INFINITY; ap->a_ray.r_dir[Y] = 0.0; } if (!ZERO(ap->a_ray.r_dir[Z])) { inv_dir[Z]=1.0/ap->a_ray.r_dir[Z]; } else { inv_dir[Z] = INFINITY; ap->a_ray.r_dir[Z] = 0.0; } /* * XXX handle infinite solids here, later. */ /* * If ray does not enter the model RPP, skip on. * If ray ends exactly at the model RPP, trace it. */ if (!rt_in_rpp(&ap->a_ray, inv_dir, rtip->mdl_min, rtip->mdl_max) || ap->a_ray.r_max < 0.0) { rtip->nmiss_model++; if (ap->a_miss) ret = ap->a_miss(ap); else ret = 0; status = "MISS model"; goto out; } /* For each type of solid to be shot at, assemble the vectors */ for (id = 1; id <= ID_MAX_SOLID; id++) { register int nsol; if ((nsol = rtip->rti_nsol_by_type[id]) <= 0) continue; /* For each instance of this solid type */ for (i = nsol-1; i >= 0; i--) { ary_stp[i] = rtip->rti_sol_by_type[id][i]; ary_rp[i] = &(ap->a_ray); /* XXX, sb [ray] */ ary_seg[i].seg_stp = SOLTAB_NULL; BU_LIST_INIT(&ary_seg[i].l); } /* bounding box check */ /* bit vector per ray check */ /* mark elements to be skipped with ary_stp[] = SOLTAB_NULL */ ap->a_rt_i->nshots += nsol; /* later: skipped ones */ if (OBJ[id].ft_vshot) { OBJ[id].ft_vshot(ary_stp, ary_rp, ary_seg, nsol, ap); } else { vshot_stub(ary_stp, ary_rp, ary_seg, nsol, ap); } /* set bits for all solids shot at for each ray */ /* append resulting seg list to input for boolweave */ for (i = nsol-1; i >= 0; i--) { register struct seg *seg2; if (ary_seg[i].seg_stp == SOLTAB_NULL) { /* MISS */ ap->a_rt_i->nmiss++; continue; } ap->a_rt_i->nhits++; /* For now, do it the slow way. sb [ray] */ /* MUST dup it -- all segs have to live till after a_hit() */ RT_GET_SEG(seg2, ap->a_resource); *seg2 = ary_seg[i]; /* struct copy */ /* rt_boolweave(seg2, &InitialPart, ap); */ bu_bomb("FIXME: need to call boolweave here"); /* Add seg chain to list of used segs awaiting reclaim */ #if 0 /* FIXME: need to use waiting_segs/finished_segs here in * conjunction with rt_boolweave() { register struct seg *seg3 = seg2; while (seg3->seg_next != RT_SEG_NULL) seg3 = seg3->seg_next; seg3->seg_next = HeadSeg; HeadSeg = seg2; } */ #endif } } /* * Ray has finally left known space. */ if (InitialPart.pt_forw == &InitialPart) { if (ap->a_miss) ret = ap->a_miss(ap); else ret = 0; status = "MISSed all primitives"; goto freeup; } /* * All intersections of the ray with the model have been computed. * Evaluate the boolean trees over each partition. */ done = rt_boolfinal(&InitialPart, &FinalPart, BACKING_DIST, INFINITY, regionbits, ap, solidbits); if (done > 0) goto hitit; if (FinalPart.pt_forw == &FinalPart) { if (ap->a_miss) ret = ap->a_miss(ap); else ret = 0; status = "MISS bool"; goto freeup; } /* * Ray/model intersections exist. Pass the list to the user's * a_hit() routine. Note that only the hit_dist elements of * pt_inhit and pt_outhit have been computed yet. To compute both * hit_point and hit_normal, use the * * RT_HIT_NORMAL(NULL, hitp, stp, rayp, 0); * * macro. To compute just hit_point, use * * VJOIN1(hitp->hit_point, rp->r_pt, hitp->hit_dist, rp->r_dir); */ hitit: if (RT_G_DEBUG&DEBUG_SHOOT) rt_pr_partitions(rtip, &FinalPart, "a_hit()"); if (ap->a_hit) ret = ap->a_hit(ap, &FinalPart, HeadSeg/* &finished_segs */); else ret = 0; status = "HIT"; /* * Processing of this ray is complete. Free dynamic resources. */ freeup: { register struct partition *pp; /* Free up initial partition list */ for (pp = InitialPart.pt_forw; pp != &InitialPart;) { register struct partition *newpp; newpp = pp; pp = pp->pt_forw; FREE_PT(newpp, ap->a_resource); } /* Free up final partition list */ for (pp = FinalPart.pt_forw; pp != &FinalPart;) { register struct partition *newpp; newpp = pp; pp = pp->pt_forw; FREE_PT(newpp, ap->a_resource); } } /* Segs can't be freed until after a_hit() has returned */ #if 0 /* FIXME: depends on commented out code above */ if (HeadSeg) RT_FREE_SEG_LIST(HeadSeg, ap->a_resource); #endif out: bu_free((char *)ary_stp, "*ary_stp[]"); bu_free((char *)ary_rp, "*ary_rp[]"); bu_free((char *)ary_seg, "ary_seg[]"); if (solidbits != NULL) { bu_bitv_free(solidbits); } if (RT_G_DEBUG&(DEBUG_ALLRAYS|DEBUG_SHOOT|DEBUG_PARTITION)) { bu_log("----------mshootray cpu=%d %d, %d lvl=%d (%s) %s ret=%d\n", ap->a_resource->re_cpu, ap->a_x, ap->a_y, ap->a_level, ap->a_purpose != (char *)0 ? ap->a_purpose : "?", status, ret); } return ret; }
int scloud_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) { register struct scloud_specific *scloud_sp = (struct scloud_specific *)dp; point_t in_pt; /* point where ray enters scloud solid */ point_t out_pt; /* point where ray leaves scloud solid */ point_t pt; vect_t v_cloud;/* vector representing ray/solid intersection */ double thickness; /* magnitude of v_cloud (distance through solid) */ int steps; /* # of samples along ray/solid intersection */ double step_delta;/* distance between sample points, texture space */ int i; double val; double trans; point_t incident_light = VINIT_ZERO; double delta_dpmm; double density; struct shadework sub_sw; struct light_specific *lp; RT_CHECK_PT(pp); RT_AP_CHECK(ap); RT_CK_REGION(pp->pt_regionp); /* compute the ray/solid in and out points, * and transform them into "shader space" coordinates */ VJOIN1(pt, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir); MAT4X3PNT(in_pt, scloud_sp->mtos, pt); VJOIN1(pt, ap->a_ray.r_pt, pp->pt_outhit->hit_dist, ap->a_ray.r_dir); MAT4X3PNT(out_pt, scloud_sp->mtos, pt); /* get ray/solid intersection vector (in noise space) * and compute thickness of solid (in noise space) along ray path */ VSUB2(v_cloud, out_pt, in_pt); thickness = MAGNITUDE(v_cloud); /* The noise field used by the bn_noise_turb and bn_noise_fbm routines * has a maximum frequency of about 1 cycle per integer step in * noise space. Each octave increases this frequency by the * "lacunarity" factor. To sample this space adequately we need * * 4 samples per integer step for the first octave, * lacunarity * 4 samples/step for the second octave, * lacunarity^2 * 4 samples/step for the third octave, * lacunarity^3 * 4 samples/step for the forth octave, * * so for a computation with 4 octaves we need something on the * order of lacunarity^3 * 4 samples per integer step in noise space. */ steps = pow(scloud_sp->lacunarity, scloud_sp->octaves-1) * 4; step_delta = thickness / (double)steps; if (rdebug&RDEBUG_SHADE) bu_log("steps=%d delta=%g thickness=%g\n", steps, step_delta, thickness); VUNITIZE(v_cloud); VMOVE(pt, in_pt); trans = 1.0; delta_dpmm = scloud_sp->max_d_p_mm - scloud_sp->min_d_p_mm; sub_sw = *swp; /* struct copy */ sub_sw.sw_inputs = MFI_HIT; for (i=0; i < steps; i++) { /* compute the next point in the cloud space */ VJOIN1(pt, in_pt, i*step_delta, v_cloud); /* get turbulence value (0 .. 1) */ val = bn_noise_turb(pt, scloud_sp->h_val, scloud_sp->lacunarity, scloud_sp->octaves); density = scloud_sp->min_d_p_mm + val * delta_dpmm; val = exp(- density * step_delta); trans *= val; if (swp->sw_xmitonly) continue; /* need to set the hit in our fake shadework structure */ MAT4X3PNT(sub_sw.sw_hit.hit_point, scloud_sp->stom, pt); sub_sw.sw_transmit = trans; sub_sw.sw_inputs = MFI_HIT; light_obs(ap, &sub_sw, swp->sw_inputs); /* now we know how much light has arrived from each * light source to this point */ for (i=ap->a_rt_i->rti_nlights-1; i >= 0; i--) { lp = (struct light_specific *)swp->sw_visible[i]; if (lp == LIGHT_NULL) continue; /* compute how much light has arrived at * this location */ incident_light[0] += sub_sw.sw_intensity[3*i+0] * lp->lt_color[0] * sub_sw.sw_lightfract[i]; incident_light[1] += sub_sw.sw_intensity[3*i+1] * lp->lt_color[1] * sub_sw.sw_lightfract[i]; incident_light[2] += sub_sw.sw_intensity[3*i+2] * lp->lt_color[2] * sub_sw.sw_lightfract[i]; } VSCALE(incident_light, incident_light, trans); } /* scloud is basically a white object with partial transparency */ swp->sw_transmit = trans; if (swp->sw_xmitonly) return 1; /* * At the point of maximum opacity, check light visibility * for light color and cloud shadowing. * OOPS: Don't use an interior point, or light_visibility() * will see an attenuated light source. */ swp->sw_hit.hit_dist = pp->pt_inhit->hit_dist; VJOIN1(swp->sw_hit.hit_point, ap->a_ray.r_pt, swp->sw_hit.hit_dist, ap->a_ray.r_dir); VREVERSE(swp->sw_hit.hit_normal, ap->a_ray.r_dir); swp->sw_inputs |= MFI_HIT | MFI_NORMAL; light_obs(ap, swp, swp->sw_inputs); VSETALL(incident_light, 0); for (i=ap->a_rt_i->rti_nlights-1; i>=0; i--) { struct light_specific *lp2; if ((lp2 = (struct light_specific *)swp->sw_visible[i]) == LIGHT_NULL) continue; /* XXX don't have a macro for this */ incident_light[0] += swp->sw_intensity[3*i+0] * lp2->lt_color[0]; incident_light[1] += swp->sw_intensity[3*i+1] * lp2->lt_color[1]; incident_light[2] += swp->sw_intensity[3*i+2] * lp2->lt_color[2]; } VELMUL(swp->sw_color, swp->sw_color, incident_light); if (rdebug&RDEBUG_SHADE) { pr_shadework("scloud: after light vis, before rr_render", swp); } if (swp->sw_reflect > 0 || swp->sw_transmit > 0) (void)rr_render(ap, pp, swp); return 1; }
/* * F I R E _ R E N D E R * * 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. */ int fire_render(struct application *ap, struct partition *pp, struct shadework *swp, char *dp) /* defined in material.h */ /* ptr to the shader-specific struct */ { #define DEBUG_SPACE_PRINT(str, i_pt, o_pt) \ if (rdebug&RDEBUG_SHADE || fire_sp->fire_debug ) { \ bu_log("fire_render() %s space \n", str); \ bu_log("fire_render() i_pt(%g %g %g)\n", V3ARGS(i_pt) ); \ bu_log("fire_render() o_pt(%g %g %g)\n", V3ARGS(o_pt) ); \ } #define SHADER_TO_NOISE(n_pt, sh_pt, fire_sp, zdelta) { \ point_t tmp_pt; \ tmp_pt[X] = sh_pt[X]; \ tmp_pt[Y] = sh_pt[Y]; \ if ( ! NEAR_ZERO(fire_sp->fire_stretch, SQRT_SMALL_FASTF) ) \ tmp_pt[Z] = exp( (sh_pt[Z]+0.125) * -fire_sp->fire_stretch ); \ else \ tmp_pt[Z] = sh_pt[Z]; \ MAT4X3PNT(n_pt, fire_sp->fire_sh_to_noise, tmp_pt); \ n_pt[Z] += zdelta; \ } register struct fire_specific *fire_sp = (struct fire_specific *)dp; point_t m_i_pt, m_o_pt; /* model space in/out points */ point_t sh_i_pt, sh_o_pt; /* shader space in/out points */ point_t noise_i_pt, noise_o_pt; /* shader space in/out points */ point_t noise_pt; point_t color; vect_t noise_r_dir; double noise_r_thick; int i; double samples_per_unit_noise; double noise_dist_per_sample; point_t shader_pt; vect_t shader_r_dir; double shader_r_thick; double shader_dist_per_sample; double noise_zdelta; int samples; double dist; double noise_val; double lumens; /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); CK_fire_SP(fire_sp); if (rdebug&RDEBUG_SHADE || fire_sp->fire_debug ) { /* bu_struct_print( "fire_render Parameters:", fire_print_tab, (char *)fire_sp ); */ bu_log("fire_render()\n"); } /* If we are performing the shading in "region" space, we must * transform the hit point from "model" space to "region" space. * See the call to db_region_mat in fire_setup(). */ /* * Compute the ray/solid in and out points, */ VMOVE(m_i_pt, swp->sw_hit.hit_point); VJOIN1(m_o_pt, ap->a_ray.r_pt, pp->pt_outhit->hit_dist, ap->a_ray.r_dir); DEBUG_SPACE_PRINT("model", m_i_pt, m_o_pt); /* map points into shader space */ MAT4X3PNT(sh_i_pt, fire_sp->fire_m_to_sh, m_i_pt); MAT4X3PNT(sh_o_pt, fire_sp->fire_m_to_sh, m_o_pt); DEBUG_SPACE_PRINT("shader", sh_i_pt, sh_o_pt); noise_zdelta = fire_sp->fire_flicker * swp->sw_frametime; SHADER_TO_NOISE(noise_i_pt, sh_i_pt, fire_sp, noise_zdelta); SHADER_TO_NOISE(noise_o_pt, sh_o_pt, fire_sp, noise_zdelta); VSUB2(noise_r_dir, noise_o_pt, noise_i_pt); noise_r_thick = MAGNITUDE(noise_r_dir); if (rdebug&RDEBUG_SHADE || fire_sp->fire_debug ) { bu_log("fire_render() noise_r_dir (%g %g %g)\n", V3ARGS(noise_r_dir) ); bu_log("fire_render() noise_r_thick %g\n", noise_r_thick); } /* compute number of samples per unit length in noise space. * * The noise field used by the bn_noise_turb and bn_noise_fbm routines * has a maximum frequency of about 1 cycle per integer step in * noise space. Each octave increases this frequency by the * "lacunarity" factor. To sample this space adequately, nyquist * tells us we need at least 4 samples/cycle at the highest octave * rate. */ samples_per_unit_noise = pow(fire_sp->noise_lacunarity, fire_sp->noise_octaves-1) * 4.0; noise_dist_per_sample = 1.0 / samples_per_unit_noise; samples = samples_per_unit_noise * noise_r_thick; if (samples < 1) samples = 1; if (rdebug&RDEBUG_SHADE || fire_sp->fire_debug ) { bu_log("samples:%d\n", samples); bu_log("samples_per_unit_noise %g\n", samples_per_unit_noise); bu_log("noise_dist_per_sample %g\n", noise_dist_per_sample); } /* To do the exponential stretch and decay properly we need to * do the computations in shader space, and convert the points * to noise space. Performance pig. */ VSUB2(shader_r_dir, sh_o_pt, sh_i_pt); shader_r_thick = MAGNITUDE(shader_r_dir); VUNITIZE(shader_r_dir); shader_dist_per_sample = shader_r_thick / samples; lumens = 0.0; for (i = 0; i < samples; i++) { dist = (double)i * shader_dist_per_sample; VJOIN1(shader_pt, sh_i_pt, dist, shader_r_dir); SHADER_TO_NOISE(noise_pt, shader_pt, fire_sp, noise_zdelta); noise_val = bn_noise_turb(noise_pt, fire_sp->noise_h_val, fire_sp->noise_lacunarity, fire_sp->noise_octaves); if (rdebug&RDEBUG_SHADE || fire_sp->fire_debug ) bu_log("bn_noise_turb(%g %g %g) = %g\n", V3ARGS(noise_pt), noise_val); /* XXX * When doing the exponential stretch, we scale the noise * value by the height in shader space */ if ( NEAR_ZERO(fire_sp->fire_stretch, SQRT_SMALL_FASTF) ) lumens += noise_val * 0.025; else { register double t; t = lumens; lumens += noise_val * 0.025 * (1.0 -shader_pt[Z]); if (rdebug&RDEBUG_SHADE || fire_sp->fire_debug ) bu_log("lumens:%g = %g + %g * %g\n", lumens, t, noise_val, 0.025 * (1.0 - shader_pt[Z]) ); } if (lumens >= 1.0) { lumens = 1.0; if (rdebug&RDEBUG_SHADE || fire_sp->fire_debug ) bu_log("early exit from lumens loop\n"); break; } } if (rdebug&RDEBUG_SHADE || fire_sp->fire_debug ) bu_log("lumens = %g\n", lumens); if (lumens < 0.0) lumens = 0.0; else if (lumens > 1.0) lumens = 1.0; rt_dspline_n(color, fire_sp->fire_colorspline_mat, (double *)flame_colors, 18, 3, lumens); VMOVE(swp->sw_color, color); /* VSETALL(swp->sw_basecolor, 1.0);*/ swp->sw_transmit = 1.0 - (lumens * 4.); if (swp->sw_reflect > 0 || swp->sw_transmit > 0 ) (void)rr_render( ap, pp, swp ); return(1); }
/* * 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. */ HIDDEN int osl_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) /* defined in ../h/shadework.h */ /* ptr to the shader-specific struct */ { register struct osl_specific *osl_sp = (struct osl_specific *)dp; void * thread_info; int nsamples; /* Number of samples */ /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); CK_OSL_SP(osl_sp); if (rdebug&RDEBUG_SHADE) bu_struct_print("osl_render Parameters:", osl_print_tab, (char *)osl_sp); bu_semaphore_acquire(BU_SEM_SYSCALL); /* Check if it is the first time this thread is calling this function */ bool visited = false; for (size_t i = 0; i < visited_addrs.size(); i++) { if (ap->a_resource == visited_addrs[i]) { visited = true; thread_info = thread_infos[i]; break; } } if (!visited) { visited_addrs.push_back(ap->a_resource); /* Get thread specific information from OSLRender system */ thread_info = oslr->CreateThreadInfo(); thread_infos.push_back(thread_info); } if (ap->a_level == 0) { default_a_hit = ap->a_hit; /* save the default hit callback (colorview @ rt) */ default_a_miss = ap->a_miss; } bu_semaphore_release(BU_SEM_SYSCALL); Color3 acc_color(0.0f); /* ----------------------------------- * Fill in all necessary information for the OSL renderer * ----------------------------------- */ RenderInfo info; /* Set hit point */ VMOVE(info.P, swp->sw_hit.hit_point); /* Set normal at the point */ VMOVE(info.N, swp->sw_hit.hit_normal); /* Set incidence ray direction */ VMOVE(info.I, ap->a_ray.r_dir); /* U-V mapping stuff */ info.u = swp->sw_uv.uv_u; info.v = swp->sw_uv.uv_v; VSETALL(info.dPdu, 0.0f); VSETALL(info.dPdv, 0.0f); /* x and y pixel coordinates */ info.screen_x = ap->a_x; info.screen_y = ap->a_y; info.depth = ap->a_level; info.surfacearea = 1.0f; info.shader_ref = osl_sp->shader_ref; /* We assume that the only information that will be written is thread_info, so that oslr->QueryColor is thread safe */ info.thread_info = thread_info; // Ray-tracing (local illumination) /* We only perform reflection if application decides to */ info.doreflection = 0; info.out_ray_type = 0; Color3 weight = oslr->QueryColor(&info); /* Fire another ray */ if ((info.out_ray_type & RAY_REFLECT) || (info.out_ray_type & RAY_TRANSMIT)) { struct application new_ap; RT_APPLICATION_INIT(&new_ap); new_ap = *ap; /* struct copy */ new_ap.a_onehit = 1; new_ap.a_hit = default_a_hit; new_ap.a_level = info.depth + 1; new_ap.a_flag = 0; VMOVE(new_ap.a_ray.r_dir, info.out_ray.dir); VMOVE(new_ap.a_ray.r_pt, info.out_ray.origin); /* This next ray represents refraction */ if (info.out_ray_type & RAY_TRANSMIT) { /* Displace the hit point a little bit in the direction of the next ray */ Vec3 tmp; VSCALE(tmp, info.out_ray.dir, 1e-4); VADD2(new_ap.a_ray.r_pt, new_ap.a_ray.r_pt, tmp); new_ap.a_onehit = 1; new_ap.a_refrac_index = 1.5; new_ap.a_flag = 2; /* mark as refraction */ new_ap.a_hit = osl_refraction_hit; } (void)rt_shootray(&new_ap); Color3 rec; VMOVE(rec, new_ap.a_color); Color3 res = rec*weight; VMOVE(swp->sw_color, res); } else { /* Final color */ VMOVE(swp->sw_color, weight); } return 1; }
/* * 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. */ int gauss_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) /* defined in material.h */ /* ptr to the shader-specific struct */ { register struct gauss_specific *gauss_sp = (struct gauss_specific *)dp; struct seg *seg_p; struct reg_db_internals *dbint_p; double optical_density = 0.0; /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); CK_gauss_SP(gauss_sp); if (rdebug&RDEBUG_SHADE) { bu_struct_print("gauss_render Parameters:", gauss_print_tab, (char *)gauss_sp); bu_log("r_pt(%g %g %g) r_dir(%g %g %g)\n", V3ARGS(ap->a_ray.r_pt), V3ARGS(ap->a_ray.r_dir)); } BU_CK_LIST_HEAD(&swp->sw_segs->l); BU_CK_LIST_HEAD(&gauss_sp->dbil); /* look at each segment that participated in the ray partition(s) */ for (BU_LIST_FOR(seg_p, seg, &swp->sw_segs->l)) { if (rdebug&RDEBUG_SHADE) { bu_log("seg %g -> %g\n", seg_p->seg_in.hit_dist, seg_p->seg_out.hit_dist); } RT_CK_SEG(seg_p); RT_CK_SOLTAB(seg_p->seg_stp); /* check to see if the seg/solid is in this partition */ if (bu_ptbl_locate(&pp->pt_seglist, (long *)seg_p) != -1) { /* XXX You might use a bu_ptbl list of the solid pointers... */ /* check to see if the solid is from this region */ for (BU_LIST_FOR(dbint_p, reg_db_internals, &gauss_sp->dbil)) { CK_DBINT(dbint_p); if (dbint_p->st_p == seg_p->seg_stp) { /* The solid from the region is * the solid from the segment * from the partition */ optical_density += eval_seg(ap, dbint_p, seg_p); break; } } } else { if (rdebug&RDEBUG_SHADE) bu_log("gauss_render() bittest failed\n"); } }
/* * 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; }
/* * 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; }
/* * R R _ M I S S */ HIDDEN int rr_miss(struct application *ap) { RT_AP_CHECK(ap); return 1; /* treat as escaping ray */ }
/* * 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 {
/* * T R E E T H E R M _ R E N D E R * * 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. */ int tthrm_render(struct application *ap, const struct partition *pp, struct shadework *swp, genptr_t dp) /* defined in material.h */ /* ptr to the shader-specific struct */ { register struct tthrm_specific *tthrm_sp = (struct tthrm_specific *)dp; struct rt_part_internal *part_p; point_t pt; vect_t pt_v; vect_t v; int solid_number; struct thrm_seg *thrm_seg; int best_idx; double best_val; double Vdot; int node; /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); CK_tthrm_SP(tthrm_sp); /* We are performing the shading in "region" space. We must * transform the hit point from "model" space to "region" space. * See the call to db_region_mat in tthrm_setup(). */ MAT4X3PNT(pt, tthrm_sp->tthrm_m_to_sh, swp->sw_hit.hit_point); if (rdebug&RDEBUG_SHADE) bu_log("tthrm_render(%s, %g %g %g)\n", tthrm_sp->tt_name, V3ARGS(pt)); solid_number = get_solid_number(pp); if (solid_number > tthrm_sp->tt_max_seg) { bu_log("%s:%d solid name %s has solid number higher than %ld\n", __FILE__, __LINE__, tthrm_sp->tt_max_seg); bu_bomb("Choke! ack! gasp! wheeeeeeze.\n"); } thrm_seg = &tthrm_sp->tt_segs[solid_number]; CK_THRM_SEG(thrm_seg); /* Extract the solid parameters for the particle we hit, * Compare them to the values for the node extracted. If they * don't match, then we probably have a mis-match between the * geometry and the treetherm output files. */ if (pp->pt_inseg->seg_stp->st_id != ID_PARTICLE) { bu_log("%d != ID_PART\n", pp->pt_inseg->seg_stp->st_id); bu_bomb(""); } part_p = (struct rt_part_internal *)pp->pt_inseg->seg_stp->st_specific; RT_PART_CK_MAGIC(part_p); VSUB2(v, part_p->part_V, thrm_seg->pt); if (MAGSQ(v) > 100.0) { double dist; dist = MAGNITUDE(v); /* Distance between particle origin and centroid of thermal * segment nodes is > 10.0mm (1cm). This suggests that * they aren't related. */ bu_log( "----------------------------- W A R N I N G -----------------------------\n\ %s:%d distance %g between origin of particle and thermal node centroid is\n\ too large. Probable mis-match between geometry and thermal data\n", __FILE__, __LINE__, dist); bu_bomb(""); }