void SceneGUICallback(SceneView sceneView) { if (mapPaintingEnabled && mainTabIndex == 1) { Event e = Event.current; HandleHotkeys(e); RaycastHit hit; Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition); Physics.Raycast(ray, out hit, float.PositiveInfinity, paintRaycastMask); bool hitTerrain = hit.transform == grassFlow.terrainTransform; if (e.type == EventType.MouseDown || e.type == EventType.MouseUp) { if (e.button == 0 && !Event.current.alt) { Selection.activeObject = grassFlow; } } if (!hitTerrain) { DisablePaintHighlight(); if (!shouldPaint) { return; } } if (grassFlow.drawMat) { grassFlow.drawMat.SetTexture("paintHighlightBrushTex", brushList.GetActiveBrush().texture); grassFlow.drawMat.SetColor("paintHightlightColor", paintBrushColor); if (shouldPaint) { if (paintToolType == PaintToolType.Color) { grassFlow.drawMat.SetVector("paintHighlightBrushParams", Vector4.zero); } else { grassFlow.drawMat.SetVector("paintHighlightBrushParams", new Vector4(hit.textureCoord.x, hit.textureCoord.y, paintBrushSize * 0.05f, 0.5f)); } } else { grassFlow.drawMat.SetVector("paintHighlightBrushParams", new Vector4(hit.textureCoord.x, hit.textureCoord.y, paintBrushSize * 0.05f, 1f)); } } int id = GUIUtility.GetControlID(grassEditorHash, FocusType.Passive); float brushDir = e.shift ? -1f : 1f; switch (e.GetTypeForControl(id)) { case EventType.Layout: HandleUtility.AddDefaultControl(id); if (continuousPaint && shouldPaint) { PaintSwitch(hit.textureCoord, brushDir); } break; case EventType.MouseMove: HandleUtility.Repaint(); break; case EventType.MouseDown: case EventType.MouseDrag: { // Don't do anything at all if someone else owns the hotControl. Fixes case 677541. if (EditorGUIUtility.hotControl != 0 && EditorGUIUtility.hotControl != id) { return; } // Don't do anything on MouseDrag if we don't own the hotControl. if (e.GetTypeForControl(id) == EventType.MouseDrag && EditorGUIUtility.hotControl != id) { return; } // If user is ALT-dragging, we want to return to main routine if (Event.current.alt) { return; } // Allow painting with LMB only if (e.button != 0) { return; } if (HandleUtility.nearestControl != id) { return; } if (e.type == EventType.MouseDown) { EditorGUIUtility.hotControl = id; shouldPaint = true; if (saveBeforeStroke) { SaveData(false); } GrassFlowRenderer.SetPaintBrushTexture(brushList.GetActiveBrush().texture); } if (!continuousPaint) { PaintSwitch(hit.textureCoord, brushDir); } e.Use(); } break; case EventType.MouseUp: { if (GUIUtility.hotControl != id) { return; } shouldPaint = false; MarkDirtyMaps(); if (saveBeforeStroke) { AssetDatabase.Refresh(); } // Release hot control GUIUtility.hotControl = 0; } break; } } else { DisablePaintHighlight(); } }
private void Start() { GrassFlowRenderer.SetPaintBrushTexture(flatTex); }
void DrawPaintGUI() { DrawMapsInspector(); EditorGUI.BeginChangeCheck(); mapPaintingEnabled = EditorGUILayout.ToggleLeft(GetContent(() => grassFlow.enableMapPainting), grassFlow.enableMapPainting); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(grassFlow, "GrassFlow Change Variable"); grassFlow.enableMapPainting = mapPaintingEnabled; Undo.FlushUndoRecordObjects(); } if (!grassFlow.enableMapPainting) { return; } EditorGUI.BeginChangeCheck(); bool _saveBeforeStroke = EditorGUILayout.ToggleLeft(new GUIContent("Save On Stroke", "Saves the maps before each stroke so that the stroke can be reverted to the save before the stroke."), saveBeforeStroke); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(this, "GrassFlow Change Variable"); saveBeforeStroke = _saveBeforeStroke; Undo.FlushUndoRecordObjects(); } if (GUILayout.Button(new GUIContent("Revert Changes", "Works kind of like a limited undo functionality, discarding changes to detail maps since they were last saved. " + "The maps are saved whenever the project assets are saved e.g. on Ctrl+S. Revert hotkey: Shift-R"))) { grassFlow.RevertDetailMaps(); } if (grassFlow.renderType == GrassFlowRenderer.GrassRenderType.Mesh) { GUILayout.Space(12); if (GUILayout.Button(new GUIContent("Bake Density to Mesh", "Creates a new mesh based on the density information in the parameter map. " + "You can use this mesh to more efficiently only render grass on certain parts of your mesh. Does NOT automatically apply the resulting mesh."))) { string fileName = EditorUtility.SaveFilePanelInProject("Choose Save Location", "GrassflowDensityMesh", "asset", ""); if (string.IsNullOrEmpty(fileName)) { return; } SaveData(); Mesh bakedMesh = MeshChunker.BakeDensityToMesh(grassFlow.grassMesh, grassFlow.paramMap); AssetDatabase.CreateAsset(bakedMesh, fileName); AssetDatabase.SaveAssets(); } } EditorGUILayout.Space(); EditorGUILayout.LabelField("", new GUIStyle(GUI.skin.horizontalScrollbarThumb) { fixedHeight = 2 }, GUILayout.Height(3)); EditorGUILayout.Space(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); EditorGUI.BeginChangeCheck(); int newToolIndex = GUILayout.Toolbar(selectedPaintToolIndex, toolIcons, GUILayout.Height(iconSize - 15), GUILayout.Width(iconSize * toolIcons.Length)); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(this, "GrassFlow Change Paint Tool"); selectedPaintToolIndex = newToolIndex; paintToolType = (PaintToolType)selectedPaintToolIndex; Undo.FlushUndoRecordObjects(); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginVertical(EditorStyles.helpBox); GUILayout.Label(toolInfos[selectedPaintToolIndex].text); GUILayout.Label(toolInfos[selectedPaintToolIndex].tooltip, EditorStyles.wordWrappedMiniLabel); GUILayout.EndVertical(); if (brushList.ShowGUI()) { Undo.RecordObject(this, "GrassFlow Change Brush"); selectedBrushIndex = brushList.selectedIndex; GrassFlowRenderer.SetPaintBrushTexture(brushList.GetActiveBrush().texture); Undo.FlushUndoRecordObjects(); } EditorGUILayout.Space(); EditorGUILayout.LabelField("Settings", bold); Undo.RecordObject(this, "GrassFlow Change Variable"); if (paintToolType == PaintToolType.Color) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Brush Color", GUILayout.Width(100)); paintBrushColor = EditorGUILayout.ColorField(new GUIContent(), paintBrushColor, true, true, true, new ColorPickerHDRConfig(0, 10, 0, 10)); EditorGUILayout.EndHorizontal(); } else { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(new GUIContent("Clamp Range", "Min and max range for parameters while painting. This can be used to essentially paint a set value instead of being additive or subtractive."), GUILayout.Width(100)); clampRange = EditorGUILayout.Vector2Field("", clampRange, GUILayout.Width(100)); GUILayout.Space(5); EditorGUILayout.MinMaxSlider("", ref clampRange.x, ref clampRange.y, 0, 1, GUILayout.MinWidth(20)); EditorGUILayout.EndHorizontal(); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Brush Size", GUILayout.Width(100)); paintBrushSize = EditorGUILayout.FloatField("", paintBrushSize, GUILayout.Width(100)); GUILayout.Space(5); paintBrushSize = GUILayout.HorizontalSlider(paintBrushSize, 0f, 1f); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Brush Strength", GUILayout.Width(100)); paintBrushStrength = EditorGUILayout.FloatField("", paintBrushStrength, GUILayout.Width(100)); GUILayout.Space(5); paintBrushStrength = GUILayout.HorizontalSlider(paintBrushStrength, 0f, 1f); EditorGUILayout.EndHorizontal(); LayerMask rayCastMask = EditorGUILayout.MaskField(new GUIContent("Raycast Layer Mask", "This mask is used when raycasting the terrain/mesh for painting. " + "You can use this to only paint on the layer your terrain is on and paint through blocking objects, or vice versa."), InternalEditorUtility.LayerMaskToConcatenatedLayersMask(paintRaycastMask), InternalEditorUtility.layers); paintRaycastMask = InternalEditorUtility.ConcatenatedLayersMaskToLayerMask(rayCastMask); continuousPaint = EditorGUILayout.ToggleLeft(new GUIContent("Paint Continuously", "If off the mouse needs to be moved to paint, otherwise it will paint continously while the mouse is down."), continuousPaint); useDeltaTimePaint = EditorGUILayout.ToggleLeft(new GUIContent("Use Delta Time Paint", "If on the brush strength is multiplied by delta time to make painting strength framerate independent. " + "It's useful to turn this off if you want to use brushes more like stamps and use strength of 1 and apply the full brush to the grass with a single click."), useDeltaTimePaint); if (grassFlow.renderType == GrassFlowRenderer.GrassRenderType.Terrain) { EditorGUILayout.Space(); EditorGUILayout.LabelField("", new GUIStyle(GUI.skin.horizontalScrollbarThumb) { fixedHeight = 2 }, GUILayout.Height(3)); EditorGUILayout.Space(); EditorGUILayout.LabelField("Splat Maps", bold); if (grassFlow.terrainObject) { int numSplatLayers = grassFlow.terrainObject.terrainData.alphamapLayers; if (numSplatLayers > 0) { EditorGUI.BeginChangeCheck(); int _splatMapLayerIdx = Mathf.Clamp(splatMapLayerIdx, 0, numSplatLayers); float _splatMapTolerance = splatMapTolerance; int[] splatInts = new int[numSplatLayers]; GUIContent[] splatStrs = new GUIContent[numSplatLayers]; for (int i = 0; i < splatInts.Length; i++) { splatInts[i] = i; splatStrs[i] = new GUIContent((i + 1).ToString() + " : " + grassFlow.terrainObject.terrainData.splatPrototypes[i].texture.name); } _splatMapLayerIdx = EditorGUILayout.IntPopup(new GUIContent("Splat Layer", "The index of the splat texture layer you want to use to mask where grass appears."), _splatMapLayerIdx, splatStrs, splatInts); _splatMapTolerance = EditorGUILayout.Slider(new GUIContent("Tolerance", "Controls opacity tolerance when applying splat map layers."), splatMapTolerance, 0f, 1f); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(this, "GrassFlow Change Paint Tool"); splatMapLayerIdx = _splatMapLayerIdx; splatMapTolerance = _splatMapTolerance; Undo.FlushUndoRecordObjects(); } EditorGUILayout.BeginHorizontal(); if (GUILayout.Button(new GUIContent("Apply Additive", "Adds grass based on the selected layer, but does not remove any existing grass."))) { grassFlow.ApplySplatTex(grassFlow.terrainObject, splatMapLayerIdx, 0, splatMapTolerance); } if (GUILayout.Button(new GUIContent("Apply Subtractive", "Removes grass based on the selected layer, but does not affect grass outside of the splat map."))) { grassFlow.ApplySplatTex(grassFlow.terrainObject, splatMapLayerIdx, 1, 1f - splatMapTolerance); } if (GUILayout.Button(new GUIContent("Apply Replace", "Adds grass based on the selected layer, removing and overwriting existing grass."))) { grassFlow.ApplySplatTex(grassFlow.terrainObject, splatMapLayerIdx, 2, splatMapTolerance); } EditorGUILayout.EndHorizontal(); } else { GUILayout.BeginVertical(EditorStyles.helpBox); GUILayout.Label("No splat layers on the terrain."); GUILayout.EndVertical(); } //grassFlow.terrainObject.terrainData.alphamapTextures[] } else { GUILayout.BeginVertical(EditorStyles.helpBox); GUILayout.Label("Please assign terrain object in settings."); GUILayout.EndVertical(); } } }