2026-06-09 룸 프로토타입 디자인 (진행중)

This commit is contained in:
skrwns304@gmail.com
2026-06-09 20:53:26 +09:00
parent 3a18351d17
commit 4c955dafc3
3544 changed files with 226503 additions and 17 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3a3be5417b7946088b3cbec887cf0dfe
timeCreated: 1729855911
AssetOrigin:
serializedVersion: 1
productId: 287769
packageName: Stylized Water 3
packageVersion: 3.2.7
assetPath: Assets/Stylized Water 3/Editor/Shader/FogIntegration.cs
uploadId: 927372

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f6df0d541f8b14642985c92afdb9f538
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 287769
packageName: Stylized Water 3
packageVersion: 3.2.7
assetPath: Assets/Stylized Water 3/Editor/Shader/MaterialUI.cs
uploadId: 927372

View File

@@ -0,0 +1,327 @@
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
#if SWS_DEV
#define ENABLE_SHADER_STRIPPING_LOG
#endif
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Serialization;
using Debug = UnityEngine.Debug;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;
#if URP
using UnityEngine.Rendering.Universal;
#endif
namespace StylizedWater3
{
public static class ShaderConfigurator
{
public const string DEFAULT_SHADER_GUID = "823f6b206953b674a9a64f9e3ec57752";
public static Shader GetDefaultShader()
{
string defaultShaderPath = AssetDatabase.GUIDToAssetPath(DEFAULT_SHADER_GUID);
Shader defaultShader = AssetDatabase.LoadAssetAtPath<Shader>(defaultShaderPath);
return defaultShader;
}
public static ShaderMessage[] GetErrorMessages(Shader shader)
{
ShaderMessage[] messages = null;
int n = ShaderUtil.GetShaderMessageCount(shader);
if (n < 1) return messages;
messages = ShaderUtil.GetShaderMessages(shader);
//Filter for errors
messages = messages.Where(x => x.severity == ShaderCompilerMessageSeverity.Error).ToArray();
return messages;
}
#if URP
//Strips keywords from the shader for features belonging to newer URP versions.
private class KeywordStripper : IPreprocessShaders
{
private const string LOG_FILEPATH = "Logs/Stylized Water 3 Compilation.log";
#if ENABLE_SHADER_STRIPPING_LOG
private System.Diagnostics.Stopwatch m_stripTimer = new System.Diagnostics.Stopwatch();
#endif
private List<ShaderKeyword> StrippedKeywords = new List<ShaderKeyword>();
//URP 18+
private readonly ShaderKeyword _TEST = new ShaderKeyword("_TEST");
private readonly bool heightPrePassEnabled;
#if SWS_DEV
[MenuItem("SWS/Debug/HeightPrePassEnabled")]
#endif
private static bool HeightPrePassEnabled()
{
bool state = false;
if (StylizedWaterRenderFeature.RequireHeightPrePass) return true;
//Check if the displacement pre-pass is enabled anywhere
for (int i = 0; i < GraphicsSettings.allConfiguredRenderPipelines.Length; i++)
{
UniversalRenderPipelineAsset pipeline = (UniversalRenderPipelineAsset)GraphicsSettings.allConfiguredRenderPipelines[i];
ScriptableRendererData[] renderers = PipelineUtilities.GetRenderDataList(pipeline);
for (int j = 0; j < renderers.Length; j++)
{
StylizedWaterRenderFeature renderFeature = (StylizedWaterRenderFeature)PipelineUtilities.GetRenderFeature<StylizedWaterRenderFeature>(renderers[j]);
if (renderFeature)
{
state |= renderFeature.heightPrePassSettings.enable;
#if SWS_DEV
//Debug.Log($"{renderers[j].name}. Enable?:{renderFeature.heightPrePassSettings.enable}");
#endif
}
}
}
#if SWS_DEV
//Debug.Log("Height pre-pass enabled somewhere: " + state);
#endif
return state;
}
//Note: Constructor is called once, when building starts
public KeywordStripper()
{
if (PlayerSettings.GetGraphicsAPIs(EditorUserBuildSettings.activeBuildTarget)[0] == GraphicsDeviceType.OpenGLES3)
{
if (UniversalRenderPipeline.asset.msaaSampleCount > 1)
{
Debug.LogWarning("[Stylized Water 3] You are deploying a build using the OpenGLES 3.0 graphics API with MSAA enabled (in your URP pipeline asset). Due to a bug in some graphics chips, transparent materials (including the water) will not render. " +
"Disable MSAA, or use the Vulkan graphics API", UniversalRenderPipeline.asset);
}
}
#if UNITY_6000_1_OR_NEWER
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.WebGL && UniversalRenderPipeline.asset.lightProbeSystem == LightProbeSystem.ProbeVolumes)
{
if (PlayerSettings.GetGraphicsAPIs(EditorUserBuildSettings.activeBuildTarget)[0] ==
GraphicsDeviceType.WebGPU)
{
Debug.LogWarning("[Stylized Water 3] WebGL builds using the WebGPU graphics API do not support Adaptive Probe Volumes. Shader will fall back to standard SH/Light Probes.");
}
}
#endif
//Check if the displacement pre-pass is enabled anywhere
heightPrePassEnabled = HeightPrePassEnabled();
StrippedKeywords.Clear();
if (!heightPrePassEnabled)
{
StrippedKeywords.Add(new ShaderKeyword(ShaderParams.Keywords.WaterHeightPass));
}
//Note: Keywords for extensions are only injected through the shader generator. Hence they don't need to be stripped
#if !UNITY_6000_0_OR_NEWER
StrippedKeywords.Add(_TEST);
#endif
LogInitialization();
}
public int callbackOrder => 0;
public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> compilerDataList)
{
#if URP
if (UniversalRenderPipeline.asset == null || compilerDataList == null || compilerDataList.Count == 0) return;
//Only run for specific shaders
if (shader.name.Contains("Stylized Water 3") == false) return;
LogStart(shader, snippet, compilerDataList);
var inputShaderVariantCount = compilerDataList.Count;
for (int i = 0; i < inputShaderVariantCount;)
{
//If any of the excluded keywords are enabled in this variant, strip it
bool removeInput = StripUnused(shader, compilerDataList[i], snippet);
// Remove at swap back
if (removeInput)
compilerDataList[i] = compilerDataList[--inputShaderVariantCount];
else
++i;
}
if (compilerDataList is List<ShaderCompilerData> inputDataList)
{
inputDataList.RemoveRange(inputShaderVariantCount, inputDataList.Count - inputShaderVariantCount);
}
else
{
for (int i = compilerDataList.Count - 1; i >= inputShaderVariantCount; --i)
compilerDataList.RemoveAt(i);
}
LogStrippingEnd(compilerDataList.Count);
#endif
}
private bool StripUnused(Shader shader, ShaderCompilerData compilerData, ShaderSnippetData snippet)
{
if (StripPass(shader, snippet))
{
return true;
}
foreach (var keyword in StrippedKeywords)
{
if (StripKeyword(shader, keyword, compilerData, snippet))
{
return true;
}
}
return false;
}
private bool StripPass(Shader shader, ShaderSnippetData snippet)
{
if (heightPrePassEnabled == false && snippet.passName == ShaderParams.Passes.HeightPrePass)
{
Log($"- Stripped Pass {snippet.passName} ({shader.name}) (Stage: {snippet.shaderType})");
return true;
}
return false;
}
private bool StripKeyword(Shader shader, ShaderKeyword keyword, ShaderCompilerData compilerData, ShaderSnippetData snippet)
{
if (compilerData.shaderKeywordSet.IsEnabled(keyword))
{
LogStripping(shader, keyword, snippet);
return true;
}
return false;
}
#region Logging
struct StrippingLog
{
public Shader shader;
public ShaderKeyword keyword;
public string passName;
public ShaderType shaderType;
}
private void LogInitialization()
{
#if ENABLE_SHADER_STRIPPING_LOG
//Clear log file first
File.WriteAllLines(LOG_FILEPATH, new string[] {});
Log("KeywordStripper initialized...", true);
Log(string.Empty);
Log($"Displacement Pre-pass enabled in build: {heightPrePassEnabled}", true);
Log(string.Empty);
for (int i = 0; i < StrippedKeywords.Count; i++)
{
Log($"• {StrippedKeywords[i].name} keyword to be stripped");
}
Log($"{StrippedKeywords.Count} total keywords to be stripped");
#endif
}
private void LogStart(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> compilerDataList)
{
#if ENABLE_SHADER_STRIPPING_LOG
m_stripTimer.Start();
var text = $"OnProcessShader running for {shader.name}. (Pass: {snippet.passName}) (Stage: {snippet.shaderType}). Num variants: {compilerDataList.Count}";
Log(text, true);
#endif
}
StrippingLog prevLog;
private void LogStripping(Shader shader, ShaderKeyword keyword, ShaderSnippetData snippet)
{
#if ENABLE_SHADER_STRIPPING_LOG
//Try to avoid spamming the log with duplicates, this otherwise slows down compilation to a crawl
if (prevLog.keyword.index == keyword.index && prevLog.shader == shader && prevLog.passName == snippet.passName && prevLog.shaderType == snippet.shaderType)
{
//File.AppendAllText(LOG_FILEPATH, "- Skipping log!\n" );
return;
}
prevLog.shader = shader;
prevLog.keyword = keyword;
prevLog.passName = snippet.passName;
prevLog.shaderType = snippet.shaderType;
var text = $"- Stripped {keyword.name} ({shader.name}) variant. (Pass {snippet.passName}) (Stage: {snippet.shaderType})";
Log(text);
#endif
}
private void LogStrippingEnd(int count)
{
#if ENABLE_SHADER_STRIPPING_LOG
m_stripTimer.Stop();
System.TimeSpan stripTimespan = m_stripTimer.Elapsed;
var text = $"Stripping took {stripTimespan.Minutes}m{stripTimespan.Seconds}s ({stripTimespan.Milliseconds}ms). Remaining variants to compile: {count}";
Log(text);
m_stripTimer.Reset();
#endif
}
private void Log(string text, bool newLine = false)
{
#if ENABLE_SHADER_STRIPPING_LOG
File.AppendAllText(LOG_FILEPATH, (newLine ? "\n" : "") + text + "\n");
#endif
}
#endregion
}
#endif
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c05c2f42980e1b649a1c2cd783b30b58
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 287769
packageName: Stylized Water 3
packageVersion: 3.2.7
assetPath: Assets/Stylized Water 3/Editor/Shader/ShaderConfigurator.cs
uploadId: 927372

View File

@@ -0,0 +1,497 @@
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using Object = UnityEngine.Object;
namespace StylizedWater3
{
public static class TemplateParser
{
public const int SHADER_GENERATOR_VERSION_MAJOR = 5;
public const int SHADER_GENERATOR_MINOR = 1;
public const int SHADER_GENERATOR_PATCH = 3;
//Stencil mask used by water shader. Currently used for underwater rendering
//Values 1-65 are used by deferred rendering
public const int STENCIL_REF = 65;
//Converts relative include paths such as (../../Libraries/File.hlsl) to an absolute path
//Supports the source file being part of a package
public static string RelativeToAbsoluteIncludePath(string filePath, string relativePath)
{
string fileDir = Path.GetDirectoryName(filePath);
//Count how many folders should be traversed up
int levels = relativePath.Split(new[]
{
".."
}, StringSplitOptions.None).Length - 1;
string traveledPath = fileDir;
if (levels > 0)
{
for (int i = 0; i < levels; i++)
{
//Remove the number of needed sub-directories needed to reach the destination
int strimStart = traveledPath.LastIndexOf(Path.DirectorySeparatorChar);
traveledPath = traveledPath.Remove(strimStart);
}
}
//The directory without the "up" navigators
string relativeFolder = relativePath.Replace("../", string.Empty);
//Concatenate them together
string absolutePath = traveledPath + "/" + relativeFolder;
//Convert back- to forward slashes
absolutePath = absolutePath.Replace("\\", "/");
return absolutePath;
}
//Pre-process the template to inject additional template contents into it
private static void ModifyTemplate(ref string[] lines, WaterShaderImporter importer)
{
StringBuilder templateBuilder = new StringBuilder();
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
//Inject additional passes into template
if (line.Contains("%passes%"))
{
List<Object> passes = new List<Object>(importer.settings.additionalPasses);
if (StylizedWaterEditor.UnderwaterRenderingInstalled())
{
string maskPassGUID = "46c22cecd601401a875ca4554695986f";
string maskPassPath = AssetDatabase.GUIDToAssetPath(maskPassGUID);;
Object underwaterMaskPass = AssetDatabase.LoadAssetAtPath<Object>(maskPassPath);
passes.Add(underwaterMaskPass);
}
int passCount = passes.Count;
for (int j = 0; j < passCount; j++)
{
if (passes[j] != null)
{
string filePath = AssetDatabase.GetAssetPath(passes[j]);
importer.RegisterDependency(filePath);
string[] passContexts = File.ReadAllLines(filePath);
for (int k = 0; k < passContexts.Length; k++)
{
templateBuilder.AppendLine(passContexts[k]);
}
}
}
continue;
}
templateBuilder.AppendLine(lines[i]);
}
lines = templateBuilder.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None);
}
public static string CreateShaderCode(string templatePath, ref string[] lines, WaterShaderImporter importer, bool tessellation = false)
{
if (importer == null)
{
throw new Exception("Failed to compile shader from template code. The importer is invalid, this should not even be possible. Whatever you did, undo it...");
}
//Extension installation states
var underwaterInstalled = StylizedWaterEditor.UnderwaterRenderingInstalled();
var dynamicEffectsInstalled = StylizedWaterEditor.DynamicEffectsInstalled();
FogIntegration.Integration fogIntegration = importer.GetFogIntegration();
AssetInfo.VersionChecking.CheckUnityVersion();
//Shader name
string prefix = importer.settings.hidden ? "Hidden/" : string.Empty;
string suffix = tessellation ? ShaderParams.ShaderNames.TESSELLATION_NAME_SUFFIX : string.Empty;
string shaderName = $"{prefix}{AssetInfo.ASSET_NAME}/{importer.settings.shaderName}";
string shaderPath = importer.assetPath;
ModifyTemplate(ref lines, importer);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lines.Length; i++)
{
//Ignore blank lines and comments for analysis
if (string.IsNullOrWhiteSpace(lines[i]) || lines[i].StartsWith("//"))
{
sb.AppendLine(lines[i]);
continue;
}
//First non-space character
int indent = System.Text.RegularExpressions.Regex.Match(lines[i], "[^-\\s]").Index;
string whitespace = lines[i].Replace(lines[i].Substring(indent), "");
//AppendLine using previous line's white spacing
void AddLine(string source)
{
sb.AppendLine(source.Insert(0, whitespace));
}
//Remove whitespaces
string line = lines[i].Remove(0, indent);
bool Matches(string source) { return string.CompareOrdinal(source, line) == 0; }
if (Matches("%asset_version%"))
{
AddLine($"//Asset version {AssetInfo.INSTALLED_VERSION}");
continue;
}
if (Matches("%compiler_version%"))
{
AddLine($"//Shader generator version: {new Version(SHADER_GENERATOR_VERSION_MAJOR, SHADER_GENERATOR_MINOR, SHADER_GENERATOR_PATCH)}");
continue;
}
if (Matches("%unity_version%"))
{
AddLine($"//Unity version: {AssetInfo.VersionChecking.GetUnityVersion()}");
continue;
}
if (Matches("%shader_name%"))
{
AddLine($"Shader \"{shaderName}{suffix}\"");
continue;
}
if (Matches("%custom_directives%"))
{
foreach (WaterShaderImporter.Directive directive in importer.settings.customIncludeDirectives)
{
if(directive.enabled == false) continue;
string directivePrefix = string.Empty;
switch (directive.type)
{
case WaterShaderImporter.Directive.Type.define:
directivePrefix = "#define ";
break;
case WaterShaderImporter.Directive.Type.include:
directivePrefix = "#include ";
break;
case WaterShaderImporter.Directive.Type.include_with_pragmas:
directivePrefix = "#include_with_pragmas ";
break;
case WaterShaderImporter.Directive.Type.pragma:
directivePrefix = "#pragma ";
break;
}
if (directive.value != string.Empty) AddLine($"{directivePrefix}{directive.value}");
}
continue;
}
if (Matches("%global_defines%"))
{
if (importer.settings.singleCausticsLayer)
{
AddLine("#define CAUSTICS_SINGLE_LAYER");
}
continue;
}
if (Matches("%pragma_target%"))
{
if (tessellation)
{
AddLine("#pragma target 4.6");
}
else
{
AddLine("#pragma target 3.0");
}
continue;
}
if (Matches("%pragma_renderers%"))
{
if (tessellation)
{
AddLine("#pragma exclude_renderers gles");
}
continue;
}
if (line.StartsWith("Fallback"))
{
if (tessellation)
{
//Fallback to non-tessellation variant (with without suffix)
AddLine($"Fallback \"{shaderName}\"");
//Test, disable fallback
//AddLine(line);
}
else
{
//Leave as is
AddLine(line);
}
continue;
}
if (Matches("%tessellation_properties%"))
{
if (tessellation)
{
AddLine("_TessValue(\"Max subdivisions\", Range(1, 64)) = 16");
AddLine("_TessMin(\"Start Distance\", Float) = 0");
AddLine("_TessMax(\"End Distance\", Float) = 15");
}
continue;
}
if (Matches("%tessellation_directives%"))
{
if (tessellation)
{
AddLine("#define TESSELLATION_ON");
AddLine("#pragma require tessellation tessHW");
AddLine("#pragma hull Hull");
AddLine("#pragma domain Domain");
}
continue;
}
if (line.Contains("%stencilID%"))
{
int stencilID = STENCIL_REF;
if (importer.settings.fogIntegration == FogIntegration.Assets.COZY)
{
}
line = line.Replace("%stencilID%", stencilID.ToString());
AddLine(line);
continue;
}
if (Matches("%stencil%"))
{
continue;
}
if (Matches("%multi_compile_light_cookies%"))
{
if (importer.settings.lightCookies)
{
AddLine("#pragma multi_compile_fragment _ _LIGHT_COOKIES");
}
continue;
}
if (line.Contains("%render_queue_offset%"))
{
int offset = 0;
switch (fogIntegration.asset)
{
//case FogIntegration.Assets.COZY: offset = 2;
//break;
//case Fog.Assets.AtmosphericHeightFog : offset = 2; //Should actually render after the fog sphere, but asset inherently relies on double fog shading it seems?
//break;
default: offset = 0;
break;
}
line = line.Replace("%render_queue_offset%", offset.ToString());
AddLine(line);
continue;
}
if (Matches("%defines%"))
{
if (importer.settings.additionalLightCaustics)
{
AddLine("#define _ADDITIONAL_LIGHT_CAUSTICS");
}
if (importer.settings.additionalLightTranslucency)
{
AddLine("#define _ADDITIONAL_LIGHT_TRANSLUCENCY");
}
continue;
}
if (Matches("%multi_compile underwater rendering%"))
{
if (underwaterInstalled)
{
importer.configurationState.underwaterRendering = true;
AddLine($"#pragma multi_compile_fragment _ {ShaderParams.Keywords.UnderwaterRendering}");
}
continue;
}
if (Matches("%multi_compile_vertex dynamic effects%"))
{
if (dynamicEffectsInstalled)
{
importer.configurationState.dynamicEffects = true;
AddLine($"#pragma multi_compile_vertex _ {ShaderParams.Keywords.DynamicEffects}");
}
continue;
}
if (Matches("%multi_compile dynamic effects%"))
{
if (dynamicEffectsInstalled)
{
importer.configurationState.dynamicEffects = true;
AddLine($"#pragma multi_compile _ {ShaderParams.Keywords.DynamicEffects}");
}
continue;
}
if (Matches("%mobile_keyword_modifiers%"))
{
if (importer.settings.forceSimpleShadingOnMobile)
{
AddLine("#if defined(SHADER_API_MOBILE) || defined(SHADER_API_SWITCH) || defined(SHADER_API_SWITCH2)");
AddLine("#undef _ADVANCED_SHADING");
AddLine("#define _ADVANCED_SHADING 0");
AddLine("#endif");
}
continue;
}
if (line.StartsWith("#include "))
{
string includePath = line.Replace("#include ", string.Empty);
//Remove parenthesis
includePath = includePath.Replace("\"", string.Empty);
importer.RegisterDependency(includePath);
}
if (importer.settings.type == WaterShaderImporter.WaterShaderSettings.ShaderType.WaterSurface)
{
if (Matches("%define_fog_integration%"))
{
AddLine($"#define {fogIntegration.asset.ToString()}");
if (fogIntegration.asset == FogIntegration.Assets.UnityFog)
{
AddLine("#pragma multi_compile_fog");
}
continue;
}
/* include FogLibrary */
if (Matches("%include_fog_integration_library%"))
{
//Default until otherwise valid
line = string.Empty;
//Mark the asset integration as being compiled in
importer.configurationState.fogIntegration = fogIntegration;
if (fogIntegration.asset != FogIntegration.Assets.None && fogIntegration.asset != FogIntegration.Assets.UnityFog)
{
string includePath = AssetDatabase.GUIDToAssetPath(fogIntegration.libraryGUID);
importer.RegisterDependency(includePath);
//Not found error
if (includePath == string.Empty)
{
if (EditorUtility.DisplayDialog(AssetInfo.ASSET_NAME,
fogIntegration.name + " fog shader library could not be found with the GUID \"" + fogIntegration.libraryGUID + "\".\n\n" +
"This means it was changed by the author (rare!), you deleted the \".meta\" file at some point, or the asset simply isn't installed.", "Ok"))
{
}
}
else
{
var pragma = fogIntegration.includeWithPragmas ? "include_with_pragmas" : "include";
line = $"#{pragma} \"{includePath}\"";
AddLine(line);
continue;
}
}
}
}
//Shaders created using "ShaderUtil.CreateShaderAsset" don't exist in a literal sense. Hence any relative file paths are invalid
//Convert them to absolute file paths
//Bonus: moving a shader file (or its folder) triggers it to re-import, thus always keeping the file path up-to-date
if (line.StartsWith("#include_library"))
{
string relativePath = line.Replace("#include_library ", string.Empty);
//Remove parenthesis
relativePath = relativePath.Replace("\"", string.Empty);
string includePath = RelativeToAbsoluteIncludePath(shaderPath, relativePath);
line = $"#include \"{includePath}\"";
importer.RegisterDependency(includePath);
AddLine(line);
continue;
}
//Insert whitespace back in
line = line.Insert(0, whitespace);
//Nothing special, keep whatever line this is
sb.AppendLine(line);
}
//Convert to separate lines again
lines = sb.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None);
//Convert to single string, respecting line breaks and spacing.
return String.Join(Environment.NewLine, lines);
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 14c82c38120d418bbd44cd7c0df0778d
timeCreated: 1729855802
AssetOrigin:
serializedVersion: 1
productId: 287769
packageName: Stylized Water 3
packageVersion: 3.2.7
assetPath: Assets/Stylized Water 3/Editor/Shader/TemplateParser.cs
uploadId: 927372

View File

@@ -0,0 +1,373 @@
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
using UnityEditor.AssetImporters;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEditor.Rendering;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.Serialization;
using Debug = UnityEngine.Debug;
using Object = UnityEngine.Object;
namespace StylizedWater3
{
[ScriptedImporter(TemplateParser.SHADER_GENERATOR_VERSION_MAJOR + TemplateParser.SHADER_GENERATOR_MINOR + TemplateParser.SHADER_GENERATOR_PATCH, TARGET_FILE_EXTENSION, 0)]
public class WaterShaderImporter : ScriptedImporter
{
private const string TARGET_FILE_EXTENSION = "watershader3";
private const string ICON_NAME = "water-shader-icon";
[Tooltip("Rather than storing the template in this file, it can be sourced from an external text file" +
"\nUse this if you intent to duplicate this asset, and need only minor modifications to its import settings")]
[SerializeField] public LazyLoadReference<Object> template;
[Space]
public WaterShaderSettings settings = new WaterShaderSettings();
/// <summary>
/// File paths of any file this shader depends on. This list will be populated with any "#include" paths present in the template
/// Registering these as dependencies is required to trigger the shader to recompile when these files are changed
/// </summary>
//[NonSerialized] //Want to keep these serialized. Will differ per-project, which also causes the file to appear as changed for every project when updating the asset (this triggers a re-import)
public List<string> dependencies = new List<string>();
[Serializable]
//Keep track of what was being compiled in
//Used to detect discrepencies between the project state, and the compiled shader
public class ConfigurationState
{
public bool underwaterRendering;
public bool dynamicEffects;
public FogIntegration.Integration fogIntegration;
public void Reset()
{
underwaterRendering = false;
dynamicEffects = false;
fogIntegration = FogIntegration.GetIntegration(FogIntegration.Assets.None);
}
}
public ConfigurationState configurationState = new ConfigurationState();
public string GetTemplatePath()
{
return template.isSet ? AssetDatabase.GetAssetPath(template.asset) : assetPath;
}
private void OnValidate()
{
if(settings.shaderName == string.Empty) settings.shaderName = $"{Application.productName} ({DateTime.Now.Ticks})";
}
public override void OnImportAsset(AssetImportContext context)
{
Shader shader = AssetDatabase.LoadAssetAtPath<Shader>(context.assetPath);
//if (shader != null) ShaderUtil.ClearShaderMessages(shader);
string templatePath = GetTemplatePath();
if (templatePath == string.Empty)
{
Debug.LogError("Failed to import water shader, template file path is null. It possibly hasn't been imported first?", shader);
return;
}
#if SWS_DEV
Stopwatch sw = new Stopwatch();
sw.Start();
#endif
string[] lines = File.ReadAllLines(templatePath);
if (lines.Length == 0)
{
Debug.LogError("Failed to generated water shader. Template or file content is empty (or wasn't yet imported)...");
return;
}
dependencies.Clear();
configurationState.Reset();
string shaderLab = TemplateParser.CreateShaderCode(context.assetPath, ref lines, this, false);
Shader shaderAsset = ShaderUtil.CreateShaderAsset(shaderLab, true);
int passCount = shaderAsset.passCount;
ShaderInfo shaderInfo = ShaderUtil.GetShaderInfo(shaderAsset);
ShaderData shaderData = ShaderUtil.GetShaderData(shaderAsset);
//Unity will always create 3 base passes: Unnamed, DepthNormalsOnly & DepthOnly
if (shaderInfo.hasErrors && shaderData.GetSubshader(0).GetPass(0).Name.Contains("Unnamed"))
{
Debug.LogError($"Failed to compile water shader at {context.assetPath}. It contains no passes. " +
$"This may happen if the shader file was imported while one or more script compile errors were present, or moving the Stylized Water 3 folder, or the meta-file was deleted. Resulting in all configurations getting wiped. To resolve this, re-import the file from the Package Manager.");
return;
}
ShaderUtil.RegisterShader(shaderAsset);
Texture2D thumbnail = Resources.Load<Texture2D>(ICON_NAME);
if(!thumbnail) thumbnail = EditorGUIUtility.IconContent("ShaderImporter Icon").image as Texture2D;
context.AddObjectToAsset("MainAsset", shaderAsset, thumbnail);
context.SetMainObject(shaderAsset);
//Do not attempt to create a tessellation variant for the underwater post-effect shaders
if (settings.type == WaterShaderSettings.ShaderType.WaterSurface)
{
//Re-read the original template again
lines = File.ReadAllLines(templatePath);
shaderLab = TemplateParser.CreateShaderCode(context.assetPath, ref lines, this, true);
Shader tessellation = ShaderUtil.CreateShaderAsset(shaderLab, true);
//ShaderUtil.RegisterShader(tessellation);
context.AddObjectToAsset("Tessellation", (Object)tessellation, thumbnail);
}
//Set up dependency, so that changes to the template triggers shaders to regenerate
if (template.isSet && AssetDatabase.TryGetGUIDAndLocalFileIdentifier(template, out var guid, out long _))
{
//Note: this strictly only works when adding the file path!
//context.DependsOnArtifact(guid);
dependencies.Insert(0, AssetDatabase.GUIDToAssetPath(guid));
}
//Dependencies are populated during the template parsing phase.
foreach (string dependency in dependencies)
{
context.DependsOnSourceAsset(dependency);
}
#if SWS_DEV
sw.Stop();
//Debug.Log($"Imported \"{Path.GetFileNameWithoutExtension(assetPath)}\" water shader in {sw.Elapsed.Milliseconds}ms. With {dependencies.Count} dependencies.", shader);
#endif
}
public bool RequiresRecompilation(out string message)
{
bool isValid = true;
var underwaterInstalled = configurationState.underwaterRendering == StylizedWaterEditor.UnderwaterRenderingInstalled();
var dynamicEffectsInstalled = configurationState.dynamicEffects == StylizedWaterEditor.DynamicEffectsInstalled();
var fogIntegration = configurationState.fogIntegration.asset == GetFogIntegration().asset;
isValid = underwaterInstalled & dynamicEffectsInstalled & fogIntegration;
message = string.Empty;
if (isValid == false)
{
if (!underwaterInstalled) message += "\nUnderwater Rendering extension installed, but not activated";
if (!dynamicEffectsInstalled) message += "\nDynamic Effects extension installed, but not activated";
if (!fogIntegration) message += $"\nFog integration does not match.\nInstalled: {configurationState.fogIntegration.name} - Detected in project: {GetFogIntegration().name}";
}
return !isValid;
}
public void Reimport()
{
this.SaveAndReimport();
}
public void ClearCache(bool recompile = false)
{
var objs = AssetDatabase.LoadAllAssetsAtPath(assetPath);
foreach (var obj in objs)
{
if (obj is Shader)
{
ShaderUtil.ClearShaderMessages((Shader)obj);
ShaderUtil.ClearCachedData((Shader)obj);
if(recompile) AssetDatabase.ImportAsset(assetPath);
#if SWS_DEV
Debug.Log($"Cleared cache for {obj.name}");
#endif
}
}
}
public void RegisterDependency(string dependencyAssetPath)
{
if (dependencyAssetPath.StartsWith("Packages/") == false)
{
string guid = AssetDatabase.AssetPathToGUID(dependencyAssetPath);
if (guid == string.Empty)
{
//Also throws an error for things like '#include_library "SurfaceModifiers/SurfaceModifiers.hlsl"', which are wrapped in an #ifdef. That's a false positive
//Debug.LogException(new Exception($"Tried to import \"{this.assetPath}\" with an missing dependency, supposedly at path: {dependencyAssetPath}."));
return;
}
}
//Tessellation variant pass may run, causing the same dependencies to be registered twice, hence check first
if(dependencies.Contains(dependencyAssetPath) == false) dependencies.Add(dependencyAssetPath);
}
//Handles correct behaviour when double-clicking a .watershader asset. Should open in the IDE
[UnityEditor.Callbacks.OnOpenAsset]
#if UNITY_6000_4_OR_NEWER
public static bool OnOpenAsset(EntityId instanceID, int line)
#else
public static bool OnOpenAsset(int instanceID, int line)
#endif
{
#if UNITY_6000_3_OR_NEWER
Object target = EditorUtility.EntityIdToObject(instanceID);
EntityId id = (EntityId)instanceID;
#else
Object target = EditorUtility.InstanceIDToObject(instanceID);
int id = instanceID;
#endif
if (target is Shader)
{
var path = AssetDatabase.GetAssetPath(id);
if (Path.GetExtension(path) != "." + TARGET_FILE_EXTENSION) return false;
string externalScriptEditor = ScriptEditorUtility.GetExternalScriptEditor();
if (externalScriptEditor != "internal" && externalScriptEditor != string.Empty)
{
InternalEditorUtility.OpenFileAtLineExternal(path, 0);
}
else
{
Application.OpenURL("file://" + path);
}
return true;
}
return false;
}
public static WaterShaderImporter GetForShader(Shader shader)
{
return AssetImporter.GetAtPath(AssetDatabase.GetAssetOrScenePath(shader)) as WaterShaderImporter;
}
public Shader GetShader()
{
return AssetDatabase.LoadAssetAtPath<Shader>(assetPath);
}
[Serializable]
public class Directive
{
public enum Type
{
[InspectorName("(no prefix)")]
custom,
[InspectorName("#include")]
include,
[InspectorName("#pragma")]
pragma,
[InspectorName("#include_with_pragmas")]
include_with_pragmas,
[InspectorName("#define")]
define
}
public bool enabled = true;
public Type type;
public string value;
public Directive(Type _type, string _value)
{
this.type = _type;
this.value = _value;
}
}
public static string[] FindAllAssets()
{
DirectoryInfo directoryInfo = new DirectoryInfo(Application.dataPath);
FileInfo[] fileInfos = directoryInfo.GetFiles("*." + TARGET_FILE_EXTENSION, SearchOption.AllDirectories);
#if SWS_DEV
//Debug.Log($"{fileInfos.Length} .{TARGET_FILE_EXTENSION} assets found");
#endif
string[] filePaths = new string[fileInfos.Length];
for (int i = 0; i < filePaths.Length; i++)
{
filePaths[i] = fileInfos[i].FullName.Replace(@"\", "/").Replace(Application.dataPath, "Assets");
}
return filePaths;
}
#if SWS_DEV
[MenuItem("SWS/Reimport water shaders")]
#endif
public static void ReimportAll()
{
string[] filePaths = FindAllAssets();
foreach (var filePath in filePaths)
{
#if SWS_DEV
//Debug.Log($"Reimporting: {filePath}");
#endif
AssetDatabase.ImportAsset(filePath);
}
}
public FogIntegration.Integration GetFogIntegration()
{
return settings != null && settings.autoIntegration ? FogIntegration.GetFirstInstalled() : FogIntegration.GetIntegration(settings.fogIntegration);
}
[Serializable]
public class WaterShaderSettings
{
[Tooltip("How it will appear in the selection menu")]
public string shaderName;
[Tooltip("Hide the shader in the selection menu. Yet still make it findable with Shader.Find()")]
public bool hidden;
public enum ShaderType
{
WaterSurface,
PostProcessing
}
public ShaderType type;
[Tooltip("Enable to disable the Advanced Shading mode on mobile platforms (including Nintendo Switch 1 & 2). Unity internally decides which platforms that is!")]
public bool forceSimpleShadingOnMobile = true;
[Tooltip("Before compiling the shader, check whichever asset is present in the project and activate its integration")]
public bool autoIntegration = true;
public FogIntegration.Assets fogIntegration = FogIntegration.Assets.UnityFog;
[Tooltip("Add support for native light cookies. Disabled by default to allow for cookies to act as caustics projectors that ignore the water surface")]
public bool lightCookies = false;
[Tooltip("Point and spot lights add caustics")]
public bool additionalLightCaustics = false;
public bool additionalLightTranslucency = true;
[Tooltip("When disabled, two caustics textures are cross-animated. Disable this when using a flipbook caustics texture!")]
public bool singleCausticsLayer;
public List<Directive> customIncludeDirectives = new List<Directive>();
[FormerlySerializedAs("passes")]
[Tooltip("Pass blocks that are to be added to the shader template")]
public Object[] additionalPasses = new Object[0];
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fb104378e2fbf2942ae8b66abb7a1d57
timeCreated: 1678979614
AssetOrigin:
serializedVersion: 1
productId: 287769
packageName: Stylized Water 3
packageVersion: 3.2.7
assetPath: Assets/Stylized Water 3/Editor/Shader/WaterShaderImporter.cs
uploadId: 927372