aboutsummaryrefslogtreecommitdiff
path: root/src/asset/texture.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/asset/texture.c')
-rw-r--r--src/asset/texture.c177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/asset/texture.c b/src/asset/texture.c
new file mode 100644
index 0000000..c790394
--- /dev/null
+++ b/src/asset/texture.c
@@ -0,0 +1,177 @@
1#include "texture.h"
2
3#include "gfx/core.h"
4
5#include "error.h"
6
7#define STB_IMAGE_IMPLEMENTATION
8#include "stb_image.h"
9
10#include <assert.h>
11
12static void flip_horizontally(
13 unsigned char* pixels, int width, int height, int components) {
14 assert(pixels);
15
16 for (int y = 0; y < height; ++y) {
17 for (int x = 0; x < width / 2; ++x) {
18 unsigned char* p1 = &pixels[(y * width + x) * components];
19 unsigned char* p2 = &pixels[(y * width + (width - x - 1)) * components];
20
21 for (int c = 0; c < components; ++c) {
22 unsigned char tmp = *p1;
23 *p1 = *p2;
24 *p2 = tmp;
25 p1++;
26 p2++;
27 }
28 }
29 }
30}
31
32// Note that the cubemap coordinate system uses the one in RenderMan:
33//
34// https://www.khronos.org/opengl/wiki/Cubemap_Texture
35//
36// This is what happens:
37//
38// - Cubemaps follow a left-handed coordinate system. Say, +X is right, +Y is
39// up, and +Z is forward.
40// - The texture coordinate system follow's DirectX's, so +V goes down, not up
41// like it does in OpenGL.
42//
43// For this reason, we do X and Y flips when doing cubemap textures so that we
44// can sample cubemaps as if they were given in the usual OpenGL coordinate
45// system.
46Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) {
47 assert(gfxcore);
48 assert(cmd);
49 assert(cmd->origin == AssetFromFile || cmd->origin == AssetFromMemory);
50 assert(cmd->type == LoadTexture || cmd->type == LoadCubemap);
51
52 int width, height, components, old_components;
53 unsigned char* pixels[6] = {0};
54
55 switch (cmd->origin) {
56 case AssetFromFile:
57 switch (cmd->type) {
58 case LoadTexture: {
59 const char* filepath = mstring_cstr(&cmd->data.texture.filepath);
60 stbi_set_flip_vertically_on_load(0);
61 pixels[0] = stbi_load(filepath, &width, &height, &components, 0);
62 if (!pixels[0]) {
63 log_error("Failed to load texture file: %s", filepath);
64 }
65 break;
66 }
67 case LoadCubemap:
68 for (int i = 0; i < 6; ++i) {
69 // Flip +Y and -Y textures vertically.
70 stbi_set_flip_vertically_on_load(((i == 2) || (i == 3)) ? 1 : 0);
71 const char* filepath =
72 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_x + i);
73 stbi_uc* image_pixels =
74 stbi_load(filepath, &width, &height, &components, 0);
75 if (!image_pixels) {
76 log_error("Failed to load texture file: %s", filepath);
77 break;
78 }
79 if (i > 0 && components != old_components) {
80 log_error("All textures in a cubemap must have the same number of "
81 "components");
82 break;
83 }
84 if ((i != 2) && (i != 3)) {
85 flip_horizontally(image_pixels, width, height, components);
86 }
87 pixels[i] = image_pixels;
88 old_components = components;
89 }
90 break;
91 }
92 break;
93 case AssetFromMemory:
94 // TODO: Load textures from memory.
95 log_error("Loading textures from memory is not yet implemented");
96 return 0;
97 }
98
99 // Error out if we failed to load a texture.
100 if (!pixels[0] ||
101 (cmd->type == LoadCubemap &&
102 (!pixels[1] || !pixels[2] || !pixels[3] || !pixels[4] || !pixels[5]))) {
103 for (int i = 0; i < 6; ++i) {
104 if (pixels[i]) {
105 stbi_image_free(pixels[i]);
106 }
107 }
108 return 0;
109 }
110
111 TextureDesc desc = (TextureDesc){0};
112 desc.width = width;
113 desc.height = height;
114
115 switch (cmd->type) {
116 case LoadTexture:
117 desc.dimension = Texture2D;
118 break;
119 case LoadCubemap:
120 desc.dimension = TextureCubeMap;
121 break;
122 }
123
124 switch (components) {
125 case 3:
126 switch (cmd->colour_space) {
127 case LinearColourSpace:
128 desc.format = TextureRGB8;
129 break;
130 case sRGB:
131 desc.format = TextureSRGB8;
132 break;
133 default:
134 log_error("Unsupported texture colour space: %d", cmd->colour_space);
135 return 0;
136 }
137 break;
138 case 4:
139 switch (cmd->colour_space) {
140 case LinearColourSpace:
141 desc.format = TextureRGBA8;
142 break;
143 case sRGB:
144 desc.format = TextureSRGBA8;
145 break;
146 default:
147 log_error("Unsupported texture colour space: %d", cmd->colour_space);
148 return 0;
149 }
150 break;
151 default:
152 log_error("Unsupported number of texture components: %d", components);
153 return 0;
154 }
155
156 desc.filtering = cmd->filtering;
157 desc.mipmaps = cmd->mipmaps;
158
159 switch (cmd->type) {
160 case LoadTexture:
161 desc.data.pixels = pixels[0];
162 break;
163 case LoadCubemap:
164 for (int i = 0; i < 6; ++i) {
165 *(&desc.data.cubemap.pixels_pos_x + i) = pixels[i];
166 }
167 break;
168 }
169
170 Texture* texture = gfx_make_texture(gfxcore, &desc);
171 for (int i = 0; i < 6; ++i) {
172 if (pixels[i]) {
173 stbi_image_free(pixels[i]);
174 }
175 }
176 return texture;
177}