- Home /
Procedural grass placement
I have managed to make a procedural grass placement system, but my problem is that the grass' density isn't heigh enough, but if I paint the grass onto the terrain it is perfect. How can it come that the grass is acting weird when procedurally placed?
My settings:
Terrain Size: 10000x10000
Detail Map: 2048x2048
Detail resolution per patch: 128
Detail Density: 1
Placement code:
for(int x = 0; x < aw; x++) {
for(int z = 0; z < aw; z++) {
int worldPosX = (x+(hw-1))*(terrainWidth/hw);
int worldPosZ = (z+(hw-1))*(terrainWidth/hw);
string Biome = GetBiome(worldPosX,worldPosZ);
if (Biome == "Plain") {
detailMap1[z,x] = 1;
} else if (Biome == "Forest") {
if (Random.Range(0,2) == 1) {
detailMap1[z,x] = 1;
} else {
detailMap2[z,x] = 1;
}
}
}
}
Chunk.terrainData.SetDetailLayer(0,0,0,detailMap1);
Chunk.terrainData.SetDetailLayer(0,0,1,detailMap2);
Answer by AlucardJay · Feb 04, 2015 at 06:51 PM
The value range for each item in the detail array is an integer between 0 and 16. Try 16 and see what you get :)
Edit :
here is a little test. As you can see, any value above 8 is really overkill :
and here was the script I used :
using UnityEngine;
using System.Collections;
public class TerrainDetailTest : MonoBehaviour
{
public Terrain terrain;
private TerrainData terrainData;
void Start()
{
if ( !terrain )
Debug.LogError( gameObject.name + " has NO TERRAIN assigned in the Inspector" );
terrainData = terrain.terrainData;
GenerateTerrainDetail();
}
void GenerateTerrainDetail()
{
int detailWidth = terrainData.detailWidth;
int detailHeight = terrainData.detailHeight;
int[,] details0 = new int[ detailWidth, detailHeight ];
int[,] details1 = new int[ detailWidth, detailHeight ];
int x, y, strength;
for ( x = 0; x < detailWidth / 4; x ++ ) // divided by 4 just to show a test patch
{
for ( y = 0; y < detailHeight / 4; y ++ ) // test patch
{
strength = ( x % 2 == 0 ? ( x / 2 ) % 17 : 0 ); // just to spread the grass out a bit to see the difference
if ( y % 4 == 0 ) // set detail layer 0 for every first row in 4
details0[ y, x ] = strength;
else if ( y % 4 == 2 ) // set detail layer 1 for every third row in 4
details1[ y, x ] = strength;
}
}
terrainData.SetDetailLayer( 0, 0, 0, details0 );
terrainData.SetDetailLayer( 0, 0, 1, details1 );
}
}
@alucardj Like this?
detail$$anonymous$$ap1[z,x] = 16; //?
Because that seems to make the detail disapear completely.
Strange, I shall check my terrain tools when I get home.
I found this information out by reading the detail layer from a terrain with details painted already, finding and returning the largest and smallest values found in the array.
Works my end, have edited the answer to show my test script and result image.
Full Generation code:
IEnumerator SpawnTheGrass() {
Terrain t = Terrain.activeTerrain;
int Dh = t.terrainData.detailWidth;
/* From here */
int CurDiff = $$anonymous$$athf.RoundToInt((8*$$anonymous$$athf.Pow (2, 8)));
for (int i = 1; i < 9; i++) {
int tmp = $$anonymous$$athf.RoundToInt((8*$$anonymous$$athf.Pow (2, i)));
if ($$anonymous$$athf.Abs (terrainSize-tmp) < CurDiff) {
CurDiff = $$anonymous$$athf.$$anonymous$$in (CurDiff,$$anonymous$$athf.Abs (terrainSize-tmp));
Ah = tmp;
}
}
detail$$anonymous$$ap1 = new int[Ah, Ah];
detail$$anonymous$$ap2 = new int[Ah, Ah];
/* To here is normaly in the Awake function */
intendedProgress = 0.0f;
for (int x = 0; x < Dh; x++) {
for (int z = 0; z < Dh; z++) {
float normalPosX = ((x*1f)/Dh)*(terrainWidth*1f);
float normalPosZ = ((z*1f)/Dh)*(terrainWidth*1f);
int worldPosX = (x+(Dh-1))*(terrainWidth/Dh);
int worldPosZ = (z+(Dh-1))*(terrainWidth/Dh);
float height = t.terrainData.GetInterpolatedHeight(normalPosX,normalPosZ);
string Biome = GetBiome (worldPosX,worldPosZ);
if (height > WaterLevel) {
if (Biome == "Plain") {
detail$$anonymous$$ap1[z,x] = 16;
} else if (Biome == "Forest") {
if (Random.Range (0,11) > 5) {
detail$$anonymous$$ap2[z,x] = 10;
} else {
detail$$anonymous$$ap1[z,x] = 10;
}
}
}
intendedProgress = (x*1f)/(Dh-1f);
if (z % (Dh-1f) == 0 && x % 100 == 0) {
yield return null;
}
}
}
t.terrainData.SetDetailLayer(0,0,0,detail$$anonymous$$ap1);
t.terrainData.SetDetailLayer(0,0,1,detail$$anonymous$$ap2);
return true;
}
EDIT: This didn't fix the issue.