#include #include #include #include #include #include #include #include #include #include #include #include typedef float Vec3[3]; typedef float Mat3x3[3*3]; typedef struct { float v[4]; } Vec4; typedef struct { float m[4*4]; } Mat4; static inline Vec4 Vec4_make(float x, float y, float z, float w) { Vec4 r = { .v = {x,y,z,w} }; return r; } static inline Mat4 Mat4_make(float a00,float a01,float a02,float a03, float a10,float a11,float a12,float a13, float a20,float a21,float a22,float a23, float a30,float a31,float a32,float a33) { Mat4 m = { .m = { a00,a01,a02,a03, a10,a11,a12,a13, a20,a21,a22,a23, a30,a31,a32,a33 } }; return m; } /* Print a Vec4 in the form [x y z w] */ static inline void Vec4_print(const Vec4 *v, FILE *out) { if (!out) out = stdout; fprintf(out, "[%g %g %g %g]\n", v->v[0], v->v[1], v->v[2], v->v[3]); } /* Print a Mat4 in 4 rows */ static inline void Mat4_print(const Mat4 *m, FILE *out) { if (!out) out = stdout; for (int i = 0; i < 4; ++i) { fprintf(out, "[%g %g %g %g]\n", m->m[i*4 + 0], m->m[i*4 + 1], m->m[i*4 + 2], m->m[i*4 + 3]); } } static inline Vec4 Mat4_mulVec4(const Mat4 m, const Vec4 v) { Vec4 r; for (int i = 0; i < 4; ++i) { r.v[i] = m.m[i*4 + 0]*v.v[0] + m.m[i*4 + 1]*v.v[1] + m.m[i*4 + 2]*v.v[2] + m.m[i*4 + 3]*v.v[3]; } return r; } /* helpers */ static inline Mat4 Mat4_identity(void) { Mat4 r = { .m = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 } }; return r; } static inline Mat4 Mat4_mul(const Mat4 a, const Mat4 b) { Mat4 c; for (int i = 0; i < 4; ++i){ for (int j = 0; j < 4; ++j) { float s = 0.0f; for (int k = 0; k < 4; ++k) s += a.m[i*4 + k] * b.m[k*4 + j]; c.m[i*4 + j] = s; } } return c; } static inline Mat4 Mat4_mul_n(size_t n, const Mat4 *mats) { Mat4 result = Mat4_identity(); /* multiply right-to-left so the last matrix is applied first: result = mats[n-1] * mats[n-2] * ... * mats[0] */ for (size_t i = n; i-- > 0; ) { result = Mat4_mul(mats[i], result); } return result; } #define MAT4_MUL_VAR(...) Mat4_mul_n((sizeof((Mat4[]){__VA_ARGS__})/sizeof(Mat4)), (Mat4[]){__VA_ARGS__}) /* Translate by (tx,ty,tz) */ static inline Mat4 Mat4_translate(float tx, float ty, float tz) { Mat4 r = Mat4_identity(); r.m[0*4 + 3] = tx; r.m[1*4 + 3] = ty; r.m[2*4 + 3] = tz; return r; } /* Scale by (sx,sy,sz) */ static inline Mat4 Mat4_scale(float sx, float sy, float sz) { Mat4 r = Mat4_identity(); r.m[0*4 + 0] = sx; r.m[1*4 + 1] = sy; r.m[2*4 + 2] = sz; return r; } /* Shear: specify XY, XZ, YX, YZ, ZX, ZY components (shear of axis_row by axis_col) Example: shear_xy is X += shear_xy * Y */ static inline Mat4 Mat4_shear(float shear_xy, float shear_xz, float shear_yx, float shear_yz, float shear_zx, float shear_zy) { Mat4 r = Mat4_identity(); r.m[0*4 + 1] = shear_xy; r.m[0*4 + 2] = shear_xz; r.m[1*4 + 0] = shear_yx; r.m[1*4 + 2] = shear_yz; r.m[2*4 + 0] = shear_zx; r.m[2*4 + 1] = shear_zy; return r; } /* Rotation around X, Y, Z (angles in radians) */ static inline Mat4 Mat4_rotate_x(float a) { float c = cosf(a), s = sinf(a); Mat4 r = Mat4_identity(); r.m[1*4 + 1] = c; r.m[1*4 + 2] = -s; r.m[2*4 + 1] = s; r.m[2*4 + 2] = c; return r; } static inline Mat4 Mat4_rotate_y(float a) { float c = cosf(a), s = sinf(a); Mat4 r = Mat4_identity(); r.m[0*4 + 0] = c; r.m[0*4 + 2] = s; r.m[2*4 + 0] = -s; r.m[2*4 + 2] = c; return r; } static inline Mat4 Mat4_rotate_z(float a) { float c = cosf(a), s = sinf(a); Mat4 r = Mat4_identity(); r.m[0*4 + 0] = c; r.m[0*4 + 1] = -s; r.m[1*4 + 0] = s; r.m[1*4 + 1] = c; return r; } /* Perspective projection fovy: vertical field of view in radians aspect: width/height znear, zfar: positive distances Produces standard right-handed projection mapping view-space z to [-1,1] (OpenGL style). */ static inline Mat4 Mat4_perspective(float fovy, float aspect, float znear, float zfar) { float f = 1.0f / tanf(fovy * 0.5f); Mat4 p = { .m = {0} }; p.m[0*4 + 0] = f / aspect; p.m[1*4 + 1] = f; p.m[2*4 + 2] = (zfar + znear) / (znear - zfar); p.m[2*4 + 3] = (2.0f * zfar * znear) / (znear - zfar); p.m[3*4 + 2] = -1.0f; return p; } /* Build full transform: scale -> rotateZ -> rotateY -> rotateX -> shear -> translate -> perspective (Order is applied right-to-left when multiplying: final = P * T * Shear * R_x * R_y * R_z * S) */ static inline Mat4 Mat4_full_transform(float sx, float sy, float sz, float rot_x, float rot_y, float rot_z, float shear_xy, float shear_xz, float shear_yx, float shear_yz, float shear_zx, float shear_zy, float tx, float ty, float tz, float fovy, float aspect, float znear, float zfar) { Mat4 S = Mat4_scale(sx, sy, sz); Mat4 R = Mat4_mul(Mat4_rotate_x(rot_x), Mat4_mul(Mat4_rotate_y(rot_y), Mat4_rotate_z(rot_z))); Mat4 Sh = Mat4_shear(shear_xy, shear_xz, shear_yx, shear_yz, shear_zx, shear_zy); Mat4 T = Mat4_translate(tx, ty, tz); Mat4 M = Mat4_mul(T, Mat4_mul(Sh, Mat4_mul(R, S))); /* M = T * Shear * R * S */ Mat4 P = Mat4_perspective(fovy, aspect, znear, zfar); Mat4 final = Mat4_mul(P, M); /* final = P * M */ return final; } int main(void) { float sx = 1.5f, sy = 1.5f, sz = 1.5f; float rx = 0.0f, ry = (float)M_PI / 4.0f, rz = 0.0f; float shear_xy = 0.2f, shear_xz = 0.0f, shear_yx = 0.0f, shear_yz = 0.0f, shear_zx = 0.0f, shear_zy = 0.0f; float tx = 2.0f, ty = 3.0f, tz = 4.0f; float fovy = 60.0f * ((float)M_PI / 180.0f); float aspect = 16.0f / 9.0f; float znear = 0.1f, zfar = 100.0f; Mat4 M = Mat4_full_transform( 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, fovy, aspect, znear, zfar ); /* example point in object space (x,y,z,1) */ Vec4 points[8] = { Vec4_make(1, 1, 1, 1), Vec4_make(1, 1, -1, 1), Vec4_make(1, -1, 1, 1), Vec4_make(1, -1, -1, 1), Vec4_make(-1, 1, 1, 1), Vec4_make(-1, 1, -1, 1), Vec4_make(-1, -1, 1, 1), Vec4_make(-1, -1, -1, 1), }; typedef struct { int e[2]; } Edge; Edge edges[] = { (Edge){.e = {0, 1}}, (Edge){.e = {1, 3}}, (Edge){.e = {3, 2}}, (Edge){.e = {0, 2}}, (Edge){.e = {4, 5}}, (Edge){.e = {5, 7}}, (Edge){.e = {7, 6}}, (Edge){.e = {4, 6}}, (Edge){.e = {0, 4}}, (Edge){.e = {1, 5}}, (Edge){.e = {2, 6}}, (Edge){.e = {3, 7}}, }; SDL_Init(SDL_INIT_VIDEO); SDL_Renderer *renderer; SDL_Window *window; SDL_CreateWindowAndRenderer("name", 512, 512, SDL_WINDOW_TRANSPARENT, &window, &renderer); float roty = 0; bool running = true; while(running){ SDL_Event e; while(SDL_PollEvent(&e)){ switch(e.type){ case SDL_EVENT_QUIT: { running = false; } break; } } SDL_SetRenderDrawColor(renderer, 0x11, 0x11, 0x11, 0xFF); SDL_RenderClear(renderer); Vec4 projected[8] = {0}; for(int i = 0; i < sizeof(points) / sizeof(*points); i++){ Mat4 rx = Mat4_rotate_x(0.3); Mat4 ry = Mat4_rotate_y(roty); Mat4 campos = Mat4_translate(0, 0, 10); Mat4 translate1 = Mat4_translate(0, 0, -1); Mat4 rz = Mat4_rotate_x(roty); Mat4 translate2 = Mat4_translate(0, 0, 1); Mat4 pers = Mat4_make( 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0.2,0 ); // Mat4 transform = Mat4_mul(ry, rx); Mat4 transform = MAT4_MUL_VAR(pers, campos, translate2, rz, translate1, rx, ry); projected[i] = Mat4_mulVec4(transform, points[i]); projected[i].v[0] /= projected[i].v[3]; projected[i].v[1] /= projected[i].v[3]; projected[i].v[2] /= projected[i].v[3]; projected[i].v[3] = 1; } SDL_SetRenderDrawColor(renderer, 0x0, 0xFF, 0x0, 0xFF); for(int i = 0; i < sizeof(edges) / sizeof(*edges); i++){ float x1 = projected[edges[i].e[0]].v[0] * 128 + 256; float y1 = projected[edges[i].e[0]].v[1] * 128 + 256; float x2 = projected[edges[i].e[1]].v[0] * 128 + 256; float y2 = projected[edges[i].e[1]].v[1] * 128 + 256; SDL_RenderLine(renderer, x1, y1, x2, y2); } float size = 20; SDL_SetRenderDrawColor(renderer, 0xFF, 0x0, 0x0, 0xFF); for(int i = 0; i < sizeof(points) / sizeof(*points); i++){ SDL_FRect rect = { .x = projected[i].v[0] * 128 + 256 - size/2, .y = projected[i].v[1] * 128 + 256 - size/2, .w = size, .h = size, }; SDL_RenderFillRect(renderer, &rect); } roty += 0.01; SDL_RenderPresent(renderer); SDL_Delay(10); } return 0; }