summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2023-02-11 17:56:56 -0800
committer3gg <3gg@shellblade.net>2023-02-11 17:56:56 -0800
commita58dbb9081a08b01931badd953ed1fe6cd5bd1c7 (patch)
treea335076f364bdd28d341c3ef2dac318bd0d447a6
parent53582d2a90fb72e02056612163239b3b3f62eb4a (diff)
Fix IBL cubemap coordinate system and put position-normal-tangent in world space in CookTorrance.
-rw-r--r--gfx/shaders/cook_torrance.frag10
-rw-r--r--gfx/shaders/cook_torrance.vert12
-rw-r--r--gfx/shaders/cubemap_filtering.vert27
-rw-r--r--gfx/shaders/irradiance_map.frag9
-rw-r--r--gfx/shaders/skyquad.frag2
-rw-r--r--gfx/shaders/skyquad.vert17
-rw-r--r--gfx/src/renderer/renderer.c10
-rw-r--r--gfx/src/util/ibl.c23
-rw-r--r--gfx/src/util/texture.c42
-rw-r--r--gltfview/src/game.c15
10 files changed, 127 insertions, 40 deletions
diff --git a/gfx/shaders/cook_torrance.frag b/gfx/shaders/cook_torrance.frag
index 1adb4ae..1975491 100644
--- a/gfx/shaders/cook_torrance.frag
+++ b/gfx/shaders/cook_torrance.frag
@@ -30,7 +30,7 @@ uniform float MaxReflectionLOD;
30 30
31uniform vec3 CameraPosition; // World space. 31uniform vec3 CameraPosition; // World space.
32 32
33// World-space position and normal. 33// World-space position, normal and tangent.
34in vec3 Position; 34in vec3 Position;
35#ifdef HAS_NORMALS 35#ifdef HAS_NORMALS
36in vec3 Normal; 36in vec3 Normal;
@@ -53,13 +53,14 @@ layout (location = 0) out vec4 Colour;
53/// |normalMapSample| is the normal map sample, not necessarily normalized. 53/// |normalMapSample| is the normal map sample, not necessarily normalized.
54/// 54///
55/// TODO: Move to "normal.h" 55/// TODO: Move to "normal.h"
56#if defined(HAS_NORMAL_MAP) && (defined(HAS_TANGENTS) || defined(HAS_TEXCOORDS))
56vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) { 57vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) {
57 vec3 N = normalize(Normal); 58 vec3 N = normalize(Normal);
58#ifdef HAS_TANGENTS 59#ifdef HAS_TANGENTS
59 //vec3 T = normalize(tangent.xyz - dot(tangent.xyz, N) * N); 60 //vec3 T = normalize(tangent.xyz - dot(tangent.xyz, N) * N);
60 vec3 T = Tangent.xyz; 61 vec3 T = Tangent.xyz;
61 vec3 B = Tangent.w * cross(N, T); 62 vec3 B = Tangent.w * cross(N, T);
62#else // No tangents 63#elif HAS_TEXCOORDS // No tangents, but must have texcoords.
63 vec3 pos_dx = dFdx(Position); 64 vec3 pos_dx = dFdx(Position);
64 vec3 pos_dy = dFdy(Position); 65 vec3 pos_dy = dFdy(Position);
65 // vec3 uv_dx = vec3(dFdx(Texcoord), 0.0); 66 // vec3 uv_dx = vec3(dFdx(Texcoord), 0.0);
@@ -71,7 +72,7 @@ vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) {
71 // vec3 T = pos_dx * uv_dy.t - pos_dy * uv_dx.t; 72 // vec3 T = pos_dx * uv_dy.t - pos_dy * uv_dx.t;
72 T = normalize(T - dot(T, N) * N); 73 T = normalize(T - dot(T, N) * N);
73 vec3 B = normalize(cross(N, T)); 74 vec3 B = normalize(cross(N, T));
74#endif // HAS_TANGENTS 75#endif
75 76
76 if (gl_FrontFacing == false) { 77 if (gl_FrontFacing == false) {
77 T = -T; 78 T = -T;
@@ -83,6 +84,7 @@ vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) {
83 //return normalize(s.x * T + s.y * B + s.z * N); 84 //return normalize(s.x * T + s.y * B + s.z * N);
84 return normalize(mat3(T,B,N) * s); 85 return normalize(mat3(T,B,N) * s);
85} 86}
87#endif // HAS_TANGENTS || HAS_TEXCOORDS
86 88
87float trowbridge_reitz_GGX(float roughness, float NdotH) { 89float trowbridge_reitz_GGX(float roughness, float NdotH) {
88 float a = roughness * roughness; 90 float a = roughness * roughness;
@@ -188,7 +190,7 @@ void main()
188#else 190#else
189 float occlusion = 1.0; 191 float occlusion = 1.0;
190#endif 192#endif
191 float metallic = metal_roughness.x; 193 float metallic = metal_roughness.x;
192 float roughness = metal_roughness.y; 194 float roughness = metal_roughness.y;
193 195
194 // For a single light direction: 196 // For a single light direction:
diff --git a/gfx/shaders/cook_torrance.vert b/gfx/shaders/cook_torrance.vert
index 697bb0c..5f126c0 100644
--- a/gfx/shaders/cook_torrance.vert
+++ b/gfx/shaders/cook_torrance.vert
@@ -1,7 +1,8 @@
1precision highp float; 1precision highp float;
2 2
3uniform mat4 ModelMatrix; 3uniform mat4 ModelMatrix;
4uniform mat4 Modelview; 4// uniform mat4 Modelview;
5uniform mat4 View;
5uniform mat4 Projection; 6uniform mat4 Projection;
6//uniform mat4 MVP; 7//uniform mat4 MVP;
7#ifdef HAS_JOINTS 8#ifdef HAS_JOINTS
@@ -34,7 +35,7 @@ layout (location = 4) in uvec4 vJoint;
34layout (location = 5) in vec4 vWeight; 35layout (location = 5) in vec4 vWeight;
35#endif 36#endif
36 37
37// World-space position and normal. 38// World-space position, normal and tangent.
38out vec3 Position; 39out vec3 Position;
39#ifdef HAS_NORMALS 40#ifdef HAS_NORMALS
40out vec3 Normal; 41out vec3 Normal;
@@ -54,9 +55,9 @@ void main()
54 vWeight.y * JointMatrices[vJoint.y] + 55 vWeight.y * JointMatrices[vJoint.y] +
55 vWeight.z * JointMatrices[vJoint.z] + 56 vWeight.z * JointMatrices[vJoint.z] +
56 vWeight.w * JointMatrices[vJoint.w]; 57 vWeight.w * JointMatrices[vJoint.w];
57 Position = vec3(Modelview * skinMatrix * vec4(vPosition, 1.0)); 58 Position = vec3(ModelMatrix * skinMatrix * vec4(vPosition, 1.0));
58#else 59#else
59 Position = vec3(Modelview * vec4(vPosition, 1.0)); 60 Position = vec3(ModelMatrix * vec4(vPosition, 1.0));
60#endif 61#endif
61#ifdef HAS_NORMALS 62#ifdef HAS_NORMALS
62 Normal = mat3(ModelMatrix) * vNormal; 63 Normal = mat3(ModelMatrix) * vNormal;
@@ -68,6 +69,7 @@ void main()
68#ifdef HAS_TEXCOORDS 69#ifdef HAS_TEXCOORDS
69 Texcoord = vTexcoord; 70 Texcoord = vTexcoord;
70#endif 71#endif
71 gl_Position = Projection * vec4(Position, 1.0); 72 gl_Position = Projection * View * vec4(Position, 1.0);
73 //gl_Position = Projection * vec4(Position, 1.0);
72 //gl_Position = MVP * vec4(vPosition, 1.0); 74 //gl_Position = MVP * vec4(vPosition, 1.0);
73} 75}
diff --git a/gfx/shaders/cubemap_filtering.vert b/gfx/shaders/cubemap_filtering.vert
index cdc291c..d0cf73f 100644
--- a/gfx/shaders/cubemap_filtering.vert
+++ b/gfx/shaders/cubemap_filtering.vert
@@ -3,32 +3,37 @@ precision highp float;
3#define PI 3.1415926535897932384626433832795 3#define PI 3.1415926535897932384626433832795
4#define FOVY (90.0 * PI / 180.0) 4#define FOVY (90.0 * PI / 180.0)
5 5
6uniform mat4 Modelview; 6uniform mat4 CameraRotation; // From camera space to world space.
7uniform float Flip;
7 8
8layout (location = 0) in vec2 vPosition; 9layout (location = 0) in vec2 vPosition;
9 10
10out vec3 Ray; 11out vec3 Ray;
11 12
13// DEBUG
14// out vec2 Texcoord;
15
12// This is very similar to the skyquad vertex shader. 16// This is very similar to the skyquad vertex shader.
13// 17//
14// The ray is not normalized because it isn't necessary for cubemap sampling. 18// The ray is not normalized because it isn't necessary for cubemap sampling.
15// 19//
16// We also use a fixed fovy = 90 degrees because we want the frustum to pass 20// We also use a fixed fovy = 90 degrees because we want the frustum to pass
17// exactly through each face of the cube. The aspect ratio is also just 1. 21// exactly through each face of the cube. The aspect ratio is also just 1.
18// 22vec3 sky_ray(vec2 FilmPosition)
19// |d| here appears with a positive sign in the returned vector because the
20// rotation part of the Modelview matrix is responsible for orienting the ray
21// appropriately.
22vec3 sky_ray(vec2 Texcoord)
23{ 23{
24 float d = 0.5 / tan(FOVY/2.0); 24 float d = 0.5 / tan(FOVY/2.0);
25 return vec3(Texcoord.x - 0.5, Texcoord.y - 0.5, d); 25 return vec3(FilmPosition, -d);
26} 26}
27 27
28void main() 28void main()
29{ 29{
30 mat3 Rotation = mat3(Modelview); 30 vec2 FilmPosition = vPosition * 0.5; // map [-1,1] -> [-1/2, +1/2]
31 Ray = Rotation * sky_ray(vPosition*0.5 + 0.5); // map [-1,1] -> [0,1] 31 FilmPosition *= Flip;
32 // Should disable depth test when rendering. 32 Ray = mat3(CameraRotation) * sky_ray(FilmPosition);
33 gl_Position = vec4(vPosition.xy, 0.99999, 1.0); 33 // TODO: Should disable depth test when rendering.
34 gl_Position = vec4(vPosition, 0.99999, 1.0); // z=1 -> send to background
35
36 // DEBUG
37 // Texcoord = FilmPosition + 0.5;
38 // Texcoord.y = 1.0 - Texcoord.y;
34} 39}
diff --git a/gfx/shaders/irradiance_map.frag b/gfx/shaders/irradiance_map.frag
index 3cfe5b2..8200e73 100644
--- a/gfx/shaders/irradiance_map.frag
+++ b/gfx/shaders/irradiance_map.frag
@@ -13,6 +13,9 @@ uniform samplerCube Sky;
13 13
14in vec3 Ray; 14in vec3 Ray;
15 15
16// DEBUG
17// in vec2 Texcoord;
18
16layout (location = 0) out vec4 Color; 19layout (location = 0) out vec4 Color;
17 20
18void main() 21void main()
@@ -29,8 +32,8 @@ void main()
29 // T 32 // T
30 vec3 N = normalize(Ray); 33 vec3 N = normalize(Ray);
31 vec3 B = (abs(N.x) - 1.0 <= EPS) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); 34 vec3 B = (abs(N.x) - 1.0 <= EPS) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
32 vec3 T = cross(B, N); 35 vec3 T = normalize(cross(B, N));
33 B = cross(N, T); 36 B = normalize(cross(N, T));
34 37
35 int num_samples = 0; 38 int num_samples = 0;
36 vec3 irradiance = vec3(0.0); 39 vec3 irradiance = vec3(0.0);
@@ -54,6 +57,8 @@ void main()
54 irradiance = PI * irradiance / float(num_samples); 57 irradiance = PI * irradiance / float(num_samples);
55 58
56 // For debugging in trace. 59 // For debugging in trace.
60 //irradiance = texture(Sky, Ray).rgb;
61 // irradiance = vec3(Texcoord, 0.0);
57 //irradiance = pow(irradiance, vec3(1.0/2.2)); 62 //irradiance = pow(irradiance, vec3(1.0/2.2));
58 63
59 Color = vec4(irradiance, 1.0); 64 Color = vec4(irradiance, 1.0);
diff --git a/gfx/shaders/skyquad.frag b/gfx/shaders/skyquad.frag
index 39af013..9b44bfd 100644
--- a/gfx/shaders/skyquad.frag
+++ b/gfx/shaders/skyquad.frag
@@ -8,4 +8,6 @@ void main()
8{ 8{
9 vec3 R = normalize(Ray); 9 vec3 R = normalize(Ray);
10 Colour = vec4(pow(texture(Skyquad, R).rgb, vec3(1.0/2.2)), 1.0); 10 Colour = vec4(pow(texture(Skyquad, R).rgb, vec3(1.0/2.2)), 1.0);
11 // Debug.
12 //Colour = vec4(pow(R*0.5 + 0.5, vec3(1.0 / 2.2)), 1.0);
11} 13}
diff --git a/gfx/shaders/skyquad.vert b/gfx/shaders/skyquad.vert
index f0658d6..c0c46e6 100644
--- a/gfx/shaders/skyquad.vert
+++ b/gfx/shaders/skyquad.vert
@@ -1,4 +1,4 @@
1uniform mat4 Modelview; 1uniform mat4 CameraRotation; // From camera space to world space.
2uniform float Fovy; 2uniform float Fovy;
3uniform float Aspect; 3uniform float Aspect;
4 4
@@ -8,21 +8,20 @@ out vec3 Ray;
8 8
9// We would typically normalize the vector, but there is no need to do so when 9// We would typically normalize the vector, but there is no need to do so when
10// sampling a cube map. 10// sampling a cube map.
11vec3 sky_ray(vec2 Texcoord) 11vec3 sky_ray(vec2 FilmPosition)
12{ 12{
13 //float d = 0.5 / tan(Fovy/2.0); 13 //float d = 0.5 / tan(Fovy/2.0);
14 // return vec3((Texcoord.x - 0.5) * Aspect, 14 // return vec3((FilmPosition.x - 0.5) * Aspect,
15 // Texcoord.y - 0.5, 15 // FilmPosition.y - 0.5,
16 // -d); 16 // -d);
17 float d = 0.5 / tan(Fovy/2.0); 17 float d = 0.5 / tan(Fovy/2.0);
18 return vec3((Texcoord.x - 0.5), 18 return vec3(FilmPosition, -d);
19 Texcoord.y - 0.5,
20 -d);
21} 19}
22 20
23void main() 21void main()
24{ 22{
25 mat3 Rotation = mat3(Modelview); 23 vec2 FilmPosition = Position * 0.5; // map [-1,1] -> [-1/2, +1/2]
26 Ray = Rotation * sky_ray(Position*0.5 + 0.5); // map [-1,1] -> [0,1] 24 Ray = mat3(CameraRotation) * sky_ray(FilmPosition);
25 // Set z to the background.
27 gl_Position = vec4(Position, 0.99999, 1.0); // z=1 -> send to background 26 gl_Position = vec4(Position, 0.99999, 1.0); // z=1 -> send to background
28} 27}
diff --git a/gfx/src/renderer/renderer.c b/gfx/src/renderer/renderer.c
index 47f1344..b0bef33 100644
--- a/gfx/src/renderer/renderer.c
+++ b/gfx/src/renderer/renderer.c
@@ -114,6 +114,7 @@ typedef struct RenderState {
114 Renderer* renderer; 114 Renderer* renderer;
115 const Scene* scene; 115 const Scene* scene;
116 const Camera* camera; 116 const Camera* camera;
117 const mat4* camera_rotation; // From camera to world space, rotation only.
117 const mat4* view_matrix; 118 const mat4* view_matrix;
118 const mat4* projection; 119 const mat4* projection;
119 Light* environment_light; 120 Light* environment_light;
@@ -167,6 +168,8 @@ static void draw_recursively(
167 const SceneObject* object = mem_get_object(node->object); 168 const SceneObject* object = mem_get_object(node->object);
168 assert(object); 169 assert(object);
169 170
171 // TODO: Avoid computing matrices like Modelview or MVP if the shader does
172 // not use them.
170 const mat4 model_matrix = mat4_mul(node_transform, object->transform); 173 const mat4 model_matrix = mat4_mul(node_transform, object->transform);
171 const mat4 modelview = mat4_mul(*state->view_matrix, model_matrix); 174 const mat4 modelview = mat4_mul(*state->view_matrix, model_matrix);
172 const mat4 mvp = mat4_mul(*state->projection, modelview); 175 const mat4 mvp = mat4_mul(*state->projection, modelview);
@@ -187,13 +190,13 @@ static void draw_recursively(
187 assert(mesh->geometry); 190 assert(mesh->geometry);
188 assert(mesh->material); 191 assert(mesh->material);
189 // Apply common shader uniforms not captured by materials. 192 // Apply common shader uniforms not captured by materials.
190 // TODO: Avoid computing matrices like Modelview or MVP if the shader does
191 // not use them.
192 ShaderProgram* shader = mesh->shader; 193 ShaderProgram* shader = mesh->shader;
193 gfx_set_mat4_uniform(shader, "ModelMatrix", &model_matrix); 194 gfx_set_mat4_uniform(shader, "ModelMatrix", &model_matrix);
194 gfx_set_mat4_uniform(shader, "Modelview", &modelview); 195 gfx_set_mat4_uniform(shader, "Modelview", &modelview);
196 gfx_set_mat4_uniform(shader, "View", state->view_matrix);
195 gfx_set_mat4_uniform(shader, "Projection", state->projection); 197 gfx_set_mat4_uniform(shader, "Projection", state->projection);
196 gfx_set_mat4_uniform(shader, "MVP", &mvp); 198 gfx_set_mat4_uniform(shader, "MVP", &mvp);
199 gfx_set_mat4_uniform(shader, "CameraRotation", state->camera_rotation);
197 gfx_set_float_uniform(shader, "Fovy", state->fovy); 200 gfx_set_float_uniform(shader, "Fovy", state->fovy);
198 gfx_set_float_uniform(shader, "Aspect", state->aspect); 201 gfx_set_float_uniform(shader, "Aspect", state->aspect);
199 gfx_set_vec3_uniform(shader, "CameraPosition", state->camera->spatial.p); 202 gfx_set_vec3_uniform(shader, "CameraPosition", state->camera->spatial.p);
@@ -247,6 +250,8 @@ void gfx_render_scene(
247 } 250 }
248 251
249 const mat4 projection = camera ? camera->camera.projection : mat4_id(); 252 const mat4 projection = camera ? camera->camera.projection : mat4_id();
253 const mat4 camera_rotation =
254 mat4_rotation(spatial3_transform(&camera->camera.spatial));
250 const mat4 view_matrix = 255 const mat4 view_matrix =
251 camera ? spatial3_inverse_transform(&camera->camera.spatial) : mat4_id(); 256 camera ? spatial3_inverse_transform(&camera->camera.spatial) : mat4_id();
252 257
@@ -259,6 +264,7 @@ void gfx_render_scene(
259 .renderer = renderer, 264 .renderer = renderer,
260 .scene = scene, 265 .scene = scene,
261 .camera = &camera->camera, 266 .camera = &camera->camera,
267 .camera_rotation = &camera_rotation,
262 .view_matrix = &view_matrix, 268 .view_matrix = &view_matrix,
263 .projection = &projection, 269 .projection = &projection,
264 .environment_light = 0, 270 .environment_light = 0,
diff --git a/gfx/src/util/ibl.c b/gfx/src/util/ibl.c
index c9bef43..6b7465c 100644
--- a/gfx/src/util/ibl.c
+++ b/gfx/src/util/ibl.c
@@ -24,7 +24,16 @@ static const CubemapFace faces[6] = {
24 CubemapFacePosY, // Up. 24 CubemapFacePosY, // Up.
25 CubemapFaceNegY, // Down. 25 CubemapFaceNegY, // Down.
26 CubemapFacePosZ, // Back. 26 CubemapFacePosZ, // Back.
27 CubemapFaceNegZ, // Forward. 27 CubemapFaceNegZ, // Front.
28};
29
30static 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.
28}; 37};
29 38
30IBL* gfx_make_ibl(RenderBackend* render_backend) { 39IBL* gfx_make_ibl(RenderBackend* render_backend) {
@@ -70,6 +79,9 @@ IBL* gfx_make_ibl(RenderBackend* render_backend) {
70 goto cleanup; 79 goto cleanup;
71 } 80 }
72 81
82 // TODO: Debug the camera rotations. Irradiance debug output should appear
83 // just like the input cubemap.
84
73 // Right. 85 // Right.
74 ibl->rotations[0] = mat4_lookat( 86 ibl->rotations[0] = mat4_lookat(
75 /*position=*/vec3_make(0, 0, 0), 87 /*position=*/vec3_make(0, 0, 0),
@@ -95,7 +107,7 @@ IBL* gfx_make_ibl(RenderBackend* render_backend) {
95 /*position=*/vec3_make(0, 0, 0), 107 /*position=*/vec3_make(0, 0, 0),
96 /*target=*/vec3_make(0, 0, 1), 108 /*target=*/vec3_make(0, 0, 1),
97 /*up=*/vec3_make(0, 1, 0)); 109 /*up=*/vec3_make(0, 1, 0));
98 // Forward. 110 // Front.
99 ibl->rotations[5] = mat4_lookat( 111 ibl->rotations[5] = mat4_lookat(
100 /*position=*/vec3_make(0, 0, 0), 112 /*position=*/vec3_make(0, 0, 0),
101 /*target=*/vec3_make(0, 0, -1), 113 /*target=*/vec3_make(0, 0, -1),
@@ -226,8 +238,9 @@ Texture* gfx_make_irradiance_map(
226 .cubemap.texture = irradiance_map})) { 238 .cubemap.texture = irradiance_map})) {
227 goto cleanup; 239 goto cleanup;
228 } 240 }
241 gfx_set_float_uniform(ibl->irradiance_map_shader, "Flip", flips[i]);
229 gfx_set_mat4_uniform( 242 gfx_set_mat4_uniform(
230 ibl->irradiance_map_shader, "Modelview", &ibl->rotations[i]); 243 ibl->irradiance_map_shader, "CameraRotation", &ibl->rotations[i]);
231 gfx_apply_uniforms(ibl->irradiance_map_shader); 244 gfx_apply_uniforms(ibl->irradiance_map_shader);
232 gfx_render_geometry(ibl->quad); 245 gfx_render_geometry(ibl->quad);
233 } 246 }
@@ -291,8 +304,10 @@ Texture* gfx_make_prefiltered_environment_map(
291 .cubemap.texture = prefiltered_env_map})) { 304 .cubemap.texture = prefiltered_env_map})) {
292 goto cleanup; 305 goto cleanup;
293 } 306 }
307 gfx_set_float_uniform(
308 ibl->prefiltered_environment_map_shader, "Flip", flips[i]);
294 gfx_set_mat4_uniform( 309 gfx_set_mat4_uniform(
295 ibl->prefiltered_environment_map_shader, "Modelview", 310 ibl->prefiltered_environment_map_shader, "CameraRotation",
296 &ibl->rotations[i]); 311 &ibl->rotations[i]);
297 gfx_apply_uniforms(ibl->prefiltered_environment_map_shader); 312 gfx_apply_uniforms(ibl->prefiltered_environment_map_shader);
298 gfx_render_geometry(ibl->quad); 313 gfx_render_geometry(ibl->quad);
diff --git a/gfx/src/util/texture.c b/gfx/src/util/texture.c
index 99241f4..0727dae 100644
--- a/gfx/src/util/texture.c
+++ b/gfx/src/util/texture.c
@@ -6,6 +6,42 @@
6#define STB_IMAGE_IMPLEMENTATION 6#define STB_IMAGE_IMPLEMENTATION
7#include <stb_image.h> 7#include <stb_image.h>
8 8
9#include <assert.h>
10
11static void flip_horizontally(
12 unsigned char* pixels, int width, int height, int components) {
13 assert(pixels);
14
15 for (int y = 0; y < height; ++y) {
16 for (int x = 0; x < width / 2; ++x) {
17 unsigned char* p1 = &pixels[(y * width + x) * components];
18 unsigned char* p2 = &pixels[(y * width + (width - x - 1)) * components];
19
20 for (int c = 0; c < components; ++c) {
21 unsigned char tmp = *p1;
22 *p1 = *p2;
23 *p2 = tmp;
24 p1++;
25 p2++;
26 }
27 }
28 }
29}
30
31// Note that the cubemap coordinate system uses the one in RenderMan:
32//
33// https://www.khronos.org/opengl/wiki/Cubemap_Texture
34//
35// This is what happens:
36//
37// - Cubemaps follow a left-handed coordinate system. Say, +X is right, +Y is
38// up, and +Z is forward.
39// - The texture coordinate system follow's DirectX's, so +V goes down, not up
40// like it does in OpenGL.
41//
42// For this reason, we do X and Y flips when doing cubemap textures so that we
43// can sample cubemaps as if they were given in the usual OpenGL coordinate
44// system.
9Texture* gfx_load_texture( 45Texture* gfx_load_texture(
10 RenderBackend* render_backend, const LoadTextureCmd* cmd) { 46 RenderBackend* render_backend, const LoadTextureCmd* cmd) {
11 assert(render_backend); 47 assert(render_backend);
@@ -21,6 +57,7 @@ Texture* gfx_load_texture(
21 switch (cmd->type) { 57 switch (cmd->type) {
22 case LoadTexture: { 58 case LoadTexture: {
23 const char* filepath = mstring_cstr(&cmd->data.texture.filepath); 59 const char* filepath = mstring_cstr(&cmd->data.texture.filepath);
60 stbi_set_flip_vertically_on_load(0);
24 pixels[0] = stbi_load(filepath, &width, &height, &components, 0); 61 pixels[0] = stbi_load(filepath, &width, &height, &components, 0);
25 if (!pixels[0]) { 62 if (!pixels[0]) {
26 gfx_set_error("Failed to load texture file: %s", filepath); 63 gfx_set_error("Failed to load texture file: %s", filepath);
@@ -29,6 +66,8 @@ Texture* gfx_load_texture(
29 } 66 }
30 case LoadCubemap: 67 case LoadCubemap:
31 for (int i = 0; i < 6; ++i) { 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);
32 const char* filepath = 71 const char* filepath =
33 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_x + i); 72 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_x + i);
34 stbi_uc* image_pixels = 73 stbi_uc* image_pixels =
@@ -43,6 +82,9 @@ Texture* gfx_load_texture(
43 "components"); 82 "components");
44 break; 83 break;
45 } 84 }
85 if ((i != 2) && (i != 3)) {
86 flip_horizontally(image_pixels, width, height, components);
87 }
46 pixels[i] = image_pixels; 88 pixels[i] = image_pixels;
47 old_components = components; 89 old_components = components;
48 } 90 }
diff --git a/gltfview/src/game.c b/gltfview/src/game.c
index 1db7cba..f822b08 100644
--- a/gltfview/src/game.c
+++ b/gltfview/src/game.c
@@ -70,8 +70,8 @@ static Texture* load_environment_map(RenderBackend* render_backend) {
70 mstring_make("/assets/skybox/clouds1/clouds1_west.bmp"), 70 mstring_make("/assets/skybox/clouds1/clouds1_west.bmp"),
71 mstring_make("/assets/skybox/clouds1/clouds1_up.bmp"), 71 mstring_make("/assets/skybox/clouds1/clouds1_up.bmp"),
72 mstring_make("/assets/skybox/clouds1/clouds1_down.bmp"), 72 mstring_make("/assets/skybox/clouds1/clouds1_down.bmp"),
73 mstring_make("/assets/skybox/clouds1/clouds1_north.bmp"), 73 mstring_make("/assets/skybox/clouds1/clouds1_south.bmp"),
74 mstring_make("/assets/skybox/clouds1/clouds1_south.bmp")} 74 mstring_make("/assets/skybox/clouds1/clouds1_north.bmp")}
75 }); 75 });
76} 76}
77 77
@@ -222,7 +222,7 @@ bool game_new(Game* game, int argc, const char** argv) {
222 // false}); 222 // false});
223 const bool play_result = gfx_play_animation( 223 const bool play_result = gfx_play_animation(
224 anima, &(AnimationPlaySettings){.name = "Walk", .loop = true}); 224 anima, &(AnimationPlaySettings){.name = "Walk", .loop = true});
225 assert(play_result); 225 // assert(play_result);
226 226
227 return true; 227 return true;
228 228
@@ -251,6 +251,15 @@ void game_update(Game* game, double t, double dt) {
251 /*radius=*/2.5, 251 /*radius=*/2.5,
252 /*azimuth=*/t * 0.5, /*zenith=*/0); 252 /*azimuth=*/t * 0.5, /*zenith=*/0);
253 spatial3_lookat(&camera->spatial, orbit_point); 253 spatial3_lookat(&camera->spatial, orbit_point);
254
255 // spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2));
256 // spatial3_lookat(&camera->spatial, vec3_make(0, 0, -1));
257
258 // spatial3_orbit(
259 // &camera->spatial, vec3_make(0, 0, 0),
260 // /*radius=*/2.5,
261 // /*azimuth=*/t * 0.2, /*zenith=*/0);
262 // spatial3_lookat(&camera->spatial, vec3_make(0, 0, 0));
254} 263}
255 264
256void game_render(const Game* game) { 265void game_render(const Game* game) {