~pbatch/sdf2d

99457fbb15bb8bb40be5e348b22f88741cfff34e — Paul Batchelor 1 year, 1 month ago cbcefd3
sdfvm initial implementation with demo
4 files changed, 697 insertions(+), 2 deletions(-)

M Makefile
A sdfvm.c
A sdfvm.h
A vmdemo.c
M Makefile => Makefile +5 -2
@@ 1,8 1,8 @@
CFLAGS = -g -I. -O3 -std=c89 -Wall -pedantic

OBJ=mathc/mathc.o sdf.o
OBJ=mathc/mathc.o sdf.o sdfvm.o

default: demo
default: demo vmdemo

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@


@@ 13,6 13,9 @@ libsdf2d.a: $(OBJ)
demo: demo.c libsdf2d.a
	$(CC) $(CFLAGS) $< -o $@ -L. -lsdf2d

vmdemo: vmdemo.c libsdf2d.a
	$(CC) $(CFLAGS) $< -o $@ -L. -lsdf2d

clean:
	$(RM) $(OBJ)
	$(RM) demo

A sdfvm.c => sdfvm.c +308 -0
@@ 0,0 1,308 @@
#include <string.h>
#include "mathc/mathc.h"
#include "sdf.h"
#define SDF2D_SDFVM_PRIV
#include "sdfvm.h"

size_t sdfvm_sizeof(void)
{
    return sizeof(sdfvm);
}

void sdfvm_init(sdfvm *vm)
{
    int i;

    vm->stackpos = 0;

    for (i = 0; i < SDFVM_STACKSIZE; i++) {
        sdfvm_stacklet *s;
        s = &vm->stack[i];
        s->type = SDFVM_NONE;

        /* zeroing out union with brute-force */
        s->data.s = 0;
        s->data.v2 = svec2_zero();
        s->data.v3 = svec3_zero();
    }
}

static int get_stacklet(sdfvm *vm, sdfvm_stacklet **sp)
{
    sdfvm_stacklet *s;

    if (vm->stackpos >= SDFVM_STACKSIZE) return 1;
    s = &vm->stack[vm->stackpos];

    vm->stackpos++;
    *sp = s;
    return 0;
}

int sdfvm_push_scalar(sdfvm *vm, float s)
{
    sdfvm_stacklet *stk;
    int rc;

    rc = get_stacklet(vm, &stk);
    if (rc) return rc;

    stk->type = SDFVM_SCALAR;
    stk->data.s = s;
    return 0;
}

int sdfvm_push_vec2(sdfvm *vm, struct vec2 v)
{
    sdfvm_stacklet *stk;
    int rc;

    rc = get_stacklet(vm, &stk);
    if (rc) return rc;

    stk->type = SDFVM_VEC2;
    stk->data.v2 = v;
    return 0;
}

int sdfvm_push_vec3(sdfvm *vm, struct vec3 v)
{
    sdfvm_stacklet *stk;
    int rc;

    rc = get_stacklet(vm, &stk);
    if (rc) return rc;

    stk->type = SDFVM_VEC3;
    stk->data.v3 = v;
    return 0;
}

int sdfvm_pop_scalar(sdfvm *vm, float *s)
{
    sdfvm_stacklet *stk;

    if (vm->stackpos <= 0) return 1;
    stk = &vm->stack[vm->stackpos - 1];

    if (stk->type != SDFVM_SCALAR) return 2;

    *s = stk->data.s;
    vm->stackpos--;

    return 0;
}

int sdfvm_pop_vec2(sdfvm *vm, struct vec2 *v)
{
    sdfvm_stacklet *stk;

    if (vm->stackpos <= 0) return 1;
    stk = &vm->stack[vm->stackpos - 1];

    if (stk->type != SDFVM_VEC2) return 2;

    *v = stk->data.v2;
    vm->stackpos--;
    return 0;
}

int sdfvm_pop_vec3(sdfvm *vm, struct vec3 *v)
{
    sdfvm_stacklet *stk;

    if (vm->stackpos <= 0) return 1;
    stk = &vm->stack[vm->stackpos - 1];

    if (stk->type != SDFVM_VEC3) return 2;

    *v = stk->data.v3;
    vm->stackpos--;
    return 0;
}

int sdfvm_circle(sdfvm *vm) 
{
    int rc;
    struct vec2 p;
    float d;
    float r;

    rc = 0;

    rc = sdfvm_pop_scalar(vm, &r);
    if (rc) return rc;
    rc = sdfvm_pop_vec2(vm, &p);
    if (rc) return rc;

    d = sdf_circle(p, r);

    rc = sdfvm_push_scalar(vm, d);

    return rc;
}

int sdfvm_poly4(sdfvm *vm) 
{
    int rc;
    struct vec2 p;
    struct vec2 points[4];
    float d;
    int i;

    rc = 0;

    for (i = 3; i >= 0; i--) {
        rc = sdfvm_pop_vec2(vm, &points[i]);
        if (rc) return rc;
    }

    rc = sdfvm_pop_vec2(vm, &p);
    if (rc) return rc;

    d = sdf_polygon(points, 4, p);

    rc = sdfvm_push_scalar(vm, d);

    return rc;
}

int sdfvm_roundness(sdfvm *vm) 
{
    float d, r;
    int rc;
    rc = sdfvm_pop_scalar(vm, &r);
    if (rc) return rc;

    rc = sdfvm_pop_scalar(vm, &d);
    if (rc) return rc;

    rc = sdfvm_push_scalar(vm, d - r);
    if (rc) return rc;

    return 0;
}

static float smoothstep(float e0, float e1, float x)
{
    float t;
    t = clampf((x - e0) / (e1 - e0), 0.0, 1.0);
    return t * t * (3.0 - 2.0 * t);
}


static float feather(float d, float amt)
{
    float alpha;
    alpha = 0;
    alpha = sdf_sign(d) > 0;
    alpha += smoothstep(amt, 0.0, fabs(d));
    alpha = clampf(alpha, 0, 1);
    return alpha;
}

int sdfvm_feather(sdfvm *vm) 
{
    float d, amt, f;
    int rc;
    rc = sdfvm_pop_scalar(vm, &amt);
    if (rc) return rc;
    rc = sdfvm_pop_scalar(vm, &d);
    if (rc) return rc;

    f = feather(d, amt);
    rc = sdfvm_push_scalar(vm, f);
    if (rc) return rc;

    return 0;
}

int sdfvm_lerp3(sdfvm *vm) 
{
    float alpha;
    struct vec3 v[2];
    struct vec3 out;

    int rc;

    rc = sdfvm_pop_vec3(vm, &v[1]);
    if (rc) return rc;
    rc = sdfvm_pop_vec3(vm, &v[0]);
    if (rc) return rc;
    rc = sdfvm_pop_scalar(vm, &alpha);
    if (rc) return rc;

    out = svec3_lerp(v[0], v[1], alpha);

    rc = sdfvm_push_vec3(vm, out);
    if (rc) return rc;

    return 0;
}

int sdfvm_mul(sdfvm *vm) 
{
    float x, y;
    int rc;

    rc = sdfvm_pop_scalar(vm, &y);
    if (rc) return rc;
    rc = sdfvm_pop_scalar(vm, &x);
    if (rc) return rc;

    rc = sdfvm_push_scalar(vm, x * y);
    if (rc) return rc;

    return 0;
}

int sdfvm_lerp(sdfvm *vm) 
{
    float x, y, a, out;
    int rc;

    rc = sdfvm_pop_scalar(vm, &a);
    if (rc) return rc;
    rc = sdfvm_pop_scalar(vm, &y);
    if (rc) return rc;
    rc = sdfvm_pop_scalar(vm, &x);
    if (rc) return rc;

    out = a*y + (1 - a)*x;
    rc = sdfvm_push_scalar(vm, out);
    if (rc) return rc;

    return 0;
}

int sdfvm_gtz(sdfvm *vm)
{
    float d;
    int rc;

    rc = sdfvm_pop_scalar(vm, &d);
    if (rc) return rc;
    rc = sdfvm_push_scalar(vm, d > 0.0);
    if (rc) return rc;

    return 0;
}

int sdfvm_normalize(sdfvm *vm)
{
    struct vec2 x, y;
    struct vec2 out;
    int rc;

    rc = sdfvm_pop_vec2(vm, &y);
    if (rc) return rc;
    rc = sdfvm_pop_vec2(vm, &x);
    if (rc) return rc;

    out = sdf_normalize(x, y);

    rc = sdfvm_push_vec2(vm, out);
    if (rc) return rc;

    return 0;
}

A sdfvm.h => sdfvm.h +51 -0
@@ 0,0 1,51 @@
#ifndef SDF2D_SDFVM_H
#define SDF2D_SDFVM_H

typedef struct sdfvm sdfvm;
typedef struct sdfvm_stacklet sdfvm_stacklet;

#ifdef SDF2D_SDFVM_PRIV
#define SDFVM_STACKSIZE 16
enum {
    SDFVM_NONE,
    SDFVM_SCALAR,
    SDFVM_VEC2,
    SDFVM_VEC3
};

struct sdfvm_stacklet {
    int type;
    union {
        float s;
        struct vec2 v2;
        struct vec3 v3;
    } data;
};

struct sdfvm {
    sdfvm_stacklet stack[SDFVM_STACKSIZE];
    int stackpos;
};
#endif

size_t sdfvm_sizeof(void);
void sdfvm_init(sdfvm *vm);
int sdfvm_push_scalar(sdfvm *vm, float s);
int sdfvm_push_vec2(sdfvm *vm, struct vec2 v);
int sdfvm_push_vec3(sdfvm *vm, struct vec3 v);

int sdfvm_pop_scalar(sdfvm *vm, float *s);
int sdfvm_pop_vec2(sdfvm *vm, struct vec2 *v);
int sdfvm_pop_vec3(sdfvm *vm, struct vec3 *v);

int sdfvm_circle(sdfvm *vm);
int sdfvm_poly4(sdfvm *vm);
int sdfvm_roundness(sdfvm *vm);
int sdfvm_feather(sdfvm *vm);
int sdfvm_lerp3(sdfvm *vm);
int sdfvm_mul(sdfvm *vm);
int sdfvm_lerp(sdfvm *vm);
int sdfvm_gtz(sdfvm *vm);
int sdfvm_normalize(sdfvm *vm);

#endif

A vmdemo.c => vmdemo.c +333 -0
@@ 0,0 1,333 @@
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "mathc/mathc.h"

#include "sdf.h"

#define SDF2D_SDFVM_PRIV
#include "sdfvm.h"

/* global feathering amount for hacky anti-aliasing */
#define FEATHER_AMT 0.03

typedef struct {
    struct vec2 iResolution;
    void *ud;
    struct vec4 *region;
} image_data;

struct canvas {
    struct vec3 *buf;
    struct vec2 res;
};

typedef struct {
    sdfvm vm;
} user_params;

#define US_MAXTHREADS 8

typedef struct thread_userdata thread_userdata;

typedef struct {
    struct vec3 *buf;
    image_data *data;
    int off;
    void (*draw)(struct vec3 *, struct vec2, thread_userdata *);
    int stride;
    sdfvm vm;
} thread_data;

struct thread_userdata {
    thread_data *th;
    image_data *data;
};

void *draw_thread(void *arg)
{
    thread_data *td;
    image_data *data;
    int x, y;
    int w, h;
    int stride;
    struct vec3 *buf;
    int nthreads;
    int xstart, ystart;
    int xend, yend;
    int maxpos;
    struct vec4 *reg;
    thread_userdata thud;

    td = arg;
    data = td->data;
    buf = td->buf;

    w = data->iResolution.x;
    h = data->iResolution.y;
    stride = td->stride;
    reg = data->region;

    ystart = td->off + reg->y;
    xstart = reg->x;
    xend = reg->z + reg->x;
    yend = reg->w + reg->y;

    /* This is hard-coded for now */
    nthreads = US_MAXTHREADS;

    maxpos = w * h;

    thud.th = td;
    thud.data = data;
    for (y = ystart; y < yend; y+=nthreads) {
        for (x = xstart; x < xend; x++) {
            int pos;
            struct vec3 *c;
            pos = y*stride + x;

            if (pos > maxpos || pos < 0) continue;
            c = &buf[pos];
            td->draw(c, svec2(x - reg->x, y - reg->y), &thud);
        }
    }

    return NULL;
}

void draw_with_stride(struct vec3 *buf,
                      struct vec2 res,
                      struct vec4 region,
                      void (*drawfunc)(struct vec3 *, struct vec2, thread_userdata *),
                      void *ud,
                      int stride)
{
    thread_data td[US_MAXTHREADS];
    pthread_t thread[US_MAXTHREADS];
    int t;
    image_data data;

    data.iResolution = res;
    data.ud = ud;
    data.region = &region;

    for (t = 0; t < US_MAXTHREADS; t++) {
        td[t].buf = buf;
        td[t].data = &data;
        td[t].off = t;
        td[t].draw = drawfunc;
        td[t].stride = stride;
        sdfvm_init(&td[t].vm);
        pthread_create(&thread[t], NULL, draw_thread, &td[t]);
    }

    for (t = 0; t < US_MAXTHREADS; t++) {
        pthread_join(thread[t], NULL);
    }
}

void draw(struct vec3 *buf,
          struct vec2 res,
          struct vec4 region,
          void (*drawfunc)(struct vec3 *, struct vec2, thread_userdata *),
          void *ud)
{
    draw_with_stride(buf, res, region, drawfunc, ud, res.x);
}

struct vec3 rgb2color(int r, int g, int b)
{
    float scale = 1.0 / 255;
    return svec3(r * scale, g * scale, b * scale);
}

static int mkcolor(float x)
{
    return floor(x * 255);
}

static void d_fill(struct vec3 *fragColor,
                   struct vec2 fragCoord,
                   thread_userdata *thud)
{
    image_data *id;
    struct vec3 *col;
    id = thud->data;

    col = id->ud;
    *fragColor = *col;
}

static void fill(struct canvas *ctx, struct vec3 clr)
{
    draw(ctx->buf, ctx->res, 
         svec4(0, 0, ctx->res.x, ctx->res.y), 
         d_fill, &clr);
}

static void write_ppm(struct vec3 *buf,
                      struct vec2 res,
                      const char *filename)
{
    int x, y;
    FILE *fp;
    unsigned char *ibuf;

    fp = fopen(filename, "w");
    fprintf(fp, "P6\n%d %d\n%d\n", (int)res.x, (int)res.y, 255);

    ibuf = malloc(3 * res.y * res.x * sizeof(unsigned char));
    for (y = 0; y < res.y; y++) {
        for (x = 0; x < res.x; x++) {
            int pos;
            pos = y * res.x + x;

            ibuf[3*pos] = mkcolor(buf[pos].x);
            ibuf[3*pos + 1] = mkcolor(buf[pos].y);
            ibuf[3*pos + 2] = mkcolor(buf[pos].z);
        }
    }

    fwrite(ibuf, 3 * res.y * res.x * sizeof(unsigned char), 1, fp);
    free(ibuf);
    fclose(fp);
}

void draw_gridlines(struct canvas *ctx)
{
    int x, y;
    int w, h;
    int size;

    w = ctx->res.x;
    h = ctx->res.y;

    size = w / 4;

    for (y = 0; y < h; y += size) {
        for (x = 0; x < w; x++) {
            int pos;
            pos = y*w + x;
            ctx->buf[pos] = svec3_zero();
        }
    }

    for (x = 0; x < w; x += size) {
        for (y = 0; y < h; y++) {
            int pos;
            pos = y*w + x;
            ctx->buf[pos] = svec3_zero();
        }
    }

}

static void d_polygon(struct vec3 *fragColor,
                      struct vec2 st,
                      thread_userdata *thud)
{
    struct vec2 p;
    image_data *id;
    struct vec3 col;
    struct vec2 points[4];
    struct vec2 res;
    sdfvm *vm;
    int i;

    id = thud->data;
    vm = &thud->th->vm;

    res = svec2(id->region->z, id->region->w);
    sdfvm_push_vec2(vm, svec2(st.x, st.y));
    sdfvm_push_vec2(vm, res);
    sdfvm_normalize(vm);
    sdfvm_pop_vec2(vm, &p);
    p.y = p.y*-1;

    points[0] = svec2(-0.5, 0.5);
    points[1] = svec2(-0.1, -0.5);
    points[2] = svec2(0.1, -0.5);
    points[3] = svec2(0.5, 0.5);

    sdfvm_push_vec2(vm, p);
    for (i = 0; i < 4; i++) {
        sdfvm_push_vec2(vm, points[i]);
    }
    sdfvm_poly4(vm);
    sdfvm_push_scalar(vm, 0.1);
    sdfvm_roundness(vm);

    sdfvm_push_vec2(vm, p);
    sdfvm_push_scalar(vm, 0.7);
    sdfvm_circle(vm);
    sdfvm_push_scalar(vm, 0.1);
    sdfvm_lerp(vm);

    sdfvm_push_scalar(vm, -1.0);
    sdfvm_mul(vm);

    sdfvm_gtz(vm);

    sdfvm_push_vec3(vm, *fragColor);
    sdfvm_push_vec3(vm, svec3_zero());
    sdfvm_lerp3(vm);

    sdfvm_pop_vec3(vm, &col);
    *fragColor = col;
}

void polygon(struct canvas *ctx,
           float x, float y,
           float w, float h,
           user_params *p)
{
    draw(ctx->buf, ctx->res, svec4(x, y, w, h), d_polygon, p);
}

int main(int argc, char *argv[])
{
    struct vec3 *buf;
    int width, height;
    struct vec2 res;
    struct canvas ctx;
    int sz;
    int clrpos;
    user_params params;

    /* rainbow colors:
     * Red: 255, 179, 186
     * Orange: 255, 223, 186
     * Yellow: 255, 255, 186
     * Green: 186, 255, 201
     * Blue: 186, 225, 255
     */

    width = 512;
    height = 512;
    clrpos = 0;

    sz = width / 1;

    res = svec2(width, height);

    buf = malloc(width * height * sizeof(struct vec3));

    ctx.res = res;
    ctx.buf = buf;

    sdfvm_init(&params.vm);

    fill(&ctx, svec3(1., 1.0, 1.0));
    polygon(&ctx, 0, 0, sz, sz, &params);
    clrpos = (clrpos + 1) % 5;

    write_ppm(buf, res, "vmdemo.ppm");

    free(buf);
    return 0;
}