Edit** To be of use to someone later, the mistake in the below code is within the SetBoneTransforms() function. There is no need to convert the assimp matrix (especially not to an array of floats). Who knows what I was thinking. But, simply pass in the matrix as is and use the [0][0] value of the matrix in the glUniform call. You'll of course need to change the parameters of the SetBoneTransforms() to accommodate this change.
Alright, so I've struggled with loading and animating models using assimp for a couple weeks now, and it's by far one of the more difficult things I've come across since I started programming (basically, any of my programming deficiencies seem to be rearing their ugly heads in trying to implement this).
I'm mostly going from the tutorial here, though I've taken ideas and information from various resources and have adapted it to some extent to try to get in implemented into my framework (basically, into a format I'm comfortable working with). The author of the tutorial uses quite a lot of his own custom math headers and custom function calls for various 3D frameworks I've had to adapt, and it's possible I've done it poorly. There's certain a lot of code below that probably isn't the ideal way to do things or have an alternative in the glm library I'm unaware of (I plan on cleaning it up once it's functional). Please, feel free to point these out though, even if it doesn't solve the larger problems.
To note, depending on various things, I don't receive an error, though the model does not animate at all (best case scenario) or doesn't render (worst case scenario) or segfaults (just a clock error, I'm fairly sure, easily fixed, i think).
Since this is a complex problem and I'm likely doing several things wrong, I'll attempt to walk you through my process and explain how I understand what I'm doing. That may be the best way to illuminate what I'm doing wrong. Apologies in advance for the wall of code. To note, I'm using ASSIMP for the model loading.
For reference, here is the header (it's a little haphazard, since I've been mutilating this code for awhile now:
#ifndef CHESSPIECE_H
#define CHESSPIECE_H
#include "Entity.h"
#define GLM_FORCE_RADIANS
//Assimp////
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#define ARRAY_SIZE_IN_ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
#define NUM_BONES_PER_VERTEX 8
#define BONE_ID_LOCATION 5
#define BONE_WEIGHT_LOCATION 6
#define INVALID_MATERIAL 0xFFFFFFFF
class ChessPiece : public Entity
{
public:
ChessPiece();
ChessPiece(glm::vec3 locationIN, GLuint &progamIDIN, std::vector<glm::vec3> &verticesIN, std::vector<glm::vec2> &uvsIN,
std::vector<glm::vec3> &normalsIN, glm::vec3 &imageIndexIN, int tileTypeIN, glm::vec3 scaleIN);
void Render(GLuint &texturearray, Controls &control);
virtual ~ChessPiece();
void CleanUp();
void Enable(GLuint &texturearray);
void Disable();
bool LoadAssimpModel( const std::string& pFile);
void ProcessAssimpModel();
void DoTheSceneProcessing(const aiScene* &sceneIN);
void BoneTransform(float TimeInSeconds, std::vector<aiMatrix4x4>& Transforms);
void ReadNodeHeirarchy(float AnimationTime, const aiNode* pNode, const aiMatrix4x4& ParentTransform);
const aiNodeAnim* FindNodeAnim(const aiAnimation* pAnimation, const std::string NodeName);
void CalcInterpolatedRotation(aiQuaternion& Out, float AnimationTime, const aiNodeAnim* pNodeAnim);
void CalcInterpolatedPosition(aiVector3D& Out, float AnimationTime, const aiNodeAnim* pNodeAnim);
void CalcInterpolatedScaling(aiVector3D& Out, float AnimationTime, const aiNodeAnim* pNodeAnim);
uint FindRotation(float AnimationTime, const aiNodeAnim* pNodeAnim);
uint FindPosition(float AnimationTime, const aiNodeAnim* pNodeAnim);
uint FindScaling(float AnimationTime, const aiNodeAnim* pNodeAnim);
void SetBoneTransform(uint Index, const float &Transform);
protected:
private:
sf::Clock clocky;
//assimp variables////
Assimp::Importer importer; // Create an instance of the Importer class
const aiScene* scene;
std::vector<aiVector3D >vertices;
std::vector<aiVector3D >uv;
std::vector<aiVector3D >normal;
aiMatrix4x4 m_GlobalInverseTransform;
struct BoneInfo
{
aiMatrix4x4 BoneOffset;
aiMatrix4x4 FinalTransformation;
};
struct VertexBoneData
{
uint IDs[NUM_BONES_PER_VERTEX];
float Weights[NUM_BONES_PER_VERTEX];
VertexBoneData()
{
Reset();
};
void Reset()
{
// std::cout<<"resetting"<<std::endl;
for(int i = 0; i < NUM_BONES_PER_VERTEX; i++)
{
Weights[i] = 0;
IDs[i]=0;
}
}
void AddBoneData(uint BoneID, float Weight);
};
struct MeshEntry
{
MeshEntry()
{
NumIndices = 0;
BaseVertex = 0;
BaseIndex = 0;
MaterialIndex = INVALID_MATERIAL;
}
unsigned int NumIndices;
unsigned int BaseVertex;
unsigned int BaseIndex;
unsigned int MaterialIndex;
};
std::vector<VertexBoneData> Bones;
GLuint boneBuffer;
GLuint weightBuffer;
GLuint boneLocBuffer;
void LoadBones(uint MeshIndex, const aiMesh* pMesh, std::vector<VertexBoneData> &Bones);
std::map<std::string,uint> m_BoneMapping; // maps a bone name to its index
uint m_NumBones = 0;
std::vector<BoneInfo> m_BoneInfo;
std::vector<MeshEntry> m_Entries;
GLuint gBones;
static const uint MAX_BONES = 100;
GLuint m_boneLocation[MAX_BONES];
};
#endif // CHESSPIECE_H
I start by loading the model:
bool ChessPiece::LoadAssimpModel( const std::string& fileIN)
{
scene = importer.ReadFile( fileIN,
aiProcess_CalcTangentSpace |
aiProcess_GenSmoothNormals |
aiProcess_Triangulate |
aiProcess_JoinIdenticalVertices |
aiProcess_SortByPType| aiProcess_FlipUVs );
if( !scene)
{
std::cout<<importer.GetErrorString()<<std::endl;;
return false;
}
else
{
m_GlobalInverseTransform = scene->mRootNode->mTransformation;
m_GlobalInverseTransform.Inverse();
}
return true;
}
and then pull the vertices, uvs and normals from the Assimp scene:
void ChessPiece::ProcessAssimpModel()
{
uint NumVertices = 0;
uint NumIndices = 0;
m_Entries.resize(scene->mNumMeshes);
for (uint i = 0 ; i < m_Entries.size() ; i++)
{
NumVertices += scene->mMeshes[i]->mNumVertices;
}
Bones.resize(NumVertices);
for(int i = 0; i < scene->mNumMeshes; i++)
{
const aiMesh* mesh = scene->mMeshes[i];
for(int j = 0; j < mesh->mNumFaces; j++)
{
const aiFace& face = mesh->mFaces[j];
for(int k = 0; k < 3; k++)
{
vertices.push_back(mesh->mVertices[face.mIndices[k]]);
uv.push_back(mesh->mTextureCoords[0][face.mIndices[k]]);
if(mesh->HasNormals())
{
normal.push_back(mesh->mNormals[face.mIndices[k]]);
}
else
{
normal.push_back( aiVector3D(1.0f, 1.0f, 1.0f));
}
}
}
LoadBones(i, mesh, Bones);
}
}
So far, so good, I suppose. The model thus far does load and render from the collada file.
Then (within the above loop) load the bones for each mesh, filling a map with the index number and weight for each bone:
void ChessPiece::LoadBones(uint MeshIndex, const aiMesh* pMesh, std::vector<VertexBoneData> &Bones)
{
for (uint i = 0 ; i < pMesh->mNumBones ; i++) {
uint BoneIndex = 0;
std::string BoneName(pMesh->mBones[i]->mName.data);
if (m_BoneMapping.find(BoneName) == m_BoneMapping.end()) {
BoneIndex = m_NumBones;
m_NumBones++;
BoneInfo bi;
m_BoneInfo.push_back(bi);
}
else {
BoneIndex = m_BoneMapping[BoneName];
}
m_BoneMapping[BoneName] = BoneIndex;
m_BoneInfo[BoneIndex].BoneOffset = pMesh->mBones[i]->mOffsetMatrix;
for (uint j = 0 ; j < pMesh->mBones[i]->mNumWeights ; j++) {
uint VertexID = m_Entries[MeshIndex].BaseVertex + pMesh->mBones[i]->mWeights[j].mVertexId;
float Weight = pMesh->mBones[i]->mWeights[j].mWeight;
Bones[VertexID].AddBoneData(BoneIndex, Weight);
}
}
}
void ChessPiece::VertexBoneData::AddBoneData(uint BoneID, float Weight)
{
for (uint i = 0 ; i < ARRAY_SIZE_IN_ELEMENTS(IDs) ; i++)
{
if (Weights[i] == 0.0)
{
IDs[i] = BoneID;
Weights[i] = Weight;
return;
}
}
std::cout<<"too many bones"<<std::endl;
assert(0);
}
Then to finalize the object loading, I index a VBO with the infomation:
I worry a little that I'm setting up layout 5 and 6 (the bone information) incorrectly. I've never set the buffer data using a map, and it's possible I'm going about this incorrectly.
MatrixID = glGetUniformLocation(programID, "MVP");
ViewMatrixID = glGetUniformLocation(programID, "V");
ModelMatrixID = glGetUniformLocation(programID, "M");
ModelView3x3MatrixID = glGetUniformLocation(programID, "MV3x3");
DiffuseTextureID = glGetUniformLocation(programID, "DiffuseTextureSampler");
NormalTextureID = glGetUniformLocation(programID, "NormalTextureSampler");
SpecularTextureID = glGetUniformLocation(programID, "SpecularTextureSampler");
std::vector<glm::vec3> tangents;
std::vector<glm::vec3> bitangents;
computeTangentBasis2(
vertices, uv, normal, // input
tangents, bitangents // output
);
indexVBO_TBN2(
vertices, uv, normal, tangents, bitangents,
indices, indexed_vertices, indexed_uvs, indexed_normals, indexed_tangents, indexed_bitangents
);
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, indexed_vertices.size() * sizeof(glm::vec3), &indexed_vertices[0], GL_STATIC_DRAW);
glGenBuffers(1, &uvbuffer);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glBufferData(GL_ARRAY_BUFFER, indexed_uvs.size() * sizeof(glm::vec2), &indexed_uvs[0], GL_STATIC_DRAW);
glGenBuffers(1, &normalbuffer);
glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
glBufferData(GL_ARRAY_BUFFER, indexed_normals.size() *sizeof(glm::vec3), &indexed_normals[0], GL_STATIC_DRAW);
glGenBuffers(1, &tangentbuffer);
glBindBuffer(GL_ARRAY_BUFFER, tangentbuffer);
glBufferData(GL_ARRAY_BUFFER, indexed_tangents.size() * sizeof(glm::vec3), &indexed_tangents[0], GL_STATIC_DRAW);
glGenBuffers(1, &bitangentbuffer);
glBindBuffer(GL_ARRAY_BUFFER, bitangentbuffer);
glBufferData(GL_ARRAY_BUFFER, indexed_bitangents.size() * sizeof(glm::vec3), &indexed_bitangents[0], GL_STATIC_DRAW);
//bones////
glEnableVertexAttribArray(5);
glBindBuffer(GL_ARRAY_BUFFER, boneBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Bones[0]) * Bones.size(), &Bones[0], GL_STATIC_DRAW);
glVertexAttribIPointer(5, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0);
glEnableVertexAttribArray(6);
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)16);
///////////////////////////////
for (unsigned int i = 0 ; i < ARRAY_SIZE_IN_ELEMENTS(m_boneLocation) ; i++) {
char Name[128];
sprintf(Name, "gBones[%d]", i);
GLuint Location = glGetUniformLocation(programID, Name);
m_boneLocation[i] = Location;
}
///////////////////////////////////
//indices buffer
glGenBuffers(1, &elementbuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW);
//gen uniforms
glUseProgram(programID);
LightID = glGetUniformLocation(programID, "LightPosition_worldspace");
LayerNumID1 = glGetUniformLocation(programID, "layer_index1");
LayerNumID2 = glGetUniformLocation(programID, "layer_index2");
LayerNumID3 = glGetUniformLocation(programID, "layer_index3");
gBones = glGetUniformLocation(programID, "gBones");
Before rendering, I call BoneTransform, creating a blank identity matrix to send to the ReadNodeHeirarchy() function, calculating the new vertices via scaling, translation and rotation based on time and nodes, and then filling the 4x4matrix Transforms with the final vertex location:
std::vector<aiMatrix4x4> Transforms;
BoneTransform(clock1.getElapsedTime().asSeconds(), Transforms);
void ChessPiece::BoneTransform(float TimeInSeconds, std::vector<aiMatrix4x4>& Transforms)
{
aiMatrix4x4 Identity;
Identity[0][0] = 1.0f; Identity[0][1] = 0.0f; Identity[0][2] = 0.0f; Identity[0][3] = 0.0f;
Identity[1][0] = 0.0f; Identity[1][1] = 1.0f; Identity[1][2] = 0.0f; Identity[1][3] = 0.0f;
Identity[2][0] = 0.0f; Identity[2][1] = 0.0f; Identity[2][2] = 1.0f; Identity[2][3] = 0.0f;
Identity[3][0] = 0.0f; Identity[3][1] = 0.0f; Identity[3][2] = 0.0f; Identity[3][3] = 1.0f;
float TicksPerSecond = scene->mAnimations[0]->mTicksPerSecond != 0 ?
scene->mAnimations[0]->mTicksPerSecond : 25.0f;
float TimeInTicks = TimeInSeconds * TicksPerSecond;
float AnimationTime = fmod(TimeInTicks, scene->mAnimations[0]->mDuration);
ReadNodeHeirarchy(AnimationTime, scene->mRootNode, Identity);
Transforms.resize(m_NumBones);
for (uint i = 0 ; i < m_NumBones ; i++) {
Transforms[i] = m_BoneInfo[i].FinalTransformation;
}
}
void ChessPiece::ReadNodeHeirarchy(float AnimationTime, const aiNode* pNode, const aiMatrix4x4& ParentTransform)
{
std::string NodeName(pNode->mName.data);
const aiAnimation* pAnimation = scene->mAnimations[0];
aiMatrix4x4 NodeTransformation(pNode->mTransformation);
const aiNodeAnim* pNodeAnim = FindNodeAnim(pAnimation, NodeName);
if (pNodeAnim) {
// Interpolate scaling and generate scaling transformation matrix
aiVector3D Scaling;
CalcInterpolatedScaling(Scaling, AnimationTime, pNodeAnim);
aiMatrix4x4 ScalingM;
// ScalingM.InitScaleTransform(Scaling.x, Scaling.y, Scaling.z);
ScalingM[0][0] = Scaling.x; ScalingM[0][1] = 0.0f; ScalingM[0][2] = 0.0f; ScalingM[0][3] = 0.0f;
ScalingM[1][0] = 0.0f; ScalingM[1][1] = Scaling.y; ScalingM[1][2] = 0.0f; ScalingM[1][3] = 0.0f;
ScalingM[2][0] = 0.0f; ScalingM[2][1] = 0.0f; ScalingM[2][2] = Scaling.z; ScalingM[2][3] = 0.0f;
ScalingM[3][0] = 0.0f; ScalingM[3][1] = 0.0f; ScalingM[3][2] = 0.0f; ScalingM[3][3] = 1.0f;
// Interpolate rotation and generate rotation transformation matrix
aiQuaternion RotationQ;
CalcInterpolatedRotation(RotationQ, AnimationTime, pNodeAnim);
aiMatrix4x4 RotationM = aiMatrix4x4(RotationQ.GetMatrix());
// Interpolate translation and generate translation transformation matrix
aiVector3D Translation;
CalcInterpolatedPosition(Translation, AnimationTime, pNodeAnim);
aiMatrix4x4 TranslationM;
//TranslationM.InitTranslationTransform(Translation.x, Translation.y, Translation.z);
TranslationM[0][0] = 1.0f; TranslationM[0][1] = 0.0f; TranslationM[0][2] = 0.0f; TranslationM[0][3] = Translation.x;
TranslationM[1][0] = 0.0f; TranslationM[1][1] = 1.0f; TranslationM[1][2] = 0.0f; TranslationM[1][3] = Translation.y;
TranslationM[2][0] = 0.0f; TranslationM[2][1] = 0.0f; TranslationM[2][2] = 1.0f; TranslationM[2][3] = Translation.z;
TranslationM[3][0] = 0.0f; TranslationM[3][1] = 0.0f; TranslationM[3][2] = 0.0f; TranslationM[3][3] = 1.0f;
// Combine the above transformations
NodeTransformation = TranslationM * RotationM * ScalingM;
std::cout<<"node matrix: "<<std::endl;
for(int i = 0; i < 4; i++)
{
for(int j = 0; j < 4; j++)
{
//for debugging purposes////
//std::cout<<NodeTransformation[i][j]<<std::endl;
}
}
}
aiMatrix4x4 GlobalTransformation = ParentTransform * NodeTransformation;
if (m_BoneMapping.find(NodeName) != m_BoneMapping.end()) {
uint BoneIndex = m_BoneMapping[NodeName];
m_BoneInfo[BoneIndex].FinalTransformation = m_GlobalInverseTransform * GlobalTransformation *
m_BoneInfo[BoneIndex].BoneOffset;
}
for (uint i = 0 ; i < pNode->mNumChildren ; i++) {
ReadNodeHeirarchy(AnimationTime, pNode->mChildren[i], GlobalTransformation);
}
}
void ChessPiece::CalcInterpolatedRotation(aiQuaternion& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
{
if (pNodeAnim->mNumRotationKeys == 1) {
Out = pNodeAnim->mRotationKeys[0].mValue;
return;
}
uint RotationIndex = FindRotation(AnimationTime, pNodeAnim);
uint NextRotationIndex = (RotationIndex + 1);
assert(NextRotationIndex < pNodeAnim->mNumRotationKeys);
float DeltaTime = pNodeAnim->mRotationKeys[NextRotationIndex].mTime - pNodeAnim->mRotationKeys[RotationIndex].mTime;
float Factor = (AnimationTime- (float)pNodeAnim->mRotationKeys[RotationIndex].mTime) / DeltaTime;
assert(Factor >= 0.0f && Factor <= 1.0f);
const aiQuaternion& StartRotationQ = pNodeAnim->mRotationKeys[RotationIndex].mValue;
const aiQuaternion& EndRotationQ = pNodeAnim->mRotationKeys[NextRotationIndex].mValue;
aiQuaternion::Interpolate(Out, StartRotationQ, EndRotationQ, Factor);
Out = Out.Normalize();
}
uint ChessPiece::FindRotation(float AnimationTime, const aiNodeAnim* pNodeAnim)
{
assert(pNodeAnim->mNumRotationKeys > 0);
for (uint i = 0 ; i < pNodeAnim->mNumRotationKeys - 1 ; i++) {
if (AnimationTime < (float)pNodeAnim->mRotationKeys[i + 1].mTime) {
return i;
}
}
assert(0);
}
uint ChessPiece::FindPosition(float AnimationTime, const aiNodeAnim* pNodeAnim)
{
for (uint i = 0 ; i < pNodeAnim->mNumPositionKeys - 1 ; i++) {
if (AnimationTime < (float)pNodeAnim->mPositionKeys[i + 1].mTime) {
return i;
}
}
assert(0);
return 0;
}
uint ChessPiece::FindScaling(float AnimationTime, const aiNodeAnim* pNodeAnim)
{
assert(pNodeAnim->mNumScalingKeys > 0);
for (uint i = 0 ; i < pNodeAnim->mNumScalingKeys - 1 ; i++) {
if (AnimationTime < (float)pNodeAnim->mScalingKeys[i + 1].mTime) {
return i;
}
}
assert(0);
return 0;
}
void ChessPiece::CalcInterpolatedPosition(aiVector3D& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
{
if (pNodeAnim->mNumPositionKeys == 1) {
Out = pNodeAnim->mPositionKeys[0].mValue;
return;
}
uint PositionIndex = FindPosition(AnimationTime, pNodeAnim);
uint NextPositionIndex = (PositionIndex + 1);
assert(NextPositionIndex < pNodeAnim->mNumPositionKeys);
float DeltaTime = (float)(pNodeAnim->mPositionKeys[NextPositionIndex].mTime - pNodeAnim->mPositionKeys[PositionIndex].mTime);
float Factor = (AnimationTime- (float)pNodeAnim->mRotationKeys[RotationIndex].mTime) / DeltaTime;
assert(Factor >= 0.0f && Factor <= 1.0f);
const aiVector3D& Start = pNodeAnim->mPositionKeys[PositionIndex].mValue;
const aiVector3D& End = pNodeAnim->mPositionKeys[NextPositionIndex].mValue;
aiVector3D Delta = End - Start;
Out = Start + Factor * Delta;
}
void ChessPiece::CalcInterpolatedScaling(aiVector3D& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
{
if (pNodeAnim->mNumScalingKeys == 1) {
Out = pNodeAnim->mScalingKeys[0].mValue;
return;
}
uint ScalingIndex = FindScaling(AnimationTime, pNodeAnim);
uint NextScalingIndex = (ScalingIndex + 1);
assert(NextScalingIndex < pNodeAnim->mNumScalingKeys);
float DeltaTime = (float)(pNodeAnim->mScalingKeys[NextScalingIndex].mTime - pNodeAnim->mScalingKeys[ScalingIndex].mTime);
float Factor = (AnimationTime- (float)pNodeAnim->mRotationKeys[RotationIndex].mTime) / DeltaTime;
assert(Factor >= 0.0f && Factor <= 10.0f);
const aiVector3D& Start = pNodeAnim->mScalingKeys[ScalingIndex].mValue;
const aiVector3D& End = pNodeAnim->mScalingKeys[NextScalingIndex].mValue;
aiVector3D Delta = End - Start;
Out = Start + Factor * Delta;
}
I worry I'm doing something wrong in the above functions. If i print out the transformations, I do get occasional outputs such as "-9.53674e-07"
Then (and I'm unsure about this part as well, it's a workaround. I'm not entirely certain how to pass an aiMatrix4x4 to glUniformMatrix4fv without converting it like this:
With that done, I start the rendering process:
EDIT***For reference, this is where the mistakes begin. Don't try to convert these and simply pass in the aiMatrix Transforms and use the [0][0] value of Transforms within the SetBones() function.
std::vector<float> floatTransforms;
for(int i = 0; i < Transforms.size(); i++)
{
for(int j = 0; j < 4; j++)
{
for(int k = 0; k < 4; k++)
{
floatTransforms.emplace_back(Transforms[i][j][k]);
}
}
}
for(int m = 0; m < floatTransforms.size(); m++)
{
SetBoneTransform(m, floatTransforms[m]);
}
void ChessPiece::SetBoneTransform(uint Index, const float &Transform)
{
glUniformMatrix4fv(m_boneLocation[Index], 1, GL_TRUE, &Transform);
}
Then, to render:
void ChessPiece::Enable(GLuint &texturearray)
{
glUseProgram(programID);
//diffuse texture////
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, texturearray);
glUniform1i(LayerNumID1, imageIndex.x);
//normal texture////
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D_ARRAY, texturearray);
glUniform1i(LayerNumID2, imageIndex.y);
//specular texture////
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D_ARRAY, texturearray);
glUniform1i(LayerNumID3, imageIndex.z);
//vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,(void*)0);
//UVs
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,0,(void*)0);
//normals
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,0,(void*)0);
//tangents
glEnableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, tangentbuffer);
glVertexAttribPointer(3,3,GL_FLOAT,GL_FALSE,0,(void*)0);
//bitangents
glEnableVertexAttribArray(4);
glBindBuffer(GL_ARRAY_BUFFER, bitangentbuffer);
glVertexAttribPointer(4,3,GL_FLOAT,GL_FALSE,0,(void*)0);
//Bones. And I'm not 100% certain this is correct////
glEnableVertexAttribArray(5);
glBindBuffer(GL_ARRAY_BUFFER, boneBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Bones[0]) * Bones.size(), &Bones[0], GL_STATIC_DRAW);
glVertexAttribIPointer(5, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0);
glEnableVertexAttribArray(6);
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)16);
//Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
}
void ChessPiece::Render(GLuint &texturearray, Controls &control)
{
glm::mat4 ViewMatrix = control.ViewMatrix;
glm::mat4 ModelMatrix = glm::translate(location)* glm::scale(modelScale);
glm::mat3 ModelView3x3Matrix = glm::mat3(ViewMatrix * ModelMatrix);
glm::mat4 MVP = control.ProjectionMatrix * ViewMatrix * ModelMatrix;
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, &ModelMatrix[0][0]);
glUniformMatrix4fv(ViewMatrixID, 1, GL_FALSE, &ViewMatrix[0][0]);
glUniformMatrix3fv(ModelView3x3MatrixID, 1, GL_FALSE, &ModelView3x3Matrix[0][0]);
glm::vec3 lightPosF = location + lightPos;
glUniform3f(LightID, lightPosF.x, lightPosF.y, lightPosF.z);
glDrawElements(GL_TRIANGLES,indices.size(),GL_UNSIGNED_SHORT,(void*)0);
}
My shader i'm not entirely certain is correct either. If i calculate gl_Position against the bone transform nothing renders at all (if I'm calculating the vertices incorrectly above, this isn't surprising and should be expected). Additionally. I'm not certain how to go about calculating the normals against it either, though I don't think that's necessary to simply render it (or am I wrong here? I'd imagine it would just look funny in the lighting until I fix that).
#version 330 core
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;
layout(location = 2) in vec3 vertexNormal_modelspace;
layout(location = 3) in vec3 vertexTangent_modelspace;
layout(location = 4) in vec3 vertexBitangent_modelspace;
layout (location = 5) in ivec4 BoneIDs;
layout (location = 6) in vec4 Weights;
out vec2 UV;
out vec3 Position_worldspace;
out vec3 EyeDirection_cameraspace;
out vec3 LightDirection_cameraspace;
out vec3 LightDirection_tangentspace;
out vec3 EyeDirection_tangentspace;
const int MAX_BONES = 100;
uniform mat4 MVP;
uniform mat4 V;
uniform mat4 M;
uniform mat3 MV3x3;
uniform vec3 LightPosition_worldspace;
uniform mat4 gBones[MAX_BONES];
void main(){
mat4 BoneTransform = gBones[BoneIDs[0]] * Weights[0];
BoneTransform += gBones[BoneIDs[1]] * Weights[1];
BoneTransform += gBones[BoneIDs[2]] * Weights[2];
BoneTransform += gBones[BoneIDs[3]] * Weights[3];
vec4 PosL = BoneTransform * vec4(vertexPosition_modelspace, 1.0);
//this one renders nothing////
//gl_Position = MVP * PosL;
gl_Position = MVP * vec4(vertexPosition_modelspace,1);
Position_worldspace = (M * PosL).xyz;
vec3 vertexPosition_cameraspace = ( V * M * vec4(vertexPosition_modelspace,1)).xyz;
EyeDirection_cameraspace = vec3(0,0,0) - vertexPosition_cameraspace;
vec3 LightPosition_cameraspace = ( V * vec4(LightPosition_worldspace,1)).xyz;
LightDirection_cameraspace = LightPosition_cameraspace + EyeDirection_cameraspace;
UV = vertexUV;
vec3 vertexTangent_cameraspace = MV3x3 * vertexTangent_modelspace;
vec3 vertexBitangent_cameraspace = MV3x3 * vertexBitangent_modelspace;
vec3 vertexNormal_cameraspace = MV3x3 * vertexNormal_modelspace;
mat3 TBN = transpose(mat3(
vertexTangent_cameraspace,
vertexBitangent_cameraspace,
vertexNormal_cameraspace
));
LightDirection_tangentspace = TBN * LightDirection_cameraspace;
EyeDirection_tangentspace = TBN * EyeDirection_cameraspace;
}
Well, that's it I appreciate anyone who has read this far, you're truly a remarkable specimen and a benefit to the human race, and I owe you a giant debt of gratitude, a huge thank you, and a pony If Haegar answers this, I think I owe him my first-born child at this point
Final notes:
There's a good chance I'm converting some of these values incorrectly. I'm not quite certain how to interface Assimp with glm with OpenGL, so have simply defaulted to doing things by hand in a few places. I'll certainly dig into that at some point before implementing this code into my project, but if you have any suggestions, I would appreciate them.
Again, there are likely dozens of little things I'm doing wrong, and I genuinely appreciate those pointed out if you're feeling generous. If I'm going about things the wrong way on a larger scale, please feel free to note that as well. Hopefully it isn't a small minor typo or something. I can deal with the embarrassment of just not quite grasping the larger picture, but I've made one too many posts where I was simply making a stupid mistake.
If I've left out any vital functions or information, please let me know and I'll update it. It's almost 4am for me, so I've probably left out something vital .
Thanks again to anyone who can shed insight on this. I've really given it a thorough try on my own, but I'm out of ideas and resources and feeling rather desperate I haven't actually done any "game" programming in almost 3 weeks trying to move my project from OBJ models to animated collada ones.