For a personal project of mine, I’m working/playing;) with LEGO bricks in the engine and want some advice regarding optimizations. Get ready for a wall of text. (Note that this is entirely personal and non-commercial, and I don’t plan on releasing any game without asking the LEGO Group)
My current workflow is to make a LEGO model using either Digital Designer or LDraw which are designed for working with LEGO units. Once done, I reassemble the model in 3ds Max using my own custom library of bricks modelled after the LDraw library (since LDraw itself is unsuitable due to a very specific 3D format).
Each brick that I model will have three LODs, the first one will be the normal brick as optimized as possible. The outer edges are beveled and use custom normals to fake rounded corners without using smoothing groups or normal maps so that each brick stands out when stacked together. The second LOD is just the basic convex shape of the brick (so no studs and no interior) but with the beveled edges whereas the third LOD is just a heavily optimized convex shape. The final model in 3ds Max is optimized as best as possible by using a script that replaces all selected models with a single source instance, according to how visible the bricks are and where the model itself is used (studs are rarely visible so can be removed, the same for the interior/underside). Rough estimates as to poly-count, including bevels, are around 60-150 at minimum (for the simple bricks) to around 1,000 for the more complex bricks (which comes mostly from geometry that can be removed when used in certain ways, which results in around 400-700 max).
As for the materials, I have a single master material with a sprite sheet containing every official LEGO color. It uses a custom material function based on the flipbook function to select an index from the palette. Every color is then a material instance with the index set correctly. Since the bricks don’t use normal textures, those materials can be reused everywhere (decals are going to be done differently, perhaps as either engine decals or offset faces).
However, the optimization process can only go so far and I don’t want to/can’t spend hours on removing every single unnecessary triangle for a whole model. In some cases the ingame models aren’t too big or are simple enough to remove non-visible triangles quickly.
I’m planning to test a few things out, but that’s going to take time, so I also wanted some advice ahead of time. One case study I plan to try out is having every brick as a separate static mesh imported into the engine. I’ll hopefully be able to create a blueprint or c++ class that can import an LDraw file (a text file with lists of bricks used) or an LDD file (an xml file with bricks used) that automatically assembles the final model by placing the correct bricks (named using the official part IDs) and assigning the correct materials. I don’t have a plan for that system yet, but that is not the most critical part of the case study. The most important part is about draw calls, geometry and culling. If I assemble a LEGO model using individual bricks (with LODs) rather than a single static mesh from 3ds Max (with multiple materials), either with (hierarchical) instanced static meshes or not, could this provide some optimization? Each model would then be a blueprint class.
TL;DR, I know. But I wanted to provide as much information as I could. At the same time, I’m also aware of the standard optimization rules (I have worked for a mobile developer so I know a thing or two about draw calls and poly-count), and normally I would remove as much geometry as I could, but I also want to optimize the time spent so I have more time for other parts of the project as it’s entirely done in my spare time. If it doesn’t work, then no harm no fowl.
As a small impression, I’ve added a picture of a cutscene model that I’m working on (it has almost 180,000 triangles and 200,000 vertices, but it’s also a lot more complex than the ingame models will be):