/* * stl_write.c -- streaming STL output for ez3d. * * Binary STL layout: 80-byte header (must not start with "solid"), uint32 * facet count, then 50 bytes per facet (normal + 3 vertices as little-endian * float32, plus a uint16 attribute byte count we leave 0). * * The facet count is not known until every triangle has been emitted, so we * write a zero placeholder after the header and fseek back to patch it in * stl_close(). This keeps memory flat no matter how many facets a * 1000x1000 heightmap produces. */ #include #include #include #include "stl_write.h" int stl_open(stl_writer_t *w, const char *path, int ascii) { memset(w, 0, sizeof *w); w->f = fopen(path, "wb"); w->ascii = ascii; w->path = path; if (!w->f) { fprintf(stderr, "ez3d: cannot open '%s' for writing\n", path); return -1; } /* Big output buffer: a 1024x768 heightmap writes ~80 MB of facets. */ static char iobuf[1 << 20]; setvbuf(w->f, iobuf, _IOFBF, sizeof iobuf); if (ascii) { fputs("solid ez3d\n", w->f); } else { char header[80]; memset(header, 0, sizeof header); snprintf(header, sizeof header, "ez3d binary STL, units: mm assumed by slicer"); fwrite(header, 1, sizeof header, w->f); uint32_t zero = 0; fwrite(&zero, sizeof zero, 1, w->f); w->count_offset = 80; } return 0; } void stl_tri(stl_writer_t *w, const vertex_t *a, const vertex_t *b, const vertex_t *c) { dvec_t norm; tri_normal_vec(a, b, c, &norm); double len = tri_normal_len(&norm); /* Degenerate facets keep a zero normal; slicers recompute normals * anyway. Our mesher avoids emitting them in the first place. */ assert(len > 0); norm.x /= len; norm.y /= len; norm.z /= len; if (w->ascii) { fprintf(w->f, " facet normal %.6g %.6g %.6g\n" " outer loop\n" " vertex %.6g %.6g %.6g\n" " vertex %.6g %.6g %.6g\n" " vertex %.6g %.6g %.6g\n" " endloop\n" " endfacet\n", norm.x, norm.y, norm.z, a->x, a->y, a->z, b->x, b->y, b->z, c->x, c->y, c->z); } else { float rec[12] = { (float)norm.x, (float)norm.y, (float)norm.z, a->x, a->y, a->z, b->x, b->y, b->z, c->x, c->y, c->z }; uint16_t attr = 0; fwrite(rec, sizeof rec, 1, w->f); fwrite(&attr, sizeof attr, 1, w->f); } w->ntri++; } long stl_close(stl_writer_t *w) { int err = 0; if (w->ascii) { fputs("endsolid ez3d\n", w->f); } else { if (fseek(w->f, w->count_offset, SEEK_SET) != 0) err = -1; else fwrite(&w->ntri, sizeof w->ntri, 1, w->f); } if (fclose(w->f) != 0) err = -1; return err ? -1 : (long)w->ntri; }