/// <summary>
    /// 左键抬起
    /// </summary>
    public override void OnLeftButtonUp()
    {
        hitInfo = Utility.SendRay(LayerMask.GetMask("Terrain"));
        if (hitInfo.Equals(default(RaycastHit)))
        {
            return;
        }

        Terrain terrain = hitInfo.collider.GetComponent <Terrain>();

        switch (Panel.modifierType)
        {
        case TerrainModifierPanel.ModifierType.Up:
        case TerrainModifierPanel.ModifierType.Down:
        case TerrainModifierPanel.ModifierType.Smooth:
            TerrainUtility.Refresh();
            TerrainUtility.AddOldData();
            break;

        case TerrainModifierPanel.ModifierType.AddTree:
            if (isAdd)
            {
                TerrainUtility.CreatTree(terrain, hitInfo.point, (int)(Panel.opticalMix.Value), (int)Panel.rangeMix.Value, Panel.PrototypeIndex);
            }
            else
            {
                TerrainUtility.RemoveTree(terrain, hitInfo.point, (int)Panel.rangeMix.Value, Panel.PrototypeIndex);
            }
            break;

        default:
            break;
        }
    }
Exemple #2
0
    void Start()
    {
        UIManager.Instance.PushPanel(UIPanelType.TerrainModifier);

        MouseEvent.Instance.ChangeState(TerrainModule.Instance.mouseTerrainModifierState);
        TerrainUtility.ConfigActiveTerrains();
    }
Exemple #3
0
 /// <summary>
 /// 初始化树木原型组
 /// </summary>
 public static void InitTreePrototype(GameObject[] treeObjs)
 {
     TreePrototype[] trees    = TerrainUtility.CreatTreePrototype(treeObjs);
     Terrain[]       terrains = Terrain.activeTerrains;
     for (int i = 0, length = terrains.Length; i < length; i++)
     {
         terrains[i].terrainData.treePrototypes = trees;
     }
 }
Exemple #4
0
 /// <summary>
 /// 读取和地图有关的全部数据
 /// </summary>
 /// <param name="folder"></param>
 public void ReadTerrainInfo(string folder)
 {
     RunAfterCreateTerrain += () =>
     {
         TerrainUtility.ConfigActiveTerrains();
         TerrainUtility.ConfigTerrainData();
         RunAfterCreateTerrain = null;
     };
     ReadTerrainData(folder);
 }
Exemple #5
0
    /// <summary>
    /// 初始化细节原型组
    /// </summary>
    public static void InitDetailPrototype(Texture2D[] textures)
    {
        DetailPrototype[] details = TerrainUtility.CreateDetailPrototype(textures);

        Terrain[] terrains = Terrain.activeTerrains;
        for (int i = 0, length = terrains.Length; i < length; i++)
        {
            terrains[i].terrainData.detailPrototypes = details;
            terrains[i].detailObjectDistance         = 250; // 设置草的消失距离
        }
    }
Exemple #6
0
        public override void OnPlatformEnter(TerrainCastHit hit)
        {
            var hitSide = TerrainUtility.NormalToControllerSide(hit.NormalAngle * Mathf.Rad2Deg - transform.eulerAngles.z);

            if ((BouncySides & hitSide) == 0)
            {
                return;
            }

            if (LockControl)
            {
                hit.Controller.GetComponent <MoveManager>().Get <GroundControl>().Lock(LockDuration);
            }
            if (!KeepOnGround)
            {
                hit.Controller.Detach();

                var moveManager = hit.Controller.GetComponent <MoveManager>();
                if (moveManager != null)
                {
                    moveManager.End <Roll>();
                    moveManager.Perform <AirControl>(true);
                }

                hit.Controller.IgnoreThisCollision();
            }

            if (AccurateBounce)
            {
                hit.Controller.Velocity = new Vector2(hit.Controller.Velocity.x * Mathf.Abs(Mathf.Sin(hit.NormalAngle)),
                                                      hit.Controller.Velocity.y * Mathf.Abs(Mathf.Cos(hit.NormalAngle)));
                hit.Controller.Velocity += DMath.AngleToVector(hit.NormalAngle) * Power;
            }
            else
            {
                hit.Controller.Velocity = DMath.AngleToVector(hit.NormalAngle) * Power;
            }

            if (hit.Controller.Animator != null)
            {
                var logWarnings = hit.Controller.Animator.logWarnings;
                hit.Controller.Animator.logWarnings = false;

                if (!string.IsNullOrEmpty(HitTrigger))
                {
                    hit.Controller.Animator.SetTrigger(HitTrigger);
                }

                hit.Controller.Animator.logWarnings = logWarnings;
            }

            TriggerObject(hit.Controller);
        }
 public void Perlin()
 {
     float[,] heightMap = resetTerrain ? GetNewMap() : GetHeightMap();
     for (int y = 0; y < terrainData.heightmapHeight; y++)
     {
         for (int x = 0; x < terrainData.heightmapWidth; x++)
         {
             heightMap[x, y] += TerrainUtility.FractalBrownianMotion((x + perlinOffsetX) * perlinXScale, (y + perlinOffsetY) * perlinYScale, perlinOctaves, perlinPersistence) * perlinHeightScale;
         }
     }
     terrainData.SetHeights(0, 0, heightMap);
 }
Exemple #8
0
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.J))
        {
            TDSaveMgr.Instance.ReadTerrainInfo("2019-03-25 14-33-15");
        }

        if (Input.GetKeyDown(KeyCode.K))
        {
            TerrainUtility.ConfigActiveTerrains();
            TerrainUtility.ConfigTerrainData();
        }
    }
    /// <summary>
    /// 左键按住
    /// </summary>
    public override void OnLeftButtonHold()
    {
        hitInfo = Utility.SendRay(LayerMask.GetMask("Terrain"));
        if (hitInfo.Equals(default(RaycastHit)))
        {
            return;
        }

        Terrain terrain = hitInfo.collider.GetComponent <Terrain>();

        switch (Panel.modifierType)
        {
        case TerrainModifierPanel.ModifierType.Up:
            TerrainUtility.ChangeHeight(hitInfo.point, (int)Panel.rangeMix.Value, Panel.opticalMix.Value);
            break;

        case TerrainModifierPanel.ModifierType.Down:
            TerrainUtility.ChangeHeight(hitInfo.point, (int)Panel.rangeMix.Value, Panel.opticalMix.Value, false);
            break;

        case TerrainModifierPanel.ModifierType.Smooth:
            TerrainUtility.Smooth(hitInfo.point, (int)Panel.rangeMix.Value, Panel.opticalMix.Value);
            break;

        case TerrainModifierPanel.ModifierType.AddDetial:
            if (MouseEvent.Instance.MouseMove)
            {
                if (isAdd)
                {
                    TerrainUtility.AddDetial(terrain, hitInfo.point, Panel.rangeMix.Value, (int)(Panel.opticalMix.Value), Panel.PrototypeIndex);
                }
                else
                {
                    TerrainUtility.RemoveDetial(terrain, hitInfo.point, Panel.rangeMix.Value, Panel.PrototypeIndex);
                }
            }
            break;

        case TerrainModifierPanel.ModifierType.AttachTexture:
            if (MouseEvent.Instance.MouseMove)
            {
                TerrainUtility.SetTexture(hitInfo.point, 1, 1);
            }
            break;

        default:
            break;
        }
    }
Exemple #10
0
    // Use this for initialization
    void Start()
    {
        GetComponent <MeshFilter>().mesh = MeshUtility.CreateCube(1f, Vector3.zero);
        BlockManager.Init(chunkPrefab);
        TextureManager.Init(texMap, tileSize);

        for (int x = 0; x < 100; x++)
        {
            for (int y = 0; y < 100; y++)
            {
                IBlock block = new GrassBlock(Vector3.left * x + (Vector3.up * TerrainUtility.GetBlockHeight(x, y)) + Vector3.forward * y);
                for (int i = 0; i < block.Position.y; i++)
                {
                    IBlock block1 = new DirtBlock(new Vector3(block.Position.x, i, block.Position.z));
                    BlockManager.AddBlock(block1);
                }
                BlockManager.AddBlock(block);
            }
        }


        //Block block = new Block(Vector3.one);
        //BlockManager.AddBlock(block);
    }
Exemple #11
0
 /// <summary>
 /// 初始化原型模板
 /// </summary>
 private void InitPrototype()
 {
     details = TerrainUtility.CreateDetailPrototype();
     trees   = TerrainUtility.CreatTreePrototype();
     splats  = TerrainUtility.CreateSplatPrototype();
 }
Exemple #12
0
    /// <summary>
    /// 创建单块地图
    /// </summary>
    /// <param name="tdData"></param>
    private void CreateTerrain(TDData tdData)
    {
        // 设置TerrainData的基础参数
        TerrainData terrainData = new TerrainData();

        terrainData.heightmapResolution = head.ResolutionSize + 1;
        terrainData.SetDetailResolution(head.ResolutionSize, 8);
        terrainData.alphamapResolution = head.ResolutionSize * 2;
        terrainData.baseMapResolution  = head.ResolutionSize;
        terrainData.size = head.terrainSize;

        terrainData.detailPrototypes = details;
        terrainData.treePrototypes   = trees;
        terrainData.terrainLayers    = splats;
        terrainData.RefreshPrototypes();

        // 高度,贴图,细节设置
        if (tdData.heightMap != null)
        {
            terrainData.SetHeights(0, 0, tdData.heightMap);
        }

        if (tdData.detailMap != null)
        {
            for (int i = 0; i < tdData.detailMap.Length; i++)
            {
                terrainData.SetDetailLayer(0, 0, i, tdData.detailMap[i]);
            }
        }

        if (tdData.alphaMap != null)
        {
            terrainData.SetAlphamaps(0, 0, tdData.alphaMap);
        }

        // 在场景中创建Terrain实体并设置实体的参数
        GameObject newTerrainGameObject = Terrain.CreateTerrainGameObject(terrainData);

        newTerrainGameObject.name               = terrainData.name;
        newTerrainGameObject.isStatic           = false;
        newTerrainGameObject.transform.position = tdData.terrainPos;
        newTerrainGameObject.layer              = LayerMask.NameToLayer("Terrain");

        // 设置Terrain类的参数
        Terrain terrain = newTerrainGameObject.GetComponent <Terrain>();

        terrain.heightmapPixelError = head.heightmapPixelError;
        terrain.basemapDistance     = head.basemapDistance;
        terrain.drawHeightmap       = head.drawHeightmap;
        terrain.name = tdData.name;

        if (tdData.treePoses != null)
        {
            // 为地形添加树木实体
            for (int index = 0; index < tdData.treePoses.Length; index++)
            {
                for (int i = 0; i < tdData.treePoses[index].Length; i++)
                {
                    TreeInstance instance = TerrainUtility.GetTreeInstance(index);
                    instance.position = tdData.treePoses[index][i];
                    terrain.AddTreeInstance(instance);
                }
            }
        }

        newTerrainGameObject.transform.SetParent(terrainParent);
    }
        private void FillHeightmapUsingNeighbors(Terrain terrain)
        {
            TerrainUtility.AutoConnect();

            Terrain[] nbrTerrains = new Terrain[4] {
                terrain.topNeighbor, terrain.bottomNeighbor, terrain.leftNeighbor, terrain.rightNeighbor
            };

            // Position of the terrain must be lowest
            Vector3 position = terrain.transform.position;

            foreach (Terrain nbrTerrain in nbrTerrains)
            {
                if (nbrTerrain)
                {
                    position.y = Mathf.Min(position.y, nbrTerrain.transform.position.y);
                }
            }
            terrain.transform.position = position;

            TerrainNeighborInfo top    = new TerrainNeighborInfo();
            TerrainNeighborInfo bottom = new TerrainNeighborInfo();
            TerrainNeighborInfo left   = new TerrainNeighborInfo();
            TerrainNeighborInfo right  = new TerrainNeighborInfo();

            TerrainNeighborInfo[] nbrInfos = new TerrainNeighborInfo[4] {
                top, bottom, left, right
            };

            const float kNeightNormFactor = 2.0f;

            for (int i = 0; i < 4; ++i)
            {
                TerrainNeighborInfo nbrInfo    = nbrInfos[i];
                Terrain             nbrTerrain = nbrTerrains[i];
                if (nbrTerrain)
                {
                    nbrInfo.terrainData = nbrTerrain.terrainData;
                    if (nbrInfo.terrainData)
                    {
                        nbrInfo.texture = nbrInfo.terrainData.heightmapTexture;
                        nbrInfo.offset  = (nbrTerrain.transform.position.y - terrain.transform.position.y) / (nbrInfo.terrainData.size.y * kNeightNormFactor);
                    }
                }
            }

            RenderTexture heightmap           = terrain.terrainData.heightmapTexture;
            Vector4       texCoordOffsetScale = new Vector4(-0.5f / heightmap.width, -0.5f / heightmap.height,
                                                            (float)heightmap.width / (heightmap.width - 1), (float)heightmap.height / (heightmap.height - 1));

            Material crossBlendMat    = GetOrCreateCrossBlendMaterial();
            Vector4  slopeEnableFlags = new Vector4(bottom.texture ? 0.0f : 1.0f, top.texture ? 0.0f : 1.0f, left.texture ? 0.0f : 1.0f, right.texture ? 0.0f : 1.0f);

            crossBlendMat.SetVector("_SlopeEnableFlags", slopeEnableFlags);
            crossBlendMat.SetVector("_TexCoordOffsetScale", texCoordOffsetScale);
            crossBlendMat.SetVector("_Offsets", new Vector4(bottom.offset, top.offset, left.offset, right.offset));
            crossBlendMat.SetFloat("_AddressMode", (float)m_FillAddressMode);
            crossBlendMat.SetTexture("_TopTex", top.texture);
            crossBlendMat.SetTexture("_BottomTex", bottom.texture);
            crossBlendMat.SetTexture("_LeftTex", left.texture);
            crossBlendMat.SetTexture("_RightTex", right.texture);

            Graphics.Blit(null, heightmap, crossBlendMat);

            terrain.terrainData.DirtyHeightmapRegion(new RectInt(0, 0, heightmap.width, heightmap.height), TerrainHeightmapSyncControl.HeightAndLod);
        }
    private void OnGUI()
    {
        GUILayout.Label("Settings", EditorStyles.boldLabel);
        fileName = EditorGUILayout.TextField("Texture Name", fileName);

        int wSize = (int)(EditorGUIUtility.currentViewWidth - 100);

        perlinXScale       = EditorGUILayout.Slider("X Scale", perlinXScale, 0, 0.1f);
        perlinYScale       = EditorGUILayout.Slider("Y Scale", perlinYScale, 0, 0.1f);
        perlinOctaves      = EditorGUILayout.IntSlider("Octaves", perlinOctaves, 1, 10);
        perlinePersistance = EditorGUILayout.Slider("Persistance", perlinePersistance, 1, 10);
        perlineHeightScale = EditorGUILayout.Slider("Height Scale", perlineHeightScale, 0, 1);
        perlinOffsetX      = EditorGUILayout.IntSlider("Offset X", perlinOffsetX, 0, 10000);
        perlinOffsetY      = EditorGUILayout.IntSlider("Offset Y", perlinOffsetY, 0, 10000);
        brightness         = EditorGUILayout.Slider("Brightness", brightness, 0, 2);
        contrast           = EditorGUILayout.Slider("Contrast", contrast, 0, 2);
        alphaToggle        = EditorGUILayout.Toggle("Alpha", alphaToggle);
        mapToggle          = EditorGUILayout.Toggle("Map", mapToggle);
        seamlessToggle     = EditorGUILayout.Toggle("Seamless", seamlessToggle);

        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();

        float minColor = 1;
        float maxColor = 0;

        if (GUILayout.Button("Generate", GUILayout.Width(wSize)))
        {
            int   w = 513;
            int   h = 513;
            float pValue;
            Color pixCol = Color.white;

            for (int y = 0; y < h; ++y)
            {
                for (int x = 0; x < w; ++x)
                {
                    if (seamlessToggle)
                    {
                        float u = x / w;
                        float v = y / h;

                        float noise00 = TerrainUtility.FractalBrownianMotion((x + perlinOffsetX) * perlinXScale,
                                                                             (y + perlinOffsetY) * perlinYScale,
                                                                             perlinOctaves,
                                                                             perlinePersistance) * perlineHeightScale;
                        float noise01 = TerrainUtility.FractalBrownianMotion((x + perlinOffsetX) * perlinXScale,
                                                                             (y + perlinOffsetY + h) * perlinYScale,
                                                                             perlinOctaves,
                                                                             perlinePersistance) * perlineHeightScale;
                        float noise10 = TerrainUtility.FractalBrownianMotion((x + perlinOffsetX + w) * perlinXScale,
                                                                             (y + perlinOffsetY) * perlinYScale,
                                                                             perlinOctaves,
                                                                             perlinePersistance) * perlineHeightScale;
                        float noise11 = TerrainUtility.FractalBrownianMotion((x + perlinOffsetX + w) * perlinXScale,
                                                                             (y + perlinOffsetY + h) * perlinYScale,
                                                                             perlinOctaves,
                                                                             perlinePersistance) * perlineHeightScale;
                        float noiseTotal = u * v * noise00 +
                                           u * (1 - v) * noise01 +
                                           (1 - u) * v * noise10 +
                                           (1 - u) * (1 - v) * noise11;
                        float value = (int)(256 * noiseTotal) + 50;
                        float r     = Mathf.Clamp((int)noise00, 0, 255);
                        float g     = Mathf.Clamp(value, 0, 255);
                        float b     = Mathf.Clamp(value + 50, 0, 255);
                        float a     = Mathf.Clamp(value + 100, 0, 255);

                        pValue = (r + g + b) / (3 * 255.0f);
                    }
                    else
                    {
                        pValue = TerrainUtility.FractalBrownianMotion((x + perlinOffsetX) * perlinXScale,
                                                                      (y + perlinOffsetY) * perlinYScale,
                                                                      perlinOctaves,
                                                                      perlinePersistance) * perlineHeightScale;
                    }
                    float colValue = contrast * (pValue - 0.5f) + 0.5f * brightness;
                    if (minColor > colValue)
                    {
                        minColor = colValue;
                    }
                    if (maxColor < colValue)
                    {
                        maxColor = colValue;
                    }
                    pixCol = new Color(colValue, colValue, colValue, alphaToggle ? colValue : 1);
                    pTexture.SetPixel(x, y, pixCol);
                }
            }

            if (mapToggle)
            {
                for (int y = 0; y < h; ++y)
                {
                    for (int x = 0; x < w; ++x)
                    {
                        pixCol = pTexture.GetPixel(x, y);
                        float colValue = pixCol.r;
                        colValue = TerrainUtility.Map(colValue, minColor, maxColor, 0, 1);
                        pixCol.r = colValue;
                        pixCol.g = colValue;
                        pixCol.b = colValue;
                        pTexture.SetPixel(x, y, pixCol);
                    }
                }
            }

            pTexture.Apply(false, false);
        }

        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();
        GUILayout.Label(pTexture, GUILayout.Width(wSize), GUILayout.Height(wSize));
        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();

        if (GUILayout.Button("Save", GUILayout.Width(wSize)))
        {
            CreatePNGFromTex();
            MakeTexReadable();
        }

        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();
    }
Exemple #15
0
    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        TerrainUtility   terrainUtility   = (TerrainUtility)target;
        TerrainGenerator terrainGenerator = terrainUtility.GetComponent <TerrainGenerator>();

        PlantGenerator plantGenerator = terrainUtility.GetComponent <PlantGenerator>();

        //Terrain
        GUILayout.Label("Terrain generation");


        terrainGenerator.terrainWidth = EditorGUILayout.FloatField(new GUIContent("Terrain size X"), terrainGenerator.terrainWidth);

        plantGenerator.xMinVal = -(terrainGenerator.terrainWidth / 2);
        plantGenerator.xMaxVal = terrainGenerator.terrainWidth / 2;
        plantGenerator.zMinVal = -(terrainGenerator.terrainLength / 2);
        plantGenerator.zMaxVal = terrainGenerator.terrainLength / 2;


        terrainGenerator.terrainLength = EditorGUILayout.FloatField(new GUIContent("Terrain size Z"), terrainGenerator.terrainLength);

        EditorGUILayout.PropertyField(_chunksPerFrame);

        GUILayout.BeginHorizontal();
        if (GUILayout.Button("Create terrain"))
        {
            TerrainGenerator.instance = terrainGenerator;
            terrainGenerator.buildTerrain(_chunksPerFrame.intValue);
        }

        if (GUILayout.Button("Delete terrain"))
        {
            TerrainGenerator.instance = terrainGenerator;
            terrainGenerator.deleteTerrain();
            EditorSceneManager.MarkAllScenesDirty();
            System.GC.Collect();
            Resources.UnloadUnusedAssets();
        }

        GUILayout.EndHorizontal();


        //Plants
        GUILayout.Label("Plant generation");

        EditorGUILayout.PropertyField(_plantsPerUnit);

        plantsToSpawn = (int)Math.Round(_plantsPerUnit.floatValue * terrainGenerator.terrainLength * terrainGenerator.terrainWidth);
        GUILayout.Label($"This will spawn {plantsToSpawn} plants");
        plantGenerator.amountOfPlantsToSpawn = plantsToSpawn;

        EditorGUILayout.PropertyField(_plantsPerFrame);


        GUILayout.BeginHorizontal();

        if (GUILayout.Button("Generate plants"))
        {
            //Old logic ->
            //plantGenerator.StartCoroutine(plantGenerator.spawnPlantsInXZRange());

            //New logic ->
            plantGenerator.StartCoroutine(plantGenerator.SpawnPlantsOnChunks(terrainGenerator, _plantsPerFrame.intValue));
        }

        if (GUILayout.Button("Delete plants"))
        {
            plantGenerator.deleteAllPlants(terrainGenerator);
            EditorSceneManager.MarkAllScenesDirty();
            System.GC.Collect();
            Resources.UnloadUnusedAssets();
        }

        GUILayout.EndHorizontal();

        serializedObject.ApplyModifiedProperties();
    }
    public void PlantDetails()
    {
        // If both the prototype and the texture are missing
        details.RemoveAll(detail => detail.prototype == null && detail.prototypeTexture == null);

        // Create new detail prototype and set its properties from detail class
        DetailPrototype[] newDetailPrototypes = new DetailPrototype[details.Count];

        float[,] heightMap = terrainData.GetHeights(0, 0, terrainData.heightmapWidth,
                                                    terrainData.heightmapHeight);

        for (int i = 0; i < details.Count; i++)
        {
            Detail detail = details[i];

            newDetailPrototypes[i] = new DetailPrototype
            {
                prototype        = detail.prototype,
                prototypeTexture = detail.prototypeTexture,
                healthyColor     = detail.healthyColor,
                dryColor         = detail.dryColor,
                bendFactor       = detail.bendFactor,
                noiseSpread      = detail.noiseSpread,
                maxHeight        = detail.heightRange.y,
                minHeight        = detail.heightRange.x,
                maxWidth         = detail.widthRange.y,
                minWidth         = detail.widthRange.x
            };

            DetailPrototype detailPrototype = newDetailPrototypes[i];

            // If prototype mesh is given, the mesh is used.
            // If the texture is given, then the texture is used, not both.
            if (detailPrototype.prototype != null)
            {
                detailPrototype.usePrototypeMesh = true;
                detailPrototype.renderMode       = DetailRenderMode.VertexLit;
                // Set prototype texture to null for better understanding in editor.
                detailPrototype.prototypeTexture = null;
            }
            else if (detailPrototype.prototypeTexture != null)
            {
                detailPrototype.usePrototypeMesh = false;
                detailPrototype.renderMode       = DetailRenderMode.GrassBillboard;
            }
        }
        // Apply detail prototypes to terrain
        terrainData.detailPrototypes = newDetailPrototypes;

        for (int i = 0; i < terrainData.detailPrototypes.Length; i++)
        {
            Detail detail = details[i];
            int[,] detailMap = new int[terrainData.detailWidth, terrainData.detailHeight];

            EditorUtility.DisplayProgressBar("Planting Details", detail.prototype != null ? detail.prototype.name : detail.prototypeTexture.name, (float)i / terrainData.detailPrototypes.Length);

            // Go through all terrain and plant details
            for (int y = 0; y < terrainData.detailHeight; y += detailSpacing)
            {
                for (int x = 0; x < terrainData.detailWidth; x += detailSpacing)
                {
                    // Less density means more details will be skipped at this (x,z) position
                    if (Random.Range(0f, 1f) > detail.density)
                    {
                        continue;
                    }

                    // Calculate x and y of the heightmap from detail width/height
                    int xHM = (int)(x / (float)terrainData.detailWidth * terrainData.heightmapWidth);
                    int yHM = (int)(y / (float)terrainData.detailHeight * terrainData.heightmapHeight);

                    float perlin    = Mathf.PerlinNoise(x * detail.heightNoiseMultiplier, y * detail.heightNoiseMultiplier);
                    float thisNoise = TerrainUtility.Map(perlin, 0, 1, 0.5f, 1);

                    float thisHeightStart = thisNoise * detail.minHeight;
                    float nextHeightStart = thisNoise * detail.maxHeight;
                    // Detailmap is rotated 90 degrees, so when converting to heightmap swap x and z
                    float thisHeight = heightMap[yHM, xHM];

                    float steepness = terrainData.GetSteepness(
                        xHM / terrainData.size.x,
                        yHM / terrainData.size.z);

                    // If the height is in between boundaries
                    if (thisHeight >= thisHeightStart && thisHeight <= nextHeightStart &&
                        // If steepness is between boundaries
                        steepness >= detail.minSlope && steepness <= detail.maxSlope)
                    {
                        // x and y is backwards for the detail map, detail map is rotated 90 degrees
                        // Apply detail map on this point
                        //detailMap[y, x] = 1;
                        detailMap[y, x] = 1;
                    }
                }
            }

            // Apply this detail on terrain with detailmap
            terrainData.SetDetailLayer(0, 0, i, detailMap);
        }

        EditorUtility.ClearProgressBar();
    }