Capture.PNG
:::First of all English is not my first language, correct me where your find mistakes.

This Tutorial will show how to load models with a popular Library known as ASSIMP.
It's a famous library that currently supports 40+ different formats!
With this, you can just browse for models online and quickly load them into your app without going through the process of conversion with external tools.
As for now the code being used has some minor bugs.Anyone is welcome to help rectify them.

(BUG:Cannot load complex models correctly, i'm attaching a simple

 Plugin disabled

Plugin attach cannot be executed.
which does load correctly, please test with other models)


I am making this tutorial because I know how hard it is for users to load other data formats into OGRE .
This method will not require the use of any external programs , neither will it require you to manually convert your models with tools like blender,all you will need is adding some code to your app, and you can load any model by it's filename.
Unfortunately the code is not currently able to load textures(fix when we complete).
In the long run I intend to make this functionality into a plugin, if you are familiar with how OGRE and ASSIMP work, you can also help us speed up the process.

ASSIMP-Open Asset Import Library Repository

(Official Repo https://github.com/assimp/assimp)
The Bug we are currently facing Complex_Model_Bug

I believe this is the first step to win back users into the OGRE project.

Lets get started. To do this, you have to build and install ASSIMP, for windows users you don't need to install it only specify the directory where the library and files will be put.
My implementation only needs 2 source files,this however might change in the future.

You will have to copy them into your application's source dirs and add ASSIMP include dir and library dir into your application.
The files are attached below.

To load a model
You just need to add the following code into your app:::

First create the following two files and paste the code into them.

Assimp.h
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/matrix4x4.h>
#include <assimp/cimport.h>

#include <Windows.h>
#include <iostream>
#include <stdint.h>
#include <vector>

class ModelManager
{
public:
	ModelManager();
	~ModelManager(void);
	
	bool loadModel(std::string file);
	bool processData();
	std::vector<float> *getVertexData();
	std::vector<uint16_t> *getIndexData();

private:
	bool assimpGetMeshData(const aiMesh *mesh);
private:
	Assimp::Importer            importer;
	const aiScene               *modelScene;
	const aiNode                *modelNode;
	const aiMesh                *modelMesh;
	const aiFace                *modelFace;
	std::vector<float>          vertexBuff;
	std::vector<uint16_t>       indexBuff;

	std::vector<const aiNode*>  nodeBuff;
	unsigned int                numNodeBuff;
};

Assimp.cpp
#include "Assimp.h"

ModelManager::ModelManager()
{
   vertexBuff.clear();
   indexBuff.clear();
   std::cout << "=======Mesh Manager  initialized======="<<std::endl;

}

ModelManager::~ModelManager(void)
{
   std::cout<<"ModelManager destroyed" << std::endl;
}

bool ModelManager::loadModel(std::string file)
{
   modelScene = importer.ReadFile(file, aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_PreTransformVertices |
      aiProcess_CalcTangentSpace |
      aiProcess_GenSmoothNormals |
      aiProcess_Triangulate |
      aiProcess_FixInfacingNormals |
      aiProcess_FindInvalidData |
      aiProcess_ValidateDataStructure | 0

   );

   if (!modelScene)
   {
      MessageBoxA(NULL, importer.GetErrorString(), "Error: ", MB_ICONERROR);
      return false;
   }
   else 
      processData();

   return true;
}


bool ModelManager::assimpGetMeshData(const aiMesh *mesh)
{
   aiFace *face;


   for (unsigned int v = 0; v < mesh->mNumVertices; v++)
   {
      vertexBuff.push_back(mesh->mVertices[v].x);
      vertexBuff.push_back(mesh->mVertices[v].y);
      vertexBuff.push_back(mesh->mVertices[v].z);


      vertexBuff.push_back(mesh->mNormals[v].x);
      vertexBuff.push_back(mesh->mNormals[v].y);
      vertexBuff.push_back(mesh->mNormals[v].z);




      if(mesh->HasTextureCoords(0)){
            vertexBuff.push_back(mesh->mTextureCoords[0][v].x);
            vertexBuff.push_back(mesh->mTextureCoords[0][v].y);
         }
      else
         {
            vertexBuff.push_back(0);
            vertexBuff.push_back(0);
         }
      /*
      vertexBuff.push_back(mesh->mTangents[v].x);
      vertexBuff.push_back(mesh->mTangents[v].y);
      vertexBuff.push_back(mesh->mTangents[v].z);*/

   }

   for (unsigned int f = 0; f<mesh->mNumFaces; f++)
   {
      face = &mesh->mFaces[f];
      indexBuff.push_back(face->mIndices[0]);
      indexBuff.push_back(face->mIndices[1]);
      indexBuff.push_back(face->mIndices[2]);
   }

   return true;
}
bool ModelManager::processData()
{
   bool repeat = true;

   nodeBuff.push_back(modelScene->mRootNode);
   

   /* if (modelScene->mNumMeshes > 0)
   {
   for (unsigned int m=0;m<modelScene->mNumMeshes;m++)
   this->assimpGetMeshData(modelScene->mMeshes[m]);
   }*/

   // I raise all nodes tree to the root level 
   while (repeat)
   {
      for (unsigned int a = 0; a<nodeBuff.size(); a++)
      {
         modelNode = nodeBuff.at(a);
         if (modelNode->mNumChildren > 0)
            for (unsigned int c = 0; c < modelNode->mNumChildren; c++)
            {
               nodeBuff.push_back(modelNode->mChildren[c]);

            }
               
         else repeat = false;
      }
   }

   // Get node information from the root level (all nodes)
   for (unsigned int a = 0; a<nodeBuff.size(); a++)
   {
      modelNode = nodeBuff.at(a);

      if (modelNode->mNumMeshes>0)
         for (unsigned int b = 0; b < modelNode->mNumMeshes; b++) {
            assimpGetMeshData(modelScene->mMeshes[b]);
            //std::cout << "::::::::PROCESSING =>" << modelScene->mMeshes[b]->mName;
         }
   }
   return true;
}

std::vector<float> *ModelManager::getVertexData()
{
   return &vertexBuff;
}

std::vector<uint16_t> *ModelManager::getIndexData()
{
   return &indexBuff;
}


include the file below
#include "Assimp.h"


Then define this function where you have your createscene:

{CODE(caption="")="cpp"}
void makeMesh(Ogre::String meshFile, Ogre::String meshName)
{
Ogre::MeshPtr Mesh = Ogre::MeshManager::getSingleton().createManual(meshName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
Ogre::SubMesh *subMesh = Mesh->createSubMesh("subMesh");
Ogre::VertexDeclaration *vertexDeclaration;
Ogre::HardwareVertexBufferSharedPtr vertexBuffer;
Ogre::HardwareIndexBufferSharedPtr indexBuffer;
stringstream stringBuffer;
size_t offset = 0;
/*
// Get file name and extension from the Ogre Resource Manager
Ogre::FileInfoListPtr fileInfoListPtr(Ogre::ResourceGroupManager::getSingleton().findResourceFileInfo(Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, meshFile, false));
Ogre::FileInfoList *fileInfoList = fileInfoListPtr.getPointer();
Ogre::FileInfo &fileInfo = fileInfoList->front();
stringBuffer << fileInfo.archive->getName().c_str() << meshFile;
*/

// ************** From Assimp code ***************************
ModelManager manager;

manager.loadModel(meshFile);
std::vector<float> *vData = manager.getVertexData();
std::vector<uint16_t> *iData = manager.getIndexData();
// ********************************************************************

Mesh->sharedVertexData = new Ogre::VertexData;

// Organizo la memoria de video
vertexDeclaration = Mesh->sharedVertexData->vertexDeclaration;
vertexDeclaration->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);

vertexDeclaration->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);

vertexDeclaration->addElement(0, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);

// Make vertex buffer
vertexBuffer = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(vertexDeclaration->getVertexSize(0),
vData->size() / 8,
Ogre::HardwareBuffer::HBU_STATIC);

// Write the vertex buffer with the target data of vData->data() located in assimp code
vertexBuffer.getPointer()->writeData(0, vertexBuffer.getPointer()->getSizeInBytes(), vData->data());

// Make index buffer
indexBuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT,
iData->size(),
Ogre::HardwareBuffer::HBU_STATIC);

indexBuffer.getPointer()->writeData(0, indexBuffer.getPointer()->getSizeInBytes(), iData->data());

Mesh->sharedVertexData->vertexBufferBinding->setBinding(0, vertexBuffer);
Mesh->sharedVertexData->vertexCount = vertexBuffer.getPointer()->getNumVertices();
Mesh->sharedVertexData->vertexStart = 0;

subMesh->useSharedVertices = true;
subMesh->indexData->indexBuffer = indexBuffer;
subMesh->indexData->indexCount = indexBuffer.getPointer()->getNumIndexes();
subMesh->indexData->indexStart = 0;
//if (manager.hasmaterial()) {
// oSceneManager->getEntity(entityName)->setMaterialName(manager.material_name());
// }
// I don't get real AABB from object, this is ok for probe
Mesh->_setBounds(Ogre::AxisAlignedBox(-400, -400, -400, 400, 400, 400));
Mesh->load();




std::cout << "total de vertices: " << vData->size() / 8 << "\n";
std::cout << "total de faces: " << indexBuffer.getPointer()->getNumIndexes() / 3 << "\n";

}

{CODE}

Then go to where you create your ogre entities(After creating the scene) and add the following

makeMesh("..//models//jin.obj", "mesh");


Example

Example
void createScene()
    {
        // Set the scene's ambient light
        mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5f, 0.5f, 0.5f));
		
	     makeMesh("..//models//jin.obj", "mesh");    
             //Here mesh is the name of our mesh in ogre resourcegroup.
             
             Ogre::Entity*  HeadEntity= mSceneMgr->createEntity("MyEntity", "mesh"); 
             //This will create a new entity called MyEntity and assign the mesh we created earlier.

             HeadEntity->setMaterialName("test.mat");
             //You must put this code regardless of wheather you have a valid material or not, otherwise ogre will crash.


             Ogre::SceneNode* headSceneNode =mSceneMgr->getRootSceneNode()->createChildSceneNode("headscene");
             //Create Child scene node for the new entity.

             headSceneNode->attachObject(HeadEntity);
             //Attach our HeadEntity to the scene node we just created.

             headSceneNode->setPosition(0, 0, 0);
            //Set the position of our headscene.
	
	

        // Create a Light and set its position
        Ogre::Light* light = mSceneMgr->createLight("MainLight");
        light->setPosition(20.0f, 80.0f, 500.0f);
    
};



That will place your object. position (0,0,0) on the screen.
Expect this code to be updated in the future, it will also be possible to load materials automatically without adding the code manually.



Please help improve this code and make OGRE users happy.
After all OGRE is growing more powerfull each the day.
You can also visit the issue on github and help out https://github.com/assimp/assimp/issues/1362

Ogre Forum page where you can discuss this code or report problems is at Assimp -> Ogre adaptor