Model Combiner

From The DarkMod Wiki
Revision as of 10:55, 2 November 2010 by Tels (talk | contribs) (first stab)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

Introduction

In TDM v1.03, there is a model combiner implemented in Darkmod/ModelGenerator.h and Darkmod/ModelGenerator.cpp.

This contains a function that takes a list of LOD stages (e.g. their render models) and a list of positions (offset, rotation, scale and color) and duplicates the appropriate LOD stage as often as nec. To speed up rendering, all the duplicated render models are 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).

Optimization

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.

Soit must be possible that we only update the visible portion of the combined model, but leave the shadows alone.

Also, every surface in a model issues one drawcall per light. If you have two different LOD stages using the same texture, it is nec. to put the data of both of these into the same surface to save one drawcall per light.

For these reasons, we need a bit more complicated code:

Pseudo Code

This is a bit of pseudo code describing how the model combiner works:

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)

No 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();

See also