#ifndef COMMON_H #define COMMON_H #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define TINYOBJ_LOADER_C_IMPLEMENTATION #include "tinyobj_loader_c.h" #include #include #include #include U8 * read_entire_file(const char *filename) { U8 *result; FILE *f; long file_size; result = 0; if (!filename) { return(result); } f = fopen(filename, "rb"); if (!f) { return(result); } fseek(f, 0, SEEK_END); file_size = ftell(f); fseek(f, 0, SEEK_SET); result = malloc(file_size + 1); fread(result, file_size, 1, f); fclose(f); result[file_size] = 0; return(result); } void * mmap_file(size_t *len, const char *filename) { struct stat sb; char* p; int fd; fd = open(filename, O_RDONLY); if (fd == -1) { perror("open"); return NULL; } if (fstat(fd, &sb) == -1) { perror("fstat"); return NULL; } if (!S_ISREG(sb.st_mode)) { fprintf(stderr, "%s is not a file\n", filename); return NULL; } p = (char*)mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { perror("mmap"); return NULL; } if (close(fd) == -1) { perror("close"); return NULL; } (*len) = sb.st_size; return p; } void read_entire_file_mmap(void* ctx, const char* filename, const int is_mtl, const char* obj_filename, char** data, size_t* len) { if (!filename) { fprintf(stderr, "[ERROR]: Filename not provided (null)\n"); *data = 0; *len = 0; return; } size_t data_len = 0; *data = mmap_file(&data_len, filename); *len = data_len; } Mesh * mesh_load_obj(Arena *arena, const char *filename) { tinyobj_attrib_t attrib; tinyobj_shape_t *shapes = 0; tinyobj_material_t *materials = 0; size_t num_shapes, num_materials, num_triangles; size_t i, j, face_offset; U32 flags; Mesh *mesh = 0; flags = TINYOBJ_FLAG_TRIANGULATE; S32 status = tinyobj_parse_obj(&attrib, &shapes, &num_shapes, &materials, &num_materials, filename, read_entire_file_mmap, 0, flags); if (status != TINYOBJ_SUCCESS) { fprintf(stderr, "[ERROR]: Failed to parse \"%s\"\n", filename); if (status == TINYOBJ_ERROR_INVALID_PARAMETER) fprintf(stderr, "[ERROR]: TINYOBJ_ERROR_INVALID_PARAMETER\n"); return(mesh); } num_triangles = attrib.num_face_num_verts; face_offset = 0; Vertex *vertices = arena_push_size(arena, sizeof(Vertex)*num_triangles*3); U32 *indices = arena_push_size(arena, num_triangles*3*sizeof(U32)); U32 vertex_count = 0; U32 index_count = 0, index_index = 0; for (i = 0; i < attrib.num_face_num_verts; ++i) { tinyobj_vertex_index_t idx; V3F pos, normal; V2F tex_coords; Assert(attrib.face_num_verts[i]%3 == 0); Assert(attrib.face_num_verts[i]/3 > 3); Assert(attrib.num_texcoords); for (j = 0; j < 3; ++j) { idx = attrib.faces[face_offset+j]; Assert(idx.v_idx >= 0); pos = v3f(attrib.vertices[3*idx.v_idx+0], attrib.vertices[3*idx.v_idx+1], attrib.vertices[3*idx.v_idx+2]); Assert(idx.vn_idx < (S32)attrib.num_normals); normal = v3f(attrib.normals[3*idx.vn_idx+0], attrib.normals[3*idx.vn_idx+1], attrib.normals[3*idx.vn_idx+2]); Assert(idx.vt_idx < (S32)attrib.num_texcoords); tex_coords = v2f(attrib.texcoords[2*idx.vt_idx+0], attrib.texcoords[2*idx.vt_idx+1]); vertices[vertex_count++] = vertex(pos, normal, tex_coords); Assert(index_count < attrib.num_faclibe_num_verts); indices[index_index++] = index_count++; } face_offset += 3; } mesh = mesh_init(arena, vertices, vertex_count, indices, num_triangles*3); tinyobj_attrib_free(&attrib); tinyobj_shapes_free(shapes, num_shapes); tinyobj_materials_free(materials, num_materials); return(mesh); } U32 compile_shader(GLenum type, const char *filename) { U32 shader; S32 status; char logs[512]; const char *source = (const char *)read_entire_file(filename); if (!source) { fprintf(stderr, "[ERROR]: Failed to read the file \"%s\"\n", filename); return(0); } shader = glCreateShader(type); glShaderSource(shader, 1, &source, 0); free((void *)source); glCompileShader(shader); glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { glGetShaderInfoLog(shader, 512, 0, logs); fprintf(stderr, "[ERROR]: Failed to compile: \"%s\"\n%s", filename, logs); } else { fprintf(stdout, "[INFO]: \"%s\" compiled successfuly.\n", filename); } return(shader); } U32 create_shader_program(char *vertex_shader_filename, char *fragment_shader_filename) { S32 success; char logs[512]; U32 vertex_shader = compile_shader(GL_VERTEX_SHADER, vertex_shader_filename); U32 fragment_shader = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_filename); U32 shader_program; shader_program = glCreateProgram(); glAttachShader(shader_program, vertex_shader); glAttachShader(shader_program, fragment_shader); glLinkProgram(shader_program); glGetProgramiv(shader_program, GL_LINK_STATUS, &success); if (success == GL_FALSE) { glGetProgramInfoLog(shader_program, 512, 0, logs); fprintf(stderr, "[ERROR]: Failed to link shader program:\n%s", logs); } else { fprintf(stdout, "[INFO]: Shader program linked successfuly.\n\n"); } glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); return(shader_program); } U32 create_shader_program_geom(char *vertex_shader_filename, char *fragment_shader_filename, char *geometry_shader_filename) { S32 success; char logs[512]; U32 vertex_shader = compile_shader(GL_VERTEX_SHADER, vertex_shader_filename); U32 fragment_shader = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_filename); U32 geometry_shader = compile_shader(GL_GEOMETRY_SHADER, geometry_shader_filename); U32 shader_program; shader_program = glCreateProgram(); glAttachShader(shader_program, vertex_shader); glAttachShader(shader_program, fragment_shader); glAttachShader(shader_program, geometry_shader); glLinkProgram(shader_program); glGetProgramiv(shader_program, GL_LINK_STATUS, &success); if (success == GL_FALSE) { glGetProgramInfoLog(shader_program, 512, 0, logs); fprintf(stderr, "[ERROR]: Failed to link shader program:\n%s", logs); } else { fprintf(stdout, "[INFO]: Shader program linked successfuly.\n\n"); } glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); glDeleteShader(geometry_shader); return(shader_program); } void shader_set_3f(U32 shader_program, char *uniform_name, F32 x, F32 y, F32 z) { U32 uniform_location = glGetUniformLocation(shader_program, uniform_name); glUniform3f(uniform_location, x, y, z); } void shader_set_1f(U32 shader_program, char *uniform_name, F32 value) { U32 uniform_location = glGetUniformLocation(shader_program, uniform_name); glUniform1f(uniform_location, value); } void shader_set_3fv(U32 shader_program, char *uniform_name, V3F value) { U32 uniform_location = glGetUniformLocation(shader_program, uniform_name); glUniform3fv(uniform_location, 1, (const GLfloat *)&value); } void shader_set_2fv(U32 shader_program, char *uniform_name, V2F value) { U32 uniform_location = glGetUniformLocation(shader_program, uniform_name); glUniform2fv(uniform_location, 1, (const GLfloat *)&value); } void shader_set_mat4fv(U32 shader_program, char *uniform_name, MAT4 value) { U32 uniform_location = glGetUniformLocation(shader_program, uniform_name); glUniformMatrix4fv(uniform_location, 1, GL_FALSE, (F32 *)&value); } void shader_set_1i(U32 shader_program, char *uniform_name, S32 value) { U32 uniform_location = glGetUniformLocation(shader_program, uniform_name); glUniform1i(uniform_location, value); } U32 load_texture(char *texture_filename) { S32 width, height, number_channels; U32 texture_id; glGenTextures(1, &texture_id); stbi_set_flip_vertically_on_load(1); U8 *data = stbi_load(texture_filename, &width, &height, &number_channels, 0); if (data) { GLenum format = 0; if (number_channels == 1) format = GL_RED; else if (number_channels == 3) format = GL_RGB; else if (number_channels == 4) format = GL_RGBA; glBindTexture(GL_TEXTURE_2D, texture_id); glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); fprintf(stdout, "[INFO]: Texture (\"%s\") is loaded successfully\n", texture_filename); } else { fprintf(stderr, "[ERROR]: Failed to load texture: \"%s\"\n", texture_filename); } stbi_image_free(data); return(texture_id); } U32 load_cubemap(const char *texture_filenames[6]) { U32 texture_id; glGenTextures(1, &texture_id); glBindTexture(GL_TEXTURE_CUBE_MAP, texture_id); S32 width, height, number_channels; U8 *data = 0; stbi_set_flip_vertically_on_load(0); for (U32 texture_index = 0; texture_index < 6; ++texture_index) { data = stbi_load(texture_filenames[texture_index], &width, &height, &number_channels, 0); if (data) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+texture_index, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); fprintf(stdout, "[INFO]: Texture (\"%s\") is loaded successfully\n", texture_filenames[texture_index]); } else { fprintf(stderr, "[ERROR]: Failed to load texture: \"%s\"\n", texture_filenames[texture_index]); } stbi_image_free(data); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); return(texture_id); } typedef struct { V3F translate; V3F scale; V3F rotate; } Transform; enum KeyState_Enum { KeyState_RELEASE = 0, KeyState_PRESS = 1 }; typedef struct { enum KeyState_Enum last; enum KeyState_Enum state; } Key; typedef struct { Key move_right; Key move_forward; Key move_left; Key move_backward; Key move_up; Key move_down; Key jump; Key action_right; Key action_up; Key action_left; Key action_down; Key exit; V2F last_mouse_pos; V2F mouse_offset; } Input; void input_update_last_state(Input *input) { input->move_right.last = input->move_right.state; input->move_forward.last = input->move_forward.state; input->move_left.last = input->move_left.state; input->move_backward.last = input->move_backward.state; input->move_up.last = input->move_up.state; input->move_down.last = input->move_down.state; input->jump.last = input->jump.state; input->action_right.last = input->action_right.state; input->action_up.last = input->action_up.state; input->action_left.last = input->action_left.state; input->action_down.last = input->action_down.state; input->exit.last = input->exit.state; } B32 key_is_pressed(Key key) { B32 result = (key.state == KeyState_PRESS); return(result); } B32 key_first_press(Key key) { B32 result = ((key.last == KeyState_RELEASE) && (key.state == KeyState_PRESS)); return(result); } B32 key_was_pressed(Key key) { B32 result = ((key.last == KeyState_PRESS) && (key.state == KeyState_RELEASE)); return(result); } MAT4 mat4_change_basis(V3F x, V3F y, V3F z) { MAT4 result = mat4_identity(); result.m0 = v4f(x.x, x.y, x.z, 0.0f); result.m1 = v4f(y.x, y.y, y.z, 0.0f); result.m2 = v4f(z.x, z.y, z.z, 0.0f); return(result); } /* * NOTE(pryazha): angles in degrees * | 1 0 0 | | cy 0 sy | | cz -sz 0 | | cy*cz -cy*sz sy | * | 0 cx -sx |*| 0 1 0 |*| sz cz 0 |=| sx*sy*cz+cx*sz -sx*sy*sz+cx*cz -sx*cy | * | 0 sx cx | | -sy 0 cy | | 0 0 1 | | -cx*sy*cz+sx*sz cx*sy*sz+sx*cz cx*cy | */ MAT4 mat4_make_rotate(V3F angles) { F32 angle, cx, sx, cy, sy, cz, sz; MAT4 result; V3F newx, newy, newz; angle = DEG2RAD*angles.x; cx = f32_cos(angle); sx = f32_sin(angle); angle = DEG2RAD*angles.y; cy = f32_cos(angle); sy = f32_sin(angle); angle = DEG2RAD*angles.z; cz = f32_cos(angle); sz = f32_sin(angle); newx = v3f(cy*cz, sx*sy*cz+cx*sz, -cx*sy*cz+sx*sz); newy = v3f(-cy*sz, -sx*sy*sz+cx*cz, cx*sy*sz+sx*cz); newz = v3f(sy, -sx*cy, cx*cy); result = mat4_change_basis(newx, newy, newz); return(result); } MAT4 mat4_rotate_angles(MAT4 source, V3F angles) { MAT4 rotate = mat4_make_rotate(angles); MAT4 result = mat4_mul(rotate, source); return(result); } Transform transform_make(V3F translate, V3F scale, V3F rotate) { Transform result; result.translate = translate; result.scale = scale; result.rotate = rotate; return(result); } Transform transform_default() { Transform result = transform_make(v3f_zero(), v3f_one(), v3f_zero()); return(result); } Transform transform_make_translate(V3F translate) { Transform result = transform_default(); result.translate = translate; return(result); } Transform transform_make_scale(V3F scale) { Transform result = transform_default(); result.scale = scale; return(result); } Transform transform_make_rotate(V3F angles) { Transform result = transform_default(); result.rotate = angles; return(result); } Transform transform_translate(Transform source, V3F translate) { Transform result = source; result.translate = v3f_add(source.translate, translate); return(result); } Transform transform_scale(Transform source, V3F scale) { Transform result = source; result.scale = v3f_dot(source.scale, scale); return(result); } Transform transform_rotate(Transform source, V3F angles) { Transform result; result.translate = source.translate; result.scale = source.scale; result.rotate = v3f_add(source.rotate, angles); return(result); } Transform transform_make_scale_translate(V3F scale, V3F translate) { Transform result = transform_default(); result.translate = translate; result.scale = scale; return(result); } MAT4 transform_apply(Transform transform) { MAT4 result = mat4_identity(); MAT4 translate = mat4_make_translate(transform.translate); MAT4 scale = mat4_make_scale(transform.scale); MAT4 rotate = mat4_make_rotate(transform.rotate); result = mat4_mul(mat4_mul(translate, scale), rotate); return(result); } MAT4 ortho(F32 l, F32 r, F32 b, F32 t, F32 n, F32 f) { MAT4 result = mat4_identity(); result.m0.x = 2.0f/(r-l); result.m1.y = 2.0f/(t-b); result.m2.z = -2.0f/(f-n); result.m3.x = -(r+l)/(r-l); result.m3.y = -(t+b)/(t-b); result.m3.z = -(f+n)/(f-n); return(result); } MAT4 perspective(F32 fovx, F32 ar, F32 n, F32 f) { F32 r = n*f32_tan(fovx/2.0f*DEG2RAD); F32 t = r/ar; MAT4 result = mat4_identity(); result.m0.x = n/r; result.m1.y = n/t; result.m2.z = -(f+n)/(f-n); result.m2.w = -1.0f; result.m3.z = (-2.0f*f*n)/(f-n); result.m3.w = 0.0f; return(result); } MAT4 look_at(V3F eye, V3F target, V3F up) { V3F f = v3f_norm(v3f_sub(eye, target)); V3F l = v3f_norm(v3f_cross(up, f)); V3F u = v3f_cross(f, l); MAT4 translate = mat4_make_translate(v3f_negate(eye)); MAT4 rotate = mat4_change_basis(l, u, f); MAT4 result = mat4_mul(mat4_transpose(rotate), translate); return(result); } V3F update_camera_pos_orbital(Input input, V3F pos, V3F target, F32 dt, F32 speed) { V3F up, f, l, u, dp, new_pos; up = v3f(0.0f, 1.0f, 0.0f); f = v3f_norm(v3f_sub(target, pos)); l = v3f_norm(v3f_cross(up, f)); u = v3f_cross(f, l); dp = v3f_zero(); if (key_is_pressed(input.move_right)) dp = v3f_sub(dp, l); if (key_is_pressed(input.move_forward)) dp = v3f_add(dp, f); if (key_is_pressed(input.move_left)) dp = v3f_add(dp, l); if (key_is_pressed(input.move_backward)) dp = v3f_sub(dp, f); if (key_is_pressed(input.move_up)) dp = v3f_add(dp, u); if (key_is_pressed(input.move_down)) dp = v3f_sub(dp, u); new_pos = v3f_add(pos, v3f_scalef(dp, speed*dt)); return(new_pos); } #endif /* COMMON_H */