#define TINYOBJ_LOADER_C_IMPLEMENTATION #include "tinyobj_loader_c.h" mat4 apply_transform(transform_t transform) { mat4 result = mat4_make_scale(transform.scale); result = mat4_rotate(result, transform.rotation); result = mat4_transl(result, transform.position); return result; } void init_mesh_buffers(mesh_t *mesh) { assert(mesh->vertices); assert(mesh->nvertices > 0); u32 vertices_size = mesh->nvertices*sizeof(vertex_t); u32 indices_size = mesh->nindices*sizeof(u32); glGenVertexArrays(1, &mesh->vao); glBindVertexArray(mesh->vao); glGenBuffers(1, &mesh->vbo); glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo); glBufferData(GL_ARRAY_BUFFER, vertices_size, mesh->vertices, GL_STATIC_DRAW); if (mesh->indices && (mesh->nindices > 0)) { glGenBuffers(1, &mesh->ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_size, mesh->indices, GL_STATIC_DRAW); } glEnableVertexAttribArray(SHADER_POSITION_LOCATION); glVertexAttribPointer(SHADER_POSITION_LOCATION, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void *)offsetof(vertex_t, position)); glEnableVertexAttribArray(SHADER_NORMAL_LOCATION); glVertexAttribPointer(SHADER_NORMAL_LOCATION, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void *)offsetof(vertex_t, normal)); glEnableVertexAttribArray(SHADER_TEXCOORDS_LOCATION); glVertexAttribPointer(SHADER_TEXCOORDS_LOCATION, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void *)offsetof(vertex_t, texcoords)); glBindVertexArray(0); } mesh_t init_mesh(transform_t transform, i32 nvertices, vertex_t *vertices, i32 nindices, u32 *indices) { mesh_t mesh = {0}; mesh.transform = transform; mesh.nvertices = nvertices; mesh.vertices = vertices; mesh.nindices = nindices; mesh.indices = indices; init_mesh_buffers(&mesh); return mesh; } void clear_mesh(mesh_t *mesh) { glBindVertexArray(mesh->vao); glDisableVertexAttribArray(SHADER_POSITION_LOCATION); glDisableVertexAttribArray(SHADER_NORMAL_LOCATION); glDisableVertexAttribArray(SHADER_TEXCOORDS_LOCATION); glBindVertexArray(0); glDeleteVertexArrays(1, &mesh->vao); glBindBuffer(GL_ARRAY_BUFFER, 0); glDeleteBuffers(1, &mesh->vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDeleteBuffers(1, &mesh->ebo); } void add_mesh_texture(mesh_t *mesh, texture_t texture) { if (!mesh) { printf("error: can't add texture to a null mesh :|\n"); return; } if (mesh->ntextures+1 >= MAX_TEXTURE_PER_MESH) { printf("warning: \"%s\" texture limit reached: %d\n", texture.name, MAX_TEXTURE_PER_MESH); return; } mesh->textures[mesh->ntextures++] = texture; } mesh_t gen_quad_mesh(arena_t *arena, transform_t transform, f32 width, f32 height) { assert(width > 0); assert(height > 0); i32 nvertices = 4; vertex_t *vertices = push_arena(arena, nvertices*sizeof(vertex_t)); vertices[0] = (vertex_t){{-width/2.0f, -height/2.0f, 0.0f}, V3_ZERO, {0.0f, 0.0f}}; vertices[1] = (vertex_t){{-width/2.0f, height/2.0f, 0.0f}, V3_ZERO, {0.0f, height}}; vertices[2] = (vertex_t){{ width/2.0f, -height/2.0f, 0.0f}, V3_ZERO, {width, 0.0f}}; vertices[3] = (vertex_t){{ width/2.0f, height/2.0f, 0.0f}, V3_ZERO, {width, height}}; i32 nindices = 6; u32 *indices = push_arena(arena, nindices*sizeof(u32)); indices[0] = 0; indices[1] = 1; indices[2] = 3; indices[3] = 0; indices[4] = 2; indices[5] = 3; mesh_t mesh = init_mesh(transform, nvertices, vertices, nindices, indices); return mesh; } mesh_t gen_circle_mesh(arena_t *arena, transform_t transform, f32 radius, i32 nvertices) { if (nvertices < 3) nvertices = 3; vertex_t *vertices = push_arena(arena, nvertices*sizeof(vertex_t)); f32 angle = 0.0f; f32 dangle = 2.0f*F_PI/nvertices; for (i32 i = 0; i < nvertices; ++i, angle += dangle) vertices[i] = (vertex_t){{fcos(angle)*radius, fsin(angle)*radius, 0.0f}, V3_ZERO, V2_ZERO}; i32 nindices = nvertices*3; u32 *indices = push_arena(arena, nindices*sizeof(u32)); for (i32 i = 0, vi = 1; i < nindices; i += 3, vi++) { indices[i] = 0; indices[i+1] = vi; indices[i+2] = ((vi+1 == nvertices) ? 1 : vi+1); } mesh_t mesh = init_mesh(transform, nvertices, vertices, nindices, indices); return mesh; } model_t init_model(transform_t transform, i32 nmeshes, mesh_t *meshes) { model_t model = { transform, nmeshes, meshes, }; return model; } void read_file_tinyobj(void* ctx, const char* filename, const i32 is_mtl, const char* obj_filename, char** data, u64* len) { if (is_mtl) printf("info: is_mtl is set (don't use it right now)\n"); if (obj_filename) printf("info: obj_filename is \"%s\"\n", obj_filename); if (!filename) { printf("error: obj filename is zero\n"); *data = 0; *len = 0; return; } arena_t *arena = (arena_t *)ctx; *len = sys_read_file(arena, data, filename); } model_t load_model_obj(arena_t *arena, transform_t transform, const char *filename) { tinyobj_attrib_t attrib; u64 nshapes; tinyobj_shape_t *shapes; u64 nmaterials; tinyobj_material_t *materials; u32 flags = TINYOBJ_FLAG_TRIANGULATE; i32 status = tinyobj_parse_obj(&attrib, &shapes, &nshapes, &materials, &nmaterials, filename, read_file_tinyobj, arena, flags); model_t model = {0}; if (status != TINYOBJ_SUCCESS) { printf("error: failed to parse \"%s\"\n", filename); return model; } u64 ntriangles = attrib.num_face_num_verts; u64 face_offset = 0; u64 nvertices = ntriangles*3; vertex_t *vertices = push_arena(arena, sizeof(vertex_t)*nvertices); u64 nindices = ntriangles*3; u32 *indices = push_arena(arena, sizeof(u32)*nindices); i32 vertex_count = 0, index_count = 0, index_index = 0; for (u32 i = 0; i < attrib.num_face_num_verts; ++i) { assert(attrib.face_num_verts[i]%3 == 0); assert(attrib.face_num_verts[i]/3 > 0); assert(attrib.num_texcoords); tinyobj_vertex_index_t idx; for (i32 j = 0; j < 3; ++j) { idx = attrib.faces[face_offset+j]; assert(idx.v_idx >= 0); v3 position = { 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 < (i32)attrib.num_normals); v3 normal = { 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 < (i32)attrib.num_texcoords); v2 texcoords = { attrib.texcoords[2*idx.vt_idx+0], attrib.texcoords[2*idx.vt_idx+1] }; vertices[vertex_count++] = (vertex_t){position, normal, texcoords}; indices[index_index++] = index_count++; } face_offset += 3; } i32 nmeshes = 1; mesh_t *meshes = push_arena(arena, sizeof(mesh_t)*nmeshes); *meshes = init_mesh(DEFAULT_TRANSFORM, nvertices, vertices, nindices, indices); tinyobj_attrib_free(&attrib); tinyobj_shapes_free(shapes, nshapes); tinyobj_materials_free(materials, nmaterials); model = init_model(transform, nmeshes, meshes); printf("info: \"%s\" loaded successfully\n", filename); return model; }