summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--linmath.h605
-rw-r--r--main.c761
-rw-r--r--pfp.pngbin0 -> 455024 bytes
-rw-r--r--shader.frag4
-rw-r--r--shader.vert26
6 files changed, 1339 insertions, 61 deletions
diff --git a/Makefile b/Makefile
index d7a272c..b771781 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
CFLAGS = -O2 -g
-LDFLAGS != pkg-config --libs vulkan sdl2
+LDFLAGS != pkg-config --libs vulkan sdl2 libpng
vulkan: main.c vert.spv frag.spv
- gcc $(CFLAGS) -o vk main.c $(LDFLAGS)
+ gcc $(CFLAGS) -o vk main.c $(LDFLAGS) -lm
%.spv: shader.%
glslc $^ -o $@
diff --git a/linmath.h b/linmath.h
new file mode 100644
index 0000000..dd329e1
--- /dev/null
+++ b/linmath.h
@@ -0,0 +1,605 @@
+#ifndef LINMATH_H
+#define LINMATH_H
+
+#include <string.h>
+#include <math.h>
+#include <string.h>
+
+#ifdef LINMATH_NO_INLINE
+#define LINMATH_H_FUNC static
+#else
+#define LINMATH_H_FUNC static inline
+#endif
+
+#define LINMATH_H_DEFINE_VEC(n) \
+typedef float vec##n[n]; \
+LINMATH_H_FUNC void vec##n##_add(vec##n r, vec##n const a, vec##n const b) \
+{ \
+ int i; \
+ for(i=0; i<n; ++i) \
+ r[i] = a[i] + b[i]; \
+} \
+LINMATH_H_FUNC void vec##n##_sub(vec##n r, vec##n const a, vec##n const b) \
+{ \
+ int i; \
+ for(i=0; i<n; ++i) \
+ r[i] = a[i] - b[i]; \
+} \
+LINMATH_H_FUNC void vec##n##_scale(vec##n r, vec##n const v, float const s) \
+{ \
+ int i; \
+ for(i=0; i<n; ++i) \
+ r[i] = v[i] * s; \
+} \
+LINMATH_H_FUNC float vec##n##_mul_inner(vec##n const a, vec##n const b) \
+{ \
+ float p = 0.f; \
+ int i; \
+ for(i=0; i<n; ++i) \
+ p += b[i]*a[i]; \
+ return p; \
+} \
+LINMATH_H_FUNC float vec##n##_len(vec##n const v) \
+{ \
+ return sqrtf(vec##n##_mul_inner(v,v)); \
+} \
+LINMATH_H_FUNC void vec##n##_norm(vec##n r, vec##n const v) \
+{ \
+ float k = 1.f / vec##n##_len(v); \
+ vec##n##_scale(r, v, k); \
+} \
+LINMATH_H_FUNC void vec##n##_min(vec##n r, vec##n const a, vec##n const b) \
+{ \
+ int i; \
+ for(i=0; i<n; ++i) \
+ r[i] = a[i]<b[i] ? a[i] : b[i]; \
+} \
+LINMATH_H_FUNC void vec##n##_max(vec##n r, vec##n const a, vec##n const b) \
+{ \
+ int i; \
+ for(i=0; i<n; ++i) \
+ r[i] = a[i]>b[i] ? a[i] : b[i]; \
+} \
+LINMATH_H_FUNC void vec##n##_dup(vec##n r, vec##n const src) \
+{ \
+ int i; \
+ for(i=0; i<n; ++i) \
+ r[i] = src[i]; \
+}
+
+LINMATH_H_DEFINE_VEC(2)
+LINMATH_H_DEFINE_VEC(3)
+LINMATH_H_DEFINE_VEC(4)
+
+LINMATH_H_FUNC void vec3_mul_cross(vec3 r, vec3 const a, vec3 const b)
+{
+ r[0] = a[1]*b[2] - a[2]*b[1];
+ r[1] = a[2]*b[0] - a[0]*b[2];
+ r[2] = a[0]*b[1] - a[1]*b[0];
+}
+
+LINMATH_H_FUNC void vec3_reflect(vec3 r, vec3 const v, vec3 const n)
+{
+ float p = 2.f * vec3_mul_inner(v, n);
+ int i;
+ for(i=0;i<3;++i)
+ r[i] = v[i] - p*n[i];
+}
+
+LINMATH_H_FUNC void vec4_mul_cross(vec4 r, vec4 const a, vec4 const b)
+{
+ r[0] = a[1]*b[2] - a[2]*b[1];
+ r[1] = a[2]*b[0] - a[0]*b[2];
+ r[2] = a[0]*b[1] - a[1]*b[0];
+ r[3] = 1.f;
+}
+
+LINMATH_H_FUNC void vec4_reflect(vec4 r, vec4 const v, vec4 const n)
+{
+ float p = 2.f*vec4_mul_inner(v, n);
+ int i;
+ for(i=0;i<4;++i)
+ r[i] = v[i] - p*n[i];
+}
+
+typedef vec4 mat4x4[4];
+LINMATH_H_FUNC void mat4x4_identity(mat4x4 M)
+{
+ int i, j;
+ for(i=0; i<4; ++i)
+ for(j=0; j<4; ++j)
+ M[i][j] = i==j ? 1.f : 0.f;
+}
+LINMATH_H_FUNC void mat4x4_dup(mat4x4 M, mat4x4 const N)
+{
+ int i;
+ for(i=0; i<4; ++i)
+ vec4_dup(M[i], N[i]);
+}
+LINMATH_H_FUNC void mat4x4_row(vec4 r, mat4x4 const M, int i)
+{
+ int k;
+ for(k=0; k<4; ++k)
+ r[k] = M[k][i];
+}
+LINMATH_H_FUNC void mat4x4_col(vec4 r, mat4x4 const M, int i)
+{
+ int k;
+ for(k=0; k<4; ++k)
+ r[k] = M[i][k];
+}
+LINMATH_H_FUNC void mat4x4_transpose(mat4x4 M, mat4x4 const N)
+{
+ // Note: if M and N are the same, the user has to
+ // explicitly make a copy of M and set it to N.
+ int i, j;
+ for(j=0; j<4; ++j)
+ for(i=0; i<4; ++i)
+ M[i][j] = N[j][i];
+}
+LINMATH_H_FUNC void mat4x4_add(mat4x4 M, mat4x4 const a, mat4x4 const b)
+{
+ int i;
+ for(i=0; i<4; ++i)
+ vec4_add(M[i], a[i], b[i]);
+}
+LINMATH_H_FUNC void mat4x4_sub(mat4x4 M, mat4x4 const a, mat4x4 const b)
+{
+ int i;
+ for(i=0; i<4; ++i)
+ vec4_sub(M[i], a[i], b[i]);
+}
+LINMATH_H_FUNC void mat4x4_scale(mat4x4 M, mat4x4 const a, float k)
+{
+ int i;
+ for(i=0; i<4; ++i)
+ vec4_scale(M[i], a[i], k);
+}
+LINMATH_H_FUNC void mat4x4_scale_aniso(mat4x4 M, mat4x4 const a, float x, float y, float z)
+{
+ vec4_scale(M[0], a[0], x);
+ vec4_scale(M[1], a[1], y);
+ vec4_scale(M[2], a[2], z);
+ vec4_dup(M[3], a[3]);
+}
+LINMATH_H_FUNC void mat4x4_mul(mat4x4 M, mat4x4 const a, mat4x4 const b)
+{
+ mat4x4 temp;
+ int k, r, c;
+ for(c=0; c<4; ++c) for(r=0; r<4; ++r) {
+ temp[c][r] = 0.f;
+ for(k=0; k<4; ++k)
+ temp[c][r] += a[k][r] * b[c][k];
+ }
+ mat4x4_dup(M, temp);
+}
+LINMATH_H_FUNC void mat4x4_mul_vec4(vec4 r, mat4x4 const M, vec4 const v)
+{
+ int i, j;
+ for(j=0; j<4; ++j) {
+ r[j] = 0.f;
+ for(i=0; i<4; ++i)
+ r[j] += M[i][j] * v[i];
+ }
+}
+LINMATH_H_FUNC void mat4x4_translate(mat4x4 T, float x, float y, float z)
+{
+ mat4x4_identity(T);
+ T[3][0] = x;
+ T[3][1] = y;
+ T[3][2] = z;
+}
+LINMATH_H_FUNC void mat4x4_translate_in_place(mat4x4 M, float x, float y, float z)
+{
+ vec4 t = {x, y, z, 0};
+ vec4 r;
+ int i;
+ for (i = 0; i < 4; ++i) {
+ mat4x4_row(r, M, i);
+ M[3][i] += vec4_mul_inner(r, t);
+ }
+}
+LINMATH_H_FUNC void mat4x4_from_vec3_mul_outer(mat4x4 M, vec3 const a, vec3 const b)
+{
+ int i, j;
+ for(i=0; i<4; ++i) for(j=0; j<4; ++j)
+ M[i][j] = i<3 && j<3 ? a[i] * b[j] : 0.f;
+}
+LINMATH_H_FUNC void mat4x4_rotate(mat4x4 R, mat4x4 const M, float x, float y, float z, float angle)
+{
+ float s = sinf(angle);
+ float c = cosf(angle);
+ vec3 u = {x, y, z};
+
+ if(vec3_len(u) > 1e-4) {
+ vec3_norm(u, u);
+ mat4x4 T;
+ mat4x4_from_vec3_mul_outer(T, u, u);
+
+ mat4x4 S = {
+ { 0, u[2], -u[1], 0},
+ {-u[2], 0, u[0], 0},
+ { u[1], -u[0], 0, 0},
+ { 0, 0, 0, 0}
+ };
+ mat4x4_scale(S, S, s);
+
+ mat4x4 C;
+ mat4x4_identity(C);
+ mat4x4_sub(C, C, T);
+
+ mat4x4_scale(C, C, c);
+
+ mat4x4_add(T, T, C);
+ mat4x4_add(T, T, S);
+
+ T[3][3] = 1.f;
+ mat4x4_mul(R, M, T);
+ } else {
+ mat4x4_dup(R, M);
+ }
+}
+LINMATH_H_FUNC void mat4x4_rotate_X(mat4x4 Q, mat4x4 const M, float angle)
+{
+ float s = sinf(angle);
+ float c = cosf(angle);
+ mat4x4 R = {
+ {1.f, 0.f, 0.f, 0.f},
+ {0.f, c, s, 0.f},
+ {0.f, -s, c, 0.f},
+ {0.f, 0.f, 0.f, 1.f}
+ };
+ mat4x4_mul(Q, M, R);
+}
+LINMATH_H_FUNC void mat4x4_rotate_Y(mat4x4 Q, mat4x4 const M, float angle)
+{
+ float s = sinf(angle);
+ float c = cosf(angle);
+ mat4x4 R = {
+ { c, 0.f, -s, 0.f},
+ { 0.f, 1.f, 0.f, 0.f},
+ { s, 0.f, c, 0.f},
+ { 0.f, 0.f, 0.f, 1.f}
+ };
+ mat4x4_mul(Q, M, R);
+}
+LINMATH_H_FUNC void mat4x4_rotate_Z(mat4x4 Q, mat4x4 const M, float angle)
+{
+ float s = sinf(angle);
+ float c = cosf(angle);
+ mat4x4 R = {
+ { c, s, 0.f, 0.f},
+ { -s, c, 0.f, 0.f},
+ { 0.f, 0.f, 1.f, 0.f},
+ { 0.f, 0.f, 0.f, 1.f}
+ };
+ mat4x4_mul(Q, M, R);
+}
+LINMATH_H_FUNC void mat4x4_invert(mat4x4 T, mat4x4 const M)
+{
+ float s[6];
+ float c[6];
+ s[0] = M[0][0]*M[1][1] - M[1][0]*M[0][1];
+ s[1] = M[0][0]*M[1][2] - M[1][0]*M[0][2];
+ s[2] = M[0][0]*M[1][3] - M[1][0]*M[0][3];
+ s[3] = M[0][1]*M[1][2] - M[1][1]*M[0][2];
+ s[4] = M[0][1]*M[1][3] - M[1][1]*M[0][3];
+ s[5] = M[0][2]*M[1][3] - M[1][2]*M[0][3];
+
+ c[0] = M[2][0]*M[3][1] - M[3][0]*M[2][1];
+ c[1] = M[2][0]*M[3][2] - M[3][0]*M[2][2];
+ c[2] = M[2][0]*M[3][3] - M[3][0]*M[2][3];
+ c[3] = M[2][1]*M[3][2] - M[3][1]*M[2][2];
+ c[4] = M[2][1]*M[3][3] - M[3][1]*M[2][3];
+ c[5] = M[2][2]*M[3][3] - M[3][2]*M[2][3];
+
+ /* Assumes it is invertible */
+ float idet = 1.0f/( s[0]*c[5]-s[1]*c[4]+s[2]*c[3]+s[3]*c[2]-s[4]*c[1]+s[5]*c[0] );
+
+ T[0][0] = ( M[1][1] * c[5] - M[1][2] * c[4] + M[1][3] * c[3]) * idet;
+ T[0][1] = (-M[0][1] * c[5] + M[0][2] * c[4] - M[0][3] * c[3]) * idet;
+ T[0][2] = ( M[3][1] * s[5] - M[3][2] * s[4] + M[3][3] * s[3]) * idet;
+ T[0][3] = (-M[2][1] * s[5] + M[2][2] * s[4] - M[2][3] * s[3]) * idet;
+
+ T[1][0] = (-M[1][0] * c[5] + M[1][2] * c[2] - M[1][3] * c[1]) * idet;
+ T[1][1] = ( M[0][0] * c[5] - M[0][2] * c[2] + M[0][3] * c[1]) * idet;
+ T[1][2] = (-M[3][0] * s[5] + M[3][2] * s[2] - M[3][3] * s[1]) * idet;
+ T[1][3] = ( M[2][0] * s[5] - M[2][2] * s[2] + M[2][3] * s[1]) * idet;
+
+ T[2][0] = ( M[1][0] * c[4] - M[1][1] * c[2] + M[1][3] * c[0]) * idet;
+ T[2][1] = (-M[0][0] * c[4] + M[0][1] * c[2] - M[0][3] * c[0]) * idet;
+ T[2][2] = ( M[3][0] * s[4] - M[3][1] * s[2] + M[3][3] * s[0]) * idet;
+ T[2][3] = (-M[2][0] * s[4] + M[2][1] * s[2] - M[2][3] * s[0]) * idet;
+
+ T[3][0] = (-M[1][0] * c[3] + M[1][1] * c[1] - M[1][2] * c[0]) * idet;
+ T[3][1] = ( M[0][0] * c[3] - M[0][1] * c[1] + M[0][2] * c[0]) * idet;
+ T[3][2] = (-M[3][0] * s[3] + M[3][1] * s[1] - M[3][2] * s[0]) * idet;
+ T[3][3] = ( M[2][0] * s[3] - M[2][1] * s[1] + M[2][2] * s[0]) * idet;
+}
+LINMATH_H_FUNC void mat4x4_orthonormalize(mat4x4 R, mat4x4 const M)
+{
+ mat4x4_dup(R, M);
+ float s = 1.f;
+ vec3 h;
+
+ vec3_norm(R[2], R[2]);
+
+ s = vec3_mul_inner(R[1], R[2]);
+ vec3_scale(h, R[2], s);
+ vec3_sub(R[1], R[1], h);
+ vec3_norm(R[1], R[1]);
+
+ s = vec3_mul_inner(R[0], R[2]);
+ vec3_scale(h, R[2], s);
+ vec3_sub(R[0], R[0], h);
+
+ s = vec3_mul_inner(R[0], R[1]);
+ vec3_scale(h, R[1], s);
+ vec3_sub(R[0], R[0], h);
+ vec3_norm(R[0], R[0]);
+}
+
+LINMATH_H_FUNC void mat4x4_frustum(mat4x4 M, float l, float r, float b, float t, float n, float f)
+{
+ M[0][0] = 2.f*n/(r-l);
+ M[0][1] = M[0][2] = M[0][3] = 0.f;
+
+ M[1][1] = 2.f*n/(t-b);
+ M[1][0] = M[1][2] = M[1][3] = 0.f;
+
+ M[2][0] = (r+l)/(r-l);
+ M[2][1] = (t+b)/(t-b);
+ M[2][2] = -(f+n)/(f-n);
+ M[2][3] = -1.f;
+
+ M[3][2] = -2.f*(f*n)/(f-n);
+ M[3][0] = M[3][1] = M[3][3] = 0.f;
+}
+LINMATH_H_FUNC void mat4x4_ortho(mat4x4 M, float l, float r, float b, float t, float n, float f)
+{
+ M[0][0] = 2.f/(r-l);
+ M[0][1] = M[0][2] = M[0][3] = 0.f;
+
+ M[1][1] = 2.f/(t-b);
+ M[1][0] = M[1][2] = M[1][3] = 0.f;
+
+ M[2][2] = -2.f/(f-n);
+ M[2][0] = M[2][1] = M[2][3] = 0.f;
+
+ M[3][0] = -(r+l)/(r-l);
+ M[3][1] = -(t+b)/(t-b);
+ M[3][2] = -(f+n)/(f-n);
+ M[3][3] = 1.f;
+}
+LINMATH_H_FUNC void mat4x4_perspective(mat4x4 m, float y_fov, float aspect, float n, float f)
+{
+ /* NOTE: Degrees are an unhandy unit to work with.
+ * linmath.h uses radians for everything! */
+ float const a = 1.f / tanf(y_fov / 2.f);
+
+ m[0][0] = a / aspect;
+ m[0][1] = 0.f;
+ m[0][2] = 0.f;
+ m[0][3] = 0.f;
+
+ m[1][0] = 0.f;
+ m[1][1] = a;
+ m[1][2] = 0.f;
+ m[1][3] = 0.f;
+
+ m[2][0] = 0.f;
+ m[2][1] = 0.f;
+ m[2][2] = -((f + n) / (f - n));
+ m[2][3] = -1.f;
+
+ m[3][0] = 0.f;
+ m[3][1] = 0.f;
+ m[3][2] = -((2.f * f * n) / (f - n));
+ m[3][3] = 0.f;
+}
+LINMATH_H_FUNC void mat4x4_look_at(mat4x4 m, vec3 const eye, vec3 const center, vec3 const up)
+{
+ /* Adapted from Android's OpenGL Matrix.java. */
+ /* See the OpenGL GLUT documentation for gluLookAt for a description */
+ /* of the algorithm. We implement it in a straightforward way: */
+
+ /* TODO: The negation of of can be spared by swapping the order of
+ * operands in the following cross products in the right way. */
+ vec3 f;
+ vec3_sub(f, center, eye);
+ vec3_norm(f, f);
+
+ vec3 s;
+ vec3_mul_cross(s, f, up);
+ vec3_norm(s, s);
+
+ vec3 t;
+ vec3_mul_cross(t, s, f);
+
+ m[0][0] = s[0];
+ m[0][1] = t[0];
+ m[0][2] = -f[0];
+ m[0][3] = 0.f;
+
+ m[1][0] = s[1];
+ m[1][1] = t[1];
+ m[1][2] = -f[1];
+ m[1][3] = 0.f;
+
+ m[2][0] = s[2];
+ m[2][1] = t[2];
+ m[2][2] = -f[2];
+ m[2][3] = 0.f;
+
+ m[3][0] = 0.f;
+ m[3][1] = 0.f;
+ m[3][2] = 0.f;
+ m[3][3] = 1.f;
+
+ mat4x4_translate_in_place(m, -eye[0], -eye[1], -eye[2]);
+}
+
+typedef float quat[4];
+#define quat_add vec4_add
+#define quat_sub vec4_sub
+#define quat_norm vec4_norm
+#define quat_scale vec4_scale
+#define quat_mul_inner vec4_mul_inner
+
+LINMATH_H_FUNC void quat_identity(quat q)
+{
+ q[0] = q[1] = q[2] = 0.f;
+ q[3] = 1.f;
+}
+LINMATH_H_FUNC void quat_mul(quat r, quat const p, quat const q)
+{
+ vec3 w, tmp;
+
+ vec3_mul_cross(tmp, p, q);
+ vec3_scale(w, p, q[3]);
+ vec3_add(tmp, tmp, w);
+ vec3_scale(w, q, p[3]);
+ vec3_add(tmp, tmp, w);
+
+ vec3_dup(r, tmp);
+ r[3] = p[3]*q[3] - vec3_mul_inner(p, q);
+}
+LINMATH_H_FUNC void quat_conj(quat r, quat const q)
+{
+ int i;
+ for(i=0; i<3; ++i)
+ r[i] = -q[i];
+ r[3] = q[3];
+}
+LINMATH_H_FUNC void quat_rotate(quat r, float angle, vec3 const axis) {
+ vec3 axis_norm;
+ vec3_norm(axis_norm, axis);
+ float s = sinf(angle / 2);
+ float c = cosf(angle / 2);
+ vec3_scale(r, axis_norm, s);
+ r[3] = c;
+}
+LINMATH_H_FUNC void quat_mul_vec3(vec3 r, quat const q, vec3 const v)
+{
+/*
+ * Method by Fabian 'ryg' Giessen (of Farbrausch)
+t = 2 * cross(q.xyz, v)
+v' = v + q.w * t + cross(q.xyz, t)
+ */
+ vec3 t;
+ vec3 q_xyz = {q[0], q[1], q[2]};
+ vec3 u = {q[0], q[1], q[2]};
+
+ vec3_mul_cross(t, q_xyz, v);
+ vec3_scale(t, t, 2);
+
+ vec3_mul_cross(u, q_xyz, t);
+ vec3_scale(t, t, q[3]);
+
+ vec3_add(r, v, t);
+ vec3_add(r, r, u);
+}
+LINMATH_H_FUNC void mat4x4_from_quat(mat4x4 M, quat const q)
+{
+ float a = q[3];
+ float b = q[0];
+ float c = q[1];
+ float d = q[2];
+ float a2 = a*a;
+ float b2 = b*b;
+ float c2 = c*c;
+ float d2 = d*d;
+
+ M[0][0] = a2 + b2 - c2 - d2;
+ M[0][1] = 2.f*(b*c + a*d);
+ M[0][2] = 2.f*(b*d - a*c);
+ M[0][3] = 0.f;
+
+ M[1][0] = 2*(b*c - a*d);
+ M[1][1] = a2 - b2 + c2 - d2;
+ M[1][2] = 2.f*(c*d + a*b);
+ M[1][3] = 0.f;
+
+ M[2][0] = 2.f*(b*d + a*c);
+ M[2][1] = 2.f*(c*d - a*b);
+ M[2][2] = a2 - b2 - c2 + d2;
+ M[2][3] = 0.f;
+
+ M[3][0] = M[3][1] = M[3][2] = 0.f;
+ M[3][3] = 1.f;
+}
+
+LINMATH_H_FUNC void mat4x4o_mul_quat(mat4x4 R, mat4x4 const M, quat const q)
+{
+/* XXX: The way this is written only works for orthogonal matrices. */
+/* TODO: Take care of non-orthogonal case. */
+ quat_mul_vec3(R[0], q, M[0]);
+ quat_mul_vec3(R[1], q, M[1]);
+ quat_mul_vec3(R[2], q, M[2]);
+
+ R[3][0] = R[3][1] = R[3][2] = 0.f;
+ R[0][3] = M[0][3];
+ R[1][3] = M[1][3];
+ R[2][3] = M[2][3];
+ R[3][3] = M[3][3]; // typically 1.0, but here we make it general
+}
+LINMATH_H_FUNC void quat_from_mat4x4(quat q, mat4x4 const M)
+{
+ float r=0.f;
+ int i;
+
+ int perm[] = { 0, 1, 2, 0, 1 };
+ int *p = perm;
+
+ for(i = 0; i<3; i++) {
+ float m = M[i][i];
+ if( m < r )
+ continue;
+ m = r;
+ p = &perm[i];
+ }
+
+ r = sqrtf(1.f + M[p[0]][p[0]] - M[p[1]][p[1]] - M[p[2]][p[2]] );
+
+ if(r < 1e-6) {
+ q[0] = 1.f;
+ q[1] = q[2] = q[3] = 0.f;
+ return;
+ }
+
+ q[0] = r/2.f;
+ q[1] = (M[p[0]][p[1]] - M[p[1]][p[0]])/(2.f*r);
+ q[2] = (M[p[2]][p[0]] - M[p[0]][p[2]])/(2.f*r);
+ q[3] = (M[p[2]][p[1]] - M[p[1]][p[2]])/(2.f*r);
+}
+
+LINMATH_H_FUNC void mat4x4_arcball(mat4x4 R, mat4x4 const M, vec2 const _a, vec2 const _b, float s)
+{
+ vec2 a; memcpy(a, _a, sizeof(a));
+ vec2 b; memcpy(b, _b, sizeof(b));
+
+ float z_a = 0.;
+ float z_b = 0.;
+
+ if(vec2_len(a) < 1.) {
+ z_a = sqrtf(1. - vec2_mul_inner(a, a));
+ } else {
+ vec2_norm(a, a);
+ }
+
+ if(vec2_len(b) < 1.) {
+ z_b = sqrtf(1. - vec2_mul_inner(b, b));
+ } else {
+ vec2_norm(b, b);
+ }
+
+ vec3 a_ = {a[0], a[1], z_a};
+ vec3 b_ = {b[0], b[1], z_b};
+
+ vec3 c_;
+ vec3_mul_cross(c_, a_, b_);
+
+ float const angle = acos(vec3_mul_inner(a_, b_)) * s;
+ mat4x4_rotate(R, M, c_[0], c_[1], c_[2], angle);
+}
+#endif
diff --git a/main.c b/main.c
index 7063979..b3648cb 100644
--- a/main.c
+++ b/main.c
@@ -1,12 +1,90 @@
+#define STB_IMAGE_IMPLEMENTATION
+
#include <stdio.h>
#include <vulkan/vulkan.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
#include <stdbool.h>
#include <string.h>
+#include <libpng16/png.h>
+#include "linmath.h"
+#include "stb_image.h"
#define MAX_FRAMES_INFLIGHT 2
+typedef mat4x4 mat4;
+
+struct ubo {
+ mat4 model;
+ mat4 view;
+ mat4 proj;
+};
+
+struct vertex {
+ vec3 pos;
+ vec3 color;
+ vec2 texture_coords;
+};
+
+struct vertex vertices[] = {
+ {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}},
+ {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}},
+ {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}},
+ {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}},
+
+ {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}},
+ {{ 0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}},
+ {{ 0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}},
+ {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}},
+
+/*
+ {{ 0.0f, -0.5f}, {0.445f, 0.09f, 0.727f}},
+
+ {{-0.5f, -1.0f}, {0.445f, 0.09f, 0.727f}},
+ {{-1.0f, -0.5f}, {0.445f, 0.09f, 0.727f}},
+
+ {{ 1.0f, -0.5f}, {0.445f, 0.09f, 0.727f}},
+ {{ 0.5f, -1.0f}, {0.445f, 0.09f, 0.727f}},
+
+ {{ 0.0f, 1.0f}, {0.445f, 0.09f, 0.727f}},
+*/
+};
+
+uint16_t indices[] = {
+ 0, 1, 2, 2, 3, 0,
+ 4, 5, 6, 6, 7, 4
+};
+// uint16_t indices[] = {1, 0, 2, 3, 0, 4, 2, 3, 5};
+
+VkVertexInputBindingDescription get_vertex_description() {
+ return (VkVertexInputBindingDescription) {
+ .binding = 0,
+ .stride = sizeof(struct vertex),
+ .inputRate = VK_VERTEX_INPUT_RATE_VERTEX
+ };
+}
+
+void get_attribute_description(VkVertexInputAttributeDescription out[3]) {
+ out[0] = (VkVertexInputAttributeDescription) {
+ .binding = 0,
+ .location = 0,
+ .format = VK_FORMAT_R32G32B32_SFLOAT,
+ .offset = offsetof(struct vertex, pos)
+ };
+ out[1] = (VkVertexInputAttributeDescription) {
+ .binding = 0,
+ .location = 1,
+ .format = VK_FORMAT_R32G32B32_SFLOAT,
+ .offset = offsetof(struct vertex, color)
+ };
+ out[2] = (VkVertexInputAttributeDescription) {
+ .binding = 0,
+ .location = 2,
+ .format = VK_FORMAT_R32G32_SFLOAT,
+ .offset = offsetof(struct vertex, texture_coords)
+ };
+}
+
static SDL_Window *window = NULL;
static VkDebugUtilsMessengerEXT debug_messenger;
static VkInstance instance = VK_NULL_HANDLE;
@@ -19,6 +97,7 @@ static VkSwapchainKHR swapchain;
static VkFormat swapchain_format;
static VkExtent2D swapchain_extent;
static VkRenderPass render_pass;
+static VkDescriptorSetLayout desc_layout;
static VkPipelineLayout pipeline_layout;
static VkCommandPool command_pool;
static VkCommandBuffer command_buffers[MAX_FRAMES_INFLIGHT];
@@ -26,7 +105,23 @@ static VkPipeline graphics_pipeline;
static VkSemaphore image_avail[MAX_FRAMES_INFLIGHT];
static VkSemaphore render_finished[MAX_FRAMES_INFLIGHT];
static VkFence in_flight_fence[MAX_FRAMES_INFLIGHT];
+static VkBuffer uniform_buffers[MAX_FRAMES_INFLIGHT];
+static VkDeviceMemory uniform_memory[MAX_FRAMES_INFLIGHT];
+static void *mapped_buffers[MAX_FRAMES_INFLIGHT];
+static VkDescriptorPool desc_pool;
+static VkDescriptorSet desc_sets[MAX_FRAMES_INFLIGHT];
static uint32_t current_frame = 0;
+static VkBuffer vertex_buffer;
+static VkDeviceMemory vertex_buffer_memory;
+static VkBuffer index_buffer;
+static VkDeviceMemory index_buffer_memroy;
+static VkImage texture_image;
+static VkDeviceMemory texture_image_memory;
+static VkImageView texture_view;
+static VkSampler texture_sampler;
+static VkImage depth_image;
+static VkDeviceMemory depth_memory;
+static VkImageView depth_image_view;
static struct {
size_t len;
VkImage data[];
@@ -131,6 +226,9 @@ bool found_queue_indx(struct queue_indices inds) {
}
void cleanup_swapchain() {
+ vkDestroyImageView(gpu, depth_image_view, NULL);
+ vkDestroyImage(gpu, depth_image, NULL);
+ vkFreeMemory(gpu, depth_memory, NULL);
for (size_t i = 0; i < framebuffers->len; i++) {
vkDestroyFramebuffer(gpu, framebuffers->data[i], NULL);
}
@@ -146,13 +244,37 @@ void cleanup() {
vkDestroySemaphore(gpu, image_avail[i], NULL);
vkDestroySemaphore(gpu, render_finished[i], NULL);
vkDestroyFence(gpu, in_flight_fence[i], NULL);
+
+ vkDestroyBuffer(gpu, uniform_buffers[i], NULL);
+ vkFreeMemory(gpu, uniform_memory[i], NULL);
}
cleanup_swapchain();
+
+ vkDestroyBuffer(gpu, index_buffer, NULL);
+ vkFreeMemory(gpu, index_buffer_memroy, NULL);
+
+ vkDestroySampler(gpu, texture_sampler, NULL);
+ vkDestroyImageView(gpu, texture_view, NULL);
+ vkDestroyImage(gpu, texture_image, NULL);
+ vkFreeMemory(gpu, texture_image_memory, NULL);
+
+ vkDestroyDescriptorPool(gpu, desc_pool, NULL);
+ vkDestroyDescriptorSetLayout(gpu, desc_layout, NULL);
+
+ vkDestroyBuffer(gpu, vertex_buffer, NULL);
+ vkFreeMemory(gpu, vertex_buffer_memory, NULL);
+
vkDestroyCommandPool(gpu, command_pool, NULL);
+
vkDestroyPipeline(gpu, graphics_pipeline, NULL);
vkDestroyPipelineLayout(gpu, pipeline_layout, NULL);
+
vkDestroyRenderPass(gpu, render_pass, NULL);
vkDestroySurfaceKHR(instance, surface, NULL);
+
+ ((PFN_vkDestroyDebugUtilsMessengerEXT)
+ vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"))(instance, debug_messenger, NULL);
+
vkDestroyDevice(gpu, NULL);
vkDestroyInstance(instance, NULL);
SDL_DestroyWindow(window);
@@ -264,6 +386,9 @@ bool is_device_ok(VkPhysicalDevice dev) {
free(props);
+ VkPhysicalDeviceFeatures supported_features;
+ vkGetPhysicalDeviceFeatures(dev, &supported_features);
+
bool adequate_swapchain = false;
if (swapchain) {
struct swapchain_details details = query_swapchain(dev);
@@ -271,7 +396,8 @@ bool is_device_ok(VkPhysicalDevice dev) {
free_swapchain_details(details);
}
- return found_queue_indx(find_queue_families(dev)) && swapchain && adequate_swapchain;
+ return found_queue_indx(find_queue_families(dev)) && swapchain
+ && adequate_swapchain && supported_features.samplerAnisotropy;
}
void pick_physical_dev() {
@@ -320,7 +446,9 @@ void create_logical_dev() {
},
};
- VkPhysicalDeviceFeatures dev_features = { 0 };
+ VkPhysicalDeviceFeatures dev_features = {
+ .samplerAnisotropy = VK_TRUE
+ };
const char * const ext = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
@@ -401,36 +529,44 @@ void create_swapchain() {
}
+VkImageView create_image_view(VkImage image, VkFormat format, VkImageAspectFlags aspect_flags) {
+ VkImageViewCreateInfo create_info = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .image = image,
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .format = format,
+ .components = {
+ .r = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .g = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .b = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .a = VK_COMPONENT_SWIZZLE_IDENTITY,
+ },
+ .subresourceRange = {
+ .aspectMask = aspect_flags,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1
+ }
+ };
+
+ VkImageView view;
+
+ VkResult res = vkCreateImageView(gpu, &create_info, NULL, &view);
+ if (res != VK_SUCCESS) {
+ fputs("failed to create image view", stderr);
+ exit(-1);
+ }
+
+ return view;
+}
+
void create_image_views() {
swapchain_image_views = malloc(sizeof(*swapchain_image_views) + sizeof(*swapchain_image_views->data) * swapchain_images->len);
swapchain_image_views->len = swapchain_images->len;
for (size_t i = 0; i < swapchain_images->len; i++) {
- VkImageViewCreateInfo create_info = {
- .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
- .image = swapchain_images->data[i],
- .viewType = VK_IMAGE_VIEW_TYPE_2D,
- .format = swapchain_format,
- .components = {
- .r = VK_COMPONENT_SWIZZLE_IDENTITY,
- .g = VK_COMPONENT_SWIZZLE_IDENTITY,
- .b = VK_COMPONENT_SWIZZLE_IDENTITY,
- .a = VK_COMPONENT_SWIZZLE_IDENTITY,
- },
- .subresourceRange = {
- .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
- .baseMipLevel = 0,
- .levelCount = 1,
- .baseArrayLayer = 0,
- .layerCount = 1
- }
- };
-
- VkResult res = vkCreateImageView(gpu, &create_info, NULL, &swapchain_image_views->data[i]);
- if (res != VK_SUCCESS) {
- fputs("failed to create image view", stderr);
- exit(-1);
- }
+ swapchain_image_views->data[i] = create_image_view(swapchain_images->data[i], swapchain_format, VK_IMAGE_ASPECT_COLOR_BIT);
}
}
@@ -447,7 +583,7 @@ struct code *readfile(const char *filename) {
struct code *bytes = malloc(sizeof(*bytes) + sizeof(*bytes->data) * len);
bytes->len = len;
- fread(bytes->data, sizeof(*bytes->data), len, fp);
+ size_t read = fread(bytes->data, sizeof(*bytes->data), len, fp);
fclose(fp);
return bytes;
@@ -493,8 +629,16 @@ void create_graphics_pipeline() {
VkPipelineShaderStageCreateInfo shader_stages[] = { vert_shader_info, frag_shader_info };
+ VkVertexInputBindingDescription desc = get_vertex_description();
+ VkVertexInputAttributeDescription attrs[3];
+ get_attribute_description(attrs);
+
VkPipelineVertexInputStateCreateInfo vertex_input_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .vertexBindingDescriptionCount = 1,
+ .pVertexBindingDescriptions = &desc,
+ .vertexAttributeDescriptionCount = sizeof(attrs) / sizeof(*attrs),
+ .pVertexAttributeDescriptions = attrs
};
VkPipelineInputAssemblyStateCreateInfo input_assembly = {
@@ -541,7 +685,7 @@ void create_graphics_pipeline() {
.polygonMode = VK_POLYGON_MODE_FILL,
.lineWidth = 1.0f,
.cullMode = VK_CULL_MODE_BACK_BIT,
- .frontFace = VK_FRONT_FACE_CLOCKWISE,
+ .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.depthBiasEnable = VK_FALSE,
};
@@ -564,8 +708,21 @@ void create_graphics_pipeline() {
.pAttachments = &color_state
};
+ VkPipelineDepthStencilStateCreateInfo depth_stencil = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+ .depthTestEnable = VK_TRUE,
+ .depthWriteEnable = VK_TRUE,
+ .depthCompareOp = VK_COMPARE_OP_LESS,
+ .depthBoundsTestEnable = VK_FALSE,
+ .minDepthBounds = 0.0f,
+ .maxDepthBounds = 1.0,
+ .stencilTestEnable = VK_FALSE,
+ };
+
VkPipelineLayoutCreateInfo pipeline_layout_create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+ .setLayoutCount = 1,
+ .pSetLayouts = &desc_layout
};
VkResult res = vkCreatePipelineLayout(gpu, &pipeline_layout_create_info, NULL, &pipeline_layout);
@@ -585,6 +742,7 @@ void create_graphics_pipeline() {
.pMultisampleState = &multisampling,
.pColorBlendState = &color_blending,
.pDynamicState = &dynamic_state_info,
+ .pDepthStencilState = &depth_stencil,
.layout = pipeline_layout,
.renderPass = render_pass,
.subpass = 0,
@@ -602,6 +760,135 @@ void create_graphics_pipeline() {
vkDestroyShaderModule(gpu, vert_shader, NULL);
}
+uint32_t find_memory_type(uint32_t type_filter, VkMemoryPropertyFlags props) {
+ VkPhysicalDeviceMemoryProperties mem_props;
+ vkGetPhysicalDeviceMemoryProperties(phy_gpu, &mem_props);
+
+ for (size_t i = 0; i < mem_props.memoryTypeCount; i++)
+ if (type_filter & (1 << i) && (mem_props.memoryTypes[i].propertyFlags & props) == props)
+ return i;
+
+ fputs("failed to find memory type", stderr);
+ exit(-1);
+}
+
+void create_buffer(VkDeviceSize size, VkBufferUsageFlags usage,
+ VkMemoryPropertyFlags props, VkBuffer *buffer, VkDeviceMemory *buffer_memory) {
+ VkBufferCreateInfo buffer_info = {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ .size = size,
+ .usage = usage,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE
+ };
+
+ VkResult res = vkCreateBuffer(gpu, &buffer_info, NULL, buffer);
+ if (res != VK_SUCCESS) {
+ fputs("failed to create vertex buffer", stderr);
+ exit(-1);
+ }
+
+ VkMemoryRequirements mem_reqs;
+ vkGetBufferMemoryRequirements(gpu, *buffer, &mem_reqs);
+
+ VkMemoryAllocateInfo alloc_info = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .allocationSize = mem_reqs.size,
+ .memoryTypeIndex = find_memory_type(mem_reqs.memoryTypeBits, props)
+ };
+
+ res = vkAllocateMemory(gpu, &alloc_info, NULL, buffer_memory);
+ if (res != VK_SUCCESS) {
+ fputs("failed to allocate memory", stderr);
+ exit(-1);
+ }
+
+ vkBindBufferMemory(gpu, *buffer, *buffer_memory, 0);
+}
+
+VkCommandBuffer being_single_command() {
+ VkCommandBufferAllocateInfo alloc_info = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ .commandPool = command_pool,
+ .commandBufferCount = 1
+ };
+
+ VkCommandBuffer cmd_buf;
+ vkAllocateCommandBuffers(gpu, &alloc_info, &cmd_buf);
+
+ VkCommandBufferBeginInfo begin_info = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
+ };
+
+ vkBeginCommandBuffer(cmd_buf, &begin_info);
+
+ return cmd_buf;
+}
+
+void end_single_command(VkCommandBuffer command_buffer) {
+ vkEndCommandBuffer(command_buffer);
+
+ VkSubmitInfo submit_info = {
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .commandBufferCount = 1,
+ .pCommandBuffers = &command_buffer
+ };
+
+ vkQueueSubmit(gfx_queue, 1, &submit_info, VK_NULL_HANDLE);
+ vkQueueWaitIdle(gfx_queue);
+
+ vkFreeCommandBuffers(gpu, command_pool, 1, &command_buffer);
+}
+
+void copy_buffer_to_image(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
+ VkCommandBuffer command_buffer = being_single_command();
+
+ VkBufferImageCopy region = {
+ .bufferRowLength = 0,
+ .bufferOffset = 0,
+ .bufferImageHeight = 0,
+ .imageSubresource = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = 0,
+ .baseArrayLayer = 0,
+ .layerCount = 1
+ },
+ .imageOffset = { 0, 0, 0 },
+ .imageExtent = { width, height, 1 }
+ };
+
+ vkCmdCopyBufferToImage(command_buffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
+
+ end_single_command(command_buffer);
+}
+
+void copy_buffer(VkBuffer src, VkBuffer dst, VkDeviceSize size) {
+ VkCommandBuffer cmd_buf = being_single_command();
+ vkCmdCopyBuffer(cmd_buf, src, dst, 1, &(VkBufferCopy) { .size = size });
+ end_single_command(cmd_buf);
+}
+
+void create_vertex_buffer() {
+ VkBuffer tmp_buffer;
+ VkDeviceMemory tmp_mem;
+ create_buffer(sizeof(vertices), VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &tmp_buffer, &tmp_mem);
+
+ void *data;
+ vkMapMemory(gpu, tmp_mem, 0, sizeof(vertices), 0, &data);
+ memcpy(data, vertices, sizeof(vertices));
+ vkUnmapMemory(gpu, tmp_mem);
+
+ create_buffer(sizeof(vertices), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &vertex_buffer, &vertex_buffer_memory);
+
+ copy_buffer(tmp_buffer, vertex_buffer, sizeof(vertices));
+
+ vkDestroyBuffer(gpu, tmp_buffer, NULL);
+ vkFreeMemory(gpu, tmp_mem, NULL);
+}
+
void create_render_pass() {
VkAttachmentDescription color_attach = {
.format = swapchain_format,
@@ -619,25 +906,44 @@ void create_render_pass() {
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
+ VkAttachmentDescription depth_attach = {
+ .format = VK_FORMAT_D32_SFLOAT,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+ .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
+ };
+
+ VkAttachmentReference depth_attach_ref = {
+ .attachment = 1,
+ .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
+ };
+
VkSubpassDescription subpass = {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = 1,
- .pColorAttachments = &color_attach_ref
+ .pColorAttachments = &color_attach_ref,
+ .pDepthStencilAttachment = &depth_attach_ref
};
VkSubpassDependency dep = {
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
- .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
- .srcAccessMask = 0,
- .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
- .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
+ .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
+ .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
+ .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT
};
+ VkAttachmentDescription attachs[] = { color_attach, depth_attach };
+
VkRenderPassCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
- .attachmentCount = 1,
- .pAttachments = &color_attach,
+ .attachmentCount = sizeof(attachs) / sizeof(*attachs),
+ .pAttachments = attachs,
.subpassCount = 1,
.pSubpasses = &subpass,
.dependencyCount = 1,
@@ -657,13 +963,14 @@ void create_framebuffers() {
for (size_t i = 0; i < swapchain_image_views->len; i++) {
VkImageView attachs[] = {
- swapchain_image_views->data[i]
+ swapchain_image_views->data[i],
+ depth_image_view
};
VkFramebufferCreateInfo framebuffer_info = {
.sType =VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = render_pass,
- .attachmentCount = 1,
+ .attachmentCount = sizeof(attachs) / sizeof(*attachs),
.pAttachments = attachs,
.width = swapchain_extent.width,
.height = swapchain_extent.height,
@@ -720,7 +1027,10 @@ void record_command_buffer(VkCommandBuffer buffer, uint32_t image_index) {
exit(-1);
}
- VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
+ VkClearValue clear_color[] = {
+ { .color = {0.0f, 0.0f, 0.0f, 1.0f}},
+ { .depthStencil = { 1.0f, 0 }}
+ };
VkRenderPassBeginInfo render_pass_info = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = render_pass,
@@ -729,8 +1039,8 @@ void record_command_buffer(VkCommandBuffer buffer, uint32_t image_index) {
.extent = swapchain_extent,
.offset = {0, 0}
},
- .clearValueCount = 1,
- .pClearValues = &clear_color
+ .clearValueCount = sizeof(clear_color) / sizeof(*clear_color),
+ .pClearValues = clear_color
};
vkCmdBeginRenderPass(buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
@@ -752,7 +1062,16 @@ void record_command_buffer(VkCommandBuffer buffer, uint32_t image_index) {
};
vkCmdSetScissor(buffer, 0, 1, &scissor);
- vkCmdDraw(buffer, 3, 1, 0, 0);
+
+ VkBuffer vertex_buffers[] = { vertex_buffer };
+ VkDeviceSize offsets[] = {0};
+ vkCmdBindVertexBuffers(buffer, 0, 1, vertex_buffers, offsets);
+ vkCmdBindIndexBuffer(buffer, index_buffer, 0, VK_INDEX_TYPE_UINT16);
+
+ vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ pipeline_layout, 0, 1, &desc_sets[current_frame], 0, NULL);
+
+ vkCmdDrawIndexed(buffer, sizeof(indices) / sizeof(*indices), 1, 0, 0, 0);
vkCmdEndRenderPass(buffer);
@@ -780,6 +1099,316 @@ void create_sync_objects() {
}
}
+void create_index_buffer() {
+ VkBuffer tmp_buffer;
+ VkDeviceMemory tmp_mem;
+ create_buffer(sizeof(indices), VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &tmp_buffer, &tmp_mem);
+
+ void *data;
+ vkMapMemory(gpu, tmp_mem, 0, sizeof(indices), 0, &data);
+ memcpy(data, indices, sizeof(indices));
+ vkUnmapMemory(gpu, tmp_mem);
+
+ create_buffer(sizeof(indices), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &index_buffer, &index_buffer_memroy);
+
+ copy_buffer(tmp_buffer, index_buffer, sizeof(indices));
+
+ vkDestroyBuffer(gpu, tmp_buffer, NULL);
+ vkFreeMemory(gpu, tmp_mem, NULL);
+}
+
+void create_descriptor_layout() {
+ VkDescriptorSetLayoutBinding layout_bind[] = {
+ {
+ .binding = 0,
+ .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT
+ }, {
+ .binding = 1,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .pImmutableSamplers = NULL,
+ .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
+ }
+ };
+
+
+
+ VkDescriptorSetLayoutCreateInfo desc_create_info = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+ .bindingCount = sizeof(layout_bind) / sizeof(*layout_bind),
+ .pBindings = layout_bind
+ };
+
+ VkResult res = vkCreateDescriptorSetLayout(gpu, &desc_create_info, NULL, &desc_layout);
+ if (res != VK_SUCCESS) {
+ fputs("failed to create descriptor set layout", stderr);
+ exit(-1);
+ }
+}
+
+void create_uniform_buffers() {
+ for (size_t i = 0; i < MAX_FRAMES_INFLIGHT; i++) {
+ create_buffer(sizeof(struct ubo), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ &uniform_buffers[i], &uniform_memory[i]);
+ vkMapMemory(gpu, uniform_memory[i], 0, sizeof(struct ubo), 0, &mapped_buffers[i]);
+ }
+}
+
+void create_descriptor_pool() {
+ VkDescriptorPoolSize pool_size[] = {
+ {
+ .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ .descriptorCount = MAX_FRAMES_INFLIGHT
+ }, {
+ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = MAX_FRAMES_INFLIGHT
+ }
+ };
+
+ VkDescriptorPoolCreateInfo pool_info = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ .poolSizeCount = sizeof(pool_size) / sizeof(*pool_size),
+ .pPoolSizes = pool_size,
+ .maxSets = MAX_FRAMES_INFLIGHT
+ };
+
+ VkResult res = vkCreateDescriptorPool(gpu, &pool_info, NULL, &desc_pool);
+ if (res != VK_SUCCESS) {
+ fputs("failed to create descriptor pool", stderr);
+ exit(-1);
+ }
+}
+
+void create_descriptor_sets() {
+ VkDescriptorSetLayout layouts[MAX_FRAMES_INFLIGHT] = {
+ desc_layout,
+ desc_layout
+ };
+
+ VkDescriptorSetAllocateInfo alloc_info = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ .descriptorPool = desc_pool,
+ .descriptorSetCount = MAX_FRAMES_INFLIGHT,
+ .pSetLayouts = layouts
+ };
+
+ VkResult res = vkAllocateDescriptorSets(gpu, &alloc_info, desc_sets);
+ if (res != VK_SUCCESS) {
+ fputs("failed to allocate descriptor sets", stderr);
+ exit(-1);
+ }
+
+ for (size_t i = 0; i < MAX_FRAMES_INFLIGHT; i++) {
+ VkDescriptorBufferInfo buffer_info = {
+ .buffer = uniform_buffers[i],
+ .offset = 0,
+ .range = sizeof(struct ubo)
+ };
+
+ VkDescriptorImageInfo image_info = {
+ .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ .imageView = texture_view,
+ .sampler = texture_sampler
+ };
+
+ VkWriteDescriptorSet desc_write[] = {
+ {
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = desc_sets[i],
+ .dstBinding = 0,
+ .dstArrayElement = 0,
+ .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ .descriptorCount = 1,
+ .pBufferInfo = &buffer_info,
+ }, {
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = desc_sets[i],
+ .dstBinding = 1,
+ .dstArrayElement = 0,
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = 1,
+ .pImageInfo = &image_info,
+ }
+ };
+
+ vkUpdateDescriptorSets(gpu, sizeof(desc_write) / sizeof(*desc_write), desc_write, 0, NULL);
+ }
+}
+
+void create_image(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling,
+ VkImageUsageFlags usage, VkMemoryPropertyFlags props, VkImage *img, VkDeviceMemory *memory) {
+ VkImageCreateInfo image_info = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .extent = {
+ .width = width,
+ .height = height,
+ .depth = 1
+ },
+ .mipLevels = 1,
+ .arrayLayers = 1,
+ .format = format,
+ .tiling = tiling,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .usage = usage,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ .samples = VK_SAMPLE_COUNT_1_BIT
+ };
+
+ VkResult res = vkCreateImage(gpu, &image_info, NULL, img);
+ if (res != VK_SUCCESS) {
+ fputs("failed to create image", stderr);
+ exit(-1);
+ }
+
+ VkMemoryRequirements mem_reqs;
+ vkGetImageMemoryRequirements(gpu, *img, &mem_reqs);
+
+ VkMemoryAllocateInfo alloc_info = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .allocationSize = mem_reqs.size,
+ .memoryTypeIndex = find_memory_type(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
+ };
+
+ res = vkAllocateMemory(gpu, &alloc_info, NULL, memory);
+ if (res != VK_SUCCESS) {
+ fputs("failed to allocate memory", stderr);
+ exit(-1);
+ }
+
+ vkBindImageMemory(gpu, *img, *memory, 0);
+}
+
+void transition_image_layout(VkImage image, VkFormat format, VkImageLayout old_layout, VkImageLayout new_layout) {
+ VkCommandBuffer command_buffer = being_single_command();
+ VkImageMemoryBarrier barrier = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .oldLayout = old_layout,
+ .newLayout = new_layout,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = image,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1
+ },
+ .srcAccessMask = 0,
+ .dstAccessMask = 0
+ };
+
+ VkPipelineStageFlags src_flags, dst_flags;
+
+ if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED && new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
+ barrier.srcAccessMask = 0;
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+ src_flags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ dst_flags = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ } else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
+ && new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+
+ src_flags = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ dst_flags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ } else {
+ fputs("unsupported layout transition", stderr);
+ exit(-1);
+ }
+
+ vkCmdPipelineBarrier(command_buffer, src_flags, dst_flags, 0, 0, NULL, 0, NULL, 1, &barrier);
+
+ end_single_command(command_buffer);
+}
+
+void create_texture_image() {
+ int32_t width, height, channels;
+
+ stbi_uc *pixels = stbi_load("pfp.png", &width, &height, &channels, STBI_rgb_alpha);
+ if (!pixels) {
+ fputs("failed to open pfp.png", stderr);
+ exit(-1);
+ }
+
+ VkDeviceSize image_size = width * height * 4;
+
+ VkBuffer tmp_buffer;
+ VkDeviceMemory tmp_mem;
+
+ create_buffer(image_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &tmp_buffer, &tmp_mem);
+
+ void *data;
+ vkMapMemory(gpu, tmp_mem, 0, image_size, 0, &data);
+ memcpy(data, pixels, image_size);
+ vkUnmapMemory(gpu, tmp_mem);
+
+ stbi_image_free(pixels);
+
+ create_image(width, height, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &texture_image, &texture_image_memory);
+
+ transition_image_layout(texture_image, VK_FORMAT_R8G8B8A8_SRGB,
+ VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+
+ copy_buffer_to_image(tmp_buffer, texture_image, width, height);
+
+ transition_image_layout(texture_image, VK_FORMAT_R8G8B8A8_SRGB,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+ vkDestroyBuffer(gpu, tmp_buffer, NULL);
+ vkFreeMemory(gpu, tmp_mem, NULL);
+}
+
+void create_texture_view() {
+ texture_view = create_image_view(texture_image, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT);
+}
+
+void create_texture_sampler() {
+ VkPhysicalDeviceProperties props;
+ vkGetPhysicalDeviceProperties(phy_gpu, &props);
+ VkSamplerCreateInfo sampler_info = {
+ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+ .magFilter = VK_FILTER_LINEAR,
+ .minFilter = VK_FILTER_LINEAR,
+ .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ .anisotropyEnable = VK_TRUE,
+ .maxAnisotropy = props.limits.maxSamplerAnisotropy,
+ .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
+ .unnormalizedCoordinates = VK_FALSE,
+ .compareEnable = VK_FALSE,
+ .compareOp = VK_COMPARE_OP_ALWAYS,
+ .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
+ .mipLodBias = 0.0f,
+ .minLod = 0.0f,
+ .maxLod = 0.0f
+ };
+
+ VkResult res = vkCreateSampler(gpu, &sampler_info, NULL, &texture_sampler);
+ if (res != VK_SUCCESS) {
+ fputs("failed to create image sampler", stderr);
+ exit(-1);
+ }
+}
+
+void create_depth_resources() {
+ VkFormat depth_format = VK_FORMAT_D32_SFLOAT;
+ create_image(swapchain_extent.width, swapchain_extent.height, VK_FORMAT_D32_SFLOAT, VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &depth_image, &depth_memory);
+
+ depth_image_view = create_image_view(depth_image, VK_FORMAT_D32_SFLOAT, VK_IMAGE_ASPECT_DEPTH_BIT);
+}
+
void recreate_swapchain() {
int32_t width = 0, height = 0;
SDL_GetWindowSize(window, &width, &height);
@@ -795,6 +1424,7 @@ void recreate_swapchain() {
create_swapchain();
create_image_views();
+ create_depth_resources();
create_framebuffers();
}
@@ -831,13 +1461,41 @@ void init() {
create_swapchain();
create_image_views();
create_render_pass();
+ create_descriptor_layout();
create_graphics_pipeline();
- create_framebuffers();
create_command_pool();
+ create_depth_resources();
+ create_framebuffers();
+ create_texture_image();
+ create_texture_view();
+ create_texture_sampler();
+ create_vertex_buffer();
+ create_index_buffer();
+ create_uniform_buffers();
+ create_descriptor_pool();
+ create_descriptor_sets();
create_command_buffers();
create_sync_objects();
}
+void update_uniform_buffer(uint32_t current_image) {
+ static double rotate = 0.1;
+ struct ubo ubo = {0};
+ mat4 id;
+ mat4x4_identity(id);
+ mat4x4_rotate(ubo.model, id, 0, 0, 1.0f, rotate * 90);
+ mat4x4_translate_in_place(ubo.model, 0, 0, 1);
+
+ mat4x4_look_at(ubo.view, (vec3){ 2, 2, 2 }, (vec3){ 0, 0, 1}, (vec3){ 0, 0, 1 });
+
+ mat4x4_perspective(ubo.proj, 45, swapchain_extent.width / (float) swapchain_extent.height, 0.1f, 10.0f);
+
+ ubo.proj[1][1] *= -1;
+
+ memcpy(mapped_buffers[current_frame], &ubo, sizeof(ubo));
+ rotate += 0.0001;
+}
+
void draw() {
vkWaitForFences(gpu, 1, &in_flight_fence[current_frame], VK_TRUE, UINT64_MAX);
@@ -845,9 +1503,9 @@ void draw() {
VkResult res = vkAcquireNextImageKHR(gpu, swapchain, UINT64_MAX, image_avail[current_frame], VK_NULL_HANDLE, &image_index);
switch (res) {
case VK_SUCCESS:
+ case VK_SUBOPTIMAL_KHR:
break;
case VK_ERROR_OUT_OF_DATE_KHR:
- case VK_SUBOPTIMAL_KHR:
recreate_swapchain();
return;
default:
@@ -873,6 +1531,8 @@ void draw() {
.pCommandBuffers = &command_buffers[current_frame]
};
+ update_uniform_buffer(current_frame);
+
res = vkQueueSubmit(gfx_queue, 1, &submit_info, in_flight_fence[current_frame]);
if (res != VK_SUCCESS) {
fputs("failed to submit draw command buffer", stderr);
@@ -889,7 +1549,18 @@ void draw() {
.pImageIndices = &image_index
};
- vkQueuePresentKHR(present_queue, &present_info);
+ res = vkQueuePresentKHR(present_queue, &present_info);
+ switch (res) {
+ case VK_SUCCESS:
+ break;
+ case VK_SUBOPTIMAL_KHR:
+ case VK_ERROR_OUT_OF_DATE_KHR:
+ recreate_swapchain();
+ return;
+ default:
+ fputs("failed to acquire swapchain images", stderr);
+ exit(-1);
+ }
current_frame = (current_frame + 1) % MAX_FRAMES_INFLIGHT;
}
diff --git a/pfp.png b/pfp.png
new file mode 100644
index 0000000..622f780
--- /dev/null
+++ b/pfp.png
Binary files differ
diff --git a/shader.frag b/shader.frag
index c9e559f..2209f4e 100644
--- a/shader.frag
+++ b/shader.frag
@@ -1,8 +1,10 @@
#version 450
+layout(binding = 1) uniform sampler2D texture_sampler;
layout(location = 0) in vec3 fragColor;
+layout(location = 1) in vec2 frag_text_coords;
layout(location = 0) out vec4 outColor;
void main() {
- outColor = vec4(fragColor, 1.0);
+ outColor = vec4(fragColor * texture(texture_sampler, frag_text_coords).rgb, 1.0);
}
diff --git a/shader.vert b/shader.vert
index 9e69113..7535193 100644
--- a/shader.vert
+++ b/shader.vert
@@ -1,20 +1,20 @@
#version 450
-layout(location = 0) out vec3 fragColor;
+layout(binding = 0) uniform UniformBufferObject {
+ mat4 model;
+ mat4 view;
+ mat4 proj;
+} ubo;
-vec2 positions[3] = vec2[](
- vec2(0.0, -0.5),
- vec2(0.5, 0.5),
- vec2(-0.5, 0.5)
-);
+layout(location = 0) in vec3 in_position;
+layout(location = 1) in vec3 in_color;
+layout(location = 2) in vec2 in_text_coords;
-vec3 colors[3] = vec3[](
- vec3(1.0, 0.0, 0.0),
- vec3(0.0, 1.0, 0.0),
- vec3(0.0, 0.0, 1.0)
-);
+layout(location = 0) out vec3 frag_color;
+layout(location = 1) out vec2 frag_text_coords;
void main() {
- gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
- fragColor = colors[gl_VertexIndex];
+ gl_Position = ubo.proj * ubo.view * ubo.model * vec4(in_position, 1.0);
+ frag_color = in_color;
+ frag_text_coords = in_text_coords;
}