// model.c
#include "model.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define TINYOBJ_LOADER_C_IMPLEMENTATION
#include "tinyobj_loader_c.h"
void simple_file_reader(
void* ctx,
const char* filename,
int is_mtl,
const char* obj_filename,
char** out_buf,
size_t* out_len)
{
(void)ctx; (void)is_mtl; (void)obj_filename;
FILE* fp = fopen(filename, "rb");
if (!fp) {
*out_buf = NULL;
*out_len = 0;
return;
}
fseek(fp, 0, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0, SEEK_SET);
char* buf = (char*)malloc(sz + 1);
if (!buf) {
fclose(fp);
*out_buf = NULL;
*out_len = 0;
return;
}
fread(buf, 1, sz, fp);
buf[sz] = 0;
fclose(fp);
*out_buf = buf;
*out_len = sz;
}
Model* model_create(const float* vertex_data, size_t vertex_count, int* attributes, int attrib_count) {
Model* model = malloc(sizeof(Model));
if (!model) return NULL;
model->vertex_count = (int)vertex_count;
int stride = 0;
for (int i = 0; i < attrib_count; i++) {
stride += attributes[i];
}
model->vertex_stride = stride;
glGenVertexArrays(1, &model->vao);
glGenBuffers(1, &model->vbo);
glBindVertexArray(model->vao);
glBindBuffer(GL_ARRAY_BUFFER, model->vbo);
glBufferData(GL_ARRAY_BUFFER, vertex_count * stride * sizeof(float), vertex_data, GL_STATIC_DRAW);
int offset = 0;
for (int i = 0; i < attrib_count; i++) {
glVertexAttribPointer(i, attributes[i], GL_FLOAT, GL_FALSE, stride * sizeof(float), (void*)(offset * sizeof(float)));
glEnableVertexAttribArray(i);
offset += attributes[i];
}
glBindVertexArray(0);
return model;
}
Model* model_load(const char* filepath) {
tinyobj_attrib_t attrib;
tinyobj_shape_t* shapes = NULL;
size_t num_shapes = 0;
tinyobj_material_t* materials = NULL;
size_t num_materials = 0;
unsigned int flags = TINYOBJ_FLAG_TRIANGULATE;
int ret = tinyobj_parse_obj(
&attrib,
&shapes,
&num_shapes,
&materials,
&num_materials,
filepath,
simple_file_reader,
NULL,
flags
);
if (ret != TINYOBJ_SUCCESS) {
printf("Failed to load model: %s\n", filepath);
return NULL;
}
size_t triangle_count = 0;
for (size_t s = 0; s < num_shapes; s++) {
const tinyobj_shape_t* shape = &shapes[s];
size_t index_offset = 0;
for (size_t f = 0; f < shape->length; f++) {
int fv = attrib.face_num_verts[f];
if (fv == 3) triangle_count++;
index_offset += fv;
}
}
size_t vertex_count = triangle_count * 3;
float* vertex_data = malloc(sizeof(float) * 8 * vertex_count);
if (!vertex_data) return NULL;
size_t data_count = 0;
for (size_t s = 0; s < num_shapes; s++) {
const tinyobj_shape_t* shape = &shapes[s];
size_t index_offset = 0;
for (size_t f = 0; f < shape->length; f++) {
int fv = attrib.face_num_verts[f];
if (fv != 3) {
index_offset += fv;
continue;
}
for (int v = 0; v < 3; v++) {
tinyobj_vertex_index_t idx = attrib.faces[index_offset + v];
float vx = attrib.vertices[3 * idx.v_idx + 0];
float vy = attrib.vertices[3 * idx.v_idx + 1];
float vz = attrib.vertices[3 * idx.v_idx + 2];
float nx = 0.0f, ny = 0.0f, nz = 1.0f;
if (idx.vn_idx >= 0 && attrib.num_normals > 0) {
nx = attrib.normals[3 * idx.vn_idx + 0];
ny = attrib.normals[3 * idx.vn_idx + 1];
nz = attrib.normals[3 * idx.vn_idx + 2];
}
float tx = 0.0f, ty = 0.0f;
if (idx.vt_idx >= 0 && attrib.num_texcoords > 0) {
tx = attrib.texcoords[2 * idx.vt_idx + 0];
ty = 1.0f - attrib.texcoords[2 * idx.vt_idx + 1];
}
vertex_data[data_count++] = vx;
vertex_data[data_count++] = vy;
vertex_data[data_count++] = vz;
vertex_data[data_count++] = nx;
vertex_data[data_count++] = ny;
vertex_data[data_count++] = nz;
vertex_data[data_count++] = tx;
vertex_data[data_count++] = ty;
}
index_offset += fv;
}
}
int attributes[] = { 3, 3, 2 };
int num_vertices = data_count / 8;
Model* model = model_create(vertex_data, num_vertices, attributes, 3);
free(vertex_data);
tinyobj_attrib_free(&attrib);
tinyobj_shapes_free(shapes, num_shapes);
tinyobj_materials_free(materials, num_materials);
if (!model) {
printf("bad model \n");
return NULL;
}
return model;
}
void model_destroy(Model* model) {
if (!model) return;
glDeleteBuffers(1, &model->vbo);
glDeleteVertexArrays(1, &model->vao);
free(model);
}