summaryrefslogtreecommitdiff
path: root/marshal.c
diff options
context:
space:
mode:
Diffstat (limited to 'marshal.c')
-rw-r--r--marshal.c939
1 files changed, 939 insertions, 0 deletions
diff --git a/marshal.c b/marshal.c
new file mode 100644
index 0000000..15e4760
--- /dev/null
+++ b/marshal.c
@@ -0,0 +1,939 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <thread.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+#include "graphics.h"
+#include "internal.h"
+
+enum {
+ NaI = ~0ULL, /* not an index */
+ MTLHTSIZ = 17,
+};
+
+typedef struct Curline Curline;
+struct Curline
+{
+ char file[256];
+ usize line;
+};
+
+typedef struct IArray IArray;
+typedef struct Wirevert Wirevert;
+typedef struct Wireprim Wireprim;
+typedef struct Mtlentry Mtlentry;
+typedef struct Mtltab Mtltab;
+
+struct IArray
+{
+ void *items;
+ usize nitems;
+ usize itemsize;
+};
+
+struct Wirevert
+{
+ usize p, n, t, c;
+};
+
+struct Wireprim
+{
+ int nv;
+ usize v[3];
+ usize T;
+ char *mtlname;
+};
+
+struct Mtlentry
+{
+ Material;
+ ulong idx;
+ Mtlentry *next;
+};
+
+struct Mtltab
+{
+ Mtlentry *mtls[MTLHTSIZ];
+ ulong nmtls;
+ int loaded; /* was the table loaded into a model already? */
+};
+
+static void
+error(Curline *l, char *fmt, ...)
+{
+ va_list va;
+ char buf[ERRMAX], *bp;
+
+ bp = seprint(buf, buf + sizeof buf, "%s:%llud ", l->file, l->line);
+
+ va_start(va, fmt);
+ vseprint(bp, buf + sizeof buf, fmt, va);
+ va_end(va);
+
+ werrstr("%s", buf);
+}
+
+static IArray *
+mkitemarray(usize is)
+{
+ IArray *a;
+
+ a = emalloc(sizeof *a);
+ memset(a, 0, sizeof *a);
+ a->itemsize = is;
+ return a;
+}
+
+static usize
+itemarrayadd(IArray *a, void *i, int dedup)
+{
+ char *p;
+ usize idx;
+
+ if(dedup){
+ p = a->items;
+ for(idx = 0; idx < a->nitems; idx++)
+ if(memcmp(i, &p[idx*a->itemsize], a->itemsize) == 0)
+ return idx;
+ }
+
+ idx = a->nitems;
+ a->items = erealloc(a->items, ++a->nitems * a->itemsize);
+ p = a->items;
+ p += idx*a->itemsize;
+ memmove(p, i, a->itemsize);
+ return idx;
+}
+
+static void *
+itemarrayget(IArray *a, usize idx)
+{
+ char *p;
+
+ if(idx >= a->nitems)
+ return nil;
+
+ p = a->items;
+ p += idx*a->itemsize;
+ return p;
+}
+
+static void
+rmitemarray(IArray *a)
+{
+ free(a->items);
+ free(a);
+}
+
+static uint
+hash(char *s)
+{
+ uint h;
+
+ h = 0x811c9dc5;
+ while(*s != 0)
+ h = (h^(uchar)*s++) * 0x1000193;
+ return h % MTLHTSIZ;
+}
+
+static Mtltab *
+mkmtltab(void)
+{
+ Mtltab *t;
+
+ t = emalloc(sizeof *t);
+ memset(t, 0, sizeof *t);
+ return t;
+}
+
+static void
+freemtlentry(Mtlentry *m)
+{
+ freetexture(m->normalmap);
+ freetexture(m->specularmap);
+ freetexture(m->diffusemap);
+ free(m->name);
+ free(m);
+}
+
+static Mtlentry *
+mtltabadd(Mtltab *t, Material *m)
+{
+ Mtlentry *nm, *mp, *prev;
+ uint h;
+
+ nm = emalloc(sizeof *nm);
+ memset(nm, 0, sizeof *nm);
+ nm->Material = *m;
+ nm->next = nil;
+
+ prev = nil;
+ h = hash(nm->name);
+ for(mp = t->mtls[h]; mp != nil; prev = mp, mp = mp->next)
+ if(strcmp(mp->name, nm->name) == 0){
+ werrstr("material already exists");
+ return nil;
+ }
+ if(prev == nil){
+ t->mtls[h] = nm;
+ t->nmtls++;
+ return nm;
+ }
+ prev->next = nm;
+ t->nmtls++;
+ return nm;
+}
+
+static Mtlentry *
+mtltabget(Mtltab *t, char *name)
+{
+ Mtlentry *m;
+ uint h;
+
+ h = hash(name);
+ for(m = t->mtls[h]; m != nil; m = m->next)
+ if(strcmp(m->name, name) == 0)
+ break;
+ return m;
+}
+
+static void
+mtltabloadmodel(Model *m, Mtltab *t)
+{
+ Mtlentry *e;
+ int i;
+
+ for(i = 0; i < nelem(t->mtls); i++)
+ for(e = t->mtls[i]; e != nil; e = e->next)
+ e->idx = m->addmaterial(m, *e);
+ t->loaded++;
+}
+
+static void
+rmmtltab(Mtltab *t)
+{
+ Mtlentry *m, *nm;
+ int i;
+
+ for(i = 0; i < nelem(t->mtls); i++)
+ for(m = t->mtls[i]; m != nil; m = nm){
+ nm = m->next;
+ if(t->loaded)
+ free(m);
+ else
+ freemtlentry(m);
+ }
+}
+
+/*
+ * TODO if materials are inserted between primitive declarations
+ * references to those materials from early primitives can cause
+ * out-of-bounds accesses. find a solution.
+ *
+ * example:
+ *
+ * mtl A {
+ * diffuse: 1 0 0
+ * }
+ * prim ... A
+ * mtl B {
+ * diffuse: 1 0 0
+ * }
+ * prim ... B
+ *
+ * now the reference to A is probably wrong because of realloc.
+ */
+Model *
+readmodel(int fd)
+{
+ Curline curline;
+ IArray *pa, *na, *ta, *ca, *Ta, *va, *prima;
+ Mtltab *mtltab;
+ Mtlentry *me;
+ Point3 p, n, T;
+ Point2 t;
+ Color c;
+ Vertex v;
+ Primitive prim;
+ Material mtl;
+ Model *m;
+ Memimage *mi;
+ Biobuf *bin;
+ void *vp;
+ char *line, *f[10], *s, assets[200], buf[256];
+ usize idx, i;
+ ulong primidx;
+ int nf, nv, inamaterial, texfd;
+
+ n.w = T.w = 0;
+ t.w = 1;
+ m = nil;
+
+ bin = Bfdopen(fd, OREAD);
+ if(bin == nil)
+ sysfatal("Bfdopen: %r");
+
+ pa = mkitemarray(sizeof(p));
+ na = mkitemarray(sizeof(n));
+ ta = mkitemarray(sizeof(t));
+ ca = mkitemarray(sizeof(c));
+ Ta = mkitemarray(sizeof(T));
+ va = mkitemarray(sizeof(v));
+ prima = mkitemarray(sizeof(prim));
+ mtltab = mkmtltab();
+
+ memset(&curline, 0, sizeof curline);
+ if(fd2path(fd, curline.file, sizeof curline.file) != 0)
+ sysfatal("fd2path: %r");
+ if((s = strrchr(curline.file, '/')) != nil){
+ *s = 0;
+ snprint(assets, sizeof assets, "%s", curline.file);
+ memmove(curline.file, s+1, strlen(s));
+ }else{
+ assets[0] = '.';
+ assets[1] = 0;
+ }
+ inamaterial = 0;
+
+ while((line = Brdline(bin, '\n')) != nil){
+ line[Blinelen(bin)-1] = 0;
+ curline.line++;
+
+ nf = tokenize(line, f, nelem(f));
+ if(nf < 1)
+ continue;
+
+ if(inamaterial){
+ if((s = strchr(f[0], ':')) != nil)
+ *s = 0;
+
+ if(strcmp(f[0], "}") == 0){
+ if(mtltabadd(mtltab, &mtl) == nil){
+ error(&curline, "mtltabadd: %r");
+ goto getout;
+ }
+ inamaterial--;
+ }else if(strcmp(f[0], "ambient") == 0){
+ if(nf != 4 && nf != 5){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ mtl.ambient.r = strtod(f[1], nil);
+ mtl.ambient.g = strtod(f[2], nil);
+ mtl.ambient.b = strtod(f[3], nil);
+ mtl.ambient.a = nf == 5? strtod(f[4], nil): 1;
+ }else if(strcmp(f[0], "diffuse") == 0){
+ if(nf != 4 && nf != 5){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+
+ mtl.diffuse.r = strtod(f[1], nil);
+ mtl.diffuse.g = strtod(f[2], nil);
+ mtl.diffuse.b = strtod(f[3], nil);
+ mtl.diffuse.a = nf == 5? strtod(f[4], nil): 1;
+ }else if(strcmp(f[0], "diffusemap") == 0){
+ if(nf != 2){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ if(mtl.diffusemap != nil){
+ error(&curline, "there is already a diffuse map");
+ goto getout;
+ }
+
+ snprint(buf, sizeof buf, "%s/%s", assets, f[1]);
+ texfd = open(buf, OREAD);
+ if(texfd < 0){
+notexture:
+ error(&curline, "could not read texture '%s'", f[1]);
+ goto getout;
+ }
+ mi = readmemimage(texfd);
+ if(mi == nil){
+ close(texfd);
+ goto notexture;
+ }
+ mtl.diffusemap = alloctexture(sRGBTexture, mi);
+ close(texfd);
+ }else if(strcmp(f[0], "specular") == 0){
+ if(nf != 4 && nf != 5){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+
+ mtl.specular.r = strtod(f[1], nil);
+ mtl.specular.g = strtod(f[2], nil);
+ mtl.specular.b = strtod(f[3], nil);
+ mtl.specular.a = nf == 5? strtod(f[4], nil): 1;
+ }else if(strcmp(f[0], "specularmap") == 0){
+ if(nf != 2){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ if(mtl.specularmap != nil){
+ error(&curline, "there is already a specular map");
+ goto getout;
+ }
+
+ snprint(buf, sizeof buf, "%s/%s", assets, f[1]);
+ texfd = open(buf, OREAD);
+ if(texfd < 0)
+ goto notexture;
+ mi = readmemimage(texfd);
+ if(mi == nil){
+ close(texfd);
+ goto notexture;
+ }
+ mtl.specularmap = alloctexture(RAWTexture, mi);
+ close(texfd);
+ }else if(strcmp(f[0], "shininess") == 0){
+ if(nf != 2){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ mtl.shininess = strtod(f[1], nil);
+ }else if(strcmp(f[0], "normals") == 0){
+ if(nf != 2){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ if(mtl.normalmap != nil){
+ error(&curline, "there is already a normal map");
+ goto getout;
+ }
+
+ snprint(buf, sizeof buf, "%s/%s", assets, f[1]);
+ texfd = open(buf, OREAD);
+ if(texfd < 0)
+ goto notexture;
+ mi = readmemimage(texfd);
+ if(mi == nil){
+ close(texfd);
+ goto notexture;
+ }
+ mtl.normalmap = alloctexture(RAWTexture, mi);
+ close(texfd);
+ }else{
+ error(&curline, "unknown mtl parameter '%s'", f[0]);
+ goto getout;
+ }
+
+ continue;
+ }
+
+ if(strcmp(f[0], "p") == 0){
+ if(nf != 4 && nf != 5){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ p.x = strtod(f[1], nil);
+ p.y = strtod(f[2], nil);
+ p.z = strtod(f[3], nil);
+ p.w = nf == 5? strtod(f[4], nil): 1;
+ itemarrayadd(pa, &p, 0);
+ }else if(strcmp(f[0], "n") == 0){
+ if(nf != 4){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ n.x = strtod(f[1], nil);
+ n.y = strtod(f[2], nil);
+ n.z = strtod(f[3], nil);
+ itemarrayadd(na, &n, 0);
+ }else if(strcmp(f[0], "t") == 0){
+ if(nf != 3){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ t.x = strtod(f[1], nil);
+ t.y = strtod(f[2], nil);
+ itemarrayadd(ta, &t, 0);
+ }else if(strcmp(f[0], "c") == 0){
+ if(nf != 4 && nf != 5){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ c.r = strtod(f[1], nil);
+ c.g = strtod(f[2], nil);
+ c.b = strtod(f[3], nil);
+ c.a = nf == 5? strtod(f[4], nil): 1;
+ itemarrayadd(ca, &c, 0);
+ }else if(strcmp(f[0], "T") == 0){
+ if(nf != 4){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ T.x = strtod(f[1], nil);
+ T.y = strtod(f[2], nil);
+ T.z = strtod(f[3], nil);
+ itemarrayadd(Ta, &T, 0);
+ }else if(strcmp(f[0], "v") == 0){
+ if(nf != 5){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ memset(&v, 0, sizeof v);
+
+ if(strcmp(f[1], "-") == 0){
+ error(&curline, "vertex has no position");
+ goto getout;
+ }
+ idx = strtoul(f[1], nil, 10);
+ vp = itemarrayget(pa, idx);
+ if(vp == nil){
+ error(&curline, "no position at idx %llud", idx);
+ goto getout;
+ }
+ v.p = *(Point3*)vp;
+
+ if(strcmp(f[2], "-") != 0){
+ idx = strtoul(f[2], nil, 10);
+ vp = itemarrayget(na, idx);
+ if(vp == nil){
+ error(&curline, "no normal at idx %llud", idx);
+ goto getout;
+ }
+ v.n = *(Point3*)vp;
+ }
+
+ if(strcmp(f[3], "-") != 0){
+ idx = strtoul(f[3], nil, 10);
+ vp = itemarrayget(ta, idx);
+ if(vp == nil){
+ error(&curline, "no texture at idx %llud", idx);
+ goto getout;
+ }
+ v.uv = *(Point2*)vp;
+ }
+
+ if(strcmp(f[4], "-") != 0){
+ idx = strtoul(f[4], nil, 10);
+ vp = itemarrayget(ca, idx);
+ if(vp == nil){
+ error(&curline, "no color at idx %llud", idx);
+ goto getout;
+ }
+ v.c = *(Color*)vp;
+ }
+
+ itemarrayadd(va, &v, 0);
+ }else if(strcmp(f[0], "prim") == 0){
+ if(nf < 3 || nf > 7){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ memset(&prim, 0, sizeof prim);
+
+ nv = strtoul(f[1], nil, 10);
+ switch(nv-1){
+ case PPoint:
+ prim.type = PPoint;
+
+ idx = strtoul(f[2], nil, 10);
+ vp = itemarrayget(va, idx);
+ if(vp == nil){
+novertex:
+ error(&curline, "no vertex at idx %llud", idx);
+ goto getout;
+ }
+ prim.v[0] = *(Vertex*)vp;
+
+ /* ignore 4th field (nf == 4) */
+
+ if(nf == 5){
+ prim.mtl = mtltabget(mtltab, f[4]);
+ if(prim.mtl == nil){
+ error(&curline, "material '%s' not found", f[4]);
+ goto getout;
+ }
+ }
+ break;
+ case PLine:
+ prim.type = PLine;
+
+ idx = strtoul(f[2], nil, 10);
+ vp = itemarrayget(va, idx);
+ if(vp == nil)
+ goto novertex;
+ prim.v[0] = *(Vertex*)vp;
+
+ if(nf < 4){
+notenough:
+ error(&curline, "not enough prim vertices");
+ goto getout;
+ }
+ idx = strtoul(f[3], nil, 10);
+ vp = itemarrayget(va, idx);
+ if(vp == nil)
+ goto novertex;
+ prim.v[1] = *(Vertex*)vp;
+
+ /* ignore 5th field (nf == 5) */
+
+ if(nf == 6){
+ prim.mtl = mtltabget(mtltab, f[5]);
+ if(prim.mtl == nil){
+ error(&curline, "material '%s' not found", f[5]);
+ goto getout;
+ }
+ }
+ break;
+ case PTriangle:
+ prim.type = PTriangle;
+
+ idx = strtoul(f[2], nil, 10);
+ vp = itemarrayget(va, idx);
+ if(vp == nil)
+ goto novertex;
+ prim.v[0] = *(Vertex*)vp;
+
+ if(nf < 4)
+ goto notenough;
+ idx = strtoul(f[3], nil, 10);
+ vp = itemarrayget(va, idx);
+ if(vp == nil)
+ goto novertex;
+ prim.v[1] = *(Vertex*)vp;
+
+ if(nf < 5)
+ goto notenough;
+ idx = strtoul(f[4], nil, 10);
+ vp = itemarrayget(va, idx);
+ if(vp == nil)
+ goto novertex;
+ prim.v[2] = *(Vertex*)vp;
+
+ if(nf < 6){
+ error(&curline, "missing triangle tangent field");
+ goto getout;
+ }
+ if(strcmp(f[5], "-") != 0){
+ idx = strtoul(f[5], nil, 10);
+ vp = itemarrayget(Ta, idx);
+ if(vp == nil){
+ error(&curline, "no tangent at idx %llud", idx);
+ goto getout;
+ }
+ prim.tangent = *(Point3*)vp;
+ }
+
+ if(nf == 7){
+ prim.mtl = mtltabget(mtltab, f[6]);
+ if(prim.mtl == nil){
+ error(&curline, "material '%s' not found", f[6]);
+ goto getout;
+ }
+ }
+ break;
+ default:
+ error(&curline, "alien primitive detected");
+ goto getout;
+ }
+
+ itemarrayadd(prima, &prim, 0);
+ }else if(strcmp(f[0], "mtl") == 0){
+ if(nf != 3 || strcmp(f[2], "{") != 0){
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ memset(&mtl, 0, sizeof mtl);
+
+ mtl.name = strdup(f[1]);
+ if(mtl.name == nil)
+ sysfatal("strdup: %r");
+ inamaterial++;
+ }else{
+ error(&curline, "syntax error");
+ goto getout;
+ }
+ }
+
+ if(prima->nitems < 1){
+ werrstr("no primitives no model");
+ goto getout;
+ }
+
+ m = newmodel();
+ mtltabloadmodel(m, mtltab);
+ for(i = 0; i < prima->nitems; i++){
+ primidx = m->addprim(m, *(Primitive*)itemarrayget(prima, i));
+ if(m->prims[primidx].mtl != nil){
+ me = mtltabget(mtltab, m->prims[primidx].mtl->name);
+ m->prims[primidx].mtl = &m->materials[me->idx];
+ }
+ }
+
+getout:
+ rmitemarray(pa);
+ rmitemarray(na);
+ rmitemarray(ta);
+ rmitemarray(ca);
+ rmitemarray(Ta);
+ rmitemarray(va);
+ rmitemarray(prima);
+ rmmtltab(mtltab);
+ Bterm(bin);
+ return m;
+}
+
+static int
+Bprintp3(Biobuf *b, Point3 *p)
+{
+ int n;
+
+ n = Bprint(b, "%g %g %g", p->x, p->y, p->z);
+ if(p->w != 1)
+ n += Bprint(b, " %g", p->w);
+ n += Bprint(b, "\n");
+ return n;
+}
+
+static int
+Bprintp2(Biobuf *b, Point2 *p)
+{
+ int n;
+
+ n = Bprint(b, "%g %g", p->x, p->y);
+ if(p->w != 1)
+ n += Bprint(b, " %g", p->w);
+ n += Bprint(b, "\n");
+ return n;
+}
+
+static int
+Bprintn3(Biobuf *b, Point3 *p)
+{
+ return Bprint(b, "%g %g %g\n", p->x, p->y, p->z);
+}
+
+static int
+Bprintp(Biobuf *b, Point3 *p)
+{
+ int n;
+
+ n = Bprint(b, "p ");
+ n += Bprintp3(b, p);
+ return n;
+}
+
+static int
+Bprintn(Biobuf *b, Point3 *p)
+{
+ int n;
+
+ n = Bprint(b, "n ");
+ n += Bprintn3(b, p);
+ return n;
+}
+
+static int
+Bprintt(Biobuf *b, Point2 *p)
+{
+ int n;
+
+ n = Bprint(b, "t ");
+ n += Bprintp2(b, p);
+ return n;
+}
+
+static int
+Bprintc(Biobuf *b, Point3 *p)
+{
+ int n;
+
+ n = Bprint(b, "c ");
+ n += Bprintp3(b, p);
+ return n;
+}
+
+static int
+BprintT(Biobuf *b, Point3 *p)
+{
+ int n;
+
+ n = Bprint(b, "T ");
+ n += Bprintn3(b, p);
+ return n;
+}
+
+static int
+Bprintidx(Biobuf *b, usize idx)
+{
+ if(idx == NaI)
+ return Bprint(b, " -");
+ return Bprint(b, " %llud", idx);
+}
+
+static int
+Bprintv(Biobuf *b, Wirevert *v)
+{
+ int n;
+
+ n = Bprint(b, "v %llud", v->p);
+ n += Bprintidx(b, v->n);
+ n += Bprintidx(b, v->t);
+ n += Bprintidx(b, v->c);
+ n += Bprint(b, "\n");
+ return n;
+}
+
+static int
+Bprintprim(Biobuf *b, Wireprim *p)
+{
+ char *s;
+ int n, i;
+
+ n = Bprint(b, "prim %d", p->nv);
+ for(i = 0; i < p->nv; i++)
+ n += Bprintidx(b, p->v[i]);
+ n += Bprintidx(b, p->T);
+ if(p->mtlname != nil){
+ s = quotestrdup(p->mtlname);
+ if(s == nil)
+ sysfatal("quotestrdup: %r");
+ n += Bprint(b, " %s", s);
+ free(s);
+ }
+ n += Bprint(b, "\n");
+ return n;
+}
+
+/* TODO how do we deal with textures? embedded? keep a path? */
+static int
+Bprintmtl(Biobuf *b, Material *m)
+{
+ char *s;
+ int n;
+
+ s = quotestrdup(m->name);
+ if(s == nil)
+ sysfatal("quotestrdup: %r");
+ n = Bprint(b, "mtl %s {\n", s);
+ free(s);
+
+ if(m->ambient.a > 0){
+ n += Bprint(b, "\tambient: ");
+ n += Bprint(b, "%g %g %g", m->ambient.r, m->ambient.g, m->ambient.b);
+ if(m->ambient.a != 1)
+ n += Bprint(b, " %g", m->ambient.a);
+ n += Bprint(b, "\n");
+ }
+
+ if(m->diffuse.a > 0 || m->diffusemap != nil){
+ n += Bprint(b, "\tdiffuse: ");
+ n += Bprint(b, "%g %g %g", m->diffuse.r, m->diffuse.g, m->diffuse.b);
+ if(m->diffuse.a != 1)
+ n += Bprint(b, " %g", m->diffuse.a);
+ n += Bprint(b, "\n");
+ }
+
+ if(m->specular.a > 0 || m->specularmap != nil){
+ n += Bprint(b, "\tspecular: ");
+ n += Bprint(b, "%g %g %g", m->specular.r, m->specular.g, m->specular.b);
+ if(m->specular.a != 1)
+ n += Bprint(b, " %g", m->specular.a);
+ n += Bprint(b, "\n");
+ }
+
+ if(m->shininess > 0){
+ n += Bprint(b, "\tshininess: ");
+ n += Bprint(b, "%g\n", m->shininess);
+ }
+
+// if(m->diffusemap != nil){
+// n += Bprint("\tdiffusemap: ");
+// n += Bprint(b, "%s\n", m
+// }
+
+// if(m->specularmap != nil){
+// n += Bprint(b, "\tspecularmap: ");
+// n += Bprint(b, "%s\n", m
+// }
+
+// if(m->normalmap != nil)
+// n += Bprint(b, "\tnormals: ");
+
+ n += Bprint(b, "}\n");
+ return n;
+}
+
+usize
+writemodel(int fd, Model *m)
+{
+ IArray *pa, *na, *ta, *ca, *Ta, *va, *prima;
+ Wirevert v;
+ Wireprim prim;
+ Primitive *p, *ep;
+ Biobuf *out;
+ usize n;
+ int i;
+
+ out = Bfdopen(fd, OWRITE);
+ if(out == nil)
+ sysfatal("Bfdopen: %r");
+
+ pa = mkitemarray(sizeof(Point3));
+ na = mkitemarray(sizeof(Point3));
+ ta = mkitemarray(sizeof(Point2));
+ ca = mkitemarray(sizeof(Color));
+ Ta = mkitemarray(sizeof(Point3));
+ va = mkitemarray(sizeof(Wirevert));
+ prima = mkitemarray(sizeof(Wireprim));
+
+ n = 0;
+ p = m->prims;
+ ep = p + m->nprims;
+
+ while(p < ep){
+ memset(&prim, 0, sizeof prim);
+
+ prim.nv = p->type+1;
+ for(i = 0; i < prim.nv; i++){
+ v.p = itemarrayadd(pa, &p->v[i].p, 1);
+ v.n = eqpt3(p->v[i].n, Vec3(0,0,0))?
+ NaI: itemarrayadd(na, &p->v[i].n, 1);
+ v.t = p->v[i].uv.w != 1?
+ NaI: itemarrayadd(ta, &p->v[i].uv, 1);
+ v.c = p->v[i].c.a == 0?
+ NaI: itemarrayadd(ca, &p->v[i].c, 1);
+ prim.v[i] = itemarrayadd(va, &v, 1);
+ }
+ prim.T = eqpt3(p->tangent, Vec3(0,0,0))?
+ NaI: itemarrayadd(Ta, &p->tangent, 1);
+ prim.mtlname = p->mtl != nil? p->mtl->name: nil;
+
+ itemarrayadd(prima, &prim, 1);
+ p++;
+ }
+
+ for(i = 0; i < m->nmaterials; i++)
+ n += Bprintmtl(out, &m->materials[i]);
+
+ for(i = 0; i < pa->nitems; i++)
+ n += Bprintp(out, itemarrayget(pa, i));
+ for(i = 0; i < na->nitems; i++)
+ n += Bprintn(out, itemarrayget(na, i));
+ for(i = 0; i < ta->nitems; i++)
+ n += Bprintt(out, itemarrayget(ta, i));
+ for(i = 0; i < ca->nitems; i++)
+ n += Bprintc(out, itemarrayget(ca, i));
+ for(i = 0; i < Ta->nitems; i++)
+ n += BprintT(out, itemarrayget(Ta, i));
+ for(i = 0; i < va->nitems; i++)
+ n += Bprintv(out, itemarrayget(va, i));
+ for(i = 0; i < prima->nitems; i++)
+ n += Bprintprim(out, itemarrayget(prima, i));
+
+ rmitemarray(pa);
+ rmitemarray(na);
+ rmitemarray(ta);
+ rmitemarray(ca);
+ rmitemarray(Ta);
+ rmitemarray(va);
+ rmitemarray(prima);
+ Bterm(out);
+ return n;
+}