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

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/29/book-of-the-dead-quixel-wind-scene-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...

2 Likes

Yeah, hope they make that script available for download soon..

[quote=“reflectingb0t”, post:2, topic: 706798]
Yeah, hope they make that script available for download soon…
[/quote]
I’ve created my own
but it uses the red vertex color channel to mark pivot regions

Here is the bake script

HierarchieBakerEditor.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using Unity.Mathematics;
using System;

public class HierarchieBakerEditor : Editor {

    [MenuItem("Tools/VegetationTools/Bake Hierarchy")]
    public static void BakeHierarchy() {
        GameObject gameObject = Selection.activeGameObject;
        UnityEngine.Object assetObject = PrefabUtility.GetCorrespondingObjectFromSource(gameObject);
        bool t = AssetDatabase.IsMainAsset(gameObject);
        if (!AssetDatabase.IsMainAsset(gameObject) && assetObject != gameObject) {
            EditorUtility.DisplayDialog("BakeHierarchy", "Please select the source asset!", "ok");
            return;
        }


        Transform transform = gameObject.transform;

        string gameObjectAssetPath = AssetDatabase.GetAssetPath(gameObject);
        string[] splittedPath = gameObjectAssetPath.Split('/');
        gameObjectAssetPath = string.Join("/", splittedPath.Take(splittedPath.Length - 1));

        DummyScriptableObject dummyAsset = CreateInstance<DummyScriptableObject>();
        string dummyAssetPath = gameObjectAssetPath + "/" + gameObject.name + ".asset";
        AssetDatabase.CreateAsset(dummyAsset, dummyAssetPath);
        AssetDatabase.ImportAsset(dummyAssetPath);
        AssetDatabase.Refresh();

        string prefabAssetPath = gameObjectAssetPath + "/" + gameObject.name + "_BakedHierarchy.prefab";
        GameObject hierarchyAssets  = PrefabUtility.CreatePrefab(prefabAssetPath, gameObject, ReplacePrefabOptions.Default);

        MeshFilter[] sourceMeshFilters = gameObject.GetComponentsInChildren<MeshFilter>();
        MeshFilter[] hierarchyMeshFilters = hierarchyAssets.GetComponentsInChildren<MeshFilter>();

        bool success = true;
        for (int i = 0; i < sourceMeshFilters.Length; i++) {
            Mesh bakeMesh = Instantiate(sourceMeshFilters[i].sharedMesh);
            bakeMesh.name = sourceMeshFilters[i].sharedMesh.name + "_BakedHierarchy";
            AssetDatabase.AddObjectToAsset(bakeMesh, dummyAsset);
            try {
                BakeHierarchy(sourceMeshFilters[i].sharedMesh, bakeMesh);
                hierarchyMeshFilters[i].sharedMesh = bakeMesh;
                EditorUtility.SetDirty(bakeMesh);
            } catch(Exception ex) {
                EditorUtility.DisplayDialog("BakeHierarchy", ex.Message + " \n" + ex.StackTrace, "ok");
                success = false;
                break;
            }
        }

        if (!success) {
            DestroyImmediate(dummyAsset, true);
            DestroyImmediate(hierarchyAssets, true);
            AssetDatabase.Refresh();
        } else {

            EditorUtility.SetDirty(hierarchyAssets);
            EditorUtility.SetDirty(dummyAsset);
            AssetDatabase.SaveAssets();
            AssetDatabase.ImportAsset(prefabAssetPath);
            AssetDatabase.ImportAsset(dummyAssetPath);
            AssetDatabase.Refresh();

            EditorUtility.DisplayDialog("BakeHierarchy","Bake complete", "ok");
        }
    }

    private static void BakeHierarchy(Mesh mesh, Mesh bakeMesh) {
        Color32[] colors = mesh.colors32;
        Vector3[] vertices = mesh.vertices;
        int[] triangles = mesh.triangles;


        Dictionary<byte, Vector3> branchPivots = new Dictionary<byte, Vector3>();
        Dictionary<int, Vector3> pivots = new Dictionary<int, Vector3>();

        // build maps;
        IEnumerable<IGrouping<byte, int>> hierarchyMap = colors.Select((color, index) => new { color, index }).GroupBy(c => c.color.g, c => c.index);
        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);


        IGrouping<byte, int> trunc = hierarchyMap.Where(g => g.Key == 0).FirstOrDefault();
        foreach (var vertex in trunc) {
            pivots.Add(vertex, Vector3.zero);
        }

        IEnumerable<IGrouping<byte, int>> branches = hierarchyMap.Where(g => g.Key != 0 && g.Key != 255);
        IEnumerable<IGrouping<byte, int>> branchePivots = pivotMap.Where(g => g.Key != 0 && g.Key != 255);

        int numberOfBranchVerts = branches.Count();
        int numberOfbranchPivots = branchePivots.Count();
        if (numberOfBranchVerts != numberOfbranchPivots) {
            throw new Exception(string.Format("Branches has/have no pivot: ({0})", string.Join(", ",  branches.Select(x=>x.Key).Except(branchePivots.Select(x=>x.Key)))));
        }

        //build Branches
        var branchGroups = branches.Join(branchePivots,
            branchId => branchId.Key,
            branchePivot => branchePivot.Key,
            (verts, pivotsPoints) => new { verts.Key, Collections = new { Verts = verts.ToArray(), Pivots = pivotsPoints.ToArray() } });

        foreach (var branch in branchGroups) {
            Vector3 branchPivot = new Vector3(
                branch.Collections.Pivots.Average(pivotId => vertices[pivotId].x),
                branch.Collections.Pivots.Average(pivotId => vertices[pivotId].y),
                branch.Collections.Pivots.Average(pivotId => vertices[pivotId].z));

            int farthestAway = branch.Collections.Verts.OrderByDescending(vertId => Vector3.Distance(vertices[vertId], branchPivot)).First();

            Vector3 pivotDir = (vertices[farthestAway] - branchPivot).normalized;

            uint3 packedData = 0;
            PackPivot0(branchPivot, pivotDir, ref packedData);

            float3 pivotPoint = UIntToSingle32Bits(packedData);
            branchPivots.Add(branch.Key, pivotPoint);
         
            foreach (var branchVert in branch.Collections.Verts) {
                pivots.Add(branchVert, pivotPoint);
            }
        }

        //build Leaves
        //var t2 = faces.Count();

        //var brancheTuples = branches.SelectMany(pair =>
        //    Enumerable.Range(0, pair.Count())
        //        .Select(x => pair.Key)
        //        .Join(pair, key => key, value => value, (key, value) => new { key, value}));

        var brancheTuples = branches.SelectMany(pair => pair.Select(value => new { key = pair.Key, value }));

        //var t3 = branches.Count();
        //var t4 = brancheTuples.Count();

        IEnumerable<int> leafIndexes = hierarchyMap.Where(x => x.Key == 255).SelectMany(x=>x);
        IEnumerable<int> leafPivotIndexes = pivotMap.Where(x => x.Key == 255).SelectMany(x=>x);

        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));
        var t0 = faces.Count();
        if (leafIndexes.Any() && leafPivotIndexes.Any()) {
            var t1 = leafIndexes.Count();
            var t2 = leafPivotIndexes.Count();

            IEnumerable<IGrouping<int, int>> leaves = leafIndexes.AsParallel().GroupBy(g => {
                var leafGroup = new HashSet<int>(faces.Where(f => f.Value.Contains(g)).SelectMany(f => f.Value).Distinct());
                return leafPivotIndexes.FirstOrDefault(p => leafGroup.Contains(p));
            }, i => i);

            var t3 = leaves.Count();
            //throw new NotImplementedException();
            foreach (IGrouping<int, int> leaf in leaves) {
                Vector3 leafPivot = vertices[leaf.Key];

                int farthestAway = leaf.OrderByDescending(i => Vector3.Distance(vertices[i], leafPivot)).First();
                Vector3 leafPivotDir = (vertices[farthestAway] - leafPivot).normalized;

                var branch = brancheTuples.OrderBy(pair => Vector3.Distance(vertices[pair.value], leafPivot)).FirstOrDefault();
                uint3 packedData = 0;
                if (branch != null) {
                    byte branchKey = branch.key;
                    packedData = SingleToUInt32Bits(branchPivots[branchKey]);
                    PackPivot1(leafPivot, leafPivotDir, ref packedData);
                }

                float3 pivotPoint = UIntToSingle32Bits(packedData);
                foreach (var leafVert in leaf) {
                    pivots.Add(leafVert, pivotPoint);
                }
            }
        }

        List<Vector3> uvPivots = new List<Vector3>();
        for (int i = 0; i < vertices.Length; i++) {
            if (!pivots.TryGetValue(i, out Vector3 pivot)) {
                throw new Exception(string.Format("No pivot found for Vertex {0}", i));
            }

            uvPivots.Add(pivot);
        }

        bakeMesh.SetUVs(3, uvPivots);
    }

    static uint PackSFloatToFixed(float val, float range, int bits, int shift) {
        uint BitMask = (1u << bits) - 1;
        val = (val + range) / (2*range);
        uint uval = (uint)(val * BitMask);
        return (uval & BitMask) << shift;
    }

    static uint PackUFloatToFixed(float val, float range, int bits, int shift) {
        uint BitMask = (1u << bits) - 1;
        val /= range;
        uint uval = (uint)(val * BitMask);
        return (uval & BitMask) << shift;
    }

    // Needs to match shader packing in baking tool
    static void PackPivot0(float3 pivotPos0, float3 pivotFwd0, ref uint3 packedData) {
        float y = pivotFwd0.y;
        pivotFwd0.y = 0;
        pivotFwd0 = math.normalize(pivotFwd0);
        pivotFwd0 *= math.sqrt(1 - y*y);

        packedData.y |= (y < 0) ? 0u : 1u << 16;

        packedData.x |= PackSFloatToFixed(pivotPos0.x, 8f, 10, 22);
        packedData.x |= PackUFloatToFixed(pivotPos0.y, 32f, 12, 10);
        packedData.x |= PackSFloatToFixed(pivotPos0.z, 8f, 10, 0);

        packedData.y |= PackSFloatToFixed(pivotFwd0.x, 1, 8, 24);
        packedData.y |= PackSFloatToFixed(pivotFwd0.z, 1, 7, 17);
    }

    // Needs to match shader packing in baking tool
    static void PackPivot1(float3 pivotPos1, float3 pivotFwd1, ref uint3 packedData) {
        float y = pivotFwd1.y;
        pivotFwd1.y = 0;
        pivotFwd1 = math.normalize(pivotFwd1);
        pivotFwd1 *= math.sqrt(1 - y * y);

        packedData.y |= (y < 0) ? 0u : 1u;
        packedData.y |= PackSFloatToFixed(pivotFwd1.x, 1f, 8, 8);
        packedData.y |= PackSFloatToFixed(pivotFwd1.z , 1f, 7, 1);

        packedData.z |= PackSFloatToFixed(pivotPos1.x, 8f, 10, 22);
        packedData.z |= PackUFloatToFixed(pivotPos1.y, 32f, 12, 10);
        packedData.z |= PackSFloatToFixed(pivotPos1.z, 8f, 10, 0);
    }

    public static unsafe float3 UIntToSingle32Bits(uint3 value) {
        return *(float3*)(&value);
    }

    //------------------------------


    static float UnpackFixedToSFloat(uint val, float range, int bits, int shift) {
        uint BitMask = (1u << bits) - 1;
        val = (val >> shift) & BitMask;
        float fval = val / (float)BitMask;
        return (fval * 2f - 1f) * range;
    }

    static float UnpackFixedToUFloat(uint val, float range, int bits, int shift) {
        uint BitMask = (1u << bits) - 1;
        val = (val >> shift) & BitMask;
        float fval = val / (float)BitMask;
        return fval * range;
    }

    // Needs to match shader packing in baking tool
    static bool UnpackPivot0(uint3 packedData, ref float3 pivotPos0, ref float3 pivotFwd0) {
        if ((packedData.y & 0xFFFF0000) != 0) {
            pivotPos0.x = UnpackFixedToSFloat(packedData.x, 8f, 10, 22);
            pivotPos0.y = UnpackFixedToUFloat(packedData.x, 32f, 12, 10);
            pivotPos0.z = UnpackFixedToSFloat(packedData.x, 8f, 10, 0);
            pivotFwd0.x = UnpackFixedToSFloat(packedData.y, 1f, 8, 24);
            pivotFwd0.z = UnpackFixedToSFloat(packedData.y, 1f, 7, 17);
            pivotFwd0.y = math.sqrt(1f - math.saturate(math.dot(pivotFwd0.xz, pivotFwd0.xz))) * (((packedData.y >> 16) & 1) == 1 ? 1f : -1f);
            pivotFwd0 = math.normalize(pivotFwd0);
            return true;
        }
        return false;
    }

    // Needs to match shader packing in baking tool
    static bool UnpackPivot1(uint3 packedData, ref float3 pivotPos1, ref float3 pivotFwd1) {
        if ((packedData.y & 0x0000FFFF) != 0) {
            pivotFwd1.x = UnpackFixedToSFloat(packedData.y, 1f, 8, 8);
            pivotFwd1.z = UnpackFixedToSFloat(packedData.y, 1f, 7, 1);
            pivotFwd1.y = math.sqrt(1f - math.saturate(math.dot(pivotFwd1.xz, pivotFwd1.xz))) * ((packedData.y & 1) == 1 ? 1f : -1f);
            pivotFwd1 = math.normalize(pivotFwd1);
            pivotPos1.x = UnpackFixedToSFloat(packedData.z, 8f, 10, 22);
            pivotPos1.y = UnpackFixedToUFloat(packedData.z, 32f, 12, 10);
            pivotPos1.z = UnpackFixedToSFloat(packedData.z, 8f, 10, 0);
            return true;
        }
        return false;
    }
    public static unsafe uint3 SingleToUInt32Bits(float3 value) {
        return *(uint3*)(&value);
    }
}

DummyScriptableObject.cs

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

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

3557860--286477--upload_2018-7-9_20-15-33.png

4 Likes

This is a debugger tool to display the baked pivots

MeshBakedHierarchieDebugger.cs

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;

public class MeshBakedHierarchieDebugger : MonoBehaviour {
    public enum Level {
        all,
        one,
        two,
    }

    [Range(0,3)]
    public int uvLevel = 3;

    public Level hierarchieLevel;
    private void OnDrawGizmosSelected() {
        Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
        List<Vector3> uvs = new List<Vector3>();
        mesh.GetUVs(uvLevel, uvs);

        float3 root = transform.position;
        foreach (Vector3 uv in uvs) {
            uint3 packedData = SingleToUInt32Bits(new float3(uv.x, uv.y, uv.z));
            float3 pivotPos0 = new float3();
            float3 pivotPos1 = new float3();
            float3 pivotFwd0 = new float3();
            float3 pivotFwd1 = new float3();
            bool pivotEnabled0 = UnpackPivot0(packedData, ref pivotPos0, ref pivotFwd0);
            bool pivotEnabled1 = UnpackPivot1(packedData, ref pivotPos1, ref pivotFwd1);

            switch (hierarchieLevel) {
                case Level.one:
                    if (pivotEnabled1 || (!pivotEnabled0)) {
                        continue;
                    }
                    break;
                case Level.two:
                    if (!(pivotEnabled0 && pivotEnabled1)) {
                        continue;
                    }
                    break;
            }

            pivotPos0 = transform.TransformPoint(pivotPos0);
            pivotPos1 = transform.TransformPoint(pivotPos1);
            pivotFwd0 = transform.TransformDirection(pivotFwd0);
            pivotFwd1 = transform.TransformDirection(pivotFwd1);

            if (pivotEnabled0 && hierarchieLevel != Level.two) {
                Gizmos.color = Color.blue;
                Gizmos.DrawSphere(pivotPos0, 0.1f);
                Gizmos.color = Color.green;
                Gizmos.DrawLine(pivotPos0, pivotPos0 + pivotFwd0 * 10);
            }

            if (pivotEnabled1 && hierarchieLevel != Level.one) {
                Gizmos.color = Color.red;
                Gizmos.DrawSphere( pivotPos1, 0.05f);
                Gizmos.color = Color.green;
                Gizmos.DrawLine(pivotPos1, pivotPos1 + pivotFwd1);
            }
        }
    }

    float UnpackFixedToSFloat(uint val, float range, int bits, int shift) {
        uint BitMask = (1u << bits) - 1;
        val = (val >> shift) & BitMask;
        float fval = val / (float)BitMask;
        return (fval * 2f - 1f) * range;
    }

    float UnpackFixedToUFloat(uint val, float range, int bits, int shift) {
        uint BitMask = (1u << bits) - 1;
        val = (val >> shift) & BitMask;
        float fval = val / (float)BitMask;
        return fval * range;
    }

    // Needs to match shader packing in baking tool
    bool UnpackPivot0(uint3 packedData, ref float3 pivotPos0, ref float3 pivotFwd0) {
        if ((packedData.y & 0xFFFF0000) != 0) {
            pivotPos0.x = UnpackFixedToSFloat(packedData.x, 8f, 10, 22);
            pivotPos0.y = UnpackFixedToUFloat(packedData.x, 32f, 12, 10);
            pivotPos0.z = UnpackFixedToSFloat(packedData.x, 8f, 10, 0);
            pivotFwd0.x = UnpackFixedToSFloat(packedData.y, 1f, 8, 24);
            pivotFwd0.z = UnpackFixedToSFloat(packedData.y, 1f, 7, 17);
            pivotFwd0.y = math.sqrt(1f - math.saturate(math.dot(pivotFwd0.xz, pivotFwd0.xz))) * (((packedData.y >> 16) & 1) == 1 ? 1f : -1f);
            pivotFwd0 = math.normalize(pivotFwd0);
            return true;
        }
        return false;
    }

    // Needs to match shader packing in baking tool
    bool UnpackPivot1(uint3 packedData, ref float3 pivotPos1, ref float3 pivotFwd1) {
        if ((packedData.y & 0x0000FFFF) != 0) {
            pivotFwd1.x = UnpackFixedToSFloat(packedData.y, 1f, 8, 8);
            pivotFwd1.z = UnpackFixedToSFloat(packedData.y, 1f, 7, 1);
            pivotFwd1.y = math.sqrt(1f - math.saturate(math.dot(pivotFwd1.xz, pivotFwd1.xz))) * ((packedData.y & 1) == 1 ? 1f : -1f);
            pivotFwd1 = math.normalize(pivotFwd1);
            pivotPos1.x = UnpackFixedToSFloat(packedData.z, 8f, 10, 22);
            pivotPos1.y = UnpackFixedToUFloat(packedData.z, 32f, 12, 10);
            pivotPos1.z = UnpackFixedToSFloat(packedData.z, 8f, 10, 0);
            return true;
        }
        return false;
    }
    public static unsafe uint3 SingleToUInt32Bits(float3 value) {
        return *(uint3*)(&value);
    }

}
3 Likes

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.

3558598--286564--upload_2018-7-10_12-42-50.jpg

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

Here is my tree setup in 3ds Max:
3558598--286567--upload_2018-7-10_12-44-39.jpg
3558598--286568--upload_2018-7-10_12-45-59.png

[quote=“reflectingb0t”, post:5, topic: 706798]
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.

After that it gives me two empty assets:

Here is my tree setup in 3ds Max:

[/quote]

“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

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

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

3558867--286618--upload_2018-7-10_16-11-21.png

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

3558867--286618--upload_2018-7-10_16-11-21.png
3558867--286618--upload_2018-7-10_16-11-21.png

[quote=“reflectingb0t”, post:7, topic: 706798]
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

[/quote]

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!

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

3559131--286651--upload_2018-7-10_19-43-40.jpg

Mesh Baked Hierachy Debugger…

[quote=“reflectingb0t”, post:7, topic: 706798]
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.
[/quote]

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!

3559131–286649–Tree_2.zip (8.59 KB)

1 Like

One more hint!
The pivot forward direction is determined by the vertex with the longest distance to the pivot...
Keep that in mind

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

1 Like

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
[quote]
Assets/HierarchyPivotBaker/Editor/HierarchyBakerEditor.cs(180,48): error CS1644: Feature `out variable declaration' cannot be used because it is not part of the C# 6.0 language specification
[/quote]

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

ah it need entities packages, that's what i missed

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.

i doubt that, it very project specific.
Also i able to get the script from @Spy-Shifty_1 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 ?

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.

[quote=“reflectingb0t”, post:16, topic: 706798]
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.
[/quote]
Hmm it might be possible to recreate that with shader graph

[quote=“Reanimate_L”, post:15, topic: 706798]
i doubt that, it very project specific.
Also i able to get the script from @Spy-Shifty_1 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 ?
[/quote]

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!

3 Likes

[quote=“torbjorn”, post:18, topic: 706798]
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!
[/quote]
Put it to github please :smile:

5 Likes

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