补齐备份工程打开依赖

This commit is contained in:
JSD\13999
2026-05-26 17:55:41 +08:00
parent e81881ad78
commit 43b4650ee3
433 changed files with 49783 additions and 3 deletions

View File

@@ -1,2 +1,8 @@
fileFormatVersion: 2
guid: 52abdb4a4468700439c02356e8f3eb64
guid: 52abdb4a4468700439c02356e8f3eb64
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 3c57523b63ddb094b835b6613da12763
folderAsset: yes
timeCreated: 1596819199
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 3d7d7a61a5341904eb3c65af025b1d86
folderAsset: yes
timeCreated: 1510075633
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
#if !UNITY_EDITOR && UNITY_ANDROID
using System.Collections.Generic;
using UnityEngine;
// Credit: https://stackoverflow.com/a/41018028/2373034
namespace IngameDebugConsole
{
public class DebugLogLogcatListener : AndroidJavaProxy
{
private Queue<string> queuedLogs;
private AndroidJavaObject nativeObject;
public DebugLogLogcatListener() : base( "com.yasirkula.unity.DebugConsoleLogcatLogReceiver" )
{
queuedLogs = new Queue<string>( 16 );
}
~DebugLogLogcatListener()
{
Stop();
if( nativeObject != null )
nativeObject.Dispose();
}
public void Start( string arguments )
{
if( nativeObject == null )
nativeObject = new AndroidJavaObject( "com.yasirkula.unity.DebugConsoleLogcatLogger" );
nativeObject.Call( "Start", this, arguments );
}
public void Stop()
{
if( nativeObject != null )
nativeObject.Call( "Stop" );
}
public void OnLogReceived( string log )
{
queuedLogs.Enqueue( log );
}
public string GetLog()
{
if( queuedLogs.Count > 0 )
return queuedLogs.Dequeue();
return null;
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: dd3b7385882055d4a8c2b91deb6b2470
timeCreated: 1510076185
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: bf909fab1c14af446b0a854de42289b2
timeCreated: 1510086220
licenseType: Free
PluginImporter:
serializedVersion: 2
iconMap: {}
executionOrder: {}
isPreloaded: 0
isOverridable: 0
platformData:
data:
first:
Android: Android
second:
enabled: 1
settings: {}
data:
first:
Any:
second:
enabled: 0
settings: {}
data:
first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 86f54622630720f4abe279acdbb8886f
folderAsset: yes
timeCreated: 1561217660
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,160 @@
using UnityEditor;
using UnityEngine;
namespace IngameDebugConsole
{
[CustomEditor( typeof( DebugLogManager ) )]
public class DebugLogManagerEditor : Editor
{
private SerializedProperty singleton;
private SerializedProperty minimumHeight;
private SerializedProperty enableHorizontalResizing;
private SerializedProperty resizeFromRight;
private SerializedProperty minimumWidth;
private SerializedProperty enablePopup;
private SerializedProperty startInPopupMode;
private SerializedProperty startMinimized;
private SerializedProperty toggleWithKey;
private SerializedProperty toggleKey;
private SerializedProperty enableSearchbar;
private SerializedProperty topSearchbarMinWidth;
private SerializedProperty receiveLogsWhileInactive;
private SerializedProperty receiveInfoLogs;
private SerializedProperty receiveWarningLogs;
private SerializedProperty receiveErrorLogs;
private SerializedProperty receiveExceptionLogs;
private SerializedProperty captureLogTimestamps;
private SerializedProperty alwaysDisplayTimestamps;
private SerializedProperty queuedLogLimit;
private SerializedProperty clearCommandAfterExecution;
private SerializedProperty commandHistorySize;
private SerializedProperty showCommandSuggestions;
private SerializedProperty receiveLogcatLogsInAndroid;
private SerializedProperty logcatArguments;
private SerializedProperty avoidScreenCutout;
private SerializedProperty popupAvoidsScreenCutout;
private SerializedProperty autoFocusOnCommandInputField;
private readonly GUIContent receivedLogTypesLabel = new GUIContent( "Received Log Types", "Only these logs will be received by the console window, other logs will simply be skipped" );
private readonly GUIContent receiveInfoLogsLabel = new GUIContent( "Info" );
private readonly GUIContent receiveWarningLogsLabel = new GUIContent( "Warning" );
private readonly GUIContent receiveErrorLogsLabel = new GUIContent( "Error" );
private readonly GUIContent receiveExceptionLogsLabel = new GUIContent( "Exception" );
private void OnEnable()
{
singleton = serializedObject.FindProperty( "singleton" );
minimumHeight = serializedObject.FindProperty( "minimumHeight" );
enableHorizontalResizing = serializedObject.FindProperty( "enableHorizontalResizing" );
resizeFromRight = serializedObject.FindProperty( "resizeFromRight" );
minimumWidth = serializedObject.FindProperty( "minimumWidth" );
enablePopup = serializedObject.FindProperty( "enablePopup" );
startInPopupMode = serializedObject.FindProperty( "startInPopupMode" );
startMinimized = serializedObject.FindProperty( "startMinimized" );
toggleWithKey = serializedObject.FindProperty( "toggleWithKey" );
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
toggleKey = serializedObject.FindProperty( "toggleBinding" );
#else
toggleKey = serializedObject.FindProperty( "toggleKey" );
#endif
enableSearchbar = serializedObject.FindProperty( "enableSearchbar" );
topSearchbarMinWidth = serializedObject.FindProperty( "topSearchbarMinWidth" );
receiveLogsWhileInactive = serializedObject.FindProperty( "receiveLogsWhileInactive" );
receiveInfoLogs = serializedObject.FindProperty( "receiveInfoLogs" );
receiveWarningLogs = serializedObject.FindProperty( "receiveWarningLogs" );
receiveErrorLogs = serializedObject.FindProperty( "receiveErrorLogs" );
receiveExceptionLogs = serializedObject.FindProperty( "receiveExceptionLogs" );
captureLogTimestamps = serializedObject.FindProperty( "captureLogTimestamps" );
alwaysDisplayTimestamps = serializedObject.FindProperty( "alwaysDisplayTimestamps" );
queuedLogLimit = serializedObject.FindProperty( "queuedLogLimit" );
clearCommandAfterExecution = serializedObject.FindProperty( "clearCommandAfterExecution" );
commandHistorySize = serializedObject.FindProperty( "commandHistorySize" );
showCommandSuggestions = serializedObject.FindProperty( "showCommandSuggestions" );
receiveLogcatLogsInAndroid = serializedObject.FindProperty( "receiveLogcatLogsInAndroid" );
logcatArguments = serializedObject.FindProperty( "logcatArguments" );
avoidScreenCutout = serializedObject.FindProperty( "avoidScreenCutout" );
popupAvoidsScreenCutout = serializedObject.FindProperty( "popupAvoidsScreenCutout" );
autoFocusOnCommandInputField = serializedObject.FindProperty( "autoFocusOnCommandInputField" );
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField( singleton );
EditorGUILayout.Space();
EditorGUILayout.PropertyField( minimumHeight );
EditorGUILayout.PropertyField( enableHorizontalResizing );
if( enableHorizontalResizing.boolValue )
{
DrawSubProperty( resizeFromRight );
DrawSubProperty( minimumWidth );
}
EditorGUILayout.PropertyField( avoidScreenCutout );
DrawSubProperty( popupAvoidsScreenCutout );
EditorGUILayout.Space();
EditorGUILayout.PropertyField( enablePopup );
if( enablePopup.boolValue )
DrawSubProperty( startInPopupMode );
else
DrawSubProperty( startMinimized );
EditorGUILayout.PropertyField( toggleWithKey );
if( toggleWithKey.boolValue )
DrawSubProperty( toggleKey );
EditorGUILayout.Space();
EditorGUILayout.PropertyField( enableSearchbar );
if( enableSearchbar.boolValue )
DrawSubProperty( topSearchbarMinWidth );
EditorGUILayout.Space();
EditorGUILayout.PropertyField( receiveLogsWhileInactive );
EditorGUILayout.PrefixLabel( receivedLogTypesLabel );
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField( receiveInfoLogs, receiveInfoLogsLabel );
EditorGUILayout.PropertyField( receiveWarningLogs, receiveWarningLogsLabel );
EditorGUILayout.PropertyField( receiveErrorLogs, receiveErrorLogsLabel );
EditorGUILayout.PropertyField( receiveExceptionLogs, receiveExceptionLogsLabel );
EditorGUI.indentLevel--;
EditorGUILayout.PropertyField( receiveLogcatLogsInAndroid );
if( receiveLogcatLogsInAndroid.boolValue )
DrawSubProperty( logcatArguments );
EditorGUILayout.PropertyField( captureLogTimestamps );
if( captureLogTimestamps.boolValue )
DrawSubProperty( alwaysDisplayTimestamps );
EditorGUILayout.PropertyField( queuedLogLimit );
EditorGUILayout.Space();
EditorGUILayout.PropertyField( clearCommandAfterExecution );
EditorGUILayout.PropertyField( commandHistorySize );
EditorGUILayout.PropertyField( showCommandSuggestions );
EditorGUILayout.PropertyField( autoFocusOnCommandInputField );
EditorGUILayout.Space();
DrawPropertiesExcluding( serializedObject, "m_Script" );
serializedObject.ApplyModifiedProperties();
}
private void DrawSubProperty( SerializedProperty property )
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField( property );
EditorGUI.indentLevel--;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4c23e5c521cb0c54b9a638b2a653d1d3
timeCreated: 1561217671
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
{
"name": "IngameDebugConsole.Editor",
"references": [
"IngameDebugConsole.Runtime"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 466e67dabd1db22468246c39eddb6c3f
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,6 @@
{
"name": "IngameDebugConsole.Runtime",
"references": [
"Unity.InputSystem"
]
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3de88c88fbbb8f944b9210d496af9762
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
= In-game Debug Console (v1.6.2) =
Documentation: https://github.com/yasirkula/UnityIngameDebugConsole
FAQ: https://github.com/yasirkula/UnityIngameDebugConsole#faq
E-mail: yasirkula@gmail.com
You can simply place the IngameDebugConsole prefab to your scene. Hovering the cursor over its properties in the Inspector will reveal explanatory tooltips.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: edf2ac73f7bc3064c96d53009106dc53
timeCreated: 1563307881
licenseType: Free
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 860c08388401a6d4e858fe4910ea9337
folderAsset: yes
timeCreated: 1465930645
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,119 @@
// #define RESET_REMOVED_ELEMENTS
namespace IngameDebugConsole
{
public class CircularBuffer<T>
{
private T[] arr;
private int startIndex;
public int Count { get; private set; }
public T this[int index] { get { return arr[( startIndex + index ) % arr.Length]; } }
public CircularBuffer( int capacity )
{
arr = new T[capacity];
}
// Old elements are overwritten when capacity is reached
public void Add( T value )
{
if( Count < arr.Length )
arr[Count++] = value;
else
{
arr[startIndex] = value;
if( ++startIndex >= arr.Length )
startIndex = 0;
}
}
}
public class DynamicCircularBuffer<T>
{
private T[] arr;
private int startIndex;
public int Count { get; private set; }
public int Capacity { get { return arr.Length; } }
public T this[int index]
{
get { return arr[( startIndex + index ) % arr.Length]; }
set { arr[( startIndex + index ) % arr.Length] = value; }
}
public DynamicCircularBuffer( int initialCapacity = 2 )
{
arr = new T[initialCapacity];
}
public void Add( T value )
{
if( Count >= arr.Length )
{
int prevSize = arr.Length;
int newSize = prevSize > 0 ? prevSize * 2 : 2; // Size must be doubled (at least), or the shift operation below must consider IndexOutOfRange situations
System.Array.Resize( ref arr, newSize );
if( startIndex > 0 )
{
if( startIndex <= ( prevSize - 1 ) / 2 )
{
// Move elements [0,startIndex) to the end
for( int i = 0; i < startIndex; i++ )
{
arr[i + prevSize] = arr[i];
#if RESET_REMOVED_ELEMENTS
arr[i] = default( T );
#endif
}
}
else
{
// Move elements [startIndex,prevSize) to the end
int delta = newSize - prevSize;
for( int i = prevSize - 1; i >= startIndex; i-- )
{
arr[i + delta] = arr[i];
#if RESET_REMOVED_ELEMENTS
arr[i] = default( T );
#endif
}
startIndex += delta;
}
}
}
this[Count++] = value;
}
public T RemoveFirst()
{
T element = arr[startIndex];
#if RESET_REMOVED_ELEMENTS
arr[startIndex] = default( T );
#endif
if( ++startIndex >= arr.Length )
startIndex = 0;
Count--;
return element;
}
public T RemoveLast()
{
int index = ( startIndex + Count - 1 ) % arr.Length;
T element = arr[index];
#if RESET_REMOVED_ELEMENTS
arr[index] = default( T );
#endif
Count--;
return element;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6136cb3c00eac0149901b8e7f2fecef8
timeCreated: 1550943949
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bb9b6e1ab379cec46bfae8f8abcc1f45
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
using UnityEngine;
namespace IngameDebugConsole.Commands
{
public class PlayerPrefsCommands
{
[ConsoleMethod( "prefs.int", "Returns the value of an Integer PlayerPrefs field" ), UnityEngine.Scripting.Preserve]
public static string PlayerPrefsGetInt( string key )
{
if( !PlayerPrefs.HasKey( key ) ) return "Key Not Found";
return PlayerPrefs.GetInt( key ).ToString();
}
[ConsoleMethod( "prefs.int", "Sets the value of an Integer PlayerPrefs field" ), UnityEngine.Scripting.Preserve]
public static void PlayerPrefsSetInt( string key, int value )
{
PlayerPrefs.SetInt( key, value );
}
[ConsoleMethod( "prefs.float", "Returns the value of a Float PlayerPrefs field" ), UnityEngine.Scripting.Preserve]
public static string PlayerPrefsGetFloat( string key )
{
if( !PlayerPrefs.HasKey( key ) ) return "Key Not Found";
return PlayerPrefs.GetFloat( key ).ToString();
}
[ConsoleMethod( "prefs.float", "Sets the value of a Float PlayerPrefs field" ), UnityEngine.Scripting.Preserve]
public static void PlayerPrefsSetFloat( string key, float value )
{
PlayerPrefs.SetFloat( key, value );
}
[ConsoleMethod( "prefs.string", "Returns the value of a String PlayerPrefs field" ), UnityEngine.Scripting.Preserve]
public static string PlayerPrefsGetString( string key )
{
if( !PlayerPrefs.HasKey( key ) ) return "Key Not Found";
return PlayerPrefs.GetString( key );
}
[ConsoleMethod( "prefs.string", "Sets the value of a String PlayerPrefs field" ), UnityEngine.Scripting.Preserve]
public static void PlayerPrefsSetString( string key, string value )
{
PlayerPrefs.SetString( key, value );
}
[ConsoleMethod( "prefs.delete", "Deletes a PlayerPrefs field" ), UnityEngine.Scripting.Preserve]
public static void PlayerPrefsDelete( string key )
{
PlayerPrefs.DeleteKey( key );
}
[ConsoleMethod( "prefs.clear", "Deletes all PlayerPrefs fields" ), UnityEngine.Scripting.Preserve]
public static void PlayerPrefsClear()
{
PlayerPrefs.DeleteAll();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 33fb3ee25c8764f4c905fa3ac7c4eb89
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
using UnityEngine;
using UnityEngine.SceneManagement;
namespace IngameDebugConsole.Commands
{
public class SceneCommands
{
[ConsoleMethod( "scene.load", "Loads a scene" ), UnityEngine.Scripting.Preserve]
public static void LoadScene( string sceneName )
{
LoadSceneInternal( sceneName, false, LoadSceneMode.Single );
}
[ConsoleMethod( "scene.load", "Loads a scene" ), UnityEngine.Scripting.Preserve]
public static void LoadScene( string sceneName, LoadSceneMode mode )
{
LoadSceneInternal( sceneName, false, mode );
}
[ConsoleMethod( "scene.loadasync", "Loads a scene asynchronously" ), UnityEngine.Scripting.Preserve]
public static void LoadSceneAsync( string sceneName )
{
LoadSceneInternal( sceneName, true, LoadSceneMode.Single );
}
[ConsoleMethod( "scene.loadasync", "Loads a scene asynchronously" ), UnityEngine.Scripting.Preserve]
public static void LoadSceneAsync( string sceneName, LoadSceneMode mode )
{
LoadSceneInternal( sceneName, true, mode );
}
private static void LoadSceneInternal( string sceneName, bool isAsync, LoadSceneMode mode )
{
if( SceneManager.GetSceneByName( sceneName ).IsValid() )
{
Debug.Log( "Scene " + sceneName + " is already loaded" );
return;
}
if( isAsync )
SceneManager.LoadSceneAsync( sceneName, mode );
else
SceneManager.LoadScene( sceneName, mode );
}
[ConsoleMethod( "scene.unload", "Unloads a scene" ), UnityEngine.Scripting.Preserve]
public static void UnloadScene( string sceneName )
{
SceneManager.UnloadSceneAsync( sceneName );
}
[ConsoleMethod( "scene.restart", "Restarts the active scene" ), UnityEngine.Scripting.Preserve]
public static void RestartScene()
{
SceneManager.LoadScene( SceneManager.GetActiveScene().name, LoadSceneMode.Single );
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 45984eacd62d9a3489fd62689265a23c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
using UnityEngine;
namespace IngameDebugConsole.Commands
{
public class TimeCommands
{
[ConsoleMethod( "time.scale", "Sets the Time.timeScale value" ), UnityEngine.Scripting.Preserve]
public static void SetTimeScale( float value )
{
Time.timeScale = Mathf.Max( value, 0f );
}
[ConsoleMethod( "time.scale", "Returns the current Time.timeScale value" ), UnityEngine.Scripting.Preserve]
public static float GetTimeScale()
{
return Time.timeScale;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb12a1f557fffa541909fcfe92d9c1bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
using System;
namespace IngameDebugConsole
{
[AttributeUsage( AttributeTargets.Method, Inherited = false, AllowMultiple = true )]
public class ConsoleMethodAttribute : Attribute
{
private string m_command;
private string m_description;
private string[] m_parameterNames;
public string Command { get { return m_command; } }
public string Description { get { return m_description; } }
public string[] ParameterNames { get { return m_parameterNames; } }
public ConsoleMethodAttribute( string command, string description, params string[] parameterNames )
{
m_command = command;
m_description = description;
m_parameterNames = parameterNames;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 324bb39c0bff0f74fa42f83e91f07e3a
timeCreated: 1520710946
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d15693a03d0d33b4892c6365a2a97e19
timeCreated: 1472036503
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,170 @@
using System.Globalization;
using System.Text;
using UnityEngine;
// Container for a simple debug entry
namespace IngameDebugConsole
{
public class DebugLogEntry : System.IEquatable<DebugLogEntry>
{
private const int HASH_NOT_CALCULATED = -623218;
public string logString;
public string stackTrace;
private string completeLog;
// Sprite to show with this entry
public Sprite logTypeSpriteRepresentation;
// Collapsed count
public int count;
private int hashValue;
public void Initialize( string logString, string stackTrace )
{
this.logString = logString;
this.stackTrace = stackTrace;
completeLog = null;
count = 1;
hashValue = HASH_NOT_CALCULATED;
}
// Check if two entries have the same origin
public bool Equals( DebugLogEntry other )
{
return this.logString == other.logString && this.stackTrace == other.stackTrace;
}
// Checks if logString or stackTrace contains the search term
public bool MatchesSearchTerm( string searchTerm )
{
return ( logString != null && DebugLogConsole.caseInsensitiveComparer.IndexOf( logString, searchTerm, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) >= 0 ) ||
( stackTrace != null && DebugLogConsole.caseInsensitiveComparer.IndexOf( stackTrace, searchTerm, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) >= 0 );
}
// Return a string containing complete information about this debug entry
public override string ToString()
{
if( completeLog == null )
completeLog = string.Concat( logString, "\n", stackTrace );
return completeLog;
}
// Credit: https://stackoverflow.com/a/19250516/2373034
public override int GetHashCode()
{
if( hashValue == HASH_NOT_CALCULATED )
{
unchecked
{
hashValue = 17;
hashValue = hashValue * 23 + ( logString == null ? 0 : logString.GetHashCode() );
hashValue = hashValue * 23 + ( stackTrace == null ? 0 : stackTrace.GetHashCode() );
}
}
return hashValue;
}
}
public struct QueuedDebugLogEntry
{
public readonly string logString;
public readonly string stackTrace;
public readonly LogType logType;
public QueuedDebugLogEntry( string logString, string stackTrace, LogType logType )
{
this.logString = logString;
this.stackTrace = stackTrace;
this.logType = logType;
}
// Checks if logString or stackTrace contains the search term
public bool MatchesSearchTerm( string searchTerm )
{
return ( logString != null && DebugLogConsole.caseInsensitiveComparer.IndexOf( logString, searchTerm, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) >= 0 ) ||
( stackTrace != null && DebugLogConsole.caseInsensitiveComparer.IndexOf( stackTrace, searchTerm, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) >= 0 );
}
}
public struct DebugLogEntryTimestamp
{
public readonly System.DateTime dateTime;
#if !IDG_OMIT_ELAPSED_TIME
public readonly float elapsedSeconds;
#endif
#if !IDG_OMIT_FRAMECOUNT
public readonly int frameCount;
#endif
#if !IDG_OMIT_ELAPSED_TIME && !IDG_OMIT_FRAMECOUNT
public DebugLogEntryTimestamp( System.DateTime dateTime, float elapsedSeconds, int frameCount )
#elif !IDG_OMIT_ELAPSED_TIME
public DebugLogEntryTimestamp( System.DateTime dateTime, float elapsedSeconds )
#elif !IDG_OMIT_FRAMECOUNT
public DebugLogEntryTimestamp( System.DateTime dateTime, int frameCount )
#else
public DebugLogEntryTimestamp( System.DateTime dateTime )
#endif
{
this.dateTime = dateTime;
#if !IDG_OMIT_ELAPSED_TIME
this.elapsedSeconds = elapsedSeconds;
#endif
#if !IDG_OMIT_FRAMECOUNT
this.frameCount = frameCount;
#endif
}
public void AppendTime( StringBuilder sb )
{
// Add DateTime in format: [HH:mm:ss]
sb.Append( "[" );
int hour = dateTime.Hour;
if( hour >= 10 )
sb.Append( hour );
else
sb.Append( "0" ).Append( hour );
sb.Append( ":" );
int minute = dateTime.Minute;
if( minute >= 10 )
sb.Append( minute );
else
sb.Append( "0" ).Append( minute );
sb.Append( ":" );
int second = dateTime.Second;
if( second >= 10 )
sb.Append( second );
else
sb.Append( "0" ).Append( second );
sb.Append( "]" );
}
public void AppendFullTimestamp( StringBuilder sb )
{
AppendTime( sb );
#if !IDG_OMIT_ELAPSED_TIME && !IDG_OMIT_FRAMECOUNT
// Append elapsed seconds and frame count in format: [1.0s at #Frame]
sb.Append( "[" ).Append( elapsedSeconds.ToString( "F1" ) ).Append( "s at " ).Append( "#" ).Append( frameCount ).Append( "]" );
#elif !IDG_OMIT_ELAPSED_TIME
// Append elapsed seconds in format: [1.0s]
sb.Append( "[" ).Append( elapsedSeconds.ToString( "F1" ) ).Append( "s]" );
#elif !IDG_OMIT_FRAMECOUNT
// Append frame count in format: [#Frame]
sb.Append( "[#" ).Append( frameCount ).Append( "]" );
#endif
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e7b1a420c564be040bf73b8a377fc2c2
timeCreated: 1466375168
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,39 @@
namespace IngameDebugConsole
{
public class DebugLogIndexList<T>
{
private T[] indices;
private int size;
public int Count { get { return size; } }
public T this[int index]
{
get { return indices[index]; }
set { indices[index] = value; }
}
public DebugLogIndexList()
{
indices = new T[64];
size = 0;
}
public void Add( T value )
{
if( size == indices.Length )
System.Array.Resize( ref indices, size * 2 );
indices[size++] = value;
}
public void Clear()
{
size = 0;
}
public int IndexOf( T value )
{
return System.Array.IndexOf( indices, value );
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 37c6c91e5bfac6f499698c03f593bcbb
timeCreated: 1520627934
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,283 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Text;
#if UNITY_EDITOR
using UnityEditor;
using System.Text.RegularExpressions;
#endif
// A UI element to show information about a debug entry
namespace IngameDebugConsole
{
public class DebugLogItem : MonoBehaviour, IPointerClickHandler
{
#region Platform Specific Elements
#if !UNITY_2018_1_OR_NEWER
#if !UNITY_EDITOR && UNITY_ANDROID
private static AndroidJavaClass m_ajc = null;
private static AndroidJavaClass AJC
{
get
{
if( m_ajc == null )
m_ajc = new AndroidJavaClass( "com.yasirkula.unity.DebugConsole" );
return m_ajc;
}
}
private static AndroidJavaObject m_context = null;
private static AndroidJavaObject Context
{
get
{
if( m_context == null )
{
using( AndroidJavaObject unityClass = new AndroidJavaClass( "com.unity3d.player.UnityPlayer" ) )
{
m_context = unityClass.GetStatic<AndroidJavaObject>( "currentActivity" );
}
}
return m_context;
}
}
#elif !UNITY_EDITOR && UNITY_IOS
[System.Runtime.InteropServices.DllImport( "__Internal" )]
private static extern void _DebugConsole_CopyText( string text );
#endif
#endif
#endregion
#pragma warning disable 0649
// Cached components
[SerializeField]
private RectTransform transformComponent;
public RectTransform Transform { get { return transformComponent; } }
[SerializeField]
private Image imageComponent;
public Image Image { get { return imageComponent; } }
[SerializeField]
private CanvasGroup canvasGroupComponent;
public CanvasGroup CanvasGroup { get { return canvasGroupComponent; } }
[SerializeField]
private Text logText;
[SerializeField]
private Image logTypeImage;
// Objects related to the collapsed count of the debug entry
[SerializeField]
private GameObject logCountParent;
[SerializeField]
private Text logCountText;
[SerializeField]
private RectTransform copyLogButton;
#pragma warning restore 0649
// Debug entry to show with this log item
private DebugLogEntry logEntry;
public DebugLogEntry Entry { get { return logEntry; } }
private DebugLogEntryTimestamp? logEntryTimestamp;
public DebugLogEntryTimestamp? Timestamp { get { return logEntryTimestamp; } }
// Index of the entry in the list of entries
private int entryIndex;
public int Index { get { return entryIndex; } }
private bool isExpanded;
public bool Expanded { get { return isExpanded; } }
private Vector2 logTextOriginalPosition;
private Vector2 logTextOriginalSize;
private float copyLogButtonHeight;
private DebugLogRecycledListView listView;
public void Initialize( DebugLogRecycledListView listView )
{
this.listView = listView;
logTextOriginalPosition = logText.rectTransform.anchoredPosition;
logTextOriginalSize = logText.rectTransform.sizeDelta;
copyLogButtonHeight = copyLogButton.anchoredPosition.y + copyLogButton.sizeDelta.y + 2f; // 2f: space between text and button
#if !UNITY_EDITOR && UNITY_WEBGL
copyLogButton.gameObject.AddComponent<DebugLogItemCopyWebGL>().Initialize( this );
#endif
}
public void SetContent( DebugLogEntry logEntry, DebugLogEntryTimestamp? logEntryTimestamp, int entryIndex, bool isExpanded )
{
this.logEntry = logEntry;
this.logEntryTimestamp = logEntryTimestamp;
this.entryIndex = entryIndex;
this.isExpanded = isExpanded;
Vector2 size = transformComponent.sizeDelta;
if( isExpanded )
{
logText.horizontalOverflow = HorizontalWrapMode.Wrap;
size.y = listView.SelectedItemHeight;
if( !copyLogButton.gameObject.activeSelf )
{
copyLogButton.gameObject.SetActive( true );
logText.rectTransform.anchoredPosition = new Vector2( logTextOriginalPosition.x, logTextOriginalPosition.y + copyLogButtonHeight * 0.5f );
logText.rectTransform.sizeDelta = logTextOriginalSize - new Vector2( 0f, copyLogButtonHeight );
}
}
else
{
logText.horizontalOverflow = HorizontalWrapMode.Overflow;
size.y = listView.ItemHeight;
if( copyLogButton.gameObject.activeSelf )
{
copyLogButton.gameObject.SetActive( false );
logText.rectTransform.anchoredPosition = logTextOriginalPosition;
logText.rectTransform.sizeDelta = logTextOriginalSize;
}
}
transformComponent.sizeDelta = size;
SetText( logEntry, logEntryTimestamp, isExpanded );
logTypeImage.sprite = logEntry.logTypeSpriteRepresentation;
}
// Show the collapsed count of the debug entry
public void ShowCount()
{
logCountText.text = logEntry.count.ToString();
if( !logCountParent.activeSelf )
logCountParent.SetActive( true );
}
// Hide the collapsed count of the debug entry
public void HideCount()
{
if( logCountParent.activeSelf )
logCountParent.SetActive( false );
}
// Update the debug entry's displayed timestamp
public void UpdateTimestamp( DebugLogEntryTimestamp timestamp )
{
logEntryTimestamp = timestamp;
if( isExpanded || listView.manager.alwaysDisplayTimestamps )
SetText( logEntry, timestamp, isExpanded );
}
private void SetText( DebugLogEntry logEntry, DebugLogEntryTimestamp? logEntryTimestamp, bool isExpanded )
{
if( !logEntryTimestamp.HasValue || ( !isExpanded && !listView.manager.alwaysDisplayTimestamps ) )
logText.text = isExpanded ? logEntry.ToString() : logEntry.logString;
else
{
StringBuilder sb = listView.manager.sharedStringBuilder;
sb.Length = 0;
if( isExpanded )
{
logEntryTimestamp.Value.AppendFullTimestamp( sb );
sb.Append( ": " ).Append( logEntry.ToString() );
}
else
{
logEntryTimestamp.Value.AppendTime( sb );
sb.Append( " " ).Append( logEntry.logString );
}
logText.text = sb.ToString();
}
}
// This log item is clicked, show the debug entry's stack trace
public void OnPointerClick( PointerEventData eventData )
{
#if UNITY_EDITOR
if( eventData.button == PointerEventData.InputButton.Right )
{
Match regex = Regex.Match( logEntry.stackTrace, @"\(at .*\.cs:[0-9]+\)$", RegexOptions.Multiline );
if( regex.Success )
{
string line = logEntry.stackTrace.Substring( regex.Index + 4, regex.Length - 5 );
int lineSeparator = line.IndexOf( ':' );
MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>( line.Substring( 0, lineSeparator ) );
if( script != null )
AssetDatabase.OpenAsset( script, int.Parse( line.Substring( lineSeparator + 1 ) ) );
}
}
else
listView.OnLogItemClicked( this );
#else
listView.OnLogItemClicked( this );
#endif
}
public void CopyLog()
{
#if UNITY_EDITOR || !UNITY_WEBGL
string log = GetCopyContent();
if( string.IsNullOrEmpty( log ) )
return;
#if UNITY_EDITOR || UNITY_2018_1_OR_NEWER || ( !UNITY_ANDROID && !UNITY_IOS )
GUIUtility.systemCopyBuffer = log;
#elif UNITY_ANDROID
AJC.CallStatic( "CopyText", Context, log );
#elif UNITY_IOS
_DebugConsole_CopyText( log );
#endif
#endif
}
internal string GetCopyContent()
{
if( !logEntryTimestamp.HasValue )
return logEntry.ToString();
else
{
StringBuilder sb = listView.manager.sharedStringBuilder;
sb.Length = 0;
logEntryTimestamp.Value.AppendFullTimestamp( sb );
sb.Append( ": " ).Append( logEntry.ToString() );
return sb.ToString();
}
}
public float CalculateExpandedHeight( DebugLogEntry logEntry, DebugLogEntryTimestamp? logEntryTimestamp )
{
string text = logText.text;
HorizontalWrapMode wrapMode = logText.horizontalOverflow;
SetText( logEntry, logEntryTimestamp, true );
logText.horizontalOverflow = HorizontalWrapMode.Wrap;
float result = logText.preferredHeight + copyLogButtonHeight;
logText.text = text;
logText.horizontalOverflow = wrapMode;
return Mathf.Max( listView.ItemHeight, result );
}
// Return a string containing complete information about the debug entry
public override string ToString()
{
return logEntry.ToString();
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d2ea291be9de70a4abfec595203c96c1
timeCreated: 1465919949
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
#if !UNITY_EDITOR && UNITY_WEBGL
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.EventSystems;
namespace IngameDebugConsole
{
public class DebugLogItemCopyWebGL : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
[DllImport( "__Internal" )]
private static extern void IngameDebugConsoleStartCopy( string textToCopy );
[DllImport( "__Internal" )]
private static extern void IngameDebugConsoleCancelCopy();
private DebugLogItem logItem;
public void Initialize( DebugLogItem logItem )
{
this.logItem = logItem;
}
public void OnPointerDown( PointerEventData eventData )
{
string log = logItem.GetCopyContent();
if( !string.IsNullOrEmpty( log ) )
IngameDebugConsoleStartCopy( log );
}
public void OnPointerUp( PointerEventData eventData )
{
if( eventData.dragging )
IngameDebugConsoleCancelCopy();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5a7d9d894141e704d8160fb4632121ac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6a4f16ed905adcd4ab0d7c8c11f0d72c
timeCreated: 1522092746
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: -9869
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,277 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
#if UNITY_EDITOR && UNITY_2021_1_OR_NEWER
using Screen = UnityEngine.Device.Screen; // To support Device Simulator on Unity 2021.1+
#endif
// Manager class for the debug popup
namespace IngameDebugConsole
{
public class DebugLogPopup : MonoBehaviour, IPointerClickHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
private RectTransform popupTransform;
// Dimensions of the popup divided by 2
private Vector2 halfSize;
// Background image that will change color to indicate an alert
private Image backgroundImage;
// Canvas group to modify visibility of the popup
private CanvasGroup canvasGroup;
#pragma warning disable 0649
[SerializeField]
private DebugLogManager debugManager;
[SerializeField]
private Text newInfoCountText;
[SerializeField]
private Text newWarningCountText;
[SerializeField]
private Text newErrorCountText;
[SerializeField]
private Color alertColorInfo;
[SerializeField]
private Color alertColorWarning;
[SerializeField]
private Color alertColorError;
#pragma warning restore 0649
// Number of new debug entries since the log window has been closed
private int newInfoCount = 0, newWarningCount = 0, newErrorCount = 0;
private Color normalColor;
private bool isPopupBeingDragged = false;
private Vector2 normalizedPosition;
// Coroutines for simple code-based animations
private IEnumerator moveToPosCoroutine = null;
private void Awake()
{
popupTransform = (RectTransform)transform;
backgroundImage = GetComponent<Image>();
canvasGroup = GetComponent<CanvasGroup>();
normalColor = backgroundImage.color;
halfSize = popupTransform.sizeDelta * 0.5f;
Vector2 pos = popupTransform.anchoredPosition;
if (pos.x != 0f || pos.y != 0f)
normalizedPosition = pos.normalized; // Respect the initial popup position set in the prefab
else
normalizedPosition = new Vector2(0.5f, 0f); // Right edge by default
}
public void NewLogsArrived(int newInfo, int newWarning, int newError)
{
if (newInfo > 0)
{
newInfoCount += newInfo;
newInfoCountText.text = newInfoCount.ToString();
}
if (newWarning > 0)
{
newWarningCount += newWarning;
newWarningCountText.text = newWarningCount.ToString();
}
if (newError > 0)
{
newErrorCount += newError;
newErrorCountText.text = newErrorCount.ToString();
}
if (newErrorCount > 0)
backgroundImage.color = alertColorError;
else if (newWarningCount > 0)
backgroundImage.color = alertColorWarning;
else
backgroundImage.color = alertColorInfo;
}
private void Reset()
{
newInfoCount = 0;
newWarningCount = 0;
newErrorCount = 0;
newInfoCountText.text = "0";
newWarningCountText.text = "0";
newErrorCountText.text = "0";
backgroundImage.color = normalColor;
}
// A simple smooth movement animation
private IEnumerator MoveToPosAnimation(Vector2 targetPos)
{
float modifier = 0f;
Vector2 initialPos = popupTransform.anchoredPosition;
while (modifier < 1f)
{
modifier += 4f * Time.unscaledDeltaTime;
popupTransform.anchoredPosition = Vector2.Lerp(initialPos, targetPos, modifier);
yield return null;
}
}
// Popup is clicked
public void OnPointerClick(PointerEventData data)
{
// Hide the popup and show the log window
if (!isPopupBeingDragged)
debugManager.ShowLogWindow();
}
// Hides the log window and shows the popup
public void Show()
{
canvasGroup.blocksRaycasts = true;
canvasGroup.alpha = 1f;
// Reset the counters
Reset();
// Update position in case resolution was changed while the popup was hidden
UpdatePosition(true);
}
// Hide the popup
public void Hide()
{
canvasGroup.blocksRaycasts = false;
canvasGroup.alpha = 0f;
isPopupBeingDragged = false;
}
public void OnBeginDrag(PointerEventData data)
{
isPopupBeingDragged = true;
// If a smooth movement animation is in progress, cancel it
if (moveToPosCoroutine != null)
{
StopCoroutine(moveToPosCoroutine);
moveToPosCoroutine = null;
}
}
// Reposition the popup
public void OnDrag(PointerEventData data)
{
Vector2 localPoint;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(debugManager.canvasTR, data.position, data.pressEventCamera, out localPoint))
popupTransform.anchoredPosition = localPoint;
}
// Smoothly translate the popup to the nearest edge
public void OnEndDrag(PointerEventData data)
{
isPopupBeingDragged = false;
UpdatePosition(false);
}
// There are 2 different spaces used in these calculations:
// RectTransform space: raw anchoredPosition of the popup that's in range [-canvasSize/2, canvasSize/2]
// Safe area space: Screen.safeArea space that's in range [safeAreaBottomLeft, safeAreaTopRight] where these corner positions
// are all positive (calculated from bottom left corner of the screen instead of the center of the screen)
public void UpdatePosition(bool immediately)
{
Vector2 canvasRawSize = debugManager.canvasTR.rect.size;
// Calculate safe area bounds
float canvasWidth = canvasRawSize.x;
float canvasHeight = canvasRawSize.y;
float canvasBottomLeftX = 0f;
float canvasBottomLeftY = 0f;
if (debugManager.popupAvoidsScreenCutout)
{
#if UNITY_2017_2_OR_NEWER && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IOS)
Rect safeArea = Screen.safeArea;
int screenWidth = Screen.width;
int screenHeight = Screen.height;
canvasWidth *= safeArea.width / screenWidth;
canvasHeight *= safeArea.height / screenHeight;
canvasBottomLeftX = canvasRawSize.x * (safeArea.x / screenWidth);
canvasBottomLeftY = canvasRawSize.y * (safeArea.y / screenHeight);
#endif
}
// Calculate safe area position of the popup
// normalizedPosition allows us to glue the popup to a specific edge of the screen. It becomes useful when
// the popup is at the right edge and we switch from portrait screen orientation to landscape screen orientation.
// Without normalizedPosition, popup could jump to bottom or top edges instead of staying at the right edge
Vector2 pos = canvasRawSize * 0.5f + (immediately ? new Vector2(normalizedPosition.x * canvasWidth, normalizedPosition.y * canvasHeight) : (popupTransform.anchoredPosition - new Vector2(canvasBottomLeftX, canvasBottomLeftY)));
// Find distances to all four edges of the safe area
float distToLeft = pos.x;
float distToRight = canvasWidth - distToLeft;
float distToBottom = pos.y;
float distToTop = canvasHeight - distToBottom;
float horDistance = Mathf.Min(distToLeft, distToRight);
float vertDistance = Mathf.Min(distToBottom, distToTop);
// Find the nearest edge's safe area coordinates
if (horDistance < vertDistance)
{
if (distToLeft < distToRight)
pos = new Vector2(halfSize.x, pos.y);
else
pos = new Vector2(canvasWidth - halfSize.x, pos.y);
pos.y = Mathf.Clamp(pos.y, halfSize.y, canvasHeight - halfSize.y);
}
else
{
if (distToBottom < distToTop)
pos = new Vector2(pos.x, halfSize.y);
else
pos = new Vector2(pos.x, canvasHeight - halfSize.y);
pos.x = Mathf.Clamp(pos.x, halfSize.x, canvasWidth - halfSize.x);
}
pos -= canvasRawSize * 0.5f;
normalizedPosition.Set(pos.x / canvasWidth, pos.y / canvasHeight);
// Safe area's bottom left coordinates are added to pos only after normalizedPosition's value
// is set because normalizedPosition is in range [-canvasWidth / 2, canvasWidth / 2]
pos += new Vector2(canvasBottomLeftX, canvasBottomLeftY);
// If another smooth movement animation is in progress, cancel it
if (moveToPosCoroutine != null)
{
StopCoroutine(moveToPosCoroutine);
moveToPosCoroutine = null;
}
if (immediately)
popupTransform.anchoredPosition = pos;
else
{
// Smoothly translate the popup to the specified position
moveToPosCoroutine = MoveToPosAnimation(pos);
StartCoroutine(moveToPosCoroutine);
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 05cc4b1999716644c9308528e38e7081
timeCreated: 1466533184
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,392 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
// Handles the log items in an optimized way such that existing log items are
// recycled within the list instead of creating a new log item at each chance
namespace IngameDebugConsole
{
public class DebugLogRecycledListView : MonoBehaviour
{
#pragma warning disable 0649
// Cached components
[SerializeField]
private RectTransform transformComponent;
[SerializeField]
private RectTransform viewportTransform;
[SerializeField]
private Color logItemNormalColor1;
[SerializeField]
private Color logItemNormalColor2;
[SerializeField]
private Color logItemSelectedColor;
#pragma warning restore 0649
internal DebugLogManager manager;
private ScrollRect scrollView;
private float logItemHeight, _1OverLogItemHeight;
private float viewportHeight;
// Unique debug entries
private List<DebugLogEntry> collapsedLogEntries = null;
// Indices of debug entries to show in collapsedLogEntries
private DebugLogIndexList<int> indicesOfEntriesToShow = null;
private DebugLogIndexList<DebugLogEntryTimestamp> timestampsOfEntriesToShow = null;
private int indexOfSelectedLogEntry = int.MaxValue;
private float positionOfSelectedLogEntry = float.MaxValue;
private float heightOfSelectedLogEntry;
private float deltaHeightOfSelectedLogEntry;
// Log items used to visualize the debug entries at specified indices
private readonly Dictionary<int, DebugLogItem> logItemsAtIndices = new Dictionary<int, DebugLogItem>( 256 );
private bool isCollapseOn = false;
// Current indices of debug entries shown on screen
private int currentTopIndex = -1, currentBottomIndex = -1;
public float ItemHeight { get { return logItemHeight; } }
public float SelectedItemHeight { get { return heightOfSelectedLogEntry; } }
private void Awake()
{
scrollView = viewportTransform.GetComponentInParent<ScrollRect>();
scrollView.onValueChanged.AddListener( ( pos ) => UpdateItemsInTheList( false ) );
viewportHeight = viewportTransform.rect.height;
}
public void Initialize( DebugLogManager manager, List<DebugLogEntry> collapsedLogEntries, DebugLogIndexList<int> indicesOfEntriesToShow, DebugLogIndexList<DebugLogEntryTimestamp> timestampsOfEntriesToShow, float logItemHeight )
{
this.manager = manager;
this.collapsedLogEntries = collapsedLogEntries;
this.indicesOfEntriesToShow = indicesOfEntriesToShow;
this.timestampsOfEntriesToShow = timestampsOfEntriesToShow;
this.logItemHeight = logItemHeight;
_1OverLogItemHeight = 1f / logItemHeight;
}
public void SetCollapseMode( bool collapse )
{
isCollapseOn = collapse;
}
// A log item is clicked, highlight it
public void OnLogItemClicked( DebugLogItem item )
{
OnLogItemClickedInternal( item.Index, item );
}
// Force expand the log item at specified index
public void SelectAndFocusOnLogItemAtIndex( int itemIndex )
{
if( indexOfSelectedLogEntry != itemIndex ) // Make sure that we aren't deselecting the target log item
OnLogItemClickedInternal( itemIndex );
float transformComponentCenterYAtTop = viewportHeight * 0.5f;
float transformComponentCenterYAtBottom = transformComponent.sizeDelta.y - viewportHeight * 0.5f;
float transformComponentTargetCenterY = itemIndex * logItemHeight + viewportHeight * 0.5f;
if( transformComponentCenterYAtTop == transformComponentCenterYAtBottom )
scrollView.verticalNormalizedPosition = 0.5f;
else
scrollView.verticalNormalizedPosition = Mathf.Clamp01( Mathf.InverseLerp( transformComponentCenterYAtBottom, transformComponentCenterYAtTop, transformComponentTargetCenterY ) );
manager.SetSnapToBottom( false );
}
private void OnLogItemClickedInternal( int itemIndex, DebugLogItem referenceItem = null )
{
if( indexOfSelectedLogEntry != itemIndex )
{
DeselectSelectedLogItem();
if( !referenceItem )
{
if( currentTopIndex == -1 )
UpdateItemsInTheList( false ); // Try to generate some DebugLogItems, we need one DebugLogItem to calculate the text height
referenceItem = logItemsAtIndices[currentTopIndex];
}
indexOfSelectedLogEntry = itemIndex;
positionOfSelectedLogEntry = itemIndex * logItemHeight;
heightOfSelectedLogEntry = referenceItem.CalculateExpandedHeight( collapsedLogEntries[indicesOfEntriesToShow[itemIndex]], ( timestampsOfEntriesToShow != null ) ? timestampsOfEntriesToShow[itemIndex] : (DebugLogEntryTimestamp?) null );
deltaHeightOfSelectedLogEntry = heightOfSelectedLogEntry - logItemHeight;
manager.SetSnapToBottom( false );
}
else
DeselectSelectedLogItem();
if( indexOfSelectedLogEntry >= currentTopIndex && indexOfSelectedLogEntry <= currentBottomIndex )
ColorLogItem( logItemsAtIndices[indexOfSelectedLogEntry], indexOfSelectedLogEntry );
CalculateContentHeight();
HardResetItems();
UpdateItemsInTheList( true );
manager.ValidateScrollPosition();
}
// Deselect the currently selected log item
public void DeselectSelectedLogItem()
{
int indexOfPreviouslySelectedLogEntry = indexOfSelectedLogEntry;
indexOfSelectedLogEntry = int.MaxValue;
positionOfSelectedLogEntry = float.MaxValue;
heightOfSelectedLogEntry = deltaHeightOfSelectedLogEntry = 0f;
if( indexOfPreviouslySelectedLogEntry >= currentTopIndex && indexOfPreviouslySelectedLogEntry <= currentBottomIndex )
ColorLogItem( logItemsAtIndices[indexOfPreviouslySelectedLogEntry], indexOfPreviouslySelectedLogEntry );
}
// Number of debug entries may be changed, update the list
public void OnLogEntriesUpdated( bool updateAllVisibleItemContents )
{
CalculateContentHeight();
viewportHeight = viewportTransform.rect.height;
if( updateAllVisibleItemContents )
HardResetItems();
UpdateItemsInTheList( updateAllVisibleItemContents );
}
// A single collapsed log entry at specified index is updated, refresh its item if visible
public void OnCollapsedLogEntryAtIndexUpdated( int index )
{
DebugLogItem logItem;
if( logItemsAtIndices.TryGetValue( index, out logItem ) )
{
logItem.ShowCount();
if( timestampsOfEntriesToShow != null )
logItem.UpdateTimestamp( timestampsOfEntriesToShow[index] );
}
}
// Log window's width has changed, update the expanded (currently selected) log's height
public void OnViewportWidthChanged()
{
if( indexOfSelectedLogEntry >= indicesOfEntriesToShow.Count )
return;
if( currentTopIndex == -1 )
{
UpdateItemsInTheList( false ); // Try to generate some DebugLogItems, we need one DebugLogItem to calculate the text height
if( currentTopIndex == -1 ) // No DebugLogItems are generated, weird
return;
}
DebugLogItem referenceItem = logItemsAtIndices[currentTopIndex];
heightOfSelectedLogEntry = referenceItem.CalculateExpandedHeight( collapsedLogEntries[indicesOfEntriesToShow[indexOfSelectedLogEntry]], ( timestampsOfEntriesToShow != null ) ? timestampsOfEntriesToShow[indexOfSelectedLogEntry] : (DebugLogEntryTimestamp?) null );
deltaHeightOfSelectedLogEntry = heightOfSelectedLogEntry - logItemHeight;
CalculateContentHeight();
HardResetItems();
UpdateItemsInTheList( true );
manager.ValidateScrollPosition();
}
// Log window's height has changed, update the list
public void OnViewportHeightChanged()
{
viewportHeight = viewportTransform.rect.height;
UpdateItemsInTheList( false );
}
private void HardResetItems()
{
if( currentTopIndex != -1 )
{
DestroyLogItemsBetweenIndices( currentTopIndex, currentBottomIndex );
currentTopIndex = -1;
}
}
private void CalculateContentHeight()
{
float newHeight = Mathf.Max( 1f, indicesOfEntriesToShow.Count * logItemHeight + deltaHeightOfSelectedLogEntry );
transformComponent.sizeDelta = new Vector2( 0f, newHeight );
}
// Calculate the indices of log entries to show
// and handle log items accordingly
public void UpdateItemsInTheList( bool updateAllVisibleItemContents )
{
// If there is at least one log entry to show
if( indicesOfEntriesToShow.Count > 0 )
{
float contentPosTop = transformComponent.anchoredPosition.y - 1f;
float contentPosBottom = contentPosTop + viewportHeight + 2f;
if( positionOfSelectedLogEntry <= contentPosBottom )
{
if( positionOfSelectedLogEntry <= contentPosTop )
{
contentPosTop -= deltaHeightOfSelectedLogEntry;
contentPosBottom -= deltaHeightOfSelectedLogEntry;
if( contentPosTop < positionOfSelectedLogEntry - 1f )
contentPosTop = positionOfSelectedLogEntry - 1f;
if( contentPosBottom < contentPosTop + 2f )
contentPosBottom = contentPosTop + 2f;
}
else
{
contentPosBottom -= deltaHeightOfSelectedLogEntry;
if( contentPosBottom < positionOfSelectedLogEntry + 1f )
contentPosBottom = positionOfSelectedLogEntry + 1f;
}
}
int newTopIndex = (int) ( contentPosTop * _1OverLogItemHeight );
int newBottomIndex = (int) ( contentPosBottom * _1OverLogItemHeight );
if( newTopIndex < 0 )
newTopIndex = 0;
if( newBottomIndex > indicesOfEntriesToShow.Count - 1 )
newBottomIndex = indicesOfEntriesToShow.Count - 1;
if( currentTopIndex == -1 )
{
// There are no log items visible on screen,
// just create the new log items
updateAllVisibleItemContents = true;
currentTopIndex = newTopIndex;
currentBottomIndex = newBottomIndex;
CreateLogItemsBetweenIndices( newTopIndex, newBottomIndex );
}
else
{
// There are some log items visible on screen
if( newBottomIndex < currentTopIndex || newTopIndex > currentBottomIndex )
{
// If user scrolled a lot such that, none of the log items are now within
// the bounds of the scroll view, pool all the previous log items and create
// new log items for the new list of visible debug entries
updateAllVisibleItemContents = true;
DestroyLogItemsBetweenIndices( currentTopIndex, currentBottomIndex );
CreateLogItemsBetweenIndices( newTopIndex, newBottomIndex );
}
else
{
// User did not scroll a lot such that, there are still some log items within
// the bounds of the scroll view. Don't destroy them but update their content,
// if necessary
if( newTopIndex > currentTopIndex )
DestroyLogItemsBetweenIndices( currentTopIndex, newTopIndex - 1 );
if( newBottomIndex < currentBottomIndex )
DestroyLogItemsBetweenIndices( newBottomIndex + 1, currentBottomIndex );
if( newTopIndex < currentTopIndex )
{
CreateLogItemsBetweenIndices( newTopIndex, currentTopIndex - 1 );
// If it is not necessary to update all the log items,
// then just update the newly created log items. Otherwise,
// wait for the major update
if( !updateAllVisibleItemContents )
UpdateLogItemContentsBetweenIndices( newTopIndex, currentTopIndex - 1 );
}
if( newBottomIndex > currentBottomIndex )
{
CreateLogItemsBetweenIndices( currentBottomIndex + 1, newBottomIndex );
// If it is not necessary to update all the log items,
// then just update the newly created log items. Otherwise,
// wait for the major update
if( !updateAllVisibleItemContents )
UpdateLogItemContentsBetweenIndices( currentBottomIndex + 1, newBottomIndex );
}
}
currentTopIndex = newTopIndex;
currentBottomIndex = newBottomIndex;
}
if( updateAllVisibleItemContents )
{
// Update all the log items
UpdateLogItemContentsBetweenIndices( currentTopIndex, currentBottomIndex );
}
}
else
HardResetItems();
}
private void CreateLogItemsBetweenIndices( int topIndex, int bottomIndex )
{
for( int i = topIndex; i <= bottomIndex; i++ )
CreateLogItemAtIndex( i );
}
// Create (or unpool) a log item
private void CreateLogItemAtIndex( int index )
{
DebugLogItem logItem = manager.PopLogItem();
// Reposition the log item
Vector2 anchoredPosition = new Vector2( 1f, -index * logItemHeight );
if( index > indexOfSelectedLogEntry )
anchoredPosition.y -= deltaHeightOfSelectedLogEntry;
logItem.Transform.anchoredPosition = anchoredPosition;
// Color the log item
ColorLogItem( logItem, index );
// To access this log item easily in the future, add it to the dictionary
logItemsAtIndices[index] = logItem;
}
private void DestroyLogItemsBetweenIndices( int topIndex, int bottomIndex )
{
for( int i = topIndex; i <= bottomIndex; i++ )
manager.PoolLogItem( logItemsAtIndices[i] );
}
private void UpdateLogItemContentsBetweenIndices( int topIndex, int bottomIndex )
{
DebugLogItem logItem;
for( int i = topIndex; i <= bottomIndex; i++ )
{
logItem = logItemsAtIndices[i];
logItem.SetContent( collapsedLogEntries[indicesOfEntriesToShow[i]], ( timestampsOfEntriesToShow != null ) ? timestampsOfEntriesToShow[i] : (DebugLogEntryTimestamp?) null, i, i == indexOfSelectedLogEntry );
if( isCollapseOn )
logItem.ShowCount();
else
logItem.HideCount();
}
}
// Color a log item using its index
private void ColorLogItem( DebugLogItem logItem, int index )
{
if( index == indexOfSelectedLogEntry )
logItem.Image.color = logItemSelectedColor;
else if( index % 2 == 0 )
logItem.Image.color = logItemNormalColor1;
else
logItem.Image.color = logItemNormalColor2;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ce231987d32488f43b6fb798f7df43f6
timeCreated: 1466373025
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
using UnityEngine;
using UnityEngine.EventSystems;
// Listens to drag event on the DebugLogManager's resize button
namespace IngameDebugConsole
{
public class DebugLogResizeListener : MonoBehaviour, IBeginDragHandler, IDragHandler
{
#pragma warning disable 0649
[SerializeField]
private DebugLogManager debugManager;
#pragma warning restore 0649
// This interface must be implemented in order to receive drag events
void IBeginDragHandler.OnBeginDrag( PointerEventData eventData )
{
}
void IDragHandler.OnDrag( PointerEventData eventData )
{
debugManager.Resize( eventData );
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6565f2084f5aef44abe57c988745b9c3
timeCreated: 1601221093
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
// Listens to scroll events on the scroll rect that debug items are stored
// and decides whether snap to bottom should be true or not
//
// Procedure: if, after a user input (drag or scroll), scrollbar is at the bottom, then
// snap to bottom shall be true, otherwise it shall be false
namespace IngameDebugConsole
{
public class DebugsOnScrollListener : MonoBehaviour, IScrollHandler, IBeginDragHandler, IEndDragHandler
{
public ScrollRect debugsScrollRect;
public DebugLogManager debugLogManager;
public void OnScroll( PointerEventData data )
{
if( IsScrollbarAtBottom() )
debugLogManager.SetSnapToBottom( true );
else
debugLogManager.SetSnapToBottom( false );
}
public void OnBeginDrag( PointerEventData data )
{
debugLogManager.SetSnapToBottom( false );
}
public void OnEndDrag( PointerEventData data )
{
if( IsScrollbarAtBottom() )
debugLogManager.SetSnapToBottom( true );
else
debugLogManager.SetSnapToBottom( false );
}
public void OnScrollbarDragStart( BaseEventData data )
{
debugLogManager.SetSnapToBottom( false );
}
public void OnScrollbarDragEnd( BaseEventData data )
{
if( IsScrollbarAtBottom() )
debugLogManager.SetSnapToBottom( true );
else
debugLogManager.SetSnapToBottom( false );
}
private bool IsScrollbarAtBottom()
{
float scrollbarYPos = debugsScrollRect.verticalNormalizedPosition;
if( scrollbarYPos <= 1E-6f )
return true;
return false;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cb564dcb180e586429c57456166a76b5
timeCreated: 1466004663
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,75 @@
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
using UnityEngine.InputSystem.UI;
#endif
namespace IngameDebugConsole
{
// Avoid multiple EventSystems in the scene by activating the embedded EventSystem only if one doesn't already exist in the scene
[DefaultExecutionOrder( 1000 )]
public class EventSystemHandler : MonoBehaviour
{
#pragma warning disable 0649
[SerializeField]
private GameObject embeddedEventSystem;
#pragma warning restore 0649
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
private void Awake()
{
StandaloneInputModule legacyInputModule = embeddedEventSystem.GetComponent<StandaloneInputModule>();
if( legacyInputModule )
{
DestroyImmediate( legacyInputModule );
embeddedEventSystem.AddComponent<InputSystemUIInputModule>();
}
}
#endif
private void OnEnable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
SceneManager.sceneLoaded += OnSceneLoaded;
SceneManager.sceneUnloaded -= OnSceneUnloaded;
SceneManager.sceneUnloaded += OnSceneUnloaded;
ActivateEventSystemIfNeeded();
}
private void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
SceneManager.sceneUnloaded -= OnSceneUnloaded;
DeactivateEventSystem();
}
private void OnSceneLoaded( Scene scene, LoadSceneMode mode )
{
#if UNITY_2017_2_OR_NEWER
DeactivateEventSystem();
#endif
ActivateEventSystemIfNeeded();
}
private void OnSceneUnloaded( Scene current )
{
// Deactivate the embedded EventSystem before changing scenes because the new scene might have its own EventSystem
DeactivateEventSystem();
}
private void ActivateEventSystemIfNeeded()
{
if( embeddedEventSystem && !EventSystem.current )
embeddedEventSystem.SetActive( true );
}
private void DeactivateEventSystem()
{
if( embeddedEventSystem )
embeddedEventSystem.SetActive( false );
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c3cc1b407f337e641ad32a2e91d5b478
timeCreated: 1658741613
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: a091b43ce3618074d8cf2beb7e538a7d
folderAsset: yes
timeCreated: 1626377678
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
mergeInto( LibraryManager.library,
{
IngameDebugConsoleStartCopy: function( textToCopy )
{
var textToCopyJS = UTF8ToString( textToCopy );
// Delete if element exist
var copyTextButton = document.getElementById( 'DebugConsoleCopyButtonGL' );
if( !copyTextButton )
{
copyTextButton = document.createElement( 'button' );
copyTextButton.setAttribute( 'id', 'DebugConsoleCopyButtonGL' );
copyTextButton.setAttribute( 'style','display:none; visibility:hidden;' );
}
copyTextButton.onclick = function( event )
{
// Credit: https://stackoverflow.com/a/30810322/2373034
if( navigator.clipboard )
{
navigator.clipboard.writeText( textToCopyJS ).then( function() { }, function( err )
{
console.error( "Couldn't copy text to clipboard using clipboard.writeText: ", err );
} );
}
else
{
var textArea = document.createElement( 'textarea' );
textArea.value = textToCopyJS;
// Avoid scrolling to bottom
textArea.style.top = "0";
textArea.style.left = "0";
textArea.style.position = "fixed";
document.body.appendChild( textArea );
textArea.focus();
textArea.select();
try
{
document.execCommand( 'copy' );
}
catch( err )
{
console.error( "Couldn't copy text to clipboard using document.execCommand", err );
}
document.body.removeChild( textArea );
}
};
document.body.appendChild( copyTextButton );
document.onmouseup = function()
{
document.onmouseup = null;
copyTextButton.click();
document.body.removeChild( copyTextButton );
};
},
IngameDebugConsoleCancelCopy: function()
{
var copyTextButton = document.getElementById( 'DebugConsoleCopyButtonGL' );
if( copyTextButton )
document.body.removeChild( copyTextButton );
document.onmouseup = null;
}
} );

View File

@@ -0,0 +1,39 @@
fileFormatVersion: 2
guid: aa23dd530e9f98c4cb0766404fc0e755
timeCreated: 1626377683
licenseType: Free
PluginImporter:
serializedVersion: 2
iconMap: {}
executionOrder: {}
isPreloaded: 0
isOverridable: 0
platformData:
data:
first:
Any:
second:
enabled: 0
settings: {}
data:
first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
data:
first:
Facebook: WebGL
second:
enabled: 1
settings: {}
data:
first:
WebGL: WebGL
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4552a2fd287aca645a670fa2b65e52a9
folderAsset: yes
timeCreated: 1586184974
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,4 @@
extern "C" void _DebugConsole_CopyText( const char* text )
{
[UIPasteboard generalPasteboard].string = [NSString stringWithUTF8String:text];
}

View File

@@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 4b53767ec4f910e4a9876cbe77d57968
timeCreated: 1498727312
licenseType: Pro
PluginImporter:
serializedVersion: 2
iconMap: {}
executionOrder: {}
isPreloaded: 0
isOverridable: 0
platformData:
data:
first:
Any:
second:
enabled: 0
settings: {}
data:
first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
data:
first:
iPhone: iOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,2 +1,8 @@
fileFormatVersion: 2
guid: e037cde050f786f48a35fc4e775dc4a4
guid: e037cde050f786f48a35fc4e775dc4a4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fd4ab3b30e03a184483ced099b43ec19
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 557c32d7360c26949947ac9dd8c308fc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b933d9ff2aad75c47a4970139d6a01e6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8217f1589415f28408ff7a0dfe9142df
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bdbc0567055e3424da2363638a6ffd14
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,144 @@
fileFormatVersion: 2
guid: f88b6df342d5c4f4a969fb6eb4799d3e
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 1
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 2
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 0
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

View File

@@ -0,0 +1,144 @@
fileFormatVersion: 2
guid: 27623e5a17b6fd44a9af57a733ee993a
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 0
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 2
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 0
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 0
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 0
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 0
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 0
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 76d974e53c74fd642a7607b4a9bbf0a4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,128 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
#if WCE_URP
using System.Collections.Generic;
using MH.WaterCausticsModules.Effect;
using UnityEngine;
using UnityEngine.Rendering;
namespace MH.WaterCausticsModules {
[ExecuteAlways]
[DisallowMultipleComponent]
[AddComponentMenu ("")]
internal class AtOnce : MonoBehaviour {
private WaterCausticsEffect _summoner;
private MeshRenderer _render;
internal MeshRenderer render => _render;
private void setRenderEnable (bool isOn) {
if (_render && _render.enabled != isOn) _render.enabled = isOn;
}
private bool _inited;
private AtOnce init (WaterCausticsEffect summoner, Material mat) {
_inited = true;
_summoner = summoner;
var mf = gameObject.AddComponent<MeshFilter> ();
mf.sharedMesh = getMesh ();
_render = gameObject.AddComponent<MeshRenderer> ();
_render.sharedMaterial = mat;
_render.shadowCastingMode = ShadowCastingMode.Off;
_render.lightProbeUsage = LightProbeUsage.Off;
_render.reflectionProbeUsage = ReflectionProbeUsage.Off;
_render.allowOcclusionWhenDynamic = true;
_render.receiveShadows = true;
updateTransform ();
// -- HideFlags --
gameObject.hideFlags = HideFlags.HideAndDontSave | HideFlags.HideInInspector;
// gameObject.hideFlags &= (~HideFlags.HideInHierarchy); // デバッグ用 Hierarchyで表示
// ---------------
return this;
}
private void OnDisable () {
setRenderEnable (false);
}
private void OnDestroy () {
destroy (ref __mesh);
}
private void LateUpdate () {
if (!_summoner || !_summoner.isActiveAndEnabled || _summoner.method != Method.AtOnce) {
setRenderEnable (false);
} else {
setRenderEnable (true);
updateTransform ();
}
}
private void updateTransform () {
if (gameObject.layer != _summoner.gameObject.layer)
gameObject.layer = _summoner.gameObject.layer;
var sumTra = _summoner.transform;
if (transform.parent != sumTra.parent)
transform.SetParent (sumTra.parent, worldPositionStays : false);
if (transform.localToWorldMatrix != sumTra.localToWorldMatrix) {
transform.localPosition = sumTra.localPosition;
transform.localRotation = sumTra.localRotation;
transform.localScale = sumTra.localScale;
}
}
private void Update () {
bool isLeaked = (!_inited || !_summoner || _summoner.atOnce != this);
if (isLeaked) destroy (gameObject);
}
// ----------------------------------------------------------- Mesh
private Mesh __mesh;
private Mesh getMesh () {
if (!__mesh) {
Mesh m = new Mesh ();
m.name = "WCEMeshForAtOnce";
m.vertices = new Vector3 [] { new Vector3 (-.5f, -.5f, -.5f), new Vector3 (.5f, -.5f, -.5f), new Vector3 (-.5f, .5f, -.5f), new Vector3 (.5f, .5f, -.5f), new Vector3 (-.5f, -.5f, .5f), new Vector3 (.5f, -.5f, .5f), new Vector3 (-.5f, .5f, .5f), new Vector3 (.5f, .5f, .5f), /* */ Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero };
m.triangles = new int [] { 2, 6, 7, 2, 7, 3, 0, 2, 3, 0, 3, 1, 1, 3, 7, 1, 7, 5, 0, 4, 6, 0, 6, 2, 4, 5, 7, 4, 7, 6, 0, 1, 5, 0, 5, 4, /* */ 15, 8, 9, 15, 9, 10, 15, 10, 11, 15, 11, 12, 15, 12, 13, 15, 13, 14 };
m.bounds = new Bounds (Vector3.zero, Vector3.one);
m.hideFlags = HideFlags.HideAndDontSave;
m.UploadMeshData (markNoLongerReadable: true);
__mesh = m;
}
return __mesh;
}
// ----------------------------------------------------------- Static
static internal AtOnce Create (WaterCausticsEffect summoner, Material mat) {
var name = "(WCE Renderer) (Deletable)";
var go = new GameObject (name);
go.SetActive (false); // ← Flags設定時にOnEnable,OnDisableが呼ばれる不具合の回避
var a = go.AddComponent<AtOnce> ().init (summoner, mat);
go.SetActive (true);
return a;
}
static internal void OnSummonerDestroyed (ref AtOnce a) {
if (a == null) return;
destroy (a.gameObject);
a = null;
}
static private void destroy<T> (ref T o) where T : Object {
if (o == null) return;
destroy (o);
o = null;
}
static private void destroy (Object o) {
if (o == null) return;
if (Application.isPlaying)
Destroy (o);
else
DestroyImmediate (o);
}
// -----------------------------------------------------------
}
}
#endif // End of WCE_URP

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 22b1121aa1f7879478a6b6b90e1a9738
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: eb81f73566a5654488dbc4e168ae6b26
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,97 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
#if UNITY_EDITOR && (!WCE_URP || WCE_DEVELOPMENT)
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
#pragma warning disable 162
namespace MH.WaterCausticsModules {
/*------------------------------------------------------------------------
URP10.4以上のパッケージを持っていない場合にEffectモジュールを削除。
initializeOnLoad と インポート時に削除を試す
-------------------------------------------------------------------------*/
public class CheckRenderPipeline : AssetPostprocessor {
static private readonly string classFileName = $"{typeof(CheckRenderPipeline).Name}.cs";
#if WCE_DEVELOPMENT
[MenuItem ("WCM/TestDialog/DeleteEffectModuleDialog")]
static void dialogTest () => showDialogAndWarning ();
static private readonly bool isDeveloping = true;
#else
static private readonly bool isDeveloping = false;
#endif
[InitializeOnLoadMethod]
private static void initializeOnLoad () {
EditorApplication.delayCall += delayCall;
}
private static void delayCall () {
if (isDeveloping) return;
if (findAsset<EffectModuleRef> (out var asset, out var path)) {
if (asset.effectModule) {
var folderPath = AssetDatabase.GetAssetPath (asset.effectModule);
if (!string.IsNullOrEmpty (folderPath) && folderPath.EndsWith (Constant.EFFECT_FOLDER_NAME)) {
deleteEffectFolder (folderPath);
}
}
}
}
private static string s_folderPath;
static void OnPostprocessAllAssets (string [] imported, string [] deleted, string [] moved, string [] movedFrom) {
if (isDeveloping) return;
if (findAsset<EffectModuleRef> (out var asset, out var path) && asset.effectModule) {
var folderPath = AssetDatabase.GetAssetPath (asset.effectModule);
if (!string.IsNullOrEmpty (folderPath) && folderPath.EndsWith (Constant.EFFECT_FOLDER_NAME)) {
if (imported.Any (a => a.StartsWith (folderPath))) {
// Shaderファイルなどを先に削除 ※Shader Error回避
deleteWithoutMat (folderPath);
// フォルダの削除はDelayCallで行う ※MaterialPostprocessorでのNullエラー回避
s_folderPath = folderPath;
EditorApplication.delayCall += () => deleteEffectFolder (s_folderPath);
} else {
deleteEffectFolder (folderPath);
}
}
}
}
static void deleteWithoutMat (string folderPath) {
string [] guids = AssetDatabase.FindAssets ("", new [] { folderPath });
string [] paths = guids.Select (guid => AssetDatabase.GUIDToAssetPath (guid)).Where (p => p.EndsWith (".shader") || p.EndsWith (".asset") || p.EndsWith (".lighting")).ToArray ();
#if UNITY_2020_1_OR_NEWER
AssetDatabase.DeleteAssets (paths, new List<string> ());
#else
foreach (var p in paths) AssetDatabase.DeleteAsset (p);
#endif
}
static void deleteEffectFolder (string folderPath) {
if (isDeveloping || string.IsNullOrEmpty (folderPath)) return;
if (AssetDatabase.DeleteAsset (folderPath)) {
AssetDatabase.Refresh ();
showDialogAndWarning ();
}
}
static void showDialogAndWarning () {
// ----- Dialog表示
EditorUtility.DisplayDialog ("Effect module removed.", $"The Effect module of this asset was removed.\n\nBecause the UniversalRP package {Constant.REQUIRE_URP_VER} could not be detected in this project.\n\nSee the Manual for details.\n\n\n({Constant.ASSET_NAME})", "OK");
}
static private bool findAsset<T> (out T asset, out string path) where T : Object {
asset = null;
path = null;
var guids = AssetDatabase.FindAssets ($"t:{typeof (T).ToString()}", new [] { "Assets" }); // ※フォルダ名の最後にスラッシュがあると古いUnityでエラーになるので注意
if (guids.Length == 0) return false;
path = AssetDatabase.GUIDToAssetPath (guids [0]);
asset = AssetDatabase.LoadAssetAtPath<T> (path);
return asset != null;
}
}
}
#endif

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 4373c16da8bab7841b9c9f883fd8ca5c
labels:
- WaterCausticsModules
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,67 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
#if UNITY_EDITOR && WCE_URP
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
namespace MH.WaterCausticsModules {
/*------------------------------------------------------------------------
Previewウィンドウでエフェクトを無効化
-------------------------------------------------------------------------*/
public class DisableInPreviewWindow : IPreprocessBuildWithReport, IPostprocessBuildWithReport {
private static readonly string KEYWORD = "_WCE_DISABLED";
public int callbackOrder => 1;
public void OnPreprocessBuild (BuildReport report) {
// ビルド前処理
disableEvent ();
}
public void OnPostprocessBuild (BuildReport report) {
// ビルド後処理
enableEvent ();
}
[InitializeOnLoadMethod]
static void OnInitialize () {
// Editor起動直後、プレイ開始時
enableEvent ();
}
static private void enableEvent () {
WaterCausticsEffectFeature.onCamRender -= onEnqueue;
WaterCausticsEffectFeature.onCamRender += onEnqueue;
}
static private void disableEvent () {
Shader.DisableKeyword (KEYWORD);
WaterCausticsEffectFeature.onCamRender -= onEnqueue;
}
static private void onEnqueue (Camera cam) {
if (cam.cameraType == CameraType.Preview)
Shader.EnableKeyword (KEYWORD);
else
Shader.DisableKeyword (KEYWORD);
}
public class ShaderPreprocessor : IPreprocessShaders {
// シェーダバリアント削除
public int callbackOrder => 1;
public void OnProcessShader (Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data) {
var deleteKeyword = new ShaderKeyword (DisableInPreviewWindow.KEYWORD);
for (var i = data.Count - 1; i >= 0; --i)
if (data [i].shaderKeywordSet.IsEnabled (deleteKeyword))
data.RemoveAt (i);
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8326956518b565e44a9cb612c6b7f963
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 276d321c87e0f494fb5e61d87eb50ad6, type: 3}
m_Name: EffectModuleRef
m_EditorClassIdentifier:
m_effectModule: {fileID: 102900000, guid: 557c32d7360c26949947ac9dd8c308fc, type: 3}
m_customFunc: {fileID: 102900000, guid: 5c458e7b6a8f6024aba7bed0ab7b3320, type: 3}
m_packageForASE: {fileID: 102900000, guid: cd1a5fad241143647aa5403add1f9fd8, type: 3}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9e71d80f209a88447bb8a57499210fd2
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
#if UNITY_EDITOR
using UnityEngine;
namespace MH.WaterCausticsModules {
#if WCE_DEVELOPMENT
[CreateAssetMenu]
#endif
public class EffectModuleRef : ScriptableObject {
// WaterCausticEffectフォルダ
[SerializeField] private Object m_effectModule;
internal Object effectModule => m_effectModule;
// ShaderFunctionsフォルダ
[SerializeField] private Object m_customFunc;
internal Object customFunc => m_customFunc;
// ForAmplifyShaderEditor.unitypackageファイル
[SerializeField] private Object m_packageForASE;
internal Object packageForASE => m_packageForASE;
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 276d321c87e0f494fb5e61d87eb50ad6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
#if UNITY_EDITOR && WCE_URP && AMPLIFY_SHADER_EDITOR
using UnityEditor;
using UnityEngine;
#pragma warning disable 162
namespace MH.WaterCausticsModules {
/*------------------------------------------------------------------------
AmplifyShaderEditorがある環境でアセットがインポートされた際、
AmplifyShaderEditor用のカスタムファンクションパッケージを自動インポート
-------------------------------------------------------------------------*/
public class ImportPackageForASE {
#if WCE_DEVELOPMENT
static private readonly bool isDeveloping = true;
#else
static private readonly bool isDeveloping = false;
#endif
[InitializeOnLoadMethod]
public static void registerCallback () {
AssetDatabase.importPackageCompleted -= importCompleted;
AssetDatabase.importPackageCompleted += importCompleted;
}
static void importCompleted (string packageName) {
if (isDeveloping) return;
if (Constant.CheckPackageName (packageName) && findAsset<EffectModuleRef> (out var asset, out var path) && asset.packageForASE) {
string packagePath = AssetDatabase.GetAssetPath (asset.packageForASE);
if (packagePath != null && packagePath.EndsWith (Constant.ASE_PACKAGE_NAME)) {
AssetDatabase.ImportPackage (packagePath, false);
AssetDatabase.DeleteAsset (packagePath);
AssetDatabase.Refresh ();
}
}
}
static private bool findAsset<T> (out T asset, out string path) where T : Object {
asset = null;
path = null;
var guids = AssetDatabase.FindAssets ($"t:{typeof (T).ToString()}", new [] { "Assets" });
if (guids.Length == 0) return false;
path = AssetDatabase.GUIDToAssetPath (guids [0]);
asset = AssetDatabase.LoadAssetAtPath<T> (path);
return asset != null;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: abb96ab8cc4602440a3c0f2ddea9415e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
#if UNITY_EDITOR && WCE_URP
using System.Collections.Generic;
using UnityEditor.Build;
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
namespace MH.WaterCausticsModules {
/*------------------------------------------------------------------------
ビルド時シェーダバリアント削除
-------------------------------------------------------------------------*/
public class OptimizeShader : IPreprocessShaders {
public int callbackOrder => 1;
private readonly ShaderKeyword [] delKeys = {
new ShaderKeyword ("WCE_DEBUG_NORMAL"),
new ShaderKeyword ("WCE_DEBUG_DEPTH"),
new ShaderKeyword ("WCE_DEBUG_FACING"),
new ShaderKeyword ("WCE_DEBUG_CAUSTICS"),
new ShaderKeyword ("WCE_DEBUG_AREA"),
#if !WCE_URP_12_0 // URP12より下の場合
new ShaderKeyword ("_GBUFFER_NORMALS_OCT"),
new ShaderKeyword ("_LIGHT_LAYERS"),
new ShaderKeyword ("_LIGHT_COOKIES"),
#endif
#if !WCE_URP_14_0 // URP14より下の場合
new ShaderKeyword ("_FORWARD_PLUS"),
#endif
};
private bool hasDelKey (ShaderKeywordSet set) {
foreach (var key in delKeys)
if (set.IsEnabled (key))
return true;
return false;
}
public void OnProcessShader (Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data) {
// シェーダ名チェック
if (!shader.name.StartsWith (Constant.SHADER_NAME_HEADER)) return;
// キーを持っているか確認 & 削除
for (var i = data.Count - 1; i >= 0; --i) {
if (hasDelKey (data [i].shaderKeywordSet))
data.RemoveAt (i);
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cacb7892edc3def4f8111ae95a40c233
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
#if UNITY_EDITOR && WCE_URP
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace MH.WaterCausticsModules {
/*------------------------------------------------------------------------
「missing SubGraph references」エラーを防ぐため、このスクリプトが移動した
ことを検知した際にShaderFunctionsフォルダを再インポート。
-------------------------------------------------------------------------*/
public class ReimportFunctions : AssetPostprocessor {
static private readonly string classFileName = $"{typeof (ReimportFunctions).Name}.cs";
static void OnPostprocessAllAssets (string [] imported, string [] deleted, string [] moved, string [] movedFrom) {
// ※移動時と名称変更時、フォルダは importedAssetsとmovedAssetsに入るので注意、アセットはmovedAssetsのみ
if (moved.Any (a => a.EndsWith (classFileName)) && !imported.Any (a => a.EndsWith (classFileName))) {
if (findAsset<EffectModuleRef> (out var asset, out var path) && asset.customFunc) {
var folderPath = AssetDatabase.GetAssetPath (asset.customFunc);
if (!string.IsNullOrEmpty (folderPath) && moved.Any (a => a == folderPath)) {
AssetDatabase.ImportAsset (folderPath, ImportAssetOptions.ImportRecursive);
}
}
}
}
static private bool findAsset<T> (out T asset, out string path) where T : Object {
asset = null;
path = null;
var guids = AssetDatabase.FindAssets ($"t:{typeof (T).ToString()}", new [] { "Assets" });
if (guids.Length == 0) return false;
path = AssetDatabase.GUIDToAssetPath (guids [0]);
asset = AssetDatabase.LoadAssetAtPath<T> (path);
return asset != null;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 262d8e92d9439494bbb7bb9d66dee10a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 71d38c23289312844bc6e88159ed75c7, type: 3}
m_Name: WaterCausticsEffectData
m_EditorClassIdentifier:
m_autoManageFeature: 1

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 062a4f28f6d145a4798a42cbe2bf6992
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
#if UNITY_EDITOR && WCE_URP
using System.IO;
using UnityEditor;
using UnityEngine;
namespace MH.WaterCausticsModules {
public class WaterCausticsEffectData : ScriptableObject {
// ---------------------------------------------------------
[SerializeField] internal bool m_autoManageFeature = true;
internal bool AutoManageFeature => m_autoManageFeature;
// ---------------------------------------------------------
static private WaterCausticsEffectData _s_ins;
static internal WaterCausticsEffectData GetAsset () {
if (_s_ins) return _s_ins;
if (findAsset<WaterCausticsEffectData> (out var ins, out var path)) return _s_ins = ins;
return _s_ins = createAsset<WaterCausticsEffectData> ();
}
static private bool findAsset<T> (out T asset, out string path) where T : Object {
asset = null;
path = null;
var guids = AssetDatabase.FindAssets ($"t:{typeof (T).ToString()}", new [] { "Assets" });
if (guids.Length == 0) return false;
path = AssetDatabase.GUIDToAssetPath (guids [0]);
asset = AssetDatabase.LoadAssetAtPath<T> (path);
return asset != null;
}
static private T createAsset<T> () where T : ScriptableObject {
T asset = ScriptableObject.CreateInstance<T> ();
MonoScript mono = MonoScript.FromScriptableObject (asset);
string scriptPath = AssetDatabase.GetAssetPath (mono);
string folderPath = Path.GetDirectoryName (scriptPath).Replace ("\\", "/");
string path = $"{folderPath}/{Path.GetFileNameWithoutExtension (scriptPath)}.asset";
AssetDatabase.CreateAsset (asset, path);
AssetDatabase.SaveAssets ();
return asset;
}
// ---------------------------------------------------------
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 71d38c23289312844bc6e88159ed75c7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,935 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
#if UNITY_EDITOR && WCE_URP
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using MH.WaterCausticsModules.Effect;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace MH.WaterCausticsModules {
[CanEditMultipleObjects]
[CustomEditor (typeof (WaterCausticsEffect))]
public class WaterCausticsEffectEditor : Editor {
private SerializedProperty m_method;
private SerializedProperty m_normalSrc;
private SerializedProperty m_debugInfo;
private SerializedProperty m_debugMode;
private SerializedProperty m_useLayer;
private SerializedProperty m_renderLayerMask;
private SerializedProperty m_layerMask;
private SerializedProperty m_clipOutside;
private SerializedProperty m_texture;
private SerializedProperty m_textureChannel;
private SerializedProperty m_textureRotation;
private SerializedProperty m_texRotSinCos;
private SerializedProperty m_useRandomTiling;
private SerializedProperty m_tilingSeed;
private SerializedProperty m_tilingRotation;
private SerializedProperty m_tilingHardness;
private SerializedProperty m_intensity;
private SerializedProperty m_mainLit;
private SerializedProperty m_addLit;
private SerializedProperty m_colorShift;
private SerializedProperty m_colorShiftDir;
private SerializedProperty m_scale;
private SerializedProperty m_surfaceY;
private SerializedProperty m_surfFadeStart;
private SerializedProperty m_surfFadeEnd;
private SerializedProperty m_useDepthFade;
private SerializedProperty m_depthFadeStart;
private SerializedProperty m_depthFadeEnd;
private SerializedProperty m_useDistanceFade;
private SerializedProperty m_distanceFadeStart;
private SerializedProperty m_distanceFadeEnd;
private SerializedProperty m_litSaturation;
private SerializedProperty m_multiply;
private SerializedProperty m_normalAttenRate;
private SerializedProperty m_normalAtten;
private SerializedProperty m_transparentBackside;
private SerializedProperty m_backsideShadow;
private SerializedProperty m_shadowIntensity;
private SerializedProperty m_receiveShadows;
private SerializedProperty m_useMainLit;
private SerializedProperty m_useAddLit;
private SerializedProperty m_useImageMask;
private SerializedProperty m_imageMaskTexture;
private SerializedProperty m_stencilRef;
private SerializedProperty m_stencilReadMask;
private SerializedProperty m_stencilWriteMask;
private SerializedProperty m_stencilComp;
private SerializedProperty m_stencilPass;
private SerializedProperty m_stencilFail;
private SerializedProperty m_stencilZFail;
private SerializedProperty m_cullMode;
private SerializedProperty m_zWriteMode;
private SerializedProperty m_zTestMode;
private SerializedProperty m_depthOffsetFactor;
private SerializedProperty m_depthOffsetUnits;
private SerializedProperty m_shader;
private SerializedProperty m_noTexture;
private SerializedProperty m_useCustomFunc;
private SerializedProperty m_renderEvent;
private SerializedProperty m_renderEventAdjust;
private void prepProperties () {
if (m_method != null) return;
m_method = serializedObject.FindProperty ("m_method");
m_normalSrc = serializedObject.FindProperty ("m_normalSrc");
m_debugInfo = serializedObject.FindProperty ("m_debugInfo");
m_debugMode = serializedObject.FindProperty ("m_debugMode");
m_useLayer = serializedObject.FindProperty ("m_useLayer");
m_renderLayerMask = serializedObject.FindProperty ("m_renderLayerMask");
m_layerMask = serializedObject.FindProperty ("m_layerMask");
m_clipOutside = serializedObject.FindProperty ("m_clipOutside");
m_texture = serializedObject.FindProperty ("m_texture");
m_textureChannel = serializedObject.FindProperty ("m_textureChannel");
m_textureRotation = serializedObject.FindProperty ("m_textureRotation");
m_texRotSinCos = serializedObject.FindProperty ("m_texRotSinCos");
m_useRandomTiling = serializedObject.FindProperty ("m_useRandomTiling");
m_tilingSeed = serializedObject.FindProperty ("m_tilingSeed");
m_tilingRotation = serializedObject.FindProperty ("m_tilingRotation");
m_tilingHardness = serializedObject.FindProperty ("m_tilingHardness");
m_intensity = serializedObject.FindProperty ("m_intensity");
m_mainLit = serializedObject.FindProperty ("m_mainLit");
m_addLit = serializedObject.FindProperty ("m_addLit");
m_colorShift = serializedObject.FindProperty ("m_colorShift");
m_colorShiftDir = serializedObject.FindProperty ("m_colorShiftDir");
m_scale = serializedObject.FindProperty ("m_scale");
m_surfaceY = serializedObject.FindProperty ("m_surfaceY");
m_surfFadeStart = serializedObject.FindProperty ("m_surfFadeStart");
m_surfFadeEnd = serializedObject.FindProperty ("m_surfFadeEnd");
m_useDepthFade = serializedObject.FindProperty ("m_useDepthFade");
m_depthFadeStart = serializedObject.FindProperty ("m_depthFadeStart");
m_depthFadeEnd = serializedObject.FindProperty ("m_depthFadeEnd");
m_useDistanceFade = serializedObject.FindProperty ("m_useDistanceFade");
m_distanceFadeStart = serializedObject.FindProperty ("m_distanceFadeStart");
m_distanceFadeEnd = serializedObject.FindProperty ("m_distanceFadeEnd");
m_litSaturation = serializedObject.FindProperty ("m_litSaturation");
m_multiply = serializedObject.FindProperty ("m_multiply");
m_normalAttenRate = serializedObject.FindProperty ("m_normalAttenRate");
m_normalAtten = serializedObject.FindProperty ("m_normalAtten");
m_transparentBackside = serializedObject.FindProperty ("m_transparentBackside");
m_backsideShadow = serializedObject.FindProperty ("m_backsideShadow");
m_shadowIntensity = serializedObject.FindProperty ("m_shadowIntensity");
m_receiveShadows = serializedObject.FindProperty ("m_receiveShadows");
m_useMainLit = serializedObject.FindProperty ("m_useMainLit");
m_useAddLit = serializedObject.FindProperty ("m_useAddLit");
m_useImageMask = serializedObject.FindProperty ("m_useImageMask");
m_imageMaskTexture = serializedObject.FindProperty ("m_imageMaskTexture");
m_stencilRef = serializedObject.FindProperty ("m_stencilRef");
m_stencilReadMask = serializedObject.FindProperty ("m_stencilReadMask");
m_stencilWriteMask = serializedObject.FindProperty ("m_stencilWriteMask");
m_stencilComp = serializedObject.FindProperty ("m_stencilComp");
m_stencilPass = serializedObject.FindProperty ("m_stencilPass");
m_stencilFail = serializedObject.FindProperty ("m_stencilFail");
m_stencilZFail = serializedObject.FindProperty ("m_stencilZFail");
m_cullMode = serializedObject.FindProperty ("m_cullMode");
m_zWriteMode = serializedObject.FindProperty ("m_zWriteMode");
m_zTestMode = serializedObject.FindProperty ("m_zTestMode");
m_depthOffsetFactor = serializedObject.FindProperty ("m_depthOffsetFactor");
m_depthOffsetUnits = serializedObject.FindProperty ("m_depthOffsetUnits");
m_shader = serializedObject.FindProperty ("m_shader");
m_noTexture = serializedObject.FindProperty ("m_noTexture");
m_useCustomFunc = serializedObject.FindProperty ("m_useCustomFunc");
m_renderEvent = serializedObject.FindProperty ("m_renderEvent");
m_renderEventAdjust = serializedObject.FindProperty ("m_renderEventAdjust");
}
private SerializedObject _wceData;
private SerializedProperty m_autoManageFeature;
private SerializedObject prepWceData () {
if (_wceData == null)
_wceData = new SerializedObject (WaterCausticsEffectData.GetAsset ());
if (m_autoManageFeature == null) {
m_autoManageFeature = _wceData.FindProperty ("m_autoManageFeature");
}
_wceData.Update ();
return _wceData;
}
static readonly GUIContent [] _cullingEnumStr = {
new GUIContent ("Both"),
new GUIContent ("Back"),
new GUIContent ("Front"),
};
static readonly string descGenFromDepth = "[Generate from Depth] \nGenerate from _CameraDepthTexture generated by the system. This method is not good for smooth surfaces, but it does produce the correct normals.";
static readonly string descCamNormalTex = "[Camera Normals Tex] \nSampling _CameraNormalsTexture generated by the system. This is high quality, but may produce strange results with materials that do not support normal output.";
static readonly GUIContent [] _normalSrcStr = {
new GUIContent ("Generate from Depth (LQ)", $"{descGenFromDepth}\n\n{descCamNormalTex}"),
new GUIContent ("Camera Normals Tex (HQ)", $"{descGenFromDepth}\n\n{descCamNormalTex}"),
};
static readonly string [] _renderingLayerMaskNamesSpare = { "Layer1", "Layer2", "Layer3", "Layer4", "Layer5", "Layer6", "Layer7", "Layer8", "Layer9", "Layer10", "Layer11", "Layer12", "Layer13", "Layer14", "Layer15", "Layer16", "Layer17", "Layer18", "Layer19", "Layer20", "Layer21", "Layer22", "Layer23", "Layer24", "Layer25", "Layer26", "Layer27", "Layer28", "Layer29", "Layer30", "Layer31", "Layer32", };
private List<ScriptableRendererData> _rendererDataList;
private bool _hasGetRenderData;
protected virtual void OnEnable () {
foreach (var tar in targets.OfType<WaterCausticsEffect> ())
tar.VersionCheck ();
}
UniversalRenderPipelineAsset urpAsset => GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
public override void OnInspectorGUI () {
if (urpAsset) {
var wceData = prepWceData ();
prepProperties ();
serializedObject.Update ();
using (var check = new EditorGUI.ChangeCheckScope ()) {
drawProperties ();
serializedObject.ApplyModifiedProperties ();
wceData.ApplyModifiedProperties ();
if (check.changed) {
foreach (var tar in targets.OfType<WaterCausticsEffect> ())
tar.OnInspectorChanged ();
}
}
} else {
// -- URP設定が完了していない場合
onInspectorGUI_NotSettingYet ();
}
}
// -----------------------------------------------------------
readonly Color colorPinkBar = new Color (1f, 0.3f, 0.6f, 0.3f);
readonly Color colorPinkContent = new Color (1f, 0.3f, 0.6f, 1f);
readonly Color colorGreenContent = new Color (0.4f, 1f, 0.7f, 1f);
private float lineH = EditorGUIUtility.singleLineHeight + 2;
readonly float SPACE_SUB_TOP_5 = 5f;
readonly float SPACE_SUB_BTM_12 = 12f;
readonly float SPACE_MAIN_TOP_7 = 7f;
readonly float SPACE_MAIN_BTM_5 = 5f;
private Color _defaultGUIColor;
private void setLabelAreaWidth (float labelWidthMin, float valWidthMin) {
if (EditorGUIUtility.labelWidth < labelWidthMin)
EditorGUIUtility.labelWidth = labelWidthMin;
if (EditorGUIUtility.currentViewWidth - EditorGUIUtility.labelWidth < valWidthMin)
EditorGUIUtility.labelWidth = EditorGUIUtility.currentViewWidth - valWidthMin;
}
private void drawProperties () {
_defaultGUIColor = GUI.color;
storeIndentWidth ();
EditorGUIUtility.labelWidth += 6;
EditorGUI.indentLevel++;
setLabelAreaWidth (labelWidthMin: 140f, valWidthMin: 170f);
bool isEditingMultiObj = serializedObject.isEditingMultipleObjects;
bool isMethodEach = (m_method.enumValueIndex == (int) Method.EachMesh && !m_method.hasMultipleDifferentValues);
bool isMethodOnce = (m_method.enumValueIndex == (int) Method.AtOnce && !m_method.hasMultipleDifferentValues);
// ---------------------------------------------------------------------------------- System
// ------ RendererFeature
if (!_hasGetRenderData) {
_hasGetRenderData = true;
WaterCausticsEffectFeatureEditor.GetAllRendererData (out _rendererDataList);
if (WaterCausticsEffectData.GetAsset ().AutoManageFeature)
WaterCausticsEffectFeatureEditor.AddFeatureToAllRenderers (_rendererDataList, useUndo : false);
}
bool someRenderNotHasFeature = !WaterCausticsEffectFeatureEditor.CheckAllHasActiveFeature (_rendererDataList);
bool currentRenderNotHasFeature = someRenderNotHasFeature && !WaterCausticsEffectFeature.effective;
// ----------------------
if (expandMainGroup (m_method, true, "System", isPink : currentRenderNotHasFeature)) {
EditorGUILayout.Space (SPACE_MAIN_TOP_7);
// ------ RendererFeature
if (someRenderNotHasFeature) {
EditorGUI.indentLevel -= 1;
// labelWarning("To apply this effect, a Renderer Feature needs to be added to a Renderer.", 11);
using (new ColorScope (currentRenderNotHasFeature ? colorPinkContent : _defaultGUIColor)) {
string str = "To apply this effect, a Renderer Feature needs to be added to a Renderer.";
if (currentRenderNotHasFeature)
EditorGUILayout.HelpBox ($"The Renderer Feature has not been added or activated in the current Renderer.\n{str}", MessageType.Warning);
else
EditorGUILayout.HelpBox ($"The Renderer Feature has not been added or activated in some Renderers.\n{str}", MessageType.Warning);
EditorGUILayout.Space (2);
Rect rect = GUILayoutUtility.GetRect (0, 0);
rect.height = lineH;
rect.width *= 0.5f;
if (GUI.Button (rect, new GUIContent ("Select Renderer", "Search and select RendererData assets."), EditorStyles.miniButton)) {
// -- 選択ボタン
if (WaterCausticsEffectFeatureEditor.GetAllRendererData (out var list)) {
// -- 成功
WaterCausticsEffectFeatureEditor.SelectAndPing (list);
if (list.Count >= 2) {
var pathStr = WaterCausticsEffectFeatureEditor.AssetsToPathStr (list);
EditorApplication.delayCall += () => EditorApplication.delayCall += () =>
EditorUtility.DisplayDialog ("Multiple found.", $"Multiple RendererData assets found.\n\n{pathStr}", "OK");
}
} else {
// -- 見つからない
EditorUtility.DisplayDialog ("Not found.", $"Not found.", "OK");
}
}
rect.x += rect.width;
if (GUI.Button (rect, new GUIContent ("Fix It", "Add a Renderer Feature to Renderers."), EditorStyles.miniButton)) {
// -- 追加ボタン
if (WaterCausticsEffectFeatureEditor.AddFeatureToAllRenderers (useUndo: true)) {
// -- 成功
} else {
// -- 失敗
bool showURL = EditorUtility.DisplayDialog ("Failed", "Processing failed. Please add the Renderer Feature to Renderers manually.", "Open URP Manual", "Cancel");
if (showURL) Application.OpenURL (Constant.URL_HOW_TO_ADD_FEATURE);
}
}
EditorGUILayout.Space (lineH);
using (new ColorScope (_defaultGUIColor)) {
EditorGUILayout.Space (1);
if (labelLink (new GUIContent ("How to add Renderer Feature to Renderer", Constant.URL_HOW_TO_ADD_FEATURE), 10))
Application.OpenURL (Constant.URL_HOW_TO_ADD_FEATURE);
EditorGUILayout.Space (4);
}
EditorGUI.indentLevel += 1;
EditorGUILayout.Space (15);
}
}
// ----------------------
EditorGUILayout.PropertyField (m_method, new GUIContent ("Effect Method", "[At Once]\nDraws the effect at once using the camera's depth and normal texture. It can also be applied to objects with materials that are deformed by shaders. \n\n[Each Mesh]\nDraws effects to each mesh. Masking with layers and the surface to be drawn can be specified. However, it cannot be applied to objects with materials that deform with shaders. For such objects, embed custom function in the shader or use the At Once method."));
if (isMethodOnce) {
using (new IndentScope (-2f, 0f)) {
popup (m_normalSrc, _normalSrcStr, "Normal Data", $"How to get normal vector in world space.\n\n{descGenFromDepth}\n\n{descCamNormalTex}");
}
}
drawBoolAndValue (m_debugInfo, m_debugMode, true, new GUIContent ("Debug Info", "Display data for debugging. This is only valid on the editor.\n\n" +
$"[{DebugMode.Normal}]\nDisplays world-space normal data.\n\n" +
$"[{DebugMode.Depth}]\nDisplays the depth data.\n\n" +
$"[{DebugMode.Facing}]\nDisplays the plane facing the camera as bright.\n\n" +
$"[{DebugMode.Caustics}]\nDisplays only caustics effects.\n\n" +
$"[{DebugMode.LightArea}]\nDisplays the affected area by each light.\n\n" +
$"If some objects are not rendering correctly on At Once method with Camera Normals Tex, the object's material is outputting the wrong normals. Check the normals on this screen and modify the material (shader) of the object."));
EditorGUILayout.Space (SPACE_SUB_BTM_12);
// ---------------------------------------------------------------------------------- Influence Scope
if (expandSubGroup (m_useImageMask, true, "Influence Scope")) {
EditorGUILayout.Space (SPACE_SUB_TOP_5);
selectGameObjectLayer (new GUIContent ("Layer", "The layer in which this effect exists."));
if (isMethodEach) {
EditorGUILayout.PropertyField (m_layerMask, new GUIContent ("Layer Mask", "Specify the layer on which the effect will be drawn. Objects on unchecked layers will be ignored."));
}
if (isMethodEach) {
EditorGUILayout.PropertyField (m_clipOutside, new GUIContent ("Clip Outside", "Draw effects only inside the volume."));
}
drawBoolAndValue (m_useImageMask, m_imageMaskTexture, hide : true, new GUIContent ("Image Mask", "Masking with an image."));
if (isMethodEach) {
popup (m_cullMode, _cullingEnumStr, "Render Face", "Which face to draw.");
}
// EditorGUILayout.Space (2);
// if (isExpand (m_renderEventAdjust, false, new GUIContent ("Advanced", "Advanced Settings"))) {
// using (new IndentScope (0f, 0f)) {
EditorGUILayout.PropertyField (m_useCustomFunc, new GUIContent ("Custom Function", "Supports Custom Function for shader. \nTransmits the settings to the WaterCausticsEmissionSync function embedded in the shader. \nTurning this On will copy the settings to a global shader variable. \n\nIf there are multiple effects with this setting On in a scene, the last active effect will be used."));
{
// 描画タイミング設定
bool isExpanded = isExpand (m_renderEvent, true, new GUIContent (""));
EditorGUILayout.Space (-EditorGUIUtility.singleLineHeight - 2f);
string baseTimingName = splitCamelCase (((RenderPassEvent) m_renderEvent.intValue).ToString ());
int sysOpqTiming = (int) WaterCausticsEffect.SYS_OPAQUE_TEX_EVENT;
string sysOpqName = WaterCausticsEffect.SYS_OPAQUE_TEX_EVENT.ToString ();
string sysOpqDesc = $"{sysOpqName}({sysOpqTiming})";
string sysOpqDescPlusOne = $"{sysOpqName}+1 ({sysOpqTiming+1})";
string defaultTiming = WaterCausticsEffect.RENDER_EVENT.ToString ();
int defaultTimingAdj = WaterCausticsEffect.RENDER_EVENT_ADJ;
int baseTiming = m_renderEvent.intValue;
int adjTiming = m_renderEventAdjust.intValue;
int adjusted = baseTiming + adjTiming;
bool isHasDifVal = m_renderEvent.hasMultipleDifferentValues || m_renderEventAdjust.hasMultipleDifferentValues;
EditorGUILayout.LabelField (new GUIContent ("Draw Timing", $"Specifies the timing of drawing.\n\nTo display this effect on _CameraOpaqueTexture, it must be drawn before {sysOpqDesc}."), new GUIContent (isHasDifVal ? "-" : $"{baseTimingName} {(adjTiming < 0 ? "-" : "+")}{Mathf.Abs(adjTiming)} ({Mathf.Clamp(adjusted, 0, 1000)})"));
if (isExpanded) {
using (new IndentScope (-2f, 4f)) {
EditorGUILayout.PropertyField (m_renderEvent, new GUIContent ($"Render Event", $"Controls when the render executes. \n[Default: {defaultTiming}]"));
EditorGUILayout.PropertyField (m_renderEventAdjust, new GUIContent ("Adjustment", $"Controls when the render executes. This number is added to the Draw Timing above. \n[Default: {defaultTimingAdj}]"));
bool isEarly = (adjusted <= sysOpqTiming);
string warning = isEarly ? $"To use a value between 0 and 1 in the Multiply Color setting, set it after {sysOpqDescPlusOne}." :
$"To display this effect on _CameraOpaqueTexture, it must be drawn before {sysOpqDesc}.";
EditorGUILayout.HelpBox (new GUIContent (warning, ""), true);
}
}
}
if (isMethodOnce) {
var names = urpAsset.renderingLayerMaskNames;
if (names == null) names = _renderingLayerMaskNamesSpare;
bitMask (m_renderLayerMask, names, "Render Mask", "Rendering Layer Mask of this effect. It works as same as RenderingLayerMask of MeshRenderer.");
}
if (isMethodEach) {
if (isExpand (m_zTestMode, false, new GUIContent ("Depth Buffer", "Adjust Depth Testing and Depth Offset."))) {
using (new IndentScope (0, 2)) {
EditorGUILayout.PropertyField (m_zWriteMode, new GUIContent ("ZWrite", "Whether to write depth values to the depth buffer."));
EditorGUILayout.PropertyField (m_zTestMode, new GUIContent ("ZTest", "Comparison method with already existing depth values."));
EditorGUILayout.PropertyField (m_depthOffsetFactor, new GUIContent ("Offset Factor", "Offset Factor"));
EditorGUILayout.PropertyField (m_depthOffsetUnits, new GUIContent ("Offset Units", "Offset Units"));
}
}
EditorGUILayout.Space (2);
}
if (isExpand (m_stencilRef, false, new GUIContent ("Stencil Buffer", "The Stencil Buffer can be used to limit the objects to be drawn or used for subsequent effects."))) {
using (new IndentScope (0, 2)) {
EditorGUILayout.PropertyField (m_stencilRef, new GUIContent ("Ref", "Stencil Reference Value"));
EditorGUILayout.PropertyField (m_stencilReadMask, new GUIContent ("ReadMask", "Stencil Read Mask"));
EditorGUILayout.PropertyField (m_stencilWriteMask, new GUIContent ("WriteMask", "Stencil Write Mask"));
EditorGUILayout.PropertyField (m_stencilComp, new GUIContent ("Comp", "Stencil Compare Operation"));
EditorGUILayout.PropertyField (m_stencilPass, new GUIContent ("Pass", "Stencil Pass Operation"));
EditorGUILayout.PropertyField (m_stencilFail, new GUIContent ("Fail", "Stencil Fail Operation"));
EditorGUILayout.PropertyField (m_stencilZFail, new GUIContent ("ZFail", "Stencil Z Fail Operation"));
}
}
// }
// }
}
EditorGUILayout.Space (SPACE_SUB_BTM_12);
}
EditorGUILayout.Space (SPACE_MAIN_BTM_5);
// ---------------------------------------------------------------------------------- Effect Group
bool useTextureWarning = (m_texture.objectReferenceValue == null && isGameObjectOnScene () && !isEditingMultiObj);
if (expandMainGroup (m_texture, true, "Caustics Effect", isPink : useTextureWarning && m_texture.isExpanded)) {
EditorGUILayout.Space (SPACE_MAIN_TOP_7);
if (expandSubGroup (m_textureRotation, true, "Texture", isPink : useTextureWarning)) {
EditorGUILayout.Space (SPACE_SUB_TOP_5);
using (new ColorScope (useTextureWarning?colorPinkContent : GUI.color)) {
EditorGUILayout.PropertyField (m_texture, new GUIContent ("Caustics Texture", $"Set the RenderTexture specified as the output destination in the {typeof(WaterCausticsTexGenerator).Name}."));
if (!m_texture.hasMultipleDifferentValues) {
if (useTextureWarning) {
EditorGUILayout.Space (1);
EditorGUILayout.BeginHorizontal ();
GUILayout.FlexibleSpace ();
if (GUILayout.Button ("Search from this Scene", EditorStyles.miniButton, GUILayout.Width (150))) {
var gen = FindObjectsOfType<WaterCausticsTexGenerator> ().FirstOrDefault (g => g.renderTexture != null);
if (gen != null)
m_texture.objectReferenceValue = gen.renderTexture;
else
EditorUtility.DisplayDialog ("Not Found", $"There is no {typeof(WaterCausticsTexGenerator).Name} with active and having RenderTexture in this scene.", "OK");
}
EditorGUILayout.EndHorizontal ();
EditorGUILayout.Space (3);
} else {
var tex = m_texture.objectReferenceValue as Texture;
if (tex != null) {
EditorGUILayout.HelpBox ($"{tex.width}x{tex.height} / {tex.graphicsFormat}", MessageType.None);
EditorGUILayout.Space (3);
}
}
}
}
EditorGUILayout.PropertyField (m_textureChannel, new GUIContent ("Channel", "Channels to be used. Set to R if using R-channel only textures."));
using (var check = new EditorGUI.ChangeCheckScope ()) {
using (new IndentScope (0f, 0f, 1)) {
EditorGUILayout.PropertyField (m_textureRotation, new GUIContent ("Rotation", "Rotate the texture."));
drawDirMark (m_textureRotation, true);
}
if (check.changed) {
float rad = m_textureRotation.floatValue * Mathf.Deg2Rad;
m_texRotSinCos.vector2Value = new Vector2 (Mathf.Sin (rad), Mathf.Cos (rad));
}
}
EditorGUILayout.PropertyField (m_useRandomTiling, new GUIContent ("Random Tiling", "Use randomized hexagonal tiling. This reduces unnatural repetitions that appear on distant planes."));
if (m_useRandomTiling.boolValue) {
using (new IndentScope (-2f, 0f)) {
EditorGUILayout.PropertyField (m_tilingSeed, new GUIContent ("Seed", "Random seed value."));
EditorGUILayout.PropertyField (m_tilingHardness, new GUIContent ("Hardness", "Edge hardness."));
EditorGUILayout.PropertyField (m_tilingRotation, new GUIContent ("Rotation", "Rotate tiles randomly."));
}
}
EditorGUILayout.Space (SPACE_SUB_BTM_12);
}
if (expandSubGroup (m_scale, true, "Dimensions")) {
EditorGUILayout.Space (SPACE_SUB_TOP_5);
EditorGUILayout.PropertyField (m_scale, new GUIContent ("Scale", "Texture size at the height of the water surface."));
EditorGUILayout.PropertyField (m_surfaceY, new GUIContent ("Water Surface Y", "Height of the water surface. Y-axis. The projected position of the light is calculated with respect to this plane."));
EditorGUILayout.Space (4);
drawStartEndProp (m_surfFadeStart, m_surfFadeEnd, new GUIContent ("Surface Fade", "Attenuates light as it approaches the surface of the water."));
EditorGUILayout.PropertyField (m_useDepthFade, new GUIContent ("Depth Fade", "Attenuates light as depth increases."));
if (m_useDepthFade.boolValue) {
using (new IndentScope (-2f, 0f))
drawStartEndProp (m_depthFadeStart, m_depthFadeEnd, new GUIContent ("Range", "Attenuates light as depth increases."));
}
EditorGUILayout.PropertyField (m_useDistanceFade, new GUIContent ("Distance Fade", "Attenuates light as distance increases."));
if (m_useDistanceFade.boolValue) {
using (new IndentScope (-2f, 0f))
drawStartEndProp (m_distanceFadeStart, m_distanceFadeEnd, new GUIContent ("Range", "Attenuates light as distance increases."));
}
EditorGUILayout.Space (SPACE_SUB_BTM_12);
}
if (expandSubGroup (m_intensity, true, "Effect")) {
EditorGUILayout.Space (SPACE_SUB_TOP_5);
EditorGUILayout.PropertyField (m_intensity, new GUIContent ("Intensity", "Intensity of effect."));
using (new IndentScope (-1f, 1f)) {
drawBoolAndValue (m_useMainLit, m_mainLit, hide : true, new GUIContent ("Main Light", "Adjust the intensity of the main light. If the checkbox is Off, the main light calculation is skipped."));
drawBoolAndValue (m_useAddLit, m_addLit, hide : true, new GUIContent ("Additional Lights", "Adjust the intensity of the additional lights. If the check box is Off, the calculation of additional lights is skipped."));
}
EditorGUILayout.Space (2);
drawBoolAndValue (m_receiveShadows, m_shadowIntensity, hide : true, new GUIContent ("Shadow", "Strength of shadow. If the checkbox is unchecked, the shadow calculation is skipped. \n\nFor the At Once method, \"Transparent Receive Shadows\" in the URP settings must also be set to On."));
EditorGUILayout.Space (2);
EditorGUILayout.PropertyField (m_colorShift, new GUIContent ("Color Shift", "Amount of RGB channel shift."));
if (m_colorShift.floatValue > 0f) {
using (new IndentScope (-2f, 0f, 2)) {
EditorGUILayout.PropertyField (m_colorShiftDir, new GUIContent ("Direction", "Direction of shift for RGB channels."));
drawDirMark (m_colorShiftDir, true);
}
}
EditorGUILayout.Space (2);
EditorGUILayout.PropertyField (m_litSaturation, new GUIContent ("Light Color", "Color intensity of the light."));
EditorGUILayout.Space (2);
EditorGUILayout.PropertyField (m_multiply, new GUIContent ("Multiply Color", "Multiply by the screen color and then add. \nIf this value is 1 or 0, it is processed faster because it uses the shader's Blend function. Otherwise, _CameraOpaqueTexture is sampled and multiplied. If the Draw Timing setting is before _CameraOpaqueTexture is drawn, it is processed as 1."));
if (!isEditingMultiObj && (m_multiply.floatValue > 0f && m_multiply.floatValue < 1f) &&
((m_renderEvent.intValue + m_renderEventAdjust.intValue) <= (int) WaterCausticsEffect.SYS_OPAQUE_TEX_EVENT)
) {
// 描画タイミングが早すぎる場合警告
using (new IndentScope (0f, 0f)) {
EditorGUILayout.HelpBox (new GUIContent ("Only 0 or 1 is valid because the Draw Timing is too early.", "Draw Timing in Influence Scope settings is too early and _CameraOpaqueTexture does not exist, so it cannot be drawn with a setting between 0 and 1. Therefore, it is drawn as 1."), true);
}
}
EditorGUILayout.Space (3);
EditorGUILayout.PropertyField (m_normalAtten, new GUIContent ("Normal Attenuation", "Attenuation due to the angle between the normal and the light ray. \n[Default: 1]")); {
using (new IndentScope (-2f, 2f)) {
using (new DisableScope (m_normalAtten.floatValue > 0f)) {
EditorGUILayout.PropertyField (m_normalAttenRate, new GUIContent ("Rate", "Rate of normal attenuation. How quickly does the light fade. \n[Default: 1.5]"));
EditorGUILayout.PropertyField (m_transparentBackside, new GUIContent ("Transparent", "The intensity of light transmitted to the backside. \n[Default: 0]"));
}
using (new DisableScope (m_receiveShadows.boolValue && (m_normalAtten.floatValue < 1f || m_transparentBackside.floatValue > 0f))) {
EditorGUILayout.PropertyField (m_backsideShadow, new GUIContent ("Backside Shadow", "The intensity of shadow on the backside. \n[Default: 0]"));
}
}
}
EditorGUILayout.Space (SPACE_SUB_BTM_12);
}
}
EditorGUILayout.Space (SPACE_MAIN_BTM_5);
// ---------------------------------------------------------------------------------- Advanced Settings
if (expandMainGroup (m_backsideShadow, false, "Advanced Settings")) {
EditorGUILayout.Space (SPACE_MAIN_TOP_7);
if (expandSubGroup (m_normalAttenRate, false, "Renderer Feature")) {
EditorGUILayout.Space (SPACE_SUB_TOP_5);
using (var check = new EditorGUI.ChangeCheckScope ()) {
EditorGUILayout.PropertyField (m_autoManageFeature, new GUIContent ("Auto-Management", "Automatically manages Renderer Feature. It automatically adds the Renderer Feature for this effect to all Renderer Data in the project.\n\nIf this is turned off, it is required to manually add the Renderer Feature to the Renderer Data."));
if (check.changed && m_autoManageFeature.boolValue == true) {
WaterCausticsEffectFeatureEditor.AddFeatureToAllRenderers (useUndo: false);
}
if (!m_autoManageFeature.boolValue) {
EditorGUILayout.Space (1);
EditorGUI.indentLevel++;
EditorGUILayout.HelpBox ("To apply this effect, a Renderer Feature needs to be added to a Renderer.", MessageType.None);
EditorGUILayout.Space (1);
if (labelLink (new GUIContent ("How to add Renderer Feature to Renderer", Constant.URL_HOW_TO_ADD_FEATURE), 10, 2))
Application.OpenURL (Constant.URL_HOW_TO_ADD_FEATURE);
EditorGUI.indentLevel--;
}
}
EditorGUILayout.Space (SPACE_SUB_BTM_12);
}
}
EditorGUILayout.Space (SPACE_SUB_BTM_12);
EditorGUILayout.Space (SPACE_MAIN_BTM_5);
EditorGUILayout.Space (SPACE_MAIN_BTM_5);
EditorGUI.indentLevel--;
}
// ----------------------------------------------------------- Parts
private string splitCamelCase (string str) {
return Regex.Replace (
Regex.Replace (str, @"(\P{Ll})(\P{Ll}\p{Ll})", "$1 $2"), @"(\p{Ll})(\P{Ll})", "$1 $2");
}
private float _indentWidth;
private void storeIndentWidth () {
if (_indentWidth != 0f) return;
var x0 = EditorGUI.IndentedRect (Rect.zero).x;
EditorGUI.indentLevel++;
_indentWidth = EditorGUI.IndentedRect (Rect.zero).x - x0;
EditorGUI.indentLevel--;
}
private void drawBoolAndValue (SerializedProperty propBool, SerializedProperty propValue, bool hide, GUIContent label) {
EditorGUILayout.PropertyField (propBool, label);
var rect = GUILayoutUtility.GetLastRect ();
var labelW = EditorGUIUtility.labelWidth;
if (!hide || propBool.boolValue) {
using (new DisableScope (propBool.boolValue)) {
EditorGUIUtility.labelWidth += 25;
EditorGUI.PropertyField (rect, propValue, new GUIContent (" "));
}
}
EditorGUIUtility.labelWidth = labelW;
}
private void selectGameObjectLayer (GUIContent label) {
var gameObjects = targets.Select (t => (t as WaterCausticsEffect).gameObject).ToArray ();
var layers = gameObjects.Select (go => go.layer).Distinct ().ToArray ();
using (var check = new EditorGUI.ChangeCheckScope ()) {
EditorGUI.showMixedValue = (layers.Length != 1);
int newVal = EditorGUILayout.LayerField (label, layers [0]);
EditorGUI.showMixedValue = false;
if (check.changed) {
Undo.RecordObjects (gameObjects, "Changed Layer");
foreach (var go in gameObjects) {
go.layer = newVal;
EditorUtility.SetDirty (go);
}
if (layers.Length >= 2 && gameObjects.Length == Selection.objects.Length) {
// 上部のレイヤー表示Multiple(-)を更新するため選択中の場合は再選択
bool isSelected = true;
foreach (var o in gameObjects) {
if (!Selection.objects.Contains (o)) {
isSelected = false;
break;
}
}
if (isSelected) {
Selection.activeGameObject = null;
EditorApplication.delayCall += () => { Selection.objects = gameObjects; };
}
}
}
}
}
private void selectLayer (SerializedProperty prop, GUIContent label) {
using (var check = new EditorGUI.ChangeCheckScope ()) {
EditorGUI.showMixedValue = prop.hasMultipleDifferentValues;
var newVal = EditorGUILayout.LayerField (label, prop.intValue);
EditorGUI.showMixedValue = false;
if (check.changed)
prop.intValue = newVal;
}
}
private void drawStartEndProp (SerializedProperty propStt, SerializedProperty propEnd, GUIContent label) {
EditorGUILayout.LabelField (label);
var rect = GUILayoutUtility.GetLastRect ();
rect.x += EditorGUIUtility.labelWidth;
rect.width -= EditorGUIUtility.labelWidth;
drawStartEndPropInRect (rect, propStt, propEnd);
}
private void drawStartEndPropInRect (Rect rect, SerializedProperty propStt, SerializedProperty propEnd) {
bool isWide = (rect.width > 140);
float span = isWide ? 5f : 3f;
var rect2 = rect;
var rect3 = rect;
rect2.width = rect3.width = (rect.width - span) * 0.5f;
rect3.x += rect2.width + span;
var storeIndent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
var labelW = EditorGUIUtility.labelWidth;
using (var check = new EditorGUI.ChangeCheckScope ()) {
EditorGUI.showMixedValue = propStt.hasMultipleDifferentValues;
EditorGUIUtility.labelWidth = isWide? 32 : 10;
float newStt = EditorGUI.FloatField (rect2, isWide ? "Start" : "S", propStt.floatValue);
if (check.changed)
propStt.floatValue = Mathf.Clamp (newStt, 0, propEnd.floatValue);
}
using (var check = new EditorGUI.ChangeCheckScope ()) {
EditorGUI.showMixedValue = propEnd.hasMultipleDifferentValues;
EditorGUIUtility.labelWidth = isWide ? 26 : 10;
float newEnd = EditorGUI.FloatField (rect3, isWide ? "End" : "E", propEnd.floatValue);
if (check.changed)
propEnd.floatValue = Mathf.Max (newEnd, propStt.floatValue);
}
EditorGUI.showMixedValue = false;
EditorGUIUtility.labelWidth = labelW;
EditorGUI.indentLevel = storeIndent;
}
static private Color colorMulAlpha (Color c, float mulAlpha) => new Color (c.r, c.g, c.b, c.a * mulAlpha);
private void drawDirMark (SerializedProperty prop, bool isActive = true) {
EditorGUI.indentLevel--;
var rect = EditorGUI.IndentedRect (GUILayoutUtility.GetLastRect ());
drawDirMark (rect, prop, isActive);
EditorGUI.indentLevel++;
}
private void drawDirMark (Rect rect, SerializedProperty prop, bool isActive = true) {
rect.width = EditorGUIUtility.labelWidth;
Vector2 origin = new Vector2 (CIRCLE_R + 1, rect.height * 0.5f);
float dir = prop.floatValue;
Handles.color = colorMulAlpha (EditorStyles.label.normal.textColor, isActive ? 0.8f : 0.4f);
var tmpMatrix = Handles.matrix;
GUI.BeginClip (rect, origin, Vector2.zero, false);
Handles.matrix = tmpMatrix * Matrix4x4.Scale (Vector3.one * CIRCLE_R);
Handles.DrawAAPolyLine (Texture2D.whiteTexture, 1, circlePts);
if (!prop.hasMultipleDifferentValues) {
Handles.matrix = tmpMatrix * Matrix4x4.Rotate (Quaternion.Euler (0f, 0f, dir)) * Matrix4x4.Scale (Vector3.one * (CIRCLE_R - 0.5f));
Handles.DrawAAConvexPolygon (arrowAry);
}
Handles.matrix = tmpMatrix;
GUI.EndClip ();
}
static private Vector2 dirToVec (float dir) => new Vector2 (Mathf.Sin (dir * Mathf.Deg2Rad), -Mathf.Cos (dir * Mathf.Deg2Rad));
const float CIRCLE_R = 5f;
static readonly private Vector3 [] arrowAry = {
dirToVec (0),
dirToVec (150f),
dirToVec (170f),
dirToVec (-170f),
dirToVec (-150f),
};
static readonly private Vector3 [] circlePts = {
new Vector3 (-1f, 0f),
new Vector3 (-0.87f, -0.5f),
new Vector3 (-0.5f, -0.87f),
new Vector3 (0f, -1f),
new Vector3 (0.5f, -0.87f),
new Vector3 (0.87f, -0.5f),
new Vector3 (1f, 0f),
new Vector3 (0.87f, 0.5f),
new Vector3 (0.5f, 0.87f),
new Vector3 (0f, 1f),
new Vector3 (-0.5f, 0.87f),
new Vector3 (-0.87f, 0.5f),
new Vector3 (-1f, 0f),
};
bool isGameObjectOnScene () {
return (target as Component).gameObject.scene.IsValid ();
}
private void drawRectMain (bool isPink = false) {
Color color = isPink ? colorPinkBar : new Color (0f, 0f, 0f, 0.2f);
Rect rect = GUILayoutUtility.GetRect (0, 0);
rect.height = lineH;
rect.x -= _indentWidth + 4;
rect.width += _indentWidth + 8;
EditorGUI.DrawRect (rect, color);
}
private bool expandMainGroup (SerializedProperty prop, bool defOpen, string label, bool isPink = false) {
EditorGUI.indentLevel--;
drawRectMain (isPink);
bool expand = isExpand (prop, true, new GUIContent (label));
EditorGUI.indentLevel++;
return expand;
}
private bool expandSubGroup (SerializedProperty prop, bool defOpen, string label, bool isPink = false) {
Color color = isPink ? colorPinkBar : colorMulAlpha (EditorStyles.label.normal.textColor, 0.1f);
GUILayout.Label (" ");
Rect rect = GUILayoutUtility.GetLastRect ();
rect.y += 1f;
rect.x += 3f;
rect.width -= 3f;
Rect rect2 = rect;
rect2.x -= 14f;
rect2.width += 15f;
EditorGUI.DrawRect (rect2, color);
GUI.Label (rect, label);
if (prop == null) {
return true;
} else {
rect.x -= 14;
prop.isExpanded = EditorGUI.Foldout (rect, prop.isExpanded != defOpen, " ") != defOpen;
return prop.isExpanded != defOpen;
}
}
private void popup (SerializedProperty prop, GUIContent [] enumStr, string text, string tooltip) {
using (var check = new EditorGUI.ChangeCheckScope ()) {
EditorGUI.showMixedValue = prop.hasMultipleDifferentValues;
var newVal = EditorGUILayout.Popup (new GUIContent (text, tooltip), prop.enumValueIndex, enumStr);
EditorGUI.showMixedValue = false;
if (check.changed)
prop.enumValueIndex = newVal;
}
}
private void popup (SerializedProperty prop, string [] enumStr, string text, string tooltip) {
using (var check = new EditorGUI.ChangeCheckScope ()) {
EditorGUI.showMixedValue = prop.hasMultipleDifferentValues;
var newVal = EditorGUILayout.Popup (new GUIContent (text, tooltip), prop.enumValueIndex, enumStr);
EditorGUI.showMixedValue = false;
if (check.changed)
prop.enumValueIndex = newVal;
}
}
private void bitMask (SerializedProperty prop, string [] options, string text, string tooltip) {
using (var check = new EditorGUI.ChangeCheckScope ()) {
EditorGUI.showMixedValue = prop.hasMultipleDifferentValues;
int newVal = EditorGUILayout.MaskField (new GUIContent (text, tooltip), prop.intValue, options);
EditorGUI.showMixedValue = false;
if (check.changed) {
uint uintVal = unchecked ((uint) newVal);
prop.longValue = uintVal;
}
}
}
private bool isExpand (SerializedProperty prop, bool isDefaultOpen, GUIContent label) {
prop.isExpanded = EditorGUILayout.Foldout (prop.isExpanded != isDefaultOpen, label) != isDefaultOpen;
return prop.isExpanded != isDefaultOpen;
}
private bool isExpandMarkOnly (SerializedProperty prop, bool isDefaultOpen, float adjustX = 0f, float adjustY = 0f) {
var rect = GUILayoutUtility.GetLastRect ();
prop.isExpanded = EditorGUI.Foldout (rect, prop.isExpanded != isDefaultOpen, " ") != isDefaultOpen;
return prop.isExpanded != isDefaultOpen;
}
private void drawLine () {
Rect r = GUILayoutUtility.GetRect (0, 0);
Color col = colorMulAlpha (EditorStyles.label.normal.textColor, 0.09f);
EditorGUI.DrawRect (new Rect (r.x + 14f, r.y, r.width - 14f, 1f), col);
}
// ----------------------------------------------------------- Scope
private class AdjustLabelSpaceWidthScope : GUI.Scope {
private readonly float _store;
internal AdjustLabelSpaceWidthScope (float adjust) {
_store = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth += adjust;
}
protected override void CloseScope () {
EditorGUIUtility.labelWidth = _store;
}
}
private class LabelAndIndentScope : GUI.Scope {
private float _spaceBtm;
internal LabelAndIndentScope (GUIContent label, float spaceTop = 0f, float spaceMid = 2f, float spaceBtm = 0f) {
_spaceBtm = spaceBtm;
EditorGUILayout.Space (spaceTop);
EditorGUILayout.LabelField (label);
EditorGUILayout.Space (spaceMid);
EditorGUI.indentLevel++;
}
protected override void CloseScope () {
EditorGUI.indentLevel--;
EditorGUILayout.Space (_spaceBtm);
}
}
private class IndentScope : GUI.Scope {
private float _spaceBtm;
private int _indent;
internal IndentScope (float spaceTop = 3f, float spaceBtm = 10f, int indent = 1) {
_spaceBtm = spaceBtm;
_indent = indent;
EditorGUILayout.Space (spaceTop);
EditorGUI.indentLevel += indent;
}
protected override void CloseScope () {
EditorGUI.indentLevel -= _indent;
EditorGUILayout.Space (_spaceBtm);
}
}
private class DisableScope : GUI.Scope {
private readonly bool _tmp;
internal DisableScope (bool isActive = false) {
_tmp = isActive;
if (!_tmp) EditorGUI.BeginDisabledGroup (true);
}
protected override void CloseScope () {
if (!_tmp) EditorGUI.EndDisabledGroup ();
}
}
private class ColorScope : GUI.Scope {
private readonly Color _tmp;
internal ColorScope (Color color) {
_tmp = GUI.color;
GUI.color = color;
}
protected override void CloseScope () {
GUI.color = _tmp;
}
}
// ----------------------------------------------------------- URPパッケージは在るけれど 設定が完了していない場合
private bool isDark => EditorGUIUtility.isProSkin;
private GUIStyle _textStyle, _linkUrlStyle, _warningStyle;
private void prepStyle () {
if (_textStyle == null) {
_textStyle = new GUIStyle (EditorStyles.label);
_textStyle.wordWrap = true;
_textStyle.fontSize = 14;
_linkUrlStyle = new GUIStyle (_textStyle);
_linkUrlStyle.wordWrap = false;
_linkUrlStyle.normal.textColor = isDark ? new Color (0f, 0.8f, 1f, 1f) : new Color (0f, 0.4f, 0.8f, 1f);
_linkUrlStyle.hover.textColor = _linkUrlStyle.normal.textColor + Color.white * (isDark ? 0.3f : 0.2f);
_linkUrlStyle.stretchWidth = false;
_warningStyle = new GUIStyle (_textStyle);
_warningStyle.normal.textColor = isDark ? Color.white : new Color (0.8f, 0.1f, 0f, 1f);
_warningStyle.normal.background = Texture2D.whiteTexture;
}
}
private void onInspectorGUI_NotSettingYet () {
prepStyle ();
storeIndentWidth ();
EditorGUILayout.Space (50);
labelWarning ($"UniversalRP setup is not completed.");
EditorGUILayout.Space (10);
GUILayout.Label ($"Please refer to the Unity manual page to setup, or create a new project with the URP 3D template.", _textStyle);
EditorGUILayout.Space (10);
var urlA = "https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@12.0/manual/InstallURPIntoAProject.html";
if (labelLink (new GUIContent ("Unity URP Setup Manual", urlA)))
Application.OpenURL (urlA);
EditorGUILayout.Space (5);
var urlB = Constant.URL_MANUAL;
if (labelLink (new GUIContent ("Asset Manual", urlB)))
Application.OpenURL (urlB);
EditorGUILayout.Space (50);
}
void labelWarning (string text, int fontSize = 18) {
prepStyle ();
_warningStyle.fontSize = fontSize;
Color tmp = GUI.backgroundColor;
GUI.backgroundColor = isDark ? new Color (1f, 0.3f, 0.6f, 0.4f) : new Color (1f, 0.3f, 0.6f, 0.5f);
GUILayout.Label (text, _warningStyle);
GUI.backgroundColor = tmp;
}
private bool labelLink (GUIContent label, int fontSize = 14, int indent = 0, params GUILayoutOption [] options) {
prepStyle ();
_linkUrlStyle.fontSize = fontSize;
var rect = GUILayoutUtility.GetRect (label, _linkUrlStyle, options);
rect.x += indent * _indentWidth;
Handles.BeginGUI ();
Handles.color = _linkUrlStyle.normal.textColor;
Handles.DrawLine (new Vector3 (rect.xMin, rect.yMax), new Vector3 (rect.xMax, rect.yMax));
Handles.color = Color.white;
Handles.EndGUI ();
EditorGUIUtility.AddCursorRect (rect, MouseCursor.Link);
return GUI.Button (rect, label, _linkUrlStyle);
}
// -----------------------------------------------------------
}
}
#endif // End of WCE_URP

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5998e4477e64d9b47a7297a804fff2ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,207 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
#if UNITY_EDITOR && WCE_URP
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace MH.WaterCausticsModules {
[CustomEditor (typeof (WaterCausticsEffectFeature), true)]
public class WaterCausticsEffectFeatureEditor : Editor {
private GUIStyle _style;
private void init () {
_style = new GUIStyle (EditorStyles.label);
_style.wordWrap = true;
_style.fontSize -= 1;
}
public override void OnInspectorGUI () {
if (_style == null) init ();
EditorGUILayout.Space (10);
string str = "This Renderer Function is required to apply WaterCausticsEffect.";
GUILayout.Label (str, _style);
}
//--------------------------------------------------------------------------------------------------------------
// static internal bool AddToCurrentRendererData (bool useUndo) {
// if (GetCurrentRendererData (out var dt))
// return AddFeatureToRenderer (dt, useUndo);
// return false;
// }
// static internal bool GetCurrentRendererData (out ScriptableRendererData dt) {
// dt = null;
// try {
// var urpAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
// var propertyInfo = typeof (UniversalRenderPipelineAsset).GetProperty ("scriptableRendererData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
// if (propertyInfo == null) return false;
// dt = propertyInfo.GetValue (urpAsset) as ScriptableRendererData;
// return dt != null;
// } catch { }
// return false;
// }
public class ModificationProcessor : UnityEditor.AssetModificationProcessor {
// WaterCausticsEffectFeatureスクリプトが削除されるとき、登録済みのRendererFeatureを削除
// ※フォルダ削除時、中身のパスは渡されないので注意
private static AssetDeleteResult OnWillDeleteAsset (string deletePath, RemoveAssetOptions options) {
bool isDirectory = File.GetAttributes (deletePath).HasFlag (FileAttributes.Directory);
string scriptPath = getScriptPath<WaterCausticsEffectFeature> ();
if ((isDirectory && scriptPath.StartsWith (deletePath)) || deletePath == scriptPath) {
// RendererFeatureを削除
DeleteAllFeatures ();
}
return AssetDeleteResult.DidNotDelete;
}
}
static private string getScriptPath<T> () where T : ScriptableObject {
T asset = ScriptableObject.CreateInstance<T> ();
MonoScript mono = MonoScript.FromScriptableObject (asset);
string path = AssetDatabase.GetAssetPath (mono);
if (Application.isPlaying) Destroy (asset);
else DestroyImmediate (asset);
return path;
}
#if WCE_DEVELOPMENT
[MenuItem ("WCM/RendererFeatureTest/DeleteAllFeatures")]
#endif
static internal void DeleteAllFeatures () {
if (GetAllRendererData (out var list))
foreach (var dt in list)
DeleteFeature (dt);
}
static internal void DeleteFeature (ScriptableRendererData dt) {
if (!dt) return;
try {
// ※ TODO URPのバージョンが上がるたび処理方法に変更がないか要確認
if (dt.rendererFeatures.Any (a => a is WaterCausticsEffectFeature)) {
if (!EditorUtility.IsPersistent (dt)) return;
var feature = dt.rendererFeatures.FirstOrDefault (a => a is WaterCausticsEffectFeature);
AssetDatabase.RemoveObjectFromAsset (feature);
dt.rendererFeatures.Remove (feature);
dt.SetDirty ();
EditorUtility.SetDirty (dt);
saveAssetSafe (dt);
}
} catch { }
}
static private void saveAssetSafe (Object o) {
// ※AssetDatabase.SaveAssetIfDirty は Unity 2021.1.17、2020.3.16で追加されたので分岐
#if !UNITY_2020_3_OR_NEWER || UNITY_2020_3_0 || UNITY_2020_3_1 || UNITY_2020_3_2 || UNITY_2020_3_3 || UNITY_2020_3_4 || UNITY_2020_3_5 || UNITY_2020_3_6 || UNITY_2020_3_7 || UNITY_2020_3_8 || UNITY_2020_3_9 || UNITY_2020_3_10 || UNITY_2020_3_11 || UNITY_2020_3_12 || UNITY_2020_3_13 || UNITY_2020_3_14 || UNITY_2020_3_15 || UNITY_2021_1
AssetDatabase.SaveAssets ();
#else
AssetDatabase.SaveAssetIfDirty (o);
#endif
}
static internal bool AddFeatureToRenderer (ScriptableRendererData dt, bool useUndo) {
if (!dt) return false;
try {
if (dt.rendererFeatures.Any (a => a is WaterCausticsEffectFeature)) {
// -- すでにある場合 アクティブ化
var feature = dt.rendererFeatures.FirstOrDefault (a => a is WaterCausticsEffectFeature) as WaterCausticsEffectFeature;
//if (!feature.isActive) {
//if (useUndo) Undo.RegisterCompleteObjectUndo (feature, "Feature Set Active");
//feature.SetActive (true);
//EditorUtility.SetDirty (feature);
//saveAssetSafe (dt);
//}
WaterCausticsEffectFeature.OnAddedByScript ();
return true;
} else {
// -- まだ無い場合
// ※ TODO URPのバージョンが上がるたび処理方法に変更がないか要確認 10.9, 12.1, 14.0 OK
if (!EditorUtility.IsPersistent (dt)) return false;
var validateMethod = dt.GetType ().GetMethod ("ValidateRendererFeatures", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (validateMethod == null) return false;
var feature = ScriptableRendererFeature.CreateInstance<WaterCausticsEffectFeature> ();
feature.name = typeof (WaterCausticsEffect).Name;
if (useUndo) {
Undo.RegisterCreatedObjectUndo (feature, "Add Renderer Feature");
Undo.RegisterCompleteObjectUndo (dt, "Add Renderer Feature");
}
AssetDatabase.AddObjectToAsset (feature, dt);
dt.rendererFeatures.Add (feature);
validateMethod.Invoke (dt, null);
dt.SetDirty ();
EditorUtility.SetDirty (dt);
saveAssetSafe (dt);
WaterCausticsEffectFeature.OnAddedByScript ();
return true;
}
} catch { }
return false;
}
static internal bool CheckAllHasActiveFeature (List<ScriptableRendererData> list) {
return list != null && !list.Any (a => checkHasActiveFeature (a) == false);
}
static internal bool checkHasActiveFeature (ScriptableRendererData dt) {
return dt != null && dt.rendererFeatures.Any (a => a is WaterCausticsEffectFeature && a.isActive);
}
static internal bool AddFeatureToAllRenderers (out List<ScriptableRendererData> list, bool useUndo) {
if (GetAllRendererData (out list))
return AddFeatureToAllRenderers (list, useUndo);
return false;
}
#if WCE_DEVELOPMENT
[MenuItem ("WCM/RendererFeatureTest/AddFeatureToAllRenderers")]
#endif
static internal bool AddFeatureToAllRenderers () {
return AddFeatureToAllRenderers (useUndo: true);
}
static internal bool AddFeatureToAllRenderers (bool useUndo) {
if (GetAllRendererData (out var list))
return AddFeatureToAllRenderers (list, useUndo);
return false;
}
static internal bool AddFeatureToAllRenderers (List<ScriptableRendererData> list, bool useUndo) {
if (list == null) return false;
bool result = true;
foreach (var dt in list)
result &= AddFeatureToRenderer (dt, useUndo);
return result;
}
static internal bool GetAllRendererData (out List<ScriptableRendererData> list) {
list = new List<ScriptableRendererData> ();
var guids = AssetDatabase.FindAssets ($"t:{typeof(ScriptableRendererData).ToString()}", new [] { "Assets" });
if (guids.Length == 0) return false;
foreach (var guid in guids) {
var dt = AssetDatabase.LoadAssetAtPath<ScriptableRendererData> (AssetDatabase.GUIDToAssetPath (guid));
list.Add (dt);
}
return list.Count != 0;
}
static internal void SelectAndPing (List<ScriptableRendererData> list) {
Selection.objects = list.ToArray ();
foreach (var dt in list) EditorGUIUtility.PingObject (dt);
}
static internal string AssetsToPathStr (List<ScriptableRendererData> list) {
var str = "";
for (int i = 0; i < list.Count; i++)
str += $"{i+1}: {AssetDatabase.GetAssetPath (list [i])}\n";
return str;
}
//------------------------------------------------------------------------
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d8b3f56e6e68f2d4fbdf5ad470a2fa4d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,48 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
#if UNITY_EDITOR && WCE_URP
using UnityEditor;
using UnityEngine;
namespace MH.WaterCausticsModules {
public class WaterCausticsEffectMenuItem {
[MenuItem ("GameObject/WaterCausticsModules/TexGen and Effect (with RenderTexture Asset)", false, 0)]
static public void CreateTexGenAndEffectPair () {
var texGen = WaterCausticsTexGeneratorMenuItem.CreateTexGeneratorWithRT ();
var effect = createEffectGO ();
effect.texture = texGen.renderTexture;
}
[MenuItem ("GameObject/WaterCausticsModules/Effect", false, 3)]
static public void CreateEffectGO () {
GameObject selectedGO = Selection.activeGameObject;
var effect = createEffectGO ();
var texGen = selectedGO?.GetComponent<WaterCausticsTexGenerator> ();
if (texGen == null)
texGen = Object.FindObjectOfType<WaterCausticsTexGenerator> ();
if (texGen != null)
effect.texture = texGen.renderTexture;
}
static private WaterCausticsEffect createEffectGO () {
var go = new GameObject ("WaterCausticsEffect");
Undo.RegisterCreatedObjectUndo (go, "Create WaterCausticsEffect");
var tra = go.transform;
if (Selection.activeGameObject != null) {
var baseTra = Selection.activeGameObject.transform;
tra.parent = baseTra.parent;
tra.SetSiblingIndex (baseTra.GetSiblingIndex () + 1);
}
tra.localPosition = Vector3.zero;
tra.localRotation = Quaternion.identity;
tra.localScale = Vector3.one * 3f;
Selection.activeObject = go;
Selection.activeObject = null;
Selection.activeObject = go;
return go.AddComponent<WaterCausticsEffect> ();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cde63416afd7d524cb16551f6e60fa41
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: e975b56feadc05a4a9da076c953ae619
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- m_imageMaskTexture: {fileID: 2800000, guid: f88b6df342d5c4f4a969fb6eb4799d3e, type: 3}
- m_texture: {instanceID: 0}
- m_shader: {fileID: 4800000, guid: 348afebc23c3c6e4c8f05f2ea8f758fc, type: 3}
- m_noTexture: {fileID: 2800000, guid: 27623e5a17b6fd44a9af57a733ee993a, type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
#if WCE_URP
using System;
using UnityEngine;
using UnityEngine.Rendering.Universal;
namespace MH.WaterCausticsModules {
/*------------------------------------------------------------------------
This RendererFeature is used to stably apply WaterCausticsEffect to cameras
created by other effects.
For example, a mirror or water reflection effect may use a temporary camera
that is not placed in the scene, and the effect cannot be applied stably.
In that case, register this RendererFeature to the RendererData Asset.
The effect will be applied stably to all cameras.
このRendererFeatureは、他のエフェクトで作成されたカメラへWaterCausticsEffect
を安定的に適用するために使用します。
鏡や水面の反射エフェクトなどではシーンに配置されない一時的なカメラを使用する
場合があり、安定してエフェクトを適用出来ません。その場合はこの RendererFeature
を RendererData Asset に登録して下さい。安定してエフェクトが適用されるように
なります。
-------------------------------------------------------------------------*/
#if UNITY_2021_2_OR_NEWER
[DisallowMultipleRendererFeature ("WaterCausticsEffect (Renderer Feature)")]
#elif WCE_URP_10_8
[DisallowMultipleRendererFeature]
#endif
[HelpURL (Constant.URL_MANUAL)]
public class WaterCausticsEffectFeature : ScriptableRendererFeature {
static private WaterCausticsEffectFeature s_ins;
static public event Action<Camera> onCamRender;
static public event Action<ScriptableRenderer, Camera> onEnqueue;
static private int s_lastFrame;
static internal bool effective => (s_ins != null && s_ins.isActive && s_lastFrame >= Time.renderedFrameCount - 1);
static internal void OnAddedByScript () => s_lastFrame = Time.renderedFrameCount;
public override void Create () { }
public override void AddRenderPasses (ScriptableRenderer renderer, ref RenderingData rendData) {
s_ins = this;
s_lastFrame = Time.renderedFrameCount;
var cam = rendData.cameraData.camera;
onCamRender?.Invoke (cam);
onEnqueue?.Invoke (renderer, cam);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 63239fdff9bcf7545bae0204cba6eb96
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5408d0311478d0c4984c6debe06c8308
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,522 @@
// WaterCausticsModules
// Copyright (c) 2021 Masataka Hakozaki
Shader "Hidden/WaterCausticsModules/Effect" {
Properties {
// --- Scope
_WCE_ClipOutside ("Clip Outside Volume", Int) = 1
_WCE_UseImageMask ("Use Image Mask", Int) = 0
[NoScaleOffset]_WCE_ImageMaskTex ("Texture", 2D) = "white" { }
[Enum(UnityEngine.Rendering.CullMode)] _CullMode ("Cull Mode", Int) = 2
// --- Texture
[NoScaleOffset]_WCE_CausticsTex ("Caustics Texture", 2D) = "black" { }
_WCE_TexChannels ("Channel", Vector) = (0, 1, 2, 0)
_WCE_TexRotateSinCos ("Rotation Sin and Cos", Vector) = (0, 1, 0, 0)
_WCE_TilingSeed ("Tiling Seed", Int) = -1
_WCE_TilingRot ("Tiling Rot", Float) = 0.02
_WCE_TilingHard ("Tiling Hard", Float) = 0.85
// --- Dimension
_WCE_Density ("Density", Float) = 0.2
_WCE_SurfaceY ("Water Surface Y", Float) = 2
_WCE_SurfFadeCoef ("Surface Fade Coef", Float) = 2
_WCE_SurfFadeStart ("Surface Fade Start", Float) = 0
_WCE_DepthFadeStart ("Depth Fade Start", Float) = 0
_WCE_DepthFadeCoef ("Depth Fade Coef", Float) = 0.01
_WCE_DistanceFadeStart ("Distance Fade Start", Float) = 0
_WCE_DistanceFadeCoef ("Distance Fade Coef", Float) = 0.01
// --- Effect
_WCE_IntensityMainLit ("Main Light Intensity", Range(0, 50)) = 1
_WCE_IntensityAddLit ("Additional Lights Intensity", Range(0, 50)) = 1
[ToggleOff(_RECEIVE_SHADOWS_OFF)] _RECEIVE_SHADOWS_OFF ("Receive Shadow", Float) = 1
_WCE_ShadowIntensity ("Shadow Intensity", Range(0, 1)) = 1
_WCE_ColorShift ("ColorShift", Vector) = (0.004, -0.001, 0, 0)
_WCE_LitSaturation ("Light Saturation", Range(0, 2)) = 0.2
_WCE_MultiplyByTex ("Multiply Color", Range(0, 1)) = 1
// --- Normal Atten
_WCE_NormalAtten ("Normal Atten Intensity", Range(0, 1)) = 1
_WCE_NormalAttenRate ("Normal Atten Rate", Range(1, 8)) = 2
_WCE_TransparentBack ("Transparent Backside", Range(0, 1)) = 0
_WCE_BacksideShadow ("Backside Shadow", Range(0, 1)) = 0
// --- Depth Buffer
_ZWrite ("ZWrite", Int) = 0
[Enum(UnityEngine.Rendering.CompareFunction)] _ZTest ("ZTest", Int) = 4
_OffsetFactor ("Offset Factor", float) = 0
_OffsetUnits ("Offset Units", float) = 0
// --- Stencil Buffer
_StencilRef ("Ref [0-255]", Range(0, 255)) = 0
_StencilReadMask ("Read Mask [0-255]", Range(0, 255)) = 255
_StencilWriteMask ("Write Mask [0-255]", Range(0, 255)) = 255
[Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp ("Comp", Int) = 8
[Enum(UnityEngine.Rendering.StencilOp)] _StencilPass ("Pass", Int) = 0
[Enum(UnityEngine.Rendering.StencilOp)] _StencilFail ("ZFail", Int) = 0
[Enum(UnityEngine.Rendering.StencilOp)] _StencilZFail ("ZFail", Int) = 0
// --- Blend
[Enum(UnityEngine.Rendering.BlendMode)] _BlendSrcFactor ("SrcFactor", Int) = 1
[Enum(UnityEngine.Rendering.BlendMode)] _BlendDstFactor ("DstFactor", Int) = 1
// ※ Properties は SRPBatcher に必須
}
SubShader {
LOD 0
Tags { "RenderPipeline" = "UniversalPipeline" "RenderType" = "Transparent" "Queue" = "Transparent" "DisableBatching" = "True" "IgnoreProjector" = "True" }
Pass {
Name "WCE_EffectPass"
Tags { "LightMode" = "WCE_EffectPass" }
Blend [_BlendSrcFactor] [_BlendDstFactor]
ZWrite [_ZWrite]
ZTest [_ZTest]
Offset [_OffsetFactor], [_OffsetUnits]
Cull [_CullMode]
Stencil {
Ref [_StencilRef]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
Comp [_StencilComp]
Pass [_StencilPass]
Fail [_StencilFail]
ZFail [_StencilZFail]
}
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma exclude_renderers d3d11_9x
#pragma target 3.5
#define WCE_EFFECT_SHADER
#define REQUIRE_OPAQUE_TEXTURE
#pragma multi_compile_local _ _WCE_ONE_PASS_NORMAL _WCE_ONE_PASS_DEPTH
#pragma multi_compile_local_fragment _ WCE_DEBUG_NORMAL WCE_DEBUG_DEPTH WCE_DEBUG_FACING WCE_DEBUG_CAUSTICS WCE_DEBUG_AREA
#if defined(_WCE_ONE_PASS_NORMAL) || defined(_WCE_ONE_PASS_DEPTH)
#define REQUIRE_DEPTH_TEXTURE
#if defined(_WCE_ONE_PASS_NORMAL)
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareNormalsTexture.hlsl"
#endif
#else
#define WCE_EACH_MESH
#endif
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Version.hlsl"
#if VERSION_GREATER_EQUAL(11, 0)
#pragma multi_compile_fragment _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#else
#pragma multi_compile_fragment _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _MAIN_LIGHT_SHADOWS_CASCADE
#endif
#pragma multi_compile_fragment _ _SHADOWS_SOFT
#pragma multi_compile_fragment _ _ADDITIONAL_LIGHTS
// ---- ※ URP14のDeferredで影が現れない問題応急処置
// ※ TODO URP14のバージョンが上がったら修正されたか要確認
#if UNITY_VERSION >= 202220 // Unity2022.2.0 URP14.0 以上
#if !defined(_RECEIVE_SHADOWS_OFF)
#define _ADDITIONAL_LIGHT_SHADOWS
#endif
#else
#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
#endif
// ----
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl"
// ---- 使用しない
// #pragma multi_compile_fragment _ LIGHTMAP_ON
// #pragma multi_compile_fragment _ LIGHTMAP_SHADOW_MIXING
// #pragma multi_compile_fragment _ SHADOWS_SHADOWMASK
// #pragma multi_compile_fragment _ _MIXED_LIGHTING_SUBTRACTIVE
// ----
#if UNITY_VERSION >= 202120 // Unity2021.2.0 URP12.0 以上
#pragma multi_compile_fragment _ _GBUFFER_NORMALS_OCT
#pragma multi_compile_fragment _ _LIGHT_LAYERS
#pragma multi_compile_fragment _ _LIGHT_COOKIES
#endif
#if UNITY_VERSION >= 202220 // Unity2022.2.0 URP14.0 以上
#pragma multi_compile_fragment _ _FORWARD_PLUS
#endif
#pragma multi_compile_fog
#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)
#define _USE_FOG
#endif
#include "WaterCausticsEffectCommon.hlsl"
#pragma multi_compile_local_fragment _ _RECEIVE_SHADOWS_OFF
struct appdata {
float4 vertex : POSITION;
#if defined(WCE_EACH_MESH)
float3 normal : NORMAL;
#else
uint vID : SV_VertexID;
#endif
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f {
float4 posClip : SV_POSITION;
float4 posScrn : TEXCOORD0;
#if defined(WCE_EACH_MESH)
float3 posWld : TEXCOORD1;
float3 posEffect : TEXCOORD2;
float3 normalWS : TEXCOORD3;
#if defined(_USE_FOG)
float viewDepth : TEXCOORD4;
#endif
#endif
UNITY_VERTEX_OUTPUT_STEREO
};
CBUFFER_START(UnityPerMaterial)
float _WCE_Density;
int3 _WCE_TexChannels;
float2 _WCE_TexRotateSinCos;
int _WCE_TilingSeed;
float _WCE_TilingRot;
float _WCE_TilingHard;
float _WCE_SurfaceY;
float _WCE_SurfFadeStart;
float _WCE_SurfFadeCoef;
float _WCE_DepthFadeStart;
float _WCE_DepthFadeCoef;
float _WCE_DistanceFadeStart;
float _WCE_DistanceFadeCoef;
half _WCE_IntensityMainLit;
half _WCE_IntensityAddLit;
float2 _WCE_ColorShift;
half _WCE_LitSaturation;
half _WCE_MultiplyByTex;
half _WCE_NormalAtten;
half _WCE_NormalAttenRate;
half _WCE_TransparentBack;
half _WCE_BacksideShadow;
half _WCE_ShadowIntensity;
int _WCE_ClipOutside;
int _WCE_UseImageMask;
CBUFFER_END
#if defined(WCE_EACH_MESH)
CBUFFER_START(FrequentlyUpdateVariables)
float4x4 _WCE_WorldToObjMatrix;
CBUFFER_END
#endif
// ------------------------------------------------------------------------ Fill Clipped Hole
bool WCE_intersectPlaneAndLine(float3 ptA, float3 ptB, float3 planeP, float3 planeN, bool isAllowEndOnPlane, out float3 PT) {
float dotPA = dot(ptA - planeP, planeN);
float dotPB = dot(ptB - planeP, planeN);
bool isCross = (sign(dotPA) != sign(dotPB));
bool isPtOnPlane = (dotPA == 0 || dotPB == 0);
[branch] if (isCross && (isAllowEndOnPlane || !isPtOnPlane)) {
float3 AB = ptB - ptA;
float dif = abs(dotPA) + abs(dotPB);
float rate = abs(dotPA) / dif;
PT = ptA + AB * rate;
return true;
} else {
PT = float3(0, 0, 0);
return false;
}
}
float3 WCE_camDirWS() {
return -UNITY_MATRIX_V[2].xyz;
}
float3 WCE_viewDirWS(float3 posWS) {
return (unity_OrthoParams.w == 0) ? normalize(posWS - _WorldSpaceCameraPos) : WCE_camDirWS();
}
float3 WCE_viewDirRawWS(float3 posWS) {
return (unity_OrthoParams.w == 0) ? posWS - _WorldSpaceCameraPos : WCE_camDirWS();
}
float4 WCE_fillClippedHole(uint vID) {
const float3 pts[8] = {
float3(-0.5, -0.5, -0.5), float3(0.5, -0.5, -0.5), float3(-0.5, 0.5, -0.5), float3(0.5, 0.5, -0.5),
float3(-0.5, -0.5, 0.5), float3(0.5, -0.5, 0.5), float3(-0.5, 0.5, 0.5), float3(0.5, 0.5, 0.5),
};
const uint2 idxs [12] = {
uint2(0, 4), uint2(1, 5), uint2(2, 6), uint2(3, 7), uint2(0, 2), uint2(1, 3), uint2(4, 6), uint2(5, 7), uint2(0, 1), uint2(4, 5), uint2(2, 3), uint2(6, 7),
};
// ObliqueMatrix対応のためClipSpaceの端点からNearClipPlaneを得る
float near = UNITY_NEAR_CLIP_VALUE; // ← D3D11/Metal/Vulkan/Switch:1, GLCore/GLES:-1
float3 p0 = ComputeWorldSpacePosition(float4(0, 0, near, 1), UNITY_MATRIX_I_VP);
float3 p1 = ComputeWorldSpacePosition(float4(1, 0, near, 1), UNITY_MATRIX_I_VP);
float3 p2 = ComputeWorldSpacePosition(float4(0, 1, near, 1), UNITY_MATRIX_I_VP);
float3 planeN_WS = cross(p1 - p0, p2 - p0);
planeN_WS *= sign(dot(planeN_WS, -WCE_camDirWS()));
float3 planeN = TransformWorldToObjectNormal(planeN_WS, true);
float3 planeP = TransformWorldToObject(p0);
float3 rightV = TransformWorldToObjectDir(p1 - p0, true);
uint cnt = 0u;
float3 center = float3(0, 0, 0);
float4 intersectPts[6];
[unroll(12)] for (uint i = 0u; i < 12u; i++) {
float3 ptA = pts [idxs [i].x];
float3 ptB = pts [idxs [i].y];
float3 PT;
bool isIntersect = WCE_intersectPlaneAndLine(ptA, ptB, planeP, planeN, (i < 4), PT);
[branch] if (isIntersect) {
intersectPts [cnt].xyz = PT;
center += PT;
cnt++;
}
}
center /= cnt;
float3 outputPt = center;
[branch] if (cnt >= 3u && vID <= cnt) {
[unroll(6)] for (uint k = 0u; k < cnt; k++) {
// 時計回りにwを設定 -2+2
float3 v = normalize(intersectPts [k].xyz - center);
intersectPts [k].w = - (dot(v, rightV) - 1.0) * sign(dot(cross(v, rightV), planeN));
}
// ソート
[unroll(5)] for (uint m = 0u; m < cnt - 1u; m++) {
[unroll(5)] for (uint o = m + 1u; o < cnt; o++) {
[branch] if (intersectPts[m].w < intersectPts[o].w) {
float4 swap = intersectPts[m];
intersectPts[m] = intersectPts[o];
intersectPts[o] = swap;
}
}
}
outputPt = intersectPts[vID % cnt].xyz;
}
float4 posClip = TransformObjectToHClip(outputPt);
posClip.z = UNITY_NEAR_CLIP_VALUE * posClip.w;
return posClip;
}
// ------------------------------------------------------------------------ Vertex Shader
v2f vert(appdata v) {
v2f o = (v2f)0;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
#if defined(WCE_EACH_MESH)
// [Each Mesh]
float4 posClip = TransformObjectToHClip(v.vertex.xyz);
float3 posWld = TransformObjectToWorld(v.vertex.xyz);
float4 posScrn = ComputeScreenPos(posClip);
o.posClip = posClip;
o.posWld = posWld;
o.posScrn = posScrn;
o.posEffect = mul(_WCE_WorldToObjMatrix, float4(posWld, 1)).xyz;
o.normalWS = TransformObjectToWorldNormal(v.normal, true);
#if defined(_USE_FOG)
o.viewDepth = -TransformWorldToView(posWld).z;
#endif
#else
// [At Once]
float4 posClip;
[branch]if (v.vID < 8u) {
posClip = TransformObjectToHClip(v.vertex.xyz);
} else {
posClip = WCE_fillClippedHole(v.vID - 8u);
}
float4 posScrn = ComputeScreenPos(posClip);
o.posClip = posClip;
o.posScrn = posScrn;
#endif
return o;
}
// ------------------------------------------------------------------------ Reconstruct World Pos and Normal
float WCE_fixReversedZ(float Depth) {
#if UNITY_REVERSED_Z
return 1 - Depth;
#else
return Depth;
#endif
}
float3 WCE_reconstructPosWS(float2 screenUV, float rawDepth) {
#if !UNITY_REVERSED_Z
rawDepth = lerp(UNITY_NEAR_CLIP_VALUE, 1, rawDepth);
#endif
return ComputeWorldSpacePosition(screenUV, rawDepth, UNITY_MATRIX_I_VP);
}
bool WCE_checkOutside(float3 posES) {
return (abs(posES.x) > 0.5 || abs(posES.y) > 0.5 || abs(posES.z) > 0.5);
}
#if !defined(WCE_EACH_MESH)
#define RECONSTRUCT_NORMAL_HQ 1
half3 WCE_reconstructNormalWS(float2 screenUV, float3 posWS, float rdC) {
#if (!RECONSTRUCT_NORMAL_HQ)
return normalize(cross(ddy(posWS), ddx(posWS)));
#else
float2 offsetU = float2(_ScreenParams.z - 1, 0);
float2 offsetV = float2(0, _ScreenParams.w - 1);
float2 uvN = screenUV + offsetV;
float2 uvS = screenUV - offsetV;
float2 uvE = screenUV + offsetU;
float2 uvW = screenUV - offsetU;
float rdN = SampleSceneDepth(uvN);
float rdS = SampleSceneDepth(uvS);
float rdE = SampleSceneDepth(uvE);
float rdW = SampleSceneDepth(uvW);
float3 vV = abs(rdN - rdC) < abs(rdS - rdC) ? WCE_reconstructPosWS(uvN, rdN) - posWS : posWS - WCE_reconstructPosWS(uvS, rdS);
float3 vU = abs(rdE - rdC) < abs(rdW - rdC) ? WCE_reconstructPosWS(uvE, rdE) - posWS : posWS - WCE_reconstructPosWS(uvW, rdW);
return normalize(cross(vV, vU));
#endif
}
#endif
half3 WCE_getNormal(v2f IN, float2 screenUV, float3 posWS, float rawDepth) {
#if defined(WCE_EACH_MESH)
float3 normal = IN.normalWS; // VFACEセマンティクスでの向きの取得はメッシュのデータに不具合があると正しくないことがあるので廃止
normal *= -sign(dot(normal, WCE_viewDirRawWS(posWS)));
return normal;
#elif defined(_WCE_ONE_PASS_NORMAL)
float3 normal = SampleSceneNormals(screenUV);
#if VERSION_LOWER(10, 9) || (VERSION_GREATER_EQUAL(11, 0) && VERSION_LOWER(12, 0))
// ViewSpaceで保存されているURPのバージョン対応
normal.z *= -1;
normal = mul((float3x3)UNITY_MATRIX_I_V, normal).xyz;
normal = normalize(normal);
#endif
// normal *= -sign(dot(normal, WCE_viewDirWS(posWS))+0.07 );
normal *= -sign(dot(normal, WCE_viewDirRawWS(posWS)));
return normal;
#else
return WCE_reconstructNormalWS(screenUV, posWS, rawDepth);
#endif
}
// ------------------------------------------------------------------------ Fog (Oblique Projection Supported)
#if defined(_USE_FOG)
float WCE_computeFogFactorZ0ToFar(float z) {
#if defined(FOG_LINEAR)
return saturate(z * unity_FogParams.z + unity_FogParams.w);
#elif defined(FOG_EXP) || defined(FOG_EXP2)
return unity_FogParams.x * z;
#else
return 0;
#endif
}
float WCE_calcFog(float viewDepth) {
float nearToFarZ = max(viewDepth - _ProjectionParams.y, 0);
return ComputeFogIntensity(WCE_computeFogFactorZ0ToFar(nearToFarZ));
}
#endif
// ------------------------------------------------------------------------
#if defined(WCE_EACH_MESH)
// [Each Mesh]
#define WCE_clipOutside (_WCE_ClipOutside == 1)
#else
// [At Once]
#define WCE_clipOutside true
#endif
// ------------------------------------------------------------------------ Fragment
#define CLR_COL half4(0, 0, 0, 0)
TEXTURE2D(_WCE_CausticsTex);
TEXTURE2D(_WCE_ImageMaskTex);
SAMPLER(sampler_WCE_CausticsTex);
SAMPLER(sampler_WCE_ImageMaskTex);
half4 frag(v2f IN) : SV_Target {
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN);
// ---------- WorldPos
float2 screenUV = IN.posScrn.xy / IN.posScrn.w;
#if defined(WCE_EACH_MESH)
// [Each Mesh]
float3 posWS = IN.posWld;
float3 posES = IN.posEffect;
float rawDepth = 0.5;
#else
// [At Once]
float rawDepth = SampleSceneDepth(screenUV);
float3 posWS = WCE_reconstructPosWS(screenUV, rawDepth);
float3 posES = TransformWorldToObject(posWS);
#endif
// ---------- Debug Info
#if defined(WCE_DEBUG_NORMAL) || defined(WCE_DEBUG_DEPTH) || defined(WCE_DEBUG_FACING) || defined(WCE_DEBUG_CAUSTICS) || defined(WCE_DEBUG_AREA)
if ((WCE_clipOutside && WCE_checkOutside(posES)) || rawDepth == UNITY_RAW_FAR_CLIP_VALUE) discard;
#endif
#if defined(WCE_DEBUG_NORMAL)
return half4(pow(saturate(WCE_getNormal(IN, screenUV, posWS, rawDepth) * 0.5 + 0.5), 4) * 0.9, 1);
#elif defined(WCE_DEBUG_DEPTH)
float debugDepth = -TransformWorldToView(posWS).z * _ProjectionParams.w;
return half4(pow(abs(debugDepth), 0.7).xxx, 1);
#elif defined(WCE_DEBUG_FACING)
float3 n = WCE_getNormal(IN, screenUV, posWS, rawDepth);
float b = dot(-n, WCE_viewDirWS(posWS));
return half4((saturate(pow(abs(b), 2) * 0.5)).xxx, 1);
#elif defined(WCE_DEBUG_CAUSTICS)
_WCE_MultiplyByTex = 0;
#elif defined(WCE_DEBUG_AREA)
_WCE_MultiplyByTex = 0;
#endif
// ---------- Clip Outside
[branch] if ((WCE_clipOutside && WCE_checkOutside(posES)) || rawDepth == UNITY_RAW_FAR_CLIP_VALUE) return CLR_COL;
// ---------- Atten Start
float atten = 1; // halfだとVRシングルパスで不安定?
const float ATTEN_TH = 0.001;
// ---------- Fog
#if defined(_USE_FOG)
#if defined(WCE_EACH_MESH)
float viewDepth = IN.viewDepth;
#else
float viewDepth = -TransformWorldToView(posWS).z;
#endif
atten *= WCE_calcFog(viewDepth);
#endif
// ---------- Distance Fade
float3 viewDir = posWS - _WorldSpaceCameraPos; // ←ifに入れるとVR SinglePassでエラー
[branch] if (atten > ATTEN_TH && _WCE_DistanceFadeCoef > 0.0001f) {
atten *= smoothstep(0, 1, 1 - (length(viewDir) - _WCE_DistanceFadeStart) * _WCE_DistanceFadeCoef);
}
// ---------- Image Mask
[branch] if (atten > ATTEN_TH && _WCE_UseImageMask != 0) {
atten *= _WCE_ImageMaskTex.Sample(sampler_WCE_ImageMaskTex, posES.xz + 0.5).r;
}
// ---------- Atten End
[branch] if (atten <= ATTEN_TH) return CLR_COL;
_WCE_IntensityMainLit *= atten;
_WCE_IntensityAddLit *= atten;
// ---------- Normal
half3 normalWS = WCE_getNormal(IN, screenUV, posWS, rawDepth);
// ---------- Caustics
half3 c = WCE_EffectCore(posWS, normalWS, screenUV, _WCE_CausticsTex, sampler_WCE_CausticsTex, _WCE_TexRotateSinCos, _WCE_TexChannels, _WCE_TilingSeed, _WCE_TilingRot, _WCE_TilingHard, _WCE_Density, _WCE_SurfaceY, _WCE_SurfFadeStart, _WCE_SurfFadeCoef, _WCE_DepthFadeStart, _WCE_DepthFadeCoef, _WCE_IntensityMainLit, _WCE_IntensityAddLit, _WCE_ShadowIntensity, _WCE_ColorShift, _WCE_LitSaturation, _WCE_NormalAtten, _WCE_NormalAttenRate, _WCE_TransparentBack, _WCE_BacksideShadow);
// ---------- Multiply Opaque Tex
[branch] if (_WCE_MultiplyByTex > 0) {
c *= 1 - (1 - SHADERGRAPH_SAMPLE_SCENE_COLOR(screenUV)) * _WCE_MultiplyByTex;
}
return half4(c, 1);
}
ENDHLSL
}
}
Fallback "Hidden/InternalErrorShader"
}

Some files were not shown because too many files have changed in this diff Show More