2024-03-05 17:34:41 +08:00
#if GRIFFIN
2023-12-30 17:37:48 +08:00
using Pinwheel.Griffin.BackupTool ;
using System.Collections.Generic ;
using UnityEditor ;
using UnityEngine ;
using Enum = System . Enum ;
using Type = System . Type ;
namespace Pinwheel.Griffin.PaintTool
{
[CustomEditor(typeof(GTerrainTexturePainter))]
public class GTerrainPainterInspector : Editor
{
private GTerrainTexturePainter painter ;
private Vector3 [ ] worldPoints = new Vector3 [ 4 ] ;
2024-03-05 17:34:41 +08:00
private bool isColorSamplingEnable ;
2023-12-30 17:37:48 +08:00
private void OnEnable ( )
{
Undo . undoRedoPerformed + = OnUndoRedo ;
SceneView . duringSceneGui + = DuringSceneGUI ;
painter = ( GTerrainTexturePainter ) target ;
Tools . hidden = true ;
GCommon . UpdateMaterials ( painter . GroupId ) ;
GCommon . RegisterBeginRender ( OnBeginRender ) ;
GCommon . RegisterBeginRenderSRP ( OnBeginRenderSRP ) ;
}
private void OnDisable ( )
{
Undo . undoRedoPerformed - = OnUndoRedo ;
SceneView . duringSceneGui - = DuringSceneGUI ;
Tools . hidden = false ;
GCommon . UnregisterBeginRender ( OnBeginRender ) ;
GCommon . UnregisterBeginRenderSRP ( OnBeginRenderSRP ) ;
GTerrainTexturePainter . Internal_ReleaseRenderTextures ( ) ;
}
private void OnUndoRedo ( )
{
if ( Selection . activeGameObject ! = painter . gameObject )
return ;
if ( string . IsNullOrEmpty ( GUndoCompatibleBuffer . Instance . CurrentBackupName ) )
return ;
GBackup . Restore ( GUndoCompatibleBuffer . Instance . CurrentBackupName ) ;
}
2024-03-05 17:34:41 +08:00
private class GBaseGUI
2023-12-30 17:37:48 +08:00
{
2024-03-05 17:34:41 +08:00
public static readonly GUIContent GROUP_ID = new GUIContent ( "Group Id" , "Id of the terrain group this painter will work on" ) ;
public static readonly GUIContent FORCE_UPDATE_GEOMETRY = new GUIContent ( "Force Update Geometry" , "Update terrain geometry even when the painter doesn't modify the height map. Turn this on when you're using Albedo To Vertex Color" ) ;
public static readonly GUIContent ENABLE_HISTORY = new GUIContent ( "Enable History" , "Enable history recording for undo. History recording may get slow when working with a large group of terrains" ) ;
public static readonly GUIContent ENABLE_LIVE_PREVIEW = new GUIContent ( "Enable Live Preview" , "Draw a preview on the terrain surface showing the painter effect" ) ;
public static readonly GUIContent ENABLE_TERRAIN_MASK = new GUIContent ( "Enable Terrain Mask" , "Use the terrain's Mask texture (R channel) to lock particular regions from being edited" ) ;
public static readonly GUIContent SHOW_TERRAIN_MASK = new GUIContent ( "Show Terrain Mask" , "Draw the terrain mask overlay in the scene view. Disable this toggle if you are experiencing some frame rate drop" ) ;
public static readonly GUIContent ENABLE_TOPOGRAPHIC = new GUIContent ( "Enable Topographic" , "Render a topographic view for better sense of elevation change" ) ;
public static readonly GUIContent NO_PAINTER_FOUND = new GUIContent ( "No painter found!" ) ;
2023-12-30 17:37:48 +08:00
}
public override void OnInspectorGUI ( )
{
EditorGUI . BeginChangeCheck ( ) ;
2024-03-05 17:34:41 +08:00
painter . GroupId = GEditorCommon . ActiveTerrainGroupPopupWithAllOption ( GBaseGUI . GROUP_ID , painter . GroupId ) ;
painter . ForceUpdateGeometry = EditorGUILayout . Toggle ( GBaseGUI . FORCE_UPDATE_GEOMETRY , painter . ForceUpdateGeometry ) ;
Color txtColor = GUI . contentColor ;
Rect r0 = EditorGUILayout . GetControlRect ( ) ;
if ( ! GEditorSettings . Instance . paintTools . enableHistory )
{
GUI . contentColor = Color . red * 1.5f ;
}
GEditorSettings . Instance . paintTools . enableHistory = EditorGUI . Toggle ( r0 , GBaseGUI . ENABLE_HISTORY , GEditorSettings . Instance . paintTools . enableHistory ) ;
GUI . contentColor = txtColor ;
GEditorSettings . Instance . paintTools . enableLivePreview = EditorGUILayout . Toggle ( GBaseGUI . ENABLE_LIVE_PREVIEW , GEditorSettings . Instance . paintTools . enableLivePreview ) ;
2023-12-30 17:37:48 +08:00
bool isMaskPainterInUsed = painter . ActivePainter is GMaskPainter ;
if ( ! isMaskPainterInUsed )
{
2024-03-05 17:34:41 +08:00
painter . EnableTerrainMask = EditorGUILayout . Toggle ( GBaseGUI . ENABLE_TERRAIN_MASK , painter . EnableTerrainMask ) ;
if ( painter . EnableTerrainMask )
{
GEditorSettings . Instance . paintTools . showTerrainMask = EditorGUILayout . Toggle ( GBaseGUI . SHOW_TERRAIN_MASK , GEditorSettings . Instance . paintTools . showTerrainMask ) ;
}
2023-12-30 17:37:48 +08:00
}
2024-03-05 17:34:41 +08:00
GEditorSettings . Instance . topographic . enable = EditorGUILayout . Toggle ( GBaseGUI . ENABLE_TOPOGRAPHIC , GEditorSettings . Instance . topographic . enable ) ;
2023-12-30 17:37:48 +08:00
DrawPaintMode ( ) ;
IGTexturePainter p = painter . ActivePainter ;
if ( p = = null )
{
2024-03-05 17:34:41 +08:00
EditorGUILayout . LabelField ( GBaseGUI . NO_PAINTER_FOUND , GEditorCommon . WordWrapItalicLabel ) ;
2023-12-30 17:37:48 +08:00
}
else
{
DrawInstructionGUI ( ) ;
DrawBrushMaskGUI ( ) ;
if ( painter . Mode = = GTexturePaintingMode . Splat | | painter . Mode = = GTexturePaintingMode . Custom )
DrawSplatGUI ( ) ;
DrawBrushGUI ( ) ;
2024-03-05 17:34:41 +08:00
if ( p is IConditionalPainter )
DrawRulesGUI ( ) ;
if ( p is IGTexturePainterWithCustomParams )
2023-12-30 17:37:48 +08:00
{
IGTexturePainterWithCustomParams activePainter = painter . ActivePainter as IGTexturePainterWithCustomParams ;
activePainter . Editor_DrawCustomParamsGUI ( ) ;
}
#if GRIFFIN_VEGETATION_STUDIO_PRO
GEditorCommon . DrawVspIntegrationGUI ( ) ;
#endif
GEditorCommon . DrawBackupHelpBox ( ) ;
}
if ( EditorGUI . EndChangeCheck ( ) )
{
GUtilities . MarkCurrentSceneDirty ( ) ;
2024-03-05 17:34:41 +08:00
EditorUtility . SetDirty ( painter ) ;
EditorUtility . SetDirty ( GEditorSettings . Instance ) ;
2023-12-30 17:37:48 +08:00
}
}
public override bool RequiresConstantRepaint ( )
{
return false ;
}
2024-03-05 17:34:41 +08:00
private class GInstructionGUI
{
public static readonly string LABEL = "Instruction" ;
public static readonly string ID = "texture-painter-instruction" ;
}
2023-12-30 17:37:48 +08:00
private void DrawInstructionGUI ( )
{
2024-03-05 17:34:41 +08:00
GEditorCommon . Foldout ( GInstructionGUI . LABEL , true , GInstructionGUI . ID , ( ) = >
2023-12-30 17:37:48 +08:00
{
string text = null ;
try
{
text = painter . ActivePainter . Instruction ;
}
catch ( System . Exception e )
{
text = e . Message ;
}
EditorGUILayout . LabelField ( text , GEditorCommon . WordWrapItalicLabel ) ;
} ) ;
}
2024-03-05 17:34:41 +08:00
private class GBrushMaskGUI
2023-12-30 17:37:48 +08:00
{
2024-03-05 17:34:41 +08:00
public static readonly string LABEL = "Brush Masks" ;
public static readonly string ID = "texture-painter-brush-masks" ;
public static readonly GUIContent NEW_BRUSH = new GUIContent ( "New Brush" ) ;
public static readonly GUIContent REFRESH = new GUIContent ( "Refresh" ) ;
public static readonly string NEW_BRUSH_DIALOG_TITLE = "Info" ;
public static readonly string NEW_BRUSH_DIALOG_INSTRUCTION = "To add a new brush, copy your brush texture to a Resources/PolarisBrushes/ folder, then Refresh." ;
public static readonly string OK = "OK" ;
}
2023-12-30 17:37:48 +08:00
2024-03-05 17:34:41 +08:00
private void DrawBrushMaskGUI ( )
{
2023-12-30 17:37:48 +08:00
GenericMenu menu = new GenericMenu ( ) ;
menu . AddItem (
2024-03-05 17:34:41 +08:00
GBrushMaskGUI . NEW_BRUSH ,
2023-12-30 17:37:48 +08:00
false ,
DisplayNewBrushDialog ) ;
menu . AddItem (
2024-03-05 17:34:41 +08:00
GBrushMaskGUI . REFRESH ,
2023-12-30 17:37:48 +08:00
false ,
painter . ReloadBrushMasks ) ;
2024-03-05 17:34:41 +08:00
GEditorCommon . Foldout ( GBrushMaskGUI . LABEL , true , GBrushMaskGUI . ID , ( ) = >
2023-12-30 17:37:48 +08:00
{
GSelectionGridArgs args = new GSelectionGridArgs ( ) ;
args . selectedIndex = painter . SelectedBrushMaskIndex ;
args . collection = painter . BrushMasks ;
args . itemSize = GEditorCommon . selectionGridTileSizeSmall ;
args . itemPerRow = 4 ;
args . drawPreviewFunction = DrawBrushMaskPreview ;
painter . SelectedBrushMaskIndex = GEditorCommon . SelectionGrid ( args ) ;
} ,
menu ) ;
}
private void DisplayNewBrushDialog ( )
{
EditorUtility . DisplayDialog (
2024-03-05 17:34:41 +08:00
GBrushMaskGUI . NEW_BRUSH_DIALOG_TITLE ,
GBrushMaskGUI . NEW_BRUSH_DIALOG_INSTRUCTION ,
GBrushMaskGUI . OK ) ;
2023-12-30 17:37:48 +08:00
}
private void DrawBrushMaskPreview ( Rect r , object o )
{
Texture2D tex = ( Texture2D ) o ;
if ( tex ! = null )
{
EditorGUI . DrawPreviewTexture ( r , tex ) ;
}
else
{
EditorGUI . DrawRect ( r , Color . black ) ;
}
}
2024-03-05 17:34:41 +08:00
private class GSplatGUI
{
public static readonly string LABEL = "Splats" ;
public static readonly string ID = "texture-painter-splats" ;
public static readonly GUIContent MULTI_SELECTION = new GUIContent ( "Multi Selection" , "Select multiple splats by setting their probability" ) ;
}
2023-12-30 17:37:48 +08:00
private void DrawSplatGUI ( )
{
2024-03-05 17:34:41 +08:00
GEditorCommon . Foldout ( GSplatGUI . LABEL , true , GSplatGUI . ID , ( ) = >
2023-12-30 17:37:48 +08:00
{
if ( GEditorSettings . Instance . paintTools . useMultiSplatsSelector )
{
painter . SelectedSplatIndices = GEditorCommon . SplatSetMultiSelectionGrid ( painter . GroupId , painter . SelectedSplatIndices ) ;
}
else
{
painter . SelectedSplatIndex = GEditorCommon . SplatSetSelectionGrid ( painter . GroupId , painter . SelectedSplatIndex ) ;
}
2024-03-05 17:34:41 +08:00
GEditorSettings . Instance . paintTools . useMultiSplatsSelector = EditorGUILayout . Toggle ( GSplatGUI . MULTI_SELECTION , GEditorSettings . Instance . paintTools . useMultiSplatsSelector ) ;
2023-12-30 17:37:48 +08:00
EditorUtility . SetDirty ( GEditorSettings . Instance ) ;
EditorGUILayout . Space ( ) ;
} ) ;
}
2024-03-05 17:34:41 +08:00
private class GBrushGUI
{
public static readonly string LABEL = "Brush" ;
public static readonly string ID = "texture-painter-brush" ;
public static readonly GUIContent JITTER = new GUIContent ( "Jitter" , "Randomness factor for the value" ) ;
public static readonly GUIContent RADIUS = new GUIContent ( "Radius" , "Radius of the brush stroke. Shortcut: - =" ) ;
public static readonly GUIContent ROTATION = new GUIContent ( "Rotation" , "Rotation of the brush stroke. Shortcut: [ ]" ) ;
public static readonly GUIContent OPACITY = new GUIContent ( "Opacity" , "Opacity of the brush stroke. Shortcut: ; '" ) ;
public static readonly GUIContent TARGET_STRENGTH = new GUIContent ( "Target Strength" , "Maximum intensity of the brush" ) ;
public static readonly GUIContent SCATTER = new GUIContent ( "Scatter" , "Pick a random position for the brush around your cursor position" ) ;
public static readonly GUIContent COLOR = new GUIContent ( "Color" , "Color of the brush. Mainly used for Albedo, Metallic (R) and Smoothness (A) painter" ) ;
public static readonly GUIContent SAMPLE_POINT = new GUIContent ( "Sample Point" , "A point in the scene for sample height. Mainly use for Height Sampling (Y) painter." ) ;
public static readonly GUIContent TRIANGLE_SAMPLER = new GUIContent ( GEditorSkin . Instance . GetTexture ( "TriangleSamplerIcon" ) , "Sample terrain Albedo color, Metallic or Smoothness value based on the current paint mode" ) ;
}
2023-12-30 17:37:48 +08:00
private void DrawBrushGUI ( )
{
2024-03-05 17:34:41 +08:00
GEditorCommon . Foldout ( GBrushGUI . LABEL , true , GBrushGUI . ID , ( ) = >
2023-12-30 17:37:48 +08:00
{
2024-03-05 17:34:41 +08:00
painter . BrushRadius = EditorGUILayout . FloatField ( GBrushGUI . RADIUS , painter . BrushRadius ) ;
painter . BrushRadiusJitter = EditorGUILayout . Slider ( GBrushGUI . JITTER , painter . BrushRadiusJitter , 0f , 1f ) ;
2023-12-30 17:37:48 +08:00
GEditorCommon . Separator ( ) ;
2024-03-05 17:34:41 +08:00
painter . BrushRotation = EditorGUILayout . Slider ( GBrushGUI . ROTATION , painter . BrushRotation , - 360f , 360f ) ;
painter . BrushRotationJitter = EditorGUILayout . Slider ( GBrushGUI . JITTER , painter . BrushRotationJitter , 0f , 1f ) ;
2023-12-30 17:37:48 +08:00
GEditorCommon . Separator ( ) ;
2024-03-05 17:34:41 +08:00
Color txtColor = GUI . contentColor ;
2023-12-30 17:37:48 +08:00
Rect r0 = EditorGUILayout . GetControlRect ( ) ;
if ( painter . BrushOpacity < = 0 | | painter . BrushOpacity > = 1 )
{
2024-03-05 17:34:41 +08:00
GUI . contentColor = Color . red * 1.5f ;
2023-12-30 17:37:48 +08:00
}
2024-03-05 17:34:41 +08:00
painter . BrushOpacity = EditorGUI . Slider ( r0 , GBrushGUI . OPACITY , painter . BrushOpacity , 0f , 1f ) ;
GUI . contentColor = txtColor ;
painter . BrushOpacityJitter = EditorGUILayout . Slider ( GBrushGUI . JITTER , painter . BrushOpacityJitter , 0f , 1f ) ;
2023-12-30 17:37:48 +08:00
Rect r1 = EditorGUILayout . GetControlRect ( ) ;
if ( painter . BrushTargetStrength < = 0 )
{
2024-03-05 17:34:41 +08:00
GUI . contentColor = Color . red * 1.5f ;
2023-12-30 17:37:48 +08:00
}
2024-03-05 17:34:41 +08:00
painter . BrushTargetStrength = EditorGUI . Slider ( r1 , GBrushGUI . TARGET_STRENGTH , painter . BrushTargetStrength , 0f , 1f ) ;
GUI . contentColor = txtColor ;
2023-12-30 17:37:48 +08:00
GEditorCommon . Separator ( ) ;
2024-03-05 17:34:41 +08:00
painter . BrushScatter = EditorGUILayout . Slider ( GBrushGUI . SCATTER , painter . BrushScatter , 0f , 1f ) ;
painter . BrushScatterJitter = EditorGUILayout . Slider ( GBrushGUI . JITTER , painter . BrushScatterJitter , 0f , 1f ) ;
2023-12-30 17:37:48 +08:00
GEditorCommon . Separator ( ) ;
2024-03-05 17:34:41 +08:00
EditorGUILayout . BeginHorizontal ( ) ;
painter . BrushColor = EditorGUILayout . ColorField ( GBrushGUI . COLOR , painter . BrushColor ) ;
GUI . enabled =
painter . Mode = = GTexturePaintingMode . Albedo | |
painter . Mode = = GTexturePaintingMode . Metallic | |
painter . Mode = = GTexturePaintingMode . Smoothness ;
GUI . color = isColorSamplingEnable ? Color . white * 1.5f : Color . white ;
if ( GUILayout . Button ( GBrushGUI . TRIANGLE_SAMPLER , GUILayout . Width ( 50 ) , GUILayout . Height ( EditorGUIUtility . singleLineHeight ) ) )
{
isColorSamplingEnable = ! isColorSamplingEnable ;
}
GUI . enabled = true ;
GUI . color = Color . white ;
EditorGUILayout . EndHorizontal ( ) ;
painter . SamplePoint = GEditorCommon . InlineVector3Field ( GBrushGUI . SAMPLE_POINT , painter . SamplePoint ) ;
} ) ;
}
private class GRulesGUI
{
public static readonly string LABEL = "Rules" ;
public static readonly string ID = "texture-painter-rules" ;
public static readonly string HEIGHT_RULE_HEADER = "Height" ;
public static readonly GUIContent ENABLE_HEIGHT_RULE = new GUIContent ( "Enable" , "Enable height rule" ) ;
public static readonly GUIContent MIN_HEIGHT = new GUIContent ( "Min" , "Minimum height in meter for the painter to take effect" ) ;
public static readonly GUIContent MAX_HEIGHT = new GUIContent ( "Max" , "Maximum height in meter for the painter to take effect" ) ;
public static readonly GUIContent HEIGHT_TRANSITION = new GUIContent ( "Transition" , "Intensity of the height blend in the range of [min,max]" ) ;
public static readonly string SLOPE_RULE_HEADER = "Slope" ;
public static readonly GUIContent ENABLE_SLOPE_RULE = new GUIContent ( "Enable" , "Enable slope rule" ) ;
public static readonly GUIContent NORMAL_MAP_MODE = new GUIContent ( "Mode" , "Slope mode to use" ) ;
public static readonly GUIContent MIN_SLOPE = new GUIContent ( "Min" , "Minimum angle between surface normal and the up vector for the painter to take effect" ) ;
public static readonly GUIContent MAX_SLOPE = new GUIContent ( "Max" , "Maximum angle between surface normal and the up vector for the painter to take effect" ) ;
public static readonly GUIContent SLOPE_TRANSITION = new GUIContent ( "Transition" , "Intensity of the slope blend in the range of [min,max]" ) ;
public static readonly string NOISE_RULE_HEADER = "Noise" ;
public static readonly GUIContent ENABLE_NOISE_RULE = new GUIContent ( "Enable" , "Enable noise rule" ) ;
public static readonly GUIContent NOISE_ORIGIN = new GUIContent ( "Origin" , "The origin point of the noise map" ) ;
public static readonly GUIContent NOISE_FREQUENCY = new GUIContent ( "Frequency" , "Frequency of the noise pattern, higher value gives thicker noise" ) ;
public static readonly GUIContent NOISE_OCTAVES = new GUIContent ( "Octaves" , "The number of noise layers to stack on top of each others" ) ;
public static readonly GUIContent NOISE_LACUNARITY = new GUIContent ( "Lacunarity" , "The change in frequency of each noise layer" ) ;
public static readonly GUIContent NOISE_PERSISTENCE = new GUIContent ( "Persistence" , "The change in amplitude of each noise layer" ) ;
public static readonly GUIContent NOISE_REMAP = new GUIContent ( "Remap" , "Remap the generated noise value" ) ;
}
private void DrawRulesGUI ( )
{
GEditorCommon . Foldout ( GRulesGUI . LABEL , false , GRulesGUI . ID , ( ) = >
{
GConditionalPaintingConfigs configs = painter . ConditionalPaintingConfigs ;
GEditorCommon . Header ( GRulesGUI . HEIGHT_RULE_HEADER ) ;
configs . BlendHeight = EditorGUILayout . Toggle ( GRulesGUI . ENABLE_HEIGHT_RULE , configs . BlendHeight ) ;
configs . MinHeight = EditorGUILayout . FloatField ( GRulesGUI . MIN_HEIGHT , configs . MinHeight ) ;
configs . MaxHeight = EditorGUILayout . FloatField ( GRulesGUI . MAX_HEIGHT , configs . MaxHeight ) ;
EditorGUI . BeginChangeCheck ( ) ;
configs . HeightTransition = EditorGUILayout . CurveField ( GRulesGUI . HEIGHT_TRANSITION , configs . HeightTransition , Color . red , GCommon . UnitRect ) ;
if ( EditorGUI . EndChangeCheck ( ) )
{
configs . UpdateCurveTextures ( ) ;
}
GEditorCommon . Header ( GRulesGUI . SLOPE_RULE_HEADER ) ;
configs . BlendSlope = EditorGUILayout . Toggle ( GRulesGUI . ENABLE_SLOPE_RULE , configs . BlendSlope ) ;
configs . NormalMapMode = ( GNormalMapMode ) EditorGUILayout . EnumPopup ( GRulesGUI . NORMAL_MAP_MODE , configs . NormalMapMode ) ;
configs . MinSlope = EditorGUILayout . FloatField ( GRulesGUI . MIN_SLOPE , configs . MinSlope ) ;
configs . MaxSlope = EditorGUILayout . FloatField ( GRulesGUI . MAX_SLOPE , configs . MaxSlope ) ;
EditorGUI . BeginChangeCheck ( ) ;
configs . SlopeTransition = EditorGUILayout . CurveField ( GRulesGUI . SLOPE_TRANSITION , configs . SlopeTransition , Color . red , GCommon . UnitRect ) ;
if ( EditorGUI . EndChangeCheck ( ) )
{
configs . UpdateCurveTextures ( ) ;
}
GEditorCommon . Header ( GRulesGUI . NOISE_RULE_HEADER ) ;
configs . BlendNoise = EditorGUILayout . Toggle ( GRulesGUI . ENABLE_NOISE_RULE , configs . BlendNoise ) ;
configs . NoiseOrigin = GEditorCommon . InlineVector3Field ( GRulesGUI . NOISE_ORIGIN , configs . NoiseOrigin ) ;
configs . NoiseFrequency = EditorGUILayout . FloatField ( GRulesGUI . NOISE_FREQUENCY , configs . NoiseFrequency ) ;
configs . NoiseLacunarity = EditorGUILayout . FloatField ( GRulesGUI . NOISE_LACUNARITY , configs . NoiseLacunarity ) ;
configs . NoisePersistence = EditorGUILayout . Slider ( GRulesGUI . NOISE_PERSISTENCE , configs . NoisePersistence , 0.01f , 1f ) ;
configs . NoiseOctaves = EditorGUILayout . IntSlider ( GRulesGUI . NOISE_OCTAVES , configs . NoiseOctaves , 1 , 4 ) ;
EditorGUI . BeginChangeCheck ( ) ;
configs . NoiseRemap = EditorGUILayout . CurveField ( GRulesGUI . NOISE_REMAP , configs . NoiseRemap , Color . red , GCommon . UnitRect ) ;
if ( EditorGUI . EndChangeCheck ( ) )
{
configs . UpdateCurveTextures ( ) ;
}
2023-12-30 17:37:48 +08:00
} ) ;
}
private void DuringSceneGUI ( SceneView sv )
{
HandleTerrainEditingInSceneView ( ) ;
HandleBrushSettingsShortcuts ( ) ;
HandleFunctionKeys ( ) ;
if ( Event . current ! = null & & Event . current . type = = EventType . MouseMove )
SceneView . RepaintAll ( ) ;
}
private void HandleBrushSettingsShortcuts ( )
{
if ( Event . current ! = null & & Event . current . isKey )
{
KeyCode k = Event . current . keyCode ;
if ( k = = KeyCode . Equals )
{
painter . BrushRadius + = GEditorSettings . Instance . paintTools . radiusStep ;
}
else if ( k = = KeyCode . Minus )
{
painter . BrushRadius - = GEditorSettings . Instance . paintTools . radiusStep ;
}
else if ( k = = KeyCode . RightBracket )
{
painter . BrushRotation + = GEditorSettings . Instance . paintTools . rotationStep ;
}
else if ( k = = KeyCode . LeftBracket )
{
painter . BrushRotation - = GEditorSettings . Instance . paintTools . rotationStep ;
}
else if ( k = = KeyCode . Quote )
{
painter . BrushOpacity + = GEditorSettings . Instance . paintTools . opacityStep ;
}
else if ( k = = KeyCode . Semicolon )
{
painter . BrushOpacity - = GEditorSettings . Instance . paintTools . opacityStep ;
}
}
}
private void HandleFunctionKeys ( )
{
if ( Event . current ! = null & & Event . current . type = = EventType . KeyDown )
{
KeyCode k = Event . current . keyCode ;
if ( k > = KeyCode . F1 & & k < = KeyCode . F12 )
{
if ( painter . Mode ! = GTexturePaintingMode . Custom & &
( k - KeyCode . F1 ) ! = ( int ) GTexturePaintingMode . Custom )
{
painter . Mode = ( GTexturePaintingMode ) ( k - KeyCode . F1 ) ;
}
else
{
painter . CustomPainterIndex = k - KeyCode . F1 ;
}
}
}
}
private void HandleTerrainEditingInSceneView ( )
{
if ( ! painter . enabled )
return ;
if ( Event . current = = null )
return ;
if ( Event . current . alt = = true )
return ;
if ( Event . current . type ! = EventType . Repaint & &
Event . current . type ! = EventType . MouseDown & &
Event . current . type ! = EventType . MouseDrag & &
Event . current . type ! = EventType . MouseUp & &
Event . current . type ! = EventType . KeyDown )
return ;
int controlId = GUIUtility . GetControlID ( this . GetHashCode ( ) , FocusType . Passive ) ;
if ( Event . current . type = = EventType . MouseDown )
{
if ( Event . current . button = = 0 )
{
//Set the hot control to this tool, to disable marquee selection tool on mouse dragging
GUIUtility . hotControl = controlId ;
}
}
else if ( Event . current . type = = EventType . MouseUp )
{
GRuntimeSettings . Instance . isEditingGeometry = false ;
if ( GUIUtility . hotControl = = controlId )
{
//Return the hot control back to Unity, use the default
GUIUtility . hotControl = 0 ;
}
}
Ray r = HandleUtility . GUIPointToWorldRay ( Event . current . mousePosition ) ;
RaycastHit hit ;
if ( GStylizedTerrain . Raycast ( r , out hit , float . MaxValue , painter . GroupId ) )
{
OnRaycast ( true , hit ) ;
}
else
{
OnRaycast ( false , hit ) ;
}
if ( GGuiEventUtilities . IsLeftMouse )
{
Event . current . Use ( ) ;
}
}
private void OnRaycast ( bool isHit , RaycastHit hitInfo )
{
if ( isHit )
{
2024-03-05 17:34:41 +08:00
if ( GEditorSettings . Instance . paintTools . enableLivePreview & &
2023-12-30 17:37:48 +08:00
painter . ActivePainter ! = null & &
painter . ActivePainter is IGTexturePainterWithLivePreview )
{
2024-03-05 17:34:41 +08:00
2023-12-30 17:37:48 +08:00
}
else
{
2024-03-05 17:34:41 +08:00
if ( ! isColorSamplingEnable )
{
DrawHandleAtCursor ( hitInfo ) ;
}
2023-12-30 17:37:48 +08:00
}
2024-03-05 17:34:41 +08:00
if ( GGuiEventUtilities . IsLeftMouseUp & & isColorSamplingEnable )
{
SampleColor ( hitInfo ) ;
isColorSamplingEnable = false ;
}
if ( GGuiEventUtilities . IsLeftMouse & & ! isColorSamplingEnable )
2023-12-30 17:37:48 +08:00
{
Paint ( hitInfo ) ;
}
}
}
2024-03-05 17:34:41 +08:00
private void SampleColor ( RaycastHit hit )
{
GStylizedTerrain t = hit . collider . GetComponentInParent < GStylizedTerrain > ( ) ;
if ( t = = null )
return ;
if ( t . TerrainData = = null )
return ;
Texture2D textureToSample =
painter . Mode = = GTexturePaintingMode . Albedo ? t . TerrainData . Shading . AlbedoMapOrDefault :
painter . Mode = = GTexturePaintingMode . Metallic ? t . TerrainData . Shading . MetallicMapOrDefault :
painter . Mode = = GTexturePaintingMode . Smoothness ? t . TerrainData . Shading . MetallicMapOrDefault :
null ;
if ( textureToSample = = null )
return ;
painter . BrushColor = textureToSample . GetPixelBilinear ( hit . textureCoord . x , hit . textureCoord . y ) . gamma ;
}
2023-12-30 17:37:48 +08:00
private GTexturePainterArgs CreateBasicArgs ( RaycastHit hit )
{
GTexturePainterArgs args = new GTexturePainterArgs ( ) ;
2024-03-05 17:34:41 +08:00
args . HitPoint = hit . point ;
2023-12-30 17:37:48 +08:00
args . MouseEventType =
Event . current . type = = EventType . MouseDown ? GPainterMouseEventType . Down :
Event . current . type = = EventType . MouseDrag ? GPainterMouseEventType . Drag :
GPainterMouseEventType . Up ;
args . ActionType =
Event . current . shift ? GPainterActionType . Alternative :
Event . current . control ? GPainterActionType . Negative :
GPainterActionType . Normal ;
return args ;
}
private void Paint ( RaycastHit hit )
{
GTexturePainterArgs args = CreateBasicArgs ( hit ) ;
painter . Paint ( args ) ;
}
2024-03-05 17:34:41 +08:00
private class GCursorHandleGUI
{
public static readonly int COLOR = Shader . PropertyToID ( "_Color" ) ;
public static readonly int MAIN_TEX = Shader . PropertyToID ( "_MainTex" ) ;
public static readonly int WORLD_TO_CURSOR = Shader . PropertyToID ( "_WorldToCursorMatrix" ) ;
}
2023-12-30 17:37:48 +08:00
private void DrawHandleAtCursor ( RaycastHit hit )
{
Color cursorColor =
Event . current . shift ? GEditorSettings . Instance . paintTools . alternativeActionCursorColor :
Event . current . control ? GEditorSettings . Instance . paintTools . negativeActionCursorColor :
GEditorSettings . Instance . paintTools . normalActionCursorColor ;
cursorColor . a = cursorColor . a * Mathf . Lerp ( 0.5f , 1f , painter . BrushOpacity ) ;
if ( GEditorSettings . Instance . paintTools . useSimpleCursor )
{
Handles . color = cursorColor ;
Vector3 [ ] corner = GCommon . GetBrushQuadCorners ( hit . point , painter . BrushRadius , painter . BrushRotation ) ;
Handles . DrawAAPolyLine ( 5 , corner [ 0 ] , corner [ 1 ] , corner [ 2 ] , corner [ 3 ] , corner [ 0 ] ) ;
}
else
{
Matrix4x4 cursorToWorld = Matrix4x4 . TRS ( hit . point , Quaternion . Euler ( 0 , painter . BrushRotation , 0 ) , 2 * painter . BrushRadius * Vector3 . one ) ;
worldPoints [ 0 ] = cursorToWorld . MultiplyPoint ( new Vector3 ( - 0.5f , 0 , - 0.5f ) ) ;
worldPoints [ 1 ] = cursorToWorld . MultiplyPoint ( new Vector3 ( - 0.5f , 0 , 0.5f ) ) ;
worldPoints [ 2 ] = cursorToWorld . MultiplyPoint ( new Vector3 ( 0.5f , 0 , 0.5f ) ) ;
worldPoints [ 3 ] = cursorToWorld . MultiplyPoint ( new Vector3 ( 0.5f , 0 , - 0.5f ) ) ;
Material mat = GInternalMaterials . PainterCursorProjectorMaterial ;
2024-03-05 17:34:41 +08:00
mat . SetColor ( GCursorHandleGUI . COLOR , cursorColor ) ;
mat . SetTexture ( GCursorHandleGUI . MAIN_TEX , painter . BrushMasks [ painter . SelectedBrushMaskIndex ] ) ;
mat . SetMatrix ( GCursorHandleGUI . WORLD_TO_CURSOR , cursorToWorld . inverse ) ;
2023-12-30 17:37:48 +08:00
mat . SetPass ( 0 ) ;
2024-03-05 17:34:41 +08:00
List < GOverlapTestResult > overlapTest = GPaintToolUtilities . OverlapTest ( painter . GroupId , hit . point , painter . BrushRadius , painter . BrushRotation ) ;
foreach ( GOverlapTestResult test in overlapTest )
2023-12-30 17:37:48 +08:00
{
2024-03-05 17:34:41 +08:00
if ( test . IsOverlapped )
{
DrawCursorProjected ( test . Terrain , test . IsChunkOverlapped ) ;
}
2023-12-30 17:37:48 +08:00
}
}
}
2024-03-05 17:34:41 +08:00
private void DrawCursorProjected ( GStylizedTerrain t , bool [ ] chunkCulling )
2023-12-30 17:37:48 +08:00
{
GTerrainChunk [ ] chunks = t . GetChunks ( ) ;
for ( int i = 0 ; i < chunks . Length ; + + i )
{
2024-03-05 17:34:41 +08:00
if ( chunkCulling [ i ] = = false )
2023-12-30 17:37:48 +08:00
continue ;
2024-03-05 17:34:41 +08:00
Mesh m = chunks [ i ] . GetMesh ( 0 ) ;
2023-12-30 17:37:48 +08:00
Graphics . DrawMeshNow (
m ,
2024-03-05 17:34:41 +08:00
chunks [ i ] . transform . localToWorldMatrix ) ;
2023-12-30 17:37:48 +08:00
}
}
2024-03-05 17:34:41 +08:00
private class GPaintModeGUI
2023-12-30 17:37:48 +08:00
{
2024-03-05 17:34:41 +08:00
public static readonly string LABEL = "Mode" ;
public static readonly string ID = "texture-painter-mode" ;
2023-12-30 17:37:48 +08:00
2024-03-05 17:34:41 +08:00
public static readonly GUIContent NO_CUSTOM_PAINTER = new GUIContent ( "No Custom Painter defined!" ) ;
public static readonly GUIContent CUSTOM_ARGS = new GUIContent ( "Custom Args" ) ;
}
2023-12-30 17:37:48 +08:00
2024-03-05 17:34:41 +08:00
private void DrawPaintMode ( )
{
GEditorCommon . Foldout ( GPaintModeGUI . LABEL , true , GPaintModeGUI . ID , ( ) = >
2023-12-30 17:37:48 +08:00
{
2024-03-05 17:34:41 +08:00
ShowPaintModeAsGrid ( ) ;
} ) ;
2023-12-30 17:37:48 +08:00
}
private void ShowPaintModeAsGrid ( )
{
GSelectionGridArgs args0 = new GSelectionGridArgs ( ) ;
args0 . selectedIndex = ( int ) painter . Mode ;
args0 . collection = Enum . GetValues ( typeof ( GTexturePaintingMode ) ) ;
args0 . itemSize = GEditorCommon . selectionGridTileSizeWide ;
args0 . itemPerRow = 2 ;
args0 . drawPreviewFunction = DrawModePreview ;
EditorGUI . BeginChangeCheck ( ) ;
painter . Mode = ( GTexturePaintingMode ) GEditorCommon . SelectionGrid ( args0 ) ;
if ( EditorGUI . EndChangeCheck ( ) )
{
}
if ( painter . Mode = = GTexturePaintingMode . Custom )
{
GEditorCommon . Separator ( ) ;
List < Type > customPainterTypes = GTerrainTexturePainter . GetCustomPainterTypes ( ) ;
if ( customPainterTypes . Count = = 0 )
{
2024-03-05 17:34:41 +08:00
EditorGUILayout . LabelField ( GPaintModeGUI . NO_CUSTOM_PAINTER , GEditorCommon . WordWrapItalicLabel ) ;
2023-12-30 17:37:48 +08:00
}
else
{
GSelectionGridArgs args1 = new GSelectionGridArgs ( ) ;
args1 . selectedIndex = painter . CustomPainterIndex ;
args1 . collection = customPainterTypes ;
args1 . itemSize = GEditorCommon . selectionGridTileSizeWide ;
args1 . itemPerRow = 2 ;
args1 . drawPreviewFunction = DrawCustomMode ;
painter . CustomPainterIndex = GEditorCommon . SelectionGrid ( args1 ) ;
GEditorCommon . Separator ( ) ;
2024-03-05 17:34:41 +08:00
painter . CustomPainterArgs = EditorGUILayout . TextField ( GPaintModeGUI . CUSTOM_ARGS , painter . CustomPainterArgs ) ;
2023-12-30 17:37:48 +08:00
}
}
}
private void DrawModePreview ( Rect r , object o )
{
GTexturePaintingMode mode = ( GTexturePaintingMode ) o ;
Texture2D icon = GEditorSkin . Instance . GetTexture ( mode . ToString ( ) + "Icon" ) ;
if ( icon = = null )
{
icon = GEditorSkin . Instance . GetTexture ( "GearIcon" ) ;
}
string label = ObjectNames . NicifyVariableName ( mode . ToString ( ) ) ;
DrawMode ( r , label , icon ) ;
}
private void DrawCustomMode ( Rect r , object o )
{
Type t = ( Type ) o ;
if ( t ! = null )
{
string label = ObjectNames . NicifyVariableName ( GEditorCommon . GetClassDisplayName ( t ) ) ;
Texture2D icon = GEditorSkin . Instance . GetTexture ( "GearIcon" ) ;
DrawMode ( r , label , icon ) ;
}
}
private void DrawMode ( Rect r , string label , Texture icon )
{
GUIStyle labelStyle = EditorStyles . miniLabel ;
Rect iconRect = new Rect ( r . min . x , r . min . y , r . height , r . height ) ;
Rect labelRect = new Rect ( r . min . x + r . height + 1 , r . min . y , r . width - r . height , r . height ) ;
GEditorCommon . DrawBodyBox ( r ) ;
GEditorCommon . DrawHeaderBox ( iconRect ) ;
if ( icon ! = null )
{
GUI . color = labelStyle . normal . textColor ;
RectOffset offset = new RectOffset ( 3 , 3 , 3 , 3 ) ;
GUI . DrawTexture ( offset . Remove ( iconRect ) , icon ) ;
GUI . color = Color . white ;
}
int indent = EditorGUI . indentLevel ;
EditorGUI . indentLevel = 0 ;
GUI . Label ( labelRect , label , labelStyle ) ;
EditorGUI . indentLevel = indent ;
}
private void OnBeginRender ( Camera cam )
{
if ( cam . cameraType ! = CameraType . SceneView )
return ;
2024-03-05 17:34:41 +08:00
if ( isColorSamplingEnable )
return ;
if ( GEditorSettings . Instance . paintTools . enableLivePreview )
2023-12-30 17:37:48 +08:00
{
bool canDrawLivePreview = painter . ActivePainter ! = null & & painter . ActivePainter is IGTexturePainterWithLivePreview ;
if ( canDrawLivePreview )
{
Ray r = HandleUtility . GUIPointToWorldRay ( Event . current . mousePosition ) ;
RaycastHit hit ;
if ( GStylizedTerrain . Raycast ( r , out hit , float . MaxValue , painter . GroupId ) )
{
DrawLivePreview ( cam , hit ) ;
}
}
}
if ( painter . EnableTerrainMask )
{
DrawMask ( cam ) ;
}
}
private void OnBeginRenderSRP ( UnityEngine . Rendering . ScriptableRenderContext context , Camera cam )
{
OnBeginRender ( cam ) ;
}
private void DrawLivePreview ( Camera cam , RaycastHit hitInfo )
{
bool canDrawLivePreview = painter . ActivePainter ! = null & & painter . ActivePainter is IGTexturePainterWithLivePreview ;
if ( ! canDrawLivePreview )
return ;
IGTexturePainterWithLivePreview activePainter = painter . ActivePainter as IGTexturePainterWithLivePreview ;
GTexturePainterArgs args = CreateBasicArgs ( hitInfo ) ;
painter . FillArgs ( ref args , true ) ;
2024-03-05 17:34:41 +08:00
List < GOverlapTestResult > overlapTest = GPaintToolUtilities . OverlapTest ( painter . GroupId , hitInfo . point , painter . BrushRadius , painter . BrushRotation ) ;
foreach ( GOverlapTestResult test in overlapTest )
2023-12-30 17:37:48 +08:00
{
2024-03-05 17:34:41 +08:00
if ( test . IsOverlapped )
{
activePainter . Editor_DrawLivePreview ( test . Terrain , args , cam ) ;
}
2023-12-30 17:37:48 +08:00
}
}
private void DrawMask ( Camera cam )
{
GCommon . ForEachTerrain ( painter . GroupId , ( t ) = >
{
if ( t . TerrainData = = null )
return ;
if ( painter . ActivePainter is GMaskPainter )
{
if ( GTexturePainterCustomParams . Instance . Mask . Visualize )
{
GLivePreviewDrawer . DrawMask4ChannelsLivePreview ( t , cam , t . TerrainData . Mask . MaskMapOrDefault , GCommon . UnitRect ) ;
}
}
else
{
2024-03-05 17:34:41 +08:00
if ( GEditorSettings . Instance . paintTools . showTerrainMask )
GLivePreviewDrawer . DrawTerrainMask ( t , cam ) ;
2023-12-30 17:37:48 +08:00
}
} ) ;
}
}
}
2024-03-05 17:34:41 +08:00
#endif