using System; using System.Text; using System.Collections.Generic; using System.Linq; using UnityEditor.ShaderGraph.Drawing.Controls; using UnityEngine; using UnityEditor.Graphing; using UnityEditor.ShaderGraph.Internal; namespace UnityEditor.ShaderGraph { [Serializable] [BlackboardInputInfo(60)] class VirtualTextureShaderProperty : AbstractShaderProperty { public VirtualTextureShaderProperty() { displayName = "VirtualTexture"; value = new SerializableVirtualTexture(); // add at least one layer value.layers = new List(); value.layers.Add(new SerializableVirtualTextureLayer("Layer0", new SerializableTexture())); value.layers.Add(new SerializableVirtualTextureLayer("Layer1", new SerializableTexture())); } public override PropertyType propertyType => PropertyType.VirtualTexture; internal override bool isExposable => true; // the textures are exposable at least.. internal override bool isRenamable => true; internal override void GetPropertyReferenceNames(List result) { result.Add(referenceName); for (int layer = 0; layer < value.layers.Count; layer++) { result.Add(value.layers[layer].layerRefName); } } internal override void GetPropertyDisplayNames(List result) { result.Add(displayName); for (int layer = 0; layer < value.layers.Count; layer++) { result.Add(value.layers[layer].layerName); } } // this is used for properties exposed to the Material in the shaderlab Properties{} block internal override void AppendPropertyBlockStrings(ShaderStringBuilder builder) { if (!value.procedural) { // adds properties in this format so: [TextureStack.MyStack(0)] [NoScaleOffset] Layer0("Layer0", 2D) = "white" {} for (int layer = 0; layer < value.layers.Count; layer++) { string layerName = value.layers[layer].layerName; string layerRefName = value.layers[layer].layerRefName; builder.AppendLine($"{hideTagString}[TextureStack.{referenceName}({layer})][NoScaleOffset]{layerRefName}(\"{layerName}\", 2D) = \"white\" {{}}"); } } else { // For procedural VT, we only need to expose a single property, indicating the referenceName and the number of layers // Adds a property as: // [ProceduralTextureStack.MyStack(1)] [NoScaleOffset] MyStack("Procedural Virtual Texture", 2D) = "white" {} // or: // [GlobalProceduralTextureStack.MyStack(2)] [NoScaleOffset] MyStack("Procedural Virtual Texture", 2D) = "white" {} string prefixString = value.shaderDeclaration == HLSLDeclaration.UnityPerMaterial ? "ProceduralTextureStack" : "GlobalProceduralTextureStack"; int numLayers = value.layers.Count; builder.AppendLine($"{hideTagString}[{prefixString}.{referenceName}({numLayers})][NoScaleOffset]{referenceName}(\"{"Procedural Virtual Texture"}\", 2D) = \"white\" {{}}"); } } internal override string GetPropertyBlockString() { // this should not be called, as it is replaced by the Append*PropertyBlockStrings function above throw new NotSupportedException(); } internal override bool AllowHLSLDeclaration(HLSLDeclaration decl) => false; // disable UI, nothing to choose internal override void ForeachHLSLProperty(Action action) { int numLayers = value.layers.Count; if (numLayers > 0) { HLSLDeclaration decl = (value.procedural) ? value.shaderDeclaration : HLSLDeclaration.UnityPerMaterial; action(new HLSLProperty(HLSLType._CUSTOM, referenceName + "_CBDecl", decl, concretePrecision) { customDeclaration = (ssb) => { ssb.TryAppendIndentation(); ssb.Append("DECLARE_STACK_CB("); ssb.Append(referenceName); ssb.Append(");"); ssb.AppendNewLine(); } }); if (!value.procedural) { //declare regular texture properties (for fallback case) for (int i = 0; i < numLayers; i++) { string layerRefName = value.layers[i].layerRefName; action(new HLSLProperty(HLSLType._Texture2D, layerRefName, HLSLDeclaration.Global)); action(new HLSLProperty(HLSLType._SamplerState, "sampler" + layerRefName, HLSLDeclaration.Global)); } } Action customDecl = (builder) => { // declare texture stack builder.TryAppendIndentation(); builder.Append("DECLARE_STACK"); builder.Append((numLayers <= 1) ? "" : numLayers.ToString()); builder.Append("("); builder.Append(referenceName); builder.Append(","); for (int i = 0; i < value.layers.Count; i++) { if (i != 0) builder.Append(","); builder.Append(value.layers[i].layerRefName); } builder.Append(");"); builder.AppendNewLine(); // declare the actual virtual texture property "variable" as a macro define to the BuildVTProperties function builder.TryAppendIndentation(); builder.Append("#define "); builder.Append(referenceName); builder.Append(" AddTextureType(BuildVTProperties_"); builder.Append(referenceName); builder.Append("()"); for (int i = 0; i < value.layers.Count; i++) { builder.Append(","); builder.Append("TEXTURETYPE_"); builder.Append(value.layers[i].layerTextureType.ToString().ToUpper()); } builder.Append(")"); builder.AppendNewLine(); }; action(new HLSLProperty(HLSLType._CUSTOM, referenceName + "_Global", HLSLDeclaration.Global, concretePrecision) { customDeclaration = customDecl }); } } // argument string used to pass this property to a subgraph internal override string GetPropertyAsArgumentString(string precisionString) { return "VTPropertyWithTextureType " + referenceName; } // if a blackboard property is deleted, or copy/pasted, all node instances of it are replaced with this: internal override AbstractMaterialNode ToConcreteNode() { return null; // return null to indicate there is NO concrete form of a VT property } internal override PreviewProperty GetPreviewMaterialProperty() { return new PreviewProperty(propertyType) { name = referenceName, vtProperty = this }; } internal override ShaderInput Copy() { var vt = new VirtualTextureShaderProperty { displayName = displayName, value = new SerializableVirtualTexture(), }; // duplicate layer data, but reset reference names (they should be unique) for (int layer = 0; layer < value.layers.Count; layer++) { var guid = Guid.NewGuid(); vt.value.layers.Add(new SerializableVirtualTextureLayer(value.layers[layer])); } return vt; } internal void AddTextureInfo(List infos) { for (int layer = 0; layer < value.layers.Count; layer++) { string layerRefName = value.layers[layer].layerRefName; var layerTexture = value.layers[layer].layerTexture; var texture = layerTexture != null ? layerTexture.texture : null; var textureInfo = new PropertyCollector.TextureInfo { name = layerRefName, textureId = texture != null ? texture.GetInstanceID() : 0, dimension = texture != null ? texture.dimension : UnityEngine.Rendering.TextureDimension.Any, modifiable = true }; infos.Add(textureInfo); } } internal override bool isAlwaysExposed => true; internal override bool isCustomSlotAllowed => false; public override void OnAfterDeserialize(string json) { // VT shader properties must be exposed so they can be picked up by the native-side VT system generatePropertyBlock = true; } } }