Wow, 2 months went by already! Loads of work has been since then though, as mentioned in the last update I wanted to add sprite data exporting so that you could playback effects buy just uploading the sprite data to the GPU and interpolating between frames for smooth playback. And this is exactly what I’ve managed to do and it works extremely well! This is just an initial proof of concept but now the basics are done and now I know it will work well I can continue to improve and add features to it. Here’s how it works:
Sprite Data Exporting
Like the sprite sheet exporter tab, the new Sprite Data tab will record an effect to memory. You can then export this data (under the file menu) which you could then load and playback the effects by either using CPU interpolation (slow) or upload the data to the GPU to use a compute shader to interpolate the data. This is much faster and also has the advantage that all the sprites can just live on the GPU and not have to be uploaded from CPU to GPU each frame. Here’s some questions/answers about what this means:
Doesn’t this take a lot of memory?
That’s where compression comes in. When the animation data is created it records the effect at 60 frames per second (you can adjust this if you want). This will of course create a lot of sprite data especially if the effect has a lot of particles. What you can then do is adjust the compression of the animation under the sprite data settings which will take the sprite data and organise it into much fewer frames so that much less sprite data is used. Because of the interpolation used between frames the animation remains super smooth. In the next 2 images you can see an example of an animation that is 600 frames that is compressed into only 60 frames. You can hover the (i) icon on the tab to see how much memory is used before and after the compression. In this case the uncompressed effect uses nearly 1 million sprites and 65mb of sprite data, but compressed down this is only 113000 sprites and only 7.5mb.
How is this used in a game/app?
The library is still under heavy development but basically right now you load the data using the library and calling LoadSpriteData with your own callback to load the particle shapes just like when loading a library. Then depending on the renderer you’re using you have to set up the compute shader (I will provide an example compute shader), upload the sprite data which consists of the following buffers: Sprite data buffer containing all the sprites of all frames and all effects you’ve bundled into the file, an image data buffer that the shader needs to lookup uv coordinates and such, emitter properties buffer which contains a few things like the sprite handle to use and an outgoing sprite buffer which is written to by the compute shader each frame for passing on to the vertex buffer to draw all the sprites. Finally there’s 2 buffers that need uploading to the GPU each frame, they are an animation instances buffer, this buffer contains the positions, scale and frame to play for each animation you’re currently wanting to play and also an offsets buffer which the shader uses to know where to lookup the sprites for each frame. These buffers are very small so there’s very little overhead to upload them. The library makes it very easy to access these buffers and get everything you need to prepare them for uploading.
What would be the main use for this type of effect playback?
Sprite data would be ideal for static effects like explosions, sparks, torches smoke columns or anything where the position of each sprite can just stay relative to the position of the animation. They wouldn’t be ideal for things like smoke trails where each particle needs to spawn and play out in place in a more dynamic way. You might have a dungeon with torches hanging on the wall where you could put a looped fire effect and add as many instances of it as you want. This could be suspended whenever they’re not in view so that they’re not rendered. All the CPU has to do is increment the frame being played for each animation instance which is handled by the library.
Is there more docs available or examples?
Not yet but the next thing I want to do is refactor the renderer that I’m using so that I can make it much more portable and show examples of how to use the library. That will be the aim of the next update.
What other features will be added?
Yes there’s loads of things I want to add. Right now there are no bounding boxes for the animation so there’s no way to really know if an effect is on screen or not for off screen culling so that needs adding. I also want to add more keyframe features for preparing the animations and also for looped effects it would be nice to add a feature so that you can include the start of the effect like a fire igniting, then a loop section which seamlessly loops, then add an effect finishing section for when the fire goes out. So you could start a fire, have it loop as many times as you want then let the effect play out finish in nice smooth way.
This is still early days for this feature but I really hope the next update I can upload some examples to play with – not just for the sprite data but also just for using the library in general.
New Optimisations
I made a huge improvement to how ordered particles work. Not only is ordering particles a lot faster now, but it also means that the footprint of the library is much smaller. Before this change there were 3 lots of control functions depending on whether the particles are unordered, ordered by age or ordered by depth. Now with the new way the particles can all be updated in an unordered way (which was always the fastest way to do it), but the writing of the sprite data is where the ordering happens so it removes the need for those extra functions. It also means that the ordering routines work on a much smaller amount of data (an index of the particle and a float value that dictates the order) so they are much faster. This also makes it way easier to add new features in the future as I only have to add a single control function for it in most cases (sometimes 2 is necessary if it involves 2d or 3d effects).
Next?
As mentioned, I will refactor my renderer, 1) to make releasing examples of library usage a lot easier and also 2) I can make it multiplatform while I’m at it and look at a Mac release at some point.
Also, I’m still unhappy with how I’m managing memory in the library. It’s not bad but I’d really like it so that you exactly how and where the library is allocating memory so I’ll be look at memory allocators and looking to implement something soon.
Here’s the full list of changes in the latest version:
* New sprite data tab that lets you setup effects to export the sprite data for playback directly using either CPU or GPU interpolation (via a compute shader)
* Big optimisation in the library for how ordered particles are managed.
* Fixed some bugs around how memory is managed.
* Layer property now has +/- signs for a bit more usability
* New button to auto set the spawn amount based on area grid sizes
* Library has much better consolidated code so 2d and 3d functions can be updated by the same function where possible.
* Refresh button on sprite tabs now greyed out if there’s nothing to update.
* Fixed a crash after deleting a sub effect in some situations.
* Set a maximum on the number of frames allowed for a particle shape (256)
* Solved an issue with particles not interpolating correctly when traversing a line and wrapping to the start of the line in loop mode.
* Removed offset into frame option when camera is in isometric mode.
* Added option on sprite tabs to match the frame length to the loop length of the effect or emitter (whichever is highest).
* Fixed some issues around shapes and also improved how the default shape is handled: If the default shape is not used in any emitters then it’s not included in the save file, otherwise it is.
* Added a small icon next to the colour ramp that shows how the colour will animate overtime.
* The actual name of the shape used is displayed in the properties instead of default shape.
* Added a new Update mode which will match the refresh rate and basically update using delta time rather then fixed step. Known issue: amount spawned is currently incorrect for this mode.
* Fixed some rendering validation issues on the sprite sheet tab.
* Z value of emitter and effect handles now display on the history tab.
* Fixed bug when undoing the deletion of sub emitters/effects.