shithub: 3dee

ref: 590c394f3cf8e8f6c8612ff8feb76a2c78a59613
dir: /plot3.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <thread.h>
#include <draw.h>
#include <memdraw.h>
#include <mouse.h>
#include <keyboard.h>
#include <geometry.h>
#include "libgraphics/graphics.h"
#include "fns.h"

typedef struct AABB AABB;
typedef struct PColor PColor;
typedef struct Plotpt Plotpt;
typedef struct Plot Plot;

struct AABB
{
	Point3 min;
	Point3 max;
	/* with its homologous bounding sphere */
	Point3 c;
	double r;
};

struct PColor
{
	char *k;
	ulong v;
	Color c;
};

struct Plotpt
{
	Point3 p;
	PColor *c;
};

struct Plot
{
	Plotpt *pts;
	ulong npts;
	AABB bbox;
	Scene *scn;
};

Mousectl *mctl;
Keyboardctl *kctl;
Mouse om;
Channel *drawc;
Image *screenb;
Plot theplot;
Camera *cam;
PColor pal[] = {
	{ .k = "black",		.v = DBlack },
	{ .k = "white",		.v = DWhite },
	{ .k = "red",		.v = DRed },
	{ .k = "green",		.v = DGreen },
	{ .k = "blue",		.v = DBlue },
	{ .k = "yellow",	.v = DYellow },
}, *brush;

static Point3
vs(Shaderparams *sp)
{
	return world2clip(sp->su->camera, model2world(sp->su->entity, sp->v->p));
}

static Color
fs(Shaderparams *sp)
{
	return sp->v->c;
}

Shadertab shaders = {
	.vs = vs,
	.fs = fs
};

void
initpalette(void)
{
	int i;

	for(i = 0; i < nelem(pal); i++)
		pal[i].c = ul2col(pal[i].v);
	brush = &pal[0];
}

void
soakbrush(char *ck)
{
	int i;

	for(i = 0; i < nelem(pal); i++)
		if(strcmp(ck, pal[i].k) == 0){
			brush = &pal[i];
			break;
		}
}

void
updatebboxfromtheplot(void)
{
	static int inited;
	Point3 *lastpt;

	lastpt = &theplot.pts[theplot.npts-1].p;

	if(!inited){
		theplot.bbox.min = theplot.bbox.max = *lastpt;
		inited++;
		return;
	}

	theplot.bbox.min = minpt3(theplot.bbox.min, *lastpt);
	theplot.bbox.max = maxpt3(theplot.bbox.max, *lastpt);
	theplot.bbox.c = divpt3(addpt3(theplot.bbox.max, theplot.bbox.min), 2);
	theplot.bbox.r = max(vec3len(theplot.bbox.min), vec3len(theplot.bbox.max));
}

void
addpttotheplot(Point3 p)
{
	if(theplot.npts % 16 == 0)
		theplot.pts = erealloc(theplot.pts, (theplot.npts + 16)*sizeof(Plotpt));
	theplot.pts[theplot.npts++] = (Plotpt){p, brush};
	updatebboxfromtheplot();
}

void
readtheplot(int fd)
{
	Biobuf *bin;
	Point3 p;
	char *line, *f[3];
	ulong lineno;
	int nf;

	bin = Bfdopen(fd, OREAD);
	if(bin == nil)
		sysfatal("Bfdopen: %r");

	lineno = 0;
	p.w = 1;
	while((line = Brdstr(bin, '\n', 1)) != nil){
		lineno++;
		nf = tokenize(line, f, nelem(f));
		if(nf == 2 && strncmp(f[0], "co", 2) == 0){
			soakbrush(f[1]);
			free(line);
			continue;
		}
		if(nf != 3){
			fprint(2, "not enough coordinates. ignoring line %uld\n", lineno);
			free(line);
			continue;
		}
		p.x = strtod(f[0], nil);
		p.y = strtod(f[1], nil);
		p.z = strtod(f[2], nil);
		addpttotheplot(p);
		free(line);
	}
	Bterm(bin);
}

#define smallestbbox(coord)	(min(theplot.bbox.min.coord, theplot.bbox.max.coord))
#define biggestbbox(coord)	(max(theplot.bbox.min.coord, theplot.bbox.max.coord))
void
frametheplot(void)
{
	Model *mdl;
	Entity *ent;
	Primitive line, mark;
	Vertex v;
	Point3 stepv, p0, p1, mp0;
	int i;

	mdl = newmodel();
	ent = newentity("axis scales", mdl);
	theplot.scn->addent(theplot.scn, ent);

	line = mkprim(PLine);
	v = mkvert();
	v.c = mdl->addcolor(mdl, Pt3(0.4,0.4,0.4,1));
	mark = line;

	/* x scale */
	v.p = mdl->addposition(mdl, Pt3(smallestbbox(x), smallestbbox(y), smallestbbox(z), 1));
	line.v[0] = mdl->addvert(mdl, v);
	v.p = mdl->addposition(mdl, Pt3(biggestbbox(x), smallestbbox(y), smallestbbox(z), 1));
	line.v[1] = mdl->addvert(mdl, v);
	mdl->addprim(mdl, line);
	p0 = *(Point3*)itemarrayget(mdl->positions, v.p - 1);
	p1 = *(Point3*)itemarrayget(mdl->positions, v.p);
	stepv = subpt3(p1, p0);
	stepv = divpt3(stepv, 10);
	for(i = 1; i <= 10; i++){
		mp0 = addpt3(p0, mulpt3(stepv, i));
		v.p = mdl->addposition(mdl, mp0);
		mark.v[0] = mdl->addvert(mdl, v);
		v.p = mdl->addposition(mdl, addpt3(mp0, qrotate(stepv, Vec3(0,1,0), 90*DEG)));
		mark.v[1] = mdl->addvert(mdl, v);
		mdl->addprim(mdl, mark);
	}

	/* y scale */
	v.p = mdl->addposition(mdl, Pt3(smallestbbox(x), biggestbbox(y), smallestbbox(z), 1));
	line.v[1] = mdl->addvert(mdl, v);
	mdl->addprim(mdl, line);
	p1 = *(Point3*)itemarrayget(mdl->positions, v.p);
	stepv = subpt3(p1, p0);
	stepv = divpt3(stepv, 10);
	for(i = 1; i <= 10; i++){
		mp0 = addpt3(p0, mulpt3(stepv, i));
		v.p = mdl->addposition(mdl, mp0);
		mark.v[0] = mdl->addvert(mdl, v);
		v.p = mdl->addposition(mdl, addpt3(mp0, qrotate(stepv, normvec3(Vec3(-1,0,1)), 90*DEG)));
		mark.v[1] = mdl->addvert(mdl, v);
		mdl->addprim(mdl, mark);
	}

	/* z scale */
	v.p = mdl->addposition(mdl, Pt3(smallestbbox(x), smallestbbox(y), biggestbbox(z), 1));
	line.v[1] = mdl->addvert(mdl, v);
	mdl->addprim(mdl, line);
	p1 = *(Point3*)itemarrayget(mdl->positions, v.p);
	stepv = subpt3(p1, p0);
	stepv = divpt3(stepv, 10);
	for(i = 1; i <= 10; i++){
		mp0 = addpt3(p0, mulpt3(stepv, i));
		v.p = mdl->addposition(mdl, mp0);
		mark.v[0] = mdl->addvert(mdl, v);
		v.p = mdl->addposition(mdl, addpt3(mp0, qrotate(stepv, Vec3(0,1,0), -90*DEG)));
		mark.v[1] = mdl->addvert(mdl, v);
		mdl->addprim(mdl, mark);
	}
}

void
understandtheplot(void)
{
	Model *mdl;
	Entity *ent;
	Scene *scn;
	Primitive prim;
	Vertex v;
	static Color prevcol;
	Plotpt *p;

	mdl = newmodel();
	ent = newentity(nil, mdl);
	scn = newscene("the plot");
	scn->addent(scn, ent);
	theplot.scn = scn;

	prim = mkprim(PPoint);
	v = mkvert();
	for(p = theplot.pts; p < theplot.pts + theplot.npts; p++){
		v.p = mdl->addposition(mdl, p->p);
		if(!eqpt3(prevcol, p->c->c)){
			v.c = mdl->addcolor(mdl, p->c->c);
			prevcol = p->c->c;
		}
		prim.v[0] = mdl->addvert(mdl, v);
		mdl->addprim(mdl, prim);
	}
	free(theplot.pts);
	theplot.pts = nil;
	theplot.npts = 0;
	frametheplot();
}

void
redrawb(void)
{
	shootcamera(cam, &shaders);
	lockdisplay(display);
	draw(screenb, screenb->r, display->white, nil, ZP);
	cam->view->draw(cam->view, screenb, nil);
	unlockdisplay(display);
	nbsend(drawc, nil);
}

void
redraw(void)
{
	lockdisplay(display);
	draw(screen, screen->r, screenb, nil, ZP);
	flushimage(display, 1);
	unlockdisplay(display);
}

void
drawproc(void *)
{
	threadsetname("drawproc");

	for(;;){
		recv(drawc, nil);
		redraw();
	}
}

void
zoomin(void)
{
	cam->fov = fclamp(cam->fov - 1*DEG, 1*DEG, 180*DEG);
	reloadcamera(cam);
	redrawb();
}

void
zoomout(void)
{
	cam->fov = fclamp(cam->fov + 1*DEG, 1*DEG, 180*DEG);
	reloadcamera(cam);
	redrawb();
}

void
lmb(void)
{
	static Quaternion orient = {1,0,0,0};
	Quaternion Δorient;
	Point3 v;

	if((om.buttons^mctl->buttons) != 0)
		return;

	Δorient = orient;
	qball(screen->r, om.xy, mctl->xy, &orient, nil);
	Δorient = mulq(Δorient, invq(orient));

	/* orbit camera around the center */
	v = subpt3(cam->p, theplot.bbox.c);
	v = vcs2world(cam, qsandwichpt3(Δorient, world2vcs(cam, v)));
	movecamera(cam, addpt3(theplot.bbox.c, v));
	aimcamera(cam, theplot.bbox.c);

	redrawb();
}

void
mouse(void)
{
	if(mctl->buttons & 1)
		lmb();
	if(mctl->buttons & 8)
		zoomin();
	if(mctl->buttons & 16)
		zoomout();
	om = mctl->Mouse;
}

void
resize(void)
{
	lockdisplay(display);
	if(getwindow(display, Refnone) < 0)
		fprint(2, "can't reattach to window\n");
	unlockdisplay(display);
	nbsend(drawc, nil);
}

void
key(Rune r)
{
	switch(r){
	case Kdel:
	case 'q':
		threadexitsall(nil);
	}
}

void
usage(void)
{
	fprint(2, "usage: %s\n", argv0);
	exits("usage");
}

void
threadmain(int argc, char *argv[])
{
	Renderer *rctl;
	Rune r;

	ARGBEGIN{
	default: usage();
	}ARGEND;
	if(argc != 0)
		usage();

	initpalette();
	readtheplot(0);
	understandtheplot();

	if(memimageinit() != 0)
		sysfatal("memimageinit: %r");
	if((rctl = initgraphics()) == nil)
		sysfatal("initgraphics: %r");
	if(initdraw(nil, nil, "plot3") < 0)
		sysfatal("initdraw: %r");
	if((mctl = initmouse(nil, screen)) == nil)
		sysfatal("initmouse: %r");
	if((kctl = initkeyboard(nil)) == nil)
		sysfatal("initkeyboard: %r");

	screenb = eallocimage(display, rectsubpt(screen->r, screen->r.min), XRGB32, 0, DNofill);
	cam = Cam(screenb->r, rctl, PERSPECTIVE, 90*DEG, 0.1, 1000);
	placecamera(cam, theplot.scn, addpt3(theplot.bbox.c, mulpt3(normvec3(Vec3(1,1,1)), 1.5*theplot.bbox.r)), theplot.bbox.c, Vec3(0,1,0));

	display->locking = 1;
	unlockdisplay(display);

	drawc = chancreate(sizeof(void*), 1);
	proccreate(drawproc, nil, mainstacksize);
	redrawb();

	enum {MOUSE, RESIZE, KEY};
	Alt a[] = {
		{mctl->c, &mctl->Mouse, CHANRCV},
		{mctl->resizec, nil, CHANRCV},
		{kctl->c, &r, CHANRCV},
		{nil, nil, CHANEND}
	};
	for(;;)
		switch(alt(a)){
		case -1: sysfatal("alt interrupted");
		case MOUSE:
			mouse();
			break;
		case RESIZE:
			resize();
			break;
		case KEY:
			key(r);
			break;
		}
}