Thought I’d talk a bit about the (probably slightly over engineered) wind system in BeeBeeQ.
Martin wanted wind affecting the plants to bring life to the scenes, and I’d wanted to do something along the lines of Uncharted 4’s wind system after reading their Siggraph presentation.
Nearly all shader wind systems work by baking a bit of extra data into the affected models vertices, a lot have a single float for wind strength, and Uncharted’s system took it further by baking pivots into the data.
The gist of how it works is for each vertex you define a set of pivot positions and in the shader you rotate the vertex position around each pivot in turn, as in a parent child relationship.
The first step was generating the pivots, in Uncharted pivots were artist authored using in house developed tools, I wanted something more automatic which wouldn’t require re-importing all the affected models.
I decided 2 pivots would be enough for BeeBeeQ, or rather since each pivot position takes 3 floats and I needed high precision for static batching I was limited to 2 pivots.
I ended up with a few strategies to automatically generate the pivots based on a few cases listed below with some code snippets;
Renderer Bounds Base
My default strategy, set each vertex pivot was set to it’s render bounds base, this let the model rock on it base in response to wind.
Bounds bounds = renderer.bounds; vertPivot0 = new Vector3(bounds.center.x, bounds.min.y, bounds.center.z);
Martin had already set up the tree as skinned meshes for exactly this purpose, each branch on the tree is a skinned mesh. To get the pivot I found the highest weighted bone per vertex and the root bone. This is closest to Uncharted’s version I think. This had another advantage as it meant the skinned meshes could be converted to normal meshes and batched.
vertPivot0 = skinnedMeshRenderer.rootBone.position; vertPivot1 = GetBestBone(skinnedMeshRenderer, vertIndex);
I defined a line by 2 points and for each vert set its pivot to its closest point on the line.
https://forum.unity.com/threads/math-problem.8114/#post-59715 vertexPivot0 = ClosestPointOnLine(linesStart, lineEnd, vertexPosition);
Multiple Combined Meshes
Many objects in BeeBeeQ are made up of multiple instances of the same mesh, for example the ivy is a big mesh made up of hundreds of the same 22 vert leaf. By picking a vert to act as the pivot (for the ivy leaf its vert 18) I was able to give each vert in the mesh a pivot.
vertPivot0 = verts[(vertIndex % subMeshVertCount) + subMeshPivotIndex];
This obviously makes a few assumptions but worked in many cases throughout BeeBeeQ.
For grass I set each verts pivot to be its position projected onto the renderers base.
vertPivot0 = new Vector3(vertPosition.x, bounds.min.y, vertPosition.z);
So that’s all the ways pivots are generated in BeeBeeQ so far, the next post will go into how the pivot data is encoded into the games meshes.