diff options
Diffstat (limited to 'vis.c')
-rw-r--r-- | vis.c | 336 |
1 files changed, 211 insertions, 125 deletions
@@ -40,6 +40,14 @@ Rune keys[Ke] = { [Kcam3] = KF|4, [Khud] = 'h', }; +char *skyboxpaths[] = { + "cubemap/skybox/left.pic", + "cubemap/skybox/right.pic", + "cubemap/skybox/bottom.pic", + "cubemap/skybox/top.pic", + "cubemap/skybox/front.pic", + "cubemap/skybox/back.pic", +}; char stats[Se][256]; Image *screenb; Mousectl *mctl; @@ -48,9 +56,10 @@ Channel *drawc; int kdown; Shadertab *shader; Model *model; -Entity *subject; Scene *scene; -double θ, ω = 0; +QLock drawlk; +Mouse om; +Quaternion orient = {1,0,0,0}; Camera cams[4], *maincam; Camcfg camcfgs[4] = { @@ -77,9 +86,11 @@ Camcfg camcfgs[4] = { Point3 center = {0,0,0,1}; LightSource light; /* global point light */ +static int showskybox; static int doprof; static int inception; static int showhud; +static int shownormals; Color (*tsampler)(Memimage*,Point2); static int @@ -94,21 +105,17 @@ max(int a, int b) return a > b? a: b; } -//void -//drawaxis(void) -//{ -// Point3 op = Pt3(0,0,0,1), -// px = Pt3(1,0,0,1), -// py = Pt3(0,1,0,1), -// pz = Pt3(0,0,1,1); -// -// line3(maincam, op, px, 0, Endarrow, display->black); -// string3(maincam, px, display->black, font, "x"); -// line3(maincam, op, py, 0, Endarrow, display->black); -// string3(maincam, py, display->black, font, "y"); -// line3(maincam, op, pz, 0, Endarrow, display->black); -// string3(maincam, pz, display->black, font, "z"); -//} +static Point3 +Vecquat(Quaternion q) +{ + return Vec3(q.i, q.j, q.k); +} + +static Point3 +Ptquat(Quaternion q, double w) +{ + return Pt3(q.i, q.j, q.k, w); +} Point3 gouraudvshader(VSparams *sp) @@ -118,41 +125,35 @@ gouraudvshader(VSparams *sp) double Kd; /* diffuse factor */ double spec; Point3 pos, lightdir, lookdir; - Material m; - Color ambient, diffuse, specular; + Material *m; + Color ambient, diffuse, specular, lightc; - sp->v->n = qrotate(sp->v->n, Vec3(0,1,0), θ+fmod(ω*sp->su->uni_time/1e9, 2*PI)); - sp->v->p = qrotate(sp->v->p, Vec3(0,1,0), θ+fmod(ω*sp->su->uni_time/1e9, 2*PI)); - pos = model2world(sp->su->entity, sp->v->p); - if(sp->v->mtl != nil){ - m = *sp->v->mtl; - - ambient = mulpt3(light.c, Ka); - ambient.r *= m.ambient.r; - ambient.g *= m.ambient.g; - ambient.b *= m.ambient.b; - ambient.a *= m.ambient.a; - - lightdir = normvec3(subpt3(light.p, pos)); - Kd = fmax(0, dotvec3(sp->v->n, lightdir)); - diffuse = mulpt3(light.c, Kd); - diffuse.r *= m.diffuse.r; - diffuse.g *= m.diffuse.g; - diffuse.b *= m.diffuse.b; - diffuse.a *= m.diffuse.a; - - lookdir = normvec3(subpt3(maincam->p, pos)); - lightdir = qrotate(lightdir, sp->v->n, PI); - spec = pow(fmax(0, dotvec3(lookdir, lightdir)), m.shininess); - specular = mulpt3(light.c, spec*Ks); - specular.r *= m.specular.r; - specular.g *= m.specular.g; - specular.b *= m.specular.b; - specular.a *= m.specular.a; - - sp->v->c = addpt3(ambient, addpt3(diffuse, specular)); - } - return world2clip(maincam, pos); + sp->v->n = model2world(sp->su->entity, sp->v->n); + sp->v->p = model2world(sp->su->entity, sp->v->p); + pos = sp->v->p; + m = sp->v->mtl; + + lightdir = normvec3(subpt3(light.p, pos)); + lightc = getlightcolor(&light, lightdir); + + ambient = mulpt3(lightc, Ka); + if(m != nil) + ambient = modulapt3(ambient, m->ambient); + + Kd = fmax(0, dotvec3(sp->v->n, lightdir)); + diffuse = mulpt3(lightc, Kd); + if(m != nil) + diffuse = modulapt3(diffuse, m->diffuse); + + lookdir = normvec3(subpt3(sp->su->camera->p, pos)); + lightdir = qrotate(lightdir, sp->v->n, PI); + spec = pow(fmax(0, dotvec3(lookdir, lightdir)), m? m->shininess: 1); + specular = mulpt3(lightc, spec*Ks); + if(m != nil) + specular = modulapt3(specular, m->specular); + + sp->v->c = addpt3(ambient, addpt3(diffuse, specular)); + return world2clip(sp->su->camera, pos); } Color @@ -167,10 +168,8 @@ gouraudshader(FSparams *sp) else tc = Pt3(1,1,1,1); + c = modulapt3(sp->v.c, tc); c.a = 1; - c.b = fclamp(sp->v.c.b*tc.b, 0, 1); - c.g = fclamp(sp->v.c.g*tc.g, 0, 1); - c.r = fclamp(sp->v.c.r*tc.r, 0, 1); return c; } @@ -182,10 +181,14 @@ phongvshader(VSparams *sp) Color a, d, s; double ss; - sp->v->n = qrotate(sp->v->n, Vec3(0,1,0), θ+fmod(ω*sp->su->uni_time/1e9, 2*PI)); - sp->v->p = qrotate(sp->v->p, Vec3(0,1,0), θ+fmod(ω*sp->su->uni_time/1e9, 2*PI)); - pos = model2world(sp->su->entity, sp->v->p); + sp->v->n = model2world(sp->su->entity, sp->v->n); + sp->v->p = model2world(sp->su->entity, sp->v->p); + pos = sp->v->p; addvattr(sp->v, "pos", VAPoint, &pos); + if(sp->v->mtl != nil && sp->v->mtl->normalmap != nil && sp->v->uv.w != 0){ + sp->v->tangent = model2world(sp->su->entity, sp->v->tangent); + addvattr(sp->v, "tangent", VAPoint, &sp->v->tangent); + } if(sp->v->mtl != nil){ a = sp->v->mtl->ambient; d = sp->v->mtl->diffuse; @@ -196,7 +199,7 @@ phongvshader(VSparams *sp) addvattr(sp->v, "specular", VAPoint, &s); addvattr(sp->v, "shininess", VANumber, &ss); } - return world2clip(maincam, pos); + return world2clip(sp->su->camera, pos); } Color @@ -206,9 +209,10 @@ phongshader(FSparams *sp) static double Ks = 0.5; /* specular factor */ double Kd; /* diffuse factor */ double spec; - Color ambient, diffuse, specular, tc, c; - Point3 pos, lookdir, lightdir; + Color ambient, diffuse, specular, tc, c, lightc; + Point3 pos, n, lightdir, lookdir; Material m; + RFrame3 TBN; Vertexattr *va; va = getvattr(&sp->v, "pos"); @@ -223,28 +227,38 @@ phongshader(FSparams *sp) va = getvattr(&sp->v, "shininess"); m.shininess = va != nil? va->n: 1; - ambient = mulpt3(light.c, Ka); - ambient.r *= m.ambient.r; - ambient.g *= m.ambient.g; - ambient.b *= m.ambient.b; - ambient.a *= m.ambient.a; - lightdir = normvec3(subpt3(light.p, pos)); - Kd = fmax(0, dotvec3(sp->v.n, lightdir)); - diffuse = mulpt3(light.c, Kd); - diffuse.r *= m.diffuse.r; - diffuse.g *= m.diffuse.g; - diffuse.b *= m.diffuse.b; - diffuse.a *= m.diffuse.a; - - lookdir = normvec3(subpt3(maincam->p, pos)); - lightdir = qrotate(lightdir, sp->v.n, PI); + lightc = getlightcolor(&light, lightdir); + + ambient = mulpt3(lightc, Ka); + ambient = modulapt3(ambient, m.ambient); + + /* normal mapping */ + va = getvattr(&sp->v, "tangent"); + if(va == nil) + n = sp->v.n; + else{ + /* TODO implement this on the VS instead and apply Gram-Schmidt here */ + n = texture(sp->v.mtl->normalmap, sp->v.uv, neartexsampler); + n = normvec3(subpt3(mulpt3(n, 2), Vec3(1,1,1))); + + TBN.p = Pt3(0,0,0,1); + TBN.bx = va->p; /* T */ + TBN.bz = sp->v.n; /* N */ + TBN.by = crossvec3(TBN.bz, TBN.bx); /* B */ + + n = normvec3(invrframexform3(n, TBN)); + } + + Kd = fmax(0, dotvec3(n, lightdir)); + diffuse = mulpt3(lightc, Kd); + diffuse = modulapt3(diffuse, m.diffuse); + + lookdir = normvec3(subpt3(sp->su->camera->p, pos)); + lightdir = qrotate(lightdir, n, PI); spec = pow(fmax(0, dotvec3(lookdir, lightdir)), m.shininess); - specular = mulpt3(light.c, spec*Ks); - specular.r *= m.specular.r; - specular.g *= m.specular.g; - specular.b *= m.specular.b; - specular.a *= m.specular.a; + specular = mulpt3(lightc, spec*Ks); + specular = modulapt3(specular, m.specular); if(sp->v.mtl != nil && sp->v.mtl->diffusemap != nil && sp->v.uv.w != 0) tc = texture(sp->v.mtl->diffusemap, sp->v.uv, tsampler); @@ -254,10 +268,8 @@ phongshader(FSparams *sp) tc = Pt3(1,1,1,1); c = addpt3(ambient, addpt3(diffuse, specular)); + c = modulapt3(c, tc); c.a = 1; - c.b = fclamp(c.b*tc.b, 0, 1); - c.g = fclamp(c.g*tc.g, 0, 1); - c.r = fclamp(c.r*tc.r, 0, 1); return c; } @@ -268,13 +280,15 @@ identvshader(VSparams *sp) Point3 pos, lightdir; double intens; - pos = model2world(sp->su->entity, sp->v->p); + sp->v->n = model2world(sp->su->entity, sp->v->n); + sp->v->p = model2world(sp->su->entity, sp->v->p); + pos = sp->v->p; lightdir = normvec3(subpt3(light.p, pos)); intens = fmax(0, dotvec3(sp->v->n, lightdir)); addvattr(sp->v, "intensity", VANumber, &intens); if(sp->v->mtl != nil) sp->v->c = sp->v->mtl->diffuse; - return world2clip(maincam, pos); + return world2clip(sp->su->camera, pos); } Color @@ -302,10 +316,8 @@ identshader(FSparams *sp) else tc = Pt3(1,1,1,1); + c = modulapt3(sp->v.c, tc); c.a = 1; - c.b = fclamp(sp->v.c.b*tc.b, 0, 1); - c.g = fclamp(sp->v.c.g*tc.g, 0, 1); - c.r = fclamp(sp->v.c.r*tc.r, 0, 1); return c; } @@ -313,7 +325,9 @@ identshader(FSparams *sp) Point3 ivshader(VSparams *sp) { - return world2clip(maincam, model2world(sp->su->entity, sp->v->p)); + sp->v->n = model2world(sp->su->entity, sp->v->n); + sp->v->p = model2world(sp->su->entity, sp->v->p); + return world2clip(sp->su->camera, sp->v->p); } Color @@ -428,14 +442,14 @@ getshader(char *name) void zoomin(void) { - maincam->fov = fclamp(maincam->fov - 1*DEG, 1*DEG, 359*DEG); + maincam->fov = fclamp(maincam->fov - 1*DEG, 1*DEG, 180*DEG); reloadcamera(maincam); } void zoomout(void) { - maincam->fov = fclamp(maincam->fov + 1*DEG, 1*DEG, 359*DEG); + maincam->fov = fclamp(maincam->fov + 1*DEG, 1*DEG, 180*DEG); reloadcamera(maincam); } @@ -452,6 +466,7 @@ drawstats(void) snprint(stats[Scambz], sizeof(stats[Scambz]), "bz %V", maincam->bz); snprint(stats[Sfps], sizeof(stats[Sfps]), "FPS %.0f/%.0f/%.0f/%.0f", !maincam->stats.max? 0: 1e9/maincam->stats.max, !maincam->stats.avg? 0: 1e9/maincam->stats.avg, !maincam->stats.min? 0: 1e9/maincam->stats.min, !maincam->stats.v? 0: 1e9/maincam->stats.v); snprint(stats[Sframes], sizeof(stats[Sframes]), "frame %llud", maincam->stats.nframes); + snprint(stats[Sorient], sizeof(stats[Sorient]), "ℍ %V", (Point3)orient); for(i = 0; i < Se; i++) stringbg(screen, addpt(screen->r.min, Pt(10,10 + i*font->height)), display->black, ZP, font, stats[i], display->white, ZP); } @@ -465,10 +480,12 @@ redraw(void) bg = eallocimage(display, UR, RGB24, 1, 0x888888FF); lockdisplay(display); - maincam->vp->draw(maincam->vp, screenb); + if(shownormals) + maincam->vp->fbctl->drawnormals(maincam->vp->fbctl, screenb); + else + maincam->vp->draw(maincam->vp, screenb); draw(screen, screen->r, bg, nil, ZP); draw(screen, screen->r, screenb, nil, ZP); -// drawaxis(); if(showhud) drawstats(); flushimage(display, 1); @@ -476,12 +493,12 @@ redraw(void) } void -drawproc(void *) +renderproc(void *) { uvlong t0, Δt; int fd; - threadsetname("drawproc"); + threadsetname("renderproc"); fd = -1; if(inception){ @@ -496,6 +513,12 @@ drawproc(void *) t0 = nsec(); for(;;){ shootcamera(maincam, shader); + if(doprof) + fprint(2, "R %llud %llud\nE %llud %llud\nT %llud %llud\nr %llud %llud\n\n", + maincam->times.R[maincam->times.cur-1].t0, maincam->times.R[maincam->times.cur-1].t1, + maincam->times.E[maincam->times.cur-1].t0, maincam->times.E[maincam->times.cur-1].t1, + maincam->times.Tn[maincam->times.cur-1].t0, maincam->times.Tn[maincam->times.cur-1].t1, + maincam->times.Rn[maincam->times.cur-1].t0, maincam->times.Rn[maincam->times.cur-1].t1); Δt = nsec() - t0; if(Δt > HZ2MS(60)*1000000ULL){ nbsend(drawc, nil); @@ -506,37 +529,97 @@ drawproc(void *) if((model->tex = readmemimage(fd)) == nil) sysfatal("readmemimage: %r"); } - light.p = qrotate(light.p, Vec3(0,1,0), θ+fmod(ω*Δt/1e9, 2*PI)); } } } void +drawproc(void *) +{ + threadsetname("drawproc"); + + for(;;) + if(recv(drawc, nil) && canqlock(&drawlk)){ + redraw(); + qunlock(&drawlk); + } +} + +static Color +ul2col(ulong l) +{ + Color c; + + c.a = (l & 0xff)/255.0; + c.b = (l>>8 & 0xff)/255.0; + c.g = (l>>16 & 0xff)/255.0; + c.r = (l>>24 & 0xff)/255.0; + return c; +} + +void +lmb(void) +{ + Entity *e; + Quaternion Δorient; + + if((om.buttons^mctl->buttons) == 0){ + Δorient = orient; + qball(screen->r, om.xy, mctl->xy, &orient, nil); + Δorient = mulq(orient, invq(Δorient)); + + for(e = scene->ents.next; e != &scene->ents; e = e->next){ + e->bx = vcs2world(maincam, Vecquat(mulq(mulq(Δorient, Quatvec(0, world2vcs(maincam, e->bx))), invq(Δorient)))); + e->by = vcs2world(maincam, Vecquat(mulq(mulq(Δorient, Quatvec(0, world2vcs(maincam, e->by))), invq(Δorient)))); + e->bz = vcs2world(maincam, Vecquat(mulq(mulq(Δorient, Quatvec(0, world2vcs(maincam, e->bz))), invq(Δorient)))); + } + }else{ + Framebuf *fb; + Point p; + Color c, n; + double z; + + p = subpt(mctl->xy, screen->r.min); + qlock(maincam->vp->fbctl); + fb = maincam->vp->getfb(maincam->vp); + c = ul2col(fb->cb[p.y*Dx(fb->r) + p.x]); + n = ul2col(fb->nb[p.y*Dx(fb->r) + p.x]); + z = fb->zb[p.y*Dx(fb->r) + p.x]; + qunlock(maincam->vp->fbctl); + snprint(stats[Spixcol], sizeof(stats[Spixcol]), "c %V z %g", c, z); + snprint(stats[Snorcol], sizeof(stats[Snorcol]), "n %V", n); + } +} + +void mmb(void) { enum { MOVELIGHT, TSNEAREST, TSBILINEAR, + SHOWNORMALS, }; static char *items[] = { [MOVELIGHT] "move light", [TSNEAREST] "use nearest sampler", [TSBILINEAR] "use bilinear sampler", + [SHOWNORMALS] "show normals", nil, }; static Menu menu = { .item = items }; char buf[256], *f[3]; int nf; + qlock(&drawlk); switch(menuhit(2, mctl, &menu, _screen)){ case MOVELIGHT: snprint(buf, sizeof buf, "%g %g %g", light.p.x, light.p.y, light.p.z); if(enter("light pos", buf, sizeof buf, mctl, kctl, nil) <= 0) - return; + break; nf = tokenize(buf, f, 3); if(nf != 3) - return; + break; light.p.x = strtod(f[0], nil); light.p.y = strtod(f[1], nil); light.p.z = strtod(f[2], nil); @@ -547,7 +630,11 @@ mmb(void) case TSBILINEAR: tsampler = bilitexsampler; break; + case SHOWNORMALS: + shownormals ^= 1; + break; } + qunlock(&drawlk); nbsend(drawc, nil); } @@ -565,18 +652,22 @@ rmb(void) static Menu menu = { .gen = genrmbmenuitem }; int idx; + qlock(&drawlk); idx = menuhit(3, mctl, &menu, _screen); - if(idx < 0) - return; - shader = &shadertab[idx]; - for(idx = 0; idx < nelem(cams); idx++) - memset(&cams[idx].stats, 0, sizeof(cams[idx].stats)); + if(idx >= 0){ + shader = &shadertab[idx]; + for(idx = 0; idx < nelem(cams); idx++) + memset(&cams[idx].stats, 0, sizeof(cams[idx].stats)); + } + qunlock(&drawlk); nbsend(drawc, nil); } void mouse(void) { + if((mctl->buttons & 1) != 0) + lmb(); if((mctl->buttons & 2) != 0) mmb(); if((mctl->buttons & 4) != 0) @@ -585,6 +676,7 @@ mouse(void) zoomin(); if((mctl->buttons & 16) != 0) zoomout(); + om = mctl->Mouse; } void @@ -726,7 +818,7 @@ confproc(void) void usage(void) { - fprint(2, "usage: %s [-t texture] [-n normals] [-s shader] [-ω yrot] model...\n", argv0); + fprint(2, "usage: %s [-s] [-t texture] model...\n", argv0); exits("usage"); } @@ -736,19 +828,16 @@ threadmain(int argc, char *argv[]) Viewport *v; Renderer *rctl; Channel *keyc; + Entity *subject; OBJ *obj; - char *texpath, *norpath, *sname, *mdlpath; + char *texpath, *mdlpath; int i, fd; GEOMfmtinstall(); texpath = nil; - norpath = nil; - sname = "gouraud"; ARGBEGIN{ + case 's': showskybox++; break; case 't': texpath = EARGF(usage()); break; - case 'n': norpath = EARGF(usage()); break; - case 's': sname = EARGF(usage()); break; - case L'ω': ω = strtod(EARGF(usage()), nil)*DEG; break; case L'ι': inception++; break; case 'p': doprof++; break; default: usage(); @@ -758,15 +847,15 @@ threadmain(int argc, char *argv[]) confproc(); - if((shader = getshader(sname)) == nil) - sysfatal("couldn't find %s shader", sname); + if((shader = getshader("gouraud")) == nil) + sysfatal("couldn't find gouraud shader"); scene = newscene(nil); while(argc--){ mdlpath = argv[argc]; model = newmodel(); subject = newentity(model); - subject->p.x = argc*4; +// subject->p.x = argc*4; scene->addent(scene, subject); if((obj = objparse(mdlpath)) == nil) @@ -782,15 +871,9 @@ threadmain(int argc, char *argv[]) sysfatal("readmemimage: %r"); close(fd); } - if(argc == 0 && norpath != nil){ - fd = open(norpath, OREAD); - if(fd < 0) - sysfatal("open: %r"); - if((model->nor = readmemimage(fd)) == nil) - sysfatal("readmemimage: %r"); - close(fd); - } } + if(showskybox) + scene->skybox = readcubemap(skyboxpaths); if(memimageinit() != 0) sysfatal("memimageinit: %r"); @@ -806,13 +889,17 @@ threadmain(int argc, char *argv[]) v = mkviewport(screenb->r); placecamera(&cams[i], camcfgs[i].p, camcfgs[i].lookat, camcfgs[i].up); configcamera(&cams[i], v, camcfgs[i].fov, camcfgs[i].clipn, camcfgs[i].clipf, camcfgs[i].ptype); - cams[i].s = scene; + cams[i].scene = scene; cams[i].rctl = rctl; } maincam = &cams[3]; light.p = Pt3(0,100,100,1); +// light.dir = Vec3(0,-1,0); light.c = Pt3(1,1,1,1); light.type = LIGHT_POINT; +// light.type = LIGHT_SPOT; +// light.θu = 30*DEG; +// light.θp = 5*DEG; tsampler = neartexsampler; kctl = emalloc(sizeof *kctl); @@ -824,22 +911,21 @@ threadmain(int argc, char *argv[]) proccreate(kbdproc, nil, mainstacksize); proccreate(keyproc, keyc, mainstacksize); + proccreate(renderproc, nil, mainstacksize); proccreate(drawproc, nil, mainstacksize); for(;;){ - enum {MOUSE, RESIZE, KEY, DRAW}; + enum {MOUSE, RESIZE, KEY}; Alt a[] = { {mctl->c, &mctl->Mouse, CHANRCV}, {mctl->resizec, nil, CHANRCV}, {keyc, nil, CHANRCV}, - {drawc, nil, CHANRCV}, {nil, nil, CHANEND} }; switch(alt(a)){ case MOUSE: mouse(); break; case RESIZE: resize(); break; case KEY: handlekeys(); break; - case DRAW: redraw(); break; } } } |