diff options
Diffstat (limited to 'src/asset/texture.c')
-rw-r--r-- | src/asset/texture.c | 177 |
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 | |||
12 | static 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. | ||
46 | Texture* 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 | } | ||