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