summaryrefslogtreecommitdiff
path: root/procgen.c
diff options
context:
space:
mode:
Diffstat (limited to 'procgen.c')
-rw-r--r--procgen.c196
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);
+}