BRL-CAD
stl_write.c
Go to the documentation of this file.
1 /* S T L _ W R I T E . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2003-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  *
20  */
21 /** @file stl_write.c
22  *
23  * Program to convert a BRL-CAD model (in a .g file) to an STL file by
24  * calling on the NMG booleans. Based on g-acad.c.
25  *
26  */
27 
28 #include "common.h"
29 
30 /* system headers */
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <ctype.h>
35 #include <math.h>
36 #include <string.h>
37 #include "bnetwork.h"
38 #include "bio.h"
39 
40 /* interface headers */
41 #include "bu/getopt.h"
42 #include "bu/cv.h"
43 #include "vmath.h"
44 #include "nmg.h"
45 #include "rtgeom.h"
46 #include "raytrace.h"
47 #include "../../plugin.h"
48 
49 
50 #define V3ARGSIN(a) (a)[X]/25.4, (a)[Y]/25.4, (a)[Z]/25.4
51 #define VSETIN(a, b) {\
52  (a)[X] = (b)[X]/25.4; \
53  (a)[Y] = (b)[Y]/25.4; \
54  (a)[Z] = (b)[Z]/25.4; \
55 }
56 
57 
58 static char usage[] =
59  "[-bvi8] [-xX lvl] [-a abs_tess_tol] [-r rel_tess_tol] [-n norm_tess_tol] [-D dist_calc_tol] [-o output_file_name.stl | -m directory_name] brlcad_db.g object(s)\n";
60 
61 static void
62 print_usage(const char *progname)
63 {
64  bu_log("Usage: %s %s", progname, usage);
65 }
66 
67 
68 struct stl_conv_data {
69  int verbose;
71  int binary; /* whether to produce binary or ascii */
72  const char *output_file; /* output filename */
73  char *output_directory; /* directory name to hold output files */
74  FILE *fp; /* Output file pointer */
75  int bfd; /* Output binary file descriptor */
76  struct db_i *dbip;
77  struct model *the_model;
78  struct bu_vls file_name; /* file name built from region name */
79  struct rt_tess_tol ttol; /* tessellation tolerance in mm */
80  struct bn_tol tol; /* calculation tolerance */
81  struct db_tree_state tree_state; /* includes tol & model */
82  int inches;
83  size_t tot_polygons;
84 };
85 
86 
87 /* Byte swaps a four byte value */
88 static void
89 lswap(unsigned int *v)
90 {
91  unsigned int r;
92 
93  r = *v;
94  *v = ((r & 0xff) << 24) | ((r & 0xff00) << 8) | ((r & 0xff0000) >> 8)
95  | ((r & 0xff000000) >> 24);
96 }
97 
98 
99 static void
100 nmg_to_stl(struct nmgregion *r, const struct db_full_path *pathp,
101  int UNUSED(region_id), int UNUSED(material_id), float UNUSED(color[3]),
102  void *client_data)
103 {
104  struct model *m;
105  struct shell *s;
106  struct vertex *v;
107  char *region_name;
108  int region_polys = 0;
109  int ret;
110  struct stl_conv_data *conv_data = (struct stl_conv_data *)client_data;
111 
112  NMG_CK_REGION(r);
113  RT_CK_FULL_PATH(pathp);
114 
115  region_name = db_path_to_string(pathp);
116 
117  if (conv_data->output_directory) {
118  char *c;
119 
120  bu_vls_trunc(&conv_data->file_name, 0);
121  bu_vls_strcpy(&conv_data->file_name, conv_data->output_directory);
122  bu_vls_putc(&conv_data->file_name, '/');
123  c = region_name;
124  c++;
125 
126  while (*c != '\0') {
127  if (*c == '/') {
128  bu_vls_putc(&conv_data->file_name, '@');
129  } else if (*c == '.' || isspace((int)*c)) {
130  bu_vls_putc(&conv_data->file_name, '_');
131  } else {
132  bu_vls_putc(&conv_data->file_name, *c);
133  }
134 
135  c++;
136  }
137 
138  bu_vls_strcat(&conv_data->file_name, ".stl");
139 
140  if (!conv_data->binary) {
141  /* Open ASCII output file */
142  if ((conv_data->fp = fopen(bu_vls_addr(&conv_data->file_name),
143  "wb+")) == NULL) {
144  perror("g-stl");
145  bu_log("Cannot open ASCII output file (%s) for writing\n",
146  bu_vls_addr(&conv_data->file_name));
147  conv_data->is_success = 0;
148  return;
149  }
150  } else {
151  char buf[81] = {0}; /* need exactly 80 char for header */
152 
153  /* Open binary output file */
154  if ((conv_data->bfd = open(bu_vls_addr(&conv_data->file_name),
155  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
156  S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
157  perror("g-stl");
158  bu_log("ERROR: Cannot open binary output file (%s) for writing\n",
159  bu_vls_addr(&conv_data->file_name));
160  conv_data->is_success = 0;
161  return;
162  }
163 
164  if (!region_name) {
165  snprintf(buf, 80, "BRL-CAD generated STL FILE (Units=%s)",
166  conv_data->inches ? "inches" : "mm");
167  } else {
168  if (region_name && strlen(region_name) > 0) {
169  snprintf(buf, 80, "BRL-CAD generated STL FILE (Units=%s) %s",
170  conv_data->inches ? "inches" : "mm", region_name);
171  } else {
172  snprintf(buf, 80, "BRL-CAD generated STL FILE (Units=%s) %s",
173  conv_data->inches ? "inches" : "mm", region_name);
174  }
175  }
176 
177  ret = write(conv_data->bfd, &buf, 80);
178 
179  if (ret < 0)
180  perror("write");
181 
182  /* write a place keeper for the number of triangles */
183  memset(buf, 0, 4);
184  ret = write(conv_data->bfd, &buf, 4);
185 
186  if (ret < 0)
187  perror("write");
188  }
189  }
190 
191  m = r->m_p;
192  NMG_CK_MODEL(m);
193 
194  /* Write pertinent info for this region */
195  if (!conv_data->binary)
196  fprintf(conv_data->fp, "solid %s\n", (region_name + 1));
197 
198  /* triangulate model */
199  nmg_triangulate_model(m, &conv_data->tol);
200 
201  /* Check triangles */
202  for (BU_LIST_FOR(s, shell, &r->s_hd)) {
203  struct faceuse *fu;
204 
205  NMG_CK_SHELL(s);
206 
207  for (BU_LIST_FOR(fu, faceuse, &s->fu_hd)) {
208  struct loopuse *lu;
209  vect_t facet_normal;
210 
211  NMG_CK_FACEUSE(fu);
212 
213  if (fu->orientation != OT_SAME)
214  continue;
215 
216  /* Grab the face normal and save it for all the vertex loops */
217  NMG_GET_FU_NORMAL(facet_normal, fu);
218 
219  for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
220  struct edgeuse *eu;
221  int vert_count = 0;
222  float flts[12];
223  float *flt_ptr = NULL;
224  unsigned char vert_buffer[50];
225 
226  NMG_CK_LOOPUSE(lu);
227 
228  if (BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC)
229  continue;
230 
231  memset(vert_buffer, 0, sizeof(vert_buffer));
232 
233  if (!conv_data->binary) {
234  fprintf(conv_data->fp, " facet normal %f %f %f\n",
235  V3ARGS(facet_normal));
236  fprintf(conv_data->fp, " outer loop\n");
237  } else {
238  flt_ptr = flts;
239  VMOVE(flt_ptr, facet_normal);
240  flt_ptr += 3;
241  }
242 
243  /* check vertex numbers for each triangle */
244  for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
245  NMG_CK_EDGEUSE(eu);
246 
247  vert_count++;
248 
249  v = eu->vu_p->v_p;
250  NMG_CK_VERTEX(v);
251 
252  if (!conv_data->binary)
253  fprintf(conv_data->fp, " vertex ");
254 
255  if (conv_data->inches)
256  if (!conv_data->binary) {
257  fprintf(conv_data->fp, "%f %f %f\n", V3ARGSIN(v->vg_p->coord));
258  } else {
259  VSETIN(flt_ptr, v->vg_p->coord);
260  flt_ptr += 3;
261  }
262  else if (!conv_data->binary) {
263  fprintf(conv_data->fp, "%f %f %f\n", V3ARGS(v->vg_p->coord));
264  } else {
265  VMOVE(flt_ptr, v->vg_p->coord);
266  flt_ptr += 3;
267  }
268  }
269 
270  if (vert_count > 3) {
271  bu_free(region_name, "region name");
272  bu_log("lu %p has %d vertices!\n", (void *)lu, vert_count);
273  bu_log("ERROR: LU is not a triangle");
274  conv_data->is_success = 0;
275  return;
276  } else if (vert_count < 3)
277  continue;
278 
279  if (!conv_data->binary) {
280  fprintf(conv_data->fp, " endloop\n");
281  fprintf(conv_data->fp, " endfacet\n");
282  } else {
283  int i;
284 
285  bu_cv_htonf(vert_buffer, (const unsigned char *)flts, 12);
286 
287  for (i = 0; i < 12; i++) {
288  lswap((unsigned int *)&vert_buffer[i * 4]);
289  }
290 
291  ret = write(conv_data->bfd, vert_buffer, 50);
292 
293  if (ret < 0)
294  perror("write");
295  }
296 
297  conv_data->tot_polygons++;
298  region_polys++;
299  }
300  }
301  }
302 
303  if (!conv_data->binary)
304  fprintf(conv_data->fp, "endsolid %s\n", (region_name + 1));
305 
306  if (conv_data->output_directory) {
307  if (conv_data->binary) {
308  unsigned char tot_buffer[4];
309 
310  /* Re-position pointer to 80th byte */
311  lseek(conv_data->bfd, 80, SEEK_SET);
312 
313  /* Write out number of triangles */
314  *(uint32_t *)tot_buffer = htonl((unsigned long)region_polys);
315  lswap((unsigned int *)tot_buffer);
316  ret = write(conv_data->bfd, tot_buffer, 4);
317 
318  if (ret < 0)
319  perror("write");
320 
321  close(conv_data->bfd);
322  } else {
323  fclose(conv_data->fp);
324  }
325  }
326 
327  bu_free(region_name, "region name");
328 }
329 
330 
331 static int
332 gcv_stl_write(const char *path, struct db_i *vdbip,
333  const struct gcv_opts *UNUSED(options))
334 {
335  int ret;
336  int use_mc = 0;
337  int mutex;
338 
339  size_t num_objects = 0;
340  char **object_names = NULL;
341 
342  struct stl_conv_data conv_data;
343  struct gcv_region_end_data gcvwriter = {nmg_to_stl, NULL};
344 
345  gcvwriter.client_data = (void *)&conv_data;
346  conv_data.verbose = 0;
347  conv_data.is_success = 1;
348  conv_data.binary = 0; /* Default output is ASCII */
349  conv_data.output_file = NULL;
350  conv_data.output_directory = NULL;
351  BU_VLS_INIT(&conv_data.file_name);
352  conv_data.inches = 0;
353  conv_data.tot_polygons = 0;
354 
355  conv_data.dbip = vdbip;
356 
357  bu_setlinebuf(stderr);
358 
359  conv_data.tree_state = rt_initial_tree_state; /* struct copy */
360  conv_data.tree_state.ts_tol = &conv_data.tol;
361  conv_data.tree_state.ts_ttol = &conv_data.ttol;
362  conv_data.tree_state.ts_m = &conv_data.the_model;
363 
364  /* Set up tessellation tolerance defaults */
365  conv_data.ttol.magic = RT_TESS_TOL_MAGIC;
366  /* Defaults, updated by command line options. */
367  conv_data.ttol.abs = 0.0;
368  conv_data.ttol.rel = 0.01;
369  conv_data.ttol.norm = 0.0;
370 
371  /* Set up calculation tolerance defaults */
372  /* FIXME: These need to be improved */
373  conv_data.tol.magic = BN_TOL_MAGIC;
374  conv_data.tol.dist = 0.0005;
375  conv_data.tol.dist_sq = conv_data.tol.dist * conv_data.tol.dist;
376  conv_data.tol.perp = 1e-6;
377  conv_data.tol.para = 1 - conv_data.tol.perp;
378 
379  /* make empty NMG model */
380  conv_data.the_model = nmg_mm();
381  BU_LIST_INIT(&RTG.rtg_vlfree); /* for vlist macros */
382 
383  conv_data.output_file = path;
384 
385  mutex = (conv_data.output_file && conv_data.output_directory);
386 
387  if (mutex) {
388  print_usage("g-stl");
389  return 0;
390  }
391 
392  if (!conv_data.output_file && !conv_data.output_directory) {
393  if (conv_data.binary) {
394  bu_log("Can't output binary to stdout\n");
395  return 0;
396  }
397 
398  conv_data.fp = stdout;
399  } else if (conv_data.output_file) {
400  if (!conv_data.binary) {
401  /* Open ASCII output file */
402  if ((conv_data.fp = fopen(conv_data.output_file, "wb+")) == NULL) {
403  perror("g-stl");
404  bu_log("%s: Cannot open ASCII output file (%s) for writing\n", "g-stl",
405  conv_data.output_file);
406  return 0;
407  }
408  } else {
409  /* Open binary output file */
410  if ((conv_data.bfd = open(conv_data.output_file,
411  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
412  S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
413  perror("g-stl");
414  bu_log("%s: Cannot open binary output file (%s) for writing\n",
415  "g-stl", conv_data.output_file);
416  return 0;
417  }
418  }
419  }
420 
421  BN_CK_TOL(conv_data.tree_state.ts_tol);
422  RT_CK_TESS_TOL(conv_data.tree_state.ts_ttol);
423 
424  if (conv_data.verbose) {
425  bu_log("\nTessellation tolerances:\n\tabs = %g mm\n\trel = %g\n\tnorm = %g\n",
426  conv_data.tree_state.ts_ttol->abs, conv_data.tree_state.ts_ttol->rel,
427  conv_data.tree_state.ts_ttol->norm);
428  bu_log("Calculational tolerances:\n\tdist = %g mm perp = %g\n",
429  conv_data.tree_state.ts_tol->dist, conv_data.tree_state.ts_tol->perp);
430  }
431 
432  /* Write out STL header if output file is binary */
433  if (conv_data.binary && conv_data.output_file) {
434  char buf[81]; /* need exactly 80 char for header */
435 
436  memset(buf, 0, sizeof(buf));
437 
438  if (conv_data.inches) {
439  bu_strlcpy(buf, "BRL-CAD generated STL FILE (Units=inches)", sizeof(buf));
440  } else {
441  bu_strlcpy(buf, "BRL-CAD generated STL FILE (Units=mm)", sizeof(buf));
442  }
443 
444  ret = write(conv_data.bfd, &buf, 80);
445 
446  if (ret < 0)
447  perror("write");
448 
449  /* write a place keeper for the number of triangles */
450  memset(buf, 0, 4);
451  ret = write(conv_data.bfd, &buf, 4);
452 
453  if (ret < 0)
454  perror("write");
455  }
456 
457  /* get toplevel objects */
458  {
459  struct directory **results;
460  db_update_nref(conv_data.dbip, &rt_uniresource);
461  num_objects = db_ls(conv_data.dbip, DB_LS_TOPS, NULL, &results);
462  object_names = db_dpv_to_argv(results);
463  bu_free(results, "tops");
464  }
465 
466  /* Walk indicated tree(s). Each region will be output separately */
467  (void) db_walk_tree(conv_data.dbip, num_objects, (const char **)object_names,
468  1,
469  &conv_data.tree_state,
470  0, /* take all regions */
472  use_mc ? NULL : nmg_booltree_leaf_tess,
473  (void *)&gcvwriter);
474 
475  bu_free(object_names, "object_names");
476 
477  bu_log("%zu triangles written\n", conv_data.tot_polygons);
478 
479  if (conv_data.output_file) {
480  if (conv_data.binary) {
481  unsigned char tot_buffer[4];
482 
483  /* Re-position pointer to 80th byte */
484  lseek(conv_data.bfd, 80, SEEK_SET);
485 
486  /* Write out number of triangles */
487  *(uint32_t *)tot_buffer = htonl((unsigned long)conv_data.tot_polygons);
488  lswap((unsigned int *)tot_buffer);
489  ret = write(conv_data.bfd, tot_buffer, 4);
490 
491  if (ret < 0)
492  perror("write");
493 
494  close(conv_data.bfd);
495  } else {
496  fclose(conv_data.fp);
497  }
498  }
499 
500  /* Release dynamic storage */
501  nmg_km(conv_data.the_model);
503 
504  return !!conv_data.is_success;
505 }
506 
507 
508 static const struct gcv_converter converters[] = {
509  {"stl", NULL, gcv_stl_write},
510  {NULL, NULL, NULL}
511 };
512 
513 const struct gcv_plugin_info gcv_plugin_conv_stl_write = {converters};
514 
515 
516 /*
517  * Local Variables:
518  * mode: C
519  * tab-width: 8
520  * indent-tabs-mode: t
521  * c-file-style: "stroustrup"
522  * End:
523  * ex: shiftwidth=4 tabstop=8
524  */
struct bu_vls file_name
Definition: stl_write.c:78
#define BU_LIST_FOR(p, structure, hp)
Definition: list.h:365
Definition: raytrace.h:800
struct model ** ts_m
ptr to ptr to NMG "model"
Definition: raytrace.h:1072
#define NMG_EDGEUSE_MAGIC
Definition: magic.h:120
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
char * progname
Definition: human.c:104
const struct db_tree_state rt_initial_tree_state
Definition: globals.c:90
double dist
>= 0
Definition: tol.h:73
if lu s
Definition: nmg_mod.c:3860
struct bu_list rtg_vlfree
head of bn_vlist freelist
Definition: raytrace.h:1698
union tree * gcv_region_end_mc(struct db_tree_state *tsp, const struct db_full_path *pathp, union tree *curtree, void *client_data)
Definition: region_end_mc.c:38
void bu_vls_strcat(struct bu_vls *vp, const char *s)
Definition: vls.c:368
lu
Definition: nmg_mod.c:3855
struct model * the_model
Definition: stl_write.c:77
void bu_vls_trunc(struct bu_vls *vp, int len)
Definition: vls.c:198
double dist_sq
dist * dist
Definition: tol.h:74
#define BN_TOL_MAGIC
Definition: magic.h:74
void rt_vlist_cleanup(void)
Definition: vlist.c:272
Header file for the BRL-CAD common definitions.
Definition: gcv.c:8
#define V3ARGSIN(a)
Definition: stl_write.c:50
char * output_directory
Definition: stl_write.c:73
NMG_CK_LOOPUSE(lu)
double rel
rel dist tol
Definition: raytrace.h:181
#define SEEK_SET
Definition: db_open.c:52
struct db_tree_state tree_state
Definition: stl_write.c:81
void * memset(void *s, int c, size_t n)
struct resource rt_uniresource
default. Defined in librt/globals.c
Definition: globals.c:41
struct rt_tess_tol ttol
Definition: stl_write.c:79
#define DB_LS_TOPS
Definition: raytrace.h:4657
#define bu_strlcpy(dst, src, size)
Definition: str.h:60
#define V3ARGS(a)
Definition: color.c:56
void lswap(unsigned int *v)
Definition: stl_read.c:402
#define RT_CK_TESS_TOL(_p)
Definition: raytrace.h:184
void nmg_km(struct model *m)
Definition: nmg_mk.c:1634
union tree * nmg_booltree_leaf_tess(struct db_tree_state *tsp, const struct db_full_path *pathp, struct rt_db_internal *ip, void *client_data)
Definition: nmg_bool.c:1175
char * options
Definition: gqa.c:56
struct db_i * dbip
Definition: stl_write.c:76
#define UNUSED(parameter)
Definition: common.h:239
void nmg_triangulate_model(struct model *m, const struct bn_tol *tol)
Definition: nmg_tri.c:3818
union tree * gcv_region_end(struct db_tree_state *tsp, const struct db_full_path *pathp, union tree *curtree, void *client_data)
Definition: region_end.c:54
Support for uniform tolerances.
Definition: tol.h:71
#define BN_CK_TOL(_p)
Definition: tol.h:82
#define BU_LIST_FIRST_MAGIC(hp)
Definition: list.h:416
char * db_path_to_string(const struct db_full_path *pp)
Definition: db_fullpath.c:191
char * bu_vls_addr(const struct bu_vls *vp)
Definition: vls.c:111
#define VSETIN(a, b)
Definition: stl_write.c:51
size_t db_ls(const struct db_i *dbip, int flags, const char *pattern, struct directory ***dpv)
Definition: ls.c:58
#define RT_CK_FULL_PATH(_p)
Definition: db_fullpath.h:59
struct model * nmg_mm(void)
Definition: nmg_mk.c:235
char ** db_dpv_to_argv(struct directory **dpv)
Definition: ls.c:123
double perp
nearly 0
Definition: tol.h:75
uint32_t magic
Definition: raytrace.h:179
#define BU_LIST_INIT(_hp)
Definition: list.h:148
uint32_t magic
Definition: tol.h:72
const struct rt_tess_tol * ts_ttol
Tessellation tolerance.
Definition: raytrace.h:1070
double abs
absolute dist tol
Definition: raytrace.h:180
const struct gcv_plugin_info gcv_plugin_conv_stl_write
Definition: stl_write.c:513
void bu_cv_htonf(unsigned char *out, const unsigned char *in, size_t count)
const char * output_file
Definition: stl_write.c:72
void bu_vls_strcpy(struct bu_vls *vp, const char *s)
Definition: vls.c:310
size_t tot_polygons
Definition: stl_write.c:83
double norm
normal tol
Definition: raytrace.h:182
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
#define RT_TESS_TOL_MAGIC
Definition: magic.h:170
NMG_CK_SHELL(s)
int db_walk_tree(struct db_i *dbip, int argc, const char **argv, int ncpu, const struct db_tree_state *init_state, int(*reg_start_func)(struct db_tree_state *, const struct db_full_path *, const struct rt_comb_internal *, void *client_data), union tree *(*reg_end_func)(struct db_tree_state *, const struct db_full_path *, union tree *, void *client_data), union tree *(*leaf_func)(struct db_tree_state *, const struct db_full_path *, struct rt_db_internal *, void *client_data), void *client_data)
struct bn_tol tol
Definition: stl_write.c:80
Definition: vls.h:56
void bu_setlinebuf(FILE *fp)
Definition: linebuf.c:44
void db_update_nref(struct db_i *dbip, struct resource *resp)
Definition: db_match.c:75
const struct bn_tol * ts_tol
Math tolerance.
Definition: raytrace.h:1071
void bu_vls_putc(struct bu_vls *vp, int c)
Definition: vls.c:666
double para
nearly 1
Definition: tol.h:76
int is_success
Definition: stl_write.c:70
#define BU_VLS_INIT(_vp)
Definition: vls.h:74
FILE * fp
Definition: stl_write.c:74
struct rt_g RTG
Definition: globals.c:39