Пример #1
0
int
get_de_pointers(union tree *tp, struct directory *dp, int de_len,
		int *de_pointers)
{
    RT_CK_TREE(tp);
    RT_CK_DIR(dp);

    switch (tp->tr_op) {
	case OP_UNION:
	case OP_SUBTRACT:
	case OP_INTERSECT:
	    get_de_pointers(tp->tr_b.tb_left, dp, de_len, de_pointers);
	    get_de_pointers(tp->tr_b.tb_right, dp, de_len, de_pointers);
	    break;
	case OP_DB_LEAF: {
	    struct directory *dp_M;

	    dp_M = db_lookup(DBIP, tp->tr_l.tl_name, LOOKUP_NOISY);
	    if (dp_M == RT_DIR_NULL)
		return 1;

	    if (dp_M->d_uses >= 0) {
		bu_log("g-iges: member (%s) in combination (%s) has not been written to iges file\n", dp_M->d_namep, dp->d_namep);
		de_pointers[de_pointer_number++] = 0;
		return 1;
	    }

	    if (tp->tr_l.tl_mat && !bn_mat_is_identity(tp->tr_l.tl_mat)) {
		/* write a solid instance entity for this member
		   with a pointer to the new matrix */

		if (!NEAR_ZERO(tp->tr_l.tl_mat[15] - 1.0, tol.dist)) {
		    /* scale factor is not 1.0, IGES can't handle it.
		       go ahead and write the solid instance anyway,
		       but warn the user twice */
		    bu_log("g-iges WARNING: member (%s) of combination (%s) is scaled, IGES cannot handle this\n", dp_M->d_namep, dp->d_namep);
		    scale_error++;
		}
		de_pointers[de_pointer_number++] = write_solid_instance(-dp_M->d_uses, tp->tr_l.tl_mat, fp_dir, fp_param);
	    } else
		de_pointers[de_pointer_number++] = (-dp_M->d_uses);
	    if (dp_M->d_nref)
		comb_form = 1;
	}
	    break;
	default:
	    bu_log("Unrecognized operator in combination!\n");
	    return 1;
    }
    return 0;
}
Пример #2
0
/**
 * D B _ T R E E _ C O U N T E R
 *
 * Count number of non-identity matrices, number of leaf nodes,
 * number of operator nodes, etc.
 *
 * Returns - maximum depth of stack needed to unpack this tree, if
 * serialized.
 *
 * Notes - We over-estimate the size of the width fields used for
 * holding the matrix subscripts.  The caller is responsible for
 * correcting by saying:
 *
 * tcsp->leafbytes -= tcsp->n_leaf * (8 - db5_enc_len[wid]);
 */
size_t
db_tree_counter(const union tree *tp, struct db_tree_counter_state *tcsp)
{
    size_t ldepth, rdepth;

    RT_CK_TREE(tp);
    DB_CK_TREE_COUNTER_STATE(tcsp);

    switch (tp->tr_op) {
	case OP_DB_LEAF:
	    tcsp->n_leaf++;
	    if (tp->tr_l.tl_mat && !bn_mat_is_identity(tp->tr_l.tl_mat)) tcsp->n_mat++;
	    /* Over-estimate storage requirement for matrix # */
	    tcsp->leafbytes += strlen(tp->tr_l.tl_name) + 1 + 8;
	    return 1;

	case OP_NOT:
	    /* Unary ops */
	    tcsp->n_oper++;
	    tcsp->non_union_seen = 1;
	    return 1 + db_tree_counter(tp->tr_b.tb_left, tcsp);

	case OP_UNION:
	    /* This node is known to be a binary op */
	    tcsp->n_oper++;
	    ldepth = db_tree_counter(tp->tr_b.tb_left, tcsp);
	    rdepth = db_tree_counter(tp->tr_b.tb_right, tcsp);
	    if (ldepth > rdepth) return ldepth;
	    return rdepth;

	case OP_INTERSECT:
	case OP_SUBTRACT:
	case OP_XOR:
	    /* This node is known to be a binary op */
	    tcsp->n_oper++;
	    tcsp->non_union_seen = 1;
	    ldepth = db_tree_counter(tp->tr_b.tb_left, tcsp);
	    rdepth = db_tree_counter(tp->tr_b.tb_right, tcsp);
	    if (ldepth > rdepth) return ldepth;
	    return rdepth;

	default:
	    bu_log("db_tree_counter: bad op %d\n", tp->tr_op);
	    bu_bomb("db_tree_counter\n");
	    /* NOTREACHED */
    }
    /* NOTREACHED */
    return 0;
}
Пример #3
0
static int
test_bn_mat_is_identity(int argc, char *argv[])
{
    mat_t m;
    int expected;

    if (argc != 4) {
	bu_exit(1, "<args> format: M <expected_result> [%s]\n", argv[0]);
    }

    scan_mat_args(argv, 2, &m);
    sscanf(argv[3], "%d", &expected);

    return !(bn_mat_is_identity(m) == expected);
}
Пример #4
0
/**
 * When performing "ev" on a region, consider whether to process the
 * whole subtree recursively.
 *
 * Normally, say "yes" to all regions by returning 0.
 *
 * Check for special case: a region of one solid, which can be
 * directly drawn as polygons without going through NMGs.  If we draw
 * it here, then return -1 to signal caller to ignore further
 * processing of this region.  A hack to view polygonal models
 * (converted from FASTGEN) more rapidly.
 */
static int
draw_nmg_region_start(struct db_tree_state *tsp, const struct db_full_path *pathp, const struct rt_comb_internal *combp, void *client_data)
{
    union tree *tp;
    struct directory *dp;
    struct rt_db_internal intern;
    mat_t xform;
    matp_t matp;
    struct bu_list vhead;
    struct _ged_client_data *dgcdp = (struct _ged_client_data *)client_data;

    if (RT_G_DEBUG&DEBUG_TREEWALK) {
	char *sofar = db_path_to_string(pathp);
	bu_vls_printf(dgcdp->gedp->ged_result_str, "nmg_region_start(%s)\n", sofar);
	bu_free((void *)sofar, "path string");
	rt_pr_tree(combp->tree, 1);
	db_pr_tree_state(tsp);
    }

    RT_CK_DBI(tsp->ts_dbip);
    RT_CK_RESOURCE(tsp->ts_resp);

    BU_LIST_INIT(&vhead);

    RT_CK_COMB(combp);
    tp = combp->tree;
    if (!tp)
	return -1;
    RT_CK_TREE(tp);
    if (tp->tr_l.tl_op != OP_DB_LEAF)
	return 0;	/* proceed as usual */

    /* The subtree is a single node.  It may be a combination, though */

    /* Fetch by name, check to see if it's an easy type */
    dp = db_lookup(tsp->ts_dbip, tp->tr_l.tl_name, LOOKUP_NOISY);
    if (!dp)
	return 0;	/* proceed as usual */

    if (!bn_mat_is_identity(tsp->ts_mat)) {
	if (tp->tr_l.tl_mat) {
	    matp = xform;
	    bn_mat_mul(xform, tsp->ts_mat, tp->tr_l.tl_mat);
	} else {
	    matp = tsp->ts_mat;
	}
    } else {
	if (tp->tr_l.tl_mat) {
	    matp = tp->tr_l.tl_mat;
	} else {
	    matp = (matp_t)NULL;
	}
    }

    if (rt_db_get_internal(&intern, dp, tsp->ts_dbip, matp, &rt_uniresource) < 0)
	return 0;	/* proceed as usual */

    switch (intern.idb_type) {
	case ID_POLY:
	    {
		if (RT_G_DEBUG&DEBUG_TREEWALK) {
		    bu_log("fastpath draw ID_POLY %s\n", dp->d_namep);
		}
		if (dgcdp->draw_wireframes) {
		    (void)rt_pg_plot(&vhead, &intern, tsp->ts_ttol, tsp->ts_tol, NULL);
		} else {
		    (void)rt_pg_plot_poly(&vhead, &intern, tsp->ts_ttol, tsp->ts_tol);
		}
	    }
	    goto out;
	case ID_BOT:
	    {
		if (RT_G_DEBUG&DEBUG_TREEWALK) {
		    bu_log("fastpath draw ID_BOT %s\n", dp->d_namep);
		}
		if (dgcdp->draw_wireframes) {
		    (void)rt_bot_plot(&vhead, &intern, tsp->ts_ttol, tsp->ts_tol, NULL);
		} else {
		    (void)rt_bot_plot_poly(&vhead, &intern, tsp->ts_ttol, tsp->ts_tol);
		}
	    }
	    goto out;
	case ID_BREP:
	    {
		if (RT_G_DEBUG&DEBUG_TREEWALK) {
		    bu_log("fastpath draw ID_BREP %s\n", dp->d_namep);
		}
		if (dgcdp->draw_wireframes) {
		    (void)rt_brep_plot(&vhead, &intern, tsp->ts_ttol, tsp->ts_tol, NULL);
		} else {
		    (void)rt_brep_plot_poly(&vhead, pathp, &intern, tsp->ts_ttol, tsp->ts_tol, NULL);
		}
	    }
	    goto out;
	case ID_COMBINATION:
	default:
	    break;
    }
    rt_db_free_internal(&intern);
    return 0;

out:
    {
	struct db_full_path pp;
	db_full_path_init(&pp);
	db_dup_full_path(&pp, pathp);

	/* Successful fastpath drawing of this solid */
	db_add_node_to_full_path(&pp, dp);
	_ged_drawH_part2(0, &vhead, &pp, tsp, dgcdp);

	db_free_full_path(&pp);
    }

    rt_db_free_internal(&intern);
    dgcdp->fastpath_count++;
    return -1;	/* SKIP THIS REGION */
}
Пример #5
0
static int
put_tree_into_comb(struct ged *gedp, struct rt_comb_internal *comb, struct directory *dp, const char *old_name, const char *new_name, const char *imstr)
{
    int i;
    int done;
    char *line;
    char *ptr;
    char relation;
    char *name;
    struct rt_tree_array *rt_tree_array;
    struct line_list *llp;
    int node_count = 0;
    int tree_index = 0;
    union tree *tp;
    matp_t matrix;
    struct bu_vls vls = BU_VLS_INIT_ZERO;
    char *str;

    if (imstr == (char *)NULL)
	return GED_ERROR;

    BU_LIST_INIT(&HeadLines.l);

    /* duplicate the immutable str (from argv) for strtok style mutation */
    str = bu_strdup(imstr);

    /* break str into lines */
    line = str;
    ptr = strchr(str, '\n');
    if (ptr != NULL)
	*ptr = '\0';

    while (line != (char *)NULL) {
	int n;

	bu_vls_strcpy(&vls, line);

	if ((n = count_nodes(gedp, bu_vls_addr(&vls))) < 0) {
	    bu_vls_free(&vls);
	    bu_list_free(&HeadLines.l);
	    bu_free(str, "dealloc bu_strdup str");
	    return GED_ERROR;
	} else if (n > 0) {
	    BU_ALLOC(llp, struct line_list);
	    BU_LIST_INSERT(&HeadLines.l, &llp->l);
	    llp->line = line;

	    node_count += n;
	} /* else blank line */

	if (ptr != NULL && *(ptr+1) != '\0') {
	    /* leap frog past EOS */
	    line = ptr + 1;

	    ptr = strchr(line, '\n');
	    if (ptr != NULL)
		*ptr = '\0';
	} else {
	    line = NULL;
	}
    }
    bu_vls_free(&vls);

    /* build tree list */
    if (node_count)
	rt_tree_array = (struct rt_tree_array *)bu_calloc(node_count, sizeof(struct rt_tree_array), "tree list");
    else
	rt_tree_array = (struct rt_tree_array *)NULL;

    for (BU_LIST_FOR (llp, line_list, &HeadLines.l)) {
	done = 0;
	ptr = strtok(llp->line, _delims);
	while (!done) {
	    if (!ptr)
		break;

	    /* First non-white is the relation operator */
	    relation = (*ptr);
	    if (relation == '\0')
		break;

	    /* Next must be the member name */
	    ptr = strtok((char *)NULL, _delims);
	    if (ptr == (char *)NULL) {
		bu_list_free(&HeadLines.l);
		if (rt_tree_array)
		    bu_free((char *)rt_tree_array, "red: tree list");
		bu_log("no name specified\n");
		bu_free(str, "dealloc bu_strdup str");
		return GED_ERROR;
	    }
	    name = ptr;

	    /* Eliminate trailing white space from name */
	    i = (int)strlen(ptr);
	    while (isspace((int)name[--i]))
		name[i] = '\0';

	    /* Check for existence of member */
	    if ((db_lookup(gedp->ged_wdbp->dbip, name, LOOKUP_QUIET)) == RT_DIR_NULL)
		bu_log("\tWARNING: ' %s ' does not exist\n", name);

	    /* get matrix */
	    ptr = strtok((char *)NULL, _delims);
	    if (ptr == (char *)NULL) {
		matrix = (matp_t)NULL;
		done = 1;
	    } else if (*ptr == 'u' ||
		       (*ptr == '-' && *(ptr+1) == '\0') ||
		       (*ptr == '+' && *(ptr+1) == '\0')) {
		/* assume another relational operator */
		matrix = (matp_t)NULL;
	    } else {
		int k;

		matrix = (matp_t)bu_calloc(16, sizeof(fastf_t), "red: matrix");
		matrix[0] = atof(ptr);
		for (k = 1; k < 16; k++) {
		    ptr = strtok((char *)NULL, _delims);
		    if (!ptr) {
			bu_log("incomplete matrix for member %s - No changes made\n", name);
			bu_free((char *)matrix, "red: matrix");
			if (rt_tree_array)
			    bu_free((char *)rt_tree_array, "red: tree list");
			bu_list_free(&HeadLines.l);
			bu_free(str, "dealloc bu_strdup str");
			return GED_ERROR;
		    }
		    matrix[k] = atof(ptr);
		}
		if (bn_mat_is_identity(matrix)) {
		    bu_free((char *)matrix, "red: matrix");
		    matrix = (matp_t)NULL;
		}

		ptr = strtok((char *)NULL, _delims);
		if (ptr == (char *)NULL)
		    done = 1;
	    }

	    /* Add it to the combination */
	    switch (relation) {
		case '+':
		    rt_tree_array[tree_index].tl_op = OP_INTERSECT;
		    break;
		case '-':
		    rt_tree_array[tree_index].tl_op = OP_SUBTRACT;
		    break;
		default:
		    if (relation != 'u') {
			bu_log("unrecognized relation (assume UNION)\n");
		    }
		    rt_tree_array[tree_index].tl_op = OP_UNION;
		    break;
	    }

	    BU_ALLOC(tp, union tree);
	    RT_TREE_INIT(tp);
	    rt_tree_array[tree_index].tl_tree = tp;
	    tp->tr_l.tl_op = OP_DB_LEAF;
	    tp->tr_l.tl_name = bu_strdup(name);
	    tp->tr_l.tl_mat = matrix;
	    tree_index++;
	}
    }

    bu_list_free(&HeadLines.l);
    i = make_tree(gedp, comb, dp, node_count, old_name, new_name, rt_tree_array, tree_index);

    bu_free(str, "dealloc bu_strdup str");

    return i;
}
Пример #6
0
/**
 * Convert from "network" doubles to machine specific.
 * Transform
 */
int
rt_arbn_import5(struct rt_db_internal *ip, const struct bu_external *ep, const fastf_t *mat, const struct db_i *dbip)
{
    struct rt_arbn_internal *aip;
    size_t i;
    unsigned long neqn;
    int double_count;
    size_t byte_count;

    /* must be double for import and export */
    double *eqn;

    RT_CK_DB_INTERNAL(ip);
    BU_CK_EXTERNAL(ep);
    if (dbip) RT_CK_DBI(dbip);

    neqn = ntohl(*(uint32_t *)ep->ext_buf);
    double_count = neqn * ELEMENTS_PER_PLANE;
    byte_count = double_count * SIZEOF_NETWORK_DOUBLE;

    BU_ASSERT_LONG(ep->ext_nbytes, ==, 4+ byte_count);

    ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
    ip->idb_type = ID_ARBN;
    ip->idb_meth = &OBJ[ID_ARBN];
    BU_ALLOC(ip->idb_ptr, struct rt_arbn_internal);

    aip = (struct rt_arbn_internal *)ip->idb_ptr;
    aip->magic = RT_ARBN_INTERNAL_MAGIC;
    aip->neqn = neqn;
    if (aip->neqn <= 0) return -1;

    eqn = (double *)bu_malloc(byte_count, "arbn plane eqn[] temp buf");
    bu_cv_ntohd((unsigned char *)eqn, (unsigned char *)ep->ext_buf + ELEMENTS_PER_PLANE, double_count);
    aip->eqn = (plane_t *)bu_malloc(double_count * sizeof(fastf_t), "arbn plane eqn[]");
    for (i = 0; i < aip->neqn; i++) {
	HMOVE(aip->eqn[i], &eqn[i*ELEMENTS_PER_PLANE]);
    }
    bu_free(eqn, "arbn plane eqn[] temp buf");

    /* Transform by the matrix, if we have one that is not the identity */
    if (mat && !bn_mat_is_identity(mat)) {
	for (i = 0; i < aip->neqn; i++) {
	    point_t orig_pt;
	    point_t pt;
	    vect_t norm;
	    fastf_t factor;

	    /* unitize the plane equation first */
	    factor = 1.0 / MAGNITUDE(aip->eqn[i]);
	    VSCALE(aip->eqn[i], aip->eqn[i], factor);
	    aip->eqn[i][W] = aip->eqn[i][W] * factor;

	    /* Pick a point on the original halfspace */
	    VSCALE(orig_pt, aip->eqn[i], aip->eqn[i][W]);

	    /* Transform the point, and the normal */
	    MAT4X3VEC(norm, mat, aip->eqn[i]);
	    MAT4X3PNT(pt, mat, orig_pt);

	    /* Measure new distance from origin to new point */
	    VUNITIZE(norm);
	    VMOVE(aip->eqn[i], norm);
	    aip->eqn[i][W] = VDOT(pt, norm);
	}
    }

    return 0;
}
Пример #7
0
int
rt_comb_import4(
    struct rt_db_internal *ip,
    const struct bu_external *ep,
    const mat_t matrix,		/* NULL if identity */
    const struct db_i *dbip,
    struct resource *resp)
{
    union record *rp;
    struct rt_tree_array *rt_tree_array;
    union tree *tree;
    struct rt_comb_internal *comb;
    size_t j;
    size_t node_count;

    BU_CK_EXTERNAL(ep);
    rp = (union record *)ep->ext_buf;
    if (dbip) RT_CK_DBI(dbip);

    if (rp[0].u_id != ID_COMB) {
	bu_log("rt_comb_import4: Attempt to import a non-combination\n");
	return -1;
    }

    /* Compute how many granules of MEMBER records follow */
    node_count = ep->ext_nbytes/sizeof(union record) - 1;

    if (node_count)
	rt_tree_array = (struct rt_tree_array *)bu_calloc(node_count, sizeof(struct rt_tree_array), "rt_tree_array");
    else
	rt_tree_array = (struct rt_tree_array *)NULL;

    for (j = 0; j < node_count; j++) {
	if (rp[j+1].u_id != ID_MEMB) {
	    bu_free((void *)rt_tree_array, "rt_comb_import4: rt_tree_array");
	    bu_log("rt_comb_import4(): granule in external buffer is not ID_MEMB, id=%d\n", rp[j+1].u_id);
	    return -1;
	}

	switch (rp[j+1].M.m_relation) {
	    case '+':
		rt_tree_array[j].tl_op = OP_INTERSECT;
		break;
	    case '-':
		rt_tree_array[j].tl_op = OP_SUBTRACT;
		break;
	    default:
		bu_log("rt_comb_import4() unknown op=x%x, assuming UNION\n", rp[j+1].M.m_relation);
		/* Fall through */
	    case 'u':
		rt_tree_array[j].tl_op = OP_UNION;
		break;
	}

	/* Build leaf node for in-memory tree */
	{
	    union tree *tp;
	    mat_t diskmat;
	    char namebuf[NAMESIZE+1];

	    RT_GET_TREE(tp, resp);
	    rt_tree_array[j].tl_tree = tp;
	    tp->tr_l.tl_op = OP_DB_LEAF;

	    /* bu_strlcpy not safe here, buffer size mismatch */
	    memset(namebuf, 0, NAMESIZE+1);
	    memcpy(namebuf, rp[j+1].M.m_instname, sizeof(rp[j+1].M.m_instname));

	    tp->tr_l.tl_name = bu_strdup(namebuf);

	    flip_mat_dbmat(diskmat, rp[j+1].M.m_mat, dbip->dbi_version < 0 ? 1 : 0);

	    /* Verify that rotation part is pure rotation */
	    if (fabs(diskmat[0]) > 1 || fabs(diskmat[1]) > 1 ||
		fabs(diskmat[2]) > 1 ||
		fabs(diskmat[4]) > 1 || fabs(diskmat[5]) > 1 ||
		fabs(diskmat[6]) > 1 ||
		fabs(diskmat[8]) > 1 || fabs(diskmat[9]) > 1 ||
		fabs(diskmat[10]) > 1)
	    {
		bu_log("ERROR: %s/%s improper scaling, rotation matrix elements > 1\n",
		       rp[0].c.c_name, namebuf);
	    }

	    /* Verify that perspective isn't used as a modeling transform */
	    if (!ZERO(diskmat[12])
		|| !ZERO(diskmat[13])
		|| !ZERO(diskmat[14]))
	    {
		bu_log("ERROR: %s/%s has perspective transform\n", rp[0].c.c_name, namebuf);
	    }

	    /* See if disk record is identity matrix */
	    if (bn_mat_is_identity(diskmat)) {
		if (matrix == NULL) {
		    tp->tr_l.tl_mat = NULL;	/* identity */
		} else {
		    tp->tr_l.tl_mat = bn_mat_dup(matrix);
		}
	    } else {
		if (matrix == NULL) {
		    tp->tr_l.tl_mat = bn_mat_dup(diskmat);
		} else {
		    mat_t prod;
		    bn_mat_mul(prod, matrix, diskmat);
		    tp->tr_l.tl_mat = bn_mat_dup(prod);
		}
	    }
/* bu_log("M_name=%s, matp=x%x\n", tp->tr_l.tl_name, tp->tr_l.tl_mat); */
	}
    }
    if (node_count)
	tree = db_mkgift_tree(rt_tree_array, node_count, &rt_uniresource);
    else
	tree = (union tree *)NULL;

    RT_DB_INTERNAL_INIT(ip);
    ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
    ip->idb_type = ID_COMBINATION;
    ip->idb_meth = &OBJ[ID_COMBINATION];

    BU_ALLOC(comb, struct rt_comb_internal);
    RT_COMB_INTERNAL_INIT(comb);

    comb->tree = tree;

    ip->idb_ptr = (void *)comb;

    switch (rp[0].c.c_flags) {
	case DBV4_NON_REGION_NULL:
	case DBV4_NON_REGION:
	    comb->region_flag = 0;
	    break;
	case DBV4_REGION:
	    comb->region_flag = 1;
	    comb->is_fastgen = REGION_NON_FASTGEN;
	    break;
	case DBV4_REGION_FASTGEN_PLATE:
	    comb->region_flag = 1;
	    comb->is_fastgen = REGION_FASTGEN_PLATE;
	    break;
	case DBV4_REGION_FASTGEN_VOLUME:
	    comb->region_flag = 1;
	    comb->is_fastgen = REGION_FASTGEN_VOLUME;
	    break;
	default:
	    bu_log("WARNING: combination %s has illegal c_flag=x%x\n",
		   rp[0].c.c_name, rp[0].c.c_flags);
	    break;
    }

    if (comb->region_flag) {
	if (dbip->dbi_version < 0) {
	    comb->region_id = flip_short(rp[0].c.c_regionid);
	    comb->aircode = flip_short(rp[0].c.c_aircode);
	    comb->GIFTmater = flip_short(rp[0].c.c_material);
	    comb->los = flip_short(rp[0].c.c_los);
	} else {
	    comb->region_id = rp[0].c.c_regionid;
	    comb->aircode = rp[0].c.c_aircode;
	    comb->GIFTmater = rp[0].c.c_material;
	    comb->los = rp[0].c.c_los;
	}
    } else {
	/* set some reasonable defaults */
	comb->region_id = 0;
	comb->aircode = 0;
	comb->GIFTmater = 0;
	comb->los = 0;
    }

    comb->rgb_valid = rp[0].c.c_override;
    if (comb->rgb_valid) {
	comb->rgb[0] = rp[0].c.c_rgb[0];
	comb->rgb[1] = rp[0].c.c_rgb[1];
	comb->rgb[2] = rp[0].c.c_rgb[2];
    }
    if (rp[0].c.c_matname[0] != '\0') {
#define MAX_SS 128
	char shader_str[MAX_SS];

	memset(shader_str, 0, MAX_SS);

	/* copy shader info to a static string */

	/* write shader name.  c_matname is a buffer, bu_strlcpy not
	 * safe here.
	 */
	memcpy(shader_str, rp[0].c.c_matname, sizeof(rp[0].c.c_matname));

	bu_strlcat(shader_str, " ", MAX_SS);

	/* write shader parameters.  c_matparm is a buffer, bu_strlcpy
	 * not safe here.
	 */
	memcpy(shader_str+strlen(shader_str), rp[0].c.c_matparm, sizeof(rp[0].c.c_matparm));

	/* convert to TCL format and place into comb->shader */
	if (bu_shader_to_list(shader_str, &comb->shader)) {
	    bu_log("rt_comb_import4: Error: Cannot convert following shader to TCL format:\n");
	    bu_log("\t%s\n", shader_str);
	    bu_vls_free(&comb->shader);
	    return -1;
	}
    }
    /* XXX Separate flags for color inherit, shader inherit, (new) material inherit? */
    /* XXX cf: ma_cinherit, ma_minherit */
    /* This ? is necessary to clean up old databases with grunge here */
    comb->inherit = (rp[0].c.c_inherit == DB_INH_HIGHER) ? 1 : 0;
    /* Automatic material table lookup here? */
    if (comb->region_flag)
	bu_vls_printf(&comb->material, "gift%ld", comb->GIFTmater);

    if (rt_tree_array) bu_free((void *)rt_tree_array, "rt_tree_array");

    return 0;
}