Files
LivePlotter/src/body.cpp

143 lines
4.0 KiB
C++

#include <cassert>
#include <glad/glad.h>
#include <map>
#include <string>
#include "util.hpp"
#include "body.hpp"
#include "shaders.hpp"
using namespace std;
enum class ParserState {
PREFIX,
VERTEX,
FACE,
FACE_SKIP,
};
static map<string, ObjData> 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;
}