From: matthew Date: Mon, 17 Sep 2018 18:48:18 +0000 (+0100) Subject: finally got cube rendering working X-Git-Url: https://git.owens.tech///git?a=commitdiff_plain;h=dc8da35ff80ca8c006af7ab49e8cdf8a1306f67a;p=csrpg.git finally got cube rendering working --- diff --git a/gl/cube.c b/gl/cube.c new file mode 100644 index 0000000..aa46a79 --- /dev/null +++ b/gl/cube.c @@ -0,0 +1,210 @@ +#include "cube.h" +#include +#include "shader.h" +#include "texture.h" +#include +#include +#include "err.h" + +/* +static float vertices[] = { + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, + + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, + + -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, + + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, + + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, + + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f +};*/ + +static GLfloat vertices[] = { + // X Y Z U V + // bottom + -1.0f,-1.0f,-1.0f, 0.0f, 0.0f, + 1.0f,-1.0f,-1.0f, 1.0f, 0.0f, + -1.0f,-1.0f, 1.0f, 0.0f, 1.0f, + 1.0f,-1.0f,-1.0f, 1.0f, 0.0f, + 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, 0.0f, 1.0f, + + // top + -1.0f, 1.0f,-1.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f,-1.0f, 1.0f, 0.0f, + 1.0f, 1.0f,-1.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + + // front + -1.0f,-1.0f, 1.0f, 1.0f, 0.0f, + 1.0f,-1.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, + -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + + // back + -1.0f,-1.0f,-1.0f, 0.0f, 0.0f, + -1.0f, 1.0f,-1.0f, 0.0f, 1.0f, + 1.0f,-1.0f,-1.0f, 1.0f, 0.0f, + 1.0f,-1.0f,-1.0f, 1.0f, 0.0f, + -1.0f, 1.0f,-1.0f, 0.0f, 1.0f, + 1.0f, 1.0f,-1.0f, 1.0f, 1.0f, + + // left + -1.0f,-1.0f, 1.0f, 0.0f, 1.0f, + -1.0f, 1.0f,-1.0f, 1.0f, 0.0f, + -1.0f,-1.0f,-1.0f, 0.0f, 0.0f, + -1.0f,-1.0f, 1.0f, 0.0f, 1.0f, + -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, 1.0f, 0.0f, + + // right + 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, + 1.0f,-1.0f,-1.0f, 1.0f, 0.0f, + 1.0f, 1.0f,-1.0f, 0.0f, 0.0f, + 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f,-1.0f, 0.0f, 0.0f, +}; + +static bool initilised = false; +static unsigned int vbo, vao, transformLoc; +static crpgShader *shader = NULL; +static crpgTexture *tex = NULL; + +typedef struct { + mat4_t transform, translation, rotation, scale; +}Cube_t; + +void initVertices() +{ + glGenBuffers(1, &vbo); + glGenVertexArrays(1, &vao); + + glBindVertexArray(vao); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + // position attribute + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + // texture attribute + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void*)(3*sizeof(float))); + glEnableVertexAttribArray(1); + glBindVertexArray(0); // unbinding vao +} + +void initShader(Cube_t *ct) +{ + // loading the textures + stbi_set_flip_vertically_on_load(true); + tex = crpgTextureNew("textures/container.jpg"); + shader = crpgShaderNew("crate"); + + crpgShaderUse(shader); + crpgShaderSetInt(shader, "texture0", 0); + transformLoc = glGetUniformLocation(crpgShaderID(shader), "transform"); + glUniformMatrix4fv(transformLoc, 1, GL_FALSE, &ct->transform); +} + +static bool printed = false; +void crpgCubeRender(crpgCube *c) +{ + crpgShaderUse(shader); + + Cube_t *ct = (Cube_t *)c; + ct->transform = ct->translation; + ct->transform = m4_mul(ct->transform, ct->scale); + ct->transform = m4_mul(ct->transform, ct->rotation); + glUniformMatrix4fv(transformLoc, 1, GL_FALSE, &ct->transform); + + if(!printed){ + printf("cube transform\n"); + m4_print(ct->transform); + printed = true; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, crpgTextureGetID(tex)); + + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); +} + +void crpgCubePosition(crpgCube *c, vec3_t pos) +{ + Cube_t *ct = (Cube_t *)c; + ct->translation = m4_translation(pos); + printf("cube position\n"); + m4_print(ct->translation); +} + +void crpgCubeScale(crpgCube *c, vec3_t scale) +{ + Cube_t *ct = (Cube_t *)c; + ct->scale = m4_scaling(scale); +} + +void crpgCubeRotation(crpgCube *c, float rot, vec3_t axis) +{ + Cube_t *ct = (Cube_t *)c; + ct->rotation = m4_rotation(rot, axis); +} + +crpgCube *crpgCubeNew() +{ + Cube_t *c = malloc(sizeof(Cube_t)); + crpgCube *ret = NULL; + + if(!initilised){ + initVertices(); + initShader(c); + } + + ret = (crpgCube *) c; + + // sane defaults so the transform doesn't calculate to a matrix of 0.f's + crpgCubePosition(ret, vec3(0.f, 0.f, 0.f)); + crpgCubeRotation(ret, 0.f, vec3(1.f, 1.f, 1.f)); + crpgCubeScale(ret, vec3(1.f, 1.f, 1.f)); + return ret; +} + +void crpgCubeFree(crpgCube *c) +{ + free(c); +} diff --git a/gl/cube.h b/gl/cube.h new file mode 100644 index 0000000..7ee7804 --- /dev/null +++ b/gl/cube.h @@ -0,0 +1,13 @@ +#ifndef CRPGCUBE_H +#define CRPGCUBE_H +#include "math_3d.h" + +typedef struct {} crpgCube; + +crpgCube *crpgCubeNew(); +void crpgCubePosition(crpgCube *c, vec3_t pos); +void crpgCubeScale(crpgCube *c, vec3_t scale); +void crpgCubeRotation(crpgCube *c, float rot, vec3_t axis); +void crpgCubeRender(crpgCube *c); +void crpgCubeFree(crpgCube *c); +#endif//CRPGCUBE_H diff --git a/gl/main.c b/gl/main.c index 6d5de9b..e67f0db 100644 --- a/gl/main.c +++ b/gl/main.c @@ -1,3 +1,4 @@ + #include #include #include @@ -6,7 +7,10 @@ #include "shader.h" #include "texture.h" #include "err.h" -#include +#include "stb/stb_image.h" +#include "math_3d.h" +#include "point.h" +#include "cube.h" static SDL_Window *window = NULL; static SDL_GLContext *context = NULL; @@ -32,7 +36,10 @@ static unsigned int ebo; static unsigned int vbo, vao; static float blendVal = 0.2f; static crpgShader *shader = NULL; -crpgTexture *tex[2]; +static crpgTexture *tex[2]; +static crpgCube *cube = NULL; +static mat4_t projection, transform, world_to_screen; +static vec3_t from, to, up, screenSpace, worldSpace; static void initShapes() { @@ -69,6 +76,30 @@ static void initShapes() crpgShaderSetInt(shader, "texture1", 0); crpgShaderSetInt(shader, "texture2", 1); crpgShaderSetFloat(shader, "blendVal", blendVal); + + transform = m4_translation(vec3(0.5, 0.5, 0.f)); + transform = m4_mul(transform, m4_scaling(vec3(.5f, .5f, 1.f))); + transform = m4_mul(transform, m4_rotation(m_deg_to_rad(45.f), vec3(0.f, 0.f, 1.f))); + + printf("wall transform\n"); + m4_print(transform); + unsigned int transformLoc = glGetUniformLocation(crpgShaderID(shader), "transform"); + glUniformMatrix4fv(transformLoc, 1, GL_FALSE, &transform); + + cube = crpgCubeNew(); + //crpgCubePosition(cube, vec3(0.5, 0.5, 0.f)); +} + +static void initView() +{ + projection = m4_perspective(60, (float)screen_width/(float)screen_height, 1, 10); + from = vec3(0, 0.5, 2); + to = vec3(0,0,0); + up = vec3(0,1,0); + transform = m4_look_at(from, to, up); + worldSpace = vec3(1, 1, -1); + world_to_screen = m4_mul(projection, transform); + screenSpace = m4_mul_pos(world_to_screen, worldSpace); } static bool init() @@ -146,6 +177,9 @@ static void render() glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBindVertexArray(vao); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + + crpgCubeRender(cube); // Not rendering?? SDL_GL_SwapWindow(window); } @@ -156,6 +190,7 @@ int main() return -1; initShapes(); + initView(); itime = SDL_GetTicks(); while(!quit){ @@ -164,5 +199,6 @@ int main() } SDL_Quit(); + crpgCubeFree(cube); return 0; } diff --git a/gl/math_3d.c b/gl/math_3d.c new file mode 100644 index 0000000..9e94a34 --- /dev/null +++ b/gl/math_3d.c @@ -0,0 +1,2 @@ +#define MATH_3D_IMPLEMENTATION +#include "math_3d.h" diff --git a/gl/math_3d.h b/gl/math_3d.h new file mode 100644 index 0000000..754603d --- /dev/null +++ b/gl/math_3d.h @@ -0,0 +1,632 @@ +/** + +Math 3D v1.0 +By Stephan Soller and Tobias Malmsheimer +Licensed under the MIT license + +Math 3D is a compact C99 library meant to be used with OpenGL. It provides basic +3D vector and 4x4 matrix operations as well as functions to create transformation +and projection matrices. The OpenGL binary layout is used so you can just upload +vectors and matrices into shaders and work with them without any conversions. + +It's an stb style single header file library. Define MATH_3D_IMPLEMENTATION +before you include this file in *one* C file to create the implementation. + + +QUICK NOTES + +- If not explicitly stated by a parameter name all angles are in radians. +- The matrices use column-major indices. This is the same as in OpenGL and GLSL. + The matrix documentation below for details. +- Matrices are passed by value. This is probably a bit inefficient but + simplifies code quite a bit. Most operations will be inlined by the compiler + anyway so the difference shouldn't matter that much. A matrix fits into 4 of + the 16 SSE2 registers anyway. If profiling shows significant slowdowns the + matrix type might change but ease of use is more important than every last + percent of performance. +- When combining matrices with multiplication the effects apply right to left. + This is the convention used in mathematics and OpenGL. Source: + https://en.wikipedia.org/wiki/Transformation_matrix#Composing_and_inverting_transformations + Direct3D does it differently. +- The `m4_mul_pos()` and `m4_mul_dir()` functions do a correct perspective + divide (division by w) when necessary. This is a bit slower but ensures that + the functions will properly work with projection matrices. If profiling shows + this is a bottleneck special functions without perspective division can be + added. But the normal multiplications should avoid any surprises. +- The library consistently uses a right-handed coordinate system. The old + `glOrtho()` broke that rule and `m4_ortho()` has be slightly modified so you + can always think of right-handed cubes that are projected into OpenGLs + normalized device coordinates. +- Special care has been taken to document all complex operations and important + sources. Most code is covered by test cases that have been manually calculated + and checked on the whiteboard. Since indices and math code is prone to be + confusing we used pair programming to avoid mistakes. + + +FURTHER IDEARS + +These are ideas for future work on the library. They're implemented as soon as +there is a proper use case and we can find good names for them. + +- bool v3_is_null(vec3_t v, float epsilon) + To check if the length of a vector is smaller than `epsilon`. +- vec3_t v3_length_default(vec3_t v, float default_length, float epsilon) + Returns `default_length` if the length of `v` is smaller than `epsilon`. + Otherwise same as `v3_length()`. +- vec3_t v3_norm_default(vec3_t v, vec3_t default_vector, float epsilon) + Returns `default_vector` if the length of `v` is smaller than `epsilon`. + Otherwise the same as `v3_norm()`. +- mat4_t m4_invert(mat4_t matrix) + Matrix inversion that works with arbitrary matrices. `m4_invert_affine()` can + already invert translation, rotation, scaling, mirroring, reflection and + shearing matrices. So a general inversion might only be useful to invert + projection matrices for picking. But with orthographic and perspective + projection it's probably simpler to calculate the ray into the scene directly + based on the screen coordinates. + + +VERSION HISTORY + +v1.0 2016-02-15 Initial release + +**/ + +#ifndef MATH_3D_HEADER +#define MATH_3D_HEADER + +#include +#include + + +// Define PI directly because we would need to define the _BSD_SOURCE or +// _XOPEN_SOURCE feature test macros to get it from math.h. That would be a +// rather harsh dependency. So we define it directly if necessary. +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + + +// +// 3D vectors +// +// Use the `vec3()` function to create vectors. All other vector functions start +// with the `v3_` prefix. +// +// The binary layout is the same as in GLSL and everything else (just 3 floats). +// So you can just upload the vectors into shaders as they are. +// + +typedef struct { float x, y, z; } vec3_t; +static inline vec3_t vec3(float x, float y, float z) { return (vec3_t){ x, y, z }; } + +static inline vec3_t v3_add (vec3_t a, vec3_t b) { return (vec3_t){ a.x + b.x, a.y + b.y, a.z + b.z }; } +static inline vec3_t v3_adds (vec3_t a, float s) { return (vec3_t){ a.x + s, a.y + s, a.z + s }; } +static inline vec3_t v3_sub (vec3_t a, vec3_t b) { return (vec3_t){ a.x - b.x, a.y - b.y, a.z - b.z }; } +static inline vec3_t v3_subs (vec3_t a, float s) { return (vec3_t){ a.x - s, a.y - s, a.z - s }; } +static inline vec3_t v3_mul (vec3_t a, vec3_t b) { return (vec3_t){ a.x * b.x, a.y * b.y, a.z * b.z }; } +static inline vec3_t v3_muls (vec3_t a, float s) { return (vec3_t){ a.x * s, a.y * s, a.z * s }; } +static inline vec3_t v3_div (vec3_t a, vec3_t b) { return (vec3_t){ a.x / b.x, a.y / b.y, a.z / b.z }; } +static inline vec3_t v3_divs (vec3_t a, float s) { return (vec3_t){ a.x / s, a.y / s, a.z / s }; } +static inline float v3_length(vec3_t v) { return sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); } +static inline vec3_t v3_norm (vec3_t v); +static inline float v3_dot (vec3_t a, vec3_t b) { return a.x*b.x + a.y*b.y + a.z*b.z; } +static inline vec3_t v3_proj (vec3_t v, vec3_t onto); +static inline vec3_t v3_cross (vec3_t a, vec3_t b); +static inline float v3_angle_between(vec3_t a, vec3_t b); + + +// +// 4x4 matrices +// +// Use the `mat4()` function to create a matrix. You can write the matrix +// members in the same way as you would write them on paper or on a whiteboard: +// +// mat4_t m = mat4( +// 1, 0, 0, 7, +// 0, 1, 0, 5, +// 0, 0, 1, 3, +// 0, 0, 0, 1 +// ) +// +// This creates a matrix that translates points by vec3(7, 5, 3). All other +// matrix functions start with the `m4_` prefix. Among them functions to create +// identity, translation, rotation, scaling and projection matrices. +// +// The matrix is stored in column-major order, just as OpenGL expects. Members +// can be accessed by indices or member names. When you write a matrix on paper +// or on the whiteboard the indices and named members correspond to these +// positions: +// +// | m[0][0] m[1][0] m[2][0] m[3][0] | +// | m[0][1] m[1][1] m[2][1] m[3][1] | +// | m[0][2] m[1][2] m[2][2] m[3][2] | +// | m[0][3] m[1][3] m[2][3] m[3][3] | +// +// | m00 m10 m20 m30 | +// | m01 m11 m21 m31 | +// | m02 m12 m22 m32 | +// | m03 m13 m23 m33 | +// +// The first index or number in a name denotes the column, the second the row. +// So m[i][j] denotes the member in the ith column and the jth row. This is the +// same as in GLSL (source: GLSL v1.3 specification, 5.6 Matrix Components). +// + +typedef union { + // The first index is the column index, the second the row index. The memory + // layout of nested arrays in C matches the memory layout expected by OpenGL. + float m[4][4]; + // OpenGL expects the first 4 floats to be the first column of the matrix. + // So we need to define the named members column by column for the names to + // match the memory locations of the array elements. + struct { + float m00, m01, m02, m03; + float m10, m11, m12, m13; + float m20, m21, m22, m23; + float m30, m31, m32, m33; + }; +} mat4_t; + +static inline mat4_t mat4( + float m00, float m10, float m20, float m30, + float m01, float m11, float m21, float m31, + float m02, float m12, float m22, float m32, + float m03, float m13, float m23, float m33 +); + +static inline float m_deg_to_rad(float deg) +{ + return deg * (M_PI/180.f); +}; + +static inline mat4_t m4_identity (); +static inline mat4_t m4_translation (vec3_t offset); +static inline mat4_t m4_scaling (vec3_t scale); +static inline mat4_t m4_rotation_x (float angle_in_rad); +static inline mat4_t m4_rotation_y (float angle_in_rad); +static inline mat4_t m4_rotation_z (float angle_in_rad); + mat4_t m4_rotation (float angle_in_rad, vec3_t axis); + + mat4_t m4_ortho (float left, float right, float bottom, float top, float back, float front); + mat4_t m4_perspective (float vertical_field_of_view_in_deg, float aspect_ratio, float near_view_distance, float far_view_distance); + mat4_t m4_look_at (vec3_t from, vec3_t to, vec3_t up); + +static inline mat4_t m4_transpose (mat4_t matrix); +static inline mat4_t m4_mul (mat4_t a, mat4_t b); + mat4_t m4_invert_affine(mat4_t matrix); + vec3_t m4_mul_pos (mat4_t matrix, vec3_t position); + vec3_t m4_mul_dir (mat4_t matrix, vec3_t direction); + + void m4_print (mat4_t matrix); + void m4_printp (mat4_t matrix, int width, int precision); + void m4_fprint (FILE* stream, mat4_t matrix); + void m4_fprintp (FILE* stream, mat4_t matrix, int width, int precision); + + + +// +// 3D vector functions header implementation +// + +static inline vec3_t v3_norm(vec3_t v) { + float len = v3_length(v); + if (len > 0) + return (vec3_t){ v.x / len, v.y / len, v.z / len }; + else + return (vec3_t){ 0, 0, 0}; +} + +static inline vec3_t v3_proj(vec3_t v, vec3_t onto) { + return v3_muls(onto, v3_dot(v, onto) / v3_dot(onto, onto)); +} + +static inline vec3_t v3_cross(vec3_t a, vec3_t b) { + return (vec3_t){ + a.y * b.z - a.z * b.y, + a.z * b.x - a.x * b.z, + a.x * b.y - a.y * b.x + }; +} + +static inline float v3_angle_between(vec3_t a, vec3_t b) { + return acosf( v3_dot(a, b) / (v3_length(a) * v3_length(b)) ); +} + + +// +// Matrix functions header implementation +// + +static inline mat4_t mat4( + float m00, float m10, float m20, float m30, + float m01, float m11, float m21, float m31, + float m02, float m12, float m22, float m32, + float m03, float m13, float m23, float m33 +) { + return (mat4_t){ + .m[0][0] = m00, .m[1][0] = m10, .m[2][0] = m20, .m[3][0] = m30, + .m[0][1] = m01, .m[1][1] = m11, .m[2][1] = m21, .m[3][1] = m31, + .m[0][2] = m02, .m[1][2] = m12, .m[2][2] = m22, .m[3][2] = m32, + .m[0][3] = m03, .m[1][3] = m13, .m[2][3] = m23, .m[3][3] = m33 + }; +} + +static inline mat4_t m4_identity() { + return mat4( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ); +} + +static inline mat4_t m4_translation(vec3_t offset) { + return mat4( + 1, 0, 0, offset.x, + 0, 1, 0, offset.y, + 0, 0, 1, offset.z, + 0, 0, 0, 1 + ); +} + +static inline mat4_t m4_scaling(vec3_t scale) { + float x = scale.x, y = scale.y, z = scale.z; + return mat4( + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 + ); +} + +static inline mat4_t m4_rotation_x(float angle_in_rad) { + float s = sinf(angle_in_rad), c = cosf(angle_in_rad); + return mat4( + 1, 0, 0, 0, + 0, c, -s, 0, + 0, s, c, 0, + 0, 0, 0, 1 + ); +} + +static inline mat4_t m4_rotation_y(float angle_in_rad) { + float s = sinf(angle_in_rad), c = cosf(angle_in_rad); + return mat4( + c, 0, s, 0, + 0, 1, 0, 0, + -s, 0, c, 0, + 0, 0, 0, 1 + ); +} + +static inline mat4_t m4_rotation_z(float angle_in_rad) { + float s = sinf(angle_in_rad), c = cosf(angle_in_rad); + return mat4( + c, -s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ); +} + +static inline mat4_t m4_transpose(mat4_t matrix) { + return mat4( + matrix.m00, matrix.m01, matrix.m02, matrix.m03, + matrix.m10, matrix.m11, matrix.m12, matrix.m13, + matrix.m20, matrix.m21, matrix.m22, matrix.m23, + matrix.m30, matrix.m31, matrix.m32, matrix.m33 + ); +} + +/** + * Multiplication of two 4x4 matrices. + * + * Implemented by following the row times column rule and illustrating it on a + * whiteboard with the proper indices in mind. + * + * Further reading: https://en.wikipedia.org/wiki/Matrix_multiplication + * But note that the article use the first index for rows and the second for + * columns. + */ +static inline mat4_t m4_mul(mat4_t a, mat4_t b) { + mat4_t result; + + for(int i = 0; i < 4; i++) { + for(int j = 0; j < 4; j++) { + float sum = 0; + for(int k = 0; k < 4; k++) { + sum += a.m[k][j] * b.m[i][k]; + } + result.m[i][j] = sum; + } + } + + return result; +} + +#endif // MATH_3D_HEADER + + +#ifdef MATH_3D_IMPLEMENTATION + +/** + * Creates a matrix to rotate around an axis by a given angle. The axis doesn't + * need to be normalized. + * + * Sources: + * + * https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle + */ +mat4_t m4_rotation(float angle_in_rad, vec3_t axis) { + vec3_t normalized_axis = v3_norm(axis); + float x = normalized_axis.x, y = normalized_axis.y, z = normalized_axis.z; + float c = cosf(angle_in_rad), s = sinf(angle_in_rad); + + return mat4( + c + x*x*(1-c), x*y*(1-c) - z*s, x*z*(1-c) + y*s, 0, + y*x*(1-c) + z*s, c + y*y*(1-c), y*z*(1-c) - x*s, 0, + z*x*(1-c) - y*s, z*y*(1-c) + x*s, c + z*z*(1-c), 0, + 0, 0, 0, 1 + ); +} + + +/** + * Creates an orthographic projection matrix. It maps the right handed cube + * defined by left, right, bottom, top, back and front onto the screen and + * z-buffer. You can think of it as a cube you move through world or camera + * space and everything inside is visible. + * + * This is slightly different from the traditional glOrtho() and from the linked + * sources. These functions require the user to negate the last two arguments + * (creating a left-handed coordinate system). We avoid that here so you can + * think of this function as moving a right-handed cube through world space. + * + * The arguments are ordered in a way that for each axis you specify the minimum + * followed by the maximum. Thats why it's bottom to top and back to front. + * + * Implementation details: + * + * To be more exact the right-handed cube is mapped into normalized device + * coordinates, a left-handed cube where (-1 -1) is the lower left corner, + * (1, 1) the upper right corner and a z-value of -1 is the nearest point and + * 1 the furthest point. OpenGL takes it from there and puts it on the screen + * and into the z-buffer. + * + * Sources: + * + * https://msdn.microsoft.com/en-us/library/windows/desktop/dd373965(v=vs.85).aspx + * https://unspecified.wordpress.com/2012/06/21/calculating-the-gluperspective-matrix-and-other-opengl-matrix-maths/ + */ +mat4_t m4_ortho(float left, float right, float bottom, float top, float back, float front) { + float l = left, r = right, b = bottom, t = top, n = front, f = back; + float tx = -(r + l) / (r - l); + float ty = -(t + b) / (t - b); + float tz = -(f + n) / (f - n); + return mat4( + 2 / (r - l), 0, 0, tx, + 0, 2 / (t - b), 0, ty, + 0, 0, 2 / (f - n), tz, + 0, 0, 0, 1 + ); +} + +/** + * Creates a perspective projection matrix for a camera. + * + * The camera is at the origin and looks in the direction of the negative Z axis. + * `near_view_distance` and `far_view_distance` have to be positive and > 0. + * They are distances from the camera eye, not values on an axis. + * + * `near_view_distance` can be small but not 0. 0 breaks the projection and + * everything ends up at the max value (far end) of the z-buffer. Making the + * z-buffer useless. + * + * The matrix is the same as `gluPerspective()` builds. The view distance is + * mapped to the z-buffer with a reciprocal function (1/x). Therefore the z-buffer + * resolution for near objects is very good while resolution for far objects is + * limited. + * + * Sources: + * + * https://unspecified.wordpress.com/2012/06/21/calculating-the-gluperspective-matrix-and-other-opengl-matrix-maths/ + */ +mat4_t m4_perspective(float vertical_field_of_view_in_deg, float aspect_ratio, float near_view_distance, float far_view_distance) { + float fovy_in_rad = vertical_field_of_view_in_deg / 180 * M_PI; + float f = 1.0f / tanf(fovy_in_rad / 2.0f); + float ar = aspect_ratio; + float nd = near_view_distance, fd = far_view_distance; + + return mat4( + f / ar, 0, 0, 0, + 0, f, 0, 0, + 0, 0, (fd+nd)/(nd-fd), (2*fd*nd)/(nd-fd), + 0, 0, -1, 0 + ); +} + +/** + * Builds a transformation matrix for a camera that looks from `from` towards + * `to`. `up` defines the direction that's upwards for the camera. All three + * vectors are given in world space and `up` doesn't need to be normalized. + * + * Sources: Derived on whiteboard. + * + * Implementation details: + * + * x, y and z are the right-handed base vectors of the cameras subspace. + * x has to be normalized because the cross product only produces a normalized + * output vector if both input vectors are orthogonal to each other. And up + * probably isn't orthogonal to z. + * + * These vectors are then used to build a 3x3 rotation matrix. This matrix + * rotates a vector by the same amount the camera is rotated. But instead we + * need to rotate all incoming vertices backwards by that amount. That's what a + * camera matrix is for: To move the world so that the camera is in the origin. + * So we take the inverse of that rotation matrix and in case of an rotation + * matrix this is just the transposed matrix. That's why the 3x3 part of the + * matrix are the x, y and z vectors but written horizontally instead of + * vertically. + * + * The translation is derived by creating a translation matrix to move the world + * into the origin (thats translate by minus `from`). The complete lookat matrix + * is then this translation followed by the rotation. Written as matrix + * multiplication: + * + * lookat = rotation * translation + * + * Since we're right-handed this equals to first doing the translation and after + * that doing the rotation. During that multiplication the rotation 3x3 part + * doesn't change but the translation vector is multiplied with each rotation + * axis. The dot product is just a more compact way to write the actual + * multiplications. + */ +mat4_t m4_look_at(vec3_t from, vec3_t to, vec3_t up) { + vec3_t z = v3_muls(v3_norm(v3_sub(to, from)), -1); + vec3_t x = v3_norm(v3_cross(up, z)); + vec3_t y = v3_cross(z, x); + + return mat4( + x.x, x.y, x.z, -v3_dot(from, x), + y.x, y.y, y.z, -v3_dot(from, y), + z.x, z.y, z.z, -v3_dot(from, z), + 0, 0, 0, 1 + ); +} + + +/** + * Inverts an affine transformation matrix. That are translation, scaling, + * mirroring, reflection, rotation and shearing matrices or any combination of + * them. + * + * Implementation details: + * + * - Invert the 3x3 part of the 4x4 matrix to handle rotation, scaling, etc. + * correctly (see source). + * - Invert the translation part of the 4x4 matrix by multiplying it with the + * inverted rotation matrix and negating it. + * + * When a 3D point is multiplied with a transformation matrix it is first + * rotated and then translated. The inverted transformation matrix is the + * inverse translation followed by the inverse rotation. Written as a matrix + * multiplication (remember, the effect applies right to left): + * + * inv(matrix) = inv(rotation) * inv(translation) + * + * The inverse translation is a translation into the opposite direction, just + * the negative translation. The rotation part isn't changed by that + * multiplication but the translation part is multiplied by the inverse rotation + * matrix. It's the same situation as with `m4_look_at()`. But since we don't + * store the rotation matrix as 3D vectors we can't use the dot product and have + * to write the matrix multiplication operations by hand. + * + * Sources for 3x3 matrix inversion: + * + * https://www.khanacademy.org/math/precalculus/precalc-matrices/determinants-and-inverses-of-large-matrices/v/inverting-3x3-part-2-determinant-and-adjugate-of-a-matrix + */ +mat4_t m4_invert_affine(mat4_t matrix) { + // Create shorthands to access matrix members + float m00 = matrix.m00, m10 = matrix.m10, m20 = matrix.m20, m30 = matrix.m30; + float m01 = matrix.m01, m11 = matrix.m11, m21 = matrix.m21, m31 = matrix.m31; + float m02 = matrix.m02, m12 = matrix.m12, m22 = matrix.m22, m32 = matrix.m32; + + // Invert 3x3 part of the 4x4 matrix that contains the rotation, etc. + // That part is called R from here on. + + // Calculate cofactor matrix of R + float c00 = m11*m22 - m12*m21, c10 = -(m01*m22 - m02*m21), c20 = m01*m12 - m02*m11; + float c01 = -(m10*m22 - m12*m20), c11 = m00*m22 - m02*m20, c21 = -(m00*m12 - m02*m10); + float c02 = m10*m21 - m11*m20, c12 = -(m00*m21 - m01*m20), c22 = m00*m11 - m01*m10; + + // Caclculate the determinant by using the already calculated determinants + // in the cofactor matrix. + // Second sign is already minus from the cofactor matrix. + float det = m00*c00 + m10*c10 + m20 * c20; + if (fabsf(det) < 0.00001) + return m4_identity(); + + // Calcuate inverse of R by dividing the transposed cofactor matrix by the + // determinant. + float i00 = c00 / det, i10 = c01 / det, i20 = c02 / det; + float i01 = c10 / det, i11 = c11 / det, i21 = c12 / det; + float i02 = c20 / det, i12 = c21 / det, i22 = c22 / det; + + // Combine the inverted R with the inverted translation + return mat4( + i00, i10, i20, -(i00*m30 + i10*m31 + i20*m32), + i01, i11, i21, -(i01*m30 + i11*m31 + i21*m32), + i02, i12, i22, -(i02*m30 + i12*m31 + i22*m32), + 0, 0, 0, 1 + ); +} + +/** + * Multiplies a 4x4 matrix with a 3D vector representing a point in 3D space. + * + * Before the matrix multiplication the vector is first expanded to a 4D vector + * (x, y, z, 1). After the multiplication the vector is reduced to 3D again by + * dividing through the 4th component (if it's not 0 or 1). + */ +vec3_t m4_mul_pos(mat4_t matrix, vec3_t position) { + vec3_t result = vec3( + matrix.m00 * position.x + matrix.m10 * position.y + matrix.m20 * position.z + matrix.m30, + matrix.m01 * position.x + matrix.m11 * position.y + matrix.m21 * position.z + matrix.m31, + matrix.m02 * position.x + matrix.m12 * position.y + matrix.m22 * position.z + matrix.m32 + ); + + float w = matrix.m03 * position.x + matrix.m13 * position.y + matrix.m23 * position.z + matrix.m33; + if (w != 0 && w != 1) + return vec3(result.x / w, result.y / w, result.z / w); + + return result; +} + +/** + * Multiplies a 4x4 matrix with a 3D vector representing a direction in 3D space. + * + * Before the matrix multiplication the vector is first expanded to a 4D vector + * (x, y, z, 0). For directions the 4th component is set to 0 because directions + * are only rotated, not translated. After the multiplication the vector is + * reduced to 3D again by dividing through the 4th component (if it's not 0 or + * 1). This is necessary because the matrix might contains something other than + * (0, 0, 0, 1) in the bottom row which might set w to something other than 0 + * or 1. + */ +vec3_t m4_mul_dir(mat4_t matrix, vec3_t direction) { + vec3_t result = vec3( + matrix.m00 * direction.x + matrix.m10 * direction.y + matrix.m20 * direction.z, + matrix.m01 * direction.x + matrix.m11 * direction.y + matrix.m21 * direction.z, + matrix.m02 * direction.x + matrix.m12 * direction.y + matrix.m22 * direction.z + ); + + float w = matrix.m03 * direction.x + matrix.m13 * direction.y + matrix.m23 * direction.z; + if (w != 0 && w != 1) + return vec3(result.x / w, result.y / w, result.z / w); + + return result; +} + +void m4_print(mat4_t matrix) { + m4_fprintp(stdout, matrix, 6, 2); +} + +void m4_printp(mat4_t matrix, int width, int precision) { + m4_fprintp(stdout, matrix, width, precision); +} + +void m4_fprint(FILE* stream, mat4_t matrix) { + m4_fprintp(stream, matrix, 6, 2); +} + +void m4_fprintp(FILE* stream, mat4_t matrix, int width, int precision) { + mat4_t m = matrix; + int w = width, p = precision; + for(int r = 0; r < 4; r++) { + fprintf(stream, "| %*.*f %*.*f %*.*f %*.*f |\n", + w, p, m.m[0][r], w, p, m.m[1][r], w, p, m.m[2][r], w, p, m.m[3][r] + ); + } +} + +#endif // MATH_3D_IMPLEMENTATION diff --git a/gl/shader.c b/gl/shader.c index cfcf4ce..d3c8aa5 100644 --- a/gl/shader.c +++ b/gl/shader.c @@ -6,7 +6,7 @@ #include "fileRead.h" #include "err.h" typedef struct { - int id; + unsigned int id; } Shader_t; static char *fileEnds[] = { @@ -147,3 +147,9 @@ void crpgShaderSetFloat(crpgShader *s, const char *name, float val) Shader_t *st = (Shader_t *)s; glUniform1f(glGetUniformLocation(st->id, name), val); } + +unsigned int crpgShaderID(crpgShader *s) +{ + Shader_t *st = (Shader_t *)s; + return st->id; +} diff --git a/gl/shader.h b/gl/shader.h index e98573b..80917f1 100644 --- a/gl/shader.h +++ b/gl/shader.h @@ -20,4 +20,5 @@ void crpgShaderSetBool(crpgShader *s, const char *name, bool val); void crpgShaderSetInt(crpgShader *s, const char *name, int val); void crpgShaderSetFloat(crpgShader *s, const char *name, float val); void crpgShaderFree(crpgShader *s); +unsigned int crpgShaderID(crpgShader *s); #endif//SHADER_H diff --git a/shaders/crate/crate.frag b/shaders/crate/crate.frag new file mode 100644 index 0000000..7633254 --- /dev/null +++ b/shaders/crate/crate.frag @@ -0,0 +1,10 @@ +#version 330 core +out vec4 fragColor; +in vec2 vTex; + +uniform sampler2D texture0; + +void main() +{ + fragColor = texture(texture0, vTex); +} diff --git a/shaders/crate/crate.vert b/shaders/crate/crate.vert new file mode 100644 index 0000000..ea04ea3 --- /dev/null +++ b/shaders/crate/crate.vert @@ -0,0 +1,12 @@ +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec2 aTex; + +out vec2 vTex; +uniform mat4 transform; + +void main() +{ + gl_Position = transform * vec4(aPos, 1.0); + vTex = aTex; +} diff --git a/shaders/tri/tri.vert b/shaders/tri/tri.vert index 7cd2afb..a3defca 100644 --- a/shaders/tri/tri.vert +++ b/shaders/tri/tri.vert @@ -5,10 +5,11 @@ layout (location = 2) in vec2 aTex; out vec3 vCol; out vec2 vTex; +uniform mat4 transform; void main() { - gl_Position = vec4(aPos, 1.0); + gl_Position = transform * vec4(aPos, 1.0); vCol = aCol; vTex = aTex; } diff --git a/textures/container.jpg b/textures/container.jpg new file mode 100644 index 0000000..d07bee4 Binary files /dev/null and b/textures/container.jpg differ