diff options
Diffstat (limited to 'gfx/src/core/texture.c')
-rw-r--r-- | gfx/src/core/texture.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/gfx/src/core/texture.c b/gfx/src/core/texture.c new file mode 100644 index 0000000..89f7ec0 --- /dev/null +++ b/gfx/src/core/texture.c | |||
@@ -0,0 +1,218 @@ | |||
1 | #include "texture.h" | ||
2 | |||
3 | #include <gfx_assert.h> | ||
4 | |||
5 | #include <error.h> | ||
6 | #include <math/defs.h> | ||
7 | |||
8 | #include <stdbool.h> | ||
9 | |||
10 | bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { | ||
11 | assert(texture); | ||
12 | assert(desc); | ||
13 | |||
14 | glGenTextures(1, &texture->id); | ||
15 | if (!texture->id) { | ||
16 | log_error("glGenTextures() failed"); | ||
17 | return false; | ||
18 | } | ||
19 | texture->target = to_GL_dimension(desc->dimension); | ||
20 | glBindTexture(texture->target, texture->id); | ||
21 | |||
22 | // glTexStorageXD | ||
23 | const int levels = | ||
24 | desc->mipmaps | ||
25 | ? max(max(log2(desc->width), log2(desc->height)), log2(desc->depth)) + | ||
26 | 1 | ||
27 | : 1; | ||
28 | const GLenum internal_format = to_GL_internal_format(desc->format); | ||
29 | switch (texture->target) { | ||
30 | case GL_TEXTURE_2D: | ||
31 | case GL_TEXTURE_CUBE_MAP: | ||
32 | glTexStorage2D( | ||
33 | texture->target, levels, internal_format, desc->width, desc->height); | ||
34 | break; | ||
35 | default: | ||
36 | FAIL("Unhandled texture dimension"); | ||
37 | gfx_del_texture(texture); | ||
38 | return false; | ||
39 | } | ||
40 | |||
41 | texture->format = to_GL_format(desc->format); | ||
42 | texture->type = to_GL_type(desc->format); | ||
43 | texture->width = desc->width; | ||
44 | texture->height = desc->height; | ||
45 | gfx_update_texture(texture, &desc->data); | ||
46 | |||
47 | // gfx_update_texture() unbinds the texture at the end, so re-bind it here. | ||
48 | glBindTexture(texture->target, texture->id); | ||
49 | |||
50 | // Mipmaps. | ||
51 | if (desc->mipmaps) { | ||
52 | glGenerateMipmap(texture->target); | ||
53 | } | ||
54 | |||
55 | // Texture filtering. | ||
56 | const bool linear = desc->filtering == LinearFiltering; | ||
57 | GLenum min = desc->mipmaps ? (linear ? GL_LINEAR_MIPMAP_LINEAR | ||
58 | : GL_NEAREST_MIPMAP_NEAREST) | ||
59 | : (linear ? GL_LINEAR : GL_NEAREST); | ||
60 | GLenum mag = linear ? GL_LINEAR : GL_NEAREST; | ||
61 | glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, min); | ||
62 | glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, mag); | ||
63 | |||
64 | // Texture wrapping. | ||
65 | GLenum wrap = GL_INVALID_ENUM; | ||
66 | switch (desc->wrap) { | ||
67 | case Repeat: | ||
68 | wrap = GL_REPEAT; | ||
69 | break; | ||
70 | case ClampToEdge: | ||
71 | wrap = GL_CLAMP_TO_EDGE; | ||
72 | break; | ||
73 | } | ||
74 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_R, wrap); | ||
75 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, wrap); | ||
76 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, wrap); | ||
77 | |||
78 | glBindTexture(texture->target, 0); | ||
79 | return true; | ||
80 | } | ||
81 | |||
82 | void gfx_del_texture(Texture* texture) { | ||
83 | assert(texture); | ||
84 | |||
85 | if (texture->id) { | ||
86 | glDeleteTextures(1, &texture->id); | ||
87 | texture->id = 0; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | void gfx_update_texture(Texture* texture, const TextureDataDesc* desc) { | ||
92 | assert(texture); | ||
93 | assert(desc); | ||
94 | |||
95 | glBindTexture(texture->target, texture->id); | ||
96 | |||
97 | // glTexSubImageXD | ||
98 | switch (texture->target) { | ||
99 | case GL_TEXTURE_2D: | ||
100 | if (desc->pixels) { | ||
101 | glTexSubImage2D( | ||
102 | GL_TEXTURE_2D, /*level=*/0, /*xoffset=*/0, | ||
103 | /*yoffset=*/0, texture->width, texture->height, texture->format, | ||
104 | texture->type, desc->pixels); | ||
105 | } | ||
106 | break; | ||
107 | case GL_TEXTURE_CUBE_MAP: | ||
108 | for (int i = 0; i < 6; ++i) { | ||
109 | const void* pixels = *(&desc->cubemap.pixels_pos_x + i); | ||
110 | if (pixels) { | ||
111 | glTexSubImage2D( | ||
112 | GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, /*level=*/0, /*xoffset=*/0, | ||
113 | /*yoffset=*/0, texture->width, texture->height, texture->format, | ||
114 | texture->type, pixels); | ||
115 | } | ||
116 | } | ||
117 | break; | ||
118 | default: | ||
119 | FAIL("Unhandled texture dimension"); | ||
120 | break; | ||
121 | } | ||
122 | |||
123 | glBindTexture(texture->target, 0); | ||
124 | } | ||
125 | |||
126 | GLenum to_GL_dimension(TextureDimension dim) { | ||
127 | switch (dim) { | ||
128 | case Texture2D: | ||
129 | return GL_TEXTURE_2D; | ||
130 | case TextureCubeMap: | ||
131 | return GL_TEXTURE_CUBE_MAP; | ||
132 | default: | ||
133 | FAIL("Unhandled TextureDimension"); | ||
134 | return GL_INVALID_ENUM; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | GLenum to_GL_internal_format(TextureFormat format) { | ||
139 | switch (format) { | ||
140 | case TextureDepth: | ||
141 | return GL_DEPTH_COMPONENT; | ||
142 | case TextureRG16: | ||
143 | return GL_RG16; | ||
144 | case TextureRG16F: | ||
145 | return GL_RG16F; | ||
146 | case TextureRGB8: | ||
147 | return GL_RGB8; | ||
148 | case TextureR11G11B10F: | ||
149 | return GL_R11F_G11F_B10F; | ||
150 | case TextureRGBA8: | ||
151 | return GL_RGBA8; | ||
152 | case TextureSRGB8: | ||
153 | return GL_SRGB8; | ||
154 | case TextureSRGBA8: | ||
155 | return GL_SRGB8_ALPHA8; | ||
156 | default: | ||
157 | FAIL("Unhandled TextureFormat"); | ||
158 | return GL_INVALID_ENUM; | ||
159 | } | ||
160 | } | ||
161 | |||
162 | GLenum to_GL_format(TextureFormat format) { | ||
163 | switch (format) { | ||
164 | case TextureDepth: | ||
165 | return GL_DEPTH_COMPONENT; | ||
166 | case TextureRG16: | ||
167 | case TextureRG16F: | ||
168 | return GL_RG; | ||
169 | case TextureRGB8: | ||
170 | case TextureR11G11B10F: | ||
171 | case TextureSRGB8: | ||
172 | return GL_RGB; | ||
173 | case TextureRGBA8: | ||
174 | case TextureSRGBA8: | ||
175 | return GL_RGBA; | ||
176 | default: | ||
177 | FAIL("Unhandled TextureFormat"); | ||
178 | return GL_INVALID_ENUM; | ||
179 | } | ||
180 | } | ||
181 | |||
182 | GLenum to_GL_type(TextureFormat format) { | ||
183 | switch (format) { | ||
184 | case TextureDepth: | ||
185 | case TextureRG16F: | ||
186 | case TextureR11G11B10F: | ||
187 | return GL_FLOAT; | ||
188 | case TextureRG16: | ||
189 | case TextureRGB8: | ||
190 | case TextureRGBA8: | ||
191 | case TextureSRGB8: | ||
192 | case TextureSRGBA8: | ||
193 | return GL_UNSIGNED_BYTE; | ||
194 | default: | ||
195 | FAIL("Unhandled TextureFormat"); | ||
196 | return GL_INVALID_ENUM; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | GLenum to_GL_cubemap_face(CubemapFace face) { | ||
201 | switch (face) { | ||
202 | case CubemapFacePosX: | ||
203 | return GL_TEXTURE_CUBE_MAP_POSITIVE_X; | ||
204 | case CubemapFaceNegX: | ||
205 | return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; | ||
206 | case CubemapFacePosY: | ||
207 | return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; | ||
208 | case CubemapFaceNegY: | ||
209 | return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; | ||
210 | case CubemapFacePosZ: | ||
211 | return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; | ||
212 | case CubemapFaceNegZ: | ||
213 | return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; | ||
214 | default: | ||
215 | FAIL("Unhandled CubemapFace"); | ||
216 | return GL_INVALID_ENUM; | ||
217 | } | ||
218 | } | ||