Beispiel #1
        public void OnSceneGUI()
            script = (ErosionBrush) target;
            preset = script.preset;

            if (!script.paint || (Event.current.mousePosition-oldMousePos).sqrMagnitude<1f) return;

            TerrainData data = script.terrain.terrainData;

            //reading keyboard
            if (Event.current.type == EventType.keyDown)
            //selecting presets with keycode
            if (script.guiSelectPresetsUsingNumkeys)
                int key = -1;
                switch (Event.current.keyCode)
                    //case KeyCode.Alpha1: key = 0; break;
                    //case KeyCode.Alpha2: key = 2; break;
                    case KeyCode.Alpha3: key = 0; break;
                    case KeyCode.Alpha4: key = 1; break;
                    case KeyCode.Alpha5: key = 2; break;
                    case KeyCode.Alpha6: key = 3; break;
                    case KeyCode.Alpha7: key = 4; break;
                    case KeyCode.Alpha8: key = 5; break;
                    case KeyCode.Alpha9: key = 6; break;
                if (key >= 0 && key < script.presets.Length) { LoadPreset(key); script.guiSelectedPreset=key; }

            //extending brush size with keykode
            if (Event.current.keyCode == KeyCode.LeftBracket || Event.current.keyCode == KeyCode.RightBracket)
                float step = (script.preset.brushSize / 10);
                step = Mathf.RoundToInt(step);
                step = Mathf.Max(1,step);

                if (Event.current.keyCode == KeyCode.LeftBracket) script.preset.brushSize -= step;
                else script.preset.brushSize += step;

                script.preset.brushSize = Mathf.Min(script.guiMaxBrushSize, script.preset.brushSize);

            //evaluation limitation
            //if (data.heightmapResolution-1 > 512 ||
            //	data.alphamapResolution > 512) return;

            //perform undo. Using Voxeland's undo system
            if (Event.current.commandName == "UndoRedoPerformed" && script.undoList.Count!=0)
            script.allowUndo = !script.allowUndo;
            if (!script.allowUndo) return;

            int lastNum = script.undoList.Count-1;
            for (int i=script.undoList[lastNum].Count-1; i>=0; i--)

            //disabling selection

            //finding aiming ray
            Vector2 mousePos = Event.current.mousePosition;
            mousePos.y = Screen.height - mousePos.y - 40;
            Camera cam =;
            if (cam==null) return;
            Ray aimRay = cam.ScreenPointToRay(mousePos);

            //aiming terrain
            Collider terrainCollider = script.terrain.GetComponent<Collider>();
            if (terrainCollider==null) { Debug.LogWarning("ErosionBrush: cannot aim terrain because it does not have a collider"); return; }
            RaycastHit hit;
            if (!terrainCollider.Raycast(aimRay, out hit, Mathf.Infinity)) return;
            Vector3 brushPos = hit.point;

            //drawing brush
            DrawBrush(brushPos, preset.brushSize, script.terrain, color:script.guiBrushColor, thickness:script.guiBrushThickness, numCorners:script.guiBrushNumCorners);
            //if (Event.current.type == EventType.MouseDrag) Handles.DrawAAPolyLine(script.guiBrushThickness, new Vector3[] { oldBrushPos, brushPos } );
            DrawBrush(brushPos, preset.brushSize*preset.brushFallof, script.terrain, color:script.guiBrushColor/2, thickness:script.guiBrushThickness, numCorners:script.guiBrushNumCorners);

            //moving brush (needed an object to perform move)
            if (script.moveTfm==null) { script.moveTfm = new GameObject().transform; script.moveTfm.hideFlags = HideFlags.HideInHierarchy; }
            script.moveTfm.position = hit.point;

            //focusing on brush
            if (script.focusOnBrush && Event.current.keyCode == KeyCode.G && Event.current.type == EventType.KeyDown)

            //returning if no key was pressed or distance from old pos is less then spacing
            if (Event.current.type == EventType.MouseUp && Event.current.button == 0) oldBrushPos = new Vector3(-65000,0,-65000);
            if (!(Event.current.type == EventType.MouseDown || Event.current.type == EventType.MouseDrag) || Event.current.button != 0) return;
            if (Event.current.type == EventType.MouseDrag && preset.brushSpacing>0.001f && (new Vector3(brushPos.x,0,brushPos.z)-oldBrushPos).magnitude < preset.brushSpacing*preset.brushSize) return;
            if (Event.current.alt) return;
            oldBrushPos = new Vector3(brushPos.x,0,brushPos.z);

            //finding heightmap-spaced and splat-spaced coordinates
            Vector3 terrainSpaceCoords = brushPos - script.terrain.transform.position; //terrain.transform.InverseTransformPoint(worldSpaceCoords);

            //applying brush
            //script.ApplyBrush(heightCoords, splatCoords, useFallof:true);

            script.ApplyBrush(new Rect(
            useFallof:true, newUndo:Event.current.type==EventType.MouseDown);
            if (script.recordUndo)
            UnityEditor.Undo.RecordObject(script,"Erosion Brush Stroke");
            script.undo = !script.undo; UnityEditor.EditorUtility.SetDirty(this); //setting object change

            //refreshin terrain for Unity5
            if (script.unity5positioning && (Event.current.type == EventType.mouseDown || unity5terrainRefreshCounter==10))
            //data.size += Vector3.up*0.01f;
            //data.size -= Vector3.up*0.01f;
Beispiel #2
        public void OnEnable()
            if (script==null) script = (ErosionBrush) target;
            if (script==null || !script.enabled) return;

            UnityEditor.EditorApplication.update -= EditorUpdate;
            UnityEditor.EditorApplication.update += EditorUpdate;
Beispiel #3
        public override void OnInspectorGUI()
            script = (ErosionBrush) target;
            preset = script.preset;
            if (script.terrain==null || script.terrain.terrainData==null) return;

            margin = 0;
            rightMargin = 0;
            fieldSize = 0.6f;

            //evaluation version
            //bool paintDisabled = false;
            //if (script.terrain.terrainData.heightmapResolution-1 > 512 ||
            //	script.terrain.terrainData.alphamapResolution > 512)
            //	Par(62);
            //	EditorGUI.HelpBox(Inset(), "Evaluation version.\nTerrain maximum resolution is limited to 512 pixels.\n", MessageType.Warning);
            //	Par(20); cursor.y -= 24;
            //	Inset(40);
            //	if (GUI.Button(Inset(width-50), "Switch resolution to 512"))
            //		if (EditorUtility.DisplayDialog("Warning", "Changing resolution will remove all terrain data. " +
            //			"This operation is not undoable. Please make a backup copy of your terrain data (not scene, but terrain .asset file).", "Switch", "Cancel"))
            //				{ script.terrain.terrainData.alphamapResolution = 512; script.terrain.terrainData.heightmapResolution = 513; }
            //	disabled = true;

            //drawing toolbar
            if (script.guiHydraulicIcon==null) script.guiHydraulicIcon = Resources.Load("ErosionBrushHydraulic") as Texture2D;
            if (script.guiWindIcon==null) script.guiWindIcon = Resources.Load("ErosionBrushNoise") as Texture2D;


            //paint button
            if (disabled) script.paint = false;
            Button(ref script.paint, "Paint", toggle:true, width:0.35f, tooltip:"A checkbutton that turns erosion or noise painting on/off. When painting is on it is terrain editing with standard Unity tools is not possible, so terrain component is disabled when “Paint” is checked. To enable terrain editing turn off paint mode.");
            disabled = false;

            //mode selector
            if (GUI.Toolbar(Inset(0.6f), preset.isErosion ? 0 : 1, new GUIContent[] {
            new GUIContent(" Erosion", script.guiHydraulicIcon, ""),
            new GUIContent(" Noise", script.guiWindIcon, "") }) == 0)
                preset.isErosion = true;
            else preset.isNoise = true;

            margin += 7;

            #region Preset
            Par(5); Par(); Foldout(ref script.guiShowPreset, "Preset");
            if (script.guiShowPreset)
            //calculating a need to re-create preset array
            bool reCreate = false;
            if (presetContents.Length != script.presets.Length) reCreate = true;
                for (int i=0; i<presetContents.Length; i++)
                    if (presetContents[i].text != script.presets[i].name) { reCreate=true; break; }

            //re-creating presets contents
            if (reCreate)
                presetContents = new GUIContent[script.presets.Length];
                for (int i=0; i<presetContents.Length; i++)
                    string postfix = "";
                    //if (i==0) postfix = " (1)";
                    if (i<8) postfix = " (key " + (i+3) + ")";
                    presetContents[i] = new GUIContent(script.presets[i].name + postfix);

            //selecting preset
            margin += 10;
            int tempSelectedPreset = EditorGUI.Popup(Inset(), script.guiSelectedPreset, presetContents);
            if (presetSelectedFromKeyboard >= 0) { tempSelectedPreset = presetSelectedFromKeyboard; presetSelectedFromKeyboard = -1; }
            if (tempSelectedPreset != script.guiSelectedPreset && tempSelectedPreset < script.presets.Length)
                script.guiSelectedPreset = tempSelectedPreset;

            //save, add, remove
            Par(); disabled = script.presets.Length==0;
            if (Button("Save", tooltip:"Save current preset changes", width:0.3333f) &&
                EditorUtility.DisplayDialog("Overwrite Preset", "Overwrite currently selected preset?", "Save", "Cancel") )

            disabled = false;
            if (Button("Save As...", tooltip:"Save current settings as new preset", width:0.3333f))
                SavePresetWindow window = new SavePresetWindow();
                window.titleContent = new GUIContent("Save Erosion Brush Preset");
                window.position = new Rect(window.position.x, window.position.y, window.windowSize.x, window.windowSize.y);
                window.main = this;

            disabled =script.presets.Length==0;
            if (Button("Remove", tooltip:"Remove currently selected preset", width:0.3333f) &&
                EditorUtility.DisplayDialog("Remove Preset", "Are you sure you wish to remove currently selected preset?", "Remove", "Cancel"))
            disabled = false;

            //DrawLabel( + " " + script.guiSelectedPreset.ToString() + "/" + script.presets.Length.ToString());

            margin -= 10;

            #region brush settings
            Par(5); Par(); Foldout(ref script.guiShowBrush, "Brush Settings");
            if (script.guiShowBrush)
            margin += 10;

            Quick<float>(ref preset.brushSize, "Brush Size", min:1, max:script.guiMaxBrushSize, tooltip:"Size of the brush in Unity units. Bigger brush size gives better terrain quality, but too big values can slow painting. Brush size is displayed as brighter circle in scene view. Brush could be resized with [ and ] keys.",  quadratic:true);
            Quick<float>(ref preset.brushFallof, "Brush Falloff", min:0.01f, max:0.99f, tooltip:"Decrease of brush opacity from center to rim. This parameter is specified in percent of the brush size. It is displayed as dark blue circle in scene view. Brush inside of the circle has the full opacity, and gradually decreases toward the bright circle.");
            Quick<float>(ref preset.brushSpacing, "Brush Spacing", min:0, max:1, tooltip:"When pressing and holding mouse button brush goes on making stamps. Script will not place brush at the same position where old brush was placed, but in a little distance. This parameter specifies how far from old brush stamp will be placed new one (while mouse is still pressed). It  is specified in percent of the brush size.");
            Quick<int>(ref preset.downscale, "Downscale", min:1, max:4, tooltip:"To perform quick operation on heightmaps of large size brush resolution could be scaled down. This will give less detail, but faster stamp.", quadratic:true);
            preset.downscale = Mathf.ClosestPowerOfTwo(preset.downscale);
            //Quick<float>(ref preset.blur, "Blur", min:0, max:1, tooltip:"The amount brush stamp should be blurred before apply. This parameter is very useful together with the donscale: faceted downscaled data could be blurred to give smooth result");

            margin -= 10;

            #region generator settings
            Par(5); Par(); Foldout(ref script.guiShowGenerator, preset.isErosion ? "Erosion Parameters" : "Noise Parameters");
            if (script.guiShowGenerator)
            margin += 10;

            if (preset.isErosion)
                //Noise Brush
                //Par(30); Label("Noise Brush is a free version \nof Erosion Brush plugin.");
                //Par(45); Label("To generate both erosion and \nnoise with the same tool \nconsider using Erosion Brush");
                //Par(); Url("!/content/27389", "Asset Store link");
                //Par(); Url("", "Video");
                //Par(); Url("", "Evaluation Version");

                //Quick<int>(ref erosion_iterations, "Iterations", "Number of algorithm iterations. Higher values will make terrain more eroded. Lowering this value and increasing amounts can speed up terrain generation, but will affect terrain quality.", min:1, max:20);
                Quick<float>(ref preset.erosion_durability, "Terrain Durability", "Baserock resistance to water erosion. Low values erode terrain more. Lowering this parameter is mainly needed to reduce the number of brush passes (iterations), but will reduce terrain quality as well.", max:1);
                Quick<int>(ref preset.erosion_fluidityIterations, "Fluidity Iterations", "This parameter sets how liquid sediment (bedrock raised by torrents) is. Low parameter value will stick sediment on sheer cliffs, high value will allow sediment to drain in hollows. As this parameter sets number of iterations, increasing it to very high values can slow down performance.", min:1, max:10);
                Quick<float>(ref preset.erosion_amount, "Erosion Amount", "Amount of bedrock that is washed away by torrents. Unlike sediment amount, this parameter sets the amount of bedrock that is subtracted from original terrain. Zero value will not erode terrain by water at all.", max:2);
                Quick<float>(ref preset.sediment_amount, "Sediment Amount", "Percent of bedrock raised by torrents that returns back to earth ) Unlike erosion amount, this parameter sets amount of land that is added to terrain. Zero value will not generate any sediment at all.", max:2);
                //Quick<float>(ref preset.wind_amount, "Wind Amount", "Wind sets the amount of bedrock that was carried away by wind, rockfall and other factors non-related with water erosion. Technically it randomly smoothes the convex surfaces of the terrain. Use low values for tropical rocks (as they are more influenced by monsoon, rains and water erosion than by wind), and high values for highland pikes (as all streams freeze at high altitudes).", max:2);
                Quick<float>(ref preset.erosion_smooth, "Smooth", "Applies additional smoothness to terrain in order to fit brush terrain into an existing terrain made with Unity standard tools. Low, but non-zero values can remove small pikes made by wind randomness or left from water erosion. Use low values if your terrain heightmap resolution is low.", max:1);

                int tempSeed = preset.noise_seed;
                Quick<int>(ref tempSeed, "Seed", "Number to initialize random generator. With the same brush size, noise size and seed the noise value will be constant for each heightmap coordinate.", slider:false);
                if (preset.noise_seed != tempSeed) { Noise.seed = tempSeed; preset.noise_seed = tempSeed; UnityEngine.Random.seed = tempSeed; }

                Quick<float>(ref preset.noise_amount, "Amount", tooltip:"Magnitude. How much noise affects the surface", quadratic:true, max:100f);
                Quick<float>(ref preset.noise_size, "Size", tooltip:"Wavelength. Sets the size of the highest iteration of fractal noise. High values will create more irregular noise. This parameter represents the percentage of brush size.", max:1000, quadratic:true);
                Quick<float>(ref preset.noise_detail, "Detail", "Defines the bias of each fractal. Low values sets low influence of low-sized fractals and high influence of high fractals. Low values will give smooth terrain, high values - detailed and even too noisy.", max:1);
                Quick<float>(ref preset.noise_uplift, "Uplift", "When value is 0, noise is subtracted from terrain. When value is 1, noise is added to terrain. Value of 0.5 will mainly remain terrain on the same level, lifting or lowering individual areas.", max:1);
                //Quick<float>(ref preset.noise_ruffle, "Ruffle", "Adds additional shallow (1-unit) noise to the resulting heightmap", max:2);

            margin -= 10;

            #region texture settings
            Par(5); Par(); Foldout(ref script.guiShowTextures, "Textures");
            if (script.guiShowTextures)
            margin += 10;

            SplatPrototype[] splats = script.terrain.terrainData.splatPrototypes;
            Texture2D[] textures = new Texture2D[splats.Length];
            for (int i=0; i<splats.Length; i++) textures[i] = splats[i].texture;

            Field<bool>(ref preset.foreground.apply, width:20);
            Label("Crag", width:70);
            Slider<float>(ref preset.foreground.opacity, width:width-130, max:2);
            Field<float>(ref preset.foreground.opacity, width:40);
            Par(42); TextureSelector(ref preset.foreground.num, textures);

            Par(5); Par();
            Field<bool>(ref preset.background.apply, width:20);
            Label("Sediment", width:70);
            Slider<float>(ref preset.background.opacity, width:width-130, max:2);
            Field<float>(ref preset.background.opacity, width:40);
            Par(42); TextureSelector(ref preset.background.num, textures);

            margin -= 10;

            #region apply to whole terrain
            Par(5); Par(); Foldout(ref script.guiShowGlobal, "Global Brush", "Apply Erosion Brush to whole terrain at once");
            if (script.guiShowGlobal)
            margin += 10; Par();
            if (Button("Apply to Whole Terrain"))
                //TerrainData data = script.terrain.terrainData;
                script.ApplyBrush(new Rect(0,0,1.01f,1.01f),useFallof:false);
                if (script.recordUndo)
                    UnityEditor.Undo.RecordObject(script,"Erosion Brush Global");
                    script.undo = !script.undo; UnityEditor.EditorUtility.SetDirty(this); //setting object change

            Quick<int>(ref script.guiApplyIterations, "Iterations", max:20);
            margin -= 10;

            #region settings
            Par(5); Par(); Foldout(ref script.guiShowSettings, "Settings");
            if (script.guiShowSettings)
            margin += 10;
            Quick<Color>(ref script.guiBrushColor, "Brush Color", "Visual representation of the brush.");
            Quick<float>(ref script.guiBrushThickness, "Brush Thickness", "Visual representation of the brush.", slider:false);
            Quick<int>(ref script.guiBrushNumCorners, "Brush Num Corners", "Visual representation of the brush.", slider:false);
            //Quick<bool>(ref script.unity5positioning, "Fix Unity5 Brush Positioning", "Unity5 Beta has incorrect terrain brush positioning (Both in Erosion Brush and Standard Terrain sculpting). Turn toggle on to fix it. WARNING: This fix is a crutch that bypasses known Unity's bug, turning it on causes some lag.");
            Quick<bool>(ref script.recordUndo, "Record Undo", "Disabling can increase performance a bit, but will make undo unavailable");
            Quick<bool>(ref script.focusOnBrush, "G Focuses on Brush", "Analog of F button, but it will focus camera not on the whole terrain, but on current brush position.");
            Quick<bool>(ref script.preserveDetail, "Preserve Detail on Downscale", "All the terrain detail edited with Downscale parameter will be returned on upscale");
            Quick<int>(ref script.guiMaxBrushSize, "Max Brush Size", "Brush size slider maximum. Note that increasing brush size will reduce performance in the quadratic dependence.", slider:false);
            if (script.guiMaxBrushSize > 100) { Par(40); EditorGUI.HelpBox(Inset(), "Increasing brush size will reduce performance in the quadratic dependence.", MessageType.Warning); }
            margin -= 10;

            #region about
            Par(5); Par(); Foldout(ref script.guiShowAbout, "About");
            if (script.guiShowAbout)

            if (script.guiPluginIcon==null) script.guiPluginIcon = Resources.Load("ErosionBrushIcon") as Texture2D;
            EditorGUI.DrawPreviewTexture(Inset(50+2), script.guiPluginIcon);
            cursor.y -= 50; cursor.y -= 7;

            margin = 70;

            Par(); Label("Erosion Brush v1.32_u5");
            Par(); Label("by Denis Pahunov");
            Par(); Label("Useful Links:");
            Par(); Url("", " - Online Documentation");
            Par(); Url("", " - Video Tutorial");
            Par(); Url("", " - Forum Thread", "Question and answers");
            Par(); Url("", " - Facebook", "News, anounces, contests");

            Par(); Label("On any issues related with plugin");
            Par(); Label("functioning you can contact the");
            Par(); Label("author by mail:");
            Par(); Url("mailto:[email protected]", "*****@*****.**");

            //margin = 10;
            //Par(1); lastRect.y -= 208; lastRect.height = 50;
            //if (script.guiPluginIcon==null) script.guiPluginIcon = Resources.Load("ErosionBrushIcon") as Texture2D;
            //EditorGUI.DrawPreviewTexture(Inset(50), script.guiPluginIcon);

Beispiel #4
 public void OnDisable()
     if (script==null) script = (ErosionBrush)target;
     if (script==null) return;
     UnityEditor.EditorApplication.update -= EditorUpdate;
     if (script.moveTfm!=null) DestroyImmediate(script.moveTfm.gameObject);