Search Unity

Mesh Baker LOD [RELEASED]

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

  1. NanoMath

    NanoMath

    Joined:
    Dec 22, 2012
    Posts:
    33
    Thank you very much !!! This is very very helpful !!! I think your 1st approach is the BEST :)
    Thanks a lot !!! Good Luck !!!
     
  2. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    Thanks for the recent v2.0 update. The direction you're taking this system is good.
     
  3. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    Thanks IFL,

    If anyone is curious, version 2 is is a big refactor and a number of performance improvements:

    • Refactored Initialization of LODs which now happens in Start instead of on first LOD change
    • Changed "Update" in MB2_LOD to "CheckIfLODNeedsToChange" so frequency of calling can be controlled.
    • Created a class for checking if LODs need to change. Close-to-camera clusters are checked frequently, distant clusters are checked less frequently
    • Added the check scheduler to the LOD Manager
    • Refactored the Cluster class by splitting into two classes. By splitting the cluster it is possible to distribute a clusters bake across several frames.
      • LODCombinedMesh is a wrapper for MB2_CombinedMesh that contains Queues and performs callbacks on baking.
      • LODCluster is a defined volume and contains one or more LODCombinedMeshes
    • Added a "SwapMeshWithLOD0" checkbox to the levels in the LOD. This makes managing animations through LOD changes MUCH easier.
     
  4. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    I'm loving the most recent update. Any chance you could work in a way to set the cast/receive shadows options?
     
  5. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    Will try to get this in the next version.
     
  6. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    Hello some questions:

    does this plugin batch dynamic lodded mesh as well? (I assume it does)

    can it actually generate lod levels? Mesh Baker Lod and Mesh Baker 2 look awesome, but it seems that they both miss a real loding system.
     
    Last edited: Apr 4, 2014
  7. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    The LOD system can easily batch objects that unity considers dynamic. Some scenarios work better than others:

    Scenario 1: Objects that don't move generated at runtime, such as chunks in an endless world. This works very well.
    Scenario 2: Objects that are static most of the time but occasionally move such as doors. Can be batched with a bit of work. Lots of options but it will take a bit of work.
    Scenario 3: Moving objects. Your best option is to bake them into a Skinned mesh or don't batch them. You can still do LOD without batching on individual objects.

    This system does not include a tool for reducing meshes. I started working on one but it is an enormous project. There are other assets in the asset store such as Cruncher that do this. Mesh Baker LOD is all about managing resources at runtime in the most efficient possible way.
     
  8. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    If dynamic objects are not batched, how is this better than the standard lod?
     
  9. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    When built in LOD is used with static batching, all LOD levels are built into huge meshes at build time. LOD works by turning parts of the meshes on and off. The vertices for the high levels of detail are still present in the static meshes even when high levels of detail are turned off. There is no ability to support static objects created at runtime. Also there are also a lot more vertices in memory than necessary. All the high level of detail meshes are effectively instanced for all objects. It is not possible to build an endless world or a big city with built in LOD and have your objects be batched with static batching.

    Mesh Baker LOD bakes the meshes as needed resulting in a much smaller memory footprint.

    The other huge bonus is the ability to combine and LOD mobs of skinned meshes. Check out this video https://www.youtube.com/watch?v=zhpyUUS3Lhs 7:00 minutes in.
     
  10. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    looks great, I wished that was available for dynamic object without skinning. Why didn't you support that?
     
  11. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    It comes down to performance. If there are many objects moving independently then they would need to be combined every frame. This is only practical for tiny combined meshes and Unity will try to do it with Dynamic Batching anyway. There is an example in Mesh Baker that does this but the performance isn't any better than Unity's dynamic batching. The best thing to do in this case is to use Mesh Baker to make as many meshes as possible use the same material and have uniform scale. Then Unity's dynamic batching should combine whatever is visible in a fairly efficient way.

    There are usually lots of optimizations that can be made on a case by case basis depending on the game. Doors for example can be static and baked into a combined mesh 99% of the time and swapped for a dynamic object only when they need to open or close. Same thing with crates and other semi-static objects.
     
    Last edited: Apr 6, 2014
  12. davidsirmons

    davidsirmons

    Joined:
    Mar 16, 2014
    Posts:
    190
    Hahahah, no joke, Phong. The path to Madness indeed. I did an art test for the Call of Duty guys where I had to create the AGR treaded machine gun unit.

    The LOD steps were:
    10,000
    7,000
    2,400
    600.

    It ate an entire day to create these LOD's. And to really get it right, if they had asked for a MIP mapped DXT, I'd have to burn a base normal map out for each LOD: 1 for each MIP level. As far as I can see, that's really the only way to keep normals correct on LOD meshes.
     
  13. ilya_ca

    ilya_ca

    Joined:
    Nov 19, 2011
    Posts:
    274
    Hi, I'm interested in your package, and have a few questions:

    1. Most of the objects in my game are static objects with LOD (a large forest). Will I get any benefits by using your system over the built-in LOD groups + static batching solution?

    2. Currently the scene files are huge because of a large number of triangles (most of them are identical objects though). Will your system help decrease the scene size?

    Thanks.
     
    Last edited: Apr 27, 2014
  14. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    The main benefits will be:

    1) Much smaller scene sizes since the combined meshes are not built until runtime.
    2) Ability to add content dynamically and have it baked into combined meshes.
    3) Ability to combine skinned meshes.
    4) Ability to easily create atlases and adjust models so they can be combined.

    Drawbacks

    1) Since baking happens at runtime performance will not be as good as with static batching.
     
  15. sathya

    sathya

    Joined:
    Jul 30, 2012
    Posts:
    297
    @Pong,
    If you are using custom clusters to render objects. How is it going to work with Unity Occlusion Culling ?
    Does it support Occlusion culling ?
    If yes how is this better from unity occlusion culling?
    Isn't it very time consuming process to prepare a huge city ?
    I am interested in your product but just wondering if i have to use many mesh bakers for objects with same material scattered all over the city ?
    My city is huge and very low poly, there is no place for LOD so can i still use this package to take advantage of cluster rendering if its better then Unity Occlusion Culling ?
    Thanks in advance.
     
  16. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    Mesh Baker LOD won't work with occlusion culling because the combined meshes are all created dynamically at runtime. There is no way to know which meshes are occluded and which are not. Mesh Baker LOD DOES work with fustrum culling. Because all the meshes that are clustered together in a cubic volume are baked together then unity can cull the whole cluster if it is outside the camera fustrum.
    It depends on your setup and how much work you want to put in.

    The simplest thing to do would be to create one mesh baker for each distinct material in your city. This would take 1-2 minutes per material. Then you would need to modify your building prefabs so that they use a single Mesh Baker LOD component. This should take 1-2 minutes per prefab.

    The next level would be to combine as many materials as you can into atlases using Mesh Baker. This may even save you work by reducing

    The next level would be to create more levels of LOD although it sounds like your buildings are so simple already that there would be no benefit to that.
    You could use this product, although in your case you may be better off using Mesh Baker by itself (without the LOD add on). You could bake your meshes together in the editor to reduce drawcalls. This would avoid the overhead of the LOD engine running in your scene.
     
    Last edited: Apr 26, 2014
  17. nikescar

    nikescar

    Joined:
    Nov 16, 2011
    Posts:
    165

    Hello, I just got mesh baker and mesh baker lod. Imported them to the project and im getting this. Looks like its looking for MB3 and everything that imported was MB2. Is there a quick fix for this? or am i missing something? I pulled in the LODmanager and it says script can not be loaded.
     
  18. nikescar

    nikescar

    Joined:
    Nov 16, 2011
    Posts:
    165
    I uninstalled the mesh baker and mesh baker lod. Re-installed 2 different times and now its working.
     
  19. dylanliu

    dylanliu

    Joined:
    Jun 13, 2014
    Posts:
    1
    hello phong.
    i have some question about lod. my avater build by 3 part , body weapon and horse. but in lod example "SkinnedMeshSceneMob", On ConstructionDudeGreen every lod level it's "lod Object" only can set one part of my avater.. i can't display my avater fully in lod..
    any way to set multi lod object in lod level ?
     
  20. jonkuze

    jonkuze

    Joined:
    Aug 19, 2012
    Posts:
    1,709
    Hello I am getting this Error Message:
    Not allowed to access uv on mesh 'Combined Mesh (root: scene)'

    Please Assist! I am following your Video Tutorials Step by Step. The Meshes just disappears from my Scene and all LODs Game Objects go inactive in my scene Hierarchy when I press Play to Test with the Camera.
     
  21. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    Sorry for the very, very slow response. I had been receiving email notifications of updates to threads so I had stopped checking them daily. I just discovered that the notifications have stopped working or are not reliable. I will go back to checking these threads daily.

    Regarding your question. At this time the only way to handle this is to combine the meshes together first into a single mesh and add that. I do plan to add multiple meshes as a feature.

    Alternatively you could create three LOD groups. One for body, One for weapon, One for the horse.
     
  22. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    Hi Kuroato,

    Sorry for the slow response. Unity added a "read/write enabled" flag to the inspector for imported meshes in Unity 4.. This needs to be checked in order to read the model data.
     
    Last edited: Sep 18, 2014
  23. cdiaz

    cdiaz

    Joined:
    Aug 1, 2012
    Posts:
    6
    Is there any proper way to use the system if I instantiate my camera after all the assets in the scene have been loaded? Not that I can't find a shortcut, I just want to know your oppinion.

    Thank you very much.
     
    Last edited: Oct 9, 2014
  24. nikescar

    nikescar

    Joined:
    Nov 16, 2011
    Posts:
    165
    Hello,

    We bought MeshBaker and MeshBakerLOD thinking it supported mobile (I thought I read in this thread that it did). I have been trying to use MeshBakerLOD for our mobile project and I am unable to get decent performance on any of our test devices (Nexus 4, Nexus 7, Nexus 10, etc). Even with the example scenes the garbage collection is killing performance and leading to consistent stuttering when it occurs multiple times a second. I have tried changing parameters in our project and in your test scenes like "Num Bakes between GC Calls", "Combine time per frame", "Grid Size", "Num Frames between checks", "Max vertices per combined mesh", and nothing will make this usable on mobile. Is this not meant for mobile game development?

    Also, in a separate question, is there an option to use MeshBaker (not MeshBakerLOD) to combine objects in a grid during editor time? Assume a large terrain with many rocks for instance. Then place a 10x10 grid over the terrain. We would like each cell to bake all objects in that cell into single mesh. Is this possible with your tool?

    Thanks.
     
  25. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    Hi Cdiaz, the LOD_Manager has a methods, AddCamera and RemoveCamera. You should be able to use these to add and remove cameras to the system.
     
  26. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    Hi Nikescar,

    MeshBakerLOD does support mobile. Most of my testing has been on iOS on an older ipod where I get roughly a doubling in frame rate with LOD versus no LOD in the example scenes. Have you tried the example scenes with LOD enabled and LOD disabled to see the difference?

    The clustering feature you describe does not currently exist in Mesh Baker although I am working on it and planning to add it over the next month or two. Here is a quick extract of the current code. This will create a new window. You need to select game objects and it will distribute them across bakers. Save this in a folder called Editor.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using DigitalOpus.MB.Core;
    6.  
    7.  
    8. public class Experiment_Clustering : EditorWindow{
    9.  
    10.     [MenuItem ("Window/Cluster Window")]
    11.     static void ShowWindow () {
    12.         EditorWindow.GetWindow (typeof(Experiment_Clustering));
    13.     }
    14.  
    15.     public Transform parentForCreatedMeshBakers;
    16.     public Vector3 cellSize = Vector3.one;
    17.     public Vector3 gridOrigin;
    18.    
    19.     void OnGUI () {
    20.         EditorGUILayout.BeginHorizontal ();
    21.         EditorGUILayout.LabelField ("Parent For Created Bakers");
    22.         parentForCreatedMeshBakers = (Transform) EditorGUILayout.ObjectField (parentForCreatedMeshBakers, typeof(Transform), true);
    23.         EditorGUILayout.EndHorizontal ();
    24.         cellSize = EditorGUILayout.Vector3Field("Cell Size",cellSize);
    25.         gridOrigin = EditorGUILayout.Vector3Field("Cell Origin",gridOrigin);
    26.         if (GUILayout.Button("CreateBakers")){
    27.             if (parentForCreatedMeshBakers == null){
    28.                 Debug.LogError("Need to create the parent for the mesh bakers");
    29.                 return;
    30.             }
    31.             if (parentForCreatedMeshBakers.transform.childCount != 0){
    32.                 Debug.LogError("The parent for the mesh bakers needs to have no children.");
    33.                 return;
    34.             }
    35.             //todo test that cellSize is positive and nonzero
    36.             Debug.Log("Collecting renderers in each cell");
    37.             Dictionary<Vector3,List<Renderer>> cell2objs = new Dictionary<Vector3,List<Renderer>>();
    38.             foreach (Transform t in Selection.transforms){
    39.                 GameObject go = t.gameObject;
    40.                 Renderer[] mrs = go.GetComponentsInChildren<Renderer>();
    41.                 for (int j = 0; j < mrs.Length; j++){
    42.                     if (mrs[j] is MeshRenderer || mrs[j] is SkinnedMeshRenderer){
    43.                         //todo apply filters here to to filter out what I don't want to add
    44.  
    45.                         //get the cell this gameObject is in
    46.                         Vector3 gridVector = mrs[j].transform.position;
    47.                         gridVector.x = Mathf.Floor((gridVector.x - gridOrigin.x) / cellSize.x)  * cellSize.x;
    48.                         gridVector.y = Mathf.Floor((gridVector.y - gridOrigin.y) / cellSize.y)  * cellSize.y;
    49.                         gridVector.z = Mathf.Floor((gridVector.z - gridOrigin.z) / cellSize.z)  * cellSize.z;
    50.                         List<Renderer> objs = null;
    51.                         if (cell2objs.ContainsKey(gridVector)){
    52.                             objs = cell2objs[gridVector];
    53.                         } else {
    54.                             objs = new List<Renderer>();
    55.                             cell2objs.Add (gridVector,objs);
    56.                         }
    57.                         objs.Add (mrs[j]);
    58.                     }
    59.                 }
    60.             }
    61.             if (cell2objs.Count == 0){
    62.                 Debug.LogWarning ("The selection contained no scene objects. With renderers as children. Select some scene objects.");
    63.             }
    64.  
    65.             Debug.Log ("Found " + cell2objs.Count + " cells with Renderers. Creating bakers.");
    66.             foreach (Vector3 key in cell2objs.Keys){
    67.                 List<Renderer> gaws = cell2objs[key];
    68.                 int numVerts = 0;
    69.                 for (int i = 0; i < gaws.Count; i++){
    70.                     Mesh m = MB_Utility.GetMesh(gaws[i].gameObject);
    71.                     if (m != null)
    72.                         numVerts = m.vertexCount;
    73.                 }
    74.                
    75.                 GameObject newMeshBaker = null;
    76.                 if (numVerts >= 65535){
    77.                     newMeshBaker = MB3_MultiMeshBakerEditor.CreateNewMeshBaker();
    78.                 } else {
    79.                     newMeshBaker = MB3_MeshBakerEditor.CreateNewMeshBaker();
    80.                 }
    81.                 MB3_TextureBaker tb = newMeshBaker.GetComponent<MB3_TextureBaker>();
    82.                 List<GameObject> objsToCombine = tb.GetObjectsToCombine();
    83.                 for (int i = 0; i < gaws.Count; i++){
    84.                     objsToCombine.Add (gaws[i].gameObject);
    85.                 }
    86.                 newMeshBaker.name = "MeshBaker-" + key;
    87.                 newMeshBaker.transform.parent = parentForCreatedMeshBakers.transform;
    88.             }
    89.         }
    90.     }
    91. }
     
    IFL likes this.
  27. negativecap

    negativecap

    Joined:
    Jan 27, 2013
    Posts:
    99
    I'm trying to write an assetimporter for my stuff and wanted to get the MB2_LOD script assembled at import so I no longer have to manually build LOD groups. The problem is I can't code ;)

    The question is: How might I assign LOD Levels on the MB2_LOD script through script (kinda like unity does, using naming convention)? I can assign the script in onpostprocess, and discover the lods in a recursive lookup, but I'm not sure how to assign the LODs to the LOD[] levels array on the script. Any help would be appreciated. Here's part of the code for the LOD lookup, this is where I'd like to add the discovered LOD levels to the script on the root object. I'm sure my code doesn't actually make sense,


    Code (CSharp):
    1.     private static void recursiveLODCheck(Transform trans) {
    2.      
    3.         var LOD0 = trans.name.ToLower ().Contains ("_lod0");
    4.         var LOD1 = trans.name.ToLower ().Contains ("_lod1");
    5.         var LOD2 = trans.name.ToLower ().Contains ("_lod2");
    6.         var LOD3 = trans.name.ToLower ().Contains ("_lod3");
    7.         var LOD4 = trans.name.ToLower ().Contains ("_lod4");
    8.  
    9.         foreach (Transform currentTran in trans) {
    10.             recursiveLODCheck (currentTran);
    11.         }
    12.         if (LOD0) {
    13.                         Debug.Log ("LOD0 Found!");
    14.  
    15.                         Component[] lod0 = trans.gameObject.GetComponentsInChildren (typeof(MeshRenderer), false);
    16.  
    17.                         foreach (MeshRenderer lod in lod0) {
    18.                                                                  
    19.                                 MB2_LOD.LOD[] levels = lod.ToArray ();
    20.                              
    21.                         }
    22.                 }
    edit: I'd also like to setup screen percentages automatically if that's possible...
     
  28. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    @Phong Clustering for MB sounds awesome!
     
  29. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    Hi negativecap,

    This should work. First do a recursive crawl to find the LODs and put them in a dictionary then. Build the LOD array from the dictionary.

    Code (CSharp):
    1.     static string[] lodNames = new string[]{"_lod0","_lod1","_lod2","_lod3","_lod4","_lod5"};
    2.  
    3.     private static void recursiveLODCheck(Transform trans, Dictionary<string,MeshRenderer> level2mesh) {
    4.         Transform found = null;
    5.         string level = "";
    6.  
    7.         foreach (string name in lodNames) {
    8.             if (trans.name.ToLower ().Contains (name)) {
    9.                     MeshRenderer[] mrs = trans.gameObject.GetComponentsInChildren<MeshRenderer> (false);
    10.                     if (mrs.Length > 1) Debug.LogWarning("Found a transform with name " + name + " that had more than one mesh. Adding first one.");
    11.                     if (level2mesh.ContainsKey(name)) Debug.LogError("Found more than one transform with name " + name);
    12.                     else {
    13.                             level2mesh.Add (name,mrs[0]);
    14.                     }
    15.             }
    16.         }
    17.  
    18.         foreach (Transform currentTran in trans) {
    19.                 recursiveLODCheck (currentTran, level2mesh);
    20.         }
    21.     }
    22.  
    23.     private void setupLOD(MB2_LOD lod, Dictionary<string,MeshRenderer> level2mesh){
    24.         MB2_LOD.LOD[] lods = new MB2_LOD.LOD[level2mesh.Count];
    25.         foreach (string key in level2mesh.Keys) {
    26.             int pos = int.Parse(key.Substring(4)); //todo this could fail should use TryParse and check result
    27.             if (pos >= lods.Length || pos < 0){
    28.                 Debug.LogError("LOD found was outside the range 0.."+level2mesh.Count);
    29.             } else {
    30.                 lods[pos] = new MB2_LOD.LOD();
    31.                 lods[pos].lodObject = level2mesh[key];
    32.             }
    33.         }
    34.  
    35.         //check that all slots were filled
    36.         for (int i = 0; i < lods.Length; i++) {
    37.             if (lods[i] == null) Debug.LogError("Missing lod level " + i);
    38.         }
    39.         lod.levels = lods;
    40.     }
    41.  
    42. //Use it like this
    43.  
    44.         MB2_LOD targetLOD = GetComponent<MB2_LOD> ();
    45.         Dictionary<string,MeshRenderer> name2renderer = new Dictionary<string, MeshRenderer> ();
    46.         recursiveLODCheck (transform, name2renderer);
    47.         Debug.Log ("Found " + name2renderer.Count + " LODs");
    48.         setupLOD (targetLOD,name2renderer);
    49.  
     
    Last edited: Oct 22, 2014
  30. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    @Phong I know this isn't directly related to MBLOD as it's advertized, but I want to get your opinion in case I'm overlooking something. I'm creating a texture atlas at runtime of prodecurally generated content. Can I simply set the result material, the source materials, and the source positions & sizes in a TextureBakeResults and have the objects work with MBLOD?
     
  31. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    Yes, there is nothing magic about the MB2_TextureBakeResults. However you generate it, it should work.
     
  32. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    Actually, I ran into some magic. ;) The material is also generated at runtime, and the MB2_LOD can't find a baker for it even though the TexResults were given the exact material and UVRect. It works great if my script assigns a material asset before setting up the MB2_LOD. That's a workaround I'm fine with though.

    Here's a shot of a generated UMA character * 7:
    MBLOD_UMA_01.jpg

    Thanks for selling such a great asset.
     
    hopeful likes this.
  33. Phong

    Phong

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

    Looks good. I believe the LODManager has methods AddBaker and RemoveBaker. I think that if you add the bakers using this they should initialize properly and it should work.
     
  34. negativecap

    negativecap

    Joined:
    Jan 27, 2013
    Posts:
    99
    Great, thanks for the working code, should help streamline things significantly.

    Is there a simple way to set screen percentage along with assigning the lods? I'd like to have some basic values plugged in, I realize I'll have to tweak them but for testing and all just having a plug and play LOD would be killer.
     
  35. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    !!!!!!!!!!!!!!! That solved another issue I had with trying to use my generated atlases. You rock. I've still got some problems to fix with UMA, but MBLOD is a freakin' masterpiece. 64 characters sharing one 1k texture across 6 drawcalls:
    MBLOD_UMA_02.jpg
     
  36. hopeful

    hopeful

    Joined:
    Nov 20, 2013
    Posts:
    5,684
    I'm glad you're looking into this, IFL. I was thinking about the possibility of using MBLOD with UMA when MBLOD first came out, but I never got around to fiddling with it.

    Please share some details on the UMA thread about how to get MBLOD working with UMA, and what it can do. :)
     
  37. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    This is a very subjective thing, so there is no fancy way to get good values. I would just have your script enter some numbers in the screenPercentage field. Something like this should work (untested). It should generate a screen percentage of 1 for level 0 and a screen percentage of 1/6 for level 5:

    Code (CSharp):
    1.                ....
    2.                 lods[pos] = new MB2_LOD.LOD();
    3.                 lods[pos].lodObject = level2mesh[key];
    4.                 lods[pos].screenPercentage = 1f  / (pos + 1f);
    5.                ...
    Now that I think about it, the area of an object onscreen will diminish as the square of the distance, so a better formula might be:

    Code (CSharp):
    1.  
    2.                 lods[pos].screenPercentage = 1f  / (pos*pos + 1f)
    3.  
     
    Last edited: Oct 24, 2014
  38. nikescar

    nikescar

    Joined:
    Nov 16, 2011
    Posts:
    165
    Thanks for the response. The dynamic mesh baking/combining, works great to reduce draw calls leading to better performance, true. But the garbage collection on mobile platforms kills it. It doesn't matter if I can get an average of 60-ish fps when I have multiple stutters a second related to garbage collections. A game doesn't feel smooth this way. The garbage created per bake by MeshBakerLOD accessing mesh data like vertices, normals, etc, is up to 1.5mb on sparsely populated world. This leads to regular garbage collection and stutters.

    This plugin may work great on PC but I wouldn't recommend it for mobile devices.
     
  39. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    I don't know why you're seeing that kind of GC allocation, but I haven't **ever** seen anything like that (outside of improper instantiating) and I've done lots of projects with this plugin. Are you getting that number with the demo provided with MBLOD or is it from your own scene(s)?
     
  40. nikescar

    nikescar

    Joined:
    Nov 16, 2011
    Posts:
    165
    That is with the village demo scene unaltered except for a script that rotated the camera around the "player". This simulates movement through a scene.
     
  41. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    Wow. I apologize for my skepticism. I'm seeing upwards of 1.4mb of GC for that scene, which is much higher than I remember when I first tested this plugin. The stuttering is pretty annoying. That said, I'm still not seeing more than about 550kb in my own scenes, and they have no noticeable stuttering on a 2.5 years-old Galaxy Note 1.
     
  42. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    I will take a look at this and see if I can figure out what is going on.
     
    IFL likes this.
  43. IFL

    IFL

    Joined:
    Apr 13, 2013
    Posts:
    408
    @Phong - thanks for checking it out.

    Actually, it's not feasible to use UMA and MBLOD together until some form of LOD gets worked into UMA. :( If you look at the vert count in my picture above, that's insane for mobile, and combining the meshes is unnecessary for PC.
     
  44. hopeful

    hopeful

    Joined:
    Nov 20, 2013
    Posts:
    5,684
    Ah, and here I thought you were working some kind of magic! Oh well. :)
     
  45. negativecap

    negativecap

    Joined:
    Jan 27, 2013
    Posts:
    99
    If anybody's interested here's some copypasta ripped from that assetimporter script Phong helped me out with a few posts ago.

    This script grabs FBX and OBJs that have the proper naming convention (ie. the FBX has multiple models in one file, one each with a "_LOD0" or "_LOD1" etc. in the file name) and creates a prefab for them in the same folder as the mesh file, and instead of adding the default Unity LODGroup component adds a MB2LOD component with some starting values. Beats manually setting up a lot of LOD prefabs. Be aware that it will do this for all models imported into the project unless you go into the script and associate it with a naming convention or something (which is what I'm doing, highly recommended).

    Just paste the following in a C# script called MyAssetImporter.cs in a folder called "Editor" in your project and it should work (untested in this version, works fine in my script though).

    Fair warning, this assetimporter script is my first foray into programming. Like, ever. And I don't really know what I'm doing. But it hasn't blown anything up yet on this end. Thanks Phong!

    Code (CSharp):
    1. using System.Linq;
    2. using UnityEngine;
    3. using UnityEditor;
    4. using System;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7. using System.Reflection;
    8. using System.IO;
    9.  
    10. public class MyAssetImporter : AssetPostprocessor {
    11.  
    12.     static string[] lodNames = new string[]{"_lod0","_lod1","_lod2","_lod3","_lod4","_lod5"};
    13.    
    14.         void OnPostprocessModel(GameObject root)    {
    15.        
    16.             // Builds MB2_LOD component instead of Unity LOD component.
    17.             Editor.DestroyImmediate (root.GetComponent<LODGroup> ());
    18.             root.AddComponent<MB2_LOD> ();
    19.             MB2_LOD targetLOD = root.GetComponent<MB2_LOD> ();
    20.             Dictionary<string, MeshRenderer> name2renderer = new Dictionary<string, MeshRenderer> ();
    21.             recursiveLODCheck (root.transform, name2renderer);
    22.             Debug.Log ("Found " + name2renderer.Count + " LODs");
    23.             setupLOD (targetLOD, name2renderer);
    24.  
    25.             }
    26.            
    27.  
    28.         private static void recursiveLODCheck(Transform trans, Dictionary<string,MeshRenderer> level2mesh) {
    29.                 Transform found = null;
    30.                 string level = "";
    31.  
    32.                 foreach (string name in lodNames) {
    33.                         if (trans.name.ToLower ().Contains (name)) {
    34.                                 MeshRenderer[] mrs = trans.gameObject.GetComponentsInChildren<MeshRenderer> (false);
    35.                                 if (mrs.Length > 1)
    36.                                         Debug.LogWarning ("Found a transform with name " + name + " that had more than one mesh. Adding the first one.");
    37.                                 if (level2mesh.ContainsKey (name))
    38.                                         Debug.LogError ("Found more than one transform with name " + name);
    39.                                 else {
    40.                                         level2mesh.Add (name, mrs [0]);
    41.                                 }
    42.                         }
    43.                 }
    44.  
    45.                 foreach (Transform currentTran in trans) {
    46.                         recursiveLODCheck (currentTran, level2mesh);
    47.                 }
    48.         }
    49.                    
    50.     private void setupLOD(MB2_LOD lod, Dictionary<string, MeshRenderer> level2mesh){
    51.                 MB2_LOD.LOD[] lods = new MB2_LOD.LOD[level2mesh.Count];
    52.                 foreach (string key in level2mesh.Keys) {
    53.                         int pos = int.Parse (key.Substring (4));
    54.  
    55.                        
    56.                         if (pos >= lods.Length || pos < 0) {
    57.                                 Debug.LogError ("LOD Found was outside the range 0.." + level2mesh.Count);
    58.                         } else {
    59.                                 lods [pos] = new MB2_LOD.LOD ();
    60.                                 lods [pos].lodObject = level2mesh [key];
    61.                 lods[pos].screenPercentage = 1f  / (pos*pos + 1f);
    62.  
    63.                         }
    64.                 }
    65.  
    66.                 for (int i = 0; i < lods.Length; i++) {
    67.                         if (lods [i] == null)
    68.                                 Debug.LogError ("Missing lod level " + i);
    69.                 }
    70.                 lod.levels = lods;
    71.                
    72.         }
    73.        
    74.     public static void OnPostprocessAllAssets(
    75.         string[] importedAssets, string[] deletedAssets,
    76.         string[] movedAssets, string[] movedFromAssetPaths)
    77.     {
    78.  
    79.         foreach(string assetPath in importedAssets)
    80.         {
    81.  
    82.             if (assetPath.ToLower ().Contains (".fbx") || (assetPath.ToLower ().Contains (".obj"))) {
    83.  
    84.                     string prefabPath = assetPath;
    85.                     prefabPath = prefabPath.Remove (prefabPath.LastIndexOf ("."));
    86.                            
    87.                     GameObject root = GameObject.Instantiate (AssetDatabase.LoadAssetAtPath (assetPath, typeof(GameObject))) as GameObject;
    88.                                            
    89.                     prefabPath += ".prefab";
    90.                    
    91.                     Debug.Log ("Creating Prefab from : " + assetPath);
    92.  
    93.                     if (AssetDatabase.LoadAssetAtPath (prefabPath, typeof(object)) == null) {
    94.                        
    95.                         GameObject newGO = new GameObject (prefabPath.Remove (prefabPath.LastIndexOf (".")));
    96.                        
    97.                         newGO.transform.position = root.transform.position;
    98.                         root.transform.parent = newGO.transform;
    99.  
    100.                         PrefabUtility.CreatePrefab (prefabPath, newGO);
    101.  
    102.                     Debug.Log ("Created Prefab at : " + prefabPath);
    103.  
    104.                         GameObject.DestroyImmediate (newGO);
    105.  
    106.                         }
    107.  
    108.                         GameObject.DestroyImmediate (root);
    109.  
    110.                     }
    111.                 }
    112.             }
    113. }
    114.  
     
    hopeful likes this.
  46. nikescar

    nikescar

    Joined:
    Nov 16, 2011
    Posts:
    165
    Hi Phong,

    I don't know if I was clear enough with my first comment on the issue. My take on this issue is that the garbage is created internally by Unity when accessing things like vertices, normals, etc. I doubt there is much you can do here though I would love to be proven wrong. :)

    I think I should mention that I think both of your assets (MeshBaker and MeshBakerLOD) are great. While we probably won't be able to use MeshBakerLOD on this project, we are using MeshBaker in a custom editor tool to make a "tile" based mesh terrain and object LOD system of our own making.

    Keep up the good work and thank you for these tools.

    - Wes
     
  47. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    Thanks for the comment,

    I spent some time looking at the scene yesterday and there are definitely some things about it that are not set up efficiently. The windows in the houses are a problem. Each window mesh is 4000 verts. There is no point in including these in combined meshes since it only saves 1-2 drawcalls. I am also trying to collect GC stats from different devices and Unity versions.

    I hope to do some testing today on a device and let you know what I find.

    UPDATE:

    OK finally managed to do some testing and was able to improve the scene considerably. I did most of the testing on an 4th Generation iPod. The main problem is the level 0 meshes for the house. There are about 10 sub objects that get baked into combined meshes when the LOD0 becomes active. Some of these sub objects have a lot of vertices (windows have 4000 verts). This is a bad setup because the LOD 0 is only active when a camera is very close so only 1-3 houses will be in this state at a time. This creates frequent baking to create combined meshes with only a few objects in order to save about 8 drawcalls.

    A better setup is to disable baking for the sub objects of LOD 0. This creates a few more drawcalls but dramatically reduces the baking and garbage collection load.

    Baseline (LOD turned off) 11-13 FPS

    With LOD turned on and including everything in the combined mesh
    25 - 30 FPS but noticeable stutters
    baking or garbage collection happening almost every frame when in village
    average bake time 33ms
    average garbage collection time 21ms

    With LOD turned on and including everything in the combined mesh except windows
    28-30 FPS hard to notice any stuttering
    baking or garbage collection happening on half of the frames
    average bake time 24ms
    average garbage collection time 18ms

    With LOD turned on and excluding all sub objects on the LOD 0 of the house
    29-30 FPS very hard to notice any stuttering
    baking or garbage collection happening approx. every 10th frame
    average bake time 26ms
    average garbage collection time 15ms

    I will try to update the package in the asset store with the modified scene.

    I would say that Mesh Baker LOD can definitely be used on mobile. I would suggest avoiding baking large meshes. Keep the size of the combined meshes below 8k. Avoid extensive use of baking on objects close to the camera.
     
    Last edited: Oct 31, 2014
    hopeful likes this.
  48. sowatnow

    sowatnow

    Joined:
    Jun 12, 2014
    Posts:
    309
    Hi
    I got mesh baker, just got couple of questions before i decide to buy the lod aswell.
    I got A tree with 3 meshes, one for leafs, one for leafs flip and another one for trunk.
    How will this work with 3 meshes which has its own shader and it costs 3 draw calls.
    How will this lod baker reduce the drawcall while still keeping the shaders effects?
    Also how will this work if I want to combine 10 trees with same shader? will each of the tree still have wind effect etc,move>?
     
  49. Phong

    Phong

    Joined:
    Apr 12, 2010
    Posts:
    2,085
    Hi Sowatnow,

    This is a fairly complex situation. Regarding the three meshes per tree. One approach if the leaves are different meshes from the trunk would be to LOD them as if they were separate objects. Your tree prefab would have an LOD hierarchy for the trunk, and LOD hierarchy for the leaves and and LOD hierarchy for the leaves flip. The advantage of this would be that you could have different levels for the trunk and each set of leaves. You would still have three draw calls for each different shader, but, for each cluster, all the trunks would be combined, all leaf 1s would be combined and all leafs flip would be combined.

    I hadn't thought about wind. I will test this and check back.
     
  50. sowatnow

    sowatnow

    Joined:
    Jun 12, 2014
    Posts:
    309
    Hi
    I tried that, combined all leaves into one mesh and all trunks into one mesh. But the problem is when I use wind shader, the base of trunk moves too.

    I am using these shaders, link is provided below. If its possible possible to reduce the draw calls even with the mesh object named Spruce singleside, it would be good.

    https://www.assetstore.unity3d.com/en/#!/content/3460
     
    Last edited: Dec 5, 2014