Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Mass place grass?

Discussion in 'Editor & General Support' started by trickyspark, Feb 26, 2010.

  1. trickyspark

    trickyspark

    Joined:
    Apr 24, 2009
    Posts:
    121
    I know that I can mass place trees, I was wondering if I could mass place grass as well?
     
  2. Chris Morris

    Chris Morris

    Joined:
    Dec 19, 2009
    Posts:
    259
  3. trickyspark

    trickyspark

    Joined:
    Apr 24, 2009
    Posts:
    121
    Will do, thanks for the reply.
     
  4. Kragh

    Kragh

    Joined:
    Jan 22, 2008
    Posts:
    656
    I made a mass place grass editor script some weeks ago, in which you simply define which index number your grass detail object has, and then define which of your splat textures you want to to fill with grass details (their indexes in the terrain component). Lastly you define how dense you want it (1-16). And click ok.
    Takes 1 second to paint even grass at the complete terrain this way :)

    I can upload it tomorrow, when I'm at work...
     
  5. SolidiusWolf

    SolidiusWolf

    Joined:
    Jan 25, 2010
    Posts:
    9
    You the man.
     
  6. Kragh

    Kragh

    Joined:
    Jan 22, 2008
    Posts:
    656
    Hi again

    Here's the mass grass placement script. This should be saved in a folder called "Editor" alongside you standard-/pro asset folders.
    This was made for my own use, and isn't very descriptive in its form. I took the time to put in some returns if some settings are invalid, but other than that, you are on your own.
    Some things to consider:

    Setting the detail density to one, doesn't mean only one is placed per detail control pixel. It's just the lowest amount. The same goes for 16. (EDIT: WRONG!)

    For non coders using this, be aware that indices starts at 0. The first detail object in your terrain detail libary will have the number "0", and if you have 8 different detail objects, and want to place the last on the list, you would write index number "7". The splat texture property is an array, and you first define how many different textures in your terrain splat libary you want to affect. And afterwards you write the actual indices.

    You should also be aware that you will delete all previously placements of the given detail object. So if you have already placed grass at some places, that will be reset. Kinda like the mass tree placement functionality works...

    Lastly this is without undo functionality. If you want to remove all grass previously placed, then just define the detail and a splat texture, and write "0" as the amount you want to place...

    When you have imported this into your project, it will be found under the Terrain menu, called "Mass Grass Placement"

    Good luck, hope it helps some. And if you have problems, just write in here...


    Code (csharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections;
    4. using System.IO;
    5.  
    6. public class GrassCreator : ScriptableWizard  {
    7.  
    8.     public Terrain terrain;
    9.     public int detailIndexToMassPlace;
    10.     public int[] splatTextureIndicesToAffect;
    11.     public int detailCountPerDetailPixel = 0;
    12.        
    13.     [MenuItem ("Terrain/Mass Grass Placement")]
    14.    
    15.     static void createWizard() {
    16.        
    17.             ScriptableWizard.DisplayWizard("Select terrain to put grass on", typeof (GrassCreator), "Place Grass on Terrain");
    18.        
    19.     }
    20.    
    21.     void OnWizardCreate () {
    22.        
    23.         if (!terrain) {
    24.             Debug.Log("You have not selected a terrain object");
    25.             return;
    26.         }
    27.        
    28.         if (detailIndexToMassPlace >= terrain.terrainData.detailPrototypes.Length) {
    29.             Debug.Log("You have chosen a detail index which is higher than the number of detail prototypes in your detail libary. Indices starts at 0");
    30.             return;
    31.         }
    32.        
    33.         if (splatTextureIndicesToAffect.Length > terrain.terrainData.splatPrototypes.Length) {
    34.             Debug.Log("You have selected more splat textures to paint on, than there are in your libary.");
    35.             return;
    36.         }
    37.        
    38.         for (int i = 0; i < splatTextureIndicesToAffect.Length; i ++) {
    39.             if (splatTextureIndicesToAffect[i] >= terrain.terrainData.splatPrototypes.Length) {
    40.                 Debug.Log("You have chosen a splat texture index which is higher than the number of splat prototypes in your splat libary. Indices starts at 0");
    41.                 return;
    42.             }
    43.         }
    44.        
    45.         if (detailCountPerDetailPixel > 16) {
    46.             Debug.Log("You have selected a non supported amount of details per detail pixel. Range is 0 to 16");
    47.             return;
    48.         }
    49.        
    50.         int alphamapWidth = terrain.terrainData.alphamapWidth;
    51.         int alphamapHeight = terrain.terrainData.alphamapHeight;
    52.         int detailWidth = terrain.terrainData.detailResolution;
    53.         int detailHeight = detailWidth;
    54.        
    55.         float resolutionDiffFactor = (float)alphamapWidth/detailWidth;
    56.        
    57.        
    58.         float[,,] splatmap = terrain.terrainData.GetAlphamaps(0,0,alphamapWidth,alphamapHeight);
    59.        
    60.        
    61.         int[,] newDetailLayer = new int[detailWidth,detailHeight];
    62.        
    63.         //loop through splatTextures
    64.         for (int i = 0; i < splatTextureIndicesToAffect.Length; i++) {
    65.            
    66.             //find where the texture is present
    67.             for (int j = 0; j < detailWidth; j++) {
    68.            
    69.                 for (int k = 0; k < detailHeight; k++) {
    70.                        
    71.                     float alphaValue = splatmap[(int)(resolutionDiffFactor*j),(int)(resolutionDiffFactor*k),splatTextureIndicesToAffect[i]];
    72.                        
    73.                     newDetailLayer[j,k] = (int)Mathf.Round(alphaValue * ((float)detailCountPerDetailPixel)) + newDetailLayer[j,k];
    74.            
    75.                 }
    76.                
    77.             }
    78.            
    79.         }
    80.        
    81.         terrain.terrainData.SetDetailLayer(0,0,detailIndexToMassPlace,newDetailLayer); 
    82.        
    83.     }
    84.    
    85.     void OnWizardUpdate ()
    86.     {
    87.         helpString = "Ready";
    88.        
    89.    
    90.        
    91.        
    92.     }
    93.    
    94. }
     

    Attached Files:

    Last edited: Apr 1, 2011
    betaFlux, marakusa and Stormy102 like this.
  7. Discord

    Discord

    Joined:
    Mar 19, 2009
    Posts:
    1,008
    Works great! Thanks
     
  8. Kragh

    Kragh

    Joined:
    Jan 22, 2008
    Posts:
    656
    I said something wrong:

    It actually means exactly that. A setting of "1" does indeed place 1 on each resolution pixel and so forth. Must have counted wrong initially.
     
  9. Tom163

    Tom163

    Joined:
    Nov 30, 2007
    Posts:
    1,272
    Hm, when I did extensive testing for my scripts, I found out that values about 9 give you odd results. Are you sure that 16 works well? I may have to go back and do some more testing.
     
  10. Kragh

    Kragh

    Joined:
    Jan 22, 2008
    Posts:
    656
    Well, it works perfectly here :)
    A value of 16 will give you 16 individual detail objects on each detail map pixel. 9 will give you... 9 :wink:
    If you put in a number bigger than 16, it will still give you 16.

    Maybe you put details on twice? A double amount of 8 will do, but 9 will go over the max. Maybe your loops takes you through the array twice, adding to what was already there the first time through?
     
  11. Tom163

    Tom163

    Joined:
    Nov 30, 2007
    Posts:
    1,272
    Yes, of course. I do have a density multiplier there, that explains it.
     
  12. LosAngeles

    LosAngeles

    Joined:
    Sep 30, 2010
    Posts:
    11
    Hi!
    Thank you very much for this great script!
    I got a problem with U3 though. Here is the log:
    I am sorry if it's one of my mistakes, I'm just non-programmer.
     
  13. Chaoss@Perdition

    Chaoss@Perdition

    Joined:
    Mar 10, 2011
    Posts:
    37
    Doesn't work :(

    NullReferenceException: Object reference not set to an instance of an object
    UnityEditor.ScriptableWizard.DisplayWizard (System.String title, System.Type klass, System.String createButtonName, System.String otherButtonName) (at C:/BuildAgent/work/6bc5f79e0a4296d6/Editor/MonoGenerated/Editor/ScriptableWizard.cs:106)
    UnityEditor.ScriptableWizard.DisplayWizard (System.String title, System.Type klass, System.String createButtonName) (at C:/BuildAgent/work/6bc5f79e0a4296d6/Editor/MonoGenerated/Editor/ScriptableWizard.cs:96)
    GrassCreator.createWizard () (at Assets/Standard Assets/grasscreator_131.cs:17)
     
  14. Kragh

    Kragh

    Joined:
    Jan 22, 2008
    Posts:
    656
    The only recreation I can make of you problems, has to do with the name of the C# file. It HAS to be the same name as the Class. So I'm sure it'll work if you call the file : "GrassCreator"
    Right now it's probably just "NewBehaviourScript" or something random of your own choice, which won't work. So, rename the file to the above, and it will work fine
    ;)

    EDIT: It might even be "grasscreator_131.cs", as for some strange reason, that is the name of the attached file :p
    Change it to GrassCreator.cs! (if you do it directly in the editor, leave out the .cs)
     
    Last edited: Apr 1, 2011
  15. paverson

    paverson

    Joined:
    Jul 17, 2012
    Posts:
    11
    Thanks for the great script! It works, but TOO WELL. It fills my scene with such a dense blanket of grass that it is unnavigable. Is there a way to make it it much less dense (while not affecting the density of other grass/plants/trees placed in scene)? Then this would be amazing...

    Thanks!
     
  16. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    Just looking at the script...try lowering this value:

    detailCountPerDetailPixel
     
  17. roger0

    roger0

    Joined:
    Feb 3, 2012
    Posts:
    1,208
    Hey, im trying out this script, and im wondering if I have to use a splat map for it to work. Or can I use it just like the mass place trees function? Also, im a bit confused at the different settings. Could someone explain it a bit more clearly?
     
  18. Kragh

    Kragh

    Joined:
    Jan 22, 2008
    Posts:
    656
    Detail Index To Mass Place: Look at the detail objects you have added to the terrain, and count to the one you want to place (starting with 0);

    Splat Texture Indices To Affect: This is the setting which control where to place the grass. If for instance you want to place grass everywhere you have a given texture, you just count which index this texture have (as with the above you start out at 0!). You can add more indexes, if for instance your grass areas are painted using more than one texture. Just add as many as you like.

    Detail Count Per Detail Pixel: A terrain is divided into squares, and each square can hold up to 16 of each detail type. This setting simply defines how many grass planes you want to add at each section. This amount will be multiplied with the actual strength of the given texture, so if you only have 50% grass strength at some position, only 50% of the amount of planes will be placed.

    Hope this script still works, haven't tested in ages...
     
    Last edited: Oct 5, 2012
    Stormy102 likes this.
  19. Baldwin

    Baldwin

    Joined:
    Apr 1, 2014
    Posts:
    37
    Hello. Seems interesting. Does this works on imported meshes as well? Thanks.
     
  20. MikeUpchat

    MikeUpchat

    Joined:
    Sep 24, 2010
    Posts:
    1,055
    If you look at the code on this rather old script I think you will see that quite clearly that it doesn't :)
     
  21. Zaxs_Reaper

    Zaxs_Reaper

    Joined:
    Jul 8, 2014
    Posts:
    13
    Hi guys. I have a question. Who do I use it cause I have no idea? ^_^
     
  22. DarkWindowStudios

    DarkWindowStudios

    Joined:
    Aug 26, 2015
    Posts:
    1
    We had an issue with adding grass to our 10 sq km terrain, so we took this script and made it a little more user-friendly. This is licensed under the MIT License. Hope someone finds use with this:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. public class GrassCreator : EditorWindow
    5. {
    6.  
    7.     public Terrain terrain;
    8.     public int detailIndexToMassPlace;
    9.     public int[] splatTextureIndicesToAffect = new int[] { 0, };
    10.     public int detailCountPerDetailPixel = 1;
    11.  
    12.     [MenuItem("Tools/Terrain/Mass Grass Placement")]
    13.  
    14.     static void Init()
    15.     {
    16.         GrassCreator window = (GrassCreator)GetWindow(typeof(GrassCreator));
    17.         window.Show();
    18.         window.titleContent = new GUIContent("Grass Creator");
    19.         window.Focus();
    20.         window.ShowUtility();
    21.         if (Selection.activeGameObject && Selection.activeGameObject.GetComponent<Terrain>())
    22.         {
    23.             window.terrain = Selection.activeGameObject.GetComponent<Terrain>();
    24.         }
    25.     }
    26.  
    27.     void OnGUI()
    28.     {
    29.         GUILayout.Label("Grass Creator", EditorStyles.boldLabel);
    30.         GUILayout.Label("Settings for Grass", EditorStyles.centeredGreyMiniLabel);
    31.  
    32.         ScriptableObject ter = this;
    33.         SerializedObject terrainso = new SerializedObject(ter);
    34.         SerializedProperty property = terrainso.FindProperty("terrain");
    35.         EditorGUILayout.PropertyField(property, new GUIContent("Terrain Object:", "Place your terrain object in here."), true);
    36.         terrainso.ApplyModifiedProperties();
    37.  
    38.         if (terrain != null)
    39.         {
    40.             detailCountPerDetailPixel = EditorGUILayout.IntSlider(new GUIContent("Detail Counter Per Detail Pixel:", "The detail count per detail pixel"), detailCountPerDetailPixel, 1, 16);
    41.             detailIndexToMassPlace = EditorGUILayout.IntSlider(new GUIContent("Detail Index to Place:", "Select the grass index to mass place"), detailIndexToMassPlace, 0, terrain.terrainData.detailPrototypes.Length - 1);
    42.  
    43.             ScriptableObject target = this;
    44.             SerializedObject so = new SerializedObject(target);
    45.             SerializedProperty stringsProperty = so.FindProperty("splatTextureIndicesToAffect");
    46.             EditorGUILayout.PropertyField(stringsProperty, new GUIContent("Splat Textures to place on:", "Indicate the splatmap index to place on."), true);
    47.             so.ApplyModifiedProperties();
    48.  
    49.             if (GUILayout.Button(new GUIContent("Mass Place Grass", "Work the magic :D")))
    50.             {
    51.                 CreateGrass();
    52.             }
    53.         }
    54.     }
    55.  
    56.     void CreateGrass()
    57.     {
    58.  
    59.         if (!terrain)
    60.         {
    61.             Debug.LogError("You have not selected a terrain object");
    62.             return;
    63.         }
    64.  
    65.         if (detailIndexToMassPlace >= terrain.terrainData.detailPrototypes.Length)
    66.         {
    67.             Debug.LogError("You have chosen a detail index which is higher than the number of detail prototypes in your detail libary. Indices starts at 0");
    68.             return;
    69.         }
    70.  
    71.         if (splatTextureIndicesToAffect.Length > terrain.terrainData.splatPrototypes.Length)
    72.         {
    73.             Debug.LogError("You have selected more splat textures to paint on, than there are in your libary.");
    74.             return;
    75.         }
    76.  
    77.         for (int i = 0; i < splatTextureIndicesToAffect.Length; i++)
    78.         {
    79.             if (splatTextureIndicesToAffect[i] >= terrain.terrainData.splatPrototypes.Length)
    80.             {
    81.                 Debug.LogError("You have chosen a splat texture index which is higher than the number of splat prototypes in your splat libary. Indices starts at 0");
    82.                 return;
    83.             }
    84.         }
    85.  
    86.         if (detailCountPerDetailPixel > 16)
    87.         {
    88.             Debug.LogError("You have selected a non supported amount of details per detail pixel. Range is 0 to 16");
    89.             return;
    90.         }
    91.  
    92.         int alphamapWidth = terrain.terrainData.alphamapWidth;
    93.         int alphamapHeight = terrain.terrainData.alphamapHeight;
    94.         int detailWidth = terrain.terrainData.detailResolution;
    95.         int detailHeight = detailWidth;
    96.         float resolutionDiffFactor = (float)alphamapWidth / detailWidth;
    97.         float[,,] splatmap = terrain.terrainData.GetAlphamaps(0, 0, alphamapWidth, alphamapHeight);
    98.         int[,] newDetailLayer = new int[detailWidth, detailHeight];
    99.  
    100.         for (int i = 0; i < splatTextureIndicesToAffect.Length; i++)
    101.         {
    102.  
    103.             for (int j = 0; j < detailWidth; j++)
    104.             {
    105.  
    106.                 for (int k = 0; k < detailHeight; k++)
    107.                 {
    108.                     float alphaValue = splatmap[(int)(resolutionDiffFactor * j), (int)(resolutionDiffFactor * k), splatTextureIndicesToAffect[i]];
    109.                     newDetailLayer[j, k] = (int)Mathf.Round(alphaValue * ((float)detailCountPerDetailPixel)) + newDetailLayer[j, k];
    110.                 }
    111.  
    112.             }
    113.  
    114.         }
    115.         terrain.terrainData.SetDetailLayer(0, 0, detailIndexToMassPlace, newDetailLayer);
    116.     }
    117. }
    118.  
     
    betaFlux and Kragh like this.
  23. florianalexandru05

    florianalexandru05

    Joined:
    Mar 31, 2014
    Posts:
    1,755
    Great script but do you get a working version for unity 5? Nvm it works fine.
     
    Last edited: Apr 2, 2016
  24. weblobsdesigner

    weblobsdesigner

    Joined:
    Apr 9, 2018
    Posts:
    1
    any way to have it updated for the last version ? with the monoBevhaviour resquested ?
     
  25. Deleted User

    Deleted User

    Guest

    1st script don t work and with second i can only place grass on the 1st texture, strange

    Detail Index to Place not working

    Ok so to make this work, you have to set Splat Texture to Place on to 1
    Then Element 0 will be the texture wich on it is placed, so 0 the 1st texture etc

    Work great
     
  26. no00ob

    no00ob

    Joined:
    Dec 13, 2017
    Posts:
    65
    This thread is ages old but I can't find any other reliable script to do this but after the terrain system update in 2018.3 this script won't work anymore and I'm out of luck as this was my main way to place grass so if anyone would like to fix it up for 2018.3 and later since I tried and it just ended up crashing Unity lmao I'd appreciate it a lot.
     
  27. Kragh

    Kragh

    Joined:
    Jan 22, 2008
    Posts:
    656
    Man... this thread is still around?!?? :D
    I have not been using the terrain system for ages, so I might not be the right guy to fix my original script. Who knows what they have changed internally...
    Hope you got it to work eventually. If I get the time some day, I may take a look. But it will not be this day! ;)

    EDIT: Just tried it in Unity 2019, and it worked fine. But man, the Terrain system has changed a bunch since the last time I worked on it, took me a while to understand how to even insert new textures and details.
    But anyways, besides the fact that my old script uses some deprecated systems (Lots of warnings in the console), it still did the job in my quick test. What exactly doesn't work for you?
     
    Last edited: Oct 9, 2019
  28. Kragh

    Kragh

    Joined:
    Jan 22, 2008
    Posts:
    656
    Updated version without warnings. But it still works. I fixed DarkWindowStudios version of it, as the window approach was more user friendly. As for why you, no00ob, can't get it to work, I don't know.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. public class GrassPlacement : EditorWindow
    5. {
    6.  
    7.     public Terrain terrain;
    8.     public int detailIndexToMassPlace;
    9.     public int[] splatTextureIndicesToAffect = new int[] { 0, };
    10.     public int detailCountPerDetailPixel = 1;
    11.  
    12.     [MenuItem("Tools/Terrain/Auto Grass Placement")]
    13.  
    14.     static void Init()
    15.     {
    16.         GrassPlacement window = (GrassPlacement)GetWindow(typeof(GrassPlacement));
    17.         window.Show();
    18.         window.titleContent = new GUIContent("Grass Creator");
    19.         window.Focus();
    20.         window.ShowUtility();
    21.         if (Selection.activeGameObject && Selection.activeGameObject.GetComponent<Terrain>())
    22.         {
    23.             window.terrain = Selection.activeGameObject.GetComponent<Terrain>();
    24.         }
    25.     }
    26.  
    27.     void OnGUI()
    28.     {
    29.         GUILayout.Label("Grass Creator", EditorStyles.boldLabel);
    30.         GUILayout.Label("Settings for Grass", EditorStyles.centeredGreyMiniLabel);
    31.  
    32.         ScriptableObject ter = this;
    33.         SerializedObject terrainso = new SerializedObject(ter);
    34.         SerializedProperty property = terrainso.FindProperty("terrain");
    35.         EditorGUILayout.PropertyField(property, new GUIContent("Terrain Object:", "Place your terrain object in here."), true);
    36.         terrainso.ApplyModifiedProperties();
    37.  
    38.         if (terrain != null)
    39.         {
    40.             detailCountPerDetailPixel = EditorGUILayout.IntSlider(new GUIContent("Detail Counter Per Detail Pixel:", "The detail count per detail pixel"), detailCountPerDetailPixel, 1, 16);
    41.             detailIndexToMassPlace = EditorGUILayout.IntSlider(new GUIContent("Detail Index to Place:", "Select the grass index to mass place"), detailIndexToMassPlace, 0, terrain.terrainData.detailPrototypes.Length - 1);
    42.  
    43.             ScriptableObject target = this;
    44.             SerializedObject so = new SerializedObject(target);
    45.             SerializedProperty stringsProperty = so.FindProperty("splatTextureIndicesToAffect");
    46.             EditorGUILayout.PropertyField(stringsProperty, new GUIContent("Splat Textures to place on:", "Indicate the splatmap index to place on."), true);
    47.             so.ApplyModifiedProperties();
    48.  
    49.             if (GUILayout.Button(new GUIContent("Mass Place Grass", "Work the magic :D")))
    50.             {
    51.                 CreateGrass();
    52.             }
    53.         }
    54.     }
    55.  
    56.     void CreateGrass()
    57.     {
    58.  
    59.         if (!terrain)
    60.         {
    61.             Debug.LogError("You have not selected a terrain object");
    62.             return;
    63.         }
    64.  
    65.         if (detailIndexToMassPlace >= terrain.terrainData.detailPrototypes.Length)
    66.         {
    67.             Debug.LogError("You have chosen a detail index which is higher than the number of detail prototypes in your detail libary. Indices starts at 0");
    68.             return;
    69.         }
    70.  
    71.         if (splatTextureIndicesToAffect.Length > terrain.terrainData.terrainLayers.Length)
    72.         {
    73.             Debug.LogError("You have selected more splat textures to paint on, than there are in your libary.");
    74.             return;
    75.         }
    76.  
    77.         for (int i = 0; i < splatTextureIndicesToAffect.Length; i++)
    78.         {
    79.             if (splatTextureIndicesToAffect[i] >= terrain.terrainData.terrainLayers.Length)
    80.             {
    81.                 Debug.LogError("You have chosen a splat texture index which is higher than the number of splat prototypes in your splat libary. Indices starts at 0");
    82.                 return;
    83.             }
    84.         }
    85.  
    86.         if (detailCountPerDetailPixel > 16)
    87.         {
    88.             Debug.LogError("You have selected a non supported amount of details per detail pixel. Range is 0 to 16");
    89.             return;
    90.         }
    91.  
    92.         int alphamapWidth = terrain.terrainData.alphamapWidth;
    93.         int alphamapHeight = terrain.terrainData.alphamapHeight;
    94.         int detailWidth = terrain.terrainData.detailResolution;
    95.         int detailHeight = detailWidth;
    96.         float resolutionDiffFactor = (float)alphamapWidth / detailWidth;
    97.         float[,,] splatmap = terrain.terrainData.GetAlphamaps(0, 0, alphamapWidth, alphamapHeight);
    98.         int[,] newDetailLayer = new int[detailWidth, detailHeight];
    99.  
    100.         for (int i = 0; i < splatTextureIndicesToAffect.Length; i++)
    101.         {
    102.  
    103.             for (int j = 0; j < detailWidth; j++)
    104.             {
    105.  
    106.                 for (int k = 0; k < detailHeight; k++)
    107.                 {
    108.                     float alphaValue = splatmap[(int)(resolutionDiffFactor * j), (int)(resolutionDiffFactor * k), splatTextureIndicesToAffect[i]];
    109.                     newDetailLayer[j, k] = (int)Mathf.Round(alphaValue * ((float)detailCountPerDetailPixel)) + newDetailLayer[j, k];
    110.                 }
    111.  
    112.             }
    113.  
    114.         }
    115.         terrain.terrainData.SetDetailLayer(0, 0, detailIndexToMassPlace, newDetailLayer);
    116.     }
    117. }
     
    Randolphjand and betaFlux like this.
  29. no00ob

    no00ob

    Joined:
    Dec 13, 2017
    Posts:
    65
    Actually I did manage to get it to work and it wasnt working due to my terrain and how I had setted it up, but nice that you finally replied lmao
     
    Kragh likes this.
  30. AlexmLutz

    AlexmLutz

    Joined:
    Nov 12, 2019
    Posts:
    1
    So I have it in my Editor Folder in my Assets folder, but I still don't have the option for mass place grass, any idea what could be wrong?

    It also says that one or more brushes is now read-only if that helps
     
  31. no00ob

    no00ob

    Joined:
    Dec 13, 2017
    Posts:
    65
    [Assets > Scripts > Editor] is the correct path for the script.
     
  32. betaFlux

    betaFlux

    Joined:
    Jan 7, 2013
    Posts:
    110
    Just as an addition. If you want your grass to be placed only in places where your splat texture is very dominant, play around with the "alphaValue" int the script above. Just add an if check inside the nested for loop at the end and look what value is best in your scenario.
    Code (CSharp):
    1.  
    2. float alphaValue = splatmap[(int)(resolutionDiffFactor * j), (int)(resolutionDiffFactor * k), splatTextureIndicesToAffect[i]];
    3. if(alphaValue < 0.9f)
    4. {
    5.     continue;
    6. }
    7. newDetailLayer[j, k] = (int)Mathf.Round(alphaValue * ((float)detailCountPerDetailPixel)) + newDetailLayer[j,
    8.  
     
  33. Randolphjand

    Randolphjand

    Joined:
    Sep 23, 2017
    Posts:
    28
    Is there a way to place the same detail on a different texture with a different detail density without changing the details as they already exist on another texture?
     
  34. Mashimaro7

    Mashimaro7

    Joined:
    Apr 10, 2020
    Posts:
    723
    Sorry to revive this 7 year thread, but I put it in this directory, and I have no idea how to mass paint grass, there's no button showing up or anything...
     
  35. mgrekt

    mgrekt

    Joined:
    Jun 22, 2019
    Posts:
    92
    Well, it's been awhile for me painting grass, but you did add the grass as a detailed mesh? When you select the terrain and in the inspector in the flower tab? If so, did it appear in what is called paint detailed?
     
  36. Mashimaro7

    Mashimaro7

    Joined:
    Apr 10, 2020
    Posts:
    723
    Yeah, I added it as a grass texture. I can paint it manually as well, but there's no mass paint grass button.
     
  37. mgrekt

    mgrekt

    Joined:
    Jun 22, 2019
    Posts:
    92
    I don't there is honestly, I usually just make the brush larger and go wild with it.
     
  38. Mashimaro7

    Mashimaro7

    Joined:
    Apr 10, 2020
    Posts:
    723
    The reason I can't really do this is my map is fairly large, and I'll be having many maps... This thread shows a way of implementing a mass paint grass button, but I can't get it to work.
     
  39. mgrekt

    mgrekt

    Joined:
    Jun 22, 2019
    Posts:
    92
    You could make the terrain smaller I realize then brush it, could be a good idea, to make it small enough to brush it all in one swipe.
     
    Mashimaro7 likes this.
  40. Mashimaro7

    Mashimaro7

    Joined:
    Apr 10, 2020
    Posts:
    723
    That actually worked amazingly haha, I shrunk it down from 1024x1024 to 50x50, put brush size to full, one click and I have grass evenly distributed across my terrain! Thank you
     
  41. BrunoBelmonte

    BrunoBelmonte

    Joined:
    Jan 16, 2013
    Posts:
    7
    I've made some changes as well to make it a little simpler to set how much grass will be placed on texture intersections.
    Now you just have a slider from 0.0 to 1.0.

    On my use, something between 0.7 to 0.8 works the best =)

    Code (CSharp):
    1. public class GrassPlacement : EditorWindow
    2. {
    3.     public Terrain terrain;
    4.     public int detailIndexToMassPlace;
    5.     public int[] splatTextureIndicesToAffect = new int[] { 0, };
    6.     public int detailCountPerDetailPixel = 1;
    7.     public float channelIntersection = 0.8f;
    8.     [MenuItem("Tools/Terrain/Auto Grass Placement")]
    9.     static void Init()
    10.     {
    11.         GrassPlacement window = (GrassPlacement)GetWindow(typeof(GrassPlacement));
    12.         window.Show();
    13.         window.titleContent = new GUIContent("Grass Creator");
    14.         window.Focus();
    15.         window.ShowUtility();
    16.         if (Selection.activeGameObject && Selection.activeGameObject.GetComponent<Terrain>())
    17.         {
    18.             window.terrain = Selection.activeGameObject.GetComponent<Terrain>();
    19.         }
    20.     }
    21.     void OnGUI()
    22.     {
    23.         GUILayout.Label("Grass Creator", EditorStyles.boldLabel);
    24.         GUILayout.Label("Settings for Grass", EditorStyles.centeredGreyMiniLabel);
    25.         ScriptableObject ter = this;
    26.         SerializedObject terrainso = new SerializedObject(ter);
    27.         SerializedProperty property = terrainso.FindProperty("terrain");
    28.         EditorGUILayout.PropertyField(property, new GUIContent("Terrain Object:", "Place your terrain object in here."), true);
    29.         terrainso.ApplyModifiedProperties();
    30.         if (terrain != null)
    31.         {
    32.             detailCountPerDetailPixel = EditorGUILayout.IntSlider(new GUIContent("Detail Counter Per Detail Pixel:", "The detail count per detail pixel"), detailCountPerDetailPixel, 1, 16);
    33.             detailIndexToMassPlace = EditorGUILayout.IntSlider(new GUIContent("Detail Index to Place:", "Select the grass index to mass place"), detailIndexToMassPlace, 0, terrain.terrainData.detailPrototypes.Length - 1);
    34.             channelIntersection = EditorGUILayout.Slider(new GUIContent("Intersection: ", "How much grass on channels intersections"), channelIntersection, 0, 1);
    35.  
    36.             ScriptableObject target = this;
    37.             SerializedObject so = new SerializedObject(target);
    38.             SerializedProperty stringsProperty = so.FindProperty("splatTextureIndicesToAffect");
    39.             EditorGUILayout.PropertyField(stringsProperty, new GUIContent("Splat Textures to place on:", "Indicate the splatmap index to place on."), true);
    40.             so.ApplyModifiedProperties();
    41.             if (GUILayout.Button(new GUIContent("Mass Place Grass", "Work the magic :D")))
    42.             {
    43.                 CreateGrass();
    44.             }
    45.         }
    46.     }
    47.     void CreateGrass()
    48.     {
    49.         if (!terrain)
    50.         {
    51.             Debug.LogError("You have not selected a terrain object");
    52.             return;
    53.         }
    54.         if (detailIndexToMassPlace >= terrain.terrainData.detailPrototypes.Length)
    55.         {
    56.             Debug.LogError("You have chosen a detail index which is higher than the number of detail prototypes in your detail libary. Indices starts at 0");
    57.             return;
    58.         }
    59.         if (splatTextureIndicesToAffect.Length > terrain.terrainData.terrainLayers.Length)
    60.         {
    61.             Debug.LogError("You have selected more splat textures to paint on, than there are in your libary.");
    62.             return;
    63.         }
    64.         for (int i = 0; i < splatTextureIndicesToAffect.Length; i++)
    65.         {
    66.             if (splatTextureIndicesToAffect[i] >= terrain.terrainData.terrainLayers.Length)
    67.             {
    68.                 Debug.LogError("You have chosen a splat texture index which is higher than the number of splat prototypes in your splat libary. Indices starts at 0");
    69.                 return;
    70.             }
    71.         }
    72.         if (detailCountPerDetailPixel > 16)
    73.         {
    74.             Debug.LogError("You have selected a non supported amount of details per detail pixel. Range is 0 to 16");
    75.             return;
    76.         }
    77.         int alphamapWidth = terrain.terrainData.alphamapWidth;
    78.         int alphamapHeight = terrain.terrainData.alphamapHeight;
    79.         int detailWidth = terrain.terrainData.detailResolution;
    80.         int detailHeight = detailWidth;
    81.         float resolutionDiffFactor = (float)alphamapWidth / detailWidth;
    82.         float[,,] splatmap = terrain.terrainData.GetAlphamaps(0, 0, alphamapWidth, alphamapHeight);
    83.         int[,] newDetailLayer = new int[detailWidth, detailHeight];
    84.         for (int i = 0; i < splatTextureIndicesToAffect.Length; i++)
    85.         {
    86.             for (int j = 0; j < detailWidth; j++)
    87.             {
    88.                 for (int k = 0; k < detailHeight; k++)
    89.                 {
    90.                     float alphaValue = splatmap[(int)(resolutionDiffFactor * j), (int)(resolutionDiffFactor * k), splatTextureIndicesToAffect[i]];
    91.                     if(alphaValue < channelIntersection)
    92.                     {
    93.                         continue;
    94.                     }
    95.                     newDetailLayer[j, k] = (int)Mathf.Round(alphaValue * ((float)detailCountPerDetailPixel)) + newDetailLayer[j, k];
    96.                 }
    97.             }
    98.         }
    99.         terrain.terrainData.SetDetailLayer(0, 0, detailIndexToMassPlace, newDetailLayer);
    100.     }
    101. }
     
  42. no00ob

    no00ob

    Joined:
    Dec 13, 2017
    Posts:
    65
    This thread just keeps on giving haha I wonder how many people have added their touch to that script!
    You won't probably need this information anymore but this is the spot where the button should appear.
    upload_2020-12-1_3-21-1.png
    Then it opens up this window where you have to tweak the settings and finally apply the grass.
    upload_2020-12-1_3-22-21.png
     
    Mashimaro7 likes this.
  43. toythebeast

    toythebeast

    Joined:
    Jul 22, 2021
    Posts:
    1
    How do i erase all?
     
  44. sk8avp

    sk8avp

    Joined:
    Feb 8, 2018
    Posts:
    1
    There is any way to set this script to create the grass only at certain position or upper in the Y axis?
    As example, i have a water level in Y 10 so i dont want to create grass below the 10 of Y
     
  45. MichaelEGA

    MichaelEGA

    Joined:
    Oct 11, 2019
    Posts:
    34
    I love this, does exactly what I want too.

    BUT when ever I try to enter my layer numbers they constantly reset to zero... so I can only ever edit layer 0... any thoughts?

    I'm using unity 2021.2.13f1

    EDIT: I managed to hack a solution by mixing up the original and the new version, but would still love to know why I was encountering that problem.

    MY CUSTOM VERSION:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections;
    4. using System.IO;
    5.  
    6. public class GrassCreator : ScriptableWizard
    7. {
    8.  
    9.     public Terrain terrain;
    10.     public int detailIndexToMassPlace;
    11.     public int[] splatTextureIndicesToAffect;
    12.     public int detailCountPerDetailPixel = 0;
    13.     public float channelIntersection = 0.8f;
    14.  
    15.     [MenuItem("Tools/Terrain/Auto Grass Placement")]
    16.  
    17.     static void createWizard()
    18.     {
    19.  
    20.         ScriptableWizard.DisplayWizard("Select terrain to put grass on", typeof(GrassCreator), "Place Grass on Terrain");
    21.  
    22.     }
    23.  
    24.     void OnWizardCreate()
    25.     {
    26.  
    27.         if (!terrain)
    28.         {
    29.             Debug.LogError("You have not selected a terrain object");
    30.             return;
    31.         }
    32.         if (detailIndexToMassPlace >= terrain.terrainData.detailPrototypes.Length)
    33.         {
    34.             Debug.LogError("You have chosen a detail index which is higher than the number of detail prototypes in your detail libary. Indices starts at 0");
    35.             return;
    36.         }
    37.         if (splatTextureIndicesToAffect.Length > terrain.terrainData.terrainLayers.Length)
    38.         {
    39.             Debug.LogError("You have selected more splat textures to paint on, than there are in your libary.");
    40.             return;
    41.         }
    42.         for (int i = 0; i < splatTextureIndicesToAffect.Length; i++)
    43.         {
    44.             if (splatTextureIndicesToAffect[i] >= terrain.terrainData.terrainLayers.Length)
    45.             {
    46.                 Debug.LogError("You have chosen a splat texture index which is higher than the number of splat prototypes in your splat libary. Indices starts at 0");
    47.                 return;
    48.             }
    49.         }
    50.  
    51.  
    52.         int alphamapWidth = terrain.terrainData.alphamapWidth;
    53.         int alphamapHeight = terrain.terrainData.alphamapHeight;
    54.         int detailWidth = terrain.terrainData.detailResolution;
    55.         int detailHeight = detailWidth;
    56.         float resolutionDiffFactor = (float)alphamapWidth / detailWidth;
    57.         float[,,] splatmap = terrain.terrainData.GetAlphamaps(0, 0, alphamapWidth, alphamapHeight);
    58.         int[,] newDetailLayer = new int[detailWidth, detailHeight];
    59.         for (int i = 0; i < splatTextureIndicesToAffect.Length; i++)
    60.         {
    61.             for (int j = 0; j < detailWidth; j++)
    62.             {
    63.                 for (int k = 0; k < detailHeight; k++)
    64.                 {
    65.                     float alphaValue = splatmap[(int)(resolutionDiffFactor * j), (int)(resolutionDiffFactor * k), splatTextureIndicesToAffect[i]];
    66.                     if (alphaValue < channelIntersection)
    67.                     {
    68.                         continue;
    69.                     }
    70.                     newDetailLayer[j, k] = (int)Mathf.Round(alphaValue * ((float)detailCountPerDetailPixel)) + newDetailLayer[j, k];
    71.                 }
    72.             }
    73.         }
    74.         terrain.terrainData.SetDetailLayer(0, 0, detailIndexToMassPlace, newDetailLayer);
    75.  
    76.     }
    77.  
    78.     void OnWizardUpdate()
    79.     {
    80.         helpString = "Ready";
    81.  
    82.  
    83.  
    84.  
    85.     }
    86.  
    87. }
     
    Last edited: Jul 9, 2022
  46. Mashimaro7

    Mashimaro7

    Joined:
    Apr 10, 2020
    Posts:
    723
    I actually found a really cool plugin that places grass on specific layers if you wanna try it out, it's free. Tons of customization, and can even be used for detail objects and trees :)
    https://assetstore.unity.com/packages/tools/terrain/vegetation-spawner-177192
     
    MichaelEGA likes this.
  47. MichaelEGA

    MichaelEGA

    Joined:
    Oct 11, 2019
    Posts:
    34

    I tried this, it was good, but couldn't handle the number of trees I was generating (like half a million) without hanging unity. My hack job works fine, just interested to know why @Kragh version doesn't work for me.
     
  48. Mashimaro7

    Mashimaro7

    Joined:
    Apr 10, 2020
    Posts:
    723
    I'm not sure, you should expect some hang time with half a million trees lol. It's probably because it adds size variation and rotation to the trees, i think also some colour variation? Maybe your method wasn't doing all this. Half a million is a lot though, why do you have so many? I just did a google search, even skyrim only has 6500. Why in the world would you need half a million in one scene? lol
     
  49. Kragh

    Kragh

    Joined:
    Jan 22, 2008
    Posts:
    656
    12 years later, and people still use variations of this script?!?? It has been many years since I myself had the need for large terrain creations, and it baffles me that Unity is still using a terrain solution that 1) Somewhat works with my script, and 2) Doesn't have this feature built-in, pre-packaged with a nice bling to it. It must have! No?
    Anyways, @MichaelEGA , I have no idea why it didn't work for you, without your hacks. And I don't really see myself digging into the problem, I am so damn old by now :D
    Glad you found a solution :)
    I'll see you all again in some years, when this thread resurfaces. Or not :)