Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice
  2. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  3. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Book of the Dead - Wind bake pivot in to the mesh

Discussion in 'Graphics Experimental Previews' started by Spy-Shifty, Jul 2, 2018.

  1. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    Hi,

    sorry for this long post but I need to describe this wind technique a little bit more detailedly...

    I've read the latest blog of Book of the Dead (BOD) and studiet the wind shader.
    https://blogs.unity3d.com/2018/06/2...ene-building-and-content-optimization-tricks/

    For trees, you need a hierarchical setup for each part of the Tree:
    • Trunk, that rests on the ground.
    • Branches Level A, that are connected to the trunk.
    • Branches Level B, that are connected to the branches of Level A.

    To do so they used vertex color and the green channel to setup this hierarchy.

    • A value of 0 for the green channel of the vertex color would signify that it is the trunk
    • A value between 0 and 1 would be the branches level A
    • A value of 1 would be the branches level B

    So far so good. Now comes the tricky part...

    They wrote a script to bake the pivot information of each single vertex into the mesh.
    This is done by saving this information into the 3rd and 4th uv layer (3 dimensions) of the mesh. So they created a new mesh of that tree (copied the mesh into an new asset) and added the pivot info into the uv layers (assets named with baked_hierarchy).

    But they have decoded the pivot information. Because they need to create the hierarchy of the pivot and store them in a single 3 dimensional point. (seen in the shader)
    So they decoded the information of maximal 2 pivot and the direction of each pivot into a single float3 variable. The coding and decoding can be taken from the shader...

    Why 2 pivots / directions?
    Ok that should be clear, too.
    The trunk won't need a pivot because it's pivot is the origin.
    The level A branches need exactly 1 extra pivot. Because they are childs of the trunk. So the pivot describes the position where the origin of the branch lies.
    And the 2nd piviot is only used for the branches of level B. They are childs of the branches of level A...
    So the first pivot there discrips the origin of the level A branche and the second pivot descrips the origin of the level B branch.

    Again, so fare so good...

    As I noticed or not.
    I don't see the use of the vertex color in the shader. This leads me to the conclusion, that this information is only used to bake the pivot.
    And also each level A branch has a different value of the green channel. Is there some reason for this? Like, to identify connected vertex more easily?


    Long story short question...
    How did they calculate the pivot information from the vertex color and the vertex positions?
    Sadly, they missed to add the script for baking the pivot information into the mesh...
     
    Last edited: Jul 2, 2018
    adamhill and Alverik like this.
  2. reflectingb0t

    reflectingb0t

    Joined:
    Apr 10, 2018
    Posts:
    18
    Yeah, hope they make that script available for download soon..
     
  3. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    I've created my own
    but it uses the red vertex color channel to mark pivot regions

    Here is the bake script

    HierarchieBakerEditor.cs
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5. using System.Linq;
    6. using Unity.Mathematics;
    7. using System;
    8.  
    9. public class HierarchieBakerEditor : Editor {
    10.  
    11.     [MenuItem("Tools/VegetationTools/Bake Hierarchy")]
    12.     public static void BakeHierarchy() {
    13.         GameObject gameObject = Selection.activeGameObject;
    14.         UnityEngine.Object assetObject = PrefabUtility.GetCorrespondingObjectFromSource(gameObject);
    15.         bool t = AssetDatabase.IsMainAsset(gameObject);
    16.         if (!AssetDatabase.IsMainAsset(gameObject) && assetObject != gameObject) {
    17.             EditorUtility.DisplayDialog("BakeHierarchy", "Please select the source asset!", "ok");
    18.             return;
    19.         }
    20.  
    21.  
    22.         Transform transform = gameObject.transform;
    23.  
    24.         string gameObjectAssetPath = AssetDatabase.GetAssetPath(gameObject);
    25.         string[] splittedPath = gameObjectAssetPath.Split('/');
    26.         gameObjectAssetPath = string.Join("/", splittedPath.Take(splittedPath.Length - 1));
    27.  
    28.         DummyScriptableObject dummyAsset = CreateInstance<DummyScriptableObject>();
    29.         string dummyAssetPath = gameObjectAssetPath + "/" + gameObject.name + ".asset";
    30.         AssetDatabase.CreateAsset(dummyAsset, dummyAssetPath);
    31.         AssetDatabase.ImportAsset(dummyAssetPath);
    32.         AssetDatabase.Refresh();
    33.  
    34.         string prefabAssetPath = gameObjectAssetPath + "/" + gameObject.name + "_BakedHierarchy.prefab";
    35.         GameObject hierarchyAssets  = PrefabUtility.CreatePrefab(prefabAssetPath, gameObject, ReplacePrefabOptions.Default);
    36.  
    37.         MeshFilter[] sourceMeshFilters = gameObject.GetComponentsInChildren<MeshFilter>();
    38.         MeshFilter[] hierarchyMeshFilters = hierarchyAssets.GetComponentsInChildren<MeshFilter>();
    39.  
    40.         bool success = true;
    41.         for (int i = 0; i < sourceMeshFilters.Length; i++) {
    42.             Mesh bakeMesh = Instantiate(sourceMeshFilters[i].sharedMesh);
    43.             bakeMesh.name = sourceMeshFilters[i].sharedMesh.name + "_BakedHierarchy";
    44.             AssetDatabase.AddObjectToAsset(bakeMesh, dummyAsset);
    45.             try {
    46.                 BakeHierarchy(sourceMeshFilters[i].sharedMesh, bakeMesh);
    47.                 hierarchyMeshFilters[i].sharedMesh = bakeMesh;
    48.                 EditorUtility.SetDirty(bakeMesh);
    49.             } catch(Exception ex) {
    50.                 EditorUtility.DisplayDialog("BakeHierarchy", ex.Message + " \n" + ex.StackTrace, "ok");
    51.                 success = false;
    52.                 break;
    53.             }
    54.         }
    55.  
    56.         if (!success) {
    57.             DestroyImmediate(dummyAsset, true);
    58.             DestroyImmediate(hierarchyAssets, true);
    59.             AssetDatabase.Refresh();
    60.         } else {
    61.  
    62.             EditorUtility.SetDirty(hierarchyAssets);
    63.             EditorUtility.SetDirty(dummyAsset);
    64.             AssetDatabase.SaveAssets();
    65.             AssetDatabase.ImportAsset(prefabAssetPath);
    66.             AssetDatabase.ImportAsset(dummyAssetPath);
    67.             AssetDatabase.Refresh();
    68.  
    69.             EditorUtility.DisplayDialog("BakeHierarchy","Bake complete", "ok");
    70.         }
    71.     }
    72.  
    73.     private static void BakeHierarchy(Mesh mesh, Mesh bakeMesh) {
    74.         Color32[] colors = mesh.colors32;
    75.         Vector3[] vertices = mesh.vertices;
    76.         int[] triangles = mesh.triangles;
    77.  
    78.  
    79.         Dictionary<byte, Vector3> branchPivots = new Dictionary<byte, Vector3>();
    80.         Dictionary<int, Vector3> pivots = new Dictionary<int, Vector3>();
    81.  
    82.         // build maps;
    83.         IEnumerable<IGrouping<byte, int>> hierarchyMap = colors.Select((color, index) => new { color, index }).GroupBy(c => c.color.g, c => c.index);
    84.         IEnumerable<IGrouping<byte, int>> pivotMap = colors.Select((color, index) => new { color, index }).Where(c => c.color.r == 255).GroupBy(c => c.color.g, c => c.index);
    85.  
    86.  
    87.         IGrouping<byte, int> trunc = hierarchyMap.Where(g => g.Key == 0).FirstOrDefault();
    88.         foreach (var vertex in trunc) {
    89.             pivots.Add(vertex, Vector3.zero);
    90.         }
    91.  
    92.         IEnumerable<IGrouping<byte, int>> branches = hierarchyMap.Where(g => g.Key != 0 && g.Key != 255);
    93.         IEnumerable<IGrouping<byte, int>> branchePivots = pivotMap.Where(g => g.Key != 0 && g.Key != 255);
    94.  
    95.         int numberOfBranchVerts = branches.Count();
    96.         int numberOfbranchPivots = branchePivots.Count();
    97.         if (numberOfBranchVerts != numberOfbranchPivots) {
    98.             throw new Exception(string.Format("Branches has/have no pivot: ({0})", string.Join(", ",  branches.Select(x=>x.Key).Except(branchePivots.Select(x=>x.Key)))));
    99.         }
    100.  
    101.         //build Branches
    102.         var branchGroups = branches.Join(branchePivots,
    103.             branchId => branchId.Key,
    104.             branchePivot => branchePivot.Key,
    105.             (verts, pivotsPoints) => new { verts.Key, Collections = new { Verts = verts.ToArray(), Pivots = pivotsPoints.ToArray() } });
    106.  
    107.         foreach (var branch in branchGroups) {
    108.             Vector3 branchPivot = new Vector3(
    109.                 branch.Collections.Pivots.Average(pivotId => vertices[pivotId].x),
    110.                 branch.Collections.Pivots.Average(pivotId => vertices[pivotId].y),
    111.                 branch.Collections.Pivots.Average(pivotId => vertices[pivotId].z));
    112.  
    113.             int farthestAway = branch.Collections.Verts.OrderByDescending(vertId => Vector3.Distance(vertices[vertId], branchPivot)).First();
    114.  
    115.             Vector3 pivotDir = (vertices[farthestAway] - branchPivot).normalized;
    116.  
    117.             uint3 packedData = 0;
    118.             PackPivot0(branchPivot, pivotDir, ref packedData);
    119.  
    120.             float3 pivotPoint = UIntToSingle32Bits(packedData);
    121.             branchPivots.Add(branch.Key, pivotPoint);
    122.          
    123.             foreach (var branchVert in branch.Collections.Verts) {
    124.                 pivots.Add(branchVert, pivotPoint);
    125.             }
    126.         }
    127.  
    128.         //build Leaves
    129.         //var t2 = faces.Count();
    130.  
    131.         //var brancheTuples = branches.SelectMany(pair =>
    132.         //    Enumerable.Range(0, pair.Count())
    133.         //        .Select(x => pair.Key)
    134.         //        .Join(pair, key => key, value => value, (key, value) => new { key, value}));
    135.  
    136.         var brancheTuples = branches.SelectMany(pair => pair.Select(value => new { key = pair.Key, value }));
    137.  
    138.         //var t3 = branches.Count();
    139.         //var t4 = brancheTuples.Count();
    140.  
    141.         IEnumerable<int> leafIndexes = hierarchyMap.Where(x => x.Key == 255).SelectMany(x=>x);
    142.         IEnumerable<int> leafPivotIndexes = pivotMap.Where(x => x.Key == 255).SelectMany(x=>x);
    143.  
    144.         Dictionary<int, HashSet<int>> faces = triangles.Select((vert, index) => new { vert, index }).GroupBy(g => g.index / 3, i => i.vert).Where(g=> leafIndexes.Any(leaf=>g.Contains(leaf))).ToDictionary(g=>g.Key, g=>new HashSet<int>(g));
    145.         var t0 = faces.Count();
    146.         if (leafIndexes.Any() && leafPivotIndexes.Any()) {
    147.             var t1 = leafIndexes.Count();
    148.             var t2 = leafPivotIndexes.Count();
    149.  
    150.             IEnumerable<IGrouping<int, int>> leaves = leafIndexes.AsParallel().GroupBy(g => {
    151.                 var leafGroup = new HashSet<int>(faces.Where(f => f.Value.Contains(g)).SelectMany(f => f.Value).Distinct());
    152.                 return leafPivotIndexes.FirstOrDefault(p => leafGroup.Contains(p));
    153.             }, i => i);
    154.  
    155.             var t3 = leaves.Count();
    156.             //throw new NotImplementedException();
    157.             foreach (IGrouping<int, int> leaf in leaves) {
    158.                 Vector3 leafPivot = vertices[leaf.Key];
    159.  
    160.                 int farthestAway = leaf.OrderByDescending(i => Vector3.Distance(vertices[i], leafPivot)).First();
    161.                 Vector3 leafPivotDir = (vertices[farthestAway] - leafPivot).normalized;
    162.  
    163.                 var branch = brancheTuples.OrderBy(pair => Vector3.Distance(vertices[pair.value], leafPivot)).FirstOrDefault();
    164.                 uint3 packedData = 0;
    165.                 if (branch != null) {
    166.                     byte branchKey = branch.key;
    167.                     packedData = SingleToUInt32Bits(branchPivots[branchKey]);
    168.                     PackPivot1(leafPivot, leafPivotDir, ref packedData);
    169.                 }
    170.  
    171.                 float3 pivotPoint = UIntToSingle32Bits(packedData);
    172.                 foreach (var leafVert in leaf) {
    173.                     pivots.Add(leafVert, pivotPoint);
    174.                 }
    175.             }
    176.         }
    177.  
    178.         List<Vector3> uvPivots = new List<Vector3>();
    179.         for (int i = 0; i < vertices.Length; i++) {
    180.             if (!pivots.TryGetValue(i, out Vector3 pivot)) {
    181.                 throw new Exception(string.Format("No pivot found for Vertex {0}", i));
    182.             }
    183.  
    184.             uvPivots.Add(pivot);
    185.         }
    186.  
    187.         bakeMesh.SetUVs(3, uvPivots);
    188.     }
    189.  
    190.     static uint PackSFloatToFixed(float val, float range, int bits, int shift) {
    191.         uint BitMask = (1u << bits) - 1;
    192.         val = (val + range) / (2*range);
    193.         uint uval = (uint)(val * BitMask);
    194.         return (uval & BitMask) << shift;
    195.     }
    196.  
    197.     static uint PackUFloatToFixed(float val, float range, int bits, int shift) {
    198.         uint BitMask = (1u << bits) - 1;
    199.         val /= range;
    200.         uint uval = (uint)(val * BitMask);
    201.         return (uval & BitMask) << shift;
    202.     }
    203.  
    204.     // Needs to match shader packing in baking tool
    205.     static void PackPivot0(float3 pivotPos0, float3 pivotFwd0, ref uint3 packedData) {
    206.         float y = pivotFwd0.y;
    207.         pivotFwd0.y = 0;
    208.         pivotFwd0 = math.normalize(pivotFwd0);
    209.         pivotFwd0 *= math.sqrt(1 - y*y);
    210.  
    211.         packedData.y |= (y < 0) ? 0u : 1u << 16;
    212.  
    213.         packedData.x |= PackSFloatToFixed(pivotPos0.x, 8f, 10, 22);
    214.         packedData.x |= PackUFloatToFixed(pivotPos0.y, 32f, 12, 10);
    215.         packedData.x |= PackSFloatToFixed(pivotPos0.z, 8f, 10, 0);
    216.  
    217.         packedData.y |= PackSFloatToFixed(pivotFwd0.x, 1, 8, 24);
    218.         packedData.y |= PackSFloatToFixed(pivotFwd0.z, 1, 7, 17);
    219.     }
    220.  
    221.     // Needs to match shader packing in baking tool
    222.     static void PackPivot1(float3 pivotPos1, float3 pivotFwd1, ref uint3 packedData) {
    223.         float y = pivotFwd1.y;
    224.         pivotFwd1.y = 0;
    225.         pivotFwd1 = math.normalize(pivotFwd1);
    226.         pivotFwd1 *= math.sqrt(1 - y * y);
    227.  
    228.         packedData.y |= (y < 0) ? 0u : 1u;
    229.         packedData.y |= PackSFloatToFixed(pivotFwd1.x, 1f, 8, 8);
    230.         packedData.y |= PackSFloatToFixed(pivotFwd1.z , 1f, 7, 1);
    231.  
    232.         packedData.z |= PackSFloatToFixed(pivotPos1.x, 8f, 10, 22);
    233.         packedData.z |= PackUFloatToFixed(pivotPos1.y, 32f, 12, 10);
    234.         packedData.z |= PackSFloatToFixed(pivotPos1.z, 8f, 10, 0);
    235.     }
    236.  
    237.     public static unsafe float3 UIntToSingle32Bits(uint3 value) {
    238.         return *(float3*)(&value);
    239.     }
    240.  
    241.     //------------------------------
    242.  
    243.  
    244.     static float UnpackFixedToSFloat(uint val, float range, int bits, int shift) {
    245.         uint BitMask = (1u << bits) - 1;
    246.         val = (val >> shift) & BitMask;
    247.         float fval = val / (float)BitMask;
    248.         return (fval * 2f - 1f) * range;
    249.     }
    250.  
    251.     static float UnpackFixedToUFloat(uint val, float range, int bits, int shift) {
    252.         uint BitMask = (1u << bits) - 1;
    253.         val = (val >> shift) & BitMask;
    254.         float fval = val / (float)BitMask;
    255.         return fval * range;
    256.     }
    257.  
    258.     // Needs to match shader packing in baking tool
    259.     static bool UnpackPivot0(uint3 packedData, ref float3 pivotPos0, ref float3 pivotFwd0) {
    260.         if ((packedData.y & 0xFFFF0000) != 0) {
    261.             pivotPos0.x = UnpackFixedToSFloat(packedData.x, 8f, 10, 22);
    262.             pivotPos0.y = UnpackFixedToUFloat(packedData.x, 32f, 12, 10);
    263.             pivotPos0.z = UnpackFixedToSFloat(packedData.x, 8f, 10, 0);
    264.             pivotFwd0.x = UnpackFixedToSFloat(packedData.y, 1f, 8, 24);
    265.             pivotFwd0.z = UnpackFixedToSFloat(packedData.y, 1f, 7, 17);
    266.             pivotFwd0.y = math.sqrt(1f - math.saturate(math.dot(pivotFwd0.xz, pivotFwd0.xz))) * (((packedData.y >> 16) & 1) == 1 ? 1f : -1f);
    267.             pivotFwd0 = math.normalize(pivotFwd0);
    268.             return true;
    269.         }
    270.         return false;
    271.     }
    272.  
    273.     // Needs to match shader packing in baking tool
    274.     static bool UnpackPivot1(uint3 packedData, ref float3 pivotPos1, ref float3 pivotFwd1) {
    275.         if ((packedData.y & 0x0000FFFF) != 0) {
    276.             pivotFwd1.x = UnpackFixedToSFloat(packedData.y, 1f, 8, 8);
    277.             pivotFwd1.z = UnpackFixedToSFloat(packedData.y, 1f, 7, 1);
    278.             pivotFwd1.y = math.sqrt(1f - math.saturate(math.dot(pivotFwd1.xz, pivotFwd1.xz))) * ((packedData.y & 1) == 1 ? 1f : -1f);
    279.             pivotFwd1 = math.normalize(pivotFwd1);
    280.             pivotPos1.x = UnpackFixedToSFloat(packedData.z, 8f, 10, 22);
    281.             pivotPos1.y = UnpackFixedToUFloat(packedData.z, 32f, 12, 10);
    282.             pivotPos1.z = UnpackFixedToSFloat(packedData.z, 8f, 10, 0);
    283.             return true;
    284.         }
    285.         return false;
    286.     }
    287.     public static unsafe uint3 SingleToUInt32Bits(float3 value) {
    288.         return *(uint3*)(&value);
    289.     }
    290. }
    291.  
    DummyScriptableObject.cs
    Code (CSharp):
    1. public class DummyScriptableObject : UnityEngine.ScriptableObject {}

    Usage:
    Create a tree object. Trunc, all branches and leaves in a single mesh with y axis is top. If you use blender... rotate you object so that the top of the modle faces along the y achis!!!

    Vertex Colors:

    Green Channel:
    Trunc = 0
    Branches: each branch group a different greenvalue between 0 and 255
    Leaves = 255

    Red Channel: marks the pivotregion (mean of all vertex in that group = pivot, R = 255 only)
    Each group needs at least one red vertex
    Trunc won't need a pivot! The origin is the pivot of the trunk...

    If you have any questions, just ask.

    Have fun

    upload_2018-7-9_20-14-35.png

    upload_2018-7-9_20-15-33.png
     
  4. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    This is a debugger tool to display the baked pivots

    MeshBakedHierarchieDebugger.cs
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using Unity.Mathematics;
    4. using UnityEngine;
    5.  
    6. public class MeshBakedHierarchieDebugger : MonoBehaviour {
    7.     public enum Level {
    8.         all,
    9.         one,
    10.         two,
    11.     }
    12.  
    13.     [Range(0,3)]
    14.     public int uvLevel = 3;
    15.  
    16.     public Level hierarchieLevel;
    17.     private void OnDrawGizmosSelected() {
    18.         Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
    19.         List<Vector3> uvs = new List<Vector3>();
    20.         mesh.GetUVs(uvLevel, uvs);
    21.  
    22.         float3 root = transform.position;
    23.         foreach (Vector3 uv in uvs) {
    24.             uint3 packedData = SingleToUInt32Bits(new float3(uv.x, uv.y, uv.z));
    25.             float3 pivotPos0 = new float3();
    26.             float3 pivotPos1 = new float3();
    27.             float3 pivotFwd0 = new float3();
    28.             float3 pivotFwd1 = new float3();
    29.             bool pivotEnabled0 = UnpackPivot0(packedData, ref pivotPos0, ref pivotFwd0);
    30.             bool pivotEnabled1 = UnpackPivot1(packedData, ref pivotPos1, ref pivotFwd1);
    31.  
    32.             switch (hierarchieLevel) {
    33.                 case Level.one:
    34.                     if (pivotEnabled1 || (!pivotEnabled0)) {
    35.                         continue;
    36.                     }
    37.                     break;
    38.                 case Level.two:
    39.                     if (!(pivotEnabled0 && pivotEnabled1)) {
    40.                         continue;
    41.                     }
    42.                     break;
    43.             }
    44.  
    45.             pivotPos0 = transform.TransformPoint(pivotPos0);
    46.             pivotPos1 = transform.TransformPoint(pivotPos1);
    47.             pivotFwd0 = transform.TransformDirection(pivotFwd0);
    48.             pivotFwd1 = transform.TransformDirection(pivotFwd1);
    49.  
    50.             if (pivotEnabled0 && hierarchieLevel != Level.two) {
    51.                 Gizmos.color = Color.blue;
    52.                 Gizmos.DrawSphere(pivotPos0, 0.1f);
    53.                 Gizmos.color = Color.green;
    54.                 Gizmos.DrawLine(pivotPos0, pivotPos0 + pivotFwd0 * 10);
    55.             }
    56.  
    57.             if (pivotEnabled1 && hierarchieLevel != Level.one) {
    58.                 Gizmos.color = Color.red;
    59.                 Gizmos.DrawSphere( pivotPos1, 0.05f);
    60.                 Gizmos.color = Color.green;
    61.                 Gizmos.DrawLine(pivotPos1, pivotPos1 + pivotFwd1);
    62.             }
    63.         }
    64.     }
    65.  
    66.     float UnpackFixedToSFloat(uint val, float range, int bits, int shift) {
    67.         uint BitMask = (1u << bits) - 1;
    68.         val = (val >> shift) & BitMask;
    69.         float fval = val / (float)BitMask;
    70.         return (fval * 2f - 1f) * range;
    71.     }
    72.  
    73.     float UnpackFixedToUFloat(uint val, float range, int bits, int shift) {
    74.         uint BitMask = (1u << bits) - 1;
    75.         val = (val >> shift) & BitMask;
    76.         float fval = val / (float)BitMask;
    77.         return fval * range;
    78.     }
    79.  
    80.     // Needs to match shader packing in baking tool
    81.     bool UnpackPivot0(uint3 packedData, ref float3 pivotPos0, ref float3 pivotFwd0) {
    82.         if ((packedData.y & 0xFFFF0000) != 0) {
    83.             pivotPos0.x = UnpackFixedToSFloat(packedData.x, 8f, 10, 22);
    84.             pivotPos0.y = UnpackFixedToUFloat(packedData.x, 32f, 12, 10);
    85.             pivotPos0.z = UnpackFixedToSFloat(packedData.x, 8f, 10, 0);
    86.             pivotFwd0.x = UnpackFixedToSFloat(packedData.y, 1f, 8, 24);
    87.             pivotFwd0.z = UnpackFixedToSFloat(packedData.y, 1f, 7, 17);
    88.             pivotFwd0.y = math.sqrt(1f - math.saturate(math.dot(pivotFwd0.xz, pivotFwd0.xz))) * (((packedData.y >> 16) & 1) == 1 ? 1f : -1f);
    89.             pivotFwd0 = math.normalize(pivotFwd0);
    90.             return true;
    91.         }
    92.         return false;
    93.     }
    94.  
    95.     // Needs to match shader packing in baking tool
    96.     bool UnpackPivot1(uint3 packedData, ref float3 pivotPos1, ref float3 pivotFwd1) {
    97.         if ((packedData.y & 0x0000FFFF) != 0) {
    98.             pivotFwd1.x = UnpackFixedToSFloat(packedData.y, 1f, 8, 8);
    99.             pivotFwd1.z = UnpackFixedToSFloat(packedData.y, 1f, 7, 1);
    100.             pivotFwd1.y = math.sqrt(1f - math.saturate(math.dot(pivotFwd1.xz, pivotFwd1.xz))) * ((packedData.y & 1) == 1 ? 1f : -1f);
    101.             pivotFwd1 = math.normalize(pivotFwd1);
    102.             pivotPos1.x = UnpackFixedToSFloat(packedData.z, 8f, 10, 22);
    103.             pivotPos1.y = UnpackFixedToUFloat(packedData.z, 32f, 12, 10);
    104.             pivotPos1.z = UnpackFixedToSFloat(packedData.z, 8f, 10, 0);
    105.             return true;
    106.         }
    107.         return false;
    108.     }
    109.     public static unsafe uint3 SingleToUInt32Bits(float3 value) {
    110.         return *(uint3*)(&value);
    111.     }
    112.    
    113. }
    114.  
     
    SteveKouts, Reanimate_L and eizenhorn like this.
  5. reflectingb0t

    reflectingb0t

    Joined:
    Apr 10, 2018
    Posts:
    18
    Wow thanks a lot!
    I tried it, but it gives me an error when baking the pivot. Do you have any idea why this might happen?
    I painted vertex colors like you said and rotated the trees pivot to have y up.

    upload_2018-7-10_12-42-50.png

    After that it gives me two empty assets:
    upload_2018-7-10_12-43-29.png

    Here is my tree setup in 3ds Max:
    upload_2018-7-10_12-44-39.png
    upload_2018-7-10_12-45-59.png
     
  6. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    "No pivot found for Vertex 1"
    Well, that means that vertex with the index 1 hasn't an assigned pivot yet.

    Maybe you have created a group without assigning at least one vertex with a value of 255 in red channel?

    A group is defined by the green channel.

    Groups:
    G=0: Trunc - no pivot painting required, uses the origin
    G=1-254: Branches - each vertex with the same green channel value will be at the same group
    G=255 : Leaves - will be determined by connected faces

    Be sure that each group (G > 0) has at least one vertex with a value of 255 in red channel!

    Hope this will help you
     
  7. reflectingb0t

    reflectingb0t

    Joined:
    Apr 10, 2018
    Posts:
    18
    I'm sorry to bother you again, but I still can't get it to work. I tried with a very simple test geometry and still have the same problem. The vertex id shown in the error definetly is in a group with a pivot and I don't understand what I'm doing wrong.

    I have a trunk with G=0 and R=0
    One branch with G=45 and and four pivot vertices R=255
    One leaf with G=255 and one pivot vertex R=255 (also tried without pivot vertex, but same error)

    Could this be an export issue / Unity settings issue or something like this? I also had to change my "Scripting Runtime Version" in player settings and some more stuff to get it to get it to work at all.

    Thanks

    upload_2018-7-10_16-10-49.png

    upload_2018-7-10_16-11-52.png

    upload_2018-7-10_16-12-46.png
     

    Attached Files:

  8. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    I rebuild your example. First I had some issues too! Because I used the brush tool. This isn't precise enought because it paints additive...

    Better is to assign the color directly (Blender = selecting the faces and press ctrl + k)

    I don't know how this works in 3ds

    I've added the fbx here for you!



    upload_2018-7-10_19-42-45.png

    upload_2018-7-10_19-43-40.png

    Mesh Baked Hierachy Debugger...



    It shouldn't be an issue on fbx import.
    Well yes you need to install the ECS package. I use the new math lib in my script. To use it you have to set the "Scripting Runtime Version" to .net4.x
    Thats correct!

    Hope this will help you!
     

    Attached Files:

    SteveKouts likes this.
  9. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    One more hint!
    The pivot forward direction is determined by the vertex with the longest distance to the pivot...
    Keep that in mind
     
  10. reflectingb0t

    reflectingb0t

    Joined:
    Apr 10, 2018
    Posts:
    18
    Great, thank you! I finally got it working now! :)
    The problem was indeed the vertex painting in 3ds Max. But I actually needed to really paint additive to get this working. I manually filled the vertices with the exact color before and noticed the leafs pivot was yellow instead of red in your screenshot so I switched to normal vertex painting in Max and it instantly worked. Guess it's the opposite from blender then.
    ...Meaning the pivots of my mesh had no green color information before, only red. My mistake.

    Anyway, thanks again for your kind support and the script!
    Cheers
     
    Spy-Shifty likes this.
  11. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    Wait, what unity version this is supported? i'm using 2018.2 b10. and .Net 4. but i cannot compile the scripts. Keep getting this error
     
  12. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    I use 2018.2 b9 but should work with 2018.2 b10 too

    Pls try to delete all the packages from
    %AppData%\Local\Unity\cache\packages\

    After that start Unity (without my scripts and any compiler errors) go to Window->Package Manager->All
    and install the latest Entities 0.0.12-preview.8

    wait for compile...
    After compiling with no errors add my script again

    Hope this will fix the error
     
  13. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    ah it need entities packages, that's what i missed
     
  14. reflectingb0t

    reflectingb0t

    Joined:
    Apr 10, 2018
    Posts:
    18
    I wondered, do you think it is possible to integrate this wind system into a new custom Shader based on "Lit" into a project without BOTD content? It seems to me there is a whole lot attached to this like the WindControl script e.g. and I can't just rename and export it.
    I would like to use this system for another project (also 2018.2.0b9), but I don't want it to be directly in the Lit Shader, because I want to get seamless updates from Unity without complications. And I also don't want all the other stuff from BOTD, because I want to use the new Unity Features.
    Btw I'm no programmer.
     
  15. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    i doubt that, it very project specific.
    Also i able to get the script from @Spy-Shifty to work just fine. But for some reason somehow i see that the current wind based on baked pivot setup used by BOTD are not very scalable, bit too complex. I prefer UE4 pivot painter workflow for this one. i guess that's the reason why they said the current pivot baker scripts are not ready for public consumption.
    or @torbjorn would you like to chime in or provide a guidance for us here ?
     
  16. reflectingb0t

    reflectingb0t

    Joined:
    Apr 10, 2018
    Posts:
    18
    Oh, too bad. Yeah, it really seems a bit complex, hope they add some kind of other wind support für HDRP then soon.
    I never used UE4 pivot painter workflow, but I'll have a look at this later, maybe can learn something from their shaders to create own in Unity.
     
  17. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    Hmm it might be possible to recreate that with shader graph
     
  18. torbjorn

    torbjorn

    Unity Technologies

    Joined:
    Jun 25, 2015
    Posts:
    211
    The intent was kinda the opposite. The pivot baking setup for this project was always intended to be 100% automatic, i.e. without having to do any kind of manual pivot painting, which is about as easy as it gets. In theory, this works fine and is easy to code up - and the internal tool is built around such a process.

    However, in practice, it can be a challenge convincing the existing tree-capable DCC authoring tools to actually output data with a structure suitable for this (they tend to be too helpful in baking everything down to already optimized data). As such, we did eventually rely on some content guides to avoid corner-case problems with some trees (like the uniquely color tagged branches, and the pivot attachment point textures). And yes, these 'workarounds' for the intended workflow, and the lack of solid authoring tool recommendations, are the primary reasons it's not super-easy to share a full workflow for authoring these assets.

    Very excited to see the community has been able to make working, alternative tools, though!
     
    Alverik, reflectingb0t and Spy-Shifty like this.
  19. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    Put it to github please :D
     
  20. norbertoravec

    norbertoravec

    Joined:
    Jun 12, 2018
    Posts:
    1
    Hi guys,

    the above mentioned approach and solution to recreate similar wind simulation like BOD is really great. I also would like to redo similar wind simulation in my own project. I can make the baked hierarchy with the aid of above posted baking pivot point script, but I have stuck with the wind. Can anyone help me how to approach the wind question to be able to give "engine" to the brunches and leaves. Right now I can see the baked pivot points on my prefab and thankfully to the HDRP/Lit material and the vertex animation ("enabled") my tree is moving in a weird way. Actually the baked pivot points don't stick to the hierarchy (I guess for this issue the wind control script should be responsible).
    (just to mention, I'm not a programmer :()

    I would be thankful for any help. :)

    Best to all,
    cheers
     
  21. reflectingb0t

    reflectingb0t

    Joined:
    Apr 10, 2018
    Posts:
    18
    @norbertoravec
    Regarding own wind shader creation, I tried to recreate a baked hierarchy wind shader within Unity Shadergraph, but atm I think Shadergraph is not ready for this (seems to have some blocking bugs). But I will upload a version here if I'll ever get it working :D I'm sure it could be done with altering the code of the Lit shader or creating a new code based shader, too, but I'm no programmer either and have no idea how to do this.

    But you could also start a project based on Unity's Book of the Dead demo. They implemented baked hierarchy wind into the HDRP Lit shader and it will work with the script provided here by @Spy-Shifty. To use this just follow the instructions on their Asset Store page (you will need to do this with a fresh project).
     
    Last edited: Aug 28, 2018
  22. GlitchInTheMatrix

    GlitchInTheMatrix

    Joined:
    Apr 12, 2010
    Posts:
    285
    Hi Spy-Shifty, thanks for share the scripts :)
    Im testing them today, wondering about what is the workflow for the grass?

    255 in the green channel for the moving parts of the pivot let it in 0 ? Without any need to use your script?

    btw, using Unity 2018.3.0f2 in the scene is necessary add a game object with the "Shader Wind Settings" Script?

    Thanks

    EDIT: Unity 2018.3.0f2 use the Mathematics Library?
     
    Last edited: Dec 27, 2018
  23. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    This technique seems awfully familiar - I'm sure I watched it on one of naughty dog's presentations - was it Uncharted 4? if so quite clever really, could use some tooling for it on PM.
     
    Alverik and AcidArrow like this.
  24. DenisGLabrecque

    DenisGLabrecque

    Joined:
    Jul 29, 2017
    Posts:
    7
    I want to make wind work on a project separate from Book of the Dead demo. I've been able to import the tree assets, plus the WindControl script and two helpers that make it work (HDRPCallbackAttribute and WindControlHelpers). The only problem is on a very simple line:

    Code (CSharp):
    1. HDRenderPipeline.OnPrepareCamera += SetupGPUData;
    SetupGPUData is a simple method which requires an HDCamera and a CommandBuffer as parameters.

    When the demo was created, the HDRP version presumably had an OnPrepareCamera event, but now that it doesn't, where has this event moved?
     
  25. rz_0lento

    rz_0lento

    Joined:
    Oct 8, 2013
    Posts:
    2,361
  26. torbjorn

    torbjorn

    Unity Technologies

    Joined:
    Jun 25, 2015
    Posts:
    211
    For all practical intents and purposes, this will effectively be singleton data. As such, you should be able to replace that callback with pretty much any callback, and execute the commands immediately as opposed to retained. Try hooking it to beginFrameRendering instead, create your own command buffer inside WindControl and just dispatch it explicitly at the end of the callback using Graphics.ExecuteCommandBuffer
     
    SteveKouts likes this.
  27. Win3xploder

    Win3xploder

    Joined:
    Dec 5, 2014
    Posts:
    159
    Does anyone know if the wind feature will be added to HDRP? I remember it being available in the HDRP preview for a bit last year. Also, does anyone know if this method is compatible with SpeedTree? What wind animation system does Speed Tree for games use?
     
  28. stamatian

    stamatian

    Joined:
    Jul 13, 2018
    Posts:
    36
    Hi guys,
    I'm making some grass, dose anyone of you know how to bake the pivot for grass?
    Thanks!
     
  29. ynj1988

    ynj1988

    Joined:
    Oct 20, 2016
    Posts:
    2
    How about the single pivot baking? how does it bake?
     
  30. ynj1988

    ynj1988

    Joined:
    Oct 20, 2016
    Posts:
    2
    where can I find the bake tools offer by unity ?
     
  31. torbjorn

    torbjorn

    Unity Technologies

    Joined:
    Jun 25, 2015
    Posts:
    211
  32. adamhill

    adamhill

    Joined:
    May 30, 2008
    Posts:
    26
    I wanted to put this here for others wanting to do vertex animations via this method.

    This a a lot like the Pivot Painter functionality in UE4. There are 3 tools out there to provide a workflow for this.

    1: Houdini - https://www.sidefx.com/tutorials/game-tools-pivot-painter/
    (current commits seem to have moved to https://github.com/sideeffects/SideFXLabs)
    2: Blender - https://github.com/Gvgeo/Pivot-Painter-for-Blender (Python)
    3: 3DS Max - Pivot Painter 2.0 - Part of the UE4 download (MaxScript)
     
    Last edited: Jun 9, 2020
    FloBeber likes this.
  33. FloBeber

    FloBeber

    Joined:
    Jun 9, 2015
    Posts:
    166
    Thank you for this.

    Do you have a compatible unity vertex shader (legacy) by any chance? It seems to be a default material in UE4 but I can't find anything for unity.


     
  34. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    Out of curiousity anyone manage to use the baked pivot with shadergraph?
     
  35. FloBeber

    FloBeber

    Joined:
    Jun 9, 2015
    Posts:
    166
    Wolfos likes this.
  36. SteveKouts

    SteveKouts

    Joined:
    Aug 23, 2012
    Posts:
    79
    Im getting this error in Unity 2021.3.21f:
    ArgumentException: Can't save persistent object as a Prefab asset
     
  37. SteveKouts

    SteveKouts

    Joined:
    Aug 23, 2012
    Posts:
    79
    Updated the code with this to get it working again.

    Code (CSharp):
    1.         var SceneObject = Instantiate(gameObject);
    2.         //GameObject hierarchyAssets = PrefabUtility.CreatePrefab(prefabAssetPath, gameObject, ReplacePrefabOptions.Default); //Old
    3.         bool prefabSuccess;
    4.         GameObject hierarchyAssets = PrefabUtility.SaveAsPrefabAsset(SceneObject, prefabAssetPath, out prefabSuccess);
    5.         if (prefabSuccess == true)
    6.             Debug.Log("Prefab was saved successfully");
    7.         else
    8.             Debug.Log("Prefab failed to save" + prefabSuccess);
    9.         DestroyImmediate(SceneObject);
     
  38. BOXOPHOBIC

    BOXOPHOBIC

    Joined:
    Jul 17, 2015
    Posts:
    506
    So glad I found this thread :) Since some of you have experience with the pivots baking maybe you can help.

    I was trying to decode the pivots based on the shader code, but in an editor script, and for some reason it is not working. Here is my code, I can't remember the details, I wrote it some time ago:

    Code (CSharp):
    1.         Vector3 UnpackPivot0(Vector3 packedData)
    2.         {
    3.             Vector3 pivotPos0;
    4.  
    5.             //if (packedData.y & 0xFFFF0000)
    6.             {
    7.                 pivotPos0.x = UnpackFixedToSFloat((uint)packedData.x, 8f, 10, 22);
    8.                 pivotPos0.y = UnpackFixedToUFloat((uint)packedData.x, 32f, 12, 10);
    9.                 pivotPos0.z = UnpackFixedToSFloat((uint)packedData.x, 8f, 10, 0);
    10.             }
    11.  
    12.             return pivotPos0;
    13.         }

    Code (CSharp):
    1.         float UnpackFixedToSFloat(uint val, float range, uint bits, uint shift)
    2.         {
    3.             uint BitMask = (1u << (int)bits) - 1u;
    4.             val = (val >> (int)shift) & BitMask;
    5.             float fval = val / (float)BitMask;
    6.             return (fval * 2f - 1f) * range;
    7.         }

    Code (CSharp):
    1.         float UnpackFixedToUFloat(uint val, float range, uint bits, uint shift)
    2.         {
    3.             uint BitMask = (1u << (int)bits) - 1u;
    4.             val = (val >> (int)shift) & BitMask;
    5.             float fval = val / (float)BitMask;
    6.             return fval * range;
    7.         }
    If anybody has any ideas, I would be happy to hear them. Thanks!

    Edit: I just noticed there is a ebugger script which might be exactly what I need
     
    Last edited: Sep 23, 2023