void HandleLodChunks() { if (terrainChunks != null) { ReleaseIndirectArgsBuffers(); } if (renderType == GrassRenderType.Mesh) { terrainChunks = MeshChunker.Chunk(grassMesh, chunksX, chunksY, chunksZ, grassPerTri, drawMat.GetFloat("bladeHeight")); } else { terrainChunks = MeshChunker.ChunkTerrain(terrainObject, chunksX, chunksZ, grassPerTri, terrainExpansion, drawMat.GetFloat("bladeHeight")); if (!terrainNormalMap) { terrainNormalMap = TextureCreator.GetTerrainNormalMap(terrainObject, gfComputeShader, normalKernel, highQualityHeightmap); terrainMapOffset = 1f / terrainNormalMap.width * 0.5f; } if (discardEmptyChunks) { DiscardUnusedChunks(); } } CheckIndirectInstancingArgs(); }
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(); } } }