diff options
author | rodri <rgl@antares-labs.eu> | 2024-09-26 15:22:02 +0000 |
---|---|---|
committer | rodri <rgl@antares-labs.eu> | 2024-09-26 15:22:02 +0000 |
commit | 3155c6737832c54310c70ecaaf8667d0362de5e8 (patch) | |
tree | 5046974fdbaef855fb29d19671a9b80a9deb41f2 | |
parent | a8c789be9ab0fec65004118b7df373e84900cb1a (diff) | |
download | libgraphics-3155c6737832c54310c70ecaaf8667d0362de5e8.tar.gz libgraphics-3155c6737832c54310c70ecaaf8667d0362de5e8.tar.bz2 libgraphics-3155c6737832c54310c70ecaaf8667d0362de5e8.zip |
implement a parser for a model(6). get rid of the libobj dependency.
-rw-r--r-- | alloc.c | 1 | ||||
-rw-r--r-- | camera.c | 1 | ||||
-rw-r--r-- | clip.c | 7 | ||||
-rw-r--r-- | color.c | 1 | ||||
-rw-r--r-- | fb.c | 1 | ||||
-rw-r--r-- | graphics.h | 8 | ||||
-rw-r--r-- | internal.h | 5 | ||||
-rw-r--r-- | marshal.c | 939 | ||||
-rw-r--r-- | mkfile | 16 | ||||
-rw-r--r-- | model.6.txt | 18 | ||||
-rw-r--r-- | obj.c | 317 | ||||
-rw-r--r-- | readme | 6 | ||||
-rw-r--r-- | render.c | 7 | ||||
-rw-r--r-- | scene.c | 5 | ||||
-rw-r--r-- | shadeop.c | 1 | ||||
-rw-r--r-- | texture.c | 1 | ||||
-rw-r--r-- | util.c | 13 | ||||
-rw-r--r-- | vertex.c | 1 | ||||
-rw-r--r-- | viewport.c | 1 | ||||
-rw-r--r-- | xform.c | 1 |
20 files changed, 987 insertions, 363 deletions
@@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" @@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" @@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" @@ -52,12 +51,6 @@ fprintpoly(int fd, Polygon *p) fprint(fd, "%d/%lud p %V\n", i, p->n, p->v[i].p); } -static int -eqpt3(Point3 a, Point3 b) -{ - return vec3len(subpt3(a, b)) < 1e-6; -} - /* * references: * - “Clipping Using Homogeneous Coordinates”, James F. Blinn, Martin E. Newell, SIGGRAPH '78, pp. 245-251 @@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" @@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" @@ -403,9 +403,9 @@ Point3 world2model(Entity*, Point3); void perspective(Matrix3, double, double, double, double); void orthographic(Matrix3, double, double, double, double, double, double); -/* obj */ -int loadobjmodel(Model*, OBJ*); -Model *readobjmodel(char*); +/* marshal */ +Model *readmodel(int); +usize writemodel(int, Model*); /* scene */ Model *newmodel(void); @@ -441,9 +441,11 @@ Point maxpt(Point, Point); Point2 modulapt2(Point2, Point2); Point2 minpt2(Point2, Point2); Point2 maxpt2(Point2, Point2); +int eqpt2(Point2, Point2); Point3 modulapt3(Point3, Point3); Point3 minpt3(Point3, Point3); Point3 maxpt3(Point3, Point3); +int eqpt3(Point3, Point3); Memimage *rgba(ulong); Memimage *dupmemimage(Memimage*); @@ -1,3 +1,8 @@ +enum { + ε1 = 1e-5, + ε2 = 1e-6, +}; + typedef struct Polygon Polygon; typedef struct Entityparam Entityparam; typedef struct Tilerparam Tilerparam; 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; +} @@ -7,7 +7,6 @@ OFILES=\ render.$O\ clip.$O\ xform.$O\ - obj.$O\ scene.$O\ vertex.$O\ texture.$O\ @@ -17,11 +16,11 @@ OFILES=\ color.$O\ util.$O\ nanosec.$O\ + marshal.$O\ HFILES=\ graphics.h\ internal.h\ - libobj/obj.h UPDATE=\ mkfile\ @@ -29,16 +28,3 @@ UPDATE=\ ${OFILES:%.$O=%.c}\ </sys/src/cmd/mklib - -libobj/libobj.a$O: - cd libobj - mk install - -pulldeps:VQ: - git/clone git://antares-labs.eu/libobj || \ - git/clone git://shithub.us/rodri/libobj || \ - git/clone https://github.com/sametsisartenep/libobj - -clean nuke:V: - rm -f *.[$OS] $LIB - @{cd libobj; mk $target} diff --git a/model.6.txt b/model.6.txt new file mode 100644 index 0000000..f91527d --- /dev/null +++ b/model.6.txt @@ -0,0 +1,18 @@ +p x y z [w] +n x y z +t u v +c r g b [a] +v pᵢ [nᵢ|-] [tᵢ|-] [cᵢ|-] + +T x y z +prim nverts vᵢ... [Tᵢ|-] [mtlname] + +mtl name { + ambient: r g b [a] + diffuse: r g b [a] + specular: r g b [a] + shininess: n + diffusemap: path + specularmap: path + normals: path +} @@ -1,317 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <thread.h> -#include <draw.h> -#include <memdraw.h> -#include <geometry.h> -#include "libobj/obj.h" -#include "graphics.h" -#include "internal.h" - -/* - * fan triangulation. - * - * TODO check that the polygon is in fact convex - * try to adapt if not (by finding a convex - * vertex), or discard it. - */ -static int -triangulate(OBJElem **newe, OBJElem *e) -{ - OBJIndexArray *newidxtab; - OBJIndexArray *idxtab; - int i; - - idxtab = &e->indextab[OBJVGeometric]; - for(i = 0; i < idxtab->nindex-2; i++){ - idxtab = &e->indextab[OBJVGeometric]; - newe[i] = emalloc(sizeof **newe); - memset(newe[i], 0, sizeof **newe); - newe[i]->type = OBJEFace; - newe[i]->mtl = e->mtl; - newidxtab = &newe[i]->indextab[OBJVGeometric]; - newidxtab->nindex = 3; - newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices)); - newidxtab->indices[0] = idxtab->indices[0]; - newidxtab->indices[1] = idxtab->indices[i+1]; - newidxtab->indices[2] = idxtab->indices[i+2]; - idxtab = &e->indextab[OBJVTexture]; - if(idxtab->nindex > 0){ - newidxtab = &newe[i]->indextab[OBJVTexture]; - newidxtab->nindex = 3; - newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices)); - newidxtab->indices[0] = idxtab->indices[0]; - newidxtab->indices[1] = idxtab->indices[i+1]; - newidxtab->indices[2] = idxtab->indices[i+2]; - } - idxtab = &e->indextab[OBJVNormal]; - if(idxtab->nindex > 0){ - newidxtab = &newe[i]->indextab[OBJVNormal]; - newidxtab->nindex = 3; - newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices)); - newidxtab->indices[0] = idxtab->indices[0]; - newidxtab->indices[1] = idxtab->indices[i+1]; - newidxtab->indices[2] = idxtab->indices[i+2]; - } - } - - return i; -} - -typedef struct OBJ2MtlEntry OBJ2MtlEntry; -typedef struct OBJ2MtlMap OBJ2MtlMap; - -struct OBJ2MtlEntry -{ - OBJMaterial *objmtl; - ulong idx; - OBJ2MtlEntry *next; -}; - -struct OBJ2MtlMap -{ - OBJ2MtlEntry *head; - Material *mtls; -}; - -static void -addmtlmap(OBJ2MtlMap *map, OBJMaterial *om, ulong idx) -{ - OBJ2MtlEntry *e; - - if(om == nil) - return; - - e = emalloc(sizeof *e); - memset(e, 0, sizeof *e); - e->objmtl = om; - e->idx = idx; - - if(map->head == nil){ - map->head = e; - return; - } - - e->next = map->head; - map->head = e; -} - -static Material * -getmtlmap(OBJ2MtlMap *map, OBJMaterial *om) -{ - OBJ2MtlEntry *e; - - for(e = map->head; e != nil; e = e->next) - if(e->objmtl == om) - return &map->mtls[e->idx]; - return nil; -} - -static void -clrmtlmap(OBJ2MtlMap *map) -{ - OBJ2MtlEntry *e, *ne; - - for(e = map->head; e != nil; e = ne){ - ne = e->next; - free(e); - } -} - -int -loadobjmodel(Model *m, OBJ *obj) -{ - Primitive *p; - OBJVertex *pverts, *tverts, *nverts, *v; /* geometric, texture and normals vertices */ - OBJElem **trielems, *e, *ne; - OBJObject *o; - OBJIndexArray *idxtab; - OBJ2MtlMap mtlmap; - OBJMaterial *objmtl; - Material *mtl; - Point3 n; /* surface normal */ - int i, idx, nt, maxnt, neednormal, gottaclean; - - if(obj == nil) - return 0; - - pverts = obj->vertdata[OBJVGeometric].verts; - tverts = obj->vertdata[OBJVTexture].verts; - nverts = obj->vertdata[OBJVNormal].verts; - trielems = nil; - maxnt = 0; - - if(m->prims != nil){ - free(m->prims); - m->prims = nil; - } - m->nprims = 0; - - mtlmap.head = nil; - for(i = 0; obj->materials != nil && i < nelem(obj->materials->mattab); i++) - for(objmtl = obj->materials->mattab[i]; objmtl != nil; objmtl = objmtl->next){ - mtlmap.mtls = m->materials = erealloc(m->materials, ++m->nmaterials*sizeof(*m->materials)); - mtl = &m->materials[m->nmaterials-1]; - memset(mtl, 0, sizeof *mtl); - - if(objmtl->name != nil){ - mtl->name = strdup(objmtl->name); - if(mtl->name == nil) - sysfatal("strdup: %r"); - } - mtl->ambient = Pt3(objmtl->Ka.r, objmtl->Ka.g, objmtl->Ka.b, 1); - mtl->diffuse = Pt3(objmtl->Kd.r, objmtl->Kd.g, objmtl->Kd.b, 1); - mtl->specular = Pt3(objmtl->Ks.r, objmtl->Ks.g, objmtl->Ks.b, 1); - mtl->shininess = objmtl->Ns; - - if(objmtl->map_Kd != nil){ - mtl->diffusemap = alloctexture(sRGBTexture, nil); - mtl->diffusemap->image = dupmemimage(objmtl->map_Kd); - } - - if(objmtl->norm != nil){ - mtl->normalmap = alloctexture(RAWTexture, nil); - mtl->normalmap->image = dupmemimage(objmtl->norm); - } - - addmtlmap(&mtlmap, objmtl, m->nmaterials-1); - } - - for(i = 0; i < nelem(obj->objtab); i++) - for(o = obj->objtab[i]; o != nil; o = o->next) - for(e = o->child; e != nil; e = ne){ - ne = e->next; - - switch(e->type){ - case OBJEPoint: - m->prims = erealloc(m->prims, ++m->nprims*sizeof(*m->prims)); - p = &m->prims[m->nprims-1]; - memset(p, 0, sizeof *p); - p->type = PPoint; - p->mtl = getmtlmap(&mtlmap, e->mtl); - - idxtab = &e->indextab[OBJVGeometric]; - v = &pverts[idxtab->indices[0]]; - p->v[0].p = Pt3(v->x, v->y, v->z, v->w); - - idxtab = &e->indextab[OBJVTexture]; - if(idxtab->nindex == 1){ - v = &tverts[idxtab->indices[0]]; - p->v[0].uv = Pt2(v->u, v->v, 1); - } - break; - case OBJELine: - m->prims = erealloc(m->prims, ++m->nprims*sizeof(*m->prims)); - p = &m->prims[m->nprims-1]; - memset(p, 0, sizeof *p); - p->type = PLine; - p->mtl = getmtlmap(&mtlmap, e->mtl); - - for(idx = 0; idx < 2; idx++){ - idxtab = &e->indextab[OBJVGeometric]; - v = &pverts[idxtab->indices[idx]]; - p->v[idx].p = Pt3(v->x, v->y, v->z, v->w); - - idxtab = &e->indextab[OBJVTexture]; - if(idxtab->nindex == 2){ - v = &tverts[idxtab->indices[idx]]; - p->v[idx].uv = Pt2(v->u, v->v, 1); - } - } - break; - case OBJEFace: - idxtab = &e->indextab[OBJVGeometric]; - assert(idxtab->nindex >= 3); - gottaclean = 0; - - /* it takes n-2 triangles to fill any given n-gon */ - nt = idxtab->nindex-2; - if(nt > maxnt){ - maxnt = nt; - trielems = erealloc(trielems, maxnt*sizeof(*trielems)); - } - if(nt > 1){ - assert(triangulate(trielems, e) == nt); - gottaclean = 1; - }else - trielems[0] = e; - - while(nt-- > 0){ - e = trielems[nt]; - neednormal = 0; - - m->prims = erealloc(m->prims, ++m->nprims*sizeof(*m->prims)); - p = &m->prims[m->nprims-1]; - memset(p, 0, sizeof *p); - p->type = PTriangle; - p->mtl = getmtlmap(&mtlmap, e->mtl); - - for(idx = 0; idx < 3; idx++){ - idxtab = &e->indextab[OBJVGeometric]; - v = &pverts[idxtab->indices[idx]]; - p->v[idx].p = Pt3(v->x, v->y, v->z, v->w); - - idxtab = &e->indextab[OBJVNormal]; - if(idxtab->nindex == 3){ - v = &nverts[idxtab->indices[idx]]; - p->v[idx].n = normvec3(Vec3(v->i, v->j, v->k)); - }else - neednormal = 1; - - idxtab = &e->indextab[OBJVTexture]; - if(idxtab->nindex == 3){ - v = &tverts[idxtab->indices[idx]]; - p->v[idx].uv = Pt2(v->u, v->v, 1); - } - } - if(p->v[0].uv.w != 0){ - Point3 e0, e1; - Point2 Δuv0, Δuv1; - double det; - - e0 = subpt3(p->v[1].p, p->v[0].p); - e1 = subpt3(p->v[2].p, p->v[0].p); - Δuv0 = subpt2(p->v[1].uv, p->v[0].uv); - Δuv1 = subpt2(p->v[2].uv, p->v[0].uv); - - det = Δuv0.x * Δuv1.y - Δuv1.x * Δuv0.y; - det = det == 0? 0: 1.0/det; - p->tangent.x = det*(Δuv1.y * e0.x - Δuv0.y * e1.x); - p->tangent.y = det*(Δuv1.y * e0.y - Δuv0.y * e1.y); - p->tangent.z = det*(Δuv1.y * e0.z - Δuv0.y * e1.z); - p->tangent = normvec3(p->tangent); - } - if(neednormal){ - n = normvec3(crossvec3(subpt3(p->v[1].p, p->v[0].p), subpt3(p->v[2].p, p->v[0].p))); - p->v[0].n = p->v[1].n = p->v[2].n = n; - } - if(gottaclean){ - free(e->indextab[OBJVGeometric].indices); - free(e->indextab[OBJVNormal].indices); - free(e->indextab[OBJVTexture].indices); - free(e); - } - } - break; - default: continue; - } - } - - free(trielems); - clrmtlmap(&mtlmap); - return m->nprims; -} - -Model * -readobjmodel(char *path) -{ - Model *m; - OBJ *obj; - - m = newmodel(); - if((obj = objparse(path)) == nil) - sysfatal("objparse: %r"); - loadobjmodel(m, obj); - objfree(obj); - return m; -} @@ -1,9 +1,7 @@ libgraphics -Libgraphics provides 3D computer graphics through memdraw(2). +Libgraphics provides 3D computer graphics through draw(2) and +memdraw(2). Still in early stages of research, subject to change, drastically, at any given time. - -It now relies on libobj to manipulate the geometry, so you need to run -`mk pulldeps` before you build it. @@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" @@ -155,7 +154,7 @@ _barycoords(Triangle2 t, Point2 p) Point3 v = crossvec3(Vec3(p0p2.x, p0p1.x, pp0.x), Vec3(p0p2.y, p0p1.y, pp0.y)); /* handle degenerate triangles—i.e. the ones where every point lies on the same line */ - if(fabs(v.z) < 1e-5) + if(fabs(v.z) < ε1) return Pt3(-1,-1,-1,1); return Pt3(1 - (v.x + v.y)/v.z, v.y/v.z, v.x/v.z, 1); } @@ -262,7 +261,7 @@ rasterize(Rastertask *task) /* interpolate z⁻¹ and get actual z */ pcz = flerp(prim->v[0].p.w, prim->v[1].p.w, perc); - pcz = 1.0/(pcz < 1e-5? 1e-5: pcz); + pcz = 1.0/(pcz < ε1? ε1: pcz); /* perspective-correct attribute interpolation */ perc *= prim->v[0].p.w * pcz; @@ -314,7 +313,7 @@ discard: /* interpolate z⁻¹ and get actual z */ pcz = fberp(prim->v[0].p.w, prim->v[1].p.w, prim->v[2].p.w, bc); - pcz = 1.0/(pcz < 1e-5? 1e-5: pcz); + pcz = 1.0/(pcz < ε1? ε1: pcz); /* perspective-correct attribute interpolation */ bc = modulapt3(bc, Vec3(prim->v[0].p.w*pcz, @@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" @@ -13,7 +12,7 @@ model_addprim(Model *m, Primitive p) { m->prims = erealloc(m->prims, ++m->nprims*sizeof(*m->prims)); m->prims[m->nprims-1] = p; - return 0; + return m->nprims-1; } static int @@ -21,7 +20,7 @@ model_addmaterial(Model *m, Material mtl) { m->materials = erealloc(m->materials, ++m->nmaterials*sizeof(*m->materials)); m->materials[m->nmaterials-1] = mtl; - return 0; + return m->nmaterials-1; } Model * @@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" @@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" @@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" @@ -52,6 +51,12 @@ maxpt2(Point2 a, Point2 b) }; } +int +eqpt2(Point2 a, Point2 b) +{ + return vec2len(subpt2(a, b)) < ε2; +} + Point3 modulapt3(Point3 a, Point3 b) { @@ -80,6 +85,12 @@ maxpt3(Point3 a, Point3 b) }; } +int +eqpt3(Point3 a, Point3 b) +{ + return vec3len(subpt3(a, b)) < ε2; +} + void memsetf(void *dp, float v, usize len) { @@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" @@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" @@ -4,7 +4,6 @@ #include <draw.h> #include <memdraw.h> #include <geometry.h> -#include "libobj/obj.h" #include "graphics.h" #include "internal.h" |