Jump to content

  • Log In with Google      Sign In   
  • Create Account

Assimp baby steps and mesh not completely rendering...


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
2 replies to this topic

#1 Lil_Lloyd   Members   -  Reputation: 287

Like
0Likes
Like

Posted 24 July 2012 - 08:55 PM

I'm back again to wreak havoc. This time, I have dipping my toes into Assimp - related goodness. Following a couple of tutorials on the web, I took the concepts and made my own static model class that uses an STL vector of vertex array objects for storing the mesh info. Now, using a simple .obj model file I can get PARTS of it to render to the screen, in experimenting I have tried GL_TRIANGLE_STRIP, GL_TRIANGLES and even turning off back face culling but the model is rendering....sporadically!

Here is my code for the static Mesh:

//StaticMesh.h - this class uses assimp to load models and is used to represent all the data in a mesh
//through assimp's supported classes it can store many kinds of file type
//last updata 24/07/2012
#pragma once
#include "Includes.h"
#include "ObjectData.h"
struct aiScene;
struct aiNode;
struct aiMatrix4x4;
namespace OpenGL_3_3Engine
{
class StaticMesh
{
    public:
      StaticMesh(){};
      ~StaticMesh();

      bool Init(const char* filename);
      void Render();

     glm::mat4x4 m_transform,m_scale;
     std::vector<ObjectData*> m_meshData;
     float m_scaleFactor;
     glm::vec3 m_boundingBoxMin,m_boundingBoxMax;
 
  private:
     //support functions for use by Init();
     void GetBoundingBox(glm::vec3& min,glm::vec3& max,const aiScene* scene);
     void GetBoundingBoxForNode(const aiNode* node,glm::vec3& min,glm::vec3& max,aiMatrix4x4& trafo,
                             const aiScene* scene);

};
};

//StaticMesh.cpp - implementation of loading meshes, initialising vertex buffer data etc
// http://www.lighthouse3d.com/cg-topics/code-samples/importing-3d-models-with-assimp/ was a great reference in building
//this
//last update 24/07/2012

#include "StaticMesh.h"
#include <Assimp/aiScene.h>
#include <Assimp/aiPostProcess.h>
#include <Assimp/aiMesh.h>
#include <Assimp/assimp.hpp>
#include <Assimp/assimp.h>
namespace OpenGL_3_3Engine
{

StaticMesh::~StaticMesh()
{
    for(std::vector<ObjectData*>::iterator meshData = m_meshData.begin(); meshData != m_meshData.end(); meshData++)
    {
       delete *meshData;
    }
    m_meshData.clear();
}


bool StaticMesh::Init(const char* filename)
{
    //check if file exists
    std::ifstream fin(filename);
    if(!fin.fail())
    {
       fin.close();
    }
    else
    {
       std::cout << "Couldn't open file "<< filename <<" it doesn't exist!" << std::endl;
       return false;
    }

    Assimp::Importer importer;
    const aiScene* scene = importer.ReadFile(filename,aiProcessPreset_TargetRealtime_Quality);

    if(!scene)
    {
       std::cout <<"couldn't load scene! Reason: "<< importer.GetErrorString() <<std::endl;
         return false;
    }

    GetBoundingBox(m_boundingBoxMin,m_boundingBoxMax,scene);
    float biggestDifferenceAxis = 0.0f;
   
    biggestDifferenceAxis = m_boundingBoxMax.x - m_boundingBoxMin.x;

    if(m_boundingBoxMax.y - m_boundingBoxMin.y > biggestDifferenceAxis )
       biggestDifferenceAxis = m_boundingBoxMax.y - m_boundingBoxMin.y;

    if(m_boundingBoxMax.z - m_boundingBoxMin.z > biggestDifferenceAxis )
       biggestDifferenceAxis = m_boundingBoxMax.z - m_boundingBoxMin.z;

    m_scaleFactor = 1.0f/biggestDifferenceAxis;
    m_scale = glm::scale(m_scaleFactor,m_scaleFactor,m_scaleFactor);

    //now init vaos and buffers...
 
    //find out if this mesh has positions, normals, textures
    for(unsigned int mesh = 0; mesh < scene->mNumMeshes; mesh++)
    {
      int numAttribs = 0;
      const aiMesh* thisMesh = scene->mMeshes[mesh];

      if(thisMesh->HasPositions())
        numAttribs++;

      /*if(thisMesh->HasNormals())
         numAttribs++;*/

      /*if(thisMesh->HasTextureCoords(0))
        numAttribs++;*/

      unsigned int numFaces = thisMesh->mNumFaces;

     //create a new objectdata to store the mesh data on the gfx card
     ObjectData* newObject = new ObjectData(numAttribs,numFaces);
  
     //copy the mesh's face data
     unsigned int *indices = new unsigned int[numFaces * 3];

     for(unsigned int face = 0, vertexIndex = 0; face < numFaces; face++, vertexIndex += 3)
     {
        const struct aiFace* meshFace = &thisMesh->mFaces[face];
        memcpy(&indices[vertexIndex],meshFace->mIndices,sizeof(unsigned int) * 3);
     }
  
     //for debugging
     /*std::cout <<"Mesh number "<<mesh<<std::endl;
     std::cout <<"number of vertices: "<<thisMesh->mNumVertices<<std::endl;
     std::cout <<"number of faces: "<<thisMesh->mNumFaces<<std::endl;*/

    //generate vertex array for this mesh using objectData
     newObject->Init(indices,thisMesh->mNumVertices,thisMesh->mVertices,3);

    //push this info into a vector of objectdatas for rendering later
     m_meshData.push_back(newObject);
     delete [] indices;
  }

  return true;
}

void StaticMesh::GetBoundingBox(glm::vec3& min,glm::vec3& max,const aiScene* scene)
{
   aiMatrix4x4 trafo;
	  aiIdentityMatrix4(&trafo);
	  min.x = min.y = min.z =  1e10f;
	  max.x = max.y = max.z = -1e10f;
	  GetBoundingBoxForNode(scene->mRootNode,min,max,trafo,scene);
}

void StaticMesh::GetBoundingBoxForNode(const aiNode* node,glm::vec3& min,glm::vec3& max,aiMatrix4x4& trafo,
							   const aiScene* scene)
{
  aiMatrix4x4 prev;
  prev = trafo;
  aiMultiplyMatrix4(&trafo,&node->mTransformation);
	 for (unsigned int n = 0; n < node->mNumMeshes; ++n)
  {
		    const struct aiMesh* mesh = scene->mMeshes[node->mMeshes[n]];
		   
   for (unsigned int t = 0; t < mesh->mNumVertices; ++t)
   {
				  struct aiVector3D tmp = mesh->mVertices[t];
				  aiTransformVecByMatrix4(&tmp,&trafo);
				  min.x = glm::min(min.x,tmp.x);
				  min.y = glm::min(min.y,tmp.y);
				  min.z = glm::min(min.z,tmp.z);
				  max.x = glm::max(max.x,tmp.x);
				  max.y = glm::max(max.y,tmp.y);
				  max.z = glm::max(max.z,tmp.z);
		    }
	  }
	  for (unsigned int n = 0; n < node->mNumChildren; n++)
      {
		    GetBoundingBoxForNode(node->mChildren[n],min,max,trafo,scene);
	  }
	  trafo = prev;
}

void StaticMesh::Render()
{
    for(std::vector<ObjectData*>::iterator mesh = m_meshData.begin(); mesh != m_meshData.end(); mesh++)
    {
       glBindVertexArray((*mesh)->GetVAO());
       glDrawElements(GL_TRIANGLES,(*mesh)->GetNumFaces(),GL_UNSIGNED_INT,0);
    }
}
};

and Here is my code for my 'objectData' class. I use this class a lot in other projects for simple shapes, planes etc...

//ObjectInfo.h
//last updated 24/07/2012
#pragma once
#include "Includes.h"
namespace OpenGL_3_3Engine
{
class ObjectData
{
    public:
     //ObjectData constructor - takes the number of VBO objects needed, eg 3 for vertices, normals
     //and texture co ordintates, and a number of triangle faces to render
     ObjectData(int numVBObjects,int numFaces) : m_numVbos(numVBObjects), m_numFaces(numFaces){};
     ~ObjectData();

     //bool Init(int numVertices,...) takes a number of vertices for the objectdata to hold
     //and a variable number of arguments that should be passed in the format data, components
     //where data is the pointer to the data and components represents the number of elems in each
     //vertex, e.g 3 for a normal, 4 for a normalised vertex, x for number of bones etc
     //so a call like Init(indices,30,vertices,3) creates an index buffer and uses the data in vertices
     //and knows there are 30 vertices and 3 components in each vertex

     bool Init(const unsigned int* indices,int numVertices,...);
  
     GLuint GetVAO()const {return m_vao;}
     GLuint GetNumFaces()const {return m_numFaces;}

  private:
     bool BindData(GLuint buffer,int numComponents,int numVertices,float* data);
     bool BindVAO();
     GLuint* m_vbos;
     GLint* m_numComponents;
     GLuint m_vao;
     GLuint m_indexBuff;
     GLuint m_numVbos;
     GLuint m_numFaces;
};
};

//ObjectData.cpp
//last updated 24/07/2012
#include "ObjectData.h"
namespace OpenGL_3_3Engine
{

ObjectData::~ObjectData()
{
  glDeleteBuffers(m_numVbos,m_vbos);
  glDeleteVertexArrays(1,&m_vao);
  delete [] m_vbos;
  delete [] m_numComponents;
}

bool ObjectData::Init(const unsigned int* indices,int numVertices,...)
{
  m_vbos = new GLuint[m_numVbos];
  m_numComponents = new GLint[m_numVbos];
  //due to variable num of args, need to use va_list and va_arg to access the data
  va_list attribList;
  va_start(attribList,numVertices);

  //generate an index buffer
  glGenBuffers(1,&m_indexBuff);

  //get info for num of faces and the index data
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_indexBuff);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * m_numFaces * 3, indices, GL_STATIC_DRAW);

  //generate info for the buffers
  glGenBuffers(m_numVbos,m_vbos);
 
  for(int buffer = 0; buffer < m_numVbos; buffer++)
  {
     float* data    = va_arg(attribList,float*);
     int    numComponents = va_arg(attribList,int);
     m_numComponents[buffer] = numComponents;
     BindData(m_vbos[buffer],numComponents,numVertices,data);
  
  }

  BindVAO();
  return true;
}

bool ObjectData::BindData(GLuint buffer,int numComponents,int numVertices, float* data)
{
  glBindBuffer(GL_ARRAY_BUFFER,buffer);
  glBufferData(GL_ARRAY_BUFFER,numVertices * sizeof(float) * numComponents,data,GL_STATIC_DRAW);
  glBindBuffer(GL_ARRAY_BUFFER,0);
  return true;
}

bool ObjectData::BindVAO()
{
  glGenVertexArrays(1,&m_vao);
  glBindVertexArray(m_vao);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_indexBuff);

  for(int buffer = 0; buffer < m_numVbos; buffer++)
  {
    glEnableVertexAttribArray(buffer);
    glBindBuffer(GL_ARRAY_BUFFER,m_vbos[buffer]);
    glVertexAttribPointer(buffer,m_numComponents[buffer],GL_FLOAT,GL_FALSE,0,(GLubyte*)NULL);
  }

  glBindVertexArray(0);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
  return true;
}
};

At the moment some lines are commented out as I merely want the vertex data to render a flat shaded 'solid' mesh first before trying any lighting or textures.

Here is how the model renders currently, and you can dload the model HERE for free! http://www.turbosquid.com/3d-models/fantasy-creature-3d-model/129639:

Posted Image

Sponsor:

#2 teutonicus   Members   -  Reputation: 518

Like
1Likes
Like

Posted 25 July 2012 - 01:24 AM

The most likely culprit:
void StaticMesh::Render()
{
	for(std::vector<ObjectData*>::iterator mesh = m_meshData.begin(); mesh != m_meshData.end(); mesh++)
	{
	   glBindVertexArray((*mesh)->GetVAO());
	   glDrawElements(GL_TRIANGLES,(*mesh)->GetNumFaces(),GL_UNSIGNED_INT,0);
	}
}

The second argument to glDrawElements (count) must specify the number of indices to use for the draw call, not the number of primitives. So multiply by three for triangle lists.

From what I could see, everything else looked okay.


In case you're interested in other feedback, here are some things that stood out as I had a quick look at your code:
for(unsigned int face = 0, vertexIndex = 0; face < numFaces; face++, vertexIndex += 3)
	 {
		const struct aiFace* meshFace = &thisMesh->mFaces[face];
		memcpy(&indices[vertexIndex],meshFace->mIndices,sizeof(unsigned int) * 3);
	 }
This loop assumes that meshFace->mNumIndices == 3, which could cause problems if your mesh is not triangulated

You've written destructors for some of your classes, but no copy constructors or assignment operators. I almost freaked out when I saw standard containers being used to store ObjectDatas but then realised that you were actually storing pointer-to-ObjectDatas ;)
http://www.drdobbs.c...three/184401400

Destructors delete/[]ing things that haven't been initialised. If ObjectData::Init is not called for some reason, bad things will happen when that object's destructor is called. I prefer one-step initialisation, but if that's not an option then be sure to initialise member pointers to nullptr in your constructors.

Edited by teutonicus, 25 July 2012 - 05:35 AM.


#3 Lil_Lloyd   Members   -  Reputation: 287

Like
0Likes
Like

Posted 25 July 2012 - 06:35 AM

Thank you so much for that! A true "schoolboy" error! That saved me a lot of time! As for assignment operators/copy constructors maybe i should write some but back them private, I don't want any inadvertent copying of reams of data.

As for initialisation I always do the meat in the init functions as I can return a bool if anything is wrong.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS