#include #include #include #include #include "util.hpp" #include "body.hpp" #include "shaders.hpp" using namespace std; enum class ParserState { PREFIX, VERTEX, FACE, FACE_SKIP, }; static map cache; bool _parse_obj(ObjData* data, const char* obj_filepath); bool load_body(Body* out_body, const char* obj_filepath) { ObjData data; if (auto it = cache.find(obj_filepath); it != cache.end()) { data = it->second; } else if (_parse_obj(&data, obj_filepath)) { cache[obj_filepath] = data; } else { return false; } // Need a good validation check here. This is a stand in. if (data.verts_len == 0 || data.faces_len == 0) { printf("Obj file appears incomplete or corrupted. Num verts %d. Num Faces %d.\n", data.verts_len, data.faces_len); return false; } uint vao, vbo, ebo; vao = 0; vbo = 0; ebo = 0; glGenVertexArrays(1, &vao); glGenBuffers(1, &vbo); glGenBuffers(1, &ebo); glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, data.verts_len * sizeof(float), data.verts, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.faces_len * sizeof(int), data.faces, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); *out_body = { .pose = glm::mat4(1), .ebo = ebo, .vao = vao, .vbo = vbo, .shader = 0, .data = data }; return true; } void draw_body(const Body& b) { use_shader(b.shader); set_uniform(b.shader, "global_t", glm::scale(b.pose, glm::vec3(b.scale))); set_uniform(b.shader, "color", b.color); glBindVertexArray(b.vao); glDrawElements(GL_TRIANGLES, b.data.faces_len, GL_UNSIGNED_INT, 0); } void create_new_sphere(Body* b, float scale, glm::vec4 color) { assert(load_body(b, "Icosphere.obj")); b->scale = scale; b->color = color; } // I need to write a good obj file parser at some point. This is basically garbage bool _parse_obj(ObjData* data, const char* obj_filepath) { string source; if (!read_file(&source, obj_filepath)) { return {}; } size_t num_verts = 0; size_t num_faces = 0; for (int i = 1; i < source.size(); i++) { if (source[i] == ' ' && source[i - 1] == 'v') { num_verts++; } else if (source[i] == ' ' && source[i - 1] == 'f') { num_faces++; } } float* verts = (float*)malloc(sizeof(float) * num_verts * 3); int* faces = (int*)malloc(sizeof(int) * num_faces * 3); int vert_i = 0; int face_i = 0; // Get ready for the parsing loop ParserState state = ParserState::PREFIX; int start = 0; for (int i = 1; i < source.size(); i++) { switch (state) { case ParserState::PREFIX: if (source[i - 1] == 'v' && source[i] == ' ') { state = ParserState::VERTEX; start = i + 1; } else if (i > 0 && source[i - 1] == 'f' && source[i] == ' ') { state = ParserState::FACE; start = i + 1; } break; case ParserState::VERTEX: if (iswspace(source[i])) { verts[vert_i++] = atof(&source[start]); start = i + 1; } break; case ParserState::FACE: if (source[i] == '/') { state = ParserState::FACE_SKIP; faces[face_i++] = atoi(&source[start]) - 1; } break; case ParserState::FACE_SKIP: if (iswspace(source[i])) { state = ParserState::FACE; start = i + 1; } } if (source[i] == '\n' || source[i] == '\r') { state = ParserState::PREFIX; continue; } } *data = { .verts = verts, .verts_len = vert_i, .faces = faces, .faces_len = face_i }; return true; }