void prepare(void) { struct rt_i *rtip = APP.a_rt_i; RT_CK_RTI(rtip); if (debug) fprintf(stderr, "prepare()\n"); /* * initialize application -- it will allocate 1 line and * set buf_mode=1, as well as do mlib_init(). */ (void)view_init(&APP, title_file, title_obj, 0, 0); do_prep(rtip); if (rtip->nsolids <= 0) bu_exit(3, "ph_matrix: No solids remain after prep.\n"); grid_setup(); /* initialize lighting */ view_2init(&APP, NULL); rtip->nshots = 0; rtip->nmiss_model = 0; rtip->nmiss_tree = 0; rtip->nmiss_solid = 0; rtip->nmiss = 0; rtip->nhits = 0; rtip->rti_nrays = 0; }
HIDDEN int envmap_setup(register struct region *rp, struct bu_vls *matparm, void **UNUSED(dpp), const struct mfuncs *UNUSED(mfp), struct rt_i *rtip) { struct mfuncs *shaders = MF_NULL; BU_CK_VLS(matparm); RT_CK_RTI(rtip); if (env_region.reg_mfuncs) { bu_log("envmap_setup: second environment map ignored\n"); return 0; /* drop region */ } env_region = *rp; /* struct copy */ /* Get copies of, or eliminate, references to dynamic structures */ env_region.reg_name = bu_strdup(rp->reg_name); env_region.reg_treetop = TREE_NULL; env_region.l.forw = BU_LIST_NULL; env_region.l.back = BU_LIST_NULL; env_region.reg_mfuncs = (char *)0; env_region.reg_mater.ma_shader = bu_vls_strdup(matparm); /* get list of available shaders */ if (!shaders) { optical_shader_init(&shaders); } if (mlib_setup(&shaders, &env_region, rtip) < 0) bu_log("envmap_setup() material '%s' failed\n", env_region.reg_mater.ma_shader); return 0; /* This region should be dropped */ }
void ph_matrix(struct pkg_conn *UNUSED(pc), char *buf) { #ifndef NO_MAGIC_CHECKING struct rt_i *rtip = APP.a_rt_i; RT_CK_RTI(rtip); #endif if (debug) fprintf(stderr, "ph_matrix: %s\n", buf); /* Start options in a known state */ AmbientIntensity = 0.4; hypersample = 0; jitter = 0; rt_perspective = 0; eye_backoff = M_SQRT2; aspect = 1; stereo = 0; use_air = 0; width = height = 0; cell_width = cell_height = 0; lightmodel = 0; incr_mode = 0; rt_dist_tol = 0; rt_perp_tol = 0; process_cmd(buf); free(buf); seen_matrix = 1; }
int rt_obj_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip) { int id; const struct rt_functab *ft; if (!stp || !ip) return -1; RT_CK_SOLTAB(stp); RT_CK_DB_INTERNAL(ip); if (rtip) RT_CK_RTI(rtip); id = stp->st_id; if (id < 0) return -2; ft = &OBJ[id]; if (!ft) return -3; if (!ft->ft_prep) return -4; return ft->ft_prep(stp, ip, rtip); }
/* * Go poke the rgb values of a region, on the fly. * This does not update the inmemory database, * so any changes will vanish on next re-prep unless other measures * are taken. */ int sh_directchange_shader(ClientData UNUSED(clientData), Tcl_Interp *interp, int argc, const char *argv[]) { long int rtip_val; struct rt_i *rtip; struct region *regp; struct directory *dp; struct bu_vls shader = BU_VLS_INIT_ZERO; if ( argc < 4 ) { Tcl_AppendResult(interp, "Usage: sh_directchange_shader $rtip comb shader_arg(s)\n", NULL); return TCL_ERROR; } rtip_val = atol(argv[1]); rtip = (struct rt_i *)rtip_val; RT_CK_RTI(rtip); if ( rtip->needprep ) { Tcl_AppendResult(interp, "rt_prep() hasn't been called yet, error.\n", NULL); return TCL_ERROR; } if ( (dp = db_lookup( rtip->rti_dbip, argv[2], LOOKUP_NOISY)) == RT_DIR_NULL ) { Tcl_AppendResult(interp, argv[2], ": not found\n", NULL); return TCL_ERROR; } bu_vls_from_argv(&shader, argc-3, argv+3); bu_vls_trimspace(&shader); /* Find all region names which match /comb/ pattern */ for ( BU_LIST_FOR( regp, region, &rtip->HeadRegion ) ) { /* if ( dp->d_flags & RT_DIR_REGION ) { */ /* name will occur at end of region string w/leading slash */ /* } else { */ /* name will occur anywhere, bracketed by slashes */ /* } */ /* XXX quick hack */ if ( strstr( regp->reg_name, argv[2] ) == NULL ) continue; /* Modify the region's shader string */ bu_log("sh_directchange_shader() changing %s\n", regp->reg_name); if ( regp->reg_mater.ma_shader ) bu_free( (void *)regp->reg_mater.ma_shader, "reg_mater.ma_shader"); regp->reg_mater.ma_shader = bu_vls_strdup(&shader); /* Update the shader */ mlib_free(regp); if ( mlib_setup( &mfHead, regp, rtip ) != 1 ) { Tcl_AppendResult(interp, regp->reg_name, ": mlib_setup() failure\n", NULL); } } bu_vls_free(&shader); return TCL_OK; }
/* * Process pixels from 'a' to 'b' inclusive. * The results are sent back all at once. * Limitation: may not do more than 'width' pixels at once, * because that is the size of the buffer (for now). */ void ph_lines(struct pkg_conn *UNUSED(pc), char *buf) { int a, b, fr; struct line_info info; struct rt_i *rtip = APP.a_rt_i; struct bu_external ext; RT_CK_RTI(rtip); if (debug > 1) fprintf(stderr, "ph_lines: %s\n", buf); if (!seen_gettrees) { bu_log("ph_lines: no MSG_GETTREES yet\n"); return; } if (!seen_matrix) { bu_log("ph_lines: no MSG_MATRIX yet\n"); return; } a=0; b=0; fr=0; if (sscanf(buf, "%d %d %d", &a, &b, &fr) != 3) bu_exit(2, "ph_lines: %s conversion error\n", buf); srv_startpix = a; /* buffer un-offset for view_pixel */ if (b-a+1 > srv_scanlen) b = a + srv_scanlen - 1; rtip->rti_nrays = 0; info.li_startpix = a; info.li_endpix = b; info.li_frame = fr; rt_prep_timer(); do_run(a, b); info.li_nrays = rtip->rti_nrays; info.li_cpusec = rt_read_timer((char *)0, 0); info.li_percent = 42.0; /* for now */ if (!bu_struct_export(&ext, (void *)&info, desc_line_info)) bu_exit(98, "ph_lines: bu_struct_export failure\n"); if (debug) { fprintf(stderr, "PIXELS fr=%d pix=%d..%d, rays=%d, cpu=%g\n", info.li_frame, info.li_startpix, info.li_endpix, info.li_nrays, info.li_cpusec); } if (pkg_2send(MSG_PIXELS, (const char *)ext.ext_buf, ext.ext_nbytes, (const char *)scanbuf, (b-a+1)*3, pcsrv) < 0) { fprintf(stderr, "MSG_PIXELS send error\n"); bu_free_external(&ext); } bu_free_external(&ext); }
/* * Go poke the rgb values of a region, on the fly. * This does not update the inmemory database, * so any changes will vanish on next re-prep unless other measures * are taken. */ int sh_directchange_rgb(ClientData UNUSED(clientData), Tcl_Interp *interp, int argc, const char *argv[]) { long int rtip_val; struct rt_i *rtip; struct region *regp; struct directory *dp; float r, g, b; if ( argc != 6 ) { Tcl_AppendResult(interp, "Usage: sh_directchange_rgb $rtip comb r g b\n", NULL); return TCL_ERROR; } r = atoi(argv[3+0]) / 255.0; g = atoi(argv[3+1]) / 255.0; b = atoi(argv[3+2]) / 255.0; rtip_val = atol(argv[1]); rtip = (struct rt_i *)rtip_val; RT_CK_RTI(rtip); if ( rtip->needprep ) { Tcl_AppendResult(interp, "rt_prep() hasn't been called yet, error.\n", NULL); return TCL_ERROR; } if ( (dp = db_lookup( rtip->rti_dbip, argv[2], LOOKUP_NOISY)) == RT_DIR_NULL ) { Tcl_AppendResult(interp, argv[2], ": not found\n", NULL); return TCL_ERROR; } /* Find all region names which match /comb/ pattern */ for ( BU_LIST_FOR( regp, region, &rtip->HeadRegion ) ) { /* if ( dp->d_flags & RT_DIR_REGION ) { */ /* name will occur at end of region string w/leading slash */ /* } else { */ /* name will occur anywhere, bracketed by slashes */ /* } */ /* XXX quick hack */ if ( strstr( regp->reg_name, argv[2] ) == NULL ) continue; /* Modify the region's color */ bu_log("sh_directchange_rgb() changing %s\n", regp->reg_name); VSET( regp->reg_mater.ma_color, r, g, b ); /* Update the shader */ mlib_free(regp); if ( mlib_setup( &mfHead, regp, rtip ) != 1 ) { Tcl_AppendResult(interp, regp->reg_name, ": mlib_setup() failure\n", NULL); } } return TCL_OK; }
/** * Given a pointer to a GED database record, and a transformation * matrix, determine if this is a valid XXX, and if so, precompute * various terms of the formula. * * Returns - * 0 XXX is OK * !0 Error in description * * Implicit return - * A struct xxx_specific is created, and its address is stored in * stp->st_specific for use by xxx_shot(). */ int rt_xxx_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip) { struct rt_xxx_internal *xxx_ip; if (stp) RT_CK_SOLTAB(stp); RT_CK_DB_INTERNAL(ip); if (rtip) RT_CK_RTI(rtip); xxx_ip = (struct rt_xxx_internal *)ip->idb_ptr; RT_XXX_CK_MAGIC(xxx_ip); return 0; }
/** * prep and build bounding volumes... unfortunately, generating the * bounding sphere is too 'loose' (I think) and O(n^2). */ int rt_metaball_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip) { struct rt_metaball_internal *mb, *nmb; struct wdb_metaballpt *mbpt, *nmbpt; fastf_t minfstr = +INFINITY; if (rtip) RT_CK_RTI(rtip); mb = (struct rt_metaball_internal *)ip->idb_ptr; RT_METABALL_CK_MAGIC(mb); /* generate a copy of the metaball */ BU_ALLOC(nmb, struct rt_metaball_internal); nmb->magic = RT_METABALL_INTERNAL_MAGIC; BU_LIST_INIT(&nmb->metaball_ctrl_head); nmb->threshold = mb->threshold; nmb->method = mb->method; /* and copy the list of control points */ for (BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)) { BU_ALLOC(nmbpt, struct wdb_metaballpt); nmbpt->fldstr = mbpt->fldstr; if (mbpt->fldstr < minfstr) minfstr = mbpt->fldstr; nmbpt->sweat = mbpt->sweat; VMOVE(nmbpt->coord, mbpt->coord); BU_LIST_INSERT(&nmb->metaball_ctrl_head, &nmbpt->l); } /* find the bounding sphere */ stp->st_aradius = rt_metaball_get_bounding_sphere(&stp->st_center, mb->threshold, mb); stp->st_bradius = stp->st_aradius * 1.01; /* XXX magic numbers, increase if scalloping is observed. :(*/ nmb->initstep = minfstr / 2.0; if (nmb->initstep < (stp->st_aradius / 200.0)) nmb->initstep = (stp->st_aradius / 200.0); else if (nmb->initstep > (stp->st_aradius / 10.0)) nmb->initstep = (stp->st_aradius / 10.0); nmb->finalstep = /*stp->st_aradius * */minfstr / 1e5; /* generate a bounding box around the sphere... * XXX this can be optimized greatly to reduce the BSP presence... */ if (rt_metaball_bbox(ip, &(stp->st_min), &(stp->st_max), &rtip->rti_tol)) return 1; stp->st_specific = (void *)nmb; return 0; }
/* ARGSUSED */ HIDDEN int rt_gettree_region_start(struct db_tree_state *tsp, struct db_full_path *pathp, const struct rt_comb_internal *combp, genptr_t client_data) /*const*/ /*const*/ { RT_CK_RTI(tsp->ts_rtip); RT_CK_RESOURCE(tsp->ts_resp); /* Ignore "air" regions unless wanted */ if ( tsp->ts_rtip->useair == 0 && tsp->ts_aircode != 0 ) { tsp->ts_rtip->rti_air_discards++; return(-1); /* drop this region */ } return(0); }
/** * This routine must be prepared to run in parallel. */ HIDDEN int _rt_gettree_region_start(struct db_tree_state *tsp, const struct db_full_path *pathp, const struct rt_comb_internal *combp, void *UNUSED(client_data)) { if (tsp) { RT_CK_RTI(tsp->ts_rtip); RT_CK_RESOURCE(tsp->ts_resp); if (pathp) RT_CK_FULL_PATH(pathp); if (combp) RT_CHECK_COMB(combp); /* Ignore "air" regions unless wanted */ if (tsp->ts_rtip->useair == 0 && tsp->ts_aircode != 0) { tsp->ts_rtip->rti_air_discards++; return -1; /* drop this region */ } } return 0; }
/** * This routine will be called by db_walk_tree() once all the solids * in this region have been visited. * * This routine must be prepared to run in parallel. As a result, * note that the details of the solids pointed to by the soltab * pointers in the tree may not be filled in when this routine is * called (due to the way multiple instances of solids are handled). * Therefore, everything which referred to the tree has been moved out * into the serial section. (_rt_tree_region_assign, rt_bound_tree) */ HIDDEN union tree * _rt_gettree_region_end(struct db_tree_state *tsp, const struct db_full_path *pathp, union tree *curtree, void *client_data) { struct region *rp; struct directory *dp = NULL; size_t shader_len=0; struct rt_i *rtip; Tcl_HashTable *tbl = (Tcl_HashTable *)client_data; Tcl_HashEntry *entry; matp_t inv_mat; struct bu_attribute_value_set avs; struct bu_attribute_value_pair *avpp; RT_CK_DBI(tsp->ts_dbip); RT_CK_FULL_PATH(pathp); RT_CK_TREE(curtree); rtip = tsp->ts_rtip; RT_CK_RTI(rtip); RT_CK_RESOURCE(tsp->ts_resp); if (curtree->tr_op == OP_NOP) { /* Ignore empty regions */ return curtree; } BU_ALLOC(rp, struct region); rp->l.magic = RT_REGION_MAGIC; rp->reg_regionid = tsp->ts_regionid; rp->reg_is_fastgen = tsp->ts_is_fastgen; rp->reg_aircode = tsp->ts_aircode; rp->reg_gmater = tsp->ts_gmater; rp->reg_los = tsp->ts_los; dp = (struct directory *)DB_FULL_PATH_CUR_DIR(pathp); if (!dp) return TREE_NULL; bu_avs_init_empty(&avs); if (db5_get_attributes(tsp->ts_dbip, &avs, dp) == 0) { /* copy avs */ bu_avs_init_empty(&(rp->attr_values)); for (BU_AVS_FOR(avpp, &(tsp->ts_attrs))) { bu_avs_add(&(rp->attr_values), avpp->name, bu_avs_get(&avs, avpp->name)); } }
/* * Each word in the command buffer is the name of a treetop. */ void ph_gettrees(struct pkg_conn *UNUSED(pc), char *buf) { size_t n = 0; long max_argc = 0; char **argv = NULL; int argc = 0; struct rt_i *rtip = APP.a_rt_i; RT_CK_RTI(rtip); if (debug) fprintf(stderr, "ph_gettrees: %s\n", buf); /* Copy values from command line options into rtip */ rtip->useair = use_air; if (rt_dist_tol > 0) { rtip->rti_tol.dist = rt_dist_tol; rtip->rti_tol.dist_sq = rt_dist_tol * rt_dist_tol; } if (rt_perp_tol > 0) { rtip->rti_tol.perp = rt_perp_tol; rtip->rti_tol.para = 1 - rt_perp_tol; } for (n = 0; n < strlen(buf); n++) { if (isspace((int)buf[n])) max_argc++; } argv = (char **)bu_calloc(max_argc+1, sizeof(char *), "alloc argv"); if ((argc = bu_argv_from_string(argv, max_argc, buf)) <= 0) { /* No words in input */ (void)free(buf); bu_free(argv, "free argv"); return; } title_obj = bu_strdup(argv[0]); if (rtip->needprep == 0) { /* First clean up after the end of the previous frame */ if (debug)bu_log("Cleaning previous model\n"); view_end(&APP); view_cleanup(rtip); rt_clean(rtip); if (rdebug&RDEBUG_RTMEM_END) bu_prmem("After rt_clean"); } /* Load the desired portion of the model */ if (rt_gettrees(rtip, argc, (const char **)argv, npsw) < 0) fprintf(stderr, "rt_gettrees(%s) FAILED\n", argv[0]); bu_free(argv, "free argv"); /* In case it changed from startup time via an OPT command */ if (npsw > 1) { RTG.rtg_parallel = 1; } else RTG.rtg_parallel = 0; seen_gettrees = 1; (void)free(buf); prepare(); /* Acknowledge that we are ready */ if (pkg_send(MSG_GETTREES_REPLY, title_obj, strlen(title_obj)+1, pcsrv) < 0) fprintf(stderr, "MSG_START error\n"); }
/** * Given a pointer to a GED database record, and a transformation * matrix, determine if this is a valid REVOLVE, and if so, precompute * various terms of the formula. * * Returns - * 0 REVOLVE is OK * !0 Error in description * * Implicit return - * A struct revolve_specific is created, and its address is stored * in stp->st_specific for use by revolve_shot(). */ int rt_revolve_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip) { struct rt_revolve_internal *rip; struct revolve_specific *rev; vect_t xEnd, yEnd; int *endcount = NULL; size_t nseg, i, j, k; if (rtip) RT_CK_RTI(rtip); RT_CK_DB_INTERNAL(ip); rip = (struct rt_revolve_internal *)ip->idb_ptr; RT_REVOLVE_CK_MAGIC(rip); /* if there's no sketch, there's nothing to do */ if (!rip->skt || rip->skt->vert_count < 1) { return -1; } stp->st_id = ID_REVOLVE; stp->st_meth = &OBJ[ID_REVOLVE]; BU_GET(rev, struct revolve_specific); stp->st_specific = (void *)rev; VMOVE(rev->v3d, rip->v3d); VMOVE(rev->zUnit, rip->axis3d); VMOVE(rev->xUnit, rip->r); VCROSS(rev->yUnit, rev->zUnit, rev->xUnit); VUNITIZE(rev->xUnit); VUNITIZE(rev->yUnit); VUNITIZE(rev->zUnit); rev->ang = rip->ang; rev->sketch_name = bu_vls_addr(&rip->sketch_name); rev->skt = rip->skt; /* calculate end plane */ VSCALE(xEnd, rev->xUnit, cos(rev->ang)); VSCALE(yEnd, rev->yUnit, sin(rev->ang)); VADD2(rev->rEnd, xEnd, yEnd); VUNITIZE(rev->rEnd); /* check the sketch - degree & closed/open */ /* count the number of times an endpoint is used: * if even, the point is ok * if odd, the point is at the end of a path */ endcount = (int *)bu_calloc(rev->skt->vert_count, sizeof(int), "endcount"); nseg = rev->skt->curve.count; for (i=0; i<nseg; i++) { uint32_t *lng; struct line_seg *lsg; struct carc_seg *csg; struct nurb_seg *nsg; struct bezier_seg *bsg; lng = (uint32_t *)rev->skt->curve.segment[i]; switch (*lng) { case CURVE_LSEG_MAGIC: lsg = (struct line_seg *)lng; endcount[lsg->start]++; endcount[lsg->end]++; break; case CURVE_CARC_MAGIC: csg = (struct carc_seg *)lng; if (csg->radius <= 0.0) break; endcount[csg->start]++; endcount[csg->end]++; break; case CURVE_BEZIER_MAGIC: bsg = (struct bezier_seg *)lng; endcount[bsg->ctl_points[0]]++; endcount[bsg->ctl_points[bsg->degree]]++; break; case CURVE_NURB_MAGIC: nsg = (struct nurb_seg *)lng; endcount[nsg->ctl_points[0]]++; endcount[nsg->ctl_points[nsg->c_size-1]]++; break; default: bu_log("rt_revolve_prep: ERROR: unrecognized segment type!\n"); break; } } /* convert endcounts to store which endpoints are odd */ for (i=0, j=0; i<rip->skt->vert_count; i++) { if (endcount[i] % 2 != 0) { /* add 'i' to list, insertion sort by vert[i][Y] */ for (k=j; k>0; k--) { if ((ZERO(rip->skt->verts[i][Y] - rip->skt->verts[endcount[k-1]][Y]) && rip->skt->verts[i][X] > rip->skt->verts[endcount[k-1]][X]) || (!ZERO(rip->skt->verts[i][Y] - rip->skt->verts[endcount[k-1]][Y]) && rip->skt->verts[i][Y] < rip->skt->verts[endcount[k-1]][Y])) { endcount[k] = endcount[k-1]; } else { break; } } endcount[k] = i; j++; } } while (j < rev->skt->vert_count) endcount[j++] = -1; rev->ends = endcount; /* bounding volume */ rt_sketch_bounds(rev->skt, rev->bounds); if (endcount[0] != -1 && rev->bounds[0] > 0) rev->bounds[0] = 0; VJOIN1(stp->st_center, rev->v3d, 0.5*(rev->bounds[2]+rev->bounds[3]), rev->zUnit); stp->st_aradius = sqrt(0.25*(rev->bounds[3]-rev->bounds[2])*(rev->bounds[3]-rev->bounds[2]) + FMAX(rev->bounds[0]*rev->bounds[0], rev->bounds[1]*rev->bounds[1])); stp->st_bradius = stp->st_aradius; /* cheat, make bounding RPP by enclosing bounding sphere (copied from g_ehy.c) */ stp->st_min[X] = stp->st_center[X] - stp->st_bradius; stp->st_max[X] = stp->st_center[X] + stp->st_bradius; stp->st_min[Y] = stp->st_center[Y] - stp->st_bradius; stp->st_max[Y] = stp->st_center[Y] + stp->st_bradius; stp->st_min[Z] = stp->st_center[Z] - stp->st_bradius; stp->st_max[Z] = stp->st_center[Z] + stp->st_bradius; return 0; /* OK */ }
/* * S T K _ S E T U P * * Returns 0 on failure, 1 on success. */ HIDDEN int sh_stk_setup(register struct region *rp, struct bu_vls *matparm, char **dpp, struct mfuncs *mf_p, struct rt_i *rtip, struct mfuncs **headp) /* parameter string */ /* pointer to user data pointer */ { register struct stk_specific *sp; char *cp, *start; int i; int inputs = 0; struct mfuncs *mfp; BU_CK_VLS( matparm ); RT_CK_RTI(rtip); BU_GETSTRUCT( sp, stk_specific ); *dpp = (char *)sp; /*bu_struct_parse( matparm, sh_stk_parse, (char *)sp );*/ if (rdebug&RDEBUG_MATERIAL || rdebug&RDEBUG_SHADE) bu_log( "sh_stk_setup called with \"%s\"\n", bu_vls_addr(matparm) ); i = 0; start = cp = bu_vls_addr(matparm); while ( *cp != '\0' ) { if (*cp == ';' ) { *cp = '\0'; if (i >= 16 ) { bu_log( "sh_stk_setup: max levels exceeded\n" ); return( 0 ); } /* add one */ if (sh_stk_dosetup(start, rp, &sp->udata[i], (char **)&sp->mfuncs[i], rtip, headp) == 0 ) { inputs |= sp->mfuncs[i]->mf_inputs; i++; } else { /* XXX else clear entry? */ bu_log("Problem in stack shader setup\n"); } start = ++cp; } else { cp++; } } if (start != cp ) { if (i >= 16 ) { bu_log( "sh_stk_setup: max levels exceeded\n" ); return( 0 ); } /* add one */ if (sh_stk_dosetup(start, rp, &sp->udata[i], (char **)&sp->mfuncs[i], rtip, headp ) == 0 ) { inputs |= sp->mfuncs[i]->mf_inputs; i++; } else { /* XXX else clear entry? */ } } /* Request only those input bits needed by subordinate shaders */ BU_GETSTRUCT( mfp, mfuncs ); memcpy((char *)mfp, (char *)rp->reg_mfuncs, sizeof(*mfp)); mfp->mf_inputs = inputs; rp->reg_mfuncs = (genptr_t)mfp; return( 1 ); }
/* * S T K _ D O S E T U P */ static int sh_stk_dosetup(char *cp, struct region *rp, char **dpp, char **mpp, struct rt_i *rtip, struct mfuncs **headp) /* udata pointer address */ /* mfuncs pointer address */ { register struct mfuncs *mfp; #ifdef HAVE_DLOPEN register struct mfuncs *mfp_new; #endif struct bu_vls arg; char matname[32]; int ret; int i; RT_CK_RTI(rtip); if (rdebug&RDEBUG_MATERIAL) bu_log( "...starting \"%s\"\n", cp ); /* skip leading white space */ while ( *cp == ' ' || *cp == '\t' ) cp++; for ( i = 0; i < 31 && *cp != '\0'; i++, cp++ ) { if (*cp == ' ' || *cp == '\t' ) { matname[i++] = '\0'; break; } else matname[i] = *cp; } matname[i] = '\0'; /* ensure null termination */ #ifdef HAVE_DLOPEN retry: #endif for ( mfp = *headp; mfp != MF_NULL; mfp = mfp->mf_forw ) { if (matname[0] != mfp->mf_name[0] || strcmp( matname, mfp->mf_name ) != 0 ) continue; goto found; } #ifdef HAVE_DLOPEN /* If we get here, then the shader wasn't found in the list of * compiled-in (or previously loaded) shaders. See if we can * dynamically load it. */ bu_log("Shader \"%s\"... ", matname); if ((mfp_new = load_dynamic_shader(matname, strlen(matname)))) { mlib_add_shader(headp, mfp_new); goto retry; } #else bu_log("****** dynamic shader loading not available ******\n"); #endif bu_log("stack_setup(%s): material not known\n", matname ); ret = -1; goto out; found: *mpp = (char *)mfp; *dpp = (char *)0; bu_vls_init( &arg ); if (*cp != '\0' ) bu_vls_strcat( &arg, ++cp ); if (rdebug&RDEBUG_MATERIAL) bu_log("calling %s with %s\n", mfp->mf_name, bu_vls_addr(&arg)); if (mfp->mf_setup( rp, &arg, dpp, mfp, rtip, headp ) < 0 ) { /* Setup has failed */ bu_vls_free( &arg ); ret = -1; /* BAD */ goto out; } bu_vls_free( &arg ); ret = 0; /* OK */ out: if (rdebug&RDEBUG_MATERIAL) bu_log( "...finished \"%s\", ret=%d\n", matname, ret ); return ret; }
HIDDEN int sh_stk_dosetup(char *cp, struct region *rp, void **dpp, struct mfuncs **mpp, struct rt_i *rtip) /* udata pointer address */ /* mfuncs pointer address */ { register struct mfuncs *mfp; register struct mfuncs *mfp_new; struct bu_vls arg = BU_VLS_INIT_ZERO; char matname[32]; int ret; int i; struct mfuncs *shaders = MF_NULL; RT_CK_RTI(rtip); if (rdebug&RDEBUG_MATERIAL) bu_log("...starting \"%s\"\n", cp); /* skip leading white space */ while (*cp == ' ' || *cp == '\t') cp++; for (i = 0; i < 31 && *cp != '\0'; i++, cp++) { if (*cp == ' ' || *cp == '\t') { matname[i++] = '\0'; break; } else matname[i] = *cp; } matname[i] = '\0'; /* ensure null termination */ retry: /* get list of available shaders */ if (!shaders) { optical_shader_init(&shaders); } for (mfp = shaders; mfp && mfp->mf_name != NULL; mfp = mfp->mf_forw) { if (matname[0] != mfp->mf_name[0] || !BU_STR_EQUAL(matname, mfp->mf_name)) continue; goto found; } /* If we get here, then the shader wasn't found in the list of * compiled-in (or previously loaded) shaders. See if we can * dynamically load it. */ bu_log("Shader \"%s\"... ", matname); if ((mfp_new = load_dynamic_shader(matname))) { mlib_add_shader(&shaders, mfp_new); goto retry; } bu_log("stack_setup(%s): material not known\n", matname); ret = -1; goto out; found: *mpp = mfp; *dpp = (char *)0; if (*cp != '\0') bu_vls_strcat(&arg, ++cp); if (rdebug&RDEBUG_MATERIAL) bu_log("calling %s with %s\n", mfp->mf_name, bu_vls_addr(&arg)); if (!mfp || !mfp->mf_setup || mfp->mf_setup(rp, &arg, dpp, mfp, rtip) < 0) { /* Setup has failed */ bu_vls_free(&arg); ret = -1; /* BAD */ goto out; } bu_vls_free(&arg); ret = 0; /* OK */ out: if (rdebug&RDEBUG_MATERIAL) bu_log("...finished \"%s\", ret=%d\n", matname, ret); return ret; }
/** * R T _ F I N D _ I D E N T I C A L _ S O L I D * * See if solid "dp" as transformed by "mat" already exists in the * soltab list. If it does, return the matching stp, otherwise, * create a new soltab structure, enrole it in the list, and return a * pointer to that. * * "mat" will be a null pointer when an identity matrix is signified. * This greatly speeds the comparison process. * * The two cases can be distinguished by the fact that stp->st_id will * be 0 for a new soltab structure, and non-zero for an existing one. * * This routine will run in parallel. * * In order to avoid a race between searching the soltab list and * adding new solids to it, the new solid to be added *must* be * enrolled in the list before exiting the critical section. * * To limit the size of the list to be searched, there are many lists. * The selection of which list is determined by the hash value * computed from the solid's name. This is the same optimization used * in searching the directory lists. * * This subroutine is the critical bottleneck in parallel tree walking. * * It is safe, and much faster, to use several different critical * sections when searching different lists. * * There are only 4 dedicated semaphores defined, TREE0 through TREE3. * This unfortunately limits the code to having only 4 CPUs doing list * searching at any one time. Hopefully, this is enough parallelism * to keep the rest of the CPUs doing I/O and actual solid prepping. * * Since the algorithm has been reduced from an O((nsolid/128)**2) * search on the entire rti_solidheads[hash] list to an O(ninstance) * search on the dp->d_use_head list for this one solid, the critical * section should be relatively short-lived. Having the 3-way split * should provide ample opportunity for parallelism through here, * while still ensuring that the necessary variables are protected. * * There are two critical variables which *both* need to be protected: * the specific rti_solidhead[hash] list head, and the specific * dp->d_use_hd list head. Fortunately, since the selection of * critical section is based upon db_dirhash(dp->d_namep), any other * processor that wants to search this same 'dp' will get the same * hash as the current thread, and will thus wait for the appropriate * semaphore to be released. Similarly, any other thread that wants * to search the same rti_solidhead[hash] list as the current thread * will be using the same hash, and will thus wait for the proper * semaphore. */ HIDDEN struct soltab *rt_find_identical_solid(register const matp_t mat, register struct directory *dp, struct rt_i *rtip) { register struct soltab *stp = RT_SOLTAB_NULL; int hash; RT_CK_DIR(dp); RT_CK_RTI(rtip); hash = db_dirhash( dp->d_namep ); /* Enter the appropriate dual critical-section */ ACQUIRE_SEMAPHORE_TREE(hash); /* * If solid has not been referenced yet, the search can be * skipped. If solid is being referenced a _lot_, it certainly * isn't all going to be in the same place, so don't bother * searching. Consider the case of a million instances of the * same tree submodel solid. */ if ( dp->d_uses > 0 && dp->d_uses < 100 && rtip->rti_dont_instance == 0 ) { struct bu_list *mid; /* Search dp->d_use_hd list for other instances */ for ( BU_LIST_FOR( mid, bu_list, &dp->d_use_hd ) ) { stp = BU_LIST_MAIN_PTR( soltab, mid, l2 ); RT_CK_SOLTAB(stp); if ( stp->st_matp == (matp_t)0 ) { if ( mat == (matp_t)0 ) { /* Both have identity matrix */ goto more_checks; } continue; } if ( mat == (matp_t)0 ) continue; /* doesn't match */ if ( !bn_mat_is_equal(mat, stp->st_matp, &rtip->rti_tol)) continue; more_checks: /* Don't instance this solid from some other model * instance. As this is nearly always equal, check it * last */ if ( stp->st_rtip != rtip ) continue; /* * stp now points to re-referenced solid. stp->st_id is * non-zero, indicating pre-existing solid. */ RT_CK_SOLTAB(stp); /* sanity */ /* Only increment use counter for non-dead solids. */ if ( !(stp->st_aradius <= -1) ) stp->st_uses++; /* dp->d_uses is NOT incremented, because number of * soltab's using it has not gone up. */ if ( RT_G_DEBUG & DEBUG_SOLIDS ) { bu_log( mat ? "rt_find_identical_solid: %s re-referenced %d\n" : "rt_find_identical_solid: %s re-referenced %d (identity mat)\n", dp->d_namep, stp->st_uses ); } /* Leave the appropriate dual critical-section */ RELEASE_SEMAPHORE_TREE(hash); return stp; } } /* * Create and link a new solid into the list. * * Ensure the search keys "dp", "st_mat" and "st_rtip" are stored * now, while still inside the critical section, because they are * searched on, above. */ BU_GETSTRUCT(stp, soltab); stp->l.magic = RT_SOLTAB_MAGIC; stp->l2.magic = RT_SOLTAB2_MAGIC; stp->st_rtip = rtip; stp->st_dp = dp; dp->d_uses++; stp->st_uses = 1; /* stp->st_id is intentionally left zero here, as a flag */ if ( mat ) { stp->st_matp = (matp_t)bu_malloc( sizeof(mat_t), "st_matp" ); MAT_COPY( stp->st_matp, mat ); } else { stp->st_matp = (matp_t)0; } /* Add to the appropriate soltab list head */ /* PARALLEL NOTE: Uses critical section on rt_solidheads element */ BU_LIST_INSERT( &(rtip->rti_solidheads[hash]), &(stp->l) ); /* Also add to the directory structure list head */ /* PARALLEL NOTE: Uses critical section on this 'dp' */ BU_LIST_INSERT( &dp->d_use_hd, &(stp->l2) ); /* * Leave the 4-way critical-section protecting dp and [hash] */ RELEASE_SEMAPHORE_TREE(hash); /* Enter an exclusive critical section to protect nsolids. * nsolids++ needs to be locked to a SINGLE thread */ bu_semaphore_acquire(RT_SEM_STATS); stp->st_bit = rtip->nsolids++; bu_semaphore_release(RT_SEM_STATS); /* * Fill in the last little bit of the structure in full parallel * mode, outside of any critical section. */ /* Init tables of regions using this solid. Usually small. */ bu_ptbl_init( &stp->st_regions, 7, "st_regions ptbl" ); return stp; }
/** * 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); }
/** * R E C _ P R E P * * Given a pointer to a GED database record, and a transformation matrix, * determine if this is a valid REC, * and if so, precompute various terms of the formulas. * * Returns - * 0 REC is OK * !0 Error in description * * Implicit return - A struct rec_specific is created, and its * address is stored in stp->st_specific for use by rt_rec_shot(). If * the TGC is really an REC, stp->st_id is modified to ID_REC. */ int rt_rec_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip) { struct rt_tgc_internal *tip; struct rec_specific *rec; double magsq_h, magsq_a, magsq_b; double mag_h, mag_a, mag_b; mat_t R; mat_t Rinv; mat_t S; vect_t invsq; /* [ 1/(|A|**2), 1/(|B|**2), 1/(|Hv|**2) ] */ vect_t work; fastf_t f; if (!stp || !ip) return -1; RT_CK_SOLTAB(stp); RT_CK_DB_INTERNAL(ip); if (rtip) RT_CK_RTI(rtip); tip = (struct rt_tgc_internal *)ip->idb_ptr; RT_TGC_CK_MAGIC(tip); /* Validate that |H| > 0, compute |A| |B| |C| |D| */ mag_h = sqrt(magsq_h = MAGSQ(tip->h)); mag_a = sqrt(magsq_a = MAGSQ(tip->a)); mag_b = sqrt(magsq_b = MAGSQ(tip->b)); /* Check for |H| > 0, |A| > 0, |B| > 0 */ if (NEAR_ZERO(mag_h, RT_LEN_TOL) || NEAR_ZERO(mag_a, RT_LEN_TOL) || NEAR_ZERO(mag_b, RT_LEN_TOL)) { return 1; /* BAD, too small */ } /* Make sure that A == C, B == D */ VSUB2(work, tip->a, tip->c); f = MAGNITUDE(work); if (! NEAR_ZERO(f, RT_LEN_TOL)) { return 1; /* BAD, !cylinder */ } VSUB2(work, tip->b, tip->d); f = MAGNITUDE(work); if (! NEAR_ZERO(f, RT_LEN_TOL)) { return 1; /* BAD, !cylinder */ } /* Check for A.B == 0, H.A == 0 and H.B == 0 */ f = VDOT(tip->a, tip->b) / (mag_a * mag_b); if (! NEAR_ZERO(f, RT_DOT_TOL)) { return 1; /* BAD */ } f = VDOT(tip->h, tip->a) / (mag_h * mag_a); if (! NEAR_ZERO(f, RT_DOT_TOL)) { return 1; /* BAD */ } f = VDOT(tip->h, tip->b) / (mag_h * mag_b); if (! NEAR_ZERO(f, RT_DOT_TOL)) { return 1; /* BAD */ } /* * This TGC is really an REC */ stp->st_id = ID_REC; /* "fix" soltab ID */ stp->st_meth = &rt_functab[ID_REC]; BU_GET(rec, struct rec_specific); stp->st_specific = (genptr_t)rec; VMOVE(rec->rec_Hunit, tip->h); VUNITIZE(rec->rec_Hunit); VMOVE(rec->rec_V, tip->v); VMOVE(rec->rec_A, tip->a); VMOVE(rec->rec_B, tip->b); rec->rec_iAsq = 1.0/magsq_a; rec->rec_iBsq = 1.0/magsq_b; VSET(invsq, 1.0/magsq_a, 1.0/magsq_b, 1.0/magsq_h); /* Compute R and Rinv matrices */ MAT_IDN(R); f = 1.0/mag_a; VSCALE(&R[0], tip->a, f); f = 1.0/mag_b; VSCALE(&R[4], tip->b, f); f = 1.0/mag_h; VSCALE(&R[8], tip->h, f); bn_mat_trn(Rinv, R); /* inv of rot mat is trn */ /* Compute S */ MAT_IDN(S); S[ 0] = sqrt(invsq[0]); S[ 5] = sqrt(invsq[1]); S[10] = sqrt(invsq[2]); /* Compute SoR and invRoS */ bn_mat_mul(rec->rec_SoR, S, R); bn_mat_mul(rec->rec_invRoS, Rinv, S); /* Compute bounding sphere and RPP */ { fastf_t dx, dy, dz; /* For bounding sphere */ if (stp->st_meth->ft_bbox(ip, &(stp->st_min), &(stp->st_max), &(rtip->rti_tol))) return 1; VSET(stp->st_center, (stp->st_max[X] + stp->st_min[X])/2, (stp->st_max[Y] + stp->st_min[Y])/2, (stp->st_max[Z] + stp->st_min[Z])/2); dx = (stp->st_max[X] - stp->st_min[X])/2; f = dx; dy = (stp->st_max[Y] - stp->st_min[Y])/2; if (dy > f) f = dy; dz = (stp->st_max[Z] - stp->st_min[Z])/2; if (dz > f) f = dz; stp->st_aradius = f; stp->st_bradius = sqrt(dx*dx + dy*dy + dz*dz); } return 0; /* OK */ }
/** * R T _ G E T T R E E _ R E G I O N _ E N D * * This routine will be called by db_walk_tree() once all the solids * in this region have been visited. * * This routine must be prepared to run in parallel. As a result, * note that the details of the solids pointed to by the soltab * pointers in the tree may not be filled in when this routine is * called (due to the way multiple instances of solids are handled). * Therefore, everything which referred to the tree has been moved out * into the serial section. (rt_tree_region_assign, rt_bound_tree) */ HIDDEN union tree *rt_gettree_region_end(register struct db_tree_state *tsp, struct db_full_path *pathp, union tree *curtree, genptr_t client_data) { struct region *rp; struct directory *dp; int shader_len=0; struct rt_i *rtip; int i; Tcl_HashTable *tbl = (Tcl_HashTable *)client_data; Tcl_HashEntry *entry; matp_t inv_mat; RT_CK_DBI(tsp->ts_dbip); RT_CK_FULL_PATH(pathp); RT_CK_TREE(curtree); rtip = tsp->ts_rtip; RT_CK_RTI(rtip); RT_CK_RESOURCE(tsp->ts_resp); if ( curtree->tr_op == OP_NOP ) { /* Ignore empty regions */ return curtree; } BU_GETSTRUCT( rp, region ); rp->l.magic = RT_REGION_MAGIC; rp->reg_regionid = tsp->ts_regionid; rp->reg_is_fastgen = tsp->ts_is_fastgen; rp->reg_aircode = tsp->ts_aircode; rp->reg_gmater = tsp->ts_gmater; rp->reg_los = tsp->ts_los; if ( tsp->ts_attrs.count && tsp->ts_attrs.avp ) { rp->attr_values = (struct bu_mro **)bu_calloc( tsp->ts_attrs.count+1, sizeof( struct bu_mro *), "regp->attr_values" ); for ( i=0; i<tsp->ts_attrs.count; i++ ) { rp->attr_values[i] = bu_malloc( sizeof( struct bu_mro ), "rp->attr_values[i]" ); bu_mro_init_with_string( rp->attr_values[i], tsp->ts_attrs.avp[i].value ); } } else { rp->attr_values = (struct bu_mro **)NULL; } rp->reg_mater = tsp->ts_mater; /* struct copy */ if ( tsp->ts_mater.ma_shader ) shader_len = strlen( tsp->ts_mater.ma_shader ); if ( shader_len ) { rp->reg_mater.ma_shader = bu_strdup( tsp->ts_mater.ma_shader ); } else rp->reg_mater.ma_shader = (char *)NULL; rp->reg_name = db_path_to_string( pathp ); dp = (struct directory *)DB_FULL_PATH_CUR_DIR(pathp); if (RT_G_DEBUG&DEBUG_TREEWALK) { bu_log("rt_gettree_region_end() %s\n", rp->reg_name ); rt_pr_tree( curtree, 0 ); } rp->reg_treetop = curtree; rp->reg_all_unions = db_is_tree_all_unions( curtree ); /* Determine material properties */ rp->reg_mfuncs = (char *)0; rp->reg_udata = (char *)0; if ( rp->reg_mater.ma_color_valid == 0 ) rt_region_color_map(rp); /* enter critical section */ bu_semaphore_acquire( RT_SEM_RESULTS ); rp->reg_instnum = dp->d_uses++; /* * Add the region to the linked list of regions. * Positions in the region bit vector are established at this time. */ BU_LIST_INSERT( &(rtip->HeadRegion), &rp->l ); /* Assign bit vector pos. */ rp->reg_bit = rtip->nregions++; /* leave critical section */ bu_semaphore_release( RT_SEM_RESULTS ); if ( tbl && bu_avs_get( &tsp->ts_attrs, "ORCA_Comp" ) ) { int newentry; long int reg_bit = rp->reg_bit; inv_mat = (matp_t)bu_calloc( 16, sizeof( fastf_t ), "inv_mat" ); if ( tsp->ts_mat ) bn_mat_inv( inv_mat, tsp->ts_mat ); else MAT_IDN( inv_mat ); /* enter critical section */ bu_semaphore_acquire( RT_SEM_RESULTS ); entry = Tcl_CreateHashEntry(tbl, (char *)reg_bit, &newentry); Tcl_SetHashValue( entry, (ClientData)inv_mat ); /* leave critical section */ bu_semaphore_release( RT_SEM_RESULTS ); } if ( RT_G_DEBUG & DEBUG_REGIONS ) { bu_log("Add Region %s instnum %d\n", rp->reg_name, rp->reg_instnum); } /* Indicate that we have swiped 'curtree' */ return(TREE_NULL); }