I'm using the following code to load in an .obj file using Assimp. Something goes wrong at the TextureCoordinates. The aiVector3D I try to access exists, but as soon as I store the values in a temp variable, it crashes my application.
Here's the code I use:
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(filename,
aiProcess_CalcTangentSpace |
aiProcess_Triangulate |
aiProcess_JoinIdenticalVertices |
aiProcess_SortByPType);
if (!scene)
{
printf("[ASSIMP] ");
printf(importer.GetErrorString());
printf("\n");
return nullptr;
}
Mesh* newMesh = new Mesh();
unsigned int vertexCount = scene->mMeshes[0]->mNumVertices;
unsigned int triangleCount = scene->mMeshes[0]->mNumFaces;
bool hasUv = scene->mMeshes[0]->HasTextureCoords(0);
newMesh->vertexCount = vertexCount;
newMesh->triangleCount = triangleCount;
newMesh->m_Vertices = new Vertex[vertexCount];
newMesh->m_Triangles = new Triangle[triangleCount];
for (unsigned int i = 0; i < vertexCount; i++) {
aiVector3D vertexPosition = scene->mMeshes[0]->mVertices[i];
aiVector3D vertexNormal = scene->mMeshes[0]->mNormals[i];
newMesh->m_Vertices[i].pos = glm::vec3(vertexPosition.x, vertexPosition.y, vertexPosition.z);
newMesh->m_Vertices[i].normal = glm::vec3(vertexNormal.x, vertexNormal.y, vertexNormal.z);
if (hasUv) {
aiVector3D uvCoordinates = scene->mMeshes[0]->mTextureCoords[i][0];
printf("uvCoordinates: %f %f %f\n", uvCoordinates.x, uvCoordinates.y, uvCoordinates.z);
newMesh->m_Vertices[i].u = uvCoordinates.x;
newMesh->m_Vertices[i].v = uvCoordinates.y;
}
}
for (unsigned int i = 0; i < triangleCount; i++) {
aiFace face = scene->mMeshes[0]->mFaces[i];
for (int j = 0; j < 3; j++) {
Triangle* tri = &newMesh->m_Triangles[i];
tri->vertex[j] = &newMesh->m_Vertices[face.mIndices[j]];
if (tri->vertex[0]->normal.y == 1.0f || tri->vertex[0]->normal.y == -1.0f) {
tri->color = glm::vec3(0.2f, 0.2f, 0.2f);
}
else
{
tri->color = glm::vec3(1.0f, 0.0f, 0.0f);
}
}
}
It crashes at the line printf("uvCoordinates: %f %f %f\n", uvCoordinates.x, uvCoordinates.y, uvCoordinates.z);, but if I remove this line, it crashes at newMesh->m_Vertices[i].u = uvCoordinates.x;. If I comment out the printf(), set both the u and v of the vertex to 0.0f and not use uvCoordinates at all, it still crashes on the newMesh->m_Vertices[i].u = uvCoordinates.x; line. If I leave the printf() uncommented, it prints the values of the uvCoordinates, but throws an access violation after.
I'm honestly out of ideas here. Here's a screenshot showing what I explained.
scene->mMeshes[0]->mTextureCoords[i][0]; is first texture coordinates for set i. What you wanted is to get first set of texture coordinates - so it should be scene->mMeshes[0]->mTextureCoords[0][i];
Related
How do I make this blob. I'm kind of confused on how to make the mesh rise up like that when I click the mouse button
Here is my mesh code. Its not too important to read through it. I just want to know how I get the blob by rising the the mesh:
{
QuadMesh qm; // The new quad mesh to be returned
qm.numVertices = 0;
qm.vertices = NULL;
qm.numQuads = 0;
qm.quads = NULL;
qm.numFacesDrawn = 0;
qm.maxMeshSize = maxMeshSize < minMeshSize ? minMeshSize : maxMeshSize;
CreateMemoryQM(&qm);
// Set up default material used for the mesh
qm.mat_ambient[0] = 0.0;
qm.mat_ambient[1] = 0.0;
qm.mat_ambient[2] = 0.0;
qm.mat_ambient[3] = 1.0;
qm.mat_specular[0] = 0.0;
qm.mat_specular[1] = 0.0;
qm.mat_specular[2] = 0.0;
qm.mat_specular[3] = 1.0;
qm.mat_diffuse[0] = 0.75;
qm.mat_diffuse[1] = 0.5;
qm.mat_diffuse[2] = 0.0;
qm.mat_diffuse[3] = 1.0;
qm.mat_shininess[0] = 0.0;
return qm;
}
void SetMaterialQM(QuadMesh* qm, Vector3D ambient, Vector3D diffuse, Vector3D specular, double shininess)
{
qm->mat_ambient[0] = ambient.x;
qm->mat_ambient[1] = ambient.y;
qm->mat_ambient[2] = ambient.z;
qm->mat_ambient[3] = 1.0;
qm->mat_specular[0] = specular.x;
qm->mat_specular[1] = specular.y;
qm->mat_specular[2] = specular.z;
qm->mat_specular[3] = 1.0;
qm->mat_diffuse[0] = diffuse.x;
qm->mat_diffuse[1] = diffuse.y;
qm->mat_diffuse[2] = diffuse.z;
qm->mat_diffuse[3] = 1.0;
qm->mat_shininess[0] = (float)shininess;
}
// Allocate dynamic arrays.
bool CreateMemoryQM(QuadMesh* qm)
{
const int maxVertices = (qm->maxMeshSize + 1) * (qm->maxMeshSize + 1);
qm->vertices = (MeshVertex *)malloc(sizeof(MeshVertex) * maxVertices);
if (qm->vertices == NULL)
{
return false;
}
const int maxQuads = qm->maxMeshSize * qm->maxMeshSize;
qm->quads = (MeshQuad *)malloc(sizeof(MeshQuad) * maxQuads);
if (qm->quads == NULL)
{
return false;
}
return true;
}
// Fills the array of vertices and the array of quads.
bool InitMeshQM(QuadMesh* qm, int meshSize, Vector3D origin, double meshLength, double meshWidth, Vector3D dir1, Vector3D dir2)
{
Vector3D o;
double sf1, sf2;
Vector3D v1,v2;
v1.x = dir1.x;
v1.y = dir1.y;
v1.z = dir1.z;
sf1 = meshLength/meshSize;
ScalarMul(&v1, (float)sf1, &v1);
v2.x = dir2.x;
v2.y = dir2.y;
v2.z = dir2.z;
sf2 = meshWidth/meshSize;
ScalarMul(&v2, (float)sf2, &v2);
Vector3D meshpt;
// Build Vertices
qm->numVertices=(meshSize+1)*(meshSize+1);
int currentVertex = 0;
// Starts at front left corner of mesh
Set(&o, origin.x,origin.y,origin.z);
for(int i=0; i< meshSize+1; i++)
{
for(int j=0; j< meshSize+1; j++)
{
// compute vertex position along mesh row (along x direction)
meshpt.x = o.x + j * v1.x;
meshpt.y = o.y + j * v1.y;
meshpt.z = o.z + j * v1.z;
Set(&qm->vertices[currentVertex].position, meshpt.x,meshpt.y,meshpt.z);
currentVertex++;
}
// go to next row in mesh (negative z direction)
Add(&o, &v2, &o);
}
// Build Quad Polygons
qm->numQuads=(meshSize)*(meshSize);
int currentQuad=0;
for (int j=0; j < meshSize; j++)
{
for (int k=0; k < meshSize; k++)
{
// Counterclockwise order
qm->quads[currentQuad].vertices[0]=&qm->vertices[j* (meshSize+1)+k];
qm->quads[currentQuad].vertices[1]=&qm->vertices[j* (meshSize+1)+k+1];
qm->quads[currentQuad].vertices[2]=&qm->vertices[(j+1)*(meshSize+1)+k+1];
qm->quads[currentQuad].vertices[3]=&qm->vertices[(j+1)*(meshSize+1)+k];
currentQuad++;
}
}
ComputeNormalsQM(qm);
return true;
}
// Draw the mesh by drawing all quads.
void DrawMeshQM(QuadMesh* qm, int meshSize)
{
int currentQuad=0;
glMaterialfv(GL_FRONT, GL_AMBIENT, qm->mat_ambient);
glMaterialfv(GL_FRONT, GL_SPECULAR, qm->mat_specular);
glMaterialfv(GL_FRONT, GL_DIFFUSE, qm->mat_diffuse);
glMaterialfv(GL_FRONT, GL_SHININESS, qm->mat_shininess);
for(int j=0; j < meshSize; j++)
{
for(int k=0; k < meshSize; k++)
{
glBegin(GL_QUADS);
glNormal3f(qm->quads[currentQuad].vertices[0]->normal.x,
qm->quads[currentQuad].vertices[0]->normal.y,
qm->quads[currentQuad].vertices[0]->normal.z);
glVertex3f(qm->quads[currentQuad].vertices[0]->position.x,
qm->quads[currentQuad].vertices[0]->position.y,
qm->quads[currentQuad].vertices[0]->position.z);
glNormal3f(qm->quads[currentQuad].vertices[1]->normal.x,
qm->quads[currentQuad].vertices[1]->normal.y,
qm->quads[currentQuad].vertices[1]->normal.z);
glVertex3f(qm->quads[currentQuad].vertices[1]->position.x,
qm->quads[currentQuad].vertices[1]->position.y,
qm->quads[currentQuad].vertices[1]->position.z);
glNormal3f(qm->quads[currentQuad].vertices[2]->normal.x,
qm->quads[currentQuad].vertices[2]->normal.y,
qm->quads[currentQuad].vertices[2]->normal.z);
glVertex3f(qm->quads[currentQuad].vertices[2]->position.x,
qm->quads[currentQuad].vertices[2]->position.y,
qm->quads[currentQuad].vertices[2]->position.z);
glNormal3f(qm->quads[currentQuad].vertices[3]->normal.x,
qm->quads[currentQuad].vertices[3]->normal.y,
qm->quads[currentQuad].vertices[3]->normal.z);
glVertex3f(qm->quads[currentQuad].vertices[3]->position.x,
qm->quads[currentQuad].vertices[3]->position.y,
qm->quads[currentQuad].vertices[3]->position.z);
glEnd();
currentQuad++;
}
}
}
// Deallocate dynamic arrays.
void FreeMemoryQM(QuadMesh* qm)
{
if (qm->vertices != NULL)
free(qm->vertices);
qm->vertices=NULL;
qm->numVertices=0;
if (qm->quads != NULL)
free(qm->quads);
qm->quads=NULL;
qm->numQuads=0;
}
// Use cross-products to compute the normal vector at each vertex
void ComputeNormalsQM(QuadMesh* qm)
{
int currentQuad=0;
for(int j=0; j < qm->maxMeshSize; j++)
{
for(int k=0; k < qm->maxMeshSize; k++)
{
Vector3D n0, n1, n2, n3;
Vector3D e0, e1, e2, e3;
for (int i=0; i < 4; i++)
{
LoadZero(&qm->quads[currentQuad].vertices[i]->normal);
}
Subtract(&qm->quads[currentQuad].vertices[1]->position, &qm->quads[currentQuad].vertices[0]->position, &e0);
Subtract(&qm->quads[currentQuad].vertices[2]->position, &qm->quads[currentQuad].vertices[1]->position, &e1);
Subtract(&qm->quads[currentQuad].vertices[3]->position, &qm->quads[currentQuad].vertices[2]->position, &e2);
Subtract(&qm->quads[currentQuad].vertices[0]->position, &qm->quads[currentQuad].vertices[3]->position, &e3);
Normalize(&e0);
Normalize(&e1);
Normalize(&e2);
Normalize(&e3);
Vector3D w; // Working vector;
Negate(&e3, &w);
CrossProduct(&e0, &w, &n0);
Normalize(&n0);
Add(&qm->quads[currentQuad].vertices[0]->normal, &n0, &qm->quads[currentQuad].vertices[0]->normal);
Negate(&e0, &w);
CrossProduct(&e1, &w, &n1);
Normalize(&n1);
Add(&qm->quads[currentQuad].vertices[1]->normal, &n1, &qm->quads[currentQuad].vertices[1]->normal);
Negate(&e1, &w);
CrossProduct(&e2, &w, &n2);
Normalize(&n2);
Add(&qm->quads[currentQuad].vertices[2]->normal, &n2, &qm->quads[currentQuad].vertices[2]->normal);
Negate(&e2, &w);
CrossProduct(&e3, &w, &n3);
Normalize(&n3);
Add(&qm->quads[currentQuad].vertices[3]->normal, &n3, &qm->quads[currentQuad].vertices[3]->normal);
for (int i = 0; i < 4; i++)
{
Normalize(&qm->quads[currentQuad].vertices[i]->normal);
}
currentQuad++;
}
}
}
It will not if you try it because its linked to a main.c file but its basically generating a paper thin mesh with a green shade.
I'm struggling to load model via assimp with multiple meshes, here is code for loading model:
bool loadAssImp(
const char * path,
std::vector<unsigned short> & indices,
std::vector<glm::vec3> & vertices,
std::vector<glm::vec2> & uvs,
std::vector<glm::vec3> & normals
) {
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate);
if (!scene) {
cout << importer.GetErrorString() << endl;
return false;
}
aiMesh* mesh;
for (unsigned int l = 0; l < scene->mNumMeshes; l++)
{
mesh = scene->mMeshes[l];
cout << l << endl;
// Fill vertices positions
vertices.reserve(mesh->mNumVertices);
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
aiVector3D pos = mesh->mVertices[i];
vertices.push_back(glm::vec3(pos.x, pos.y, pos.z));
}
// Fill vertices texture coordinates
uvs.reserve(mesh->mNumVertices);
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
aiVector3D UVW = mesh->mTextureCoords[0][i]; // Assume only 1 set of UV coords; AssImp supports 8 UV sets.
uvs.push_back(glm::vec2(UVW.x, UVW.y));
}
// Fill vertices normals
normals.reserve(mesh->mNumVertices);
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
aiVector3D n = mesh->mNormals[i];
normals.push_back(glm::vec3(n.x, n.y, n.z));
}
// Fill face indices
indices.reserve(3 * mesh->mNumFaces);
for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
// Assume the model has only triangles.
indices.push_back(mesh->mFaces[i].mIndices[0]);
indices.push_back(mesh->mFaces[i].mIndices[1]);
indices.push_back(mesh->mFaces[i].mIndices[2]);
}
}
// The "scene" pointer will be deleted automatically by "importer"
return true;
}
model I'm trying to load has two meshes, head and torso
if I start loop like this: for (int l = scene->mNumMeshes - 1; l >= 0 ; l--) then it loads model almost correctly, some of the model vertices don't get drawn correctly but head and almost all of torso is drawn.
if I start loop like this: for (unsigned int l = 0; l < scene->mNumMeshes; l++) only torso is drawn (but is drawn fully without any missing parts)
strangely in both ways vertices and index count is the same.
You need to set the transformations descriped by the aiNode-instances as well. So when loading a scene loop over the nodes, set the local node transformation, the draw the assigned meshes to the current node.
Whithout applying these transformation information the rendering of the model will be broken.
I'm trying to load FBX models using OpenGL, I got the importer to work and read the mesh data so I can draw it
but some parts are not displayed correctly.
Here's what I use
FbxMesh*mesh = pNode->GetMesh();
//================= Get Vertices ====================================
int numVerts = mesh->GetControlPointsCount();
for(int j = 0; j < numVerts; j++)
{
FbxVector4 vert = mesh->GetControlPointAt(j);
vertices[numVertices].x=(float)vert.mData[0];
vertices[numVertices].y=(float)vert.mData[1];
vertices[numVertices++].z=(float)vert.mData[2];
printf("MeshVert: x: %f y: %f z: %f\n", vertices[numVertices-1].x, vertices[numVertices-1].y, vertices[numVertices-1].z);
}
//================= Get Indices ====================================
numIndices = mesh->GetPolygonVertexCount();
int triangleCount = numIndices / 3;
indices = new int[numIndices];
indices = mesh->GetPolygonVertices();
printf("numIndices: %i\n", numIndices);
printf("TriangleCount: %i\n", triangleCount);
//================= Get Normals ====================================
FbxGeometryElementNormal*normalEl = mesh->GetElementNormal();
if(normalEl)
{
int numNormals = mesh->GetPolygonCount()*3;
normals = new float[numNormals*3];
int vertexCounter = 0;
for(int polyCounter = 0 ; polyCounter<mesh->GetPolygonCount(); polyCounter++)
{
for(int k = 0; k < 3; k++)
{
FbxVector4 normal = normalEl->GetDirectArray().GetAt(vertexCounter);
normals[vertexCounter*3+0] = (float)normal[0];
normals[vertexCounter*3+1] = (float)normal[1];
normals[vertexCounter*3+2] = (float)normal[2];
//cout<<"\n"<<normals[vertexCounter*3+0]<<" "<<normals[vertexCounter*3+1]<<" "<<normals[vertexCounter*3+2];
vertexCounter++;
}
}
}
And primitives to draw
for(int i = 0; i < numIndices - 3; i++)
{
glBegin(GL_TRIANGLES);
glNormal3f(normals[i*3+0], normals[i*3+1], normals[i*3+2]);
for(int j = i; j <= i + 2; j++)
{
glVertex3f(vertices[indices[j]].x, vertices[indices[j]].y, vertices[indices[j]].z);
glColor3f(0.3f, 0.3f, 0.3f);
}
glEnd();
}
Just wondering if anyone can help get this to work.
The rest of my projects code is in https://github.com/buttburger/FBXEssentials/tree/master/OpenGL2
Fixed it up now
for(int i = 0; i < numIndices - 3; i+=3)
{
glBegin(GL_TRIANGLES);
glNormal3f(normals[i*3+0], normals[i*3+1], normals[i*3+2]);
for(int j = i; j <= i + 2; j++)
{
glVertex3f(vertices[indices[j]].x, vertices[indices[j]].y, vertices[indices[j]].z);
glColor3f(1.0f, 1.0f, 1.0f);
}
}
glEnd();
glFlush();
glutSwapBuffers();
Have been trying to render a MD5 mesh to my games engine but when I do the mesh seems to me glued to the same (follows it around) and looks flat. for an example of the problem, look here
AnimatedMesh.cpp:
#include "AnimatedMesh.h"
#define POSITION_LOCATION 0
#define TEX_COORD_LOCATION 1
#define NORMAL_LOCATION 2
#define BONE_ID_LOCATION 3
#define BONE_WEIGHT_LOCATION 4
void AnimMesh::VertexBoneData::AddBoneData(unsigned int BoneID, float Weight)
{
for (unsigned int i = 0; i < ARRAY_SIZE_IN_ELEMENTS(IDs); i++) {
if (Weights[i] == 0.0) {
IDs[i] = BoneID;
Weights[i] = Weight;
return;
}
}
assert(0);
}
AnimMesh::AnimMesh()
{
m_VAO = 0;
ZERO_MEM(m_Buffers);
m_NumBones = 0;
m_pScene = NULL;
}
AnimMesh::~AnimMesh()
{
Clear();
}
void AnimMesh::Clear()
{
for (unsigned int i = 0; i < m_Textures.size(); i++) {
SAFE_DELETE(m_Textures[i]);
}
if (m_Buffers[0] != 0) {
glDeleteBuffers(ARRAY_SIZE_IN_ELEMENTS(m_Buffers), m_Buffers);
}
if (m_VAO != 0) {
glDeleteVertexArrays(1, &m_VAO);
m_VAO = 0;
}
}
bool AnimMesh::LoadAnimMesh(const string& Filename)
{
Clear();
glGenVertexArrays(1, &m_VAO);
glBindVertexArray(m_VAO);
glGenBuffers(ARRAY_SIZE_IN_ELEMENTS(m_Buffers), m_Buffers);
bool Ret = false;
m_pScene = m_Importer.ReadFile(Filename.c_str(), aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs);
if (m_pScene) {
m_GlobalInverseTransform = m_pScene->mRootNode->mTransformation;
m_GlobalInverseTransform.Inverse();
Ret = InitFromScene(m_pScene, Filename);
}
else {
printf("Error parsing '%s': '%s'\n", Filename.c_str(), m_Importer.GetErrorString());
}
// Make sure the VAO is not changed from the outside
glBindVertexArray(0);
return Ret;
}
bool AnimMesh::InitFromScene(const aiScene* pScene, const string& Filename)
{
m_Entries.resize(pScene->mNumMeshes);
m_Textures.resize(pScene->mNumMaterials);
vector<Vector3f> Positions;
vector<Vector3f> Normals;
vector<Vector2f> TexCoords;
vector<VertexBoneData> Bones;
vector<unsigned int> Indices;
unsigned int NumVertices = 0;
unsigned int NumIndices = 0;
// Count the number of vertices and indices
for (unsigned int i = 0; i < m_Entries.size(); i++) {
m_Entries[i].MaterialIndex = pScene->mMeshes[i]->mMaterialIndex;
m_Entries[i].NumIndices = pScene->mMeshes[i]->mNumFaces * 3;
m_Entries[i].BaseVertex = NumVertices;
m_Entries[i].BaseIndex = NumIndices;
NumVertices += pScene->mMeshes[i]->mNumVertices;
NumIndices += m_Entries[i].NumIndices;
}
// Reserve space in the vectors for the vertex attributes and indices
Positions.reserve(NumVertices);
Normals.reserve(NumVertices);
TexCoords.reserve(NumVertices);
Bones.resize(NumVertices);
Indices.reserve(NumIndices);
// Initialize the meshes in the scene one by one
for (unsigned int i = 0; i < m_Entries.size(); i++) {
const aiMesh* paiMesh = pScene->mMeshes[i];
InitMesh(i, paiMesh, Positions, Normals, TexCoords, Bones, Indices);
}
if (!InitMaterials(pScene, Filename)) {
return false;
}
// 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);
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);
return GLCheckError();
}
void AnimMesh::InitMesh(unsigned int MeshIndex,
const aiMesh* paiMesh,
vector<Vector3f>& Positions,
vector<Vector3f>& Normals,
vector<Vector2f>& TexCoords,
vector<VertexBoneData>& Bones,
vector<unsigned int>& Indices)
{
const aiVector3D Zero3D(0.0f, 0.0f, 0.0f);
// Populate the vertex attribute vectors
for (unsigned int i = 0; i < paiMesh->mNumVertices; i++) {
const aiVector3D* pPos = &(paiMesh->mVertices[i]);
const aiVector3D* pNormal = &(paiMesh->mNormals[i]);
const aiVector3D* pTexCoord = paiMesh->HasTextureCoords(0) ? &(paiMesh->mTextureCoords[0][i]) : &Zero3D;
Positions.push_back(Vector3f(pPos->x, pPos->y, pPos->z));
Normals.push_back(Vector3f(pNormal->x, pNormal->y, pNormal->z));
TexCoords.push_back(Vector2f(pTexCoord->x, pTexCoord->y));
}
LoadBones(MeshIndex, paiMesh, Bones);
// Populate the index buffer
for (unsigned int i = 0; i < paiMesh->mNumFaces; i++) {
const aiFace& Face = paiMesh->mFaces[i];
assert(Face.mNumIndices == 3);
Indices.push_back(Face.mIndices[0]);
Indices.push_back(Face.mIndices[1]);
Indices.push_back(Face.mIndices[2]);
}
}
void AnimMesh::LoadBones(unsigned int MeshIndex, const aiMesh* pMesh, vector<VertexBoneData>& Bones)
{
for (unsigned int i = 0; i < pMesh->mNumBones; i++) {
unsigned int BoneIndex = 0;
string BoneName(pMesh->mBones[i]->mName.data);
if (m_BoneMapping.find(BoneName) == m_BoneMapping.end()) {
// Allocate an index for a new bone
BoneIndex = m_NumBones;
m_NumBones++;
BoneInfo bi;
m_BoneInfo.push_back(bi);
m_BoneInfo[BoneIndex].BoneOffset = pMesh->mBones[i]->mOffsetMatrix;
m_BoneMapping[BoneName] = BoneIndex;
}
else {
BoneIndex = m_BoneMapping[BoneName];
}
for (unsigned int j = 0; j < pMesh->mBones[i]->mNumWeights; j++) {
unsigned int VertexID = m_Entries[MeshIndex].BaseVertex + pMesh->mBones[i]->mWeights[j].mVertexId;
float Weight = pMesh->mBones[i]->mWeights[j].mWeight;
Bones[VertexID].AddBoneData(BoneIndex, Weight);
}
}
}
bool AnimMesh::InitMaterials(const aiScene* pScene, const string& Filename)
{
// Extract the directory part from the file name
string::size_type SlashIndex = Filename.find_last_of("/");
string Dir;
if (SlashIndex == string::npos) {
Dir = ".";
}
else if (SlashIndex == 0) {
Dir = "/";
}
else {
Dir = Filename.substr(0, SlashIndex);
}
bool Ret = true;
// Initialize the materials
for (unsigned int i = 0; i < pScene->mNumMaterials; i++) {
const aiMaterial* pMaterial = pScene->mMaterials[i];
m_Textures[i] = NULL;
if (pMaterial->GetTextureCount(aiTextureType_DIFFUSE) > 0) {
aiString Path;
if (pMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &Path, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) {
string p(Path.data);
if (p.substr(0, 2) == ".\\") {
p = p.substr(2, p.size() - 2);
}
string FullPath = Dir + "/" + p;
m_Textures[i] = new AnimatedTexture(GL_TEXTURE_2D, FullPath.c_str());
if (!m_Textures[i]->Load()) {
printf("Error loading texture '%s'\n", FullPath.c_str());
delete m_Textures[i];
m_Textures[i] = NULL;
Ret = false;
}
else {
printf("%d - loaded texture '%s'\n", i, FullPath.c_str());
}
}
}
}
return Ret;
}
void AnimMesh::Renderer(float time, glm::mat4 model, glm::mat4 view, glm::mat4 projection, LightDirectional directionalLight, float baseAmbient, glm::vec3 cameraPos, Shader animationShader)
{
m_pEffect = new Skinning(animationShader, directionalLight, baseAmbient, 0.0f, 0.0f);
vector<Matrix4f> Transforms;
BoneTransform(time, Transforms);
for (GLuint i = 0; i < Transforms.size(); i++)
{
m_pEffect->SetBoneTransform(animationShader, i, Transforms[i]);
}
m_pEffect->SetEyeWorldPos(animationShader, cameraPos);
m_pEffect->SetUp(animationShader, model, view, projection);
glBindVertexArray(m_VAO);
for (unsigned int i = 0; i < m_Entries.size(); i++) {
const unsigned int MaterialIndex = m_Entries[i].MaterialIndex;
assert(MaterialIndex < m_Textures.size());
if (m_Textures[MaterialIndex]) {
m_Textures[MaterialIndex]->Bind(GL_TEXTURE0);
}
glDrawElementsBaseVertex(GL_TRIANGLES,
m_Entries[i].NumIndices,
GL_UNSIGNED_INT,
(void*)(sizeof(unsigned int)* m_Entries[i].BaseIndex),
m_Entries[i].BaseVertex);
}
// Make sure the VAO is not changed from the outside
glBindVertexArray(0);
}
unsigned int AnimMesh::FindPosition(float AnimationTime, const aiNodeAnim* pNodeAnim)
{
for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys - 1; i++) {
if (AnimationTime < (float)pNodeAnim->mPositionKeys[i + 1].mTime) {
return i;
}
}
assert(0);
return 0;
}
unsigned int AnimMesh::FindRotation(float AnimationTime, const aiNodeAnim* pNodeAnim)
{
assert(pNodeAnim->mNumRotationKeys > 0);
for (unsigned int i = 0; i < pNodeAnim->mNumRotationKeys - 1; i++) {
if (AnimationTime < (float)pNodeAnim->mRotationKeys[i + 1].mTime) {
return i;
}
}
assert(0);
return 0;
}
unsigned int AnimMesh::FindScaling(float AnimationTime, const aiNodeAnim* pNodeAnim)
{
assert(pNodeAnim->mNumScalingKeys > 0);
for (unsigned int i = 0; i < pNodeAnim->mNumScalingKeys - 1; i++) {
if (AnimationTime < (float)pNodeAnim->mScalingKeys[i + 1].mTime) {
return i;
}
}
assert(0);
return 0;
}
void AnimMesh::CalcInterpolatedPosition(aiVector3D& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
{
if (pNodeAnim->mNumPositionKeys == 1) {
Out = pNodeAnim->mPositionKeys[0].mValue;
return;
}
unsigned int PositionIndex = FindPosition(AnimationTime, pNodeAnim);
unsigned int 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 AnimMesh::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;
}
unsigned int RotationIndex = FindRotation(AnimationTime, pNodeAnim);
unsigned int 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 AnimMesh::CalcInterpolatedScaling(aiVector3D& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
{
if (pNodeAnim->mNumScalingKeys == 1) {
Out = pNodeAnim->mScalingKeys[0].mValue;
return;
}
unsigned int ScalingIndex = FindScaling(AnimationTime, pNodeAnim);
unsigned int 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;
}
void AnimMesh::ReadNodeHeirarchy(float AnimationTime, const aiNode* pNode, const Matrix4f& ParentTransform)
{
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.InitScaleTransform(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.InitTranslationTransform(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()) {
unsigned int BoneIndex = m_BoneMapping[NodeName];
m_BoneInfo[BoneIndex].FinalTransformation = m_GlobalInverseTransform * GlobalTransformation * m_BoneInfo[BoneIndex].BoneOffset;
}
for (unsigned int i = 0; i < pNode->mNumChildren; i++) {
ReadNodeHeirarchy(AnimationTime, pNode->mChildren[i], GlobalTransformation);
}
}
void AnimMesh::BoneTransform(float TimeInSeconds, vector<Matrix4f>& Transforms)
{
Matrix4f Identity;
Identity.InitIdentity();
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);
ReadNodeHeirarchy(AnimationTime, m_pScene->mRootNode, Identity);
Transforms.resize(m_NumBones);
for (unsigned int i = 0; i < m_NumBones; i++) {
Transforms[i] = m_BoneInfo[i].FinalTransformation;
}
}
const aiNodeAnim* AnimMesh::FindNodeAnim(const aiAnimation* pAnimation, const string NodeName)
{
for (unsigned int i = 0; i < pAnimation->mNumChannels; i++) {
const aiNodeAnim* pNodeAnim = pAnimation->mChannels[i];
if (string(pNodeAnim->mNodeName.data) == NodeName) {
return pNodeAnim;
}
}
return NULL;
}
Skinning.vert:
#version 330
layout (location = 0) in vec3 Position;
layout (location = 1) in vec2 TexCoord;
layout (location = 2) in vec3 Normal;
layout (location = 3) in ivec4 BoneIDs;
layout (location = 4) in vec4 Weights;
out vec2 TexCoord0;
out vec3 Normal0;
out vec3 WorldPos0;
const int MAX_BONES = 100;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 gBones[MAX_BONES];
uniform mat4 modelInverseTranspose;
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(Position, 1.0);
gl_Position = model * view * projection * PosL;
TexCoord0 = TexCoord;
vec4 NormalL = BoneTransform * vec4(Normal, 0.0);
Normal0 = (modelInverseTranspose * NormalL).xyz;
WorldPos0 = (model * PosL).xyz;
}
I think you might have gotten the matrix multiplication order in the shader wrong. Quote from another question:
Model maps from an object's local coordinate space into world space, view from world space to camera space, projection from camera to screen.
Try changing
gl_Position = model * view * projection * PosL;
into
gl_Position = projection * view * model * PosL;
I am trying to draw something like this in OpenGL 2.1 using GLU and GLUT primitives such as gluCylinder, glutSolidCylinder, etc.
Basically it's a solid cylinder with a hole in the middle. So far I was unable to achieved it.
I also tried to import wavefront obj from Blender to my program in C++ but what I got was this.
glBegin(GL_TRIANGLES);
for(int i = 0; i < wheel->faces.size(); i++) {
int nx = atoi(wheel->faces.at(i).at(0).substr(wheel->faces.at(i).at(0).find_last_of('/') + 1).c_str()) - 1;
int ny = atoi(wheel->faces.at(i).at(1).substr(wheel->faces.at(i).at(1).find_last_of('/') + 1).c_str()) - 1;
int nz = atoi(wheel->faces.at(i).at(2).substr(wheel->faces.at(i).at(2).find_last_of('/') + 1).c_str()) - 1;
int vx = atoi(wheel->faces.at(i).at(0).substr(0, wheel->faces.at(i).at(0).find_first_of('/')).c_str()) - 1;
int vy = atoi(wheel->faces.at(i).at(1).substr(0, wheel->faces.at(i).at(1).find_first_of('/')).c_str()) - 1;
int vz = atoi(wheel->faces.at(i).at(2).substr(0, wheel->faces.at(i).at(2).find_first_of('/')).c_str()) - 1;
glNormal3f(wheel->normals.at(nx).x, wheel->normals.at(ny).y, wheel->normals.at(nz).z);
glVertex3f(wheel->vertices.at(vx).x, wheel->vertices.at(vy).y, wheel->vertices.at(vz).z);
}
glEnd();
The parsing should be correct, I double checked it line by line. I guess it's the wrong way I tried to draw it.
However I don't want to use obj since my way of drawing makes animation slow (I am fairly new to OpenGL). And I don't want to use OpenGL 3+ either.
Any suggestions?
In your code you're drawing a single point with one face, however a face (a triangle) here is supposed to have three points.
Your may try this code:
glBegin(GL_TRIANGLES);
for(int i = 0; i < wheel->faces.size(); i++) {
int v0 = atoi(wheel->faces.at(i).at(0).substr(0, wheel->faces.at(i).at(0).find_first_of('/')).c_str()) - 1;
int v1 = atoi(wheel->faces.at(i).at(1).substr(0, wheel->faces.at(i).at(1).find_first_of('/')).c_str()) - 1;
int v2 = atoi(wheel->faces.at(i).at(2).substr(0, wheel->faces.at(i).at(2).find_first_of('/')).c_str()) - 1;
glVertex3f(wheel->vertices.at(v0).x, wheel->vertices.at(v0).y, wheel->vertices.at(v0).z);
glVertex3f(wheel->vertices.at(v1).x, wheel->vertices.at(v1).y, wheel->vertices.at(v1).z);
glVertex3f(wheel->vertices.at(v2).x, wheel->vertices.at(v2).y, wheel->vertices.at(v2).z);
}
glEnd();
I removed the normal part since I don't think you would need it for now. You may add it later.
EDTIED:
This seems better:
glBegin(GL_TRIANGLES);
for(int i = 0; i < wheel->faces.size(); i++)
for (int j = 0; j < 3; j++)
{
int v_id = atoi(wheel->faces.at(i).at(j).substr(0, wheel->faces.at(i).at(j).find_first_of('/')).c_str()) - 1;
glVertex3f(wheel->vertices.at(v_id).x, wheel->vertices.at(v_id).y, wheel->vertices.at(v_id).z);
}
glEnd();
Found for my self this small lib for opening obj files.
Drawing loaded mesh with next code:
glBegin(GL_TRIANGLES);
for (size_t f = 0; f < shapes[0].mesh.indices.size(); f++) {
glVertex3f(shapes[0].mesh.positions[3*f+0],
shapes[0].mesh.positions[3*f+1],
shapes[0].mesh.positions[3*f+2]);
}
glEnd();
Load mesh:
std::vector<tinyobj::shape_t> shapes;
int loadMesh(const char *file) {
const char* basepath = NULL;
std::string err = tinyobj::LoadObj(shapes, file, basepath);
if (!err.empty()) {
std::cerr << err << std::endl;
return 0;
}
std::cout << "# of shapes : " << shapes.size() << std::endl;
return 1;
}