Model in COLLADA format
How this cube looks in my app
How this cube looks in Blender
You can notice that my cube has green points at the bottom while in Blender - it does not. I want to realize what is the problem with my weights, mb problem with how I store my data. Help me please to find the mistake!
Below I provide some mandatory parts of the code and link to the whole project to clearly understand and reproduce the problem
Link: https://github.com/theoldestsc/PyrhonScratch
Mandatory parts of the code:
The way how I load the Mesh(I have a Model class that contains all meshes, and I have a separate vector of vertices for every Mesh)
Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene)
{
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
std::vector<Texture> textures;
for(unsigned int vIdx = 0; vIdx < mesh->mNumVertices; vIdx++)
{
Vertex vertex;
QVector3D vector;
vector.setX(mesh->mVertices[vIdx].x);
vector.setY(mesh->mVertices[vIdx].y);
vector.setZ(mesh->mVertices[vIdx].z);
vertex.position = vector;
if (mesh->HasNormals())
{
vector.setX(mesh->mNormals[vIdx].x);
vector.setY(mesh->mNormals[vIdx].y);
vector.setZ(mesh->mNormals[vIdx].z);
vertex.normal = vector;
}
if(mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?
{
QVector2D vec;
vec.setX(mesh->mTextureCoords[0][vIdx].x);
vec.setY(mesh->mTextureCoords[0][vIdx].y);
vertex.texcoord = vec;
}
else
vertex.texcoord = QVector2D(0.0f, 0.0f);
vertices.push_back(vertex);
}
for(unsigned int fIdx = 0; fIdx < mesh->mNumFaces; fIdx++)
{
aiFace face = mesh->mFaces[fIdx];
for(unsigned int j = 0; j < face.mNumIndices; j++)
{
indices.push_back(face.mIndices[j]);
}
}
if(mesh->mMaterialIndex >= 0)
{
aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
std::vector<Texture> diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
std::vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
}
//TODO:Print status of mesh loading(Which mesh, path, number, sizes, bones)
if(mesh->HasBones())
{
qDebug() << "Bones processing ...";
for(int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
{
auto bone = mesh->mBones[boneIndex];
std::string name = bone->mName.C_Str();
int index = -1;
if (boneIndexMap.find(name) == boneIndexMap.end()) {
index = bones.size();
Bone newBone;
newBone.name = name;
newBone.offsetMatrix = aiToQt(bone->mOffsetMatrix);
bones.push_back(newBone);
boneIndexMap[name] = index;
} else {
index = boneIndexMap[name];
}
for(int i = 0; i < bone->mNumWeights; ++i)
{
auto weightInfo = bone->mWeights[i];
auto vertexId = weightInfo.mVertexId;
auto weight = weightInfo.mWeight;
for(int k = 0; k < WEIGHTS_PER_VERTEX; ++k)
{
if(vertices.at(vertexId).weight[k] == 0.0)
{
vertices.at(vertexId).id[k] = index;
vertices.at(vertexId).weight[k] = weight;
break;
}
}
}
}
}
qDebug() << vertices.size() << " " << mesh->mNumFaces << " Bones data: " << boneIndexMap.size() << " " << bones.size() << "\n";
return Mesh(vertices, indices, textures, bones);
}
The way how I send data to OpenGL
void Mesh::setupMesh()
{
auto functions = QOpenGLContext::currentContext()->functions();
auto extraFunctions = QOpenGLContext::currentContext()->extraFunctions();
VAO = std::shared_ptr<QOpenGLVertexArrayObject>(new QOpenGLVertexArrayObject());
VBO = std::shared_ptr<QOpenGLBuffer>(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer));
EBO = std::shared_ptr<QOpenGLBuffer>(new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer));
VAO->create();
VBO->create();
EBO->create();
VAO->bind();
VBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
EBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
VBO->bind();
VBO->allocate(vertices.data(), vertices.size() * sizeof(Vertex));
EBO->bind();
EBO->allocate(indices.data(), indices.size() * sizeof(unsigned int));
functions->glEnableVertexAttribArray(0);
functions->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
functions->glEnableVertexAttribArray(1);
functions->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, normal));
functions->glEnableVertexAttribArray(2);
functions->glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, texcoord));
functions->glEnableVertexAttribArray(3);
functions->glVertexAttribPointer(3, WEIGHTS_PER_VERTEX, GL_FLOAT, GL_TRUE, sizeof(Vertex),
(void*)offsetof(Vertex, weight));
functions->glEnableVertexAttribArray(4);
functions->glVertexAttribPointer(4, WEIGHTS_PER_VERTEX, GL_UNSIGNED_INT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, id));
extraFunctions->glBindVertexArray(0);
}
My Vertex structure I store 4 weights for every vertex, then I send the vector of it to OpenGL
#define WEIGHTS_PER_VERTEX 4
struct Vertex
{
QVector3D position;
QVector2D texcoord;
QVector3D normal;
float weight[WEIGHTS_PER_VERTEX] = {0.0f, 0.0f, 0.0f, 0.0f};
unsigned int id[WEIGHTS_PER_VERTEX] = {0, 0, 0, 0};
};
My Bone structure
struct Bone {
std::string name; //Only this field is used
int parentIndex;
QMatrix4x4 offsetMatrix; //Only this field is used
QMatrix4x4 finalTransform;
};
Here are shaders vertex shader:
#version 440
const int MAX_BONES = 100;
layout (location = 0) in vec3 vertex_position;
layout (location = 1) in vec3 vertex_normal;
layout (location = 2) in vec2 vertex_texcoord;
layout (location = 3) in vec4 weights;
layout (location = 4) in ivec4 s_vIDs;
uniform mat4 ModelMatrix;
uniform mat4 ViewMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 gBones[MAX_BONES];
out vec2 TexCoords;
out vec4 we;
out vec4 id;
out vec3 interpolatedNormal;
void main()
{
TexCoords = vertex_texcoord;
vec4 totalPosition = vec4(0.0f);
for(int i = 0 ; i < 4 ; i++)
{
if(s_vIDs[i] == -1)
continue;
if(s_vIDs[i] >= MAX_BONES)
{
totalPosition = vec4(vertex_position, 1.0f);
break;
}
vec4 localPosition = gBones[s_vIDs[i]] * vec4(vertex_position, 1.0f);
totalPosition += localPosition * weights[i];
}
interpolatedNormal = normalize(mat3(transpose(inverse(ModelMatrix))) * vertex_normal);
mat4 viewModel = ViewMatrix * ModelMatrix;
gl_Position = ProjectionMatrix * viewModel * totalPosition;
we = weights;
id = s_vIDs;
}
Fragment Shader:
#version 440
out vec4 FragColor;
in vec4 we;
in vec4 id;
in vec3 interpolatedNormal;
in vec2 TexCoords;
struct Material {
sampler2D texture_diffuse1;
};
uniform Material material;
void main()
{
FragColor = we;
}
Just I want to be sure that my animation in the future will work correctly so I want to resolve this mistake while my pet project doesn't become bigger. Mb the problem with my understanding of how Blender and OpenGL visualize weights data