(Re-posted as a proper entry since it’s apparently impossible to format comments properly in this blog)
I was trying to answer the question from “George” in another post. So, as I was saying, there’s no big difficulty in this one:
- First the decal is created. I use the clipping code from Eric Lengyel in GPG2. The output is an arbitrary number of triangles. Let’s call that the decal geometry.
- The decal geometry is passed to a decal manager, along with an owner (the mesh upon wich the decal has been created). The geometry is copied to a cycling vertex buffer. The start index within the VB and the number of vertices for this decal are recorded. Those two values form an interval [a;b] within the vertex buffer, with:
a = start index
b = start index + number of decal vertices
I also bind the decal to its owner, i.e. any mesh can enumerate its decals if necessary.
- Decal culling is implicit: I just reuse mesh culling. I call the decal manager for each visible mesh, with something like this:
DecalManager->StartRender()
for each visible mesh
DecalManager->RenderMeshDecals(CurrentMesh)
DecalManager->EndRender()
- “RenderMeshDecals” enumerates all the decals of input mesh. As said before, each decal is an interval in the cycling vertex buffer. Intervals are pushed to a dynamic buffer but not rendered yet.
- “EndRender” performs an “interval reduction” pass on previous interval buffer. That is, intervals [a;b] and [b;c] are merged into a single interval [a;c]. Intervals [a;b], [b;c] and [c;d] would also be reduced to a single interval [a;d]. This is just to reduce the number of DrawPrimitive calls. For each remaining interval, there’s a DrawPrimitive call to render the decals from the VB.
- The number of decals is actually not unlimited: it’s limited by the size of the cycling VB. At the moment it’s big enough for 100.000 vertices. The buffer is cycling, meaning we wrap around the end of the VB and start overwriting its beginning when necessary. Old (overwritten) decals become automatically obsolete when the mesh tries to enumerate them and figures out their address (index) has been invalidated. There’s no need to remove decals from the VB when a mesh gets deleted: it will simply never be rendered anymore, we’ll not call “RenderMeshDecals” for it, hence its decals won’t get rendered. They will still be in the VB, but only until we overwrite them with new decals (i.e. it’s all automatic, nothing to do).
And that’s about it. Here’s an old picture with ~10.000 decals randomly spawned in the scene (old scene from Explora)
February 19th, 2008 at 2:10 am
Thanx for answer. And how do you perform “interval reduction� Is it based on material differences? And what about decals on dynamic rigid and skinned meshes - do you have it?
February 21st, 2008 at 3:09 pm
There are many ways to perform internal reduction but I use a simple 2-passes approach:
1) radix-sort by interval min,
2) iterate over them and merge.
Never showed up in any profile so I never tried to find something better. Different materials are just one more entry in the sort key. It looks a lot like batching meshes, actually.
I don’t support decals on dynamic meshes at the moment but it would be easy to add: it’s again an extra entry in the sort key. When a discontinuity is found (new world matrix, new material, new interval), a new DrawPrimitive call is issued. But just before that call, there’s an opportunity to change whatever needs to be changed (world matrix, texture, etc)