I’m trying to do skeletal animation with assimp and opengl. However, it seems that my vertex shader is not receiving data about the bone weights. I am not sure why this is happening. I am sure that my array of vertices has the data, as I have printed it out before drawing.
I’m really not sure why. Could someone take a look at the relevant code and explain what is going on to me?
My vertex shader
#version 330 core
#define MAX_BONES_PER_VERTEX 5
#define MAX_BONES 256
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texCoords;
layout (location = 3) in uint[MAX_BONES_PER_VERTEX] boneIDs;
layout (location = 4) in float[MAX_BONES_PER_VERTEX] boneWeights;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 bones[MAX_BONES];
out vec3 Normal;
out vec3 fragPos;
out vec2 TexCoords;
void main() {
vec4 vertex = vec4(position, 1.0);
mat4 boneTransform = mat4(1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
for (int i = 0; i < MAX_BONES_PER_VERTEX; i++)
boneTransform += bones[boneIDs[i]] * boneWeights[i];
vec4 finalVertex = boneTransform * vertex;
mat4 normalMatrix = transpose(inverse(model));
gl_Position = projection * view * model * finalVertex;
fragPos = vec3(model * vec4(position, 1.0));
Normal = (normalMatrix * vec4(normal, 1.0)).xyz;
TexCoords = texCoords;
}
My mesh class:
#ifndef MESH_HPP
#define MESH_HPP
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtx/string_cast.hpp>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <string>
#include <vector>
#include "../shader.hpp"
#include "vertex.hpp"
using glm::vec3;
using glm::vec2;
using std::vector;
using std::string;
struct Texture {
GLuint id;
string type;
aiString filePath;
};
class Mesh {
public:
// Mesh Data
vector<Vertex> vertices;
vector<GLuint> indices;
vector<Texture> textures;
vector<glm::mat4> bones;
// Constructor
Mesh(vector<Vertex> verts, vector<GLuint> inds, vector<Texture> texs, vector<glm::mat4> bones, const char* name);
void draw(Shader&, glm::mat4& model);
const char* name;
private:
GLuint VAO, VBO, EBO;
};
inline Mesh::Mesh(vector<Vertex> verts, vector<GLuint> inds, vector<Texture> texs, vector<glm::mat4> b, const char* n):
vertices(verts),
indices(inds),
textures(texs),
bones(b),
name(n) {
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW);
// Vertex Positions
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0);
glEnableVertexAttribArray(0);
// Vertex Normals
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, normal));
glEnableVertexAttribArray(1);
// Vertex Texture Coords
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, texCoords));
glEnableVertexAttribArray(2);
// Bone IDs
glVertexAttribIPointer(3, MAX_BONES_PER_VERTEX, GL_INT, sizeof(Vertex), (GLvoid*)offsetof(Vertex, boneIDs));
glEnableVertexAttribArray(3);
// Bone Weights
glVertexAttribPointer(4, MAX_BONES_PER_VERTEX, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, boneWeights));
glEnableVertexAttribArray(4);
glBindVertexArray(0);
}
inline void Mesh::draw(Shader& shader, glm::mat4& model) {
GLuint diffuseNr = 1;
GLuint specularNr = 1;
for (GLuint i = 0; i < this->textures.size(); i++) {
glActiveTexture(GL_TEXTURE0 + i); // Activate proper texture unit before binding
// Retrieve texture number (the N in diffuse_textureN)
std::stringstream ss;
string number;
string name = textures[i].type;
if(name == "texture_diffuse")
ss << diffuseNr++; // Transfer GLuint to stream
else if(name == "texture_specular")
ss << specularNr++; // Transfer GLuint to stream
number = ss.str();
glUniform1f(glGetUniformLocation(shader.program, ("material." + name + number).c_str()), i);
glBindTexture(GL_TEXTURE_2D, textures[i].id);
}
glActiveTexture(GL_TEXTURE0);
shader.setUniform("model", model);
bones[1] = glm::rotate(bones[1], 0.1f, vec3(0.0f, 1.0f, 0.4f));
for (int i = 0, size = bones.size(); i < size && i < 256; i++) {
shader.setUniform(("bones[" + std::to_string(i) + ']').c_str(), bones[i]);
}
// Draw mesh
glBindVertexArray(VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
#endif // MESH_HPP
My vertex struct:
#ifndef VERTEX_HPP
#define VERTEX_HPP
#include <glm/glm.hpp>
#include <GL/glew.h>
#include <vector>
static constexpr GLuint MAX_BONES_PER_VERTEX = 5;
struct Vertex {
glm::vec3 position;
glm::vec3 normal;
glm::vec2 texCoords;
GLuint boneIDs[MAX_BONES_PER_VERTEX];
GLfloat boneWeights[MAX_BONES_PER_VERTEX];
Vertex() {
for (unsigned int i = 0; i < MAX_BONES_PER_VERTEX; i++)
boneIDs[i] = 0;
}
GLint getNextBoneID() {
for (unsigned int i = 0; i < MAX_BONES_PER_VERTEX; i++)
if (boneIDs[i] == 0)
return i;
return -1;
}
};
#endif // VERTEX_HPP
If you need more code, I can give it.