Post a screenshot to give us a better idea of what's going on.
Opengl Spazzing out for no reason
I'll also add, as I said above, this is occuring in projects that worked perfectly fine before, but are now all spazzing out in the same way without any new changes.
If you are seeing displaced vertices and corruption over time, then you may have a heat or voltage issue. I have had it many times before, the last one being because my power unit could not give enough Watts to the GPU (and machine overall).
Here are some diagnosable examples from a random Google search:
http://www.playtool.com/pages/artifacts/artifacts.html
I had what happens in the second image, slow corruption over time. It was fixed by upgrading to a better PSU, and was not bad GPU memory.
This smells a lot like uninitialized memory or undefined behaviour to me.
Can you post code for where you load your matrices, your vertex data and how you submit draw calls (glDrawArrays, glDrawElements, etc)?
Get ready for code overload:
LOADING
bool MESH::Load(const char* filename)
{
Clear();
m_hasTexture = false;
m_numBones = 0;
std::vector<VECTOR3f> positions;
std::vector<VECTOR3f> normals;
std::vector<VECTOR2f> texCoords;
std::vector<VertexBoneData> bones;
std::vector<uint> indices;
uint numVertices = 0;
uint numIndices = 0;
// Use assimp to get data from the file
Assimp::Importer Importer;
m_pScene = m_Importer.ReadFile(filename, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs);
if (!m_pScene)
{
printf("Error parsing '%s': '%s'\n", filename, Importer.GetErrorString());
return false;
}
m_globalInverseTransform = m_pScene->mRootNode->mTransformation;
m_globalInverseTransform.Inverse();
// Make all vectors the appropriate size for the data
m_objects.resize(m_pScene->mNumMeshes);
for (uint i = 0; i < m_objects.size(); i++)
{
m_objects[i].numVertices = m_pScene->mMeshes[i]->mNumVertices;
m_objects[i].numIndices = m_pScene->mMeshes[i]->mNumFaces * 3;
m_objects[i].startVertex = numVertices;
m_objects[i].startIndex = numIndices;
numVertices += m_objects[i].numVertices;
numIndices += m_objects[i].numIndices;
}
positions.reserve(numVertices);
normals.reserve(numVertices);
texCoords.reserve(numVertices);
bones.resize(numVertices);
indices.reserve(numIndices);
// Initialize the meshes
int fuck = m_objects.size();
for (uint i = 0; i < m_objects.size(); i++)
{
const aiMesh* paiMesh = m_pScene->mMeshes[i];
const aiVector3D Zero3D(0.0f, 0.0f, 0.0f);
// Populate Vertex Attributes
for (uint j = 0; j < paiMesh->mNumVertices; j++)
{
const aiVector3D* aiTexCoord = paiMesh->HasTextureCoords(0) ? &(paiMesh->mTextureCoords[0][j]) : &Zero3D;
positions.push_back(VECTOR3f(paiMesh->mVertices[j].x, paiMesh->mVertices[j].y, paiMesh->mVertices[j].z));
normals.push_back(VECTOR3f(paiMesh->mNormals[j].x, paiMesh->mNormals[j].y, paiMesh->mNormals[j].z));
texCoords.push_back(VECTOR2f(aiTexCoord->x, aiTexCoord->y));
}
// Populate Indices
for (uint j = 0; j < paiMesh->mNumFaces; j++)
{
const aiFace& face = paiMesh->mFaces[j];
assert(face.mNumIndices == 3);
indices.push_back(face.mIndices[0]);
indices.push_back(face.mIndices[1]);
indices.push_back(face.mIndices[2]);
}
// Load Bones for current Mesh
for (uint j = 0; j < paiMesh->mNumBones; j++)
{
uint boneIndex = 0;
std::string boneName(paiMesh->mBones[j]->mName.data);
if (m_boneMapping.find(boneName) == m_boneMapping.end())
{
boneIndex = m_numBones;
m_numBones++;
m_boneInfo.push_back(BoneInfo());
m_boneInfo[boneIndex].BoneOffset = paiMesh->mBones[j]->mOffsetMatrix;
m_boneMapping[boneName] = boneIndex;
}
else
boneIndex = m_boneMapping[boneName];
m_boneMapping[boneName] = boneIndex;
m_boneInfo[boneIndex].BoneOffset = paiMesh->mBones[j]->mOffsetMatrix;
for (uint k = 0; k < paiMesh->mBones[j]->mNumWeights; k++)
{
uint VertexID = m_objects[i].startVertex + paiMesh->mBones[j]->mWeights[k].mVertexId;
float weight = paiMesh->mBones[j]->mWeights[k].mWeight;
bones[VertexID].AddBoneData(boneIndex, weight);
}
}
}
// Load the material (only using one for now)
// We assume the texture file is in the same directory as the model file
const aiMaterial* pMaterial = m_pScene->mMaterials[0];
aiString path;
if (pMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &path, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS)
{
std::string::size_type startIndex = std::string(path.C_Str()).find_last_of("\\");
if (startIndex == std::string::npos)
startIndex = std::string(path.C_Str()).find_last_of("/");
std::string texFilename;
if (startIndex != std::string::npos)
{
texFilename = std::string(path.C_Str()).substr(startIndex + 1);
}
else
texFilename = path.C_Str();
// get texture path, and make it lower case
std::string texPath = std::string(filename).substr(0, std::string(filename).find_last_of("/")) + "/";
for (uint i = 0; i < texFilename.length(); i++)
texPath += texFilename[i];
// Load Texture
if (!m_texture.Load(texPath.c_str()))
{
printf("Failed to load texture: \"%s\"", texPath.c_str());
}
else
m_hasTexture = true;
}
const aiNodeAnim* pNodeAnim = NULL;
uint i = 0;
while (!pNodeAnim)
{
pNodeAnim = FindNodeAnim(m_pScene->mAnimations[0], m_pScene->mRootNode->mChildren[i]->mName.data);
i++;
if (i > 500) // Too many loops
continue;
}
//FindNodeAnim(pAnimation, nodeName);
//m_pScene->mAnimations[0]->mChannels->
if (pNodeAnim)
{
m_animInfo.numKeyframes = pNodeAnim->mNumPositionKeys;
m_animInfo.frameDuration = m_pScene->mAnimations[0]->mDuration / (pNodeAnim->mNumPositionKeys);
}
// Negate the last frame, as it is the same as the first
m_animInfo.MaxAnimTime = m_animInfo.frameDuration * (m_animInfo.numKeyframes - 1);
// Initialize buffers with the data
InitBuffers(positions, normals, texCoords, indices, bones);
return true;
}
bool MESH::InitBuffers(const std::vector<VECTOR3f>& positions,
const std::vector<VECTOR3f>& normals,
const std::vector<VECTOR2f>& texCoords,
const std::vector<uint>& indices,
const std::vector<VertexBoneData>& bones)
{
// Create the VAO
glGenVertexArrays(1, &m_VAO);
glBindVertexArray(m_VAO);
// Create the buffers for the vertices attributes
glGenBuffers(ArraySizeInElements(m_buffers), m_buffers);
// Generate and populate the buffers with vertex attributes and the indices
glBindBuffer(GL_ARRAY_BUFFER, m_buffers[POS_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(positions[0]) * positions.size(), &positions[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(POSITION_LOCATION);
glVertexAttribPointer(POSITION_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, m_buffers[TEXCOORD_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords[0]) * texCoords.size(), &texCoords[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(TEX_COORD_LOCATION);
glVertexAttribPointer(TEX_COORD_LOCATION, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, m_buffers[NORMAL_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(normals[0]) * normals.size(), &normals[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(NORMAL_LOCATION);
glVertexAttribPointer(NORMAL_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, 0);
if (m_numBones > 0)
{
glBindBuffer(GL_ARRAY_BUFFER, m_buffers[BONE_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(bones[0]) * bones.size(), &bones[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(BONE_ID_LOCATION);
glVertexAttribIPointer(BONE_ID_LOCATION, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0);
glEnableVertexAttribArray(BONE_WEIGHT_LOCATION);
glVertexAttribPointer(BONE_WEIGHT_LOCATION, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)16);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_buffers[INDEX_BUFFER]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), &indices[0], GL_STATIC_DRAW);
glBindVertexArray(0);
return true;
}
void MESH::Clear()
{
if (m_buffers[0] != 0)
glDeleteBuffers(ArraySizeInElements(m_buffers), m_buffers);
if (m_VAO != 0)
{
glDeleteVertexArrays(1, &m_VAO);
m_VAO = 0;
}
if (m_objects.size() > 0)
m_objects.empty();
}
DRAWING
void GAME::RenderModels()
{
// Draw Geometry / Scene Objects
m_pipeline.MatrixMode(MODEL_MATRIX);
m_pipeline.PushMatrix();
stdShader->Bind();
m_pipeline.CalcMatrices(stdShader->GetProgramID());
plane.Render();
stdShader->Unbind();
skinShader->Bind();
m_pipeline.RotateY(angle);
m_pipeline.Scale(0.10f);
//m_pipeline.Translate(0.0f, 0.0f, -6.0f);
//m_pipeline.Translate(0.0f, 1.0f, 0.0f);
std::vector<MATRIX4f> transforms;
float time = m_timer.m_secsPassedSinceStart / ninja.GetAnimTime() * 0.5f;
ninja.BoneTransform(time, transforms);
for (uint i = 0; i < transforms.size(); i++)
glUniformMatrix4fv(m_boneLocations[i], 1, GL_TRUE, (const GLfloat*)transforms[i].m_matrix);
m_pipeline.Translate(0.0f, 0.0f, 1.0f);
m_pipeline.CalcMatrices(skinShader->GetProgramID());
ninja.Render();
skinShader->Unbind();
m_pipeline.PopMatrix();
}
void MESH::Render()
{
glBindVertexArray(m_VAO);
if (m_hasTexture)
m_texture.Bind(GL_TEXTURE0);
for (uint i = 0; i < m_objects.size(); i++)
{
glDrawElementsBaseVertex(GL_TRIANGLES,
m_objects[i].numIndices,
GL_UNSIGNED_INT,
(void*)(sizeof(uint) * m_objects[i].startIndex),
m_objects[i].startVertex);
}
if (m_hasTexture)
m_texture.Unbind(GL_TEXTURE0);
glBindVertexArray(0);
}
// This function simply has the bones be where they're supposed to be depending on the time in seconds passed in. At the moment its used as a loop
void MESH::BoneTransform(float& TimeInSeconds, std::vector<MATRIX4f>& transforms)
{
MATRIX4f identity;
identity.LoadIdentity();
float ticksPerSecond = (float)(m_pScene->mAnimations[0]->mTicksPerSecond != 0 ? m_pScene->mAnimations[0]->mTicksPerSecond : 25.0f);
float TimeInTicks = TimeInSeconds * ticksPerSecond;
//float AnimationTime = fmod(TimeInTicks, (float)m_pScene->mAnimations[0]->mDuration);
float AnimationTime = fmod(TimeInTicks, m_animInfo.MaxAnimTime);
ReadNodeHeirarchy(AnimationTime, m_pScene->mRootNode, identity);
transforms.resize(m_numBones);
for (uint i = 0; i < m_numBones; i++)
transforms[i] = m_boneInfo[i].FinalTransformation;
}
void MESH::ReadNodeHeirarchy(float AnimationTime, const aiNode* pNode, const MATRIX4f& ParentTransform)
{
std::string nodeName(pNode->mName.data);
const aiAnimation* pAnimation = m_pScene->mAnimations[0];
MATRIX4f NodeTransformation(pNode->mTransformation);
const aiNodeAnim* pNodeAnim = FindNodeAnim(pAnimation, nodeName);
if (pNodeAnim)
{
// Interpolate scaling and generate scaling transformation matrix
aiVector3D Scaling;
CalcInterpolatedScaling(Scaling, AnimationTime, pNodeAnim);
MATRIX4f ScalingM;
ScalingM.InitScale(Scaling.x, Scaling.y, Scaling.z);
// Interpolate rotation and generate rotation transformation matrix
aiQuaternion RotationQ;
CalcInterpolatedRotation(RotationQ, AnimationTime, pNodeAnim);
MATRIX4f RotationM = MATRIX4f(RotationQ.GetMatrix());
// Interpolate translation and generate translation transformation matrix
aiVector3D Translation;
CalcInterpolatedPosition(Translation, AnimationTime, pNodeAnim);
MATRIX4f TranslationM;
TranslationM.InitTranslate(Translation.x, Translation.y, Translation.z);
// Combine the above transformations
NodeTransformation = TranslationM * RotationM * ScalingM;
}
MATRIX4f 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);
}
Now this is where I suspect the problem may lie, however I don't really understand it:
void MESH::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->mPositionKeys[PositionIndex].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 MESH::CalcInterpolatedRotation(aiQuaternion& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
{
// we need at least two values to interpolate...
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 = (float)(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();
}
void MESH::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->mScalingKeys[ScalingIndex].mTime) / DeltaTime;
//assert(Factor >= 0.0f && Factor <= 1.0f);
const aiVector3D& Start = pNodeAnim->mScalingKeys[ScalingIndex].mValue;
const aiVector3D& End = pNodeAnim->mScalingKeys[NextScalingIndex].mValue;
aiVector3D Delta = End - Start;
Out = Start + Factor * Delta;
}
You will notice i commented out the assertion because i do actually get values outside 0 and 1. I must confess I don't really understand this function.
Any help is greatly appreciated!
You will notice i commented out the assertion because i do actually get values outside 0 and 1.
So what do you think will happen to Factor at the end of the animation, or if 2 keyframes are on the same time-stamp, etc.?
L. Spiro
Sorry everyone I'm retarded.
My shader:
void main()
{
mat4 BoneTransform;
BoneTransform += gBones[BoneIDs[0]] * Weights[0];
BoneTransform += gBones[BoneIDs[1]] * Weights[1];
BoneTransform += gBones[BoneIDs[2]] * Weights[2];
BoneTransform += gBones[BoneIDs[3]] * Weights[3];
bonetransform is just full of nothing. Changed to:
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];
All better
So, it was well-defined undefined behavior after all :)
It's something we've all been struggling with over the years..