From 95895750e836028a63ec5375dd06742a8d901df5 Mon Sep 17 00:00:00 2001
From: Abdelrahman <said.abdelrahman89@gmail.com>
Date: Fri, 13 Sep 2024 23:55:11 +0100
Subject: [PATCH] Split shaders code to separate files

---
 src/shader/main_shader.c   | 129 +++++++++++++++++++++++++++++++++++++
 src/shader/main_shader.h   |  19 ++++++
 src/shader/shader_base.inc |   7 ++
 3 files changed, 155 insertions(+)
 create mode 100644 src/shader/main_shader.c
 create mode 100644 src/shader/main_shader.h
 create mode 100644 src/shader/shader_base.inc

diff --git a/src/shader/main_shader.c b/src/shader/main_shader.c
new file mode 100644
index 0000000..da95de8
--- /dev/null
+++ b/src/shader/main_shader.c
@@ -0,0 +1,129 @@
+#include "main_shader.h"
+#include "obj.h"
+#include "shader.h"
+#include "utils.h"
+#include "vec.h"
+
+VertexData general_shader_vertex(void *shader, const VertexData *vert, u8 index,
+                                 const Model *model) {
+  Shader *shdr = (Shader *)shader;
+
+  V4f vh = V3_to_V4(vert->position);
+  vh.y   = 0.0 - vh.y;
+  vh     = mat4x4_mul_vec4(shdr->final, vh);
+
+  shdr->vertices[index].position = project_vec4(vh);
+  shdr->vertices[index].uv       = vert->uv;
+
+  M4x4f inv_transpose          = mat4x4_inv(mat4x4_transpose(shdr->proj_mv));
+  V4f hnorm                    = V3_to_V4(vert->normal);
+  hnorm                        = mat4x4_mul_vec4(inv_transpose, hnorm);
+  shdr->vertices[index].normal = project_vec4(hnorm);
+  normalise_v3(shdr->vertices[index].normal);
+
+  return shdr->vertices[index];
+}
+
+FragmentResult diffuse_shader_fragment(void *shader, const V3f *barycentric,
+                                       const Colour *colour,
+                                       const Model *model) {
+  Shader *shdr = (Shader *)shader;
+
+  M3x3f pos_mat    = {.rows = {shdr->vertices[0].position, shdr->vertices[1].position, shdr->vertices[2].position}};
+  pos_mat          = mat3x3_transpose(pos_mat);
+  M3x3f normal_mat = {.rows = {shdr->vertices[0].normal, shdr->vertices[1].normal, shdr->vertices[2].normal}};
+  normal_mat       = mat3x3_transpose(normal_mat);
+  M3x2f uvs        = {shdr->vertices[0].uv, shdr->vertices[1].uv, shdr->vertices[2].uv};
+  M2x3f uv_mat     = mat3x2_transpose(uvs);
+
+  V3f position = mat3x3_mul_vec3(pos_mat, (*barycentric));
+  V3f normal   = mat3x3_mul_vec3(normal_mat, (*barycentric));
+  V2f uv       = mat2x3_mul_vec3(uv_mat, (*barycentric));
+
+#pragma region darboux_frame_tangent_normals
+  /**
+   * Based on the following section of the tinyrenderer tutorial
+   * https://github.com/ssloy/tinyrenderer/wiki/Lesson-6bis:-tangent-space-normal-mapping#starting-point-phong-shading
+   */
+
+  if (model->normal) {
+    u64 nm_x = uv.u * model->normal->width;
+    u64 nm_y = uv.v * model->normal->height;
+
+    Colour pixel = get_pixel(Colour, model->normal, nm_x, nm_y);
+    V3f tangent = (V3f){
+        .x = pixel.r / 255.f * 2.f - 1.f,
+        .y = pixel.g / 255.f * 2.f - 1.f,
+        .z = pixel.b / 255.f * 2.f - 1.f,
+    };
+
+    V3f p0p1 = sub_v3(shdr->vertices[1].position, shdr->vertices[0].position);
+    V3f p0p2 = sub_v3(shdr->vertices[2].position, shdr->vertices[0].position);
+
+    M3x3f A = {.rows = {p0p1, p0p2, normal}};
+    M3x3f A_inv = mat3x3_inv(A);
+
+    V2f uv0 = shdr->vertices[0].uv;
+    V2f uv1 = shdr->vertices[1].uv;
+    V2f uv2 = shdr->vertices[2].uv;
+
+    V3f u_vec = {uv1.u - uv0.u, uv2.u - uv0.u, 0};
+    V3f v_vec = {uv1.v - uv0.v, uv2.v - uv0.v, 0};
+
+    V3f i = mat3x3_mul_vec3(A_inv, u_vec);
+    normalise_v3(i);
+    V3f j = mat3x3_mul_vec3(A_inv, v_vec);
+    normalise_v3(j);
+
+    M3x3f B = {.rows = {i, j, normal}};
+    B = mat3x3_transpose(B);
+
+    normal = mat3x3_mul_vec3(B, tangent);
+    normalise_v3(normal);
+  }
+#pragma endregion darboux_frame_tangent_normals
+
+  Colour output;
+  if (model->texture) {
+    u64 tx_x = uv.u * model->texture->width;
+    u64 tx_y = uv.v * model->texture->height;
+    output   = get_pixel(Colour, model->texture, tx_x, tx_y);
+  } else {
+    output = *colour;
+  }
+
+  f32 intensity = max(0.001f, dot_v3(normal, shdr->light_dir));
+  f32 r         = clamp(intensity + shdr->ambient.r, 0.0f, 1.0f);
+  f32 g         = clamp(intensity + shdr->ambient.g, 0.0f, 1.0f);
+  f32 b         = clamp(intensity + shdr->ambient.b, 0.0f, 1.0f);
+
+  output.r *= r;
+  output.g *= g;
+  output.b *= b;
+
+  return (FragmentResult){.colour = output};
+}
+
+FragmentResult albedo_shader_fragment(void *shader, const V3f *barycentric,
+                                      const Colour *colour,
+                                      const Model *model) {
+  Shader *shdr = (Shader *)shader;
+
+  // clang-format off
+  M3x2f uvs = {shdr->vertices[0].uv, shdr->vertices[1].uv, shdr->vertices[2].uv};
+  M2x3f uv_mat = mat3x2_transpose(uvs);
+  // clang-format on
+
+  V2f uv = mat2x3_mul_vec3(uv_mat, (*barycentric));
+
+  Colour output;
+  if (model->texture) {
+    u64 tx_x = uv.u * model->texture->width;
+    u64 tx_y = uv.v * model->texture->height;
+    output   = get_pixel(Colour, model->texture, tx_x, tx_y);
+  } else {
+    output = *colour;
+  }
+
+  return (FragmentResult){.colour = output};
+}
diff --git a/src/shader/main_shader.h b/src/shader/main_shader.h
new file mode 100644
index 0000000..5f5fdfe
--- /dev/null
+++ b/src/shader/main_shader.h
@@ -0,0 +1,19 @@
+#ifndef MAIN_SHADER_H
+#define MAIN_SHADER_H
+
+#include "shader.h"
+#include "vec.h"
+
+typedef struct shader Shader;
+struct shader {
+#include "shader_base.inc"
+};
+
+VertexData general_shader_vertex(void *shader, const VertexData *vert, u8 index,
+                                 const Model *model);
+FragmentResult diffuse_shader_fragment(void *shader, const V3f *barycentric,
+                                       const Colour *colour,
+                                       const Model *model);
+FragmentResult albedo_shader_fragment(void *shader, const V3f *barycentric,
+                                      const Colour *colour, const Model *model);
+#endif // !MAIN_SHADER_H
diff --git a/src/shader/shader_base.inc b/src/shader/shader_base.inc
new file mode 100644
index 0000000..5fb7dc7
--- /dev/null
+++ b/src/shader/shader_base.inc
@@ -0,0 +1,7 @@
+V3f light_dir;
+V3f ambient;
+M4x4f proj_mv;
+M4x4f proj_mv_inv_t;
+M4x4f viewport;
+M4x4f final;
+VertexData vertices[TRIANGLE_VERTICES];