summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrodri <rgl@antares-labs.eu>2024-05-03 13:32:48 +0000
committerrodri <rgl@antares-labs.eu>2024-05-03 13:32:48 +0000
commitb429311ed087ee7cf7fc7771a8e1834ea074f8da (patch)
treeef05d69723ebcdf89a2d8a25b069075b2b51fc63
parentcf3e0cb65c03955ef18624a50b9e33c3f6e8c6e5 (diff)
downloadlibgraphics-b429311ed087ee7cf7fc7771a8e1834ea074f8da.tar.gz
libgraphics-b429311ed087ee7cf7fc7771a8e1834ea074f8da.tar.bz2
libgraphics-b429311ed087ee7cf7fc7771a8e1834ea074f8da.zip
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.
-rw-r--r--clip.c220
-rw-r--r--doc/libgraphics.ms1
-rw-r--r--doc/libgraphics.pdfbin19045 -> 19735 bytes
-rw-r--r--doc/libgraphics.ps126
-rw-r--r--graphics.h15
-rw-r--r--internal.h14
-rw-r--r--mkfile1
-rw-r--r--render.c511
-rw-r--r--scene.c273
-rw-r--r--util.c20
10 files changed, 863 insertions, 318 deletions
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 <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"
+
+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
--- a/doc/libgraphics.pdf
+++ b/doc/libgraphics.pdf
Binary files 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
@@ -33,6 +33,26 @@ fmax(double a, double 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)
{
double *dp;