This has been solved! The issue wasn’t with my code! It was with Blender messing up indexes when the “Triangulate faces” option was checked. I instead selected all faces with Ctrl + A, then pressed CTRL + T to manually triangulate everything and then ignored the option in exporting. It fixed everything
I’m trying to interpolate between two VBOs to create animation (I know this can be costly, not here to discuss that). It seems the in-between state of the “missing” frame is breaking. So if I have keyframe 1 and want to interp to keyframe 3, I want the interp to obviously “make” frame 2 by smoothing, but frame 2 ends up broken.
If I don’t use any interpolation, and simply “hard” render from frame to frame, none of the meshes break and everything renders fine besides the fact it’s not smooth, which obviously we want smooth, non choppy animation.
Heres a gif showing what it looks like:
Another gif showing the issue. You can see for some reason it’s almost “rotating” the verts in the leg.
Here’s an image with some more info too:
I don’t know if this is an issue with the same verts not being manipulated or what, I don’t even know how to go about a good way debugging it to see if it’s the proper vertex being moved.
I am using blender to export in obj format with these settings:
Vertex Shader:
layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec2 a_tex_coord;
layout (location = 2) in vec3 a_norm;
layout (location = 3) in vec3 next_a_pos;
layout (location = 4) in vec2 next_a_tex_coord;
layout (location = 5) in vec3 next_a_norm;
uniform float interpolation;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec2 tex_coord;
void
main()
{
vec3 vert_interp = mix(a_pos, next_a_pos, interpolation);
vec3 norm_interp = mix(a_norm, next_a_norm, interpolation);
vec4 world_pos = model * vec4(vert_interp, 1.0f);
gl_Position = projection * view * world_pos;
tex_coord = vec2(a_tex_coord.x, 1.0 - a_tex_coord.y);
}
Could it be because of how I am loading in my .obj file? I have written a custom parser, maybe there is a chance I’ve messed something up? It’s confusing because without interpolating, the meshes are correct and working.
obj_mdl.h
#pragma once
#include <cglm/cglm.h>
#include <stdint.h>
#define VERTS_IN_BYTES(a) (a * sizeof(struct vertex))
#define INDICES_IN_BYTES(a) (a * sizeof(uint32_t))
struct
vertex
{
vec3 pos;
vec2 uv;
vec3 norm;
};
struct
obj_mdl
{
struct vertex *verts;
uint32_t verts_size;
uint32_t vert_count;
uint32_t *indices;
uint32_t indices_size;
uint32_t indice_count;
};
struct
obj_mdl_face
{
vec3 vert_index;
vec3 uv_index;
vec3 norm_index;
};
struct
obj_mdl_data
{
vec3 *verts;
uint32_t vert_count;
vec2 *uvs;
uint32_t uv_count;
vec3 *norms;
uint32_t norm_count;
struct obj_mdl_face *faces;
uint32_t face_count;
};
bool obj_mdl_load(struct obj_mdl *mdl, const char *path);
void obj_mdl_destroy(struct obj_mdl *mdl);
obj_mdl.c
#include <stdio.h>
#include <string.h>
#include "../io/io.h"
#include "obj_mdl.h"
static void
obj_mdl_load_prepare_data(struct obj_mdl_data *data, const char *path)
{
FILE *fp = fopen(path, "r");
while (1)
{
char line[256];
int32_t res = fscanf(fp, "%s", line);
if (res == EOF) break;
if (strcmp(line, "v") == 0) data->vert_count++;
else if (strcmp(line, "vt") == 0) data->uv_count++;
else if (strcmp(line, "vn") == 0) data->norm_count++;
else if (strcmp(line, "f") == 0) data->face_count++;
}
data->verts = malloc(sizeof(vec3) * data->vert_count);
data->uvs = malloc(sizeof(vec2) * data->uv_count);
data->norms = malloc(sizeof(vec3) * data->norm_count);
data->faces = malloc(sizeof(struct obj_mdl_face) * data->face_count);
fclose(fp);
}
static void
obj_mdl_load_process_data(struct obj_mdl *mdl, struct obj_mdl_data *data)
{
struct vertex *verts = malloc(sizeof(struct vertex) * (data->face_count * 3));
uint32_t vert_count = 0;
uint32_t *indices = malloc(sizeof(struct vertex) * (data->face_count * 3));
for (uint32_t i = 0; i < data->face_count; i++)
{
for (int j = 0; j < 3; j++)
{
struct vertex v;
int32_t vert_index = (int32_t)(data->faces[i].vert_index[j]-1);
int32_t uv_index = (int32_t)(data->faces[i].uv_index[j]-1);
int32_t norm_index = (int32_t)(data->faces[i].norm_index[j]-1);
memcpy(v.pos, data->verts[vert_index], sizeof(vec3));
memcpy(v.uv, data->uvs[uv_index], sizeof(vec2));
memcpy(v.norm, data->norms[norm_index], sizeof(vec3));
indices[vert_count] = vert_count;
verts[vert_count] = v;
vert_count++;
}
}
uint32_t indice_count = vert_count;
uint32_t verts_size = VERTS_IN_BYTES(vert_count);
mdl->verts = malloc(verts_size);
mdl->verts_size = verts_size;
mdl->vert_count = vert_count;
memcpy(mdl->verts, verts, verts_size);
uint32_t indices_size = INDICES_IN_BYTES(indice_count);
mdl->indices = malloc(indices_size);
mdl->indices_size = indices_size;
mdl->indice_count = indice_count;
memcpy(mdl->indices, indices, indices_size);
free(verts);
free(indices);
}
bool
obj_mdl_load(struct obj_mdl *mdl, const char *path)
{
/* Currently we assume that faces will always have the 3 values, but it could be
* "f 4//5" for example if theres no tex coords, so we should eventually check that */
FILE *fp = fopen(path, "r");
if (fp == NULL)
{
return false;
}
struct obj_mdl_data data = {NULL, 0, NULL, 0, NULL, 0, NULL, 0};
obj_mdl_load_prepare_data(&data, path);
uint32_t v_i = 0, u_i = 0, n_i = 0, f_i = 0;
while (1)
{
char line[256];
int32_t res = fscanf(fp, "%s", line);
if (res == EOF) break;
if (strcmp(line, "v") == 0)
{
fscanf(fp, "%f %f %f
", &data.verts[v_i][0], &data.verts[v_i][1], &data.verts[v_i][2]);
v_i++;
}
else if (strcmp(line, "vt") == 0)
{
fscanf(fp, "%f %f
", &data.uvs[u_i][0], &data.uvs[u_i][1]);
u_i++;
}
else if (strcmp(line, "vn") == 0)
{
fscanf(fp, "%f %f %f
", &data.norms[n_i][0], &data.norms[n_i][1], &data.norms[n_i][2]);
n_i++;
}
else if (strcmp(line, "f") == 0)
{
/* vert / uv / norm */
fscanf(fp, "%f/%f/%f %f/%f/%f %f/%f/%f
",
&data.faces[f_i].vert_index[0],
&data.faces[f_i].uv_index[0],
&data.faces[f_i].norm_index[0],
&data.faces[f_i].vert_index[1],
&data.faces[f_i].uv_index[1],
&data.faces[f_i].norm_index[1],
&data.faces[f_i].vert_index[2],
&data.faces[f_i].uv_index[2],
&data.faces[f_i].norm_index[2]);
f_i++;
}
}
fclose(fp);
obj_mdl_load_process_data(mdl, &data);
free(data.verts);
free(data.uvs);
free(data.norms);
free(data.faces);
printf("Verts: %u UV: %u Normals: %u Faces: %u
",
data.vert_count, data.uv_count, data.norm_count, data.face_count);
return true;
}
void
obj_mdl_destroy(struct obj_mdl *mdl)
{
free(mdl->verts);
free(mdl->indices);
}
To render, I simply generate VAO, VBO, EBO out of obj_mdl struct verts and indices, and then use this:
glBindVertexArray(a->vaos[a->cur_frame]);
glBindBuffer(GL_ARRAY_BUFFER, a->vbos[a->cur_frame]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (void*)(3 * sizeof(float)));
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (void*)(5 * sizeof(float)));
glBindBuffer(GL_ARRAY_BUFFER, a->vbos[a->next_frame]);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (void*)0);
glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (void*)(3 * sizeof(float)));
glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (void*)(5 * sizeof(float)));
for (uint32_t i = 0; i < 6; i++) glEnableVertexAttribArray(i);
glBindVertexArray(0);
a->interp += perc;
shader_set_float(SHADER_DEFAULT, "interpolation", a->interp);