ref: 2a1566a9ccda2ec5a256b16306c04cf8b43c14b5
dir: /stl.c/
#include <u.h> #include <libc.h> #include <bio.h> #include "stl.h" #define min(a,b) ((a)<(b)?(a):(b)) enum { TFACET, TNORMAL, TOUTER, TLOOP, TVERTEX, TENDLOOP, TENDFACET, TENDSOLID, TNUM, TNL, }; typedef struct Line Line; typedef struct Token Token; typedef struct Tokenizer Tokenizer; struct Line { char *file; ulong line; }; struct Token { int t; char *s; double v; }; struct Tokenizer { Biobuf *in; Token tok; Line *ctx; char *line; char *f[10]; int nf; int cur; }; static void error(Line *l, char *fmt, ...) { va_list va; char buf[ERRMAX], *bp; va_start(va, fmt); bp = seprint(buf, buf + sizeof(buf), "line %lud: ", l->line); vseprint(bp, buf + sizeof(buf), fmt, va); va_end(va); werrstr("%s", buf); } static int Bgets(Biobuf *b, u16int *s) { uchar buf[2]; if(Bread(b, buf, 2) != 2){ werrstr("could not get 2 bytes"); return -1; } *s = buf[0] | buf[1] << 8; return 0; } static int Bgetl(Biobuf *b, u32int *l) { uchar buf[4]; if(Bread(b, buf, 4) != 4){ werrstr("could not get 4 bytes"); return -1; } *l = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; return 0; } static int Bgetf(Biobuf *b, float *f) { u32int l; if(Bgetl(b, &l) < 0){ werrstr("Bgetl: %r"); return -1; } *f = *(float*)&l; return 0; } static int bunpack(Biobuf*, char*, ...); static int vbunpack(Biobuf *b, char *fmt, va_list a) { u16int s; u32int l; float f, *v; void *p; for(;;){ switch(*fmt++){ case '\0': return 0; case 's': if(Bgets(b, &s) < 0) goto error; *va_arg(a, ushort*) = s; break; case 'l': if(Bgetl(b, &l) < 0) goto error; *va_arg(a, ulong*) = l; break; case 'f': if(Bgetf(b, &f) < 0) goto error; *va_arg(a, float*) = f; break; case 'v': v = va_arg(a, float*); if(bunpack(b, "fff", v+0, v+1, v+2) < 0) goto error; break; case '[': p = va_arg(a, void*); s = va_arg(a, ushort); if(Bread(b, p, s) != s){ werrstr("Bread: could not read %ud bytes", s); goto error; } break; } } error: return -1; } static int bunpack(Biobuf *b, char *fmt, ...) { va_list a; int n; va_start(a, fmt); n = vbunpack(b, fmt, a); va_end(a); return n; } static char * getline(Biobuf *b) { char *line; if((line = Brdstr(b, '\n', 1)) == nil) return nil; return line; } static int idtoken(char *tok) { static char *tokens[] = { [TFACET] "facet", [TNORMAL] "normal", [TOUTER] "outer", [TLOOP] "loop", [TVERTEX] "vertex", [TENDLOOP] "endloop", [TENDFACET] "endfacet", [TENDSOLID] "endsolid", }; int i; for(i = 0; i < nelem(tokens); i++) if(strcmp(tok, tokens[i]) == 0) return i; return -1; } #define isnum(c) ((c)>='0'&&(c)<='9') #define couldbenum(c) ((c)=='+'||c=='-'||isnum(c)) static int lex(Tokenizer *t) { char *ep; int new; new = t->line == nil; if(t->cur < t->nf){ t->tok.s = t->f[t->cur++]; if(couldbenum(t->tok.s[0])){ t->tok.t = TNUM; t->tok.v = strtod(t->tok.s, &ep); if(*ep != 0){ werrstr("misleading number-like '%s'", t->tok.s); return -1; } }else{ t->tok.t = idtoken(t->tok.s); if(t->tok.t < 0){ werrstr("unknown token '%s'", t->tok.s); return -1; } } }else{ free(t->line); if(!new){ t->line = nil; t->tok.t = TNL; return TNL; } if((t->line = getline(t->in)) == nil){ werrstr("could not read a line"); return -1; } t->ctx->line++; t->nf = tokenize(t->line, t->f, nelem(t->f)); t->cur = 0; t->tok.t = lex(t); } return t->tok.t; } static Stl * parsetxt(Biobuf *bin) { Line ctx; Tokenizer tok; Stltri *tri; Stl *stl; char *line; int i, j; memset(&ctx, 0, sizeof(Line)); memset(&tok, 0, sizeof(Tokenizer)); tok.in = bin; tok.ctx = &ctx; ctx.line = 1; /* header */ if((line = getline(bin)) == nil){ error(&ctx, "getline: %r"); return nil; } stl = mallocz(sizeof(Stl), 1); if(stl == nil){ error(&ctx, "mallocz0: %r"); free(line); return nil; } memmove(stl->hdr, line, min(sizeof(stl->hdr), Blinelen(bin))); free(line); for(;;){ tri = nil; /* facet normal i j k */ if(lex(&tok) != TFACET || lex(&tok) != TNORMAL){ faceerr: error(&ctx, "expected 'facet normal i j k'"); goto error; } tri = mallocz(sizeof(Stltri), 1); if(tri == nil){ werrstr("mallocz: %r"); goto error; } for(i = 0; i < 3; i++){ if(lex(&tok) != TNUM){ error(&ctx, "expected number got '%s'", tok.tok.s); goto error; } tri->n[i] = tok.tok.v; } if(lex(&tok) != TNL) goto faceerr; /* outer loop */ if(lex(&tok) != TOUTER || lex(&tok) != TLOOP || lex(&tok) != TNL){ error(&ctx, "expected 'outer loop'"); goto error; } /* [3]vertex x y z */ for(i = 0; i < 3; i++){ if(lex(&tok) != TVERTEX){ verterr: error(&ctx, "expected 'vertex x y z'"); goto error; } for(j = 0; j < 3; j++){ if(lex(&tok) != TNUM){ error(&ctx, "expected number got '%s'", tok.tok.s); goto error; } tri->v[i][j] = tok.tok.v; } if(lex(&tok) != TNL) goto verterr; } /* endloop */ if(lex(&tok) != TENDLOOP || lex(&tok) != TNL){ error(&ctx, "expected 'endloop'"); goto error; } /* endfacet */ if(lex(&tok) != TENDFACET || lex(&tok) != TNL){ error(&ctx, "expected 'endfacet'"); goto error; } stl->tris = realloc(stl->tris, (stl->ntris+1)*sizeof(*stl->tris)); if(stl->tris == nil){ error(&ctx, "realloc1: %r"); goto error; } stl->tris[stl->ntris++] = tri; /* endsolid? */ if(lex(&tok) != TENDSOLID){ tok.cur--; /* unget */ continue; } while(lex(&tok) != TNL) ; break; } return stl; error: free(tri); freestl(stl); return nil; } static Stl * parsebin(Biobuf *bin, char *magic) { Stltri *tri; Stl *stl; u32int i, ntris; stl = mallocz(sizeof(Stl), 1); if(stl == nil){ werrstr("mallocz0: %r"); return nil; } memmove(stl->hdr, magic, 5); if(bunpack(bin, "[l", stl->hdr+5, sizeof(stl->hdr)-5, &ntris) < 0){ werrstr("hdr bunpack: %r"); goto error; } for(i = 0; i < ntris; i++){ tri = mallocz(sizeof(Stltri), 1); if(tri == nil){ werrstr("mallocz1: %r"); goto error; } if(bunpack(bin, "vvvvs", tri->n, tri->v[0], tri->v[1], tri->v[2], &tri->attrlen) < 0){ free(tri); werrstr("tri bunpack0: %r"); goto error; } tri = realloc(tri, sizeof(Stltri) + tri->attrlen); if(tri == nil){ werrstr("realloc0: %r"); goto error; } if(bunpack(bin, "[", tri->attrs, tri->attrlen) < 0){ free(tri); werrstr("tri bunpack1: %r"); goto error; } stl->tris = realloc(stl->tris, (stl->ntris+1)*sizeof(*stl->tris)); if(stl->tris == nil){ free(tri); werrstr("realloc1: %r"); goto error; } stl->tris[stl->ntris++] = tri; } return stl; error: freestl(stl); return nil; } Stl * readstl(int fd) { Biobuf *bin; Stl *stl; char magic[5+1]; stl = nil; bin = Bfdopen(fd, OREAD); if(bin == nil){ werrstr("Bfdopen: %r"); return nil; } if(Bread(bin, magic, 5) != 5){ werrstr("Bread: could not read 5 bytes"); goto out; } magic[5] = 0; if(strcmp(magic, "solid") == 0){ if((stl = parsetxt(bin)) == nil) werrstr("parsetxt: %r"); }else if((stl = parsebin(bin, magic)) == nil) werrstr("parsebin: %r"); out: Bterm(bin); return stl; } void freestl(Stl *stl) { int i; if(stl == nil) return; for(i = 0; i < stl->ntris; i++) free(stl->tris[i]); free(stl->tris); free(stl); }