A game about forced loneliness, made by TACStudios
1using System;
2using System.Text;
3using System.Collections.Generic;
4using System.Linq;
5using UnityEditor.ShaderGraph.Drawing.Controls;
6using UnityEngine;
7using UnityEditor.Graphing;
8using UnityEditor.ShaderGraph.Internal;
9
10namespace UnityEditor.ShaderGraph
11{
12 [Serializable]
13 [BlackboardInputInfo(60)]
14 class VirtualTextureShaderProperty : AbstractShaderProperty<SerializableVirtualTexture>
15 {
16 public VirtualTextureShaderProperty()
17 {
18 displayName = "VirtualTexture";
19 value = new SerializableVirtualTexture();
20
21 // add at least one layer
22 value.layers = new List<SerializableVirtualTextureLayer>();
23 value.layers.Add(new SerializableVirtualTextureLayer("Layer0", new SerializableTexture()));
24 value.layers.Add(new SerializableVirtualTextureLayer("Layer1", new SerializableTexture()));
25 }
26
27 public override PropertyType propertyType => PropertyType.VirtualTexture;
28
29 internal override bool isExposable => true; // the textures are exposable at least..
30 internal override bool isRenamable => true;
31
32 internal override void GetPropertyReferenceNames(List<string> result)
33 {
34 result.Add(referenceName);
35 for (int layer = 0; layer < value.layers.Count; layer++)
36 {
37 result.Add(value.layers[layer].layerRefName);
38 }
39 }
40
41 internal override void GetPropertyDisplayNames(List<string> result)
42 {
43 result.Add(displayName);
44 for (int layer = 0; layer < value.layers.Count; layer++)
45 {
46 result.Add(value.layers[layer].layerName);
47 }
48 }
49
50 // this is used for properties exposed to the Material in the shaderlab Properties{} block
51 internal override void AppendPropertyBlockStrings(ShaderStringBuilder builder)
52 {
53 if (!value.procedural)
54 {
55 // adds properties in this format so: [TextureStack.MyStack(0)] [NoScaleOffset] Layer0("Layer0", 2D) = "white" {}
56 for (int layer = 0; layer < value.layers.Count; layer++)
57 {
58 string layerName = value.layers[layer].layerName;
59 string layerRefName = value.layers[layer].layerRefName;
60 builder.AppendLine($"{hideTagString}[TextureStack.{referenceName}({layer})][NoScaleOffset]{layerRefName}(\"{layerName}\", 2D) = \"white\" {{}}");
61 }
62 }
63 else
64 {
65 // For procedural VT, we only need to expose a single property, indicating the referenceName and the number of layers
66
67 // Adds a property as:
68 // [ProceduralTextureStack.MyStack(1)] [NoScaleOffset] MyStack("Procedural Virtual Texture", 2D) = "white" {}
69 // or:
70 // [GlobalProceduralTextureStack.MyStack(2)] [NoScaleOffset] MyStack("Procedural Virtual Texture", 2D) = "white" {}
71 string prefixString = value.shaderDeclaration == HLSLDeclaration.UnityPerMaterial
72 ? "ProceduralTextureStack"
73 : "GlobalProceduralTextureStack";
74
75 int numLayers = value.layers.Count;
76 builder.AppendLine($"{hideTagString}[{prefixString}.{referenceName}({numLayers})][NoScaleOffset]{referenceName}(\"{"Procedural Virtual Texture"}\", 2D) = \"white\" {{}}");
77 }
78 }
79
80 internal override string GetPropertyBlockString()
81 {
82 // this should not be called, as it is replaced by the Append*PropertyBlockStrings function above
83 throw new NotSupportedException();
84 }
85
86 internal override bool AllowHLSLDeclaration(HLSLDeclaration decl) => false; // disable UI, nothing to choose
87
88 internal override void ForeachHLSLProperty(Action<HLSLProperty> action)
89 {
90 int numLayers = value.layers.Count;
91 if (numLayers > 0)
92 {
93 HLSLDeclaration decl = (value.procedural) ? value.shaderDeclaration : HLSLDeclaration.UnityPerMaterial;
94
95 action(new HLSLProperty(HLSLType._CUSTOM, referenceName + "_CBDecl", decl, concretePrecision)
96 {
97 customDeclaration = (ssb) =>
98 {
99 ssb.TryAppendIndentation();
100 ssb.Append("DECLARE_STACK_CB(");
101 ssb.Append(referenceName);
102 ssb.Append(");");
103 ssb.AppendNewLine();
104 }
105 });
106
107 if (!value.procedural)
108 {
109 //declare regular texture properties (for fallback case)
110 for (int i = 0; i < numLayers; i++)
111 {
112 string layerRefName = value.layers[i].layerRefName;
113 action(new HLSLProperty(HLSLType._Texture2D, layerRefName, HLSLDeclaration.Global));
114 action(new HLSLProperty(HLSLType._SamplerState, "sampler" + layerRefName, HLSLDeclaration.Global));
115 }
116 }
117
118 Action<ShaderStringBuilder> customDecl = (builder) =>
119 {
120 // declare texture stack
121 builder.TryAppendIndentation();
122 builder.Append("DECLARE_STACK");
123 builder.Append((numLayers <= 1) ? "" : numLayers.ToString());
124 builder.Append("(");
125 builder.Append(referenceName);
126 builder.Append(",");
127 for (int i = 0; i < value.layers.Count; i++)
128 {
129 if (i != 0) builder.Append(",");
130 builder.Append(value.layers[i].layerRefName);
131 }
132 builder.Append(");");
133 builder.AppendNewLine();
134
135 // declare the actual virtual texture property "variable" as a macro define to the BuildVTProperties function
136 builder.TryAppendIndentation();
137 builder.Append("#define ");
138 builder.Append(referenceName);
139 builder.Append(" AddTextureType(BuildVTProperties_");
140 builder.Append(referenceName);
141 builder.Append("()");
142 for (int i = 0; i < value.layers.Count; i++)
143 {
144 builder.Append(",");
145 builder.Append("TEXTURETYPE_");
146 builder.Append(value.layers[i].layerTextureType.ToString().ToUpper());
147 }
148 builder.Append(")");
149 builder.AppendNewLine();
150 };
151
152 action(new HLSLProperty(HLSLType._CUSTOM, referenceName + "_Global", HLSLDeclaration.Global, concretePrecision)
153 {
154 customDeclaration = customDecl
155 });
156 }
157 }
158
159 // argument string used to pass this property to a subgraph
160 internal override string GetPropertyAsArgumentString(string precisionString)
161 {
162 return "VTPropertyWithTextureType " + referenceName;
163 }
164
165 // if a blackboard property is deleted, or copy/pasted, all node instances of it are replaced with this:
166 internal override AbstractMaterialNode ToConcreteNode()
167 {
168 return null; // return null to indicate there is NO concrete form of a VT property
169 }
170
171 internal override PreviewProperty GetPreviewMaterialProperty()
172 {
173 return new PreviewProperty(propertyType)
174 {
175 name = referenceName,
176 vtProperty = this
177 };
178 }
179
180 internal override ShaderInput Copy()
181 {
182 var vt = new VirtualTextureShaderProperty
183 {
184 displayName = displayName,
185 value = new SerializableVirtualTexture(),
186 };
187
188 // duplicate layer data, but reset reference names (they should be unique)
189 for (int layer = 0; layer < value.layers.Count; layer++)
190 {
191 var guid = Guid.NewGuid();
192 vt.value.layers.Add(new SerializableVirtualTextureLayer(value.layers[layer]));
193 }
194
195 return vt;
196 }
197
198 internal void AddTextureInfo(List<PropertyCollector.TextureInfo> infos)
199 {
200 for (int layer = 0; layer < value.layers.Count; layer++)
201 {
202 string layerRefName = value.layers[layer].layerRefName;
203 var layerTexture = value.layers[layer].layerTexture;
204 var texture = layerTexture != null ? layerTexture.texture : null;
205
206 var textureInfo = new PropertyCollector.TextureInfo
207 {
208 name = layerRefName,
209 textureId = texture != null ? texture.GetInstanceID() : 0,
210 dimension = texture != null ? texture.dimension : UnityEngine.Rendering.TextureDimension.Any,
211 modifiable = true
212 };
213 infos.Add(textureInfo);
214 }
215 }
216
217 internal override bool isAlwaysExposed => true;
218 internal override bool isCustomSlotAllowed => false;
219
220 public override void OnAfterDeserialize(string json)
221 {
222 // VT shader properties must be exposed so they can be picked up by the native-side VT system
223 generatePropertyBlock = true;
224 }
225 }
226}