diff options
author | 3gg <3gg@shellblade.net> | 2025-06-27 10:18:39 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2025-06-27 10:18:39 -0700 |
commit | bd57f345ed9dbed1d81683e48199626de2ea9044 (patch) | |
tree | 4221f2f2a7ad2244d2e93052bd68187ec91b8ea9 /src/util | |
parent | 9a82ce0083437a4f9f58108b2c23b957d2249ad8 (diff) |
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/geometry.c | 44 | ||||
-rw-r--r-- | src/util/ibl.c | 328 | ||||
-rw-r--r-- | src/util/shader.c | 136 | ||||
-rw-r--r-- | src/util/skyquad.c | 161 |
4 files changed, 669 insertions, 0 deletions
diff --git a/src/util/geometry.c b/src/util/geometry.c new file mode 100644 index 0000000..afe0109 --- /dev/null +++ b/src/util/geometry.c | |||
@@ -0,0 +1,44 @@ | |||
1 | #include <gfx/util/geometry.h> | ||
2 | |||
3 | #include <math/vec2.h> | ||
4 | |||
5 | static void make_quad_11_positions(vec2 positions[4]) { | ||
6 | positions[0] = vec2_make(-1, +1); | ||
7 | positions[1] = vec2_make(-1, -1); | ||
8 | positions[2] = vec2_make(+1, +1); | ||
9 | positions[3] = vec2_make(+1, -1); | ||
10 | } | ||
11 | |||
12 | static void make_quad_01_positions(vec2 positions[4]) { | ||
13 | positions[0] = vec2_make(0, 0); | ||
14 | positions[1] = vec2_make(1, 0); | ||
15 | positions[2] = vec2_make(1, 1); | ||
16 | positions[3] = vec2_make(0, 1); | ||
17 | } | ||
18 | |||
19 | static GeometryDesc make_quad_desc(vec2 positions[4]) { | ||
20 | GeometryDesc desc = (GeometryDesc){0}; | ||
21 | desc.positions2d.data = positions; | ||
22 | desc.positions2d.size_bytes = 4 * sizeof(vec2); | ||
23 | desc.num_verts = 4; | ||
24 | desc.type = TriangleStrip; | ||
25 | return desc; | ||
26 | } | ||
27 | |||
28 | Geometry* gfx_make_quad_11(GfxCore* gfxcore) { | ||
29 | assert(gfxcore); | ||
30 | |||
31 | vec2 positions[4]; | ||
32 | make_quad_11_positions(positions); | ||
33 | const GeometryDesc geometry_desc = make_quad_desc(positions); | ||
34 | return gfx_make_geometry(gfxcore, &geometry_desc); | ||
35 | } | ||
36 | |||
37 | Geometry* gfx_make_quad_01(GfxCore* gfxcore) { | ||
38 | assert(gfxcore); | ||
39 | |||
40 | vec2 positions[4]; | ||
41 | make_quad_01_positions(positions); | ||
42 | const GeometryDesc geometry_desc = make_quad_desc(positions); | ||
43 | return gfx_make_geometry(gfxcore, &geometry_desc); | ||
44 | } | ||
diff --git a/src/util/ibl.c b/src/util/ibl.c new file mode 100644 index 0000000..5a79990 --- /dev/null +++ b/src/util/ibl.c | |||
@@ -0,0 +1,328 @@ | |||
1 | #include <gfx/util/ibl.h> | ||
2 | |||
3 | #include <gfx/core.h> | ||
4 | #include <gfx/util/geometry.h> | ||
5 | #include <gfx/util/shader.h> | ||
6 | #include <math/mat4.h> | ||
7 | |||
8 | #include <assert.h> | ||
9 | #include <stdlib.h> | ||
10 | |||
11 | typedef struct IBL { | ||
12 | Geometry* quad; | ||
13 | ShaderProgram* brdf_integration_map_shader; | ||
14 | ShaderProgram* irradiance_map_shader; | ||
15 | ShaderProgram* prefiltered_environment_map_shader; | ||
16 | Texture* brdf_integration_map; | ||
17 | FrameBuffer* framebuffer; | ||
18 | mat4 rotations[6]; | ||
19 | } IBL; | ||
20 | |||
21 | static const CubemapFace faces[6] = { | ||
22 | CubemapFacePosX, // Right. | ||
23 | CubemapFaceNegX, // Left. | ||
24 | CubemapFacePosY, // Up. | ||
25 | CubemapFaceNegY, // Down. | ||
26 | CubemapFacePosZ, // Back. | ||
27 | CubemapFaceNegZ, // Front. | ||
28 | }; | ||
29 | |||
30 | static const float flips[6] = { | ||
31 | -1.0f, // Right. | ||
32 | -1.0f, // Left. | ||
33 | +1.0f, // Up. | ||
34 | +1.0f, // Down. | ||
35 | -1.0f, // Back. | ||
36 | -1.0f, // Front. | ||
37 | }; | ||
38 | |||
39 | IBL* gfx_make_ibl(GfxCore* gfxcore) { | ||
40 | assert(gfxcore); | ||
41 | |||
42 | IBL* ibl = calloc(1, sizeof(IBL)); | ||
43 | if (!ibl) { | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | if (!(ibl->quad = gfx_make_quad_11(gfxcore))) { | ||
48 | goto cleanup; | ||
49 | } | ||
50 | |||
51 | // We only need the BRDF integration once since we are caching the map, but | ||
52 | // compiling the shader up front may lead to fewer surprises. Not that the | ||
53 | // shader is fully compiled up front anyway, since the driver will typically | ||
54 | // defer full compilation to the first draw call. | ||
55 | if (!(ibl->brdf_integration_map_shader = | ||
56 | gfx_make_brdf_integration_map_shader(gfxcore))) { | ||
57 | goto cleanup; | ||
58 | } | ||
59 | |||
60 | if (!(ibl->irradiance_map_shader = gfx_make_irradiance_map_shader(gfxcore))) { | ||
61 | goto cleanup; | ||
62 | } | ||
63 | |||
64 | if (!(ibl->prefiltered_environment_map_shader = | ||
65 | gfx_make_prefiltered_environment_map_shader(gfxcore))) { | ||
66 | goto cleanup; | ||
67 | } | ||
68 | |||
69 | // Create an empty framebuffer for now. Will attach the colour buffer later | ||
70 | // as we render the faces of the cube. | ||
71 | if (!(ibl->framebuffer = gfx_make_framebuffer( | ||
72 | gfxcore, | ||
73 | &(FrameBufferDesc){ | ||
74 | .colour = | ||
75 | (FrameBufferAttachment){.type = FrameBufferNoAttachment}, | ||
76 | .depth = (FrameBufferAttachment){ | ||
77 | .type = FrameBufferNoAttachment}}))) { | ||
78 | goto cleanup; | ||
79 | } | ||
80 | |||
81 | // TODO: Debug the camera rotations. Irradiance debug output should appear | ||
82 | // just like the input cubemap. | ||
83 | |||
84 | // Right. | ||
85 | ibl->rotations[0] = mat4_lookat( | ||
86 | /*position=*/vec3_make(0, 0, 0), | ||
87 | /*target=*/vec3_make(1, 0, 0), | ||
88 | /*up=*/vec3_make(0, 1, 0)); | ||
89 | // Left. | ||
90 | ibl->rotations[1] = mat4_lookat( | ||
91 | /*position=*/vec3_make(0, 0, 0), | ||
92 | /*target=*/vec3_make(-1, 0, 0), | ||
93 | /*up=*/vec3_make(0, 1, 0)); | ||
94 | // Up. | ||
95 | ibl->rotations[2] = mat4_lookat( | ||
96 | /*position=*/vec3_make(0, 0, 0), | ||
97 | /*target=*/vec3_make(0, 1, 0), | ||
98 | /*up=*/vec3_make(0, 0, 1)); | ||
99 | // Down. | ||
100 | ibl->rotations[3] = mat4_lookat( | ||
101 | /*position=*/vec3_make(0, 0, 0), | ||
102 | /*target=*/vec3_make(0, -1, 0), | ||
103 | /*up=*/vec3_make(0, 0, -1)); | ||
104 | // Back. | ||
105 | ibl->rotations[4] = mat4_lookat( | ||
106 | /*position=*/vec3_make(0, 0, 0), | ||
107 | /*target=*/vec3_make(0, 0, 1), | ||
108 | /*up=*/vec3_make(0, 1, 0)); | ||
109 | // Front. | ||
110 | ibl->rotations[5] = mat4_lookat( | ||
111 | /*position=*/vec3_make(0, 0, 0), | ||
112 | /*target=*/vec3_make(0, 0, -1), | ||
113 | /*up=*/vec3_make(0, 1, 0)); | ||
114 | |||
115 | return ibl; | ||
116 | |||
117 | cleanup: | ||
118 | gfx_destroy_ibl(gfxcore, &ibl); | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | void gfx_destroy_ibl(GfxCore* gfxcore, IBL** ibl) { | ||
123 | if (!ibl) { | ||
124 | return; | ||
125 | } | ||
126 | if ((*ibl)->quad) { | ||
127 | gfx_destroy_geometry(gfxcore, &(*ibl)->quad); | ||
128 | } | ||
129 | if ((*ibl)->brdf_integration_map_shader) { | ||
130 | gfx_destroy_shader_program(gfxcore, &(*ibl)->brdf_integration_map_shader); | ||
131 | } | ||
132 | if ((*ibl)->irradiance_map_shader) { | ||
133 | gfx_destroy_shader_program(gfxcore, &(*ibl)->irradiance_map_shader); | ||
134 | } | ||
135 | if ((*ibl)->prefiltered_environment_map_shader) { | ||
136 | gfx_destroy_shader_program( | ||
137 | gfxcore, &(*ibl)->prefiltered_environment_map_shader); | ||
138 | } | ||
139 | if ((*ibl)->brdf_integration_map) { | ||
140 | gfx_destroy_texture(gfxcore, &(*ibl)->brdf_integration_map); | ||
141 | } | ||
142 | if ((*ibl)->framebuffer) { | ||
143 | gfx_destroy_framebuffer(gfxcore, &(*ibl)->framebuffer); | ||
144 | } | ||
145 | free(*ibl); | ||
146 | *ibl = 0; | ||
147 | } | ||
148 | |||
149 | Texture* gfx_make_brdf_integration_map( | ||
150 | IBL* ibl, GfxCore* gfxcore, int width, int height) { | ||
151 | assert(ibl); | ||
152 | assert(gfxcore); | ||
153 | |||
154 | if (ibl->brdf_integration_map) { | ||
155 | return ibl->brdf_integration_map; | ||
156 | } | ||
157 | |||
158 | bool success = false; | ||
159 | |||
160 | if (!(ibl->brdf_integration_map = gfx_make_texture( | ||
161 | gfxcore, &(TextureDesc){ | ||
162 | .width = width, | ||
163 | .height = height, | ||
164 | .depth = 1, | ||
165 | .dimension = Texture2D, | ||
166 | .format = TextureRG16F, | ||
167 | .filtering = LinearFiltering, | ||
168 | .wrap = ClampToEdge, | ||
169 | .mipmaps = false}))) { | ||
170 | goto cleanup; | ||
171 | } | ||
172 | |||
173 | gfx_activate_framebuffer(ibl->framebuffer); | ||
174 | gfx_framebuffer_set_viewport(ibl->framebuffer, 0, 0, width, height); | ||
175 | gfx_activate_shader_program(ibl->brdf_integration_map_shader); | ||
176 | if (!gfx_framebuffer_attach_colour( | ||
177 | ibl->framebuffer, &(FrameBufferAttachment){ | ||
178 | .type = FrameBufferTexture, | ||
179 | .texture.texture = ibl->brdf_integration_map, | ||
180 | .texture.mip_level = 0})) { | ||
181 | goto cleanup; | ||
182 | } | ||
183 | gfx_render_geometry(ibl->quad); | ||
184 | |||
185 | success = true; | ||
186 | |||
187 | cleanup: | ||
188 | gfx_deactivate_shader_program(ibl->brdf_integration_map_shader); | ||
189 | gfx_deactivate_framebuffer(ibl->framebuffer); | ||
190 | if (!success && ibl->brdf_integration_map) { | ||
191 | gfx_destroy_texture(gfxcore, &ibl->brdf_integration_map); | ||
192 | return 0; | ||
193 | } else { | ||
194 | return ibl->brdf_integration_map; | ||
195 | } | ||
196 | } | ||
197 | |||
198 | Texture* gfx_make_irradiance_map( | ||
199 | IBL* ibl, GfxCore* gfxcore, const Texture* environment_map, int width, | ||
200 | int height) { | ||
201 | assert(ibl); | ||
202 | assert(gfxcore); | ||
203 | assert(environment_map); | ||
204 | |||
205 | bool success = false; | ||
206 | |||
207 | Texture* irradiance_map = 0; | ||
208 | |||
209 | // TODO: Could define colour-renderable texture formats separately to make | ||
210 | // framebuffer creation less error-prone. Or, at the very least, validate the | ||
211 | // choice at runtime. | ||
212 | // | ||
213 | // Make sure to use a float colour format to avoid [0,1] clamping when the | ||
214 | // irradiance values are computed! | ||
215 | if (!(irradiance_map = gfx_make_texture( | ||
216 | gfxcore, &(TextureDesc){ | ||
217 | .width = width, | ||
218 | .height = height, | ||
219 | .depth = 1, | ||
220 | .dimension = TextureCubeMap, | ||
221 | .format = TextureR11G11B10F, | ||
222 | .filtering = LinearFiltering, | ||
223 | .mipmaps = false}))) { | ||
224 | goto cleanup; | ||
225 | } | ||
226 | |||
227 | gfx_activate_framebuffer(ibl->framebuffer); | ||
228 | gfx_framebuffer_set_viewport(ibl->framebuffer, 0, 0, width, height); | ||
229 | gfx_activate_shader_program(ibl->irradiance_map_shader); | ||
230 | gfx_set_texture_uniform(ibl->irradiance_map_shader, "Sky", environment_map); | ||
231 | for (int i = 0; i < 6; ++i) { | ||
232 | if (!gfx_framebuffer_attach_colour( | ||
233 | ibl->framebuffer, &(FrameBufferAttachment){ | ||
234 | .type = FrameBufferCubemapTexture, | ||
235 | .cubemap.face = faces[i], | ||
236 | .cubemap.texture = irradiance_map})) { | ||
237 | goto cleanup; | ||
238 | } | ||
239 | gfx_set_float_uniform(ibl->irradiance_map_shader, "Flip", flips[i]); | ||
240 | gfx_set_mat4_uniform( | ||
241 | ibl->irradiance_map_shader, "CameraRotation", &ibl->rotations[i]); | ||
242 | gfx_apply_uniforms(ibl->irradiance_map_shader); | ||
243 | gfx_render_geometry(ibl->quad); | ||
244 | } | ||
245 | |||
246 | success = true; | ||
247 | |||
248 | cleanup: | ||
249 | gfx_deactivate_shader_program(ibl->irradiance_map_shader); | ||
250 | gfx_deactivate_framebuffer(ibl->framebuffer); | ||
251 | if (!success && irradiance_map) { | ||
252 | gfx_destroy_texture(gfxcore, &irradiance_map); | ||
253 | return 0; | ||
254 | } else { | ||
255 | return irradiance_map; | ||
256 | } | ||
257 | } | ||
258 | |||
259 | Texture* gfx_make_prefiltered_environment_map( | ||
260 | IBL* ibl, GfxCore* gfxcore, const Texture* environment_map, int width, | ||
261 | int height, int* max_mip_level) { | ||
262 | assert(ibl); | ||
263 | assert(gfxcore); | ||
264 | assert(environment_map); | ||
265 | assert(max_mip_level); | ||
266 | |||
267 | bool success = false; | ||
268 | |||
269 | Texture* prefiltered_env_map = 0; | ||
270 | |||
271 | if (!(prefiltered_env_map = gfx_make_texture( | ||
272 | gfxcore, &(TextureDesc){ | ||
273 | .width = width, | ||
274 | .height = height, | ||
275 | .depth = 1, | ||
276 | .dimension = TextureCubeMap, | ||
277 | .format = TextureR11G11B10F, | ||
278 | .filtering = LinearFiltering, | ||
279 | .mipmaps = true}))) { | ||
280 | goto cleanup; | ||
281 | } | ||
282 | |||
283 | gfx_activate_framebuffer(ibl->framebuffer); | ||
284 | gfx_activate_shader_program(ibl->prefiltered_environment_map_shader); | ||
285 | gfx_set_texture_uniform( | ||
286 | ibl->prefiltered_environment_map_shader, "Sky", environment_map); | ||
287 | const int max_mip = (int)(rlog2(min(width, height))); | ||
288 | for (int mip = 0; mip <= max_mip; ++mip) { | ||
289 | const int mip_width = width >> mip; | ||
290 | const int mip_height = height >> mip; | ||
291 | const float roughness = (float)mip / (float)(max_mip); | ||
292 | gfx_framebuffer_set_viewport(ibl->framebuffer, 0, 0, mip_width, mip_height); | ||
293 | gfx_set_float_uniform( | ||
294 | ibl->prefiltered_environment_map_shader, "Roughness", roughness); | ||
295 | |||
296 | for (int i = 0; i < 6; ++i) { | ||
297 | if (!gfx_framebuffer_attach_colour( | ||
298 | ibl->framebuffer, &(FrameBufferAttachment){ | ||
299 | .type = FrameBufferCubemapTexture, | ||
300 | .cubemap.face = faces[i], | ||
301 | .cubemap.mip_level = mip, | ||
302 | .cubemap.texture = prefiltered_env_map})) { | ||
303 | goto cleanup; | ||
304 | } | ||
305 | gfx_set_float_uniform( | ||
306 | ibl->prefiltered_environment_map_shader, "Flip", flips[i]); | ||
307 | gfx_set_mat4_uniform( | ||
308 | ibl->prefiltered_environment_map_shader, "CameraRotation", | ||
309 | &ibl->rotations[i]); | ||
310 | gfx_apply_uniforms(ibl->prefiltered_environment_map_shader); | ||
311 | gfx_render_geometry(ibl->quad); | ||
312 | } | ||
313 | } | ||
314 | |||
315 | *max_mip_level = max_mip; | ||
316 | |||
317 | success = true; | ||
318 | |||
319 | cleanup: | ||
320 | gfx_deactivate_shader_program(ibl->prefiltered_environment_map_shader); | ||
321 | gfx_deactivate_framebuffer(ibl->framebuffer); | ||
322 | if (!success && prefiltered_env_map) { | ||
323 | gfx_destroy_texture(gfxcore, &prefiltered_env_map); | ||
324 | return 0; | ||
325 | } else { | ||
326 | return prefiltered_env_map; | ||
327 | } | ||
328 | } | ||
diff --git a/src/util/shader.c b/src/util/shader.c new file mode 100644 index 0000000..f5c22cc --- /dev/null +++ b/src/util/shader.c | |||
@@ -0,0 +1,136 @@ | |||
1 | #include <gfx/util/shader.h> | ||
2 | |||
3 | #include <gfx/core.h> | ||
4 | #include <shaders/brdf_integration_map.frag.h> | ||
5 | #include <shaders/cook_torrance.frag.h> | ||
6 | #include <shaders/cook_torrance.vert.h> | ||
7 | #include <shaders/cubemap_filtering.vert.h> | ||
8 | #include <shaders/debug3d.frag.h> | ||
9 | #include <shaders/debug3d.vert.h> | ||
10 | #include <shaders/immediate_mode.frag.h> | ||
11 | #include <shaders/immediate_mode.vert.h> | ||
12 | #include <shaders/irradiance_map.frag.h> | ||
13 | #include <shaders/prefiltered_environment_map.frag.h> | ||
14 | #include <shaders/quad.vert.h> | ||
15 | #include <shaders/skyquad.frag.h> | ||
16 | #include <shaders/skyquad.vert.h> | ||
17 | #include <shaders/view_normal_mapped_normals.frag.h> | ||
18 | #include <shaders/view_normal_mapped_normals.vert.h> | ||
19 | #include <shaders/view_normals.frag.h> | ||
20 | #include <shaders/view_normals.vert.h> | ||
21 | #include <shaders/view_tangents.frag.h> | ||
22 | #include <shaders/view_tangents.vert.h> | ||
23 | #include <shaders/view_texture.frag.h> | ||
24 | #include <shaders/view_texture.vert.h> | ||
25 | |||
26 | #include <assert.h> | ||
27 | #include <string.h> | ||
28 | |||
29 | static ShaderProgram* make_shader_program( | ||
30 | GfxCore* gfxcore, const char* vert_source, const char* frag_source, | ||
31 | const ShaderCompilerDefine* defines, size_t num_defines) { | ||
32 | assert(gfxcore); | ||
33 | assert(vert_source); | ||
34 | assert(frag_source); | ||
35 | |||
36 | Shader* vert = 0; | ||
37 | Shader* frag = 0; | ||
38 | |||
39 | ShaderDesc vertex_shader_desc = { | ||
40 | .code = vert_source, .type = VertexShader, .num_defines = num_defines}; | ||
41 | ShaderDesc fragment_shader_desc = { | ||
42 | .code = frag_source, .type = FragmentShader, .num_defines = num_defines}; | ||
43 | if (num_defines > 0) { | ||
44 | memcpy( | ||
45 | vertex_shader_desc.defines, defines, | ||
46 | num_defines * sizeof(ShaderCompilerDefine)); | ||
47 | memcpy( | ||
48 | fragment_shader_desc.defines, defines, | ||
49 | num_defines * sizeof(ShaderCompilerDefine)); | ||
50 | } | ||
51 | vert = gfx_make_shader(gfxcore, &vertex_shader_desc); | ||
52 | if (!vert) { | ||
53 | goto cleanup; | ||
54 | } | ||
55 | frag = gfx_make_shader(gfxcore, &fragment_shader_desc); | ||
56 | if (!frag) { | ||
57 | goto cleanup; | ||
58 | } | ||
59 | |||
60 | ShaderProgramDesc shader_program_desc = { | ||
61 | .vertex_shader = vert, .fragment_shader = frag}; | ||
62 | ShaderProgram* prog = gfx_make_shader_program(gfxcore, &shader_program_desc); | ||
63 | if (!prog) { | ||
64 | goto cleanup; | ||
65 | } | ||
66 | return prog; | ||
67 | |||
68 | cleanup: | ||
69 | if (vert) { | ||
70 | gfx_destroy_shader(gfxcore, &vert); | ||
71 | } | ||
72 | if (frag) { | ||
73 | gfx_destroy_shader(gfxcore, &frag); | ||
74 | } | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | ShaderProgram* gfx_make_brdf_integration_map_shader(GfxCore* gfxcore) { | ||
79 | return make_shader_program( | ||
80 | gfxcore, quad_vert, brdf_integration_map_frag, 0, 0); | ||
81 | } | ||
82 | |||
83 | ShaderProgram* gfx_make_cook_torrance_shader(GfxCore* gfxcore) { | ||
84 | return make_shader_program( | ||
85 | gfxcore, cook_torrance_vert, cook_torrance_frag, 0, 0); | ||
86 | } | ||
87 | |||
88 | ShaderProgram* gfx_make_cook_torrance_shader_perm( | ||
89 | GfxCore* gfxcore, const ShaderCompilerDefine* defines, size_t num_defines) { | ||
90 | return make_shader_program( | ||
91 | gfxcore, cook_torrance_vert, cook_torrance_frag, defines, num_defines); | ||
92 | } | ||
93 | |||
94 | ShaderProgram* gfx_make_immediate_mode_shader(GfxCore* gfxcore) { | ||
95 | return make_shader_program( | ||
96 | gfxcore, immediate_mode_vert, immediate_mode_frag, 0, 0); | ||
97 | } | ||
98 | |||
99 | ShaderProgram* gfx_make_irradiance_map_shader(GfxCore* gfxcore) { | ||
100 | return make_shader_program( | ||
101 | gfxcore, cubemap_filtering_vert, irradiance_map_frag, 0, 0); | ||
102 | } | ||
103 | |||
104 | ShaderProgram* gfx_make_prefiltered_environment_map_shader(GfxCore* gfxcore) { | ||
105 | return make_shader_program( | ||
106 | gfxcore, cubemap_filtering_vert, prefiltered_environment_map_frag, 0, 0); | ||
107 | } | ||
108 | |||
109 | ShaderProgram* gfx_make_debug3d_shader(GfxCore* gfxcore) { | ||
110 | return make_shader_program(gfxcore, debug3d_vert, debug3d_frag, 0, 0); | ||
111 | } | ||
112 | |||
113 | ShaderProgram* gfx_make_skyquad_shader(GfxCore* gfxcore) { | ||
114 | return make_shader_program(gfxcore, skyquad_vert, skyquad_frag, 0, 0); | ||
115 | } | ||
116 | |||
117 | ShaderProgram* gfx_make_view_normal_mapped_normals_shader(GfxCore* gfxcore) { | ||
118 | return make_shader_program( | ||
119 | gfxcore, view_normal_mapped_normals_vert, view_normal_mapped_normals_frag, | ||
120 | 0, 0); | ||
121 | } | ||
122 | |||
123 | ShaderProgram* gfx_make_view_normals_shader(GfxCore* gfxcore) { | ||
124 | return make_shader_program( | ||
125 | gfxcore, view_normals_vert, view_normals_frag, 0, 0); | ||
126 | } | ||
127 | |||
128 | ShaderProgram* gfx_make_view_tangents_shader(GfxCore* gfxcore) { | ||
129 | return make_shader_program( | ||
130 | gfxcore, view_tangents_vert, view_tangents_frag, 0, 0); | ||
131 | } | ||
132 | |||
133 | ShaderProgram* gfx_make_view_texture_shader(GfxCore* gfxcore) { | ||
134 | return make_shader_program( | ||
135 | gfxcore, view_texture_vert, view_texture_frag, 0, 0); | ||
136 | } | ||
diff --git a/src/util/skyquad.c b/src/util/skyquad.c new file mode 100644 index 0000000..08fa044 --- /dev/null +++ b/src/util/skyquad.c | |||
@@ -0,0 +1,161 @@ | |||
1 | #include <gfx/util/skyquad.h> | ||
2 | |||
3 | #include <gfx/core.h> | ||
4 | #include <gfx/gfx.h> | ||
5 | #include <gfx/scene/light.h> | ||
6 | #include <gfx/scene/material.h> | ||
7 | #include <gfx/scene/mesh.h> | ||
8 | #include <gfx/scene/node.h> | ||
9 | #include <gfx/scene/object.h> | ||
10 | #include <gfx/scene/scene.h> | ||
11 | #include <gfx/util/geometry.h> | ||
12 | #include <gfx/util/shader.h> | ||
13 | |||
14 | #include <math/vec4.h> | ||
15 | |||
16 | #include <assert.h> | ||
17 | |||
18 | SceneObject* gfx_make_skyquad(GfxCore* gfxcore, const Texture* texture) { | ||
19 | assert(gfxcore); | ||
20 | assert(texture); | ||
21 | |||
22 | ShaderProgram* shader = 0; | ||
23 | Geometry* geometry = 0; | ||
24 | Material* material = 0; | ||
25 | Mesh* mesh = 0; | ||
26 | SceneObject* object = 0; | ||
27 | |||
28 | shader = gfx_make_skyquad_shader(gfxcore); | ||
29 | if (!shader) { | ||
30 | goto cleanup; | ||
31 | } | ||
32 | |||
33 | geometry = gfx_make_quad_11(gfxcore); | ||
34 | if (!geometry) { | ||
35 | goto cleanup; | ||
36 | } | ||
37 | |||
38 | MaterialDesc material_desc = (MaterialDesc){0}; | ||
39 | material_desc.uniforms[0] = (ShaderUniform){ | ||
40 | .type = UniformTexture, | ||
41 | .value.texture = texture, | ||
42 | .name = sstring_make("Skyquad")}; | ||
43 | material_desc.num_uniforms = 1; | ||
44 | material = gfx_make_material(&material_desc); | ||
45 | if (!material) { | ||
46 | goto cleanup; | ||
47 | } | ||
48 | |||
49 | MeshDesc mesh_desc = (MeshDesc){0}; | ||
50 | mesh_desc.geometry = geometry; | ||
51 | mesh_desc.material = material; | ||
52 | mesh_desc.shader = shader; | ||
53 | mesh = gfx_make_mesh(&mesh_desc); | ||
54 | if (!mesh) { | ||
55 | goto cleanup; | ||
56 | } | ||
57 | |||
58 | object = gfx_make_object(&(ObjectDesc){.num_meshes = 1, .meshes = {mesh}}); | ||
59 | if (!object) { | ||
60 | goto cleanup; | ||
61 | } | ||
62 | |||
63 | return object; | ||
64 | |||
65 | cleanup: | ||
66 | if (shader) { | ||
67 | gfx_destroy_shader_program(gfxcore, &shader); | ||
68 | } | ||
69 | if (geometry) { | ||
70 | gfx_destroy_geometry(gfxcore, &geometry); | ||
71 | } | ||
72 | if (material) { | ||
73 | gfx_destroy_material(&material); | ||
74 | } | ||
75 | if (mesh) { | ||
76 | gfx_destroy_mesh(&mesh); | ||
77 | } | ||
78 | if (object) { | ||
79 | gfx_destroy_object(&object); | ||
80 | } | ||
81 | return false; | ||
82 | } | ||
83 | |||
84 | /// Create an environment light node. | ||
85 | static SceneNode* make_environment_light( | ||
86 | SceneNode* root, const Texture* environment_map) { | ||
87 | assert(root); | ||
88 | assert(environment_map); | ||
89 | |||
90 | Light* light = 0; | ||
91 | SceneNode* light_node = 0; | ||
92 | |||
93 | light = gfx_make_light(&(LightDesc){ | ||
94 | .type = EnvironmentLightType, | ||
95 | .light = {(EnvironmentLightDesc){.environment_map = environment_map}}}); | ||
96 | if (!light) { | ||
97 | goto cleanup; | ||
98 | } | ||
99 | |||
100 | light_node = gfx_make_light_node(light); | ||
101 | if (!light_node) { | ||
102 | goto cleanup; | ||
103 | } | ||
104 | gfx_set_node_parent(light_node, root); | ||
105 | |||
106 | return light_node; | ||
107 | |||
108 | cleanup: | ||
109 | if (light) { | ||
110 | gfx_destroy_light(&light); | ||
111 | } | ||
112 | if (light_node) { | ||
113 | gfx_destroy_node(&light_node); | ||
114 | } | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | SceneNode* gfx_setup_skyquad( | ||
119 | GfxCore* gfxcore, SceneNode* root, const Texture* environment_map) { | ||
120 | assert(gfxcore); | ||
121 | assert(root); | ||
122 | assert(environment_map); | ||
123 | |||
124 | SceneObject* skyquad_object = 0; | ||
125 | SceneNode* object_node = 0; | ||
126 | SceneNode* light_node = 0; | ||
127 | |||
128 | // Create the skyquad object. | ||
129 | skyquad_object = gfx_make_skyquad(gfxcore, environment_map); | ||
130 | if (!skyquad_object) { | ||
131 | goto cleanup; | ||
132 | } | ||
133 | |||
134 | // Create an object node to render the skyquad in the background. | ||
135 | object_node = gfx_make_object_node(skyquad_object); | ||
136 | if (!object_node) { | ||
137 | goto cleanup; | ||
138 | } | ||
139 | gfx_set_node_parent(object_node, root); | ||
140 | |||
141 | // Create an environment light node under which to root objects affected by | ||
142 | // the skyquad. | ||
143 | light_node = make_environment_light(root, environment_map); | ||
144 | if (!light_node) { | ||
145 | goto cleanup; | ||
146 | } | ||
147 | |||
148 | return light_node; | ||
149 | |||
150 | cleanup: | ||
151 | if (skyquad_object) { | ||
152 | gfx_destroy_object(&skyquad_object); | ||
153 | } | ||
154 | if (object_node) { | ||
155 | gfx_destroy_node(&object_node); | ||
156 | } | ||
157 | if (light_node) { | ||
158 | gfx_destroy_node(&light_node); | ||
159 | } | ||
160 | return 0; | ||
161 | } | ||