#if GRIFFIN using UnityEngine; using System.Collections.Generic; using Type = System.Type; using Rand = System.Random; #if UNITY_EDITOR using Pinwheel.Griffin.BackupTool; #endif namespace Pinwheel.Griffin.PaintTool { [System.Serializable] [ExecuteInEditMode] public class GFoliagePainter : MonoBehaviour { private static readonly List BUILTIN_PAINTER_NAME = new List(new string[] { "GTreePainter", "GTreeScaler", "GGrassPainter", "GGrassScaler" }); private static List customPainterTypes; public static List CustomPainterTypes { get { if (customPainterTypes == null) customPainterTypes = new List(); return customPainterTypes; } private set { customPainterTypes = value; } } public static string FoliagePainterInterfaceName { get { return typeof(IGFoliagePainter).Name; } } static GFoliagePainter() { RefreshCustomPainterTypes(); } public static void RefreshCustomPainterTypes() { List loadedTypes = GCommon.GetAllLoadedTypes(); CustomPainterTypes = loadedTypes.FindAll( t => t.GetInterface(FoliagePainterInterfaceName) != null && !BUILTIN_PAINTER_NAME.Contains(t.Name)); } [SerializeField] private int groupId; public int GroupId { get { return groupId; } set { groupId = value; } } [SerializeField] private GFoliagePaintingMode mode; public GFoliagePaintingMode Mode { get { return mode; } set { mode = value; } } [SerializeField] private int customPainterIndex; public int CustomPainterIndex { get { return customPainterIndex; } set { customPainterIndex = value; } } [SerializeField] private string customPainterArgs; public string CustomPainterArgs { get { return customPainterArgs; } set { customPainterArgs = value; } } [SerializeField] private bool enableTerrainMask; public bool EnableTerrainMask { get { return enableTerrainMask; } set { enableTerrainMask = value; } } public IGFoliagePainter ActivePainter { get { if (Mode == GFoliagePaintingMode.PaintTree) { return new GTreePainter(); } else if (Mode == GFoliagePaintingMode.ScaleTree) { return new GTreeScaler(); } else if (Mode == GFoliagePaintingMode.PaintGrass) { return new GGrassPainter(); } else if (Mode == GFoliagePaintingMode.ScaleGrass) { return new GGrassScaler(); } else if (mode == GFoliagePaintingMode.Custom) { if (CustomPainterIndex >= 0 && CustomPainterIndex < CustomPainterTypes.Count) return System.Activator.CreateInstance(CustomPainterTypes[CustomPainterIndex]) as IGFoliagePainter; } return null; } } [SerializeField] private float brushRadius; public float BrushRadius { get { return brushRadius; } set { brushRadius = Mathf.Max(0.01f, value); } } [SerializeField] private float brushRadiusJitter; public float BrushRadiusJitter { get { return brushRadiusJitter; } set { brushRadiusJitter = Mathf.Clamp01(value); } } [SerializeField] private float brushRotation; public float BrushRotation { get { return brushRotation; } set { brushRotation = value; } } [SerializeField] private float brushRotationJitter; public float BrushRotationJitter { get { return brushRotationJitter; } set { brushRotationJitter = Mathf.Clamp01(value); } } [SerializeField] private int brushDensity; public int BrushDensity { get { return brushDensity; } set { brushDensity = Mathf.Clamp(value, 1, 100); } } [SerializeField] private float brushDensityJitter; public float BrushDensityJitter { get { return brushDensityJitter; } set { brushDensityJitter = Mathf.Clamp01(value); } } [SerializeField] private float brushScatter; public float BrushScatter { get { return brushScatter; } set { brushScatter = Mathf.Clamp01(value); } } [SerializeField] private float brushScatterJitter; public float BrushScatterJitter { get { return brushScatterJitter; } set { brushScatterJitter = Mathf.Clamp01(value); } } [SerializeField] private List brushMasks; public List BrushMasks { get { if (brushMasks == null) brushMasks = new List(); return brushMasks; } set { brushMasks = value; } } [SerializeField] private int selectedBrushMaskIndex; public int SelectedBrushMaskIndex { get { return selectedBrushMaskIndex; } set { if (BrushMasks.Count > 0) selectedBrushMaskIndex = Mathf.Clamp(value, 0, BrushMasks.Count); else selectedBrushMaskIndex = -1; } } [SerializeField] private List selectedTreeIndices; public List SelectedTreeIndices { get { if (selectedTreeIndices == null) { selectedTreeIndices = new List(); } return selectedTreeIndices; } set { selectedTreeIndices = value; } } [SerializeField] private List selectedGrassIndices; public List SelectedGrassIndices { get { if (selectedGrassIndices == null) { selectedGrassIndices = new List(); } return selectedGrassIndices; } set { selectedGrassIndices = value; } } [SerializeField] private float eraseRatio; public float EraseRatio { get { return eraseRatio; } set { eraseRatio = Mathf.Clamp01(value); } } [SerializeField] private float scaleStrength; public float ScaleStrength { get { return scaleStrength; } set { scaleStrength = Mathf.Max(0, value); } } private void OnEnable() { ReloadBrushMasks(); } private void Reset() { GroupId = 0; Mode = GFoliagePaintingMode.PaintTree; BrushRadius = 50; BrushRadiusJitter = 0; BrushDensity = 1; BrushDensityJitter = 0; BrushRotation = 0; BrushRotationJitter = 0; EraseRatio = 1; ScaleStrength = 1; } public void ReloadBrushMasks() { BrushMasks = new List(Resources.LoadAll(GCommon.BRUSH_MASK_RESOURCES_PATH)); } public void Paint(GFoliagePainterArgs args) { IGFoliagePainter p = ActivePainter; if (p == null) return; args.Radius = BrushRadius; args.Rotation = BrushRotation; args.Density = BrushDensity; args.EraseRatio = EraseRatio; args.ScaleStrength = ScaleStrength; args.TreeIndices = SelectedTreeIndices; args.GrassIndices = SelectedGrassIndices; args.CustomArgs = CustomPainterArgs; if (SelectedBrushMaskIndex >= 0 && SelectedBrushMaskIndex < BrushMasks.Count) { args.Mask = BrushMasks[SelectedBrushMaskIndex]; } args.Filters = GetComponents(); args.EnableTerrainMask = EnableTerrainMask; ProcessBrushDynamic(ref args); Vector3[] corners = GCommon.GetBrushQuadCorners(args.HitPoint, args.Radius, args.Rotation); args.WorldPointCorners = corners; List overlappedTerrain = GUtilities.ExtractTerrainsFromOverlapTest(GPaintToolUtilities.OverlapTest(GroupId, args.HitPoint, args.Radius, args.Rotation)); #if UNITY_EDITOR if ((args.MouseEventType == GPainterMouseEventType.Down || args.MouseEventType == GPainterMouseEventType.Drag) && args.ShouldCommitNow == false) { Editor_CreateInitialHistoryEntry(args, overlappedTerrain); } #endif foreach (GStylizedTerrain t in overlappedTerrain) { p.Paint(t, args); } #if UNITY_EDITOR EditedTerrains.UnionWith(overlappedTerrain); if (args.MouseEventType == GPainterMouseEventType.Up) { Editor_CreateHistory(args); currentInitialBackupName = null; InitialRecordedTerrains.Clear(); EditedTerrains.Clear(); } #endif } #if UNITY_EDITOR private HashSet initialRecordedTerrains; private HashSet InitialRecordedTerrains { get { if (initialRecordedTerrains == null) { initialRecordedTerrains = new HashSet(); } return initialRecordedTerrains; } } private HashSet editedTerrains; private HashSet EditedTerrains { get { if (editedTerrains == null) { editedTerrains = new HashSet(); } return editedTerrains; } } private string currentInitialBackupName; private void Editor_CreateInitialHistoryEntry(GFoliagePainterArgs args, List overlappedTerrains) { if (!GEditorSettings.Instance.paintTools.enableHistory) return; if (overlappedTerrains.Count == 0) return; List flags = new List(); flags.AddRange(ActivePainter.GetResourceFlagForHistory(args)); if (InitialRecordedTerrains.Count == 0) { currentInitialBackupName = GBackup.TryCreateInitialBackup(ActivePainter.HistoryPrefix, overlappedTerrains[0], flags, false); if (!string.IsNullOrEmpty(currentInitialBackupName)) { InitialRecordedTerrains.Add(overlappedTerrains[0]); } } else { if (!string.IsNullOrEmpty(currentInitialBackupName)) { for (int i = 0; i < overlappedTerrains.Count; ++i) { if (InitialRecordedTerrains.Contains(overlappedTerrains[i])) continue; GBackup.BackupTerrain(overlappedTerrains[i], currentInitialBackupName, flags); InitialRecordedTerrains.Add(overlappedTerrains[i]); } } } } private void Editor_CreateHistory(GFoliagePainterArgs args) { if (!GEditorSettings.Instance.paintTools.enableHistory) return; if (EditedTerrains.Count == 0) return; List flags = new List(); flags.AddRange(ActivePainter.GetResourceFlagForHistory(args)); List terrainList = new List(EditedTerrains); string backupName = GBackup.TryCreateBackup(ActivePainter.HistoryPrefix, terrainList[0], flags, false); if (!string.IsNullOrEmpty(backupName)) { for (int i = 1; i < terrainList.Count; ++i) { GBackup.BackupTerrain(terrainList[i], backupName, flags); } } } #endif private Rand GetRandomGenerator() { return new Rand(Time.frameCount); } private void ProcessBrushDynamic(ref GFoliagePainterArgs args) { Rand rand = GetRandomGenerator(); args.Radius -= BrushRadius * BrushRadiusJitter * (float)rand.NextDouble(); args.Rotation += Mathf.Sign((float)rand.NextDouble() - 0.5f) * BrushRotation * BrushRotationJitter * (float)rand.NextDouble(); args.Density -= Mathf.RoundToInt(BrushDensity * BrushDensityJitter * (float)rand.NextDouble()); Vector3 scatterDir = new Vector3((float)(rand.NextDouble() * 2 - 1), 0, (float)(rand.NextDouble() * 2 - 1)).normalized; float scatterLengthMultiplier = BrushScatter - (float)rand.NextDouble() * BrushScatterJitter; float scatterLength = args.Radius * scatterLengthMultiplier; args.HitPoint += scatterDir * scatterLength; } } } #endif