Exemple #1
0
parg_mesh* parg_mesh_aar(parg_aar rect)
{
    parg_mesh* surf = malloc(sizeof(struct parg_mesh_s));
    surf->normals = 0;
    surf->indices = 0;
    surf->ntriangles = 2;
    int vertexCount = 4;
    int vertexStride = sizeof(float) * 2;
    surf->coords =
        parg_buffer_alloc(vertexCount * vertexStride, PARG_GPU_ARRAY);
    float* position = (float*) parg_buffer_lock(surf->coords, PARG_WRITE);
    *position++ = rect.left;
    *position++ = rect.bottom;
    *position++ = rect.right;
    *position++ = rect.bottom;
    *position++ = rect.left;
    *position++ = rect.top;
    *position++ = rect.right;
    *position = rect.top;
    parg_buffer_unlock(surf->coords);
    surf->uvs = parg_buffer_alloc(vertexCount * vertexStride, PARG_GPU_ARRAY);
    float* texcoord = (float*) parg_buffer_lock(surf->uvs, PARG_WRITE);
    *texcoord++ = 0;
    *texcoord++ = 0;
    *texcoord++ = 1;
    *texcoord++ = 0;
    *texcoord++ = 0;
    *texcoord++ = 1;
    *texcoord++ = 1;
    *texcoord = 1;
    parg_buffer_unlock(surf->uvs);
    return surf;
}
Exemple #2
0
parg_mesh* parg_mesh_from_shape(struct par_shapes_mesh_s const* src)
{
    parg_mesh* dst = calloc(sizeof(struct parg_mesh_s), 1);
    dst->coords = parg_buffer_alloc(4 * 3 * src->npoints, PARG_GPU_ARRAY);
    float* pcoords = (float*) parg_buffer_lock(dst->coords, PARG_WRITE);
    memcpy(pcoords, src->points, 4 * 3 * src->npoints);
    parg_buffer_unlock(dst->coords);
    if (src->tcoords) {
        dst->uvs = parg_buffer_alloc(4 * 2 * src->npoints, PARG_GPU_ARRAY);
        float* puvs = (float*) parg_buffer_lock(dst->uvs, PARG_WRITE);
        memcpy(puvs, src->tcoords, 4 * 2 * src->npoints);
        parg_buffer_unlock(dst->uvs);
    }
    if (src->normals) {
        dst->normals = parg_buffer_alloc(4 * 3 * src->npoints, PARG_GPU_ARRAY);
        float* pnorms = (float*) parg_buffer_lock(dst->normals, PARG_WRITE);
        memcpy(pnorms, src->normals, 4 * 3 * src->npoints);
        parg_buffer_unlock(dst->normals);
    }
    dst->indices =
        parg_buffer_alloc(2 * 3 * src->ntriangles, PARG_GPU_ELEMENTS);
    uint16_t* ptris = (uint16_t*) parg_buffer_lock(dst->indices, PARG_WRITE);
    memcpy(ptris, src->triangles, 2 * 3 * src->ntriangles);
    parg_buffer_unlock(dst->indices);
    dst->ntriangles = src->ntriangles;
    return dst;
}
Exemple #3
0
parg_mesh* parg_mesh_knot(int slices, int stacks, float major, float minor)
{
    parg_mesh* surf = malloc(sizeof(struct parg_mesh_s));
    float ds = 1.0f / slices;
    float dt = 1.0f / stacks;
    int vertexCount = slices * stacks * 3;
    int vertexStride = sizeof(float) * 3;
    surf->coords =
        parg_buffer_alloc(vertexCount * vertexStride, PARG_GPU_ARRAY);
    surf->normals =
        parg_buffer_alloc(vertexCount * vertexStride, PARG_GPU_ARRAY);
    surf->uvs = 0;
    Point3* position = (Point3*) parg_buffer_lock(surf->coords, PARG_WRITE);
    Vector3* normal = (Vector3*) parg_buffer_lock(surf->normals, PARG_WRITE);
    for (float s = 0; s < 1 - ds / 2; s += ds) {
        for (float t = 0; t < 1 - dt / 2; t += dt) {
            const float E = 0.01f;
            Point3 p = knot_fn(s, t);
            Vector3 u = P3Sub(knot_fn(s + E, t), p);
            Vector3 v = P3Sub(knot_fn(s, t + E), p);
            Vector3 n = V3Normalize(V3Cross(u, v));
            *position++ = p;
            *normal++ = n;
        }
    }
    parg_buffer_unlock(surf->coords);
    parg_buffer_unlock(surf->normals);

    surf->ntriangles = slices * stacks * 2;
    int indexCount = surf->ntriangles * 3;
    surf->indices = parg_buffer_alloc(indexCount * 2, PARG_GPU_ELEMENTS);
    uint16_t* index = (uint16_t*) parg_buffer_lock(surf->indices, PARG_WRITE);
    int v = 0;
    for (int i = 0; i < slices - 1; i++) {
        for (int j = 0; j < stacks; j++) {
            int next = (j + 1) % stacks;
            *index++ = v + next + stacks;
            *index++ = v + next;
            *index++ = v + j;
            *index++ = v + j;
            *index++ = v + j + stacks;
            *index++ = v + next + stacks;
        }
        v += stacks;
    }
    for (int j = 0; j < stacks; j++) {
        int next = (j + 1) % stacks;
        *index++ = next;
        *index++ = v + next;
        *index++ = v + j;
        *index++ = v + j;
        *index++ = j;
        *index++ = next;
    }
    parg_buffer_unlock(surf->indices);
    return surf;
}
Exemple #4
0
parg_buffer* parg_buffer_dup(parg_buffer* srcbuf, parg_buffer_type memtype)
{
    int nbytes = parg_buffer_length(srcbuf);
    parg_buffer* dstbuf = parg_buffer_alloc(nbytes, memtype);
    void* src = parg_buffer_lock(srcbuf, PARG_READ);
    void* dst = parg_buffer_lock(dstbuf, PARG_WRITE);
    memcpy(dst, src, nbytes);
    parg_buffer_unlock(dstbuf);
    parg_buffer_unlock(srcbuf);
    return dstbuf;
}
Exemple #5
0
void parg_mesh_compute_normals(parg_mesh* mesh)
{
    par_shapes_mesh m = {0};
    int nbytes = parg_buffer_length(mesh->coords);
    m.points = (float*) parg_buffer_lock(mesh->coords, PARG_READ);
    m.triangles = (uint16_t*) parg_buffer_lock(mesh->indices, PARG_READ);
    m.npoints = nbytes / 12;
    m.ntriangles = mesh->ntriangles;
    par_shapes_compute_normals(&m);
    mesh->normals = parg_buffer_create(m.normals, nbytes, PARG_CPU);
    parg_buffer_unlock(mesh->coords);
    parg_buffer_unlock(mesh->indices);
    free(m.normals);
}
Exemple #6
0
void parg_mesh_update_from_shape(parg_mesh* dst,
    struct par_shapes_mesh_s const* src)
{
    float* pcoords = (float*) parg_buffer_lock(dst->coords, PARG_WRITE);
    memcpy(pcoords, src->points, 4 * 3 * src->npoints);
    parg_buffer_unlock(dst->coords);
}
Exemple #7
0
void init(float winwidth, float winheight, float pixratio)
{
    const Vector4 bgcolor = {0.937, 0.937, 0.93, 1.00};
    parg_state_clearcolor(bgcolor);
    parg_state_cullfaces(1);
    parg_state_depthtest(1);
    parg_shader_load_from_asset(SHADER_SIMPLE);

    int* rawdata;
    colorbuf = parg_buffer_slurp_asset(TEXTURE_COLOR, (void*) &rawdata);
    int width = *rawdata++;
    int height = *rawdata++;
    int ncomps = *rawdata++;
    parg_buffer_unlock(colorbuf);

    colortex =
        parg_texture_from_u8(colorbuf, width, height, ncomps, 3 * sizeof(int));
    graybuf = parg_buffer_from_asset(BIN_ISLAND);
    graytex = parg_texture_from_fp32(graybuf, IMGWIDTH, IMGHEIGHT, 1, 0);
    const float h = 1.5f;
    const float w = h * winwidth / winheight;
    const float znear = 10;
    const float zfar = 90;
    projection = M4MakeFrustum(-w, w, -h, h, znear, zfar);
    Point3 eye = {0, -50, 50};
    Point3 target = {0, 0, 0};
    Vector3 up = {0, 1, 0};
    view = M4MakeLookAt(eye, target, up);
    rectmesh = parg_mesh_rectangle(20, 20);
}
Exemple #8
0
parg_buffer* parg_buffer_to_gpu(parg_buffer* cpubuf, parg_buffer_type memtype)
{
    int nbytes = parg_buffer_length(cpubuf);
    void* bytes = parg_buffer_lock(cpubuf, PARG_READ);
    parg_buffer* gpubuf = parg_buffer_create(bytes, nbytes, memtype);
    parg_buffer_unlock(cpubuf);
    return gpubuf;
}
Exemple #9
0
void parg_buffer_to_file(parg_buffer* buf, const char* filepath)
{
    FILE* f = fopen(filepath, "wb");
    parg_verify(f, "Unable to open file", filepath);
    char* contents = parg_buffer_lock(buf, PARG_READ);
    fwrite(contents, 1, parg_buffer_length(buf), f);
    fclose(f);
    parg_buffer_unlock(buf);
}
Exemple #10
0
parg_buffer* parg_buffer_from_file(const char* filepath)
{
    FILE* f = fopen(filepath, "rb");
    parg_verify(f, "Unable to open file", filepath);
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    fseek(f, 0, SEEK_SET);
    parg_buffer* retval = parg_buffer_alloc(fsize + 1, PARG_CPU);
    char* contents = parg_buffer_lock(retval, PARG_WRITE);
    fread(contents, fsize, 1, f);
    fclose(f);
    contents[fsize] = 0;
    parg_buffer_unlock(retval);
    return retval;
}
Exemple #11
0
void init(float winwidth, float winheight, float pixratio)
{
    printf(
        "Spacebar to toggle texture modes.\n"
        "D to toggle auto-zooming demo mode.\n"
        "G to toggle the slippy map grid.\n");
    parg_state_clearcolor((Vector4){0.43, 0.61, 0.8, 1});
    parg_state_cullfaces(1);
    parg_state_depthtest(0);
    parg_shader_load_from_asset(SHADER_DEFAULT);
    parg_zcam_init(1, 1, fovy);
    parg_zcam_set_position(0, 0, STARTZ);
    ocean_texture = parg_texture_from_asset(TEXTURE_OCEAN);
    paper_texture = parg_texture_from_asset(TEXTURE_PAPER);

    // Decode the europe image.
    int* rawdata;
    parg_buffer* colorbuf =
        parg_buffer_slurp_asset(TEXTURE_EUROPE, (void*) &rawdata);
    int width = *rawdata++;
    int height = *rawdata++;
    int ncomps = *rawdata++;
    parg_texture_fliprows(rawdata, width * ncomps, height);

    // Sample the ocean color from one corner of the image.
    int ocean_color = rawdata[0];

    // Perform marching squares and generate a mesh.
    par_msquares_meshlist* mlist =
        par_msquares_color((parg_byte*) rawdata, width, height, 16, ocean_color,
            4, PAR_MSQUARES_SWIZZLE | PAR_MSQUARES_DUAL | PAR_MSQUARES_HEIGHTS |
            PAR_MSQUARES_SIMPLIFY);
    par_msquares_mesh const* mesh;
    mesh = par_msquares_get_mesh(mlist, 0);
    landmass_mesh = parg_mesh_create(
        mesh->points, mesh->npoints, mesh->triangles, mesh->ntriangles);
    mesh = par_msquares_get_mesh(mlist, 1);
    ocean_mesh = parg_mesh_create(
        mesh->points, mesh->npoints, mesh->triangles, mesh->ntriangles);
    parg_buffer_unlock(colorbuf);
    par_msquares_free(mlist);
}
Exemple #12
0
void parg_asset_preload(parg_token id)
{
    if (!_pngsuffix) {
        _pngsuffix = sdsnew(".png");
    }
    sds filename = parg_token_to_sds(id);
    parg_buffer* buf = parg_buffer_from_path(filename);
    parg_assert(buf, "Unable to load asset");
    if (sdslen(filename) > 4) {
        sds suffix = sdsdup(filename);
        sdsrange(suffix, -4, -1);
        if (!sdscmp(suffix, _pngsuffix)) {
            unsigned char* decoded;
            unsigned dims[3] = {0, 0, 4};
            unsigned char* filedata = parg_buffer_lock(buf, PARG_READ);
            unsigned err = lodepng_decode_memory(&decoded, &dims[0], &dims[1],
                    filedata, parg_buffer_length(buf), LCT_RGBA, 8);
            parg_assert(err == 0, "PNG decoding error");
            parg_buffer_free(buf);
            int nbytes = dims[0] * dims[1] * dims[2];
            buf = parg_buffer_alloc(nbytes + 12, PARG_CPU);
            int* ptr = parg_buffer_lock(buf, PARG_WRITE);
            *ptr++ = dims[0];
            *ptr++ = dims[1];
            *ptr++ = dims[2];
            memcpy(ptr, decoded, nbytes);
            free(decoded);
            parg_buffer_unlock(buf);
        }
        sdsfree(suffix);
    }
    if (!_asset_registry) {
        _asset_registry = kh_init(assmap);
    }
    int ret;
    int iter = kh_put(assmap, _asset_registry, id, &ret);
    kh_value(_asset_registry, iter) = buf;
}
Exemple #13
0
parg_mesh* parg_mesh_sierpinski(float width, int depth)
{
    parg_mesh* surf = malloc(sizeof(struct parg_mesh_s));
    surf->normals = 0;
    surf->indices = 0;
    surf->uvs = 0;
    surf->ntriangles = pow(3, depth);
    int vstride = sizeof(float) * 2;
    int ntriangles = 1;
    int nverts = ntriangles * 3;
    float height = width * sqrt(0.75);

    parg_buffer* src = parg_buffer_alloc(nverts * vstride, PARG_CPU);
    float* psrc = (float*) parg_buffer_lock(src, PARG_WRITE);
    *psrc++ = 0;
    *psrc++ = height * 0.5;
    *psrc++ = width * 0.5;
    *psrc++ = -height * 0.5;
    *psrc++ = -width * 0.5;
    *psrc++ = -height * 0.5;
    parg_buffer_unlock(src);

#define WRITE_TRIANGLE(a, b, c) \
    *pdst++ = x[a];             \
    *pdst++ = y[a];             \
    *pdst++ = x[b];             \
    *pdst++ = y[b];             \
    *pdst++ = x[c];             \
    *pdst++ = y[c]

    float x[6];
    float y[6];
    while (depth--) {
        ntriangles *= 3;
        nverts = ntriangles * 3;
        parg_buffer* dst = parg_buffer_alloc(nverts * vstride, PARG_CPU);
        float* pdst = parg_buffer_lock(dst, PARG_WRITE);
        const float* psrc = parg_buffer_lock(src, PARG_READ);
        for (int i = 0; i < ntriangles / 3; i++) {
            x[0] = *psrc++;
            y[0] = *psrc++;
            x[1] = *psrc++;
            y[1] = *psrc++;
            x[2] = *psrc++;
            y[2] = *psrc++;
            x[3] = 0.5 * (x[0] + x[1]);
            y[3] = 0.5 * (y[0] + y[1]);
            x[4] = 0.5 * (x[1] + x[2]);
            y[4] = 0.5 * (y[1] + y[2]);
            x[5] = 0.5 * (x[0] + x[2]);
            y[5] = 0.5 * (y[0] + y[2]);
            WRITE_TRIANGLE(0, 3, 5);
            WRITE_TRIANGLE(3, 1, 4);
            WRITE_TRIANGLE(5, 4, 2);
        }
        parg_buffer_unlock(src);
        parg_buffer_unlock(dst);
        parg_buffer_free(src);
        src = dst;
    }

    assert(surf->ntriangles == ntriangles);
    surf->coords = parg_buffer_alloc(nverts * vstride, PARG_GPU_ARRAY);
    float* pdst = parg_buffer_lock(surf->coords, PARG_WRITE);
    psrc = parg_buffer_lock(src, PARG_READ);
    memcpy(pdst, psrc, nverts * vstride);
    parg_buffer_unlock(src);
    parg_buffer_unlock(surf->coords);
    parg_buffer_free(src);
    return surf;
}
Exemple #14
0
parg_mesh* parg_mesh_torus(int slices, int stacks, float major, float minor)
{
    parg_mesh* surf = malloc(sizeof(struct parg_mesh_s));
    float dphi = PARG_TWOPI / stacks;
    float dtheta = PARG_TWOPI / slices;
    int vertexCount = slices * stacks * 3;
    int vertexStride = sizeof(float) * 3;
    surf->coords =
        parg_buffer_alloc(vertexCount * vertexStride, PARG_GPU_ARRAY);
    surf->uvs = 0;
    Point3* position = (Point3*) parg_buffer_lock(surf->coords, PARG_WRITE);
    for (int slice = 0; slice < slices; slice++) {
        float theta = slice * dtheta;
        for (int stack = 0; stack < stacks; stack++) {
            float phi = stack * dphi;
            *position++ = torus_fn(major, minor, phi, theta);
        }
    }
    parg_buffer_unlock(surf->coords);

    surf->normals =
        parg_buffer_alloc(vertexCount * vertexStride, PARG_GPU_ARRAY);
    Vector3* normal = (Vector3*) parg_buffer_lock(surf->normals, PARG_WRITE);
    for (int slice = 0; slice < slices; slice++) {
        float theta = slice * dtheta;
        for (int stack = 0; stack < stacks; stack++) {
            float phi = stack * dphi;
            Point3 p = torus_fn(major, minor, phi, theta);
            Point3 p1 = torus_fn(major, minor, phi, theta + 0.01);
            Point3 p2 = torus_fn(major, minor, phi + 0.01, theta);
            Vector3 du = P3Sub(p2, p);
            Vector3 dv = P3Sub(p1, p);
            *normal = V3Normalize(V3Cross(du, dv));
            ++normal;
        }
    }
    parg_buffer_unlock(surf->normals);

    surf->ntriangles = slices * stacks * 2;
    int indexCount = surf->ntriangles * 3;
    surf->indices = parg_buffer_alloc(indexCount * 2, PARG_GPU_ELEMENTS);
    uint16_t* index = (uint16_t*) parg_buffer_lock(surf->indices, PARG_WRITE);
    int v = 0;
    for (int i = 0; i < slices - 1; i++) {
        for (int j = 0; j < stacks; j++) {
            int next = (j + 1) % stacks;
            *index++ = v + next + stacks;
            *index++ = v + next;
            *index++ = v + j;
            *index++ = v + j;
            *index++ = v + j + stacks;
            *index++ = v + next + stacks;
        }
        v += stacks;
    }
    for (int j = 0; j < stacks; j++) {
        int next = (j + 1) % stacks;
        *index++ = next;
        *index++ = v + next;
        *index++ = v + j;
        *index++ = v + j;
        *index++ = j;
        *index++ = next;
    }
    parg_buffer_unlock(surf->indices);
    return surf;
}
Exemple #15
0
static void create_mesh()
{
    par_msquares_meshlist* mlist = 0;
    float threshold = 0;
    int flags = 0;
    if (state == STATE_GRAY_DEFAULT) {
        float const* graydata = parg_buffer_lock(graybuf, PARG_READ);
        mlist = par_msquares_grayscale(
            graydata, IMGWIDTH, IMGHEIGHT, CELLSIZE, threshold, flags);
        parg_buffer_unlock(graybuf);
    } else if (state == STATE_GRAY_SIMPLIFY) {
        float const* graydata = parg_buffer_lock(graybuf, PARG_READ);
        flags = PAR_MSQUARES_SIMPLIFY;
        mlist = par_msquares_grayscale(
            graydata, IMGWIDTH, IMGHEIGHT, CELLSIZE, threshold, flags);
        parg_buffer_unlock(graybuf);
    } else if (state == STATE_GRAY_INVERT) {
        float const* graydata = parg_buffer_lock(graybuf, PARG_READ);
        flags = PAR_MSQUARES_INVERT;
        mlist = par_msquares_grayscale(
            graydata, IMGWIDTH, IMGHEIGHT, CELLSIZE, threshold, flags);
        parg_buffer_unlock(graybuf);
    } else if (state == STATE_GRAY_DUAL) {
        float const* graydata = parg_buffer_lock(graybuf, PARG_READ);
        flags = PAR_MSQUARES_DUAL;
        mlist = par_msquares_grayscale(
            graydata, IMGWIDTH, IMGHEIGHT, CELLSIZE, threshold, flags);
        parg_buffer_unlock(graybuf);
    } else if (state == STATE_GRAY_HEIGHTS) {
        float const* graydata = parg_buffer_lock(graybuf, PARG_READ);
        flags = PAR_MSQUARES_HEIGHTS;
        mlist = par_msquares_grayscale(
            graydata, IMGWIDTH, IMGHEIGHT, CELLSIZE, threshold, flags);
        parg_buffer_unlock(graybuf);
    } else if (state == STATE_GRAY_DHS) {
        float const* graydata = parg_buffer_lock(graybuf, PARG_READ);
        flags = PAR_MSQUARES_DUAL | PAR_MSQUARES_HEIGHTS | PAR_MSQUARES_SNAP;
        mlist = par_msquares_grayscale(
            graydata, IMGWIDTH, IMGHEIGHT, CELLSIZE, threshold, flags);
        parg_buffer_unlock(graybuf);
    } else if (state == STATE_GRAY_DHSC) {
        float const* graydata = parg_buffer_lock(graybuf, PARG_READ);
        flags = PAR_MSQUARES_DUAL | PAR_MSQUARES_HEIGHTS | PAR_MSQUARES_SNAP |
            PAR_MSQUARES_CONNECT;
        mlist = par_msquares_grayscale(
            graydata, IMGWIDTH, IMGHEIGHT, CELLSIZE, threshold, flags);
        parg_buffer_unlock(graybuf);
    } else if (state == STATE_GRAY_MULTI) {
        float const* graydata = parg_buffer_lock(graybuf, PARG_READ);
        float thresholds[] = {0.0, 0.1};
        flags = PAR_MSQUARES_SIMPLIFY | PAR_MSQUARES_HEIGHTS |
            PAR_MSQUARES_SNAP | PAR_MSQUARES_CONNECT;
        mlist = par_msquares_grayscale_multi(
            graydata, IMGWIDTH, IMGHEIGHT, CELLSIZE, thresholds, 2, flags);
        parg_buffer_unlock(graybuf);
    } else if (state == STATE_COLOR_DEFAULT) {
        parg_byte const* rgbadata = parg_buffer_lock(colorbuf, PARG_READ);
        rgbadata += sizeof(int) * 3;
        mlist = par_msquares_color(
            rgbadata, IMGWIDTH, IMGHEIGHT, CELLSIZE, 0x214562, 4, flags);
        parg_buffer_unlock(colorbuf);
    } else if (state == STATE_COLOR_IH) {
        parg_byte const* rgbadata = parg_buffer_lock(colorbuf, PARG_READ);
        rgbadata += sizeof(int) * 3;
        flags = PAR_MSQUARES_INVERT | PAR_MSQUARES_HEIGHTS;
        mlist = par_msquares_color(
            rgbadata, IMGWIDTH, IMGHEIGHT, CELLSIZE, 0x214562, 4, flags);
        parg_buffer_unlock(colorbuf);
    } else if (state == STATE_COLOR_DHSCSI) {
        parg_byte const* rgbadata = parg_buffer_lock(colorbuf, PARG_READ);
        rgbadata += sizeof(int) * 3;
        flags = PAR_MSQUARES_DUAL | PAR_MSQUARES_HEIGHTS | PAR_MSQUARES_SNAP |
            PAR_MSQUARES_CONNECT | PAR_MSQUARES_SIMPLIFY |
            PAR_MSQUARES_INVERT;
        mlist = par_msquares_color(
            rgbadata, IMGWIDTH, IMGHEIGHT, CELLSIZE, 0x214562, 4, flags);
        parg_buffer_unlock(colorbuf);
    } else if (state == STATE_MULTI_RGB) {
        unsigned dims[2] = {0, 0};
        unsigned char* pixels;
        lodepng_decode_file(
            &pixels, &dims[0], &dims[1], "extern/par/test/rgb.png", LCT_RGB, 8);
        mlist = par_msquares_color_multi(
            pixels, dims[0], dims[1], 16, 3, PAR_MSQUARES_SIMPLIFY);
        free(pixels);
    } else if (state == STATE_MULTI_RGBA) {
        unsigned dims[2] = {0, 0};
        unsigned char* pixels;
        lodepng_decode_file(&pixels, &dims[0], &dims[1],
            "extern/par/test/rgba.png", LCT_RGBA, 8);
        mlist = par_msquares_color_multi(pixels, dims[0], dims[1], 16, 4,
                PAR_MSQUARES_HEIGHTS | PAR_MSQUARES_CONNECT |
                PAR_MSQUARES_SIMPLIFY);
        free(pixels);
    } else if (state == STATE_MULTI_DIAGRAM) {
        parg_byte const* rgbadata = parg_buffer_lock(colorbuf, PARG_READ);
        rgbadata += sizeof(int) * 3;
        mlist = par_msquares_color_multi(rgbadata, IMGWIDTH, IMGHEIGHT,
                CELLSIZE, 4, PAR_MSQUARES_SIMPLIFY | PAR_MSQUARES_HEIGHTS);
        parg_buffer_unlock(colorbuf);
    }

    nmeshes = par_msquares_get_count(mlist);
    printf("%d meshes\n", nmeshes);
    for (int imesh = 0; imesh < nmeshes; imesh++) {
        par_msquares_mesh const* mesh = par_msquares_get_mesh(mlist, imesh);

        // mquares_mesh might have dimensionality of 2 or 3, while parg_mesh
        // only
        // supports the latter.  So, we potentially need to expand the data from
        // vec2 to vec3.

        float* points = mesh->points;
        if (mesh->dim == 2) {
            points = malloc(mesh->npoints * sizeof(float) * 3);
            for (int i = 0; i < mesh->npoints; i++) {
                points[i * 3] = mesh->points[i * 2];
                points[i * 3 + 1] = mesh->points[i * 2 + 1];
                points[i * 3 + 2] = 0;
            }
        }
        meshcolors[imesh] = mesh->color;
        trimesh[imesh] = parg_mesh_create(
            points, mesh->npoints, mesh->triangles, mesh->ntriangles);
        if (mesh->dim == 2) {
            free(points);
        }
    }

    par_msquares_free(mlist);
}