aboutsummaryrefslogtreecommitdiff
path: root/src/graphics/texture.c
blob: 4c7f68c8ca0db8aa38083bc9a2bcb438484742aa (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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "../../include/mgl/graphics/texture.h"
#include "../../include/mgl/mgl.h"

#define STBI_NO_PSD
#define STBI_NO_TGA
#define STBI_NO_HDR
#define STBI_NO_PIC
#define STBI_NO_PNM
#define STB_IMAGE_IMPLEMENTATION
#include "../../external/stb_image.h"

/* TODO: Check for glTexImage2D failure */

static int mgl_texture_format_to_opengl_format(mgl_texture_format format) {
    switch(format) {
        case MGL_TEXTURE_ALPHA:      return GL_ALPHA8;
        case MGL_TEXTURE_GRAY:       return GL_LUMINANCE8;
        case MGL_TEXTURE_GRAY_ALPHA: return GL_LUMINANCE8_ALPHA8;
        case MGL_TEXTURE_RGB:        return GL_RGB8;
        case MGL_TEXTURE_RGBA:       return GL_RGBA8;
    }
    return 0;
}

static int mgl_texture_format_to_compressed_opengl_format(mgl_texture_format format) {
    switch(format) {
        case MGL_TEXTURE_ALPHA:      return GL_ALPHA8;
        case MGL_TEXTURE_GRAY:       return GL_LUMINANCE8;
        case MGL_TEXTURE_GRAY_ALPHA: return GL_LUMINANCE8_ALPHA8;
        case MGL_TEXTURE_RGB:        return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
        case MGL_TEXTURE_RGBA:       return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
    }
    return 0;
}

static int mgl_texture_format_to_source_opengl_format(mgl_texture_format format) {
    switch(format) {
        case MGL_TEXTURE_ALPHA:      return GL_ALPHA;
        case MGL_TEXTURE_GRAY:       return GL_LUMINANCE8;
        case MGL_TEXTURE_GRAY_ALPHA: return GL_LUMINANCE8_ALPHA8;
        case MGL_TEXTURE_RGB:        return GL_RGB;
        case MGL_TEXTURE_RGBA:       return GL_RGBA;
    }
    return 0;
}

static mgl_texture_format stbi_format_to_mgl_texture_format(int stbi_format) {
    switch(stbi_format) {
        case STBI_grey:
            return MGL_TEXTURE_GRAY;
        case STBI_grey_alpha:
            return MGL_TEXTURE_GRAY_ALPHA;
        case STBI_rgb:
            return MGL_TEXTURE_RGB;
        case STBI_rgb_alpha:
            return MGL_TEXTURE_RGBA;
    }
    return 0;
}

/* TODO: Ensure texture is power of 2 if the hardware doesn't support non power of two textures */
/* TODO: Verify if source format should always be 4 components (RGBA) because apparently if its another format then opengl will internally convert it to RGBA */
int mgl_texture_load_from_file(mgl_texture *self, const char *filepath, mgl_texture_load_options *load_options) {
    self->id = 0;
    
    int format;
    stbi_uc *image_data = stbi_load(filepath, &self->width, &self->height, &format, 0);
    if(!image_data) {
        fprintf(stderr, "Error: failed to load image %s, error: %s\n", filepath, stbi_failure_reason());
        return -1;
    }
    self->format = stbi_format_to_mgl_texture_format(format);

    mgl_context *context = mgl_get_context();
    context->gl.glGenTextures(1, &self->id);
    if(self->id == 0) {
        fprintf(stderr, "Error: failed to load image %s", filepath);
        stbi_image_free(image_data);
        return -1;
    }

    const int opengl_texture_format = load_options && load_options->compressed ? mgl_texture_format_to_compressed_opengl_format(self->format) : mgl_texture_format_to_opengl_format(self->format);

    context->gl.glBindTexture(GL_TEXTURE_2D, self->id);
    context->gl.glTexImage2D(GL_TEXTURE_2D, 0, opengl_texture_format, self->width, self->height, 0, mgl_texture_format_to_source_opengl_format(self->format), GL_UNSIGNED_BYTE, image_data);
    context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    context->gl.glBindTexture(GL_TEXTURE_2D, 0);

    stbi_image_free(image_data);
    return 0;
}

int mgl_texture_load_from_memory(mgl_texture *self, const unsigned char *data, int width, int height, mgl_texture_format format, mgl_texture_load_options *load_options) {
    self->id = 0;
    self->width = width;
    self->height = height;
    self->format = format;

    mgl_context *context = mgl_get_context();
    context->gl.glGenTextures(1, &self->id);
    if(self->id == 0)
        return -1;

    const int opengl_texture_format = load_options && load_options->compressed ? mgl_texture_format_to_compressed_opengl_format(self->format) : mgl_texture_format_to_opengl_format(self->format);

    context->gl.glBindTexture(GL_TEXTURE_2D, self->id);
    context->gl.glTexImage2D(GL_TEXTURE_2D, 0, opengl_texture_format, self->width, self->height, 0, mgl_texture_format_to_source_opengl_format(self->format), GL_UNSIGNED_BYTE, data);
    context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    context->gl.glBindTexture(GL_TEXTURE_2D, 0);

    return 0;
}

void mgl_texture_unload(mgl_texture *self) {
    mgl_context *context = mgl_get_context();
    if(self->id) {
        context->gl.glDeleteTextures(1, &self->id);
        self->id = 0;
    }
    self->width = 0;
    self->height = 0;
    self->format = 0;
}