summaryrefslogtreecommitdiff
path: root/src/resource.c
blob: 2b617d4c2530b112906d7341db8e56120d2f17b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#define STB_IMAGE_IMPLEMENTATION
#include "lib/stb_image.h"
#include <assert.h>
#include "util/file.h"
#include "util/err.h"
#include "resource.h"
#include "cube.h"

static void image_copy(unsigned int dst_size[2], rgba_data dst, unsigned int src_size[2], const rgba_data src, unsigned int off[2])
{
    assert(off[0] + dst_size[0] <= src_size[0]);
    assert(off[1] + dst_size[1] <= src_size[1]);
    for (unsigned int y = 0; y < dst_size[1]; y++)
        memcpy(
            (char *)dst + dst_size[0]*y * 4,
            (const char *)src + ((off[1] + y)*src_size[0] + off[0]) * 4,
            dst_size[0]*4);
}

static texture_id create_texture(struct texture_source *tex, struct source *src, struct draw_backend *draw)
{
    FILE *f = file_open(tex->path.str, "rb");
    if (!f)
        source_error(src, tex->path.loc, "%.*s: failed to open file", PSTR(tex->path.str));
    int width, height;
    void *img_data = stbi_load_from_file(f, &width, &height, nullptr, 4);
    if (!img_data)
        source_error(src, tex->path.loc, "%.*s: failed to load image", PSTR(tex->path.str));
    unsigned int img_size[2] = { width, height };
    texture_id id;
    switch (tex->type) {
        case TEXTURE_2D:
            if (!draw->create_texture(tex->type, img_size, &img_data, &id))
                source_error(src, tex->path.loc, "%.*s: failed to create texture", PSTR(tex->path.str));
            break;
        case TEXTURE_CUBEMAP: {
            if (img_size[0] % 4)
                source_error(src, tex->path.loc, "%.*s: cubemap image width is not divisible by 4", PSTR(tex->path.str));
            if (img_size[1] % 3)
                source_error(src, tex->path.loc, "%.*s: cubemap image height is not divisible by 3", PSTR(tex->path.str));
            unsigned int face_size[2] = { img_size[0] / 4, img_size[1] / 3 };
            static const unsigned int coords[6][2] = {
                // right, left, top, bottom, front, back
                { 2, 1 }, { 0, 1 }, { 1, 0 }, { 1, 2 }, { 1, 1 }, { 1, 3 },
            };
            rgba_data faces[6];
            size_t face_len = 4 * face_size[0] * face_size[1];
            void *buffer = malloc(6 * face_len);
            for (size_t i = 0; i < 6; i++)
                image_copy(
                    face_size,
                    faces[i] = (char *)buffer + face_len*i,
                    img_size,
                    img_data,
                    (unsigned int [2]) { coords[i][0] * face_size[0], coords[i][1] * face_size[1] });
            if (!draw->create_texture(tex->type, face_size, faces, &id))
                source_error(src, tex->path.loc, "%.*s: failed to create texture", PSTR(tex->path.str));
            free(buffer);
        }
    }
    stbi_image_free(img_data);
    return id;
}

static mesh_id create_mesh(struct mesh_source *mesh, struct source *src, struct draw_backend *draw)
{
    (void) draw;
    source_error(src, mesh->path.loc, "TODO: obj loading");
}

#define LOAD_CACHED(what, cache_field) do { \
        what ## _id id, *p_id = nullptr; \
        map_find(&cache->cache_field, what->source.path.str, &p_id); \
        if (p_id) { \
            id = *p_id; \
        } else { \
            id = create_ ## what(&what->source, src, draw); \
            map_insert_end(&cache->cache_field, what->source.path.str, id); \
        } \
        what->id = id; \
    } while (0)

void texture_load(struct texture *texture, struct source *src, struct draw_backend *draw, struct cache *cache)
{
    LOAD_CACHED(texture, textures[texture->source.type]);
}

void mesh_load(struct mesh *mesh, struct source *src, struct draw_backend *draw, struct cache *cache)
{
    switch (mesh->source.type) {
        case MESH_CUBE:
            if (cache->cube_mesh.present) {
                mesh->id = cache->cube_mesh.data;
            } else {
                if (!draw->create_mesh(sizeof cube_verts / sizeof *cube_verts, cube_verts, &mesh->id))
                    eprintf("failed to create cube mesh\n");
                cache->cube_mesh.present = true;
                cache->cube_mesh.data = mesh->id;
            }
            break;
        case MESH_OBJ:
            LOAD_CACHED(mesh, meshes);
            break;
    }
}

void cache_free(struct cache *cache)
{
    free(cache->textures[TEXTURE_2D].ptr);
    free(cache->textures[TEXTURE_CUBEMAP].ptr);
    free(cache->meshes.ptr);
}