Renderer refactoring: On the Renderer side, we did some housecleaning and optimizations. Based on the refactorings from last month to increase the object count, we started to simplify the data upload to the GPU. Previously the CryeEngine system is based on reflection, so that the code could find out what data was needed on the GPU and only upload this data. While this sounds like a solid idea, finding out what was needed was more expensive than a straight data upload. So we began to remove those reflection code paths in the time critical areas. This also improved the readability of the code, as we can now see what the code does and not the logic to find out what to do. Related to this change, we also ensured to only upload the same data once to GPU. Previously it uploaded a data buffer for each object, and then uploaded the same data again if the code decided to use instancing. This is now fixed.
Data Patcher: On the Data Patcher (the tool which will be responsible to create the data for the engine to use when we switch to incremental patching), we made a little progress by better defining how to store the data. Not much reportable progress here as much work is about infrastructure discussions.
Optimizations: To further optimize the streaming code, we added timeslicing support to it again. This way the cost to update the distance to objects not visible to the player is done less frequent.
Tag System comes into the ZoneSystem: Initial support was written to support storing tags inside the ZoneSystem. A Tag is a small string which we use to give context information to an entity object. For example if we want to know if something is a chair, we can tag it as a chair. This way the AI system can ask all objects and find out if they are chairs. We already have such systems inside the engine but those are lacking spatial information, so they can only answer: is this object a chair, but not “find me all chairs around me”. This lead to some in-efficient solutions as the code had to brute force get many objects and check their type. To overcome this limitation we are moving the support for tags into the ZoneSystem, our spatial position system. This required some changes and new systems:
Storing and comparing a string is not very efficient on a computer (but very convenient for a human, thus we need it), so we implemented a Trie to allow us to very an efficient way to map unique strings to a fixed integer range. (We wanted to get an integer range instead of a hash as a range allows us some better broad phase checks and more efficient data storage)
Since not all data types which we stored inside the ZoneSystem require tags, we made the whole zone system more flexible to allow the client code to specify the properties to store per object type. This also reduced our memory usage in crusader by 50MB.
We implemented a specialized allocator for the tags so they can be efficiently culled by the low level zone system, which is implemented in SIMD, so the tags must follow a certain size and alignment.
And as a last thing, we implemented code to allow filtering tags by a DNF (Disjunctive normal form), which is a fixed format and can be used for efficient checking of arbitrary boolean expressions.
Runtime Skel-Extensions: The character customization system in Star Citizen is internally using an engine feature called “skinned attachments”. With skinned attachments it is possible to replace every deformable item on a character (i.e. cloth, shoes, spaces suits, helmets, etc) and even entire body parts such as faces, hands, or upper and lower body parts. Each skin-attachment has its own set of joints that are automatically animated and deformed by the base skeleton. It is also possible to use skinned attachments that have more joints and different joints then the base skeleton and it is possible to merge all types of skeletons together, even skeletons from totally different characters. That means you can have a minimalistic base skeleton which can be extended by an arbitrarily complex skinning skeleton. In the original CryEngine this was an offline- or loading-time feature, because the entire process was pretty CPU intensive. For Star Citizen we turned this into a runtime-feature that allows us to extend a base-skeleton anytime while the game is running, no matter if the character is alive and playing animations or in a driven- or floppy-ragdoll state. This means that you don’t have to know in advance the type of joints you might need in the base skeleton nor do you have to carry extra joints around just in case you might need them. Instead the system allows you to add new joints at will and whenever they are required.
Full-Body Experience: we also invested a lot of time to improve the full-body experience in first person. Our main goal was to make the head-bobbing customizable. In Star Citizen the head-bobbing is a natural side-effect of the mocap data, because third- and first-person are using the same animation. To make the controls as smooth and precise as possible, we implemented a new IK-solution to eliminate all unwanted effects from the 3rd person body animations on the first person view and weapon handling.