Sunday, May 12, 2013

ASSIMP + DirectX




It just had to happen eventually. After beating my head across various mesh formats, I decided to chose Assimp. I manually loaded OBJ mesh format, the one which is indeed very popular across all the developers. But as it is well aware, it takes lot of time to load OBJ mesh inside the scene. More the number, more the time. So far, my framework was happily loading X mesh format, the one which is provided by DirectX SDK. I was working more towards the scene management, hence was happy with X mesh file format, as it allowed me to concentrate more towards overall meshes in the scene. 

In the meanwhile, I wrote custom shader based particle system for the framework, screenshots in later posts. Eventually, I had to turn my attention back to Mesh Format. As always, I decided to go step by step. Starting with OBJ, i moved towards MD2 then MD3; ended up with MD5 mesh format. I wanted to learn the key basics all these mesh formats incorporated. I was also distracted towards COLLADA, FBX mesh formats, but gave it up on them for COLLADA being way too much data to digest ( my laziness too ) & FBX being way too close mesh format. 

After understanding basics about MD2 & MD3, obvious choice was to go for something latest, so MD5 was given attention. I must say, though I played around lot of code I was finding it difficult to keep track of so many things in my head & also integrate all of them in single framework.

Hence, I was looking for single solution which will help me provide wide range of mesh formats to load. I stumbled across ASSIMP after going through various threads on Gamedev. Though, personally I found Assimp's documentation clumpsy & lack of samples for DirectX to be precise, it was important to digest ASSIMP for what it was worth, multiple mesh formats support!  


I chose simple DirectX application over my codebase to just get the basics right. After playing with ASSIMP for a while, I was impressed with the way data was stored & wrappers written to support the data handling. I got initial understanding of ASSIMP & now it was good time to move back to my small framework. The biggest advantage of ASSIMP is the variety of mesh formats it supports, but at the same time I would like to warn about lack of samples it offers on DirectX front, so be ready to break your head understanding how each subdata is linked to textures etc etc. Overall, once you pass on the basic hurdle of understanding data storage & mapping of sub parts & their textures based on ids, its a piece of cake. 




For my Framework, I had to write AssimpLoader class, whose main task is to just load the mesh ASSIMP way. I also wrote data structure Model which holds, model related VB, IB, textures & transformations. Model data structure also incorporates my custom shader engine, so each model has shader information which is handled by shader engine. Each model also has, Render, Update functions. Each model was now getting rendered through Shader Engine & was getting loaded through Assimp Wrapper I wrote. 




Soon, it was evident that there is a need of upper management class to manage overall meshes in the scene. It was long pending list to write generic GameObject. Main objective of GameObject class is to load mesh, assign unique ID & unique mesh name to the model loaded, set it's shaders & transformations. later on, each mesh in the world can be accessed simply via either ID or name which user has provided. 



 
Next step was to have a level file, which will have collection of these GameObjects predefined by user & then load step by step everything. Here is how my scene description or rather Game level file looks at the moment, its very basic :

SceneBegin
    ObjectBegin
        ObjectId [1]
        ObjectName [Phoenix]
        ObjectPath [Data\\ASSIMP\\MD2\\phoenix_ugv.md2]       
        ObjectShader [Diffuse]
        ObjectPosition [0.0f] [100.0f] [0.0f]
        ObjectRotation [0.0f] [1.0f] [0.0f] [0.0f]
        ObjectScale [1.0f] [1.0f] [1.0f]
        ObjectVisibility [1]
    ObjectEnd
    ObjectBegin
        ObjectId [2]
        ObjectName [Railgun]
        ObjectPath [Data\\ASSIMP\\MD3\\railgun.md3]       
        ObjectShader [Specular]
        ObjectPosition [100.0f] [100.0f] [0.0f]
        ObjectRotation [0.0f] [0.0f] [1.0f] [90.0f]
        ObjectScale [3.0f] [3.0f] [3.0f]
        ObjectVisibility [1]
    ObjectEnd
    ObjectBegin
        ObjectId [3]
        ObjectName [BFG]
        ObjectPath [Data\\ASSIMP\\MD3\\weapons2\\bfg\\bfg.md3]       
        ObjectShader [Specular]
        ObjectPosition [0.0f] [200.0f] [10.0f]
        ObjectRotation [0.0f] [1.0f] [0.0f] [0.0f]
        ObjectScale [1.0f] [1.0f] [1.0f]
        ObjectVisibility [1]
    ObjectEnd
    ObjectBegin
        ObjectId [4]
        ObjectName [Gauntlet]
        ObjectPath [Data\\ASSIMP\\MD3\\weapons2\\gauntlet\\gauntlet.md3]       
        ObjectShader [Diffuse]
        ObjectPosition [0.0f] [150.0f] [10.0f]
        ObjectRotation [0.0f] [1.0f] [0.0f] [0.0f]
        ObjectScale [1.0f] [1.0f] [1.0f]
        ObjectVisibility [1]
    ObjectEnd
    ObjectBegin
        ObjectId [5]
        ObjectName [RocketL]
        ObjectPath [Data\\ASSIMP\\MD3\\weapons2\\rocketl\\rocketl.md3]       
        ObjectShader [Diffuse]
        ObjectPosition [50.0f] [150.0f] [10.0f]
        ObjectRotation [0.0f] [1.0f] [0.0f] [0.0f]
        ObjectScale [1.0f] [1.0f] [1.0f]
        ObjectVisibility [1]
    ObjectEnd
    ObjectBegin
        ObjectId [6]
        ObjectName [Boy]
        ObjectPath [Data\\ASSIMP\\MD5\\boy.md5mesh]       
        ObjectShader [Diffuse]
        ObjectPosition [-100.0f] [200.0f] [0.0f]
        ObjectRotation [1.0f] [0.0f] [0.0f] [90.0f]

        ObjectScale [0.6f] [0.6f] [0.6f]
        ObjectVisibility [1]
    ObjectEnd
SceneEnd



As one can see, level file consists of all the objects in the scene to be loaded. It has ID & name provided by user, asset location, shader to be attached to the mesh, its position, rotation, scaling & lastly visibility information. I Had to write simple Text file parser to accomadate this though. Parse, call AssimpLoader, load each model & map it to GameObject using GameObjectManager class.  Rendering / Updating of mesh is quite simple. You can choose to Render / Update everything or choose based on ID, name or even visibility. 





Screenshot shows MD2 mesh ( tank ) , MD3 ( quake assets, I am sure you know they are Railgun, BFG, Rocket Launcher & my favourite Gauntlet! ) & MD5Mesh ( Boy in yellow!!! ) asset. MD5 mesh has been taken from following website : http://www.braynzarsoft.net/index.php?p=D3D11MD51. Brilliant tutorial for loading MD5Mesh using DirectX 11. 

Obviously, there is lot more to be added. Final screenshot also shows integration with Shader pipeline. Red directional light doing basic NdotL math to calculate illumination. Now, since any mesh format which ASSIMP supports can be rendered using my custom scene description file, I am kind of happy. Next logical step would be to enhance lighting system & make it a part of scene description, Obviously, find better models to render :) 

UPDATE : Render with few Better Models 




But at end of the day, it was wonderful feeling to see Quake guns which I grew up using to frag, ended up being rendered using my framework :) 

Cheers.

No comments: