public void SpawnGrass(GrassPrefab item) { if (item.collisionCheck) { RebuildCollisionCacheIfNeeded(); } item.instanceCount = 0; foreach (Terrain terrain in terrains) { //2 texels per unit //int density = Mathf.NextPowerOfTwo(Mathf.RoundToInt(terrain.terrainData.size.x * 2f)); //int[,] map = new int[density, density]; //terrain.terrainData.SetDetailResolution(density, terrain.terrainData.detailResolutionPerPatch); #if UNITY_EDITOR //UnityEditor.EditorUtility.DisplayProgressBar("Vegetation Spawner", "Spawning grass...", 1f); #endif int[,] map = terrain.terrainData.GetDetailLayer(0, 0, terrain.terrainData.detailWidth, terrain.terrainData.detailHeight, item.index); for (int x = 0; x < terrain.terrainData.detailWidth; x++) { for (int y = 0; y < terrain.terrainData.detailHeight; y++) { InitializeSeed(x * y + item.seed); //Default int instanceCount = 1; map[x, y] = 0; //XZ world position Vector3 wPos = terrain.DetailToWorld(y, x); Vector2 normalizedPos = terrain.GetNormalizedPosition(wPos); if (item.collisionCheck) { //Check for collision if (InsideOccupiedCell(terrain, wPos, normalizedPos)) { continue; } /* 1 second slower * RaycastHit hit; * if (Physics.Raycast(wPos + (Vector3.up * 50f), -Vector3.up, out hit, 100f, -1)) * { * if (hit.collider.gameObject != terrain.gameObject) * { * continue; * } * } */ } //Skip if failing probability check if (((Random.value * 100f) <= item.probability) == false) { instanceCount = 0; continue; } terrain.SampleHeight(normalizedPos, out _, out wPos.y, out _); if (item.rejectUnderwater && wPos.y < waterHeight) { instanceCount = 0; continue; } //Check height if (wPos.y < item.heightRange.x || wPos.y > item.heightRange.y) { instanceCount = 0; continue; } if (item.slopeRange.x > 0 || item.slopeRange.y < 90) { float slope = terrain.GetSlope(normalizedPos); //Reject if slope check fails if (slope < item.slopeRange.x || slope > item.slopeRange.y) { instanceCount = 0; continue; } } if (item.curvatureRange.x > 0 || item.curvatureRange.y < 1f) { float curvature = terrain.SampleConvexity(normalizedPos); //0=concave, 0.5=flat, 1=convex curvature = TerrainSampler.ConvexityToCurvature(curvature); if (curvature < item.curvatureRange.x || curvature > item.curvatureRange.y) { instanceCount = 0; continue; } } //Reject based on layer masks float spawnChance = 0f; Vector2Int texelIndex = terrain.SplatmapTexelIndex(normalizedPos); if (item.layerMasks.Count == 0) { spawnChance = 100f; } foreach (TerrainLayerMask layer in item.layerMasks) { Texture2D splat = terrain.terrainData.GetAlphamapTexture(GetSplatmapID(layer.layerID)); Color color = splat.GetPixel(texelIndex.x, texelIndex.y); int channel = layer.layerID % 4; float value = SampleChannel(color, channel); if (value > 0) { value = Mathf.Clamp01(value - layer.threshold); } value *= 100f; spawnChance += value; } InitializeSeed(x * y + item.seed); if ((Random.value <= spawnChance) == false) { instanceCount = 0; } //if (instanceCount == 1) DebugPoints.Add(wPos, true, sampler.slope); item.instanceCount += instanceCount; //Passed all conditions, spawn one instance here map[x, y] = instanceCount; } } terrain.terrainData.SetDetailLayer(0, 0, item.index, map); #if UNITY_EDITOR UnityEditor.EditorUtility.ClearProgressBar(); #endif } onGrassRespawn?.Invoke(item); }
public void SpawnTree(TreeType item) { if (item.collisionCheck) { RebuildCollisionCacheIfNeeded(); } item.instanceCount = 0; RefreshTreePrefabs(); float height, worldHeight, normalizedHeight; foreach (Terrain terrain in terrains) { List <TreeInstance> treeInstanceCollection = new List <TreeInstance>(terrain.terrainData.treeInstances); //Clear all existing instances first for (int i = 0; i < treeInstanceCollection.Count; i++) { foreach (TreePrefab prefab in item.prefabs) { treeInstanceCollection.RemoveAll(x => x.prototypeIndex == prefab.index); } } InitializeSeed(item.seed); item.spawnPoints = PoissonDisc.GetSpawnpoints(terrain, item.distance, item.seed + seed); foreach (Vector3 pos in item.spawnPoints) { //InitializeSeed(item.seed + index); //Relative position as 0-1 value Vector2 normalizedPos = terrain.GetNormalizedPosition(pos); if (item.collisionCheck) { //Check for collision if (InsideOccupiedCell(terrain, pos, normalizedPos)) { continue; } } InitializeSeed(item.seed + (int)pos.x + (int)pos.z); //Skip if failing global probability check if (((Random.value * 100f) <= item.probability) == false) { continue; } TreePrefab prefab = SpawnerBase.GetProbableTree(item); //Failed probability checks entirely if (prefab == null) { continue; } terrain.SampleHeight(normalizedPos, out height, out worldHeight, out normalizedHeight); if (item.rejectUnderwater && worldHeight < waterHeight) { continue; } //Check height if (worldHeight < item.heightRange.x || worldHeight > item.heightRange.y) { continue; } if (item.slopeRange.x > 0 || item.slopeRange.y < 90f) { float slope = terrain.GetSlope(normalizedPos, false); //Reject if slope check fails if (!(slope >= (item.slopeRange.x + 0.001f) && slope <= (item.slopeRange.y))) { continue; } } if (item.curvatureRange.x > 0 || item.curvatureRange.y < 1f) { float curvature = terrain.SampleConvexity(normalizedPos); //0=concave, 0.5=flat, 1=convex curvature = TerrainSampler.ConvexityToCurvature(curvature); if (curvature < item.curvatureRange.x || curvature > item.curvatureRange.y) { continue; } } //Reject based on layer masks Vector2Int texelIndex = terrain.SplatmapTexelIndex(normalizedPos); float spawnChance = 0f; if (item.layerMasks.Count == 0) { spawnChance = 100f; } foreach (TerrainLayerMask layer in item.layerMasks) { Texture2D splat = terrain.terrainData.GetAlphamapTexture(GetSplatmapID(layer.layerID)); Color color = splat.GetPixel(texelIndex.x, texelIndex.y); int channel = layer.layerID % 4; float value = SampleChannel(color, channel); if (value > 0) { value = Mathf.Clamp01(value - layer.threshold); } value *= 100f; spawnChance += value; } InitializeSeed((int)pos.x * (int)pos.z); if ((Random.value <= spawnChance) == false) { continue; } //Passed all conditions, add instance TreeInstance treeInstance = new TreeInstance(); treeInstance.prototypeIndex = prefab.index; treeInstance.position = new Vector3(normalizedPos.x, normalizedHeight, normalizedPos.y); treeInstance.rotation = Random.Range(0f, 359f) * Mathf.Deg2Rad; float scale = Random.Range(item.scaleRange.x, item.scaleRange.y); treeInstance.heightScale = scale; treeInstance.widthScale = scale; treeInstance.color = Color.white; treeInstance.lightmapColor = Color.white; treeInstanceCollection.Add(treeInstance); item.instanceCount++; } item.spawnPoints.Clear(); #if UNITY_2019_1_OR_NEWER terrain.terrainData.SetTreeInstances(treeInstanceCollection.ToArray(), false); #else terrain.terrainData.treeInstances = treeInstanceCollection.ToArray(); #endif } for (int i = 0; i < item.prefabs.Count; i++) { onTreeRespawn?.Invoke(item.prefabs[i]); } }