Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Mesh Baker LOD [RELEASED]

Discussion in 'Assets and Asset Store' started by Phong, Nov 1, 2013.

  1. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083


    THIS ASSET REQUIRES MESH BAKER

    Make your open world fly! It's like static batching and LOD combined!

    In test scenes drawcalls are reduced by 95%. rendered vertices reduced by 60%.

    A replacement for Unity's LOD. Mesh Baker LODs are baked into combined meshes at runtime, dramatically reducing drawcalls. Provides an easy, flexible way to optimize huge scenes or scenes with procedural content at runtime.
    • Works with Unity free
    • No scripting required
    • All baking happens at runtime
    • Easily bake many skinned meshes into mobs that take one drawcall to render
    • Fine grained control over how many meshes are allowed in each level of detail.
    There are some major shortcomings to the Static Batching and LOD included with Unity Pro:
    • Static Batching bakes combined meshes at build time
      • The memory footprint for the combined meshes can be prohibitively large
      • If static content is created at runtime it can't be included in the combined mesh.
      • Builds can become too large for mobile platforms
    • Dynamic Batching bakes every frame
    Mesh Baker LOD overcomes these problems by only baking what is necessary at the moment.

    Mesh Baker LOD Videos
    Demo



    Tutorial Part 1


    Tutorial Part 2
    [video=youtube:I_ylX5IA1Vo]


    Tutorial Part 3


    Tutorial Part 4


    Mesh Baker LOD is now available in the asset store
     
    Last edited: Jul 8, 2017
  2. Indigo13

    Indigo13

    Joined:
    Nov 6, 2013
    Posts:
    1
    Looks promising.

    I am designing a sandbox game with procedurally generated terrain. We are creating an infinite landscape by creating terrain chunks in front of the player and deleting them them behind the player. Can these LODs be prefabed then Instantiated and Destroyed at runtime?
     
  3. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Yes. The LODs and manager setup can be prefabed and re-used in other scenes.

    The recommended workflow is to build and prefab the LODs in a simple scene then just drag them into other scenes to use them.
     
  4. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Version 1.1 Released

    • Fixed bug which would cause SkinnedMeshLODs with max per level set to bake every frame
    • Can add and remove bakers at runtime
    • Can use orthographic cameras
     
  5. mstath1s

    mstath1s

    Joined:
    Dec 31, 2012
    Posts:
    5
    Do you plan to release an evaluation version of Mesh Baker LOD as you have done for the Mesh baker?
     
  6. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    I don't plan to. My experience with the Mesh Baker evaluation version has not been great. It is very time consuming to manage and I hate the limitations of DLLs. If you contact me personally and can convince me that you are reasonably honest I would consider sending you a version for evaluation purposes.
     
  7. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    Is there a way to get rid of the popping between LOD levels? I've tried lots of different settings, but it always happens. :neutral:

    Here's a quick tool that I threw together to convert LODGroups to MB2_LOD. It doesn't sort by material, so some cleanup is required, but it's nice to use Unity's built-in LODGroup inspector tools for visually determining the Screen Percentages. The code is sloppy, but it worked for converting more than 2k LODGroups.
    Code (csharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections;
    4. using DigitalOpus.MB.Core;
    5.  
    6. public class LODGroupToMeshBakerLOD : ScriptableObject
    7. {
    8.     // TODO: sort by materials
    9.     [MenuItem ("Tools/Mesh Baker/Convert LODGroup to MeshBaker LOD")]
    10.     static void MenuConvLODGroupToMBLOD()
    11.     {
    12.         Transform[] transforms = Selection.GetTransforms(SelectionMode.TopLevel | SelectionMode.OnlyUserModifiable);
    13.  
    14.         foreach(Transform curTransform in transforms) {
    15.             // TODO: the undo doesn't always work as expected
    16.             Undo.RegisterCompleteObjectUndo(curTransform.gameObject, "Convert LODGroup To MeshBakerLOD");
    17.             LODGroup curLODGroup = curTransform.GetComponent<LODGroup>();
    18.             if(curLODGroup != null) {
    19.  
    20. //              float LODGroupBound = getBoundsOfLODGroup(curTransform.gameObject);
    21.                 MB2_LOD meshBakerLOD = curTransform.gameObject.AddComponent(typeof(MB2_LOD)) as MB2_LOD;
    22.                 meshBakerLOD.LOG_LEVEL = MB2_LogLevel.error;
    23.                 meshBakerLOD.levels = new MB2_LOD.LOD[curLODGroup.lodCount];
    24.                 for(int i = 0; i < curLODGroup.lodCount; i++)
    25.                     meshBakerLOD.levels[i] = new MB2_LOD.LOD();
    26.                 if(curTransform.gameObject.isStatic) {
    27.                     meshBakerLOD.renderType = MB_RenderType.meshRenderer;
    28.                     curTransform.gameObject.isStatic = false;
    29.                 } else
    30.                     meshBakerLOD.renderType = MB_RenderType.skinnedMeshRenderer;
    31.  
    32.  
    33.                 SerializedObject obj = new SerializedObject(curLODGroup);
    34.  
    35.                 for(int curLOD = 0; curLOD < curLODGroup.lodCount; curLOD++) {
    36.                     float screenRelativeHeight = obj.FindProperty("m_LODs.Array.data[" + curLOD.ToString() + "].screenRelativeHeight").floatValue;
    37.                     SerializedProperty prop = obj.FindProperty("m_LODs.Array.data[" + curLOD.ToString() + "].renderers");
    38.                     ArrayList gameObjectsArrayList = new ArrayList();
    39.                     for(int curGO = 0; curGO < prop.arraySize; curGO++) {
    40.                         // TODO: catch NPE if LODGroup LOD has no meshes
    41.                         gameObjectsArrayList.Add((prop.GetArrayElementAtIndex(curGO).FindPropertyRelative("renderer").objectReferenceValue as MeshRenderer).gameObject);
    42.                     }
    43.                     GameObject[] gameObjectsArray = gameObjectsArrayList.ToArray(typeof(GameObject)) as GameObject[];
    44.  
    45.                     for(int curRenderer = 0; curRenderer < gameObjectsArray.Length; curRenderer++) {
    46.                         GameObject curGO = gameObjectsArray[curRenderer];
    47.                         curGO.isStatic = false;
    48.  
    49.                         if(curRenderer==0) {
    50.                             meshBakerLOD.levels[curLOD].lodObject = curGO.GetComponent< Renderer >();
    51.                             meshBakerLOD.levels[curLOD].screenPercentage = Mathf.Max(screenRelativeHeight,0.0001f);
    52.                             curGO.transform.parent = curTransform;
    53.                             curGO.name = "LOD" + curLOD + "_" + curGO.name;
    54.                         } else {
    55.                             GameObject tempParent = new GameObject();
    56.                             tempParent.transform.parent = curGO.transform;
    57.                             tempParent.transform.localRotation = Quaternion.identity;
    58.                             tempParent.transform.localPosition = Vector3.zero;
    59.                             tempParent.transform.localScale = Vector3.one;
    60.                             tempParent.transform.parent = gameObjectsArray[0].transform;
    61.                             curGO.transform.parent = tempParent.transform;
    62.                             tempParent.name = "LOD" + curLOD + "_" + curGO.name;
    63.  
    64.                             MB2_LOD innerMeshBakerLOD = tempParent.AddComponent(typeof(MB2_LOD)) as MB2_LOD;
    65.                             innerMeshBakerLOD.LOG_LEVEL = MB2_LogLevel.error;
    66.                             innerMeshBakerLOD.levels = new MB2_LOD.LOD[1];
    67.                             innerMeshBakerLOD.levels[0] = new MB2_LOD.LOD();
    68.                             innerMeshBakerLOD.renderType = meshBakerLOD.renderType;
    69.                             innerMeshBakerLOD.levels[0].lodObject = curGO.GetComponent< Renderer >();
    70.                             // if it's a child of an LOD, it's going to be hidden anyway.
    71.                             innerMeshBakerLOD.levels[0].screenPercentage = .001f;//meshBakerLOD.levels[curLOD].screenPercentage;
    72.                         }
    73.                     }
    74.                 }
    75.                 DestroyImmediate(curLODGroup,false);
    76.             } else {
    77.                 Debug.Log(curTransform.name + " has no LODGroup.");
    78.             }
    79.         }
    80.     }
    81.  
    82.     static float getBoundsOfLODGroup(GameObject GO) {
    83.         float toReturn = 0f;
    84.         GameObject dummy = (GameObject)Instantiate( GO, Vector3.zero, Quaternion.identity );
    85.         Renderer[] renderers = dummy.GetComponentsInChildren< Renderer >();
    86.         if( renderers.Length >= 1 )
    87.         {
    88.             Bounds bounds = new Bounds( Vector3.zero, Vector3.zero );
    89.             foreach( Renderer r in renderers )
    90.             {
    91.                 bounds.Encapsulate( r.bounds );
    92.             }
    93.            
    94.             Vector3 size = bounds.size;
    95.             toReturn = (size.x + size.y + size.z) / 3f;
    96.         }
    97.         DestroyImmediate( dummy ,false);
    98.         return toReturn;
    99.     }
    100.  
    101.     static float getBoundsOfSingleObject(GameObject GO) {
    102.         float toReturn = 0f;
    103.         GameObject dummy = (GameObject)Instantiate( GO, Vector3.zero, Quaternion.identity );
    104.         Renderer renderer = dummy.GetComponent< Renderer >();
    105.         if( renderer != null )
    106.         {
    107.             Vector3 size = renderer.bounds.size;
    108.             toReturn = (size.x + size.y + size.z) / 3f;
    109.         }
    110.         DestroyImmediate( dummy ,false);
    111.         return toReturn;
    112.     }
    113. }
    Cheers,
    IFL
     
  8. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Hi IFL,

    Thanks for posting the LOD code! This is a great idea, will try to include something like this in the next version.

    By poping I presume that you mean the noticeable transition when a model switches to another level of detail. About the only thing you can do is have lots of levels of detail. Also try to make sure that the silhouette is preserved when the switch happens.

    There is something called a progressive mesh. Mesh Baker LOD does NOT implement this currently but I would like to implement it at some point. The idea is that the edges in a mesh are sorted by importance in a pre-processing step. Then at runtime the mesh can be put into any level of detail! Not easy to implement though. Sorting the edges is very tricky. Especially when factoring UVs and Submeshes on top of considering the geometry.
     
  9. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    Thanks for the fast reply!
    I figured that out just a while ago. I'm rewriting the script above to account for it.

    Looked up that technique - it's very intriguing. It'd do away with a lot of the work that artists would normally have to do. I might be missing something, but having multiple viewpoints seems to be the only thing that would make it an ordeal to implement. Collapsing edges is pretty straightforward, and the sorting could be as simple as going from smallest to largest. Thanks for sharing that - it gives me something to play around with in my downtime.
     
  10. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    It is fun to play around with. The ordeal comes when trying to handle the UVs and normals. The UVs are by far the hardest to deal with. You need to find all the UV islands and attempt to measure how much of a distortion will be introduced by eliminating an edge in UV space taking into account concavity in the UV island boundaries. To be robust you even need to look at the pixel values in the the UV texture to try to ensure sharp contrasts are preserved as much as possible. Therein lies the path to madness.
     
  11. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    There is a new version in the asset store with two bug fixes:

    1) Fixes errors when switching scenes with the LOD Manager singleton.

    2) Level of detail hierarchies are now deactivated from the child parented to the LOD object instead of the game object with the renderer attached.
     
  12. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    Thanks for the updates!

    Is there an easy way to set the combined-mesh(es) of specific MeshBakers to specific layers? Or maybe just retrieve the linked CombinedMesh (parent) GO tied to each baker? I know how to do it in a complicated way, but I haven't found an easy route that's also efficient.
     
  13. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    I will add this to the baker class and submit an update. In the meantime here is how to access the combined mesh objects, it is a bit of a hack:

    In file MB2_LODClusterBase you will need to make line 70 public instead of protected:

    Code (csharp):
    1.         public MB2_MultiMeshCombiner combinedMesh;
    In the script where you want to access the result scene object you will need to add:

    Code (csharp):
    1.                 using DigitalOpus.MB.Core;
    2.                 using DigitalOpus.MB.Lod;
    If the baker type is grid use this:

    Code (csharp):
    1.            
    2. LODClusterManagerGrid gridClusterManager = (LODClusterManagerGrid) MB2_LODManager.Manager().bakers[0].baker;
    3. for (int i = 0; i < gridClusterManager.clusters.Count; i++){
    4.         //this is actually the parent. you will need to set the layer on its children
    5.     gridClusterManager.clusters[i].combinedMesh.resultSceneObject.layer = LayerMask.NameToLayer("testLayer");;
    6. }
    7.  
    If the baker type is simple use this (you will need to make cluster public in file MB2_ClusterManagerSimple (line 33):

    Code (csharp):
    1.            
    2. LODClusterManagerSimple simpleClusterManager = (LODClusterManagerSimple) MB2_LODManager.Manager().bakers[0].baker;
    3. //this is actually the parent. you will need to set the layer on its children
    4. simpleClusterManager.cluster.combinedMesh.resultSceneObject.layer = LayerMask.NameToLayer("testLayer");
    5.  
    Note that "resultSceneObject" is the parent to the mesh filter game objects so you will need to set the layer on the children.
     
    Last edited: Dec 27, 2013
  14. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    Hey, I didn't see your edited reply until just now.

    Your layer solution works perfectly, and the update is great.

    If you don't mind sharing, has this asset done well on the Asset Store? There aren't many ratings, but I can't imagine publishing a project on mobile without it - it's one of the best assets I've purchased. Regardless, I appreciate the hard work you've put into it.

    [Edit]
    A nice addition would be the ability to enable/disable the casting and receiving of shadows for each group. I've done it through code, but others might like it in LOD Manager's inspector. I'd say make it possible to enable light probes as well, but those work off of one point in space, so it wouldn't make much sense for a group of baked meshes.
     
    Last edited: Jan 6, 2014
  15. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Thanks for the feedback. It hasn't done great in the Asset Store so far but these things can take a while to catch on. So far January looks like an improvement. I am trying to get it in a sale.

    The shadow casting options and light probes are a good idea. I will try to get these in the next version.
     
  16. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    A new update for Mesh Baker LOD is now available. New features include:

    • Can do LOD without baking meshes
    • Can set the layer for the combined meshes
     
  17. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    Before I try to implement it, is there anything that would make having multiple meshes per LOD level difficult? It'd save a lot in terms of run-time performance, but not be worth it if it would take too much time to implement. It seems like the bounds checks and editor code would be the most difficult part, unless I'm missing something. This isn't a feature request, just asking your opinion.
     
  18. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Off the top of my head I can't think of any serious reason why this would be particularly difficult. I think your effort assessment is fairly good. If I think of anything in the next little while I will modify this post.
     
  19. gecko

    gecko

    Joined:
    Aug 10, 2006
    Posts:
    2,240
    I'm looking at Mesh Baker LOD as a possible way to handle a large forest. I'm thinking of a three-level LOD system, with 3D mesh trees closest to the camera (LOD1), a tree-shaped mesh billboard for LOD2, and a single mesh with 20-30 trees for LOD 3 (seen only at a great distance). Basically, it shifts LOD detection from individual trees to a tree cluster for LOD3. This is sorta the opposite of nesting as described in your manual. Is it possible with your tool?
     
  20. gecko

    gecko

    Joined:
    Aug 10, 2006
    Posts:
    2,240
    I went ahead and purchased this. Testing the "CityScene" in the editor and on an ipad 2, the MB2_LODManager.LateUpdate has a pretty major performance hit every second. Even in the Editor, on my 2013 MBP, the frame rate drops to 30 each time, and on iPad, it drops to 15fps every second. See the profiler screenshot for latter. What can I do to improve that?

    EDIT: Image doesn't seem to be showing up. On iPad2, MB2_LODManager.LateUpdate spikes to 86ms every second, which pushes fps down to 15. During that spike, GC is 8ms, GameObject.Activate/Deactivate are .09 and .04.
     

    Attached Files:

  21. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    This would be tricky since each LOD3 mesh is associated with many LOD2 meshes. The tool would not support this directly.
     
  22. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Hi Dave,

    I had some time this evening and was able to take a look at this. There is a chapter in the manual on performance tuning. I would recommend taking a look at that. Here is what I did:

    1) Turned the "Combine Time Per frame" down to .005 sec. This will spread long bakes across several frames.

    2) Modified the Screen Percentage on the tree LODs to: .8, .2, .05. This reduces the rendered vertices by about 50% lightening the baking load.

    3) Reduced the maximum number of vertices per combined mesh. This will mean more meshes (drawcalls) but the time to modify each combined mesh is reduced. This is only effective if the a single bake is taking longer than the "Combine Time per Frame".

    4) Increase the "time between LOD checks" to 20. This means bakes will happen less often and more meshes will be changed with each bake.

    With these changed I managed to get the scene up to 30 fps on a very, very wimpy android phone (ARMv6 with Virtual Floating Point).
     
    Last edited: Jan 16, 2014
  23. gecko

    gecko

    Joined:
    Aug 10, 2006
    Posts:
    2,240
    Thanks, I'm trying that. But it won't let me select-all and edit all the "TreePrefab(Clone)" objects in the CityScene at once -- do I really have to change those values one by one? Is there any way to globally change them?
     
    Last edited: Jan 16, 2014
  24. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    That is a pain. I did it by writing a short editor script.

    Inserted this temporarily at line 96 of MB2_LODManagerEditor:

    Code (csharp):
    1.  
    2.         EditorGUILayout.HelpBox(m.GetStats(),MessageType.Info);
    3.         if (GUILayout.Button(" set LOD values ")){
    4.             MB2_LOD[] lods = (MB2_LOD[]) FindObjectsOfType(typeof(MB2_LOD));
    5.             for (int i = 0; i < lods.Length; i++){
    6.                 if (lods[i].name.Equals("TreePrefab(Clone)")){
    7.                     lods[i].framesBetweenLODChangedChecks = 20;
    8.                     MB2_LOD.LOD[] levels = lods[i].levels;
    9.                     //original values .2,.05,.01
    10.                     levels[0].screenPercentage = .8f;
    11.                     levels[1].screenPercentage = .2f;
    12.                     levels[2].screenPercentage = .05f;
    13.                 }
    14.             }
    15.         }
    16.  
     
  25. gecko

    gecko

    Joined:
    Aug 10, 2006
    Posts:
    2,240
    Thanks! So this changes those settings invisibly? (They remain unchanged on the tree prefabs.) There's no improvement on iPad 3 -- still 15-30fps.

    Also, I am getting this warning repeatedly in the CityScene. I assumed I didn't have to bake anything in your sample scene, but do I?

    LOD [MB2_LOD Window id=162888: inComb=False inQ=False act=toAdd nxt=0 curr=1] has out of bounds UVs. It probably requires a baker that exactly matches the materials (no extra). Such a baker could not be found.
     
    Last edited: Jan 17, 2014
  26. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    That warning can be ignored. The out of bounds UVs in this case is more of a rounding error.

    The trees in that scene arn't connected to the prefab. I placed them using a script and it didn't maintain the prefab connection. You should be able to check that the changes have taken effect by looking at the trees in the inspector.

    When I get a chance I will send you the modified scene.
     
  27. gecko

    gecko

    Joined:
    Aug 10, 2006
    Posts:
    2,240
    In the Inspector, the trees still have the original Screen Percentage settings (.2, .05, .01). Does that mean the script change didn't affect them?
     
  28. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Hi Dave,

    The code adds a button to the inspector that you can click and it will update all the trees at once. You will want to remove this code once you are done fiddling.

    That being said, I finally got some time this weekend and tried the scene on an iPod. There is a noticeable hickup every second when the baking happens. I will spend some time week to see if the load can be spread over more frames.

    UPDATE:

    I was able to improve the frame-rate considerably by reducing the grid size. Unfortunately there was a bug that caused the gridSize to not take effect. I had to insert a line in MB2_LODManager.cs line 127 I changed:
    Code (csharp):
    1.  
    2.             public void CreateClusterManager(){
    3.                 LODClusterManager b = null;
    4.                 if (clusterType == BakerPrototype.CombinerType.grid){
    5.                     b = new LODClusterManagerGrid(this);
    6.                 } else if (clusterType == BakerPrototype.CombinerType.simple){
    7.                     b = new LODClusterManagerSimple(this);
    8.                 }
    9.                 baker = b;
    10.             }
    11.  
    to
    Code (csharp):
    1.  
    2.             public void CreateClusterManager(){
    3.                 LODClusterManager b = null;
    4.                 if (clusterType == BakerPrototype.CombinerType.grid){
    5.                     b = new LODClusterManagerGrid(this);
    6.                     ((LODClusterManagerGrid)b).gridSize = (int) gridSize;
    7.                 } else if (clusterType == BakerPrototype.CombinerType.simple){
    8.                     b = new LODClusterManagerSimple(this);
    9.                 }
    10.                 baker = b;
    11.             }
    12.  
    In the process of investigating this I came up with a few more ideas that should smooth things out. I should have an update ready in a couple of days that should make things much smoother.
     
    Last edited: Jan 20, 2014
  29. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    In MB2_LOD's update method, do you know of any reason that I shouldn't change the frame check "Time.frameCount" to "Time.frameCount - 1" to force the bake to happen immediately at the start of the level?
     
  30. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    This would not cause anything bad.
     
  31. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    After a week of profiling I have released a new update to the asset store (should be live in 2-3 days):

    There are three main improvements:

    • I added a frame check offset so that the LODs in each cluster check whether the LOD needs to change in a different frame from the LODs in other clusters. This helps distribute the load more evenly.
    • Time taken by the garbage collector is included in the scheduling so that long frames are less likely.
    • There is a new diagnostic printout when MB2_LODManager is set to LOG LEVEL debug which prints bake times per frame. This makes it easier to see if long bakes are occuring

    I also have a few ideas how to improve things further so expect another update soon.
     
  32. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    That's great to hear!

    Do you know a good way to bake lightmaps for objects using MBLOD? I have a script that activates a specific LOD level across the entire scene, but is there a better way?

    My apologies for asking so many questions, and thanks for being so helpful!
     
  33. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Hi IFL,

    Unfortunately I don't know of a good way to do this. I have wondered this myself. Unfortunately I have not done a lot of light-mapping.
     
  34. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    Thanks. I came up with another, much easier, way to do lightmapping, but it requires LODGroup so it's Pro only. I basically just have an editor script that finds all active MB2_LOD objects, gives them an LODGroup set up to match the MB2_LOD, bakes the scene, and then deletes the LODGroups. I have another script that sets each renderer's lightmap scale, but it could be easily included in that process (if it's even necessary; Beast reuses UVs for LOD levels, but I don't know how well that works). It's much faster and uses fewer light lightmaps than my previous method so I figured I'd share it in case anyone else needs it.

    Also, using time minus one breaks the changing of LOD levels. I haven't investigated it so it might just be another script that is messing it up somehow.

    Thanks again for making this wonderful tool.
     
  35. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Thanks for the info. This is useful. I may try to include it in the manual. BTW, I am incorporating the code for adjusting the LOD level in the inspector. There are some errors in unity 3.5 so I have a in tweeking to do but I will try to include it in the next version.
     
  36. gecko

    gecko

    Joined:
    Aug 10, 2006
    Posts:
    2,240
    For the skinned meshes, can they have any variation in their animations--playing different clips or even staggered timing on the same animation? Or do they all have to play exactly the same clip?
     
  37. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    Skinned meshes can be combined and use different animations and/or animator controllers. I use a script to offset the animation of each of my characters connected to the same animator controller to keep them from being synchronized, but that's not really related directly to Mesh Baker - it's just an example that proves the functionality. Afaik, Mesh Baker just links the vertices of the combined mesh to the bones from the original mesh. Unity doesn't have their API for blendshapes exposed yet, so that's the only thing I've seen Mesh Baker not handle. I've even used Megafiers to deform a mesh before activating the MB2_LOD component, and the mesh keeps the deformation after it's included in the combined mesh. Hope this answers your question.

    IFL
     
  38. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Mesh Baker shares the bones with the source meshes so, whatever is animating the original skinned meshes (Physics,Script,Animation) will animate the combined skinned mesh. If the animations on the source models are phased then the animation on the combined mesh will be phased too.
     
  39. gecko

    gecko

    Joined:
    Aug 10, 2006
    Posts:
    2,240
    Oh, so what IFL said is not accurate -- all the meshes will be synchronized, no variation possible?
     
  40. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    IFL is correct, whatever your source skinned meshes are doing, that is what the combined mesh will do. If your source meshes have variation, then the combined mesh will have variation.
     
  41. gecko

    gecko

    Joined:
    Aug 10, 2006
    Posts:
    2,240
    ah, great, thanks!
     
  42. Diputs

    Diputs

    Joined:
    Feb 14, 2014
    Posts:
    9
    Hello, I downloaded this today and I got some errors when i tried the SkinnedMeshSceneMob example scene:

    LOD [MB2_LOD ConstructionDudeGreen id=205128: inComb=False inQ=False act=none nxt=0 curr=0] the object has no size
    UnityEngine.Debug:LogError(Object)
    MB2_LOD:CalculateSwitchDistances(Single, Boolean) (at Assets/MeshBakerLOD/Scripts/MB2_LOD.cs:317)
    MB2_LOD:Init() (at Assets/MeshBakerLOD/Scripts/MB2_LOD.cs:260)
    MB2_LOD:Update() (at Assets/MeshBakerLOD/Scripts/MB2_LOD.cs:368)
     
  43. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    The problem is probably the bounds field on the ConstructionDudeGreen prefab. It is probably set to scale of 0,0,0. This prefab is not actually included in the package, it is part of the MeshBaker package. You may have an older version of Mesh Baker or have imported the construction dude separately in standard assets.

    It should be easy to fix by setting the bounds on one of the skinned mesh renderer instances and clicking apply. You will need to check the other levels of detail as well.
     
  44. Diputs

    Diputs

    Joined:
    Feb 14, 2014
    Posts:
    9
    I tried reimporting both Meshbaker and Meshbaker LOD from the asset store, but to no avail. I also tried following your tutorial on youtube but i still got the same error when i hit Play. I also tried importing a completely different model but it still gives me the same error and i doublechecked the bounds on all models and they look fine.

    Am i still missing something?
     
  45. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Hi Diputs,

    Wow, it looks like a few things have are different in 4.3: you will need to replace the function CalculateSwitchDistances in MB2_LOD.cs with this:

    Code (csharp):
    1.  
    2.     Vector3 AbsVector3(Vector3 v){
    3.         return new Vector3(Mathf.Abs (v.x),Mathf.Abs (v.y),Mathf.Abs (v.z));
    4.     }
    5.  
    6.     public bool CalculateSwitchDistances(float fieldOfView, bool showWarnings){
    7.         bool success = true;
    8.         if (levels.Length == 0){
    9.             Debug.LogError(this + " does not have any LOD levels set up.");
    10.             success = false;
    11.         }
    12.        
    13.         for (int i = 1; i < levels.Length; i++){
    14.             if (levels[i].screenPercentage >= levels[i - 1].screenPercentage){
    15.                 if (showWarnings) Debug.LogError("LOD object " + this + " screenPercentage must be in decending order");   
    16.                 success = false;
    17.             }
    18.         }      
    19.        
    20.         float[] hs = new float[levels.Length];
    21.         for (int i = 0; i < levels.Length; i++){
    22.             Renderer mr = levels[i].lodObject;
    23.             if (mr != null){
    24.                 Bounds b = mr.bounds;
    25.                 if (mr is SkinnedMeshRenderer){ //bounds of an inactive mesh renderer is always zero
    26.                     bool a = MB2_Version.GetActive(mr.gameObject);
    27.                     bool e = mr.enabled;
    28.                     MB2_Version.SetActive(mr.gameObject,true);
    29.                     mr.enabled = true;
    30.                     Matrix4x4 l2w = mr.transform.localToWorldMatrix;
    31.                     SkinnedMeshRenderer smr = (SkinnedMeshRenderer) mr;
    32.                     b =  new Bounds(l2w * (smr.localBounds.center),
    33.                                     AbsVector3(l2w * (smr.localBounds.size)));
    34.                     MB2_Version.SetActive(mr.gameObject, a);
    35.                     mr.enabled = e;
    36.                 }
    37.                 levels[i].switchDistances = 0;
    38.                 levels[i].sqrtDist = 0;
    39.                 if (levels[i].screenPercentage <= 0){
    40.                     if (showWarnings) Debug.LogError("LOD object " + this + " screenPercentage must be greater than zero.");   
    41.                     success = false;
    42.                     continue;
    43.                 }              
    44.                 float h = (b.size.x + b.size.y + b.size.z) / 3f;
    45.                 if (h == 0){
    46.                     if (showWarnings) Debug.LogError("LOD " + this + " the object at lod " + i + " has no size " + b);
    47.                     success = false;
    48.                     continue;
    49.                 }
    50.                 hs[i] = h;
    51.                 levels[i].dimension = h;
    52.                 for (int j = 0; j < i; j++){
    53.                     if (hs[j] > 1.5f*h || hs[j] < h/1.5f){
    54.                         if (showWarnings) Debug.LogError("LOD " + this + " the render bounds of lod levels " + i + " and " + j + " are very differnt sizes." +
    55.                             "They should be very close to the same size. LOD uses these to determine when to switch from one LOD to another.");
    56.                         success = false;
    57.                     }
    58.                 }
    59.                 float d = 50 / Mathf.Tan( Mathf.Deg2Rad * fieldOfView / 2f); //distance to virtual screen that is 100 units wide
    60.                 levels[i].switchDistances =  h * d / (50f * levels[i].screenPercentage);
    61.                 levels[i].sqrtDist = levels[i].switchDistances;
    62.                 levels[i].switchDistances = levels[i].switchDistances * levels[i].switchDistances; 
    63.             } else {
    64.                 success = false;   
    65.             }
    66.         }
    67.         if (myLog != null) myLog.Log(MB2_LogLevel.trace,this + "CalculateSwitchDistances called fov=" +fieldOfView + " success="+success,LOG_LEVEL);       
    68.         return success;
    69.     }
    70.  
    4.3 also broke CharacterControler.SimpleMove. The Update function will need to change in

    MB2_SimpleCharacter

    Code (csharp):
    1.  
    2.     void Update () {
    3.         if (Time.frameCount % 500 == 0) return;
    4.         Vector3 playerPos = playerCamera.transform.position + playerCamera.transform.forward * 2f;
    5.         Vector3 toPlayer = playerPos - transform.position;
    6.         toPlayer.Normalize();
    7.         characterController.Move(toPlayer * speed * Time.deltaTime);
    8.     }
    9.  
    I will submit an update to the asset store.
     
    Last edited: Feb 17, 2014
  46. Diputs

    Diputs

    Joined:
    Feb 14, 2014
    Posts:
    9
    Thanks! that fixed it
     
  47. NanoMath

    NanoMath

    Joined:
    Dec 22, 2012
    Posts:
    33
    Great plugins !!! Well done !!! Awesome work !!! Thank you very much !!! Mesh Baker 2 Mesh Baker LOD I need for all my current and future projects !!!
    I have just one question till now about Mesh Baker LOD :) Phong, can you help me please ?

    Question: for MB2_LODManager class I can add only MB2_MeshBaker class ? or can I use MB2_MultiMeshBaker class too ? if YES, then describe please how to do this :)

    Sorry if I misunderstood something ...

    Thanks !
     
  48. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Thanks for the kind comments.

    You can only add MB2_MeshBaker classes. However, it is important to understand that these MB2_MeshBakers ARE NOT USED FOR THE ACTUAL BAKING. They are templates, their settings are copied to multiple MB2_MultiMeshCombiner instances created and used by the clusters.
     
  49. NanoMath

    NanoMath

    Joined:
    Dec 22, 2012
    Posts:
    33
    Thank you very much for your quick response !!!

    Can you help me please with another question one ? :) I have a lot of MB2_MultiMeshCombiner objects for different entities and I don't want to remake them from START with new MB2_MeshCombiner for MB2_LODManager, can you show me please some tips tricks in your code (some kind of hacks) :) how to change your code for MB2_LODManager to copy their settings NOT from MeshBaker, but from MultiMeshBaker so I don't need to remake all stuff I was made before buying your GREAT Mesh Baker LOD plugin ? can you please just answer if this is possible and where in the code I need to make little changes ?

    Thank you very much !!!!
     
  50. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,083
    Hi NanoMath,

    You will not need to regenerate the MB2_MeshBakers from the start. You don't need the MB2_TextureBaker at all (you can remove it). You only need to drag the "Material Bake Result" asset to the new Mesh Baker and set the options in "output" section of the inspector. That is all that is needed. You don't need to give it any objects to combine. It should not take long to set these up. I would recommend this approach.

    You could probably change the code so it uses MB2_MultiMeshBakers instead but I can't guarantee it will work and it will probably break all the example scenes.. If you search in MB2_LODManager.cs there is a class called BakerPrototype which has a field called "meshBaker". Change the type of this to MB2_MultiMeshBaker. There will probably be a few errors generated where you will need to change MB2_MeshBaker to MB2_MultiMeshBaker.

    Hope this helps.