Particle System
DOWNLOAD HERE!!

Summary

A particle system is a technique in game physics, motion graphics, and computer graphics that uses a large number of very small sprites, 3D models, or other graphic objects to simulate certain kinds of "fuzzy" phenomena, which are otherwise very hard to reproduce with conventional rendering techniques - usually highly chaotic systems, natural phenomena, or processes caused by chemical reactions [Source: Wikipedia]

The Particle System that I am providing here has a platform specific implementation for both OpenGL and DirectX. There is an easy to use, platform independent implementation on top of that. That interface can be extended in C++ and also in Lua.

 

Setup

The source project can be downloaded here. To begin using this Particle System, follow these steps:

  1. Add an existing project to your Solution -> Specify the ParticleSystem Project.
  2. Add the appropriate property sheets (if not already added).
  3. Inside AssetBuildFunctions.lua, add the following code snippet:

NewAssetTypeInfo( "particles",

        {

                GetBuilderRelativePath = function()

                        return "ParticleSystemBuilder.exe"

                end,        

                

                RegisterReferencedAssets = function( i_sourceRelativePath )

                        local sourceAbsolutePath = FindSourceContentAbsolutePathFromRelativePath( i_sourceRelativePath )

                        if DoesFileExist( sourceAbsolutePath ) then

                                local file = io.open( sourceAbsolutePath, "rb" )

                                local fileContents = file:read("*all")

                                file:close()

                                includes = (load (fileContents .. "\nreturn Includes"))()

                                

                                if includes ~= nil then

                                        for i = 1, #includes do

                                                RegisterAssetToBeBuilt( includes[i], "particles" )

                                        end

                                end

                        end

                end

        }

)

Interface

Using a particle effect is the same as using a mesh. You bind it with an effect and update the transform based on the effect’s world position and then render it. The Particle System uses reference counting, the same way as a mesh. There are only three function exposed: Create, Update and Render.

Just like a mesh, an effect needs to be sent to the Graphics thread using a bucket, and once it is no longer required, its reference count needs to be decremented. There is, however, no reference counting for particle system.

Lua Scripts

The behaviour of the effect is defined inside Lua files. These lua files are executed as separate processes when the effect is alive inside the game. For communicating between Lua files and C++ interface, some of the following things need to be kept in mind:

  1. None of the Lua scripts should return anything.
  2. The Lua script which is used directly by the C++ needs to have a function called Init. Any other Lua script which describes the effect but isn’t used by the C++ directly, doesn’t need to define the Init function.
  3. The effect can be written in multiple Lua files and all those different files can be included in another Lua file which can be used by the C++. If a Lua file needs to include another file, it needs to specify that file in a table, Includes.


    These included files can include their own lua files, as long as they specify those in the Includes table in their own script.
    Aside from this, there is one more thing required to start using the included lua file. You need to load them using the following code:


    In the first line, you specify which file (mentioned inside the Include table) you want to load. Here, I am loading the Lightening effect. In the fourth line, you specify the variable in which you want to load that file. Here, I am loading it inside the Thunder variable.
  4. In order for Lua to communicate with C++, there is a common class called Particle which transfers the data between the two. Inside Lua, Particle is a table which contains several other tables such as Position, Color, Update. Inside C++, Particle is a class, but it is automatically handled by the ParticleSystem Class, so you don’t have to worry about it.

  5. The Particle table may have anything inside it, but the C++ class expects the following specifically from it:
  1. ParticleType: Line, Traingles, Quads etc. Currently, only two are supported; Line(0) and Quads(1).
  2. MaxParticleCount: Maximum number of particles the effect can have.
  3. CurrentParticleCount: The number of active particles the effect has at a given frame.
  4. LineWidth/QuadSize: The width of the line, or the size of the quad (square).
  5. Position: If it is a line, then the Position table is an array of lines. Every line has two points: Starting point and End point, and each point has three floats: x, y, z. If it is a quad, then the Position table is an array of center points for every quad.
  6. Color: Same as position, except every point has four integers: r, g, b, a (0 - 255)
  7. Update Function: An update function that expects one float argument i.e. dt.

A line type Particle inside Lua.


A line type Particle inside Lua.

A quad type Particle inside Lua.

  1. Lua communicates with other Lua scripts using the same Particle class (i.e. the file which is being included must have a Particle table), however the Particle table can have anything inside. It depends on the programmer creating the Lua script files.
  2. Inside AssetsToBuild.lua, specify the files which are being used directly by C++, if not already included. If a lua script A includes lua script B and C and only A is included by C++, then only A needs to be specified. B and C is optional.

Drawbacks

This project has some drawbacks that one must know. It trades performance for flexibility. It allows you to create Particle effects without recompiling the entire game, but since the entire behaviour is happening inside Lua and then that data needs to be transferred to C++, it makes it a tad bit slower. Also, since it is happening inside Lua, it is not that trivial when it comes to debugging.

Things I learnt and applied from this class

This project definitely took me more than 30 hours and even though I can spend another 10 hours on it, polishing it and making it more robust, I am really happy and content with what i accomplished. I was able to do what I had originally thought, and I am proud of that.
Mine was a graphics feature, and hence, had code which was very different for different platforms. But I tried to replicate the same thing as I did with meshes and shaders, and in the end, I did a pretty good job. Earlier I had issues with colors happening only in one platform or crashes happening in other and that took me some time to fix it. Also, my particle system had an Update and Render function and both of them used the same buffers (or memory) and yet were being executed on different threads. Figuring that out really took a lot of time. Because of that, OpenGL was not displaying the effects at all in the beginning.

There was another cool thing I did when making it platform independent. I remember JP, my professor, asking us this at the beginning of the class: Is there a way to make a common platform independent header without using any if-defs. I couldn’t come up with any during that time, but for my Particle System, I got clever. I forward declared a structure inside my header file, ParticleInterface. Inside .d3d.cpp and .gl.cpp, I actually defined that structure which contained all the platform specific data structures. Because of this, when you look at the header file, you don’t find any if defs, and it is the same for both platforms. I thought it was pretty cool.

Direct3DOpenGL

The second most interesting thing I did was with the AssetBuilder thing. The reason was, my Lua files were including other lua files and not returning anything. All the asset files we used so far always returned a table, mine didn’t. Mine also were being executed as a process and then linking with other lua files, This created path issues because lua files are always executed in the same path as the C++.exe. And, I wanted to do something similar to what I did in Shaders, I specify one file, and all the dependent files are automatically built. But the paths then would be different when building the assets and when actually executing them. How do I fix that? Also, some lua files required to return a table (the ones included by other lua files) and some didn’t (the ones only included by C++). How do I make that consistent? because my goal was to have these lua files reusable. Also, I wanted to include other Lua scripts while running the game, not while building the assets. How do I prevent that? So I came up with a very crafty solution.

For the builder, I used a table called Includes inside my Lua assets. There, I specified the Content Folder relative path of all the lua files being included.

And then inside the AssetsBuildFunction, I would read the whole lua file (using file read/write, not dofile) and store it as a string, append “return Includes” and then execute the entire thing using load.

This way, I can get the include table and not load the other lua table while I am building. And also, not care whatever I am doing in that file. So Task 1 accomplished: I have something similar to Shader thing. Now or Task 2: how do I load other Lua files during game?

For that, I came up with another function called Init. Inside Init, I would load the other lua files. But how do I load them? Same as AssetBuilder, but this time, I appended “return Particle” instead and it worked.

Also, note how I appended “Data/” while reading the file. This resolved my path issues. And yes, it took me 5-6 hours to come up with this solution, and a lot (a very lot) of hair pulling, but I think it was pretty neat. At one point, I had almost given up, I was about to compromise on the reusability feature, but it worked.

All in all, I am pretty happy with this project and my accomplishments. There are still a few things I can improve on and make it better, but I doubt if that’s the end goal and if it’s gonna help me much in learning new things. I didn’t make any compromises, developed what I intended and learnt a great deal. I also tried to keep everything as simple as possible so that others can use it. For example, I wanted to use glDrawArraysInstanced to make my effect faster, and I had to change OpenGlExtensions\20170823\glext.h to make that work (that was also a fun thing in itself but I didn’t talk about it since I later decided against using it) but that would have required other students doing a lot more changes on their project. Now I am looking forward to other students using this and curious on their take: is my project too complicated to use or is it just fine?