distance function generation from level data, first pass
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 20 May 2023 03:50:10 +0000 (06:50 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 20 May 2023 03:50:10 +0000 (06:50 +0300)
Makefile
sdr/raydungeon.p.glsl
src/darray.h
src/game.c
src/game.h
src/level.c
src/level.h
src/options.c
src/scr_game.c
src/scr_lvled.c

index 9c0f1ce..0af9e1a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,13 +1,13 @@
-src = $(wildcard src/*.c)
+src = $(wildcard src/*.c) libs/glew/glew.c
 obj = $(src:.c=.o)
 dep = $(src:.c=.d)
 bin = game
 
 warn = -pedantic -Wall
 dbg = -g
-def = -DMINIGLUT_USE_LIBC
+def = -DMINIGLUT_USE_LIBC -DGLEW_STATIC
 
-inc = -Ilibs -Ilibs/assfile -Ilibs/treestor/include
+inc = -Ilibs -Ilibs/assfile -Ilibs/treestor/include -Ilibs/glew
 libs = -lassfile -ldrawtext -lgoat3d -limago -ltreestor
 
 CFLAGS = $(warn) $(opt) $(dbg) $(inc) $(def) -MMD
@@ -24,7 +24,7 @@ ifeq ($(sys), mingw)
 
        libdir = -Llibs/w32
 else
-       libgl = -lGL -lGLU -lX11
+       libgl = -lGL -lX11
 
        libdir = -Llibs/unix
 endif
index 8a66df7..c10be18 100644 (file)
@@ -1,5 +1,117 @@
+uniform float aspect;
+uniform mat4 matrix;
+uniform mat3 dirmatrix;
+
+#define DIST_THRES     1e-3
+#define MAX_ITER       250
+#define MAX_STEP       1.0
+
+vec3 raymarch(inout vec3 p, in vec3 dir, out float depth);
+vec3 shade(in vec3 p, in vec3 dir, in float dist, in float total_dist);
+vec3 backdrop(in vec3 dir);
+float eval_sdf(in vec3 p);
+vec3 eval_grad(in vec3 p, float dist);
+vec3 primray(in vec2 uv, out vec3 org);
+
+float boxdist(in vec3 p, in vec3 b);
+float sphdist(in vec3 p, in vec3 sp, in float srad);
+
 void main()
 {
-       vec3 col = vec3(gl_TexCoord[0].st, 1.0);
-       gl_FragColor = vec4(col, 1.0);
+       vec2 uv = gl_TexCoord[0].st;
+       vec3 rorg;
+       vec3 rdir = primray(uv, rorg);
+       float depth;
+
+       gl_FragColor.rgb = raymarch(rorg, rdir, depth);
+       gl_FragColor.a = 1.0;
+
+       vec4 projp = gl_ProjectionMatrix * vec4(0.0, 0.0, -depth, 1.0);
+       float zval = projp.z / projp.w;
+
+       gl_FragDepth = zval;
+}
+
+vec3 raymarch(inout vec3 p, in vec3 dir, out float depth)
+{
+       float d, total_d = 0.0;
+
+       for(int i=0; i<MAX_ITER; i++) {
+               if((d = eval_sdf(p)) <= DIST_THRES) {
+                       depth = total_d;
+                       return shade(p, dir, d, total_d);
+               }
+
+               d = min(d, MAX_STEP);
+
+               p = p + dir * d;
+               total_d += d;
+       }
+
+       depth = -gl_DepthRange.far;
+       return backdrop(dir);
+}
+
+vec3 shade(in vec3 p, in vec3 dir, in float dist, in float total_dist)
+{
+    const vec3 kd = vec3(1.0, 0.3, 0.1);
+    const vec3 ks = vec3(0.7, 0.7, 0.7);
+    const vec3 ldir = normalize(vec3(-1.0, 1.0, -1.5));
+    const vec3 vdir = vec3(0.0, 0.0, -1.0);
+
+    vec3 diffuse = vec3(0.0, 0.0, 0.0);
+    vec3 specular = vec3(0.0, 0.0, 0.0);
+    
+    vec3 n = eval_grad(p, dist);
+       return n * 0.5 + 0.5;
+    vec3 hdir = normalize(ldir + vdir);
+
+    float ndotl = max(dot(n, ldir), 0.0);
+    float ndoth = max(dot(n, hdir), 0.0);
+    
+    diffuse += kd * ndotl;
+    specular += ks * pow(ndoth, 50.0);
+    
+    float fog = clamp(300.0 / (total_dist * total_dist), 0.0, 1.0);
+
+    return mix(backdrop(dir), diffuse + specular, fog);
+}
+
+vec3 backdrop(in vec3 dir)
+{
+       return vec3(0.1, 0.1, 0.1);
+}
+
+#define DELTA  1e-4
+vec3 eval_grad(in vec3 p, float dist)
+{
+       float dfdx = eval_sdf(p + vec3(DELTA, 0.0, 0.0)) - dist;
+       float dfdy = eval_sdf(p + vec3(0.0, DELTA, 0.0)) - dist;
+       float dfdz = eval_sdf(p + vec3(0.0, 0.0, DELTA)) - dist;
+       return normalize(vec3(dfdx, dfdy, dfdz));
+}
+
+vec3 primray(in vec2 uv, out vec3 org)
+{
+       vec3 origin = (gl_ModelViewMatrixInverse * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
+       org = origin;
+
+       vec2 nuv = uv * 2.0 - 1.0;
+       vec4 cp_near = vec4(nuv, -1.0, 1.0);
+
+       vec4 vdir = gl_ProjectionMatrixInverse * cp_near;
+       vec4 wdir = gl_ModelViewMatrixInverse * vec4(vdir.xyz, 0.0);
+
+       return normalize(wdir.xyz);
+}
+
+float boxdist(in vec3 p, in vec3 b)
+{
+       vec3 q = abs(p) - b;
+       return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
+}
+
+float sphdist(in vec3 p, in vec3 sp, in float srad)
+{
+       return length(p - sp) - srad;
 }
index b9a7051..fd2594b 100644 (file)
@@ -31,17 +31,17 @@ void *darr_finalize(void *da);
 #define darr_strpush(da, c) \
        do { \
                char cnull = 0, ch = (char)(c); \
-               (da) = dynarr_pop_impl(da); \
-               (da) = dynarr_push_impl((da), &ch); \
-               (da) = dynarr_push_impl((da), &cnull); \
+               (da) = darr_pop_impl(da); \
+               (da) = darr_push_impl((da), &ch); \
+               (da) = darr_push_impl((da), &cnull); \
        } while(0)
 
 #define darr_strpop(da) \
        do { \
                char cnull = 0; \
-               (da) = dynarr_pop_impl(da); \
-               (da) = dynarr_pop_impl(da); \
-               (da) = dynarr_push_impl((da), &cnull); \
+               (da) = darr_pop_impl(da); \
+               (da) = darr_pop_impl(da); \
+               (da) = darr_push_impl((da), &cnull); \
        } while(0)
 
 
index f7306d6..f4a80e3 100644 (file)
@@ -15,6 +15,7 @@ int fullscr;
 long time_msec;
 
 struct game_screen *cur_scr;
+char *start_scr_name;
 
 /* available screens */
 extern struct game_screen scr_menu, scr_game, scr_map, scr_lvled;
@@ -26,7 +27,7 @@ static int num_screens;
 int game_init(int argc, char **argv)
 {
        int i;
-       char *start_scr_name;
+       char *env;
 
        load_options(GAME_CFG_FILE);
        if(parse_options(argc, argv) == -1) {
@@ -44,7 +45,9 @@ int game_init(int argc, char **argv)
        screens[num_screens++] = &scr_map;
        screens[num_screens++] = &scr_lvled;
 
-       start_scr_name = getenv("START_SCREEN");
+       if((env = getenv("START_SCREEN"))) {
+               start_scr_name = env;
+       }
 
        for(i=0; i<num_screens; i++) {
                if(screens[i]->init() == -1) {
index 36e5854..7984996 100644 (file)
@@ -45,6 +45,7 @@ extern int fullscr;
 
 extern long time_msec;
 extern struct game_screen *cur_scr;
+extern char *start_scr_name;
 
 
 int game_init(int argc, char **argv);
index 4120867..dd0e3a1 100644 (file)
@@ -180,6 +180,29 @@ int save_level(const struct level *lvl, const char *fname)
                        rect++;
                }
                fprintf(fp, "RECTEND\n\n");
+
+
+               fprintf(fp, "SDRSTART\n");
+               fprintf(fp, "float eval_sdf(in vec3 p)\n");
+               fprintf(fp, "{\n");
+               rect = lvl->rects;
+               for(i=0; i<num; i++) {
+                       float cx = (rect->x - 0.5f + rect->w * 0.5f) * lvl->scale;
+                       float cy = (rect->y - 0.5f + rect->h * 0.5f) * lvl->scale;
+                       float rx = (rect->w + 0.1f) * lvl->scale * 0.5f;
+                       float ry = (rect->h + 0.1f) * lvl->scale * 0.5f;
+
+                       if(i == 0) {
+                               fprintf(fp, "\tfloat d = boxdist(p - vec3(%f, 0.0, %f), vec3(%f, 1.0, %f));\n",
+                                               cx, cy, rx, ry);
+                       } else {
+                               fprintf(fp, "\td = min(d, boxdist(p - vec3(%f, 0.0, %f), vec3(%f, 1.0, %f)));\n",
+                                               cx, cy, rx, ry);
+                       }
+                       rect++;
+               }
+               fprintf(fp, "\treturn d;\n}\n");
+               fprintf(fp, "SDREND\n\n");
        }
 
        fclose(fp);
@@ -200,6 +223,13 @@ static char *clean_line(char *s)
        return *s ? s : 0;
 }
 
+enum {
+       ST_HEADER,
+       ST_ASSIGN,
+       ST_MAP,
+       ST_SDR
+};
+
 int load_level(struct level *lvl, const char *fname)
 {
        ass_file *fp;
@@ -209,6 +239,7 @@ int load_level(struct level *lvl, const char *fname)
        int state = 0;
        int i, nrow;
        struct level_rect rect;
+       char *sdr = 0;
 
        if(!(fp = ass_fopen(fname, "rb"))) {
                fprintf(stderr, "load_level: failed to open: %s\n", fname);
@@ -217,16 +248,16 @@ int load_level(struct level *lvl, const char *fname)
 
        while(ass_fgets(buf, sizeof buf, fp)) {
                switch(state) {
-               case 0:
+               case ST_HEADER:
                        if(memcmp(buf, "RDLEVEL", 7) != 0) {
                                fprintf(stderr, "load_level: invalid level file: %s\n", fname);
                                ass_fclose(fp);
                                return -1;
                        }
-                       state++;
+                       state = ST_ASSIGN;
                        break;
 
-               case 1:
+               case ST_ASSIGN:
                        if(!(line = clean_line(buf)) || *line == '#') {
                                continue;
                        }
@@ -267,7 +298,15 @@ int load_level(struct level *lvl, const char *fname)
 
                                lvl->cells = calloc_nf(lvl->xsz * lvl->ysz, sizeof *lvl->cells);
                                nrow = 0;
-                               state++;
+                               state = ST_MAP;
+
+                       } else if(strcmp(line, "SDRSTART") == 0) {
+                               if(sdr) {
+                                       darr_clear(sdr);
+                               } else {
+                                       sdr = darr_alloc(0, 1);
+                               }
+                               state = ST_SDR;
 
                        } else if(sscanf(line, "rect %d %d %d %d", &rect.x, &rect.y, &rect.w, &rect.h) == 4) {
                                rect.dbgcol[0] = (rand() & 0x7f) + 0x7f;
@@ -278,9 +317,9 @@ int load_level(struct level *lvl, const char *fname)
                        }
                        break;
 
-               case 2:
+               case ST_MAP:
                        if(memcmp(buf, "MAPEND", 6) == 0) {
-                               state = 1;
+                               state = ST_ASSIGN;
                                break;
                        }
                        for(i=0; i<lvl->xsz; i++) {
@@ -288,13 +327,27 @@ int load_level(struct level *lvl, const char *fname)
                                parse_cell(lvl, i, nrow, buf[i]);
                        }
                        if(++nrow >= lvl->ysz) {
-                               state = 1;
+                               state = ST_ASSIGN;
                                break;
                        }
                        break;
+
+               case ST_SDR:
+                       if(memcmp(buf, "SDREND", 6) == 0) {
+                               state = ST_ASSIGN;
+                               break;
+                       }
+                       for(i=0; buf[i]; i++) {
+                               darr_strpush(sdr, buf[i]);
+                       }
+                       break;
                }
        }
 
+       if(sdr) {
+               lvl->sdf_src = darr_finalize(sdr);
+       }
+
        ass_fclose(fp);
        return 0;
 }
index 0a1a605..e3b53fe 100644 (file)
@@ -24,6 +24,8 @@ struct level {
        int sx, sy;
 
        struct level_rect *rects;       /* darr, empty spaces */
+
+       char *sdf_src;
 };
 
 void init_level(struct level *lvl);
index 34dee8f..b4964f9 100644 (file)
@@ -3,6 +3,7 @@
 #include <string.h>
 #include <errno.h>
 #include "options.h"
+#include "game.h"
 #include "treestor.h"
 
 #define DEF_XRES               1280
@@ -40,6 +41,8 @@ int parse_options(int argc, char **argv)
                                        fprintf(stderr, "%s must be followed by a screen name\n", argv[i - 1]);
                                        return -1;
                                }
+                               start_scr_name = argv[i];
+
                        } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-help") == 0) {
                                printf(usage_fmt, argv[0]);
                                exit(0);
index 2f1f31b..d19ff70 100644 (file)
@@ -35,18 +35,13 @@ static cgm_vec3 cam_pan;
 
 static unsigned int sdr;
 
-
 static int ginit(void)
 {
-       if(!(sdr = create_program_load("sdr/raydungeon.v.glsl", "sdr/raydungeon.p.glsl"))) {
-               return -1;
-       }
        return 0;
 }
 
 static void gdestroy(void)
 {
-       free_program(sdr);
 }
 
 static int gstart(void)
@@ -59,6 +54,19 @@ static int gstart(void)
        cam_pan.x = -(lvl->xsz / 2.0f) * lvl->scale;
        cam_pan.y = 0;
        cam_pan.z = -(lvl->ysz / 2.0f) * lvl->scale;
+
+       glEnable(GL_DEPTH_TEST);
+
+       if(lvl->sdf_src) {
+               add_shader_footer(GL_FRAGMENT_SHADER, lvl->sdf_src);
+       } else {
+               add_shader_footer(GL_FRAGMENT_SHADER, "float eval_sdf(in vec3 p) { return 10000.0; }\n");
+       }
+       if(!(sdr = create_program_load("sdr/raydungeon.v.glsl", "sdr/raydungeon.p.glsl"))) {
+               return -1;
+       }
+       clear_shader_footer(GL_FRAGMENT_SHADER);
+       glUseProgram(sdr);
        return 0;
 }
 
@@ -66,6 +74,8 @@ static void gstop(void)
 {
        destroy_level(lvl);
        free(lvl);
+
+       free_program(sdr);
 }
 
 static void gdisplay(void)
@@ -85,13 +95,11 @@ static void gdisplay(void)
 
        glUseProgram(sdr);
 
-       glDepthMask(0);
        glBegin(GL_TRIANGLES);
        glTexCoord2f(0, 0); glVertex2f(-1, -1);
-       glTexCoord2f(2, 0); glVertex2f(4, -1);
-       glTexCoord2f(0, 2); glVertex2f(-1, 4);
+       glTexCoord2f(2, 0); glVertex2f(3, -1);
+       glTexCoord2f(0, 2); glVertex2f(-1, 3);
        glEnd();
-       glDepthMask(1);
 
        glUseProgram(0);
 
@@ -118,7 +126,7 @@ static void gdisplay(void)
 
 static void greshape(int x, int y)
 {
-       cgm_mperspective(proj_mat, cgm_deg_to_rad(60), win_aspect, 0.5, 500.0);
+       cgm_mperspective(proj_mat, cgm_deg_to_rad(60), win_aspect, 0.5, 40.0);
        glMatrixMode(GL_PROJECTION);
        glLoadMatrixf(proj_mat);
 }
index e06cd32..3b5eda3 100644 (file)
@@ -2,6 +2,7 @@
 #include <stdlib.h>
 #include <math.h>
 #include <GL/gl.h>
+#include "cgmath/cgmath.h"
 #include "game.h"
 #include "level.h"
 #include "util.h"
@@ -64,6 +65,8 @@ static void display(void)
        struct level_cell *cell;
        struct level_rect *rect;
 
+       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glTranslatef(0, 0, -cam_dist);
@@ -113,6 +116,11 @@ static void display(void)
 
 static void reshape(int x, int y)
 {
+       float proj_mat[16];
+
+       cgm_mperspective(proj_mat, cgm_deg_to_rad(60), win_aspect, 0.5, 40.0);
+       glMatrixMode(GL_PROJECTION);
+       glLoadMatrixf(proj_mat);
 }
 
 static void keyb(int key, int press)