The Model Generator is part of the SEED system and can copy arbitrary render models together to form a combined model.
For TDM v1.03, there was a model combiner implemented in Darkmod/ModelGenerator.h and Darkmod/ModelGenerator.cpp.
It contains a function that takes a list of render models (usually LOD stages, but can be arbitrary models) and a list of positions (offset, rotation, scale and color) and duplicates the appropriate stage as often as nec.
To speed up rendering, all the duplicated render models are thus combined into one giant model.
Whenever the player moves, some of the parts of the combined model need to be changed (f.i. when an LOD stage no longer has a shadow, or uses a different model, or this model at this position should now be invisible). Currently, this means the entire model is regenerated from scratch.
TODO describe here the current working version
Unfortunately, the naïve approach of just copying all the rendermodels together everytime something changes at the LOD level works, but is very unpractical. This is because he calculation of the shadow of the combined rendermodel takes very long (on the order of a factor of 12..20 longer than copying the data together). This means updates to the rendermodel take on the order of 300ms (1/3 s), and this makes it stutter a lot when updating such a combined model.
Although the curent version avoids calling FinishSurfaces() as much as possible, if there are shadow casting surfaces and the stage should cast a shadow, we cannot eliminate that call and have thus to incur the penalty.
So it might be possible that we only update the visible portion of the combined model, but leave the shadows alone.
Possible Pseudo Code
This is a bit of pseudo code describing how the model combiner could work:
If hModel is NULL (we combine for the first time):
Step 1: Collect all the visible surfaces (Because it is not possible to remove Surfaces, we need to collet all possible surfaces upfront, and create them as empty surfaces) Empty the list of NeededSurfaces: For every possible LOD stage source model: For all surfaces of this source model: If this surface ! IsDrawn() && SurfaceCastsShadows(): * ignore for now If this surface IsDrawn() && ! SurfaceCastsShadows(): * include in NeededSurfaces (and adjust numVertices/numIndexes for this surface). If this surface IsDrawn() && SurfaceCastsShadows(): * find or create shader variant with noshadows, include this in NeededSurfaces (and adjust numVertices/numIndexes for this surface). Step 2: Count how many shadow vertices and indices we need: if (at least one position has !noshadows and LOD stage 0 has a shadow) set numVertices = 0 set numIndexes = 0 Take LOD stage 0: For all surfaces of this source model (LOD stage 0): If this surface ! IsDrawn() && SurfaceCastsShadows(): * add surface->numVertices to numVertices * add surface->numIndexes to numIndexs multiply numVertices && numIndexes by the number of positions (we use for each position the same shadow model from LOD stage 0). for the shadowsurface: allocateTriangles() // we need to call FinishSurfaces() below: needFinish = true;
Else hModel is not NULL (we update an already existing model):
for all used surfaces on hModel: if !SurfaceCastsShadow(): FreeSurfTriangles() (leave shadow surfaces alone)
Now generate the combine rendermodel:
for each in NeededSurfaces: if surf->numVertices > 0: FreeSurfTriangles() for each offset: take the appropriate LOD stage model: for each surface on this model: map to the NeededSurface, then copy data to it clean up: if needFinish, call hModel->FinishSurfaces();