From b429311ed087ee7cf7fc7771a8e1834ea074f8da Mon Sep 17 00:00:00 2001 From: rodri Date: Fri, 3 May 2024 13:32:48 +0000 Subject: add a general primitive with support for points, lines and triangles. also got rid of the dependency on OBJ for the entire renderer, instead letting the user load a Model from any given OBJ. this modularity will allow for other formats to be used in the same way, relying on a single, internal representation for the entire pipeline. --- clip.c | 220 ++++++++++++++++++++++ doc/libgraphics.ms | 1 + doc/libgraphics.pdf | Bin 19045 -> 19735 bytes doc/libgraphics.ps | 126 +++++++++++++ graphics.h | 15 +- internal.h | 14 ++ mkfile | 1 + render.c | 511 +++++++++++++++++++++++++--------------------------- scene.c | 273 +++++++++++++++++++++++----- util.c | 20 ++ 10 files changed, 863 insertions(+), 318 deletions(-) create mode 100644 clip.c diff --git a/clip.c b/clip.c new file mode 100644 index 0000000..ded387b --- /dev/null +++ b/clip.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include +#include +#include +#include "libobj/obj.h" +#include "graphics.h" +#include "internal.h" + +enum { + CLIPL = 1, + CLIPR = 2, + CLIPT = 4, + CLIPB = 8, +}; + +static void +mulsdm(double r[6], double m[6][4], Point3 p) +{ + int i; + + for(i = 0; i < 6; i++) + r[i] = m[i][0]*p.x + m[i][1]*p.y + m[i][2]*p.z + m[i][3]*p.w; +} + +static int +addvert(Polygon *p, Vertex v) +{ + if(++p->n > p->cap) + p->v = erealloc(p->v, (p->cap = p->n)*sizeof(*p->v)); + p->v[p->n-1] = v; + return p->n; +} + +static void +swappoly(Polygon *a, Polygon *b) +{ + Polygon tmp; + + tmp = *a; + *a = *b; + *b = tmp; +} + +static void +cleanpoly(Polygon *p) +{ + int i; + + for(i = 0; i < p->n; i++) + delvattrs(&p->v[i]); + p->n = 0; +} + +/* + * references: + * - James F. Blinn, Martin E. Newell, “Clipping Using Homogeneous Coordinates”, + * SIGGRAPH '78, pp. 245-251 + * - https://cs418.cs.illinois.edu/website/text/clipping.html + * - https://github.com/aap/librw/blob/14dab85dcae6f3762fb2b1eda4d58d8e67541330/tools/playground/tl_tests.cpp#L522 + */ +int +clipprimitive(Primitive *p) +{ + /* signed distance from each clipping plane */ + static double sdm[6][4] = { + 1, 0, 0, 1, /* l */ + -1, 0, 0, 1, /* r */ + 0, 1, 0, 1, /* b */ + 0, -1, 0, 1, /* t */ + 0, 0, 1, 1, /* f */ + 0, 0, -1, 1, /* n */ + }; + double sd0[6], sd1[6]; + double d0, d1, perc; + Polygon Vin, Vout; + Vertex *v0, *v1, v; /* edge verts and new vertex (line-plane intersection) */ + int i, j, nt; + + nt = 0; + memset(&Vin, 0, sizeof Vin); + memset(&Vout, 0, sizeof Vout); + for(i = 0; i < p[0].type+1; i++) + addvert(&Vin, p[0].v[i]); + + for(j = 0; j < 6 && Vin.n > 0; j++){ + for(i = 0; i < Vin.n; i++){ + v0 = &Vin.v[i]; + v1 = &Vin.v[(i+1) % Vin.n]; + + mulsdm(sd0, sdm, v0->p); + mulsdm(sd1, sdm, v1->p); + + if(sd0[j] < 0 && sd1[j] < 0) + continue; + + if(sd0[j] >= 0 && sd1[j] >= 0) + goto allin; + + d0 = (j&1) == 0? sd0[j]: -sd0[j]; + d1 = (j&1) == 0? sd1[j]: -sd1[j]; + perc = d0/(d0 - d1); + + lerpvertex(&v, v0, v1, perc); + addvert(&Vout, v); + + if(sd1[j] >= 0){ +allin: + addvert(&Vout, dupvertex(v1)); + } + } + cleanpoly(&Vin); + if(j < 6-1) + swappoly(&Vin, &Vout); + } + + if(Vout.n < 2) + cleanpoly(&Vout); + else switch(p[0].type){ + case PLine: + /* TODO fix line clipping (they disappear instead, why?) */ + p[0].v[0] = dupvertex(&Vout.v[0]); + p[0].v[1] = dupvertex(&Vout.v[1]); + cleanpoly(&Vout); + break; + case PTriangle: + /* triangulate */ + for(i = 0; i < Vout.n-2; i++, nt++){ + /* + * when performing fan triangulation, indices 0 and 2 + * are referenced on every triangle, so duplicate them + * to avoid complications during rasterization. + */ + memmove(&p[nt], &p[0], sizeof *p); + p[nt].v[0] = i < Vout.n-2-1? dupvertex(&Vout.v[0]): Vout.v[0]; + p[nt].v[1] = Vout.v[i+1]; + p[nt].v[2] = i < Vout.n-2-1? dupvertex(&Vout.v[i+2]): Vout.v[i+2]; + } + break; + } + free(Vout.v); + free(Vin.v); + + return nt; +} + +static int +ptisinside(int code) +{ + return !code; +} + +static int +lineisinside(int code0, int code1) +{ + return !(code0|code1); +} + +static int +lineisoutside(int code0, int code1) +{ + return code0 & code1; +} + +static int +outcode(Point p, Rectangle r) +{ + int code; + + code = 0; + if(p.x < r.min.x) code |= CLIPL; + if(p.x > r.max.x) code |= CLIPR; + if(p.y < r.min.y) code |= CLIPT; + if(p.y > r.max.y) code |= CLIPB; + return code; +} + +/* + * Cohen-Sutherland rectangle-line clipping + */ +void +rectclipline(Rectangle r, Point *p0, Point *p1) +{ + int code0, code1; + int Δx; + double m; + + Δx = p1->x - p0->x; + m = Δx == 0? 0: (p1->y - p0->y)/Δx; + + for(;;){ + code0 = outcode(*p0, r); + code1 = outcode(*p1, r); + + if(lineisinside(code0, code1) || lineisoutside(code0, code1)) + break; + + if(ptisinside(code0)){ + swappt(p0, p1); + swapi(&code0, &code1); + } + + if(code0 & CLIPL){ + p0->y += (r.min.x - p0->x)*m; + p0->x = r.min.x; + }else if(code0 & CLIPR){ + p0->y += (r.max.x - p0->x)*m; + p0->x = r.max.x; + }else if(code0 & CLIPB){ + if(p0->x != p1->x) + p0->x += (r.min.y - p0->y)/m; + p0->y = r.min.y; + }else if(code0 & CLIPT){ + if(p0->x != p1->x) + p0->x += (r.max.y - p0->y)/m; + p0->y = r.max.y; + } + } +} diff --git a/doc/libgraphics.ms b/doc/libgraphics.ms index 31a44af..8e48b87 100644 --- a/doc/libgraphics.ms +++ b/doc/libgraphics.ms @@ -13,6 +13,7 @@ Rendering graph .ps 7 circlerad = 0.3 moveht = 0.1 +arrowhead = 9 box "Renderjob" arrow R: circle "renderer" diff --git a/doc/libgraphics.pdf b/doc/libgraphics.pdf index 775d825..b803382 100644 Binary files a/doc/libgraphics.pdf and b/doc/libgraphics.pdf differ diff --git a/doc/libgraphics.ps b/doc/libgraphics.ps index 081c5d0..6784015 100644 --- a/doc/libgraphics.ps +++ b/doc/libgraphics.ps @@ -636,11 +636,25 @@ mark (Renderjob) 1123 3834 w 1566 3820 1926 3820 Dl 1854 3838 1926 3820 Dl +1853 3833 1925 3820 Dl +1852 3829 1925 3820 Dl +1851 3824 1925 3820 Dl +1851 3820 1925 3820 Dl +1851 3815 1925 3819 Dl +1852 3810 1925 3819 Dl +1853 3806 1925 3819 Dl 1854 3802 1926 3820 Dl 1926 3820 432 432 De (renderer) 1997 3834 w 2358 3820 2718 3820 Dl 2646 3838 2718 3820 Dl +2645 3833 2717 3820 Dl +2644 3829 2717 3820 Dl +2643 3824 2717 3820 Dl +2643 3820 2717 3820 Dl +2643 3815 2717 3819 Dl +2644 3810 2717 3819 Dl +2645 3806 2717 3819 Dl 2646 3802 2718 3820 Dl 2718 3820 432 432 De (entityproc) 2763 3834 w @@ -672,39 +686,123 @@ mark (n) 4671 4590 w 3090 3670 3569 3213 Dl 3530 3275 3569 3213 Dl +3526 3273 3569 3213 Dl +3522 3270 3568 3213 Dl +3519 3267 3569 3213 Dl +3515 3264 3569 3213 Dl +3512 3261 3568 3214 Dl +3510 3257 3569 3213 Dl +3507 3253 3568 3213 Dl 3504 3249 3568 3213 Dl 3139 3754 3519 3634 Dl 3456 3672 3519 3634 Dl +3454 3668 3519 3633 Dl +3452 3664 3519 3634 Dl +3450 3660 3519 3634 Dl +3449 3655 3519 3633 Dl +3448 3651 3520 3633 Dl +3447 3647 3519 3634 Dl +3446 3642 3519 3633 Dl 3445 3637 3519 3633 Dl 3139 3885 3519 4005 Dl 3445 4002 3519 4006 Dl +3446 3997 3519 4006 Dl +3447 3992 3519 4005 Dl +3448 3988 3520 4006 Dl +3449 3984 3519 4006 Dl +3450 3979 3519 4005 Dl +3452 3975 3519 4005 Dl +3454 3971 3519 4006 Dl 3456 3967 3519 4005 Dl 3090 3969 3569 4426 Dl 3504 4390 3568 4426 Dl +3507 4386 3568 4426 Dl +3510 4382 3569 4426 Dl +3512 4378 3568 4425 Dl +3515 4375 3569 4426 Dl +3519 4372 3569 4426 Dl +3522 4369 3568 4426 Dl +3526 4366 3569 4426 Dl 3530 4364 3569 4426 Dl 3942 3064 4302 3064 Dl 4230 3082 4302 3064 Dl +4229 3077 4301 3064 Dl +4228 3073 4301 3064 Dl +4227 3068 4301 3064 Dl +4227 3064 4301 3064 Dl +4227 3059 4301 3063 Dl +4228 3054 4301 3063 Dl +4229 3050 4301 3063 Dl 4230 3046 4302 3064 Dl 3908 3179 4335 3451 Dl 4265 3428 4335 3451 Dl +4266 3424 4335 3452 Dl +4268 3420 4334 3451 Dl +4271 3416 4335 3452 Dl +4273 3412 4335 3451 Dl +4275 3408 4334 3451 Dl +4278 3404 4335 3451 Dl +4281 3401 4335 3451 Dl 4284 3398 4335 3452 Dl 3859 3233 4383 3901 Dl 4325 3856 4383 3901 Dl +4328 3853 4383 3901 Dl +4332 3849 4384 3901 Dl +4335 3846 4384 3901 Dl +4338 3843 4384 3901 Dl +4342 3840 4384 3901 Dl +4345 3838 4383 3901 Dl +4350 3836 4384 3901 Dl 4354 3834 4384 3901 Dl 3826 3255 4417 4384 Dl 4368 4329 4417 4384 Dl +4371 4326 4417 4384 Dl +4375 4323 4417 4384 Dl +4379 4321 4417 4384 Dl +4383 4318 4417 4383 Dl +4387 4316 4417 4383 Dl +4391 4315 4416 4384 Dl +4395 4313 4416 4384 Dl 4400 4312 4417 4384 Dl 3908 3452 4335 3180 Dl 4284 3233 4335 3179 Dl +4281 3230 4335 3180 Dl +4278 3227 4335 3180 Dl +4275 3223 4334 3180 Dl +4273 3219 4335 3180 Dl +4271 3215 4335 3179 Dl +4268 3211 4334 3180 Dl +4266 3208 4335 3180 Dl 4265 3203 4335 3180 Dl 3942 3568 4302 3568 Dl 4230 3586 4302 3568 Dl +4229 3581 4301 3568 Dl +4228 3577 4301 3568 Dl +4227 3572 4301 3568 Dl +4227 3568 4301 3568 Dl +4227 3563 4301 3567 Dl +4228 3558 4301 3567 Dl +4229 3554 4301 3567 Dl 4230 3550 4302 3568 Dl 3908 3683 4335 3955 Dl 4265 3932 4335 3955 Dl +4266 3928 4335 3956 Dl +4268 3924 4334 3955 Dl +4271 3920 4335 3956 Dl +4273 3916 4335 3955 Dl +4275 3912 4334 3955 Dl +4278 3908 4335 3955 Dl +4281 3905 4335 3955 Dl 4284 3902 4335 3956 Dl 3859 3737 4383 4405 Dl 4325 4360 4383 4405 Dl +4328 4357 4383 4405 Dl +4332 4353 4384 4405 Dl +4335 4350 4384 4405 Dl +4338 4347 4384 4405 Dl +4342 4344 4384 4405 Dl +4345 4342 4383 4405 Dl +4350 4340 4384 4405 Dl 4354 4338 4384 4405 Dl 10 /LucidaSans-Demi f (Figure 1) 720 4990 w @@ -1098,15 +1196,43 @@ mark 3272 3644 3292 3614 Dl 3309 3588 3329 3558 Dl 3304 3627 3329 3558 Dl +3300 3626 3329 3558 Dl +3296 3624 3329 3558 Dl +3292 3622 3329 3558 Dl +3288 3619 3329 3558 Dl +3285 3617 3329 3558 Dl +3281 3614 3329 3558 Dl +3278 3611 3329 3558 Dl 3275 3607 3329 3558 Dl 2970 4242 3330 4062 Dl 3273 4110 3329 4062 Dl +3270 4106 3329 4062 Dl +3268 4103 3329 4062 Dl +3265 4099 3329 4062 Dl +3263 4095 3329 4062 Dl +3261 4090 3329 4062 Dl +3260 4086 3329 4062 Dl +3258 4082 3329 4062 Dl 3257 4077 3329 4062 Dl 2970 4386 3330 4566 Dl 3257 4550 3329 4565 Dl +3258 4545 3329 4565 Dl +3260 4541 3329 4565 Dl +3261 4537 3329 4565 Dl +3263 4532 3329 4565 Dl +3265 4528 3329 4565 Dl +3268 4524 3329 4565 Dl +3270 4521 3329 4565 Dl 3273 4517 3329 4565 Dl 2970 4530 3330 5070 Dl 3275 5020 3329 5069 Dl +3278 5016 3329 5069 Dl +3281 5013 3329 5069 Dl +3285 5010 3329 5069 Dl +3288 5008 3329 5069 Dl +3292 5005 3329 5069 Dl +3296 5003 3329 5069 Dl +3300 5001 3329 5069 Dl 3304 5000 3329 5069 Dl 10 /LucidaSans-Demi f (Figure 3) 720 5484 w diff --git a/graphics.h b/graphics.h index 5abfa0c..7d4006c 100644 --- a/graphics.h +++ b/graphics.h @@ -86,7 +86,7 @@ struct Vertex Point3 n; /* surface normal */ Color c; /* shading color */ Point2 uv; /* texture coordinate */ - OBJMaterial *mtl; + Material *mtl; /* TODO it'd be neat to use a dynamic hash table instead */ Vertexattr *attrs; /* attributes (aka varyings) */ @@ -106,25 +106,24 @@ struct Material Color diffuse; Color specular; double shininess; + Memimage *diffusemap; }; struct Primitive { int type; Vertex v[3]; + Material *mtl; }; struct Model { - Primitive *mesh; + Primitive *prims; + ulong nprims; Memimage *tex; /* texture map */ Memimage *nor; /* normals map */ Material *materials; ulong nmaterials; - - OBJ *obj; - OBJElem **elems; /* cache of renderable elems */ - ulong nelems; }; struct Entity @@ -167,7 +166,7 @@ struct SUparams Memimage *frag; Renderjob *job; Entity *entity; - OBJElem **eb, **ee; + Primitive *eb, *ee; uvlong uni_time; @@ -283,7 +282,7 @@ void perspective(Matrix3, double, double, double, double); void orthographic(Matrix3, double, double, double, double, double, double); /* scene */ -int refreshmodel(Model*); +int loadobjmodel(Model*, OBJ*); Model *newmodel(void); void delmodel(Model*); Entity *newentity(Model*); diff --git a/internal.h b/internal.h index 213326d..a982f2c 100644 --- a/internal.h +++ b/internal.h @@ -1,7 +1,15 @@ +typedef struct Polygon Polygon; typedef struct Tilerparam Tilerparam; typedef struct Rasterparam Rasterparam; typedef struct Rastertask Rastertask; +struct Polygon +{ + Vertex *v; + ulong n; + ulong cap; +}; + struct Tilerparam { int id; @@ -42,9 +50,15 @@ void berpvertex(Vertex*, Vertex*, Vertex*, Vertex*, Point3); void delvattrs(Vertex*); void fprintvattrs(int, Vertex*); +/* clip */ +int clipprimitive(Primitive*); +void rectclipline(Rectangle, Point*, Point*); + /* util */ int min(int, int); int max(int, int); +void swapi(int*, int*); +void swappt(Point*, Point*); void memsetd(double*, double, usize); /* nanosec */ diff --git a/mkfile b/mkfile index 7a05817..b84fe8d 100644 --- a/mkfile +++ b/mkfile @@ -5,6 +5,7 @@ OFILES=\ camera.$O\ viewport.$O\ render.$O\ + clip.$O\ scene.$O\ vertex.$O\ texture.$O\ diff --git a/render.c b/render.c index 8163bc7..4a7fb41 100644 --- a/render.c +++ b/render.c @@ -52,133 +52,6 @@ isfacingback(Primitive p) return sa <= 0; } -static void -mulsdm(double r[6], double m[6][4], Point3 p) -{ - int i; - - for(i = 0; i < 6; i++) - r[i] = m[i][0]*p.x + m[i][1]*p.y + m[i][2]*p.z + m[i][3]*p.w; -} - -typedef struct -{ - Vertex *v; - ulong n; - ulong cap; -} Polygon; - -static int -addvert(Polygon *p, Vertex v) -{ - if(++p->n > p->cap) - p->v = erealloc(p->v, (p->cap = p->n)*sizeof(*p->v)); - p->v[p->n-1] = v; - return p->n; -} - -static void -swappoly(Polygon *a, Polygon *b) -{ - Polygon tmp; - - tmp = *a; - *a = *b; - *b = tmp; -} - -static void -cleanpoly(Polygon *p) -{ - int i; - - for(i = 0; i < p->n; i++) - delvattrs(&p->v[i]); - p->n = 0; -} - -/* - * references: - * - James F. Blinn, Martin E. Newell, “Clipping Using Homogeneous Coordinates”, - * SIGGRAPH '78, pp. 245-251 - * - https://cs418.cs.illinois.edu/website/text/clipping.html - * - https://github.com/aap/librw/blob/14dab85dcae6f3762fb2b1eda4d58d8e67541330/tools/playground/tl_tests.cpp#L522 - */ -static int -cliptriangle(Primitive *p) -{ - /* signed distance from each clipping plane */ - static double sdm[6][4] = { - 1, 0, 0, 1, /* l */ - -1, 0, 0, 1, /* r */ - 0, 1, 0, 1, /* b */ - 0, -1, 0, 1, /* t */ - 0, 0, 1, 1, /* f */ - 0, 0, -1, 1, /* n */ - }; - double sd0[6], sd1[6]; - double d0, d1, perc; - Polygon Vin, Vout; - Vertex *v0, *v1, v; /* edge verts and new vertex (line-plane intersection) */ - int i, j, nt; - - nt = 0; - memset(&Vin, 0, sizeof Vin); - memset(&Vout, 0, sizeof Vout); - for(i = 0; i < 3; i++) - addvert(&Vin, p[0].v[i]); - - for(j = 0; j < 6 && Vin.n > 0; j++){ - for(i = 0; i < Vin.n; i++){ - v0 = &Vin.v[i]; - v1 = &Vin.v[(i+1) % Vin.n]; - - mulsdm(sd0, sdm, v0->p); - mulsdm(sd1, sdm, v1->p); - - if(sd0[j] < 0 && sd1[j] < 0) - continue; - - if(sd0[j] >= 0 && sd1[j] >= 0) - goto allin; - - d0 = (j&1) == 0? sd0[j]: -sd0[j]; - d1 = (j&1) == 0? sd1[j]: -sd1[j]; - perc = d0/(d0 - d1); - - lerpvertex(&v, v0, v1, perc); - addvert(&Vout, v); - - if(sd1[j] >= 0){ -allin: - addvert(&Vout, dupvertex(v1)); - } - } - cleanpoly(&Vin); - if(j < 6-1) - swappoly(&Vin, &Vout); - } - - /* triangulate */ - if(Vout.n < 3) - cleanpoly(&Vout); - else - for(i = 0; i < Vout.n-2; i++, nt++){ - /* - * when performing fan triangulation, indices 0 and 2 - * are referenced on every triangle, so duplicate them - * to avoid complications during rasterization. - */ - p[nt].v[0] = i < Vout.n-2-1? dupvertex(&Vout.v[0]): Vout.v[0]; - p[nt].v[1] = Vout.v[i+1]; - p[nt].v[2] = i < Vout.n-2-1? dupvertex(&Vout.v[i+2]): Vout.v[i+2]; - } - free(Vout.v); - free(Vin.v); - - return nt; -} - /* * transforms p from e's reference frame into * the world. @@ -297,54 +170,83 @@ rasterize(Rastertask *task) SUparams *params; Primitive prim; FSparams fsp; - Triangle2 t₂; + Triangle2 t; Rectangle bbox; - Point p; + Point p, dp, Δp, p0, p1; Point3 bc; Color c; - double z, depth; + double z, depth, dplen, perc; + int steep = 0, Δe, e, Δy; params = task->params; prim = task->p; memmove(prim.v, task->p.v, sizeof prim.v); - - t₂.p0 = Pt2(prim.v[0].p.x, prim.v[0].p.y, 1); - t₂.p1 = Pt2(prim.v[1].p.x, prim.v[1].p.y, 1); - t₂.p2 = Pt2(prim.v[2].p.x, prim.v[2].p.y, 1); - /* find the triangle's bbox and clip it against our wr */ - bbox.min.x = min(min(t₂.p0.x, t₂.p1.x), t₂.p2.x); - bbox.min.y = min(min(t₂.p0.y, t₂.p1.y), t₂.p2.y); - bbox.max.x = max(max(t₂.p0.x, t₂.p1.x), t₂.p2.x)+1; - bbox.max.y = max(max(t₂.p0.y, t₂.p1.y), t₂.p2.y)+1; - bbox.min.x = max(bbox.min.x, task->wr.min.x); - bbox.min.y = max(bbox.min.y, task->wr.min.y); - bbox.max.x = min(bbox.max.x, task->wr.max.x); - bbox.max.y = min(bbox.max.y, task->wr.max.y); fsp.su = params; memset(&fsp.v, 0, sizeof fsp.v); - for(p.y = bbox.min.y; p.y < bbox.max.y; p.y++) - for(p.x = bbox.min.x; p.x < bbox.max.x; p.x++){ - bc = barycoords(t₂, Pt2(p.x,p.y,1)); - if(bc.x < 0 || bc.y < 0 || bc.z < 0) - continue; + switch(prim.type){ + case PPoint: + p = Pt(prim.v[0].p.x, prim.v[0].p.y); + + depth = fclamp(prim.v[0].p.z, 0, 1); + if(depth <= params->fb->zb[p.x + p.y*Dx(params->fb->r)]) + break; + params->fb->zb[p.x + p.y*Dx(params->fb->r)] = depth; + + fsp.v = dupvertex(&prim.v[0]); + fsp.p = p; + c = params->fshader(&fsp); + memfillcolor(params->frag, col2ul(c)); + + pixel(params->fb->cb, p, params->frag); + delvattrs(&fsp.v); + break; + case PLine: + p0 = Pt(prim.v[0].p.x, prim.v[0].p.y); + p1 = Pt(prim.v[1].p.x, prim.v[1].p.y); + /* clip it against our wr */ + rectclipline(task->wr, &p0, &p1); + + /* transpose the points */ + if(abs(p0.x-p1.x) < abs(p0.y-p1.y)){ + steep = 1; + swapi(&p0.x, &p0.y); + swapi(&p1.x, &p1.y); + } + + /* make them left-to-right */ + if(p0.x > p1.x){ + swapi(&p0.x, &p1.x); + swapi(&p0.y, &p1.y); + } + + dp = subpt(p1, p0); + Δe = 2*abs(dp.y); + e = 0; + Δy = p1.y > p0.y? 1: -1; + + /* TODO find out why sometimes lines go invisible depending on their location */ - z = fberp(prim.v[0].p.z, prim.v[1].p.z, prim.v[2].p.z, bc); + for(p = p0; p.x <= p1.x; p.x++){ + Δp = subpt(p, p0); + dplen = hypot(dp.x, dp.y); + perc = dplen == 0? 0: hypot(Δp.x, Δp.y)/dplen; + + if(steep) swapi(&p.x, &p.y); + + z = flerp(prim.v[0].p.z, prim.v[1].p.z, perc); depth = fclamp(z, 0, 1); if(depth <= params->fb->zb[p.x + p.y*Dx(params->fb->r)]) - continue; + break; params->fb->zb[p.x + p.y*Dx(params->fb->r)] = depth; /* interpolate z⁻¹ and get actual z */ - z = fberp(prim.v[0].p.w, prim.v[1].p.w, prim.v[2].p.w, bc); + z = flerp(prim.v[0].p.w, prim.v[1].p.w, perc); z = 1.0/(z < 1e-5? 1e-5: z); /* perspective-correct attribute interpolation */ - bc.x *= prim.v[0].p.w; - bc.y *= prim.v[1].p.w; - bc.z *= prim.v[2].p.w; - bc = mulpt3(bc, z); - berpvertex(&fsp.v, &prim.v[0], &prim.v[1], &prim.v[2], bc); + perc *= prim.v[0].p.w * z; + lerpvertex(&fsp.v, &prim.v[0], &prim.v[1], perc); fsp.p = p; c = params->fshader(&fsp); @@ -352,7 +254,62 @@ rasterize(Rastertask *task) pixel(params->fb->cb, p, params->frag); delvattrs(&fsp.v); + + if(steep) swapi(&p.x, &p.y); + + e += Δe; + if(e > dp.x){ + p.y += Δy; + e -= 2*dp.x; + } } + break; + case PTriangle: + t.p0 = Pt2(prim.v[0].p.x, prim.v[0].p.y, 1); + t.p1 = Pt2(prim.v[1].p.x, prim.v[1].p.y, 1); + t.p2 = Pt2(prim.v[2].p.x, prim.v[2].p.y, 1); + /* find the triangle's bbox and clip it against our wr */ + bbox.min.x = min(min(t.p0.x, t.p1.x), t.p2.x); + bbox.min.y = min(min(t.p0.y, t.p1.y), t.p2.y); + bbox.max.x = max(max(t.p0.x, t.p1.x), t.p2.x)+1; + bbox.max.y = max(max(t.p0.y, t.p1.y), t.p2.y)+1; + bbox.min.x = max(bbox.min.x, task->wr.min.x); + bbox.min.y = max(bbox.min.y, task->wr.min.y); + bbox.max.x = min(bbox.max.x, task->wr.max.x); + bbox.max.y = min(bbox.max.y, task->wr.max.y); + + for(p.y = bbox.min.y; p.y < bbox.max.y; p.y++) + for(p.x = bbox.min.x; p.x < bbox.max.x; p.x++){ + bc = barycoords(t, Pt2(p.x,p.y,1)); + if(bc.x < 0 || bc.y < 0 || bc.z < 0) + continue; + + z = fberp(prim.v[0].p.z, prim.v[1].p.z, prim.v[2].p.z, bc); + depth = fclamp(z, 0, 1); + if(depth <= params->fb->zb[p.x + p.y*Dx(params->fb->r)]) + continue; + params->fb->zb[p.x + p.y*Dx(params->fb->r)] = depth; + + /* interpolate z⁻¹ and get actual z */ + z = fberp(prim.v[0].p.w, prim.v[1].p.w, prim.v[2].p.w, bc); + z = 1.0/(z < 1e-5? 1e-5: z); + + /* perspective-correct attribute interpolation */ + bc.x *= prim.v[0].p.w; + bc.y *= prim.v[1].p.w; + bc.z *= prim.v[2].p.w; + bc = mulpt3(bc, z); + berpvertex(&fsp.v, &prim.v[0], &prim.v[1], &prim.v[2], bc); + + fsp.p = p; + c = params->fshader(&fsp); + memfillcolor(params->frag, col2ul(c)); + + pixel(params->fb->cb, p, params->frag); + delvattrs(&fsp.v); + } + break; + } } static void @@ -363,6 +320,7 @@ rasterizer(void *arg) SUparams *params; Memimage *frag; uvlong t0; + int i; rp = arg; frag = rgb(DBlack); @@ -389,9 +347,8 @@ rasterizer(void *arg) params->frag = frag; rasterize(task); - delvattrs(&task->p.v[0]); - delvattrs(&task->p.v[1]); - delvattrs(&task->p.v[2]); + for(i = 0; i < task->p.type+1; i++) + delvattrs(&task->p.v[i]); params->job->times.Rn.t1 = nanosec(); free(params); free(task); @@ -405,11 +362,7 @@ tilerdurden(void *arg) SUparams *params, *newparams; Rastertask *task; VSparams vsp; - OBJVertex *verts, *tverts, *nverts; /* geometric, texture and normals vertices */ - OBJIndexArray *idxtab; - OBJElem **ep; - Point3 n; /* surface normal */ - Primitive *p; /* primitives to raster */ + Primitive *ep, *p; /* primitives to raster */ Rectangle *wr, bbox; Channel **taskchans; ulong Δy, nproc; @@ -452,98 +405,31 @@ tilerdurden(void *arg) if(wr[nproc-1].max.y < params->fb->r.max.y) wr[nproc-1].max.y = params->fb->r.max.y; - verts = params->entity->mdl->obj->vertdata[OBJVGeometric].verts; - tverts = params->entity->mdl->obj->vertdata[OBJVTexture].verts; - nverts = params->entity->mdl->obj->vertdata[OBJVNormal].verts; - for(ep = params->eb; ep != params->ee; ep++){ np = 1; /* start with one. after clipping it might change */ - /* TODO handle all the primitive types */ - - idxtab = &(*ep)->indextab[OBJVGeometric]; - p[0].v[0].p = Pt3(verts[idxtab->indices[0]].x, - verts[idxtab->indices[0]].y, - verts[idxtab->indices[0]].z, - verts[idxtab->indices[0]].w); - p[0].v[1].p = Pt3(verts[idxtab->indices[1]].x, - verts[idxtab->indices[1]].y, - verts[idxtab->indices[1]].z, - verts[idxtab->indices[1]].w); - p[0].v[2].p = Pt3(verts[idxtab->indices[2]].x, - verts[idxtab->indices[2]].y, - verts[idxtab->indices[2]].z, - verts[idxtab->indices[2]].w); - - idxtab = &(*ep)->indextab[OBJVNormal]; - if(idxtab->nindex == 3){ - p[0].v[0].n = Vec3(nverts[idxtab->indices[0]].i, - nverts[idxtab->indices[0]].j, - nverts[idxtab->indices[0]].k); - p[0].v[0].n = normvec3(p[0].v[0].n); - p[0].v[1].n = Vec3(nverts[idxtab->indices[1]].i, - nverts[idxtab->indices[1]].j, - nverts[idxtab->indices[1]].k); - p[0].v[1].n = normvec3(p[0].v[1].n); - p[0].v[2].n = Vec3(nverts[idxtab->indices[2]].i, - nverts[idxtab->indices[2]].j, - nverts[idxtab->indices[2]].k); - p[0].v[2].n = normvec3(p[0].v[2].n); - }else{ - /* TODO build a list of per-vertex normals earlier */ - n = normvec3(crossvec3(subpt3(p[0].v[1].p, p[0].v[0].p), subpt3(p[0].v[2].p, p[0].v[0].p))); - p[0].v[0].n = p[0].v[1].n = p[0].v[2].n = n; - } + memmove(p, ep, sizeof *p); + switch(ep->type){ + case PPoint: + p[0].v[0].c = Pt3(1,1,1,1); + p[0].v[0].mtl = ep->mtl; + p[0].v[0].attrs = nil; + p[0].v[0].nattrs = 0; - idxtab = &(*ep)->indextab[OBJVTexture]; - if(idxtab->nindex == 3){ - p[0].v[0].uv = Pt2(tverts[idxtab->indices[0]].u, - tverts[idxtab->indices[0]].v, 1); - p[0].v[1].uv = Pt2(tverts[idxtab->indices[1]].u, - tverts[idxtab->indices[1]].v, 1); - p[0].v[2].uv = Pt2(tverts[idxtab->indices[2]].u, - tverts[idxtab->indices[2]].v, 1); - }else{ - p[0].v[0].uv = p[0].v[1].uv = p[0].v[2].uv = Vec2(0,0); - } + vsp.v = &p[0].v[0]; + vsp.idx = 0; + p[0].v[0].p = params->vshader(&vsp); - for(i = 0; i < 3; i++){ - p[0].v[i].c = Pt3(1,1,1,1); - p[0].v[i].mtl = (*ep)->mtl; - p[0].v[i].attrs = nil; - p[0].v[i].nattrs = 0; - } + if(!isvisible(p[0].v[0].p)) + break; - vsp.v = &p[0].v[0]; - vsp.idx = 0; - p[0].v[0].p = params->vshader(&vsp); - vsp.v = &p[0].v[1]; - vsp.idx = 1; - p[0].v[1].p = params->vshader(&vsp); - vsp.v = &p[0].v[2]; - vsp.idx = 2; - p[0].v[2].p = params->vshader(&vsp); - - if(!isvisible(p[0].v[0].p) || !isvisible(p[0].v[1].p) || !isvisible(p[0].v[2].p)) - np = cliptriangle(p); - - while(np--){ - p[np].v[0].p = clip2ndc(p[np].v[0].p); - p[np].v[1].p = clip2ndc(p[np].v[1].p); - p[np].v[2].p = clip2ndc(p[np].v[2].p); - - /* culling */ -// if(isfacingback(p[np])) -// goto skiptri; - - p[np].v[0].p = ndc2viewport(params->fb, p[np].v[0].p); - p[np].v[1].p = ndc2viewport(params->fb, p[np].v[1].p); - p[np].v[2].p = ndc2viewport(params->fb, p[np].v[2].p); - - bbox.min.x = min(min(p[np].v[0].p.x, p[np].v[1].p.x), p[np].v[2].p.x); - bbox.min.y = min(min(p[np].v[0].p.y, p[np].v[1].p.y), p[np].v[2].p.y); - bbox.max.x = max(max(p[np].v[0].p.x, p[np].v[1].p.x), p[np].v[2].p.x)+1; - bbox.max.y = max(max(p[np].v[0].p.y, p[np].v[1].p.y), p[np].v[2].p.y)+1; + p[0].v[0].p = clip2ndc(p[0].v[0].p); + p[0].v[0].p = ndc2viewport(params->fb, p[0].v[0].p); + + bbox.min.x = p[0].v[0].p.x; + bbox.min.y = p[0].v[0].p.y; + bbox.max.x = p[0].v[0].p.x+1; + bbox.max.y = p[0].v[0].p.y+1; for(i = 0; i < nproc; i++) if(rectXrect(bbox,wr[i])){ @@ -552,15 +438,112 @@ tilerdurden(void *arg) task = emalloc(sizeof *task); task->params = newparams; task->wr = wr[i]; - task->p.v[0] = dupvertex(&p[np].v[0]); - task->p.v[1] = dupvertex(&p[np].v[1]); - task->p.v[2] = dupvertex(&p[np].v[2]); + memmove(&task->p, &p[0], sizeof task->p); + task->p.v[0] = dupvertex(&p[0].v[0]); sendp(taskchans[i], task); } + delvattrs(&p[0].v[0]); + break; + case PLine: + for(i = 0; i < 2; i++){ + p[0].v[i].c = Pt3(1,1,1,1); + p[0].v[i].mtl = ep->mtl; + p[0].v[i].attrs = nil; + p[0].v[i].nattrs = 0; + + vsp.v = &p[0].v[i]; + vsp.idx = i; + p[0].v[i].p = params->vshader(&vsp); + } + + if(!isvisible(p[0].v[0].p) || !isvisible(p[0].v[1].p)) + np = clipprimitive(p); + + while(np--){ + p[np].v[0].p = clip2ndc(p[np].v[0].p); + p[np].v[1].p = clip2ndc(p[np].v[1].p); + + /* culling */ +// if(isfacingback(p[np])) +// goto skiptri2; + + p[np].v[0].p = ndc2viewport(params->fb, p[np].v[0].p); + p[np].v[1].p = ndc2viewport(params->fb, p[np].v[1].p); + + bbox.min.x = min(p[np].v[0].p.x, p[np].v[1].p.x); + bbox.min.y = min(p[np].v[0].p.y, p[np].v[1].p.y); + bbox.max.x = max(p[np].v[0].p.x, p[np].v[1].p.x)+1; + bbox.max.y = max(p[np].v[0].p.y, p[np].v[1].p.y)+1; + + for(i = 0; i < nproc; i++) + if(rectXrect(bbox,wr[i])){ + newparams = emalloc(sizeof *newparams); + *newparams = *params; + task = emalloc(sizeof *task); + task->params = newparams; + task->wr = wr[i]; + memmove(&task->p, &p[np], sizeof task->p); + task->p.v[0] = dupvertex(&p[np].v[0]); + task->p.v[1] = dupvertex(&p[np].v[1]); + sendp(taskchans[i], task); + } +//skiptri2: + delvattrs(&p[np].v[0]); + delvattrs(&p[np].v[1]); + } + break; + case PTriangle: + for(i = 0; i < 3; i++){ + p[0].v[i].c = Pt3(1,1,1,1); + p[0].v[i].mtl = p->mtl; + p[0].v[i].attrs = nil; + p[0].v[i].nattrs = 0; + + vsp.v = &p[0].v[i]; + vsp.idx = i; + p[0].v[i].p = params->vshader(&vsp); + } + + if(!isvisible(p[0].v[0].p) || !isvisible(p[0].v[1].p) || !isvisible(p[0].v[2].p)) + np = clipprimitive(p); + + while(np--){ + p[np].v[0].p = clip2ndc(p[np].v[0].p); + p[np].v[1].p = clip2ndc(p[np].v[1].p); + p[np].v[2].p = clip2ndc(p[np].v[2].p); + + /* culling */ +// if(isfacingback(p[np])) +// goto skiptri; + + p[np].v[0].p = ndc2viewport(params->fb, p[np].v[0].p); + p[np].v[1].p = ndc2viewport(params->fb, p[np].v[1].p); + p[np].v[2].p = ndc2viewport(params->fb, p[np].v[2].p); + + bbox.min.x = min(min(p[np].v[0].p.x, p[np].v[1].p.x), p[np].v[2].p.x); + bbox.min.y = min(min(p[np].v[0].p.y, p[np].v[1].p.y), p[np].v[2].p.y); + bbox.max.x = max(max(p[np].v[0].p.x, p[np].v[1].p.x), p[np].v[2].p.x)+1; + bbox.max.y = max(max(p[np].v[0].p.y, p[np].v[1].p.y), p[np].v[2].p.y)+1; + + for(i = 0; i < nproc; i++) + if(rectXrect(bbox,wr[i])){ + newparams = emalloc(sizeof *newparams); + *newparams = *params; + task = emalloc(sizeof *task); + task->params = newparams; + task->wr = wr[i]; + memmove(&task->p, &p[np], sizeof task->p); + task->p.v[0] = dupvertex(&p[np].v[0]); + task->p.v[1] = dupvertex(&p[np].v[1]); + task->p.v[2] = dupvertex(&p[np].v[2]); + sendp(taskchans[i], task); + } //skiptri: - delvattrs(&p[np].v[0]); - delvattrs(&p[np].v[1]); - delvattrs(&p[np].v[2]); + delvattrs(&p[np].v[0]); + delvattrs(&p[np].v[1]); + delvattrs(&p[np].v[2]); + } + break; } } params->job->times.Tn.t1 = nanosec(); @@ -575,9 +558,9 @@ entityproc(void *arg) Tilerparam *tp; Rasterparam *rp; SUparams *params, *newparams; - OBJElem **eb, **ee; + Primitive *eb, *ee; char *nprocs; - ulong stride, nelems, nproc, nworkers; + ulong stride, nprims, nproc, nworkers; int i; uvlong t0; @@ -622,16 +605,16 @@ entityproc(void *arg) continue; } - eb = params->entity->mdl->elems; - nelems = params->entity->mdl->nelems; - ee = eb + nelems; + eb = params->entity->mdl->prims; + nprims = params->entity->mdl->nprims; + ee = eb + nprims; - if(nelems <= nproc){ - nworkers = nelems; + if(nprims <= nproc){ + nworkers = nprims; stride = 1; }else{ nworkers = nproc; - stride = nelems/nproc; + stride = nprims/nproc; } for(i = 0; i < nworkers; i++){ diff --git a/scene.c b/scene.c index 0984023..a430183 100644 --- a/scene.c +++ b/scene.c @@ -20,16 +20,16 @@ triangulate(OBJElem **newe, OBJElem *e) { OBJIndexArray *newidxtab; OBJIndexArray *idxtab; - int i, nt; + int i; - nt = 0; idxtab = &e->indextab[OBJVGeometric]; for(i = 0; i < idxtab->nindex-2; i++){ idxtab = &e->indextab[OBJVGeometric]; - newe[nt++] = emalloc(sizeof **newe); - newe[nt-1]->type = OBJEFace; - newe[nt-1]->mtl = e->mtl; - newidxtab = &newe[nt-1]->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]; @@ -37,7 +37,7 @@ triangulate(OBJElem **newe, OBJElem *e) newidxtab->indices[2] = idxtab->indices[i+2]; idxtab = &e->indextab[OBJVTexture]; if(idxtab->nindex > 0){ - newidxtab = &newe[nt-1]->indextab[OBJVTexture]; + newidxtab = &newe[i]->indextab[OBJVTexture]; newidxtab->nindex = 3; newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices)); newidxtab->indices[0] = idxtab->indices[0]; @@ -46,7 +46,7 @@ triangulate(OBJElem **newe, OBJElem *e) } idxtab = &e->indextab[OBJVNormal]; if(idxtab->nindex > 0){ - newidxtab = &newe[nt-1]->indextab[OBJVNormal]; + newidxtab = &newe[i]->indextab[OBJVNormal]; newidxtab->nindex = 3; newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices)); newidxtab->indices[0] = idxtab->indices[0]; @@ -55,53 +55,236 @@ triangulate(OBJElem **newe, OBJElem *e) } } - return nt; + 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); + } } -/* - * rebuild the cache of renderable OBJElems. - * - * run it every time the Model's geometry changes. - */ int -refreshmodel(Model *m) +loadobjmodel(Model *m, OBJ *obj) { - OBJElem **trielems; + Primitive *p; + OBJVertex *pverts, *tverts, *nverts, *v; /* geometric, texture and normals vertices */ + OBJElem **trielems, *e, *ne; OBJObject *o; - OBJElem *e; OBJIndexArray *idxtab; - int i, nt; + OBJ2MtlMap mtlmap; + OBJMaterial *objmtl; + Material *mtl; + Point3 n; /* surface normal */ + int i, idx, nt, maxnt, neednormal, gottaclean; - if(m->obj == nil) + if(obj == nil) return 0; - if(m->elems != nil){ - free(m->elems); - m->elems = nil; + 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->nelems = 0; - for(i = 0; i < nelem(m->obj->objtab); i++) - for(o = m->obj->objtab[i]; o != nil; o = o->next) - for(e = o->child; e != nil; e = e->next){ - idxtab = &e->indextab[OBJVGeometric]; - /* discard non-surfaces */ - if(e->type != OBJEFace || idxtab->nindex < 3) - continue; - if(idxtab->nindex > 3){ + 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); + + mtl->ambient.r = objmtl->Ka.r; + mtl->ambient.g = objmtl->Ka.g; + mtl->ambient.b = objmtl->Ka.b; + mtl->ambient.a = 1; + mtl->diffuse.r = objmtl->Kd.r; + mtl->diffuse.g = objmtl->Kd.g; + mtl->diffuse.b = objmtl->Kd.b; + mtl->diffuse.a = 1; + mtl->specular.r = objmtl->Ks.r; + mtl->specular.g = objmtl->Ks.g; + mtl->specular.b = objmtl->Ks.b; + mtl->specular.a = 1; + mtl->shininess = objmtl->Ns; + + if(objmtl->map_Kd != nil){ + mtl->diffusemap = allocmemimaged(objmtl->map_Kd->r, objmtl->map_Kd->chan, objmtl->map_Kd->data); + if(mtl->diffusemap == nil) + sysfatal("allocmemimaged: %r"); + mtl->diffusemap->data->ref++; + } + + 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 */ - trielems = emalloc((idxtab->nindex-2)*sizeof(*trielems)); - nt = triangulate(trielems, e); - m->nelems += nt; - m->elems = erealloc(m->elems, m->nelems*sizeof(*m->elems)); - while(nt-- > 0) - m->elems[m->nelems-nt-1] = trielems[nt]; - free(trielems); - }else{ - m->elems = erealloc(m->elems, ++m->nelems*sizeof(*m->elems)); - m->elems[m->nelems-1] = e; + 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(neednormal){ + /* TODO build a list of per-vertex normals earlier */ + 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; } } - return m->nelems; + + free(trielems); + clrmtlmap(&mtlmap); + return m->nprims; } Model * @@ -119,14 +302,12 @@ delmodel(Model *m) { if(m == nil) return; - if(m->obj != nil) - objfree(m->obj); if(m->tex != nil) freememimage(m->tex); if(m->nor != nil) freememimage(m->nor); - if(m->nelems > 0) - free(m->elems); + if(m->nprims > 0) + free(m->prims); free(m); } diff --git a/util.c b/util.c index 0cd4354..e114da5 100644 --- a/util.c +++ b/util.c @@ -32,6 +32,26 @@ fmax(double a, double b) return a > b? a: b; } +void +swapi(int *a, int *b) +{ + int t; + + t = *a; + *a = *b; + *b = t; +} + +void +swappt(Point *a, Point *b) +{ + Point t; + + t = *a; + *a = *b; + *b = t; +} + void memsetd(double *p, double v, usize len) { -- cgit v1.2.3