2024-03-05 17:34:41 +08:00
#if GRIFFIN
2023-12-30 17:37:48 +08:00
using Pinwheel.Griffin.BackupTool ;
2024-03-05 17:34:41 +08:00
using System.Collections ;
2023-12-30 17:37:48 +08:00
using System.Collections.Generic ;
2024-03-05 17:34:41 +08:00
using Unity.EditorCoroutines.Editor ;
2023-12-30 17:37:48 +08:00
using UnityEditor ;
using UnityEngine ;
2024-03-05 17:34:41 +08:00
using UnityEngine.Rendering ;
2023-12-30 17:37:48 +08:00
namespace Pinwheel.Griffin.SplineTool
{
[CustomEditor(typeof(GFoliageSpawner))]
public class GFoliageSpawnerInspector : Editor
{
private GFoliageSpawner instance ;
private Dictionary < GStylizedTerrain , RenderTexture > previewTextures ;
private MaterialPropertyBlock previewPropertyBlock ;
2024-03-05 17:34:41 +08:00
private bool isUrp ;
private ScriptableRenderContext urpContext ;
2023-12-30 17:37:48 +08:00
private const string HISTORY_PREFIX = "Spawn Foliage Along Path" ;
private void OnEnable ( )
{
Undo . undoRedoPerformed + = OnUndoRedo ;
instance = target as GFoliageSpawner ;
instance . Internal_UpdateFalloffTexture ( ) ;
previewPropertyBlock = new MaterialPropertyBlock ( ) ;
GCommon . RegisterBeginRender ( OnCameraRender ) ;
GCommon . RegisterBeginRenderSRP ( OnCameraRenderSRP ) ;
}
private void OnDisable ( )
{
Undo . undoRedoPerformed - = OnUndoRedo ;
GCommon . UnregisterBeginRender ( OnCameraRender ) ;
GCommon . UnregisterBeginRenderSRP ( OnCameraRenderSRP ) ;
if ( previewTextures ! = null )
{
foreach ( GStylizedTerrain t in previewTextures . Keys )
{
RenderTexture rt = previewTextures [ t ] ;
if ( rt = = null )
continue ;
rt . Release ( ) ;
Object . DestroyImmediate ( rt ) ;
}
}
}
private void OnUndoRedo ( )
{
if ( Selection . activeGameObject ! = instance . gameObject )
return ;
if ( string . IsNullOrEmpty ( GUndoCompatibleBuffer . Instance . CurrentBackupName ) )
return ;
GBackup . Restore ( GUndoCompatibleBuffer . Instance . CurrentBackupName ) ;
}
public override void OnInspectorGUI ( )
{
2024-03-05 17:34:41 +08:00
DrawBaseGUI ( ) ;
2023-12-30 17:37:48 +08:00
if ( instance . SplineCreator = = null )
return ;
2024-03-05 17:34:41 +08:00
DrawFalloffGUI ( ) ;
DrawMaskGUI ( ) ;
DrawRotationScaleGUI ( ) ;
DrawTreesGUI ( ) ;
DrawGrassesGUI ( ) ;
DrawActionGUI ( ) ;
}
private class GBaseGUI
{
public static readonly GUIContent SPLINE_CREATOR = new GUIContent ( "Spline Creator" , "The Spline Creator component which this modifier belongs to" ) ;
public static readonly GUIContent LIVE_PREVIEW = new GUIContent ( "Live Preview" , "Draw a preview on the terrain" ) ;
}
private void DrawBaseGUI ( )
{
instance . SplineCreator = EditorGUILayout . ObjectField ( GBaseGUI . SPLINE_CREATOR , instance . SplineCreator , typeof ( GSplineCreator ) , true ) as GSplineCreator ;
2023-12-30 17:37:48 +08:00
EditorGUI . BeginChangeCheck ( ) ;
2024-03-05 17:34:41 +08:00
GEditorSettings . Instance . splineTools . livePreview . foliageSpawner = EditorGUILayout . Toggle ( GBaseGUI . LIVE_PREVIEW , GEditorSettings . Instance . splineTools . livePreview . foliageSpawner ) ;
2023-12-30 17:37:48 +08:00
if ( EditorGUI . EndChangeCheck ( ) )
{
2024-03-05 17:34:41 +08:00
EditorUtility . SetDirty ( GEditorSettings . Instance ) ;
2023-12-30 17:37:48 +08:00
}
2024-03-05 17:34:41 +08:00
}
private class GFalloffGUI
{
public static readonly string LABEL = "Falloff" ;
public static readonly string ID = "spline-foliage-spawner-falloff" ;
public static readonly GUIContent FALL_OFF = new GUIContent ( "Falloff" , "Falloff factor of the spline effect, from the center to the edge" ) ;
public static readonly GUIContent FALL_OFF_NOISE = new GUIContent ( "Falloff Noise" , "Noise map in world space to blend with the falloff curve" ) ;
public static readonly GUIContent FALL_OFF_NOISE_SIZE = new GUIContent ( "Falloff Noise Size" , "Size of the falloff noise in world space" ) ;
}
2023-12-30 17:37:48 +08:00
2024-03-05 17:34:41 +08:00
private void DrawFalloffGUI ( )
{
GEditorCommon . Foldout ( GFalloffGUI . LABEL , false , GFalloffGUI . ID , ( ) = >
2023-12-30 17:37:48 +08:00
{
2024-03-05 17:34:41 +08:00
EditorGUI . BeginChangeCheck ( ) ;
instance . Falloff = EditorGUILayout . CurveField ( GFalloffGUI . FALL_OFF , instance . Falloff , Color . red , new Rect ( 0 , 0 , 1 , 1 ) ) ;
if ( EditorGUI . EndChangeCheck ( ) )
{
instance . Internal_UpdateFalloffTexture ( ) ;
}
instance . FalloffNoise = EditorGUILayout . ObjectField ( GFalloffGUI . FALL_OFF_NOISE , instance . FalloffNoise , typeof ( Texture2D ) , false ) as Texture2D ;
if ( instance . FalloffNoise ! = null )
instance . FalloffNoiseSize = GEditorCommon . InlineVector2Field ( GFalloffGUI . FALL_OFF_NOISE_SIZE , instance . FalloffNoiseSize ) ;
} ) ;
}
private class GMaskGUI
{
public static readonly string LABEL = "Mask" ;
public static readonly string ID = "spline-foliage-spawner-mask" ;
public static readonly GUIContent MASK_RESOLUTION = new GUIContent ( "Mask Resolution" , "Resolution of the mask which is rendered by the spline, this mask will be used for sample foliage instances" ) ;
}
2023-12-30 17:37:48 +08:00
2024-03-05 17:34:41 +08:00
private void DrawMaskGUI ( )
{
GEditorCommon . Foldout ( GMaskGUI . LABEL , false , GMaskGUI . ID , ( ) = >
{
instance . MaskResolution = EditorGUILayout . DelayedIntField ( GMaskGUI . MASK_RESOLUTION , instance . MaskResolution ) ;
} ) ;
}
2023-12-30 17:37:48 +08:00
2024-03-05 17:34:41 +08:00
private class GTreesGUI
{
public static readonly string LABEL = "Trees" ;
public static readonly string ID = "spline-foliage-spawner-trees" ;
public static readonly GUIContent SPAWN_TREES = new GUIContent ( "Spawn Trees" , "Toggle trees spawning along the spline" ) ;
public static readonly GUIContent PROTOTYPES = new GUIContent ( "Prototypes" , "The tree types to spawn" ) ;
public static readonly GUIContent DENSITY = new GUIContent ( "Density" , "Number of instances per a meter-squared" ) ;
}
private void DrawTreesGUI ( )
{
GEditorCommon . Foldout ( GTreesGUI . LABEL , false , GTreesGUI . ID , ( ) = >
2023-12-30 17:37:48 +08:00
{
2024-03-05 17:34:41 +08:00
instance . SpawnTrees = EditorGUILayout . Toggle ( GTreesGUI . SPAWN_TREES , instance . SpawnTrees ) ;
if ( instance . SpawnTrees )
{
EditorGUILayout . LabelField ( GTreesGUI . PROTOTYPES ) ;
instance . TreePrototypeIndices = GEditorCommon . TreeSetMultiSelectionGrid ( instance . SplineCreator . GroupId , instance . TreePrototypeIndices ) ;
instance . TreeDensity = EditorGUILayout . FloatField ( GTreesGUI . DENSITY , instance . TreeDensity ) ;
}
} ) ;
}
private class GGrassesGUI
{
public static readonly string LABEL = "Grasses" ;
public static readonly string ID = "spline-foliage-spawner-grasses" ;
public static readonly GUIContent SPAWN_GRASSES = new GUIContent ( "Spawn Grasses" , "Toggle grasses spawning along the spline" ) ;
public static readonly GUIContent PROTOTYPES = new GUIContent ( "Prototypes" , "The grass types to spawn" ) ;
public static readonly GUIContent DENSITY = new GUIContent ( "Density" , "Number of instances per a meter-squared" ) ;
}
private void DrawGrassesGUI ( )
{
GEditorCommon . Foldout ( GGrassesGUI . LABEL , false , GGrassesGUI . ID , ( ) = >
{
instance . SpawnGrasses = EditorGUILayout . Toggle ( GGrassesGUI . SPAWN_GRASSES , instance . SpawnGrasses ) ;
if ( instance . SpawnGrasses )
{
EditorGUILayout . LabelField ( GGrassesGUI . PROTOTYPES ) ;
instance . GrassPrototypeIndices = GEditorCommon . GrassSetMultiSelectionGrid ( instance . SplineCreator . GroupId , instance . GrassPrototypeIndices ) ;
instance . GrassDensity = EditorGUILayout . FloatField ( GGrassesGUI . DENSITY , instance . GrassDensity ) ;
}
} ) ;
}
private class GRotationScaleGUI
{
public static readonly string LABEL = "Rotation & Scale" ;
public static readonly string ID = "spline-foliage-spawner-rotation-scale" ;
public static readonly GUIContent MIN_ROTATION = new GUIContent ( "Min Rotation" , "Minimum rotation of each instance, in degree, on Y axis" ) ;
public static readonly GUIContent MAX_ROTATION = new GUIContent ( "Max Rotation" , "Maximum rotation of each instance, in degree, on Y axis" ) ;
public static readonly GUIContent MIN_SCALE = new GUIContent ( "Min Scale" , "Minimum scale of each instance" ) ;
public static readonly GUIContent MAX_SCALE = new GUIContent ( "Max Scale" , "Maximum scale of each instance" ) ;
}
private void DrawRotationScaleGUI ( )
{
GEditorCommon . Foldout ( GRotationScaleGUI . LABEL , false , GRotationScaleGUI . ID , ( ) = >
{
instance . MinRotation = EditorGUILayout . FloatField ( GRotationScaleGUI . MIN_ROTATION , instance . MinRotation ) ;
instance . MaxRotation = EditorGUILayout . FloatField ( GRotationScaleGUI . MAX_ROTATION , instance . MaxRotation ) ;
instance . MinScale = GEditorCommon . InlineVector3Field ( GRotationScaleGUI . MIN_SCALE , instance . MinScale ) ;
instance . MaxScale = GEditorCommon . InlineVector3Field ( GRotationScaleGUI . MAX_SCALE , instance . MaxScale ) ;
} ) ;
}
private class GActionGUI
{
public static readonly string LABEL = "Action" ;
public static readonly string ID = "spline-foliage-spawner-action" ;
public static readonly GUIContent SPAWN_TREES = new GUIContent ( "Spawn Trees" ) ;
public static readonly GUIContent SPAWN_GRASSES = new GUIContent ( "Spawn Grasses" ) ;
public static readonly GUIContent SPAWN_TREES_GRASSES = new GUIContent ( "Spawn Trees & Grasses" ) ;
}
private void DrawActionGUI ( )
{
GEditorCommon . ExpandFoldout ( GActionGUI . ID ) ;
GEditorCommon . Foldout ( GActionGUI . LABEL , true , GActionGUI . ID , ( ) = >
{
GUIContent btnLabel ;
if ( instance . SpawnTrees & & instance . SpawnGrasses )
{
btnLabel = GActionGUI . SPAWN_TREES_GRASSES ;
}
else if ( instance . SpawnTrees & & ! instance . SpawnGrasses )
{
btnLabel = GActionGUI . SPAWN_TREES ;
}
else if ( ! instance . SpawnTrees & & instance . SpawnGrasses )
{
btnLabel = GActionGUI . SPAWN_GRASSES ;
}
else
{
btnLabel = GActionGUI . SPAWN_TREES_GRASSES ;
}
GUI . enabled = instance . SpawnTrees | | instance . SpawnGrasses ;
if ( GUILayout . Button ( btnLabel ) )
{
List < GStylizedTerrain > terrains = GUtilities . ExtractTerrainsFromOverlapTest ( instance . SplineCreator . SweepTest ( ) ) ;
CreateInitialBackup ( terrains ) ;
Apply ( ) ;
CreateBackupAfterApply ( terrains ) ;
}
GUI . enabled = true ;
} ) ;
2023-12-30 17:37:48 +08:00
}
2024-03-05 17:34:41 +08:00
private void CreateInitialBackup ( List < GStylizedTerrain > terrains )
2023-12-30 17:37:48 +08:00
{
GBackupInternal . TryCreateAndMergeInitialBackup ( HISTORY_PREFIX , terrains , GCommon . FoliageInstancesResourceFlags , true ) ;
}
private void Apply ( )
{
EditorUtility . DisplayProgressBar ( "Applying" , "Spawning foliage..." , 1f ) ;
try
{
instance . Apply ( ) ;
}
catch ( System . Exception e )
{
Debug . LogError ( e . ToString ( ) ) ;
}
EditorUtility . ClearProgressBar ( ) ;
}
2024-03-05 17:34:41 +08:00
private void CreateBackupAfterApply ( List < GStylizedTerrain > terrains )
2023-12-30 17:37:48 +08:00
{
GBackupInternal . TryCreateAndMergeBackup ( HISTORY_PREFIX , terrains , GCommon . FoliageInstancesResourceFlags , true ) ;
}
private void OnCameraRender ( Camera cam )
{
2024-03-05 17:34:41 +08:00
isUrp = false ;
urpContext = default ;
if ( cam . cameraType ! = CameraType . SceneView )
return ;
if ( GEditorSettings . Instance . splineTools . livePreview . foliageSpawner )
2023-12-30 17:37:48 +08:00
DrawLivePreview ( cam ) ;
}
private void OnCameraRenderSRP ( UnityEngine . Rendering . ScriptableRenderContext context , Camera cam )
{
2024-03-05 17:34:41 +08:00
isUrp = true ;
urpContext = context ;
if ( cam . cameraType ! = CameraType . SceneView )
return ;
if ( GEditorSettings . Instance . splineTools . livePreview . foliageSpawner )
{
EditorCoroutineUtility . StartCoroutine ( IDrawLivePreviewEndOfFrameURP ( cam ) , this ) ;
}
}
private IEnumerator IDrawLivePreviewEndOfFrameURP ( Camera cam )
{
yield return new WaitForEndOfFrame ( ) ;
if ( cam ! = null )
{
DrawLivePreview ( cam ) ;
}
2023-12-30 17:37:48 +08:00
}
private void DrawLivePreview ( Camera cam )
{
2024-03-05 17:34:41 +08:00
List < GOverlapTestResult > sweepTests = instance . SplineCreator . SweepTest ( ) ;
foreach ( GOverlapTestResult st in sweepTests )
2023-12-30 17:37:48 +08:00
{
2024-03-05 17:34:41 +08:00
if ( ! st . IsOverlapped )
2023-12-30 17:37:48 +08:00
continue ;
2024-03-05 17:34:41 +08:00
DrawLivePreview ( st . Terrain , cam , st . IsChunkOverlapped ) ;
2023-12-30 17:37:48 +08:00
}
}
2024-03-05 17:34:41 +08:00
private void DrawLivePreview ( GStylizedTerrain t , Camera cam , bool [ ] chunkCulling )
2023-12-30 17:37:48 +08:00
{
if ( t . transform . rotation ! = Quaternion . identity | |
t . transform . lossyScale ! = Vector3 . one )
return ;
2024-03-05 17:34:41 +08:00
RenderTexture rt = GetPreviewTexture ( t ) ;
if ( isUrp )
2023-12-30 17:37:48 +08:00
{
2024-03-05 17:34:41 +08:00
instance . Internal_Apply ( t , rt , urpContext ) ;
}
else
{
instance . Internal_Apply ( t , rt ) ;
2023-12-30 17:37:48 +08:00
}
GLivePreviewDrawer . DrawMasksLivePreview (
t , cam ,
new Texture [ ] { rt } ,
new Color [ ] { GEditorSettings . Instance . splineTools . positiveHighlightColor } ,
2024-03-05 17:34:41 +08:00
chunkCulling ) ;
2023-12-30 17:37:48 +08:00
}
private RenderTexture GetPreviewTexture ( GStylizedTerrain t )
{
if ( previewTextures = = null )
{
previewTextures = new Dictionary < GStylizedTerrain , RenderTexture > ( ) ;
}
int resolution = instance . MaskResolution ;
if ( ! previewTextures . ContainsKey ( t ) | |
previewTextures [ t ] = = null )
{
RenderTexture rt = new RenderTexture ( resolution , resolution , 0 , RenderTextureFormat . ARGB32 , RenderTextureReadWrite . Linear ) ;
previewTextures [ t ] = rt ;
}
else if ( previewTextures [ t ] . width ! = resolution | | previewTextures [ t ] . height ! = resolution )
{
previewTextures [ t ] . Release ( ) ;
Object . DestroyImmediate ( previewTextures [ t ] ) ;
RenderTexture rt = new RenderTexture ( resolution , resolution , 0 , RenderTextureFormat . ARGB32 , RenderTextureReadWrite . Linear ) ;
previewTextures [ t ] = rt ;
}
previewTextures [ t ] . wrapMode = TextureWrapMode . Clamp ;
return previewTextures [ t ] ;
}
}
}
2024-03-05 17:34:41 +08:00
#endif