diff options
Diffstat (limited to 'procgen.c')
-rw-r--r-- | procgen.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/procgen.c b/procgen.c new file mode 100644 index 0000000..7d48777 --- /dev/null +++ b/procgen.c @@ -0,0 +1,196 @@ +/* + * Greek Sunset + * + * based on Morgan McGuire's work: + * - https://casual-effects.com/research/McGuire2019ProcGen/McGuire2019ProcGen.pdf + * - https://www.shadertoy.com/view/WsdXWr + */ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include <draw.h> +#include <memdraw.h> +#include <mouse.h> +#include <keyboard.h> +#include <geometry.h> +#include "libobj/obj.h" +#include "libgraphics/graphics.h" +#include "fns.h" + +#define min(a, b) ((a)<(b)?(a):(b)) +#define max(a, b) ((a)>(b)?(a):(b)) + +Renderer *rctl; +Camera *cam; +Scene *scn; +Entity *ent; +Model *mdl; +Primitive quad[2]; + +static Color +getskycolor(double x, double y) +{ + Color c; + double h; + + h = max(0, 1.4 - y - pow(fabs(x - 0.5), 3)); + c.r = pow(h, 3); + c.g = pow(h, 7); + c.b = 0.2 + pow(max(0, h - 0.1), 10); + c.a = 1; + return c; +} + +static double +fract(double x) +{ + double n; + + return modf(x, &n); +} + +static double +hash(double x) +{ + return fract(sin(x) * 1e4); +} + +static double +noise(double x) +{ + double i, f, u; + + i = floor(x); + f = fract(x); + u = f*f * (3 - 2*f); + return 2 * flerp(hash(i), hash(i + 1), u) - 1; +} + +static double +terrain(double x) +{ + double y, k; + int oct; + + y = 0; + for(oct = 0; oct < 10; oct++){ + k = 1<<oct; + y += noise(x*k)/k; + } + return y * 0.3 + 0.36; +} + +static double +water(double x, double dt) +{ + return (sin(71*x - 7*dt) * 0.5 + sin(200*x - 8*dt)) * 0.002 + 0.25; +} + +static double +tree(double x, double h) +{ + if(h < 0.5) + return 0; + else + return 0.2 * max(0, max(max( + fabs(sin(x * 109)), + fabs(sin(x * 150))), + fabs(sin(x * 117))) + + noise(37 * x) + + noise(64 * x + 100) - 1.6); +} + +static Point3 +vs(VSparams *sp) +{ + return vcs2clip(sp->su->camera, sp->v->p); +} + +static Color +fs(FSparams *sp) +{ + Point2 uv; + double dt, shift, h; + + uv = Pt2(sp->p.x,sp->p.y,1); + uv.x /= Dx(sp->su->fb->r); + uv.y /= Dy(sp->su->fb->r); + uv.y = 1 - uv.y; /* make [0 0] the bottom-left corner */ + + dt = sp->su->uni_time/1e9; + shift = 0.09*dt + 0.2; + uv.x += shift; + + h = max(water(uv.x, dt), terrain(uv.x)); + h += tree(uv.x, h); + + if(uv.y < h) + return Pt3(0,0,0,1); + return srgb2linear(getskycolor(uv.x, uv.y)); +} + +Shadertab shaders = { + .vshader = vs, + .fshader = fs +}; + +void +usage(void) +{ + fprint(2, "usage: %s [-s frames] [dx [dy]]\n", argv0); + exits("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + Memimage *out; + Point dim; + int skip; + + dim = Pt(800,400); + skip = 0; + ARGBEGIN{ + case 's': skip = strtoul(EARGF(usage()), nil, 10); break; + default: usage(); + }ARGEND; + if(argc > 0) + switch(argc){ + case 1: dim.x = dim.y = strtoul(argv[0], nil, 10); break; + case 2: + dim.x = strtoul(argv[0], nil, 10); + dim.y = strtoul(argv[1], nil, 10); + break; + default: usage(); + } + + if(memimageinit() != 0) + sysfatal("memimageinit: %r"); + if((rctl = initgraphics()) == nil) + sysfatal("initgraphics: %r"); + + scn = newscene(nil); + mdl = newmodel(); + ent = newentity(nil, mdl); + + out = eallocmemimage(Rect(0,0,dim.x,dim.y), XRGB32); + cam = Cam(out->r, rctl, ORTHOGRAPHIC, 40*DEG, 1, 10); + placecamera(cam, scn, Pt3(0,0,0,1), Vec3(0,0,-1), Vec3(0,1,0)); + + quad[0].type = quad[1].type = PTriangle; + quad[0].v[0].p = viewport2vcs(cam, Pt3(out->r.min.x, out->r.max.y, 1, 1)); + quad[0].v[1].p = viewport2vcs(cam, Pt3(out->r.max.x, out->r.min.y, 1, 1)); + quad[0].v[2].p = viewport2vcs(cam, Pt3(out->r.min.x, out->r.min.y, 1, 1)); + quad[1].v[0].p = quad[0].v[0].p; + quad[1].v[1].p = viewport2vcs(cam, Pt3(out->r.max.x, out->r.max.y, 1, 1)); + quad[1].v[2].p = quad[0].v[1].p; + mdl->addprim(mdl, quad[0]); + mdl->addprim(mdl, quad[1]); + scn->addent(scn, ent); + + do shootcamera(cam, &shaders); while(skip--); + cam->view->memdraw(cam->view, out); + writememimage(1, out); + + threadexitsall(nil); +} |