Repository: needle-tools/shadergraph-markdown Branch: main Commit: e55d6980c838 Files: 1751 Total size: 5.2 MB Directory structure: gitextract_2ejx0txv/ ├── .github/ │ └── workflows/ │ └── Release.yml ├── .gitignore ├── README.md ├── package/ │ ├── .npmignore │ ├── Changelog.md │ ├── Changelog.md.meta │ ├── Documentation/ │ │ └── Shader Graph Markdown - Documentation.pdf.meta │ ├── Documentation.meta │ ├── Editor/ │ │ ├── Drawers/ │ │ │ ├── GradientDrawer.cs │ │ │ ├── GradientDrawer.cs.meta │ │ │ ├── GradientGeneratorDrawer.cs │ │ │ ├── GradientGeneratorDrawer.cs.meta │ │ │ ├── InlineTextureDrawer.cs │ │ │ ├── InlineTextureDrawer.cs.meta │ │ │ ├── MarkdownMaterialPropertyDrawer.cs │ │ │ ├── MarkdownMaterialPropertyDrawer.cs.meta │ │ │ ├── MinMaxDrawer.cs │ │ │ ├── MinMaxDrawer.cs.meta │ │ │ ├── MultiPropertyDrawer.cs │ │ │ ├── MultiPropertyDrawer.cs.meta │ │ │ ├── VectorSliderDrawer.cs │ │ │ └── VectorSliderDrawer.cs.meta │ │ ├── Drawers.meta │ │ ├── Extensions/ │ │ │ ├── LogicExpressionParser.cs │ │ │ ├── LogicExpressionParser.cs.meta │ │ │ ├── ShaderRefactoringWindow.cs │ │ │ └── ShaderRefactoringWindow.cs.meta │ │ ├── Extensions.meta │ │ ├── Internal/ │ │ │ ├── Common/ │ │ │ │ ├── CommonInternalExtensions.cs │ │ │ │ ├── CommonInternalExtensions.cs.meta │ │ │ │ ├── Needle.ShaderGraphMarkdownCommonInternal.asmref │ │ │ │ └── Needle.ShaderGraphMarkdownCommonInternal.asmref.meta │ │ │ ├── Common.meta │ │ │ ├── CommonExtensions.cs │ │ │ ├── CommonExtensions.cs.meta │ │ │ ├── HDRP/ │ │ │ │ ├── MarkdownHDExtensions.cs │ │ │ │ ├── MarkdownHDExtensions.cs.meta │ │ │ │ ├── Needle.ShaderGraphMarkdown.HDInternal.asmref │ │ │ │ └── Needle.ShaderGraphMarkdown.HDInternal.asmref.meta │ │ │ ├── HDRP.meta │ │ │ ├── Needle.ShaderGraphMarkdown.Internal.asmdef │ │ │ ├── Needle.ShaderGraphMarkdown.Internal.asmdef.meta │ │ │ ├── ShaderGraph/ │ │ │ │ ├── Experimental/ │ │ │ │ │ ├── MarkdownBlackboardProperties.cs │ │ │ │ │ ├── MarkdownBlackboardProperties.cs.meta │ │ │ │ │ ├── PropertyManipulation.cs │ │ │ │ │ └── PropertyManipulation.cs.meta │ │ │ │ ├── Experimental.meta │ │ │ │ ├── MarkdownSGExtensions.cs │ │ │ │ ├── MarkdownSGExtensions.cs.meta │ │ │ │ ├── Needle.ShaderGraphMarkdown.SGInternal.asmref │ │ │ │ └── Needle.ShaderGraphMarkdown.SGInternal.asmref.meta │ │ │ ├── ShaderGraph.meta │ │ │ ├── URP/ │ │ │ │ ├── MarkdownURPExtensions.cs │ │ │ │ ├── MarkdownURPExtensions.cs.meta │ │ │ │ ├── Needle.ShaderGraphMarkdown.URPInternal.asmref │ │ │ │ └── Needle.ShaderGraphMarkdown.URPInternal.asmref.meta │ │ │ └── URP.meta │ │ ├── Internal.meta │ │ ├── MarkdownShaderGUI.cs │ │ ├── MarkdownShaderGUI.cs.meta │ │ ├── Needle/ │ │ │ ├── MarkdownNeedleIcons.cs │ │ │ ├── MarkdownNeedleIcons.cs.meta │ │ │ ├── logo-button.png.meta │ │ │ ├── needlelogo.png.meta │ │ │ └── needlelogo_white.png.meta │ │ ├── Needle.ShaderGraphMarkdown.asmdef │ │ ├── Needle.ShaderGraphMarkdown.asmdef.meta │ │ ├── Needle.meta │ │ ├── Resources/ │ │ │ ├── Styles/ │ │ │ │ ├── ShaderGraphMarkdown.uss │ │ │ │ └── ShaderGraphMarkdown.uss.meta │ │ │ └── Styles.meta │ │ ├── Resources.meta │ │ ├── Settings/ │ │ │ ├── ShaderGraphMarkdownSettings.cs │ │ │ └── ShaderGraphMarkdownSettings.cs.meta │ │ ├── Settings.meta │ │ ├── ShaderGraphPropertyUI.cs │ │ ├── ShaderGraphPropertyUI.cs.meta │ │ ├── Shims/ │ │ │ ├── CoreEditorUtilsShims.cs │ │ │ └── CoreEditorUtilsShims.cs.meta │ │ └── Shims.meta │ ├── Editor.meta │ ├── Readme.md │ ├── Readme.md.meta │ ├── Samples~/ │ │ ├── Amplify/ │ │ │ ├── Markdown Amplify Shader.mat │ │ │ ├── Markdown Amplify Shader.mat.meta │ │ │ ├── Markdown Amplify Shader.shader │ │ │ └── Markdown Amplify Shader.shader.meta │ │ ├── Amplify.meta │ │ ├── Built-In/ │ │ │ ├── Gradient Textures/ │ │ │ │ ├── SampleSurfaceShader_MyGradientTexture_1_Gradient.png.meta │ │ │ │ └── SampleSurfaceShader_MyGradientTexture_2_Gradient.png.meta │ │ │ ├── Gradient Textures.meta │ │ │ ├── SampleSurfaceShader.mat │ │ │ ├── SampleSurfaceShader.mat.meta │ │ │ ├── SampleSurfaceShader.shader │ │ │ └── SampleSurfaceShader.shader.meta │ │ ├── Built-In.meta │ │ ├── HDRP 7+ (Unity 2019.4+)/ │ │ │ ├── SampleShader-HDRP7.shadergraph │ │ │ ├── SampleShader-HDRP7.shadergraph.meta │ │ │ ├── Shader Graphs_SampleShader-HDRP7.mat │ │ │ └── Shader Graphs_SampleShader-HDRP7.mat.meta │ │ ├── HDRP 7+ (Unity 2019.4+).meta │ │ ├── URP 7+ (Unity 2019.4+)/ │ │ │ ├── Gradient Textures/ │ │ │ │ └── Shader Graphs_SampleShader-URP7_RampTexture_Gradient.png.meta │ │ │ ├── Gradient Textures.meta │ │ │ ├── SampleShader-URP7.shadergraph │ │ │ ├── SampleShader-URP7.shadergraph.meta │ │ │ ├── Shader Graphs_SampleShader-URP7.mat │ │ │ └── Shader Graphs_SampleShader-URP7.mat.meta │ │ ├── URP 7+ (Unity 2019.4+).meta │ │ ├── URP and HDRP 10+ (Unity 2020.2+)/ │ │ │ ├── Gradient Textures/ │ │ │ │ ├── Shader Graphs_SampleShader-SRP10_MyGradientTexture_1_Gradient.png.meta │ │ │ │ └── Shader Graphs_SampleShader-SRP10_MyGradientTexture_2_Gradient.png.meta │ │ │ ├── Gradient Textures.meta │ │ │ ├── SampleShader-SRP10.shadergraph │ │ │ ├── SampleShader-SRP10.shadergraph.meta │ │ │ ├── SampleShader-Unnamed Properties.shadergraph │ │ │ ├── SampleShader-Unnamed Properties.shadergraph.meta │ │ │ ├── Shader Graphs_SampleShader-SRP10.mat │ │ │ ├── Shader Graphs_SampleShader-SRP10.mat.meta │ │ │ ├── Shader Graphs_Unnamed Properties.mat │ │ │ └── Shader Graphs_Unnamed Properties.mat.meta │ │ ├── URP and HDRP 10+ (Unity 2020.2+).meta │ │ ├── Uber Shader (Unity 2020.2+)/ │ │ │ ├── Cloth.mat │ │ │ ├── Cloth.mat.meta │ │ │ ├── FloatingSpheres.mat │ │ │ ├── FloatingSpheres.mat.meta │ │ │ ├── Floor.mat │ │ │ ├── Floor.mat.meta │ │ │ ├── FloorBorder.mat │ │ │ ├── FloorBorder.mat.meta │ │ │ ├── FloorSpheres.mat │ │ │ ├── FloorSpheres.mat.meta │ │ │ ├── Gradient Textures/ │ │ │ │ ├── Cloth_RampTexture_Gradient.png.meta │ │ │ │ ├── Greybox_RampTexture_Gradient.png.meta │ │ │ │ ├── Mat 1_RampTexture_Gradient.png.meta │ │ │ │ ├── Mat 2_RampTexture_Gradient.png.meta │ │ │ │ ├── Mat 3_RampTexture_Gradient.png.meta │ │ │ │ ├── Rings_RampTexture_Gradient.png.meta │ │ │ │ └── Spheres_RampTexture_Gradient.png.meta │ │ │ ├── Gradient Textures.meta │ │ │ ├── Models/ │ │ │ │ ├── MarkdownSampleObjects-1.fbx │ │ │ │ ├── MarkdownSampleObjects-1.fbx.meta │ │ │ │ ├── MarkdownSampleObjects-2.fbx │ │ │ │ └── MarkdownSampleObjects-2.fbx.meta │ │ │ ├── Models.meta │ │ │ ├── Ring.mat │ │ │ ├── Ring.mat.meta │ │ │ ├── Sample Scene/ │ │ │ │ ├── LightingData.asset │ │ │ │ ├── LightingData.asset.meta │ │ │ │ ├── Markdown-SkyboxBlurred.mat │ │ │ │ ├── Markdown-SkyboxBlurred.mat.meta │ │ │ │ ├── Markdown-blurred_background.jpg.meta │ │ │ │ ├── ReflectionProbe-0.exr │ │ │ │ ├── ReflectionProbe-0.exr.meta │ │ │ │ ├── ReflectionProbe-1.exr │ │ │ │ ├── ReflectionProbe-1.exr.meta │ │ │ │ ├── ReflectionProbe-2.exr │ │ │ │ ├── ReflectionProbe-2.exr.meta │ │ │ │ ├── ReflectionProbe-3.exr │ │ │ │ ├── ReflectionProbe-3.exr.meta │ │ │ │ ├── ReflectionProbe-4.exr │ │ │ │ ├── ReflectionProbe-4.exr.meta │ │ │ │ ├── Sample Scene Lighting.lighting │ │ │ │ ├── Sample Scene Lighting.lighting.meta │ │ │ │ ├── Volume Profile.asset │ │ │ │ └── Volume Profile.asset.meta │ │ │ ├── Sample Scene.meta │ │ │ ├── Sample Scene.unity │ │ │ ├── Sample Scene.unity.meta │ │ │ ├── SampleUberShader.shadergraph │ │ │ ├── SampleUberShader.shadergraph.meta │ │ │ ├── SmallRings.mat │ │ │ ├── SmallRings.mat.meta │ │ │ ├── Torus.mat │ │ │ └── Torus.mat.meta │ │ └── Uber Shader (Unity 2020.2+).meta │ ├── package.json │ └── package.json.meta └── projects/ ├── ShadergraphMarkdown 2019.4/ │ ├── .vscode/ │ │ └── settings.json │ ├── Needle.ShaderGraphMarkdown.Internal.csproj │ ├── Needle.ShaderGraphMarkdown.csproj │ ├── Packages/ │ │ ├── com.unity.asset-store-tools/ │ │ │ ├── CHANGELOG.md │ │ │ ├── CHANGELOG.md.meta │ │ │ ├── Editor/ │ │ │ │ ├── Api/ │ │ │ │ │ ├── Abstractions/ │ │ │ │ │ │ ├── AuthenticationBase.cs │ │ │ │ │ │ ├── AuthenticationBase.cs.meta │ │ │ │ │ │ ├── IAssetStoreApi.cs │ │ │ │ │ │ ├── IAssetStoreApi.cs.meta │ │ │ │ │ │ ├── IAssetStoreClient.cs │ │ │ │ │ │ ├── IAssetStoreClient.cs.meta │ │ │ │ │ │ ├── IAuthenticationType.cs │ │ │ │ │ │ ├── IAuthenticationType.cs.meta │ │ │ │ │ │ ├── IPackageUploader.cs │ │ │ │ │ │ ├── IPackageUploader.cs.meta │ │ │ │ │ │ ├── PackageUploaderBase.cs │ │ │ │ │ │ └── PackageUploaderBase.cs.meta │ │ │ │ │ ├── Abstractions.meta │ │ │ │ │ ├── ApiUtility.cs │ │ │ │ │ ├── ApiUtility.cs.meta │ │ │ │ │ ├── AssetStoreApi.cs │ │ │ │ │ ├── AssetStoreApi.cs.meta │ │ │ │ │ ├── AssetStoreClient.cs │ │ │ │ │ ├── AssetStoreClient.cs.meta │ │ │ │ │ ├── CloudTokenAuthentication.cs │ │ │ │ │ ├── CloudTokenAuthentication.cs.meta │ │ │ │ │ ├── CredentialsAuthentication.cs │ │ │ │ │ ├── CredentialsAuthentication.cs.meta │ │ │ │ │ ├── Models/ │ │ │ │ │ │ ├── Category.cs │ │ │ │ │ │ ├── Category.cs.meta │ │ │ │ │ │ ├── Package.cs │ │ │ │ │ │ ├── Package.cs.meta │ │ │ │ │ │ ├── PackageAdditionalData.cs │ │ │ │ │ │ ├── PackageAdditionalData.cs.meta │ │ │ │ │ │ ├── User.cs │ │ │ │ │ │ └── User.cs.meta │ │ │ │ │ ├── Models.meta │ │ │ │ │ ├── Responses/ │ │ │ │ │ │ ├── AssetStoreResponse.cs │ │ │ │ │ │ ├── AssetStoreResponse.cs.meta │ │ │ │ │ │ ├── AssetStoreToolsVersionResponse.cs │ │ │ │ │ │ ├── AssetStoreToolsVersionResponse.cs.meta │ │ │ │ │ │ ├── AuthenticationResponse.cs │ │ │ │ │ │ ├── AuthenticationResponse.cs.meta │ │ │ │ │ │ ├── CategoryDataResponse.cs │ │ │ │ │ │ ├── CategoryDataResponse.cs.meta │ │ │ │ │ │ ├── PackageThumbnailResponse.cs │ │ │ │ │ │ ├── PackageThumbnailResponse.cs.meta │ │ │ │ │ │ ├── PackageUploadedUnityVersionDataResponse.cs │ │ │ │ │ │ ├── PackageUploadedUnityVersionDataResponse.cs.meta │ │ │ │ │ │ ├── PackagesAdditionalDataResponse.cs │ │ │ │ │ │ ├── PackagesAdditionalDataResponse.cs.meta │ │ │ │ │ │ ├── PackagesDataResponse.cs │ │ │ │ │ │ ├── PackagesDataResponse.cs.meta │ │ │ │ │ │ ├── RefreshedPackageDataResponse.cs │ │ │ │ │ │ ├── RefreshedPackageDataResponse.cs.meta │ │ │ │ │ │ ├── UploadResponse.cs │ │ │ │ │ │ └── UploadResponse.cs.meta │ │ │ │ │ ├── Responses.meta │ │ │ │ │ ├── SessionAuthentication.cs │ │ │ │ │ ├── SessionAuthentication.cs.meta │ │ │ │ │ ├── UnityPackageUploader.cs │ │ │ │ │ ├── UnityPackageUploader.cs.meta │ │ │ │ │ ├── UploadStatus.cs │ │ │ │ │ └── UploadStatus.cs.meta │ │ │ │ ├── Api.meta │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ ├── AssemblyInfo.cs.meta │ │ │ │ ├── AssetStoreTools.cs │ │ │ │ ├── AssetStoreTools.cs.meta │ │ │ │ ├── AssetStoreToolsWindow.cs │ │ │ │ ├── AssetStoreToolsWindow.cs.meta │ │ │ │ ├── Constants.cs │ │ │ │ ├── Constants.cs.meta │ │ │ │ ├── Exporter/ │ │ │ │ │ ├── Abstractions/ │ │ │ │ │ │ ├── IPackageExporter.cs │ │ │ │ │ │ ├── IPackageExporter.cs.meta │ │ │ │ │ │ ├── IPreviewInjector.cs │ │ │ │ │ │ ├── IPreviewInjector.cs.meta │ │ │ │ │ │ ├── PackageExporterBase.cs │ │ │ │ │ │ ├── PackageExporterBase.cs.meta │ │ │ │ │ │ ├── PackageExporterSettings.cs │ │ │ │ │ │ └── PackageExporterSettings.cs.meta │ │ │ │ │ ├── Abstractions.meta │ │ │ │ │ ├── DefaultExporterSettings.cs │ │ │ │ │ ├── DefaultExporterSettings.cs.meta │ │ │ │ │ ├── DefaultPackageExporter.cs │ │ │ │ │ ├── DefaultPackageExporter.cs.meta │ │ │ │ │ ├── LegacyExporterSettings.cs │ │ │ │ │ ├── LegacyExporterSettings.cs.meta │ │ │ │ │ ├── LegacyPackageExporter.cs │ │ │ │ │ ├── LegacyPackageExporter.cs.meta │ │ │ │ │ ├── PackageExporterResult.cs │ │ │ │ │ ├── PackageExporterResult.cs.meta │ │ │ │ │ ├── PreviewInjector.cs │ │ │ │ │ └── PreviewInjector.cs.meta │ │ │ │ ├── Exporter.meta │ │ │ │ ├── Previews/ │ │ │ │ │ ├── Scripts/ │ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ │ ├── CustomPreviewGenerationSettings.cs │ │ │ │ │ │ │ ├── CustomPreviewGenerationSettings.cs.meta │ │ │ │ │ │ │ ├── FileNameFormat.cs │ │ │ │ │ │ │ ├── FileNameFormat.cs.meta │ │ │ │ │ │ │ ├── GenerationType.cs │ │ │ │ │ │ │ ├── GenerationType.cs.meta │ │ │ │ │ │ │ ├── NativePreviewGenerationSettings.cs │ │ │ │ │ │ │ ├── NativePreviewGenerationSettings.cs.meta │ │ │ │ │ │ │ ├── PreviewDatabase.cs │ │ │ │ │ │ │ ├── PreviewDatabase.cs.meta │ │ │ │ │ │ │ ├── PreviewFormat.cs │ │ │ │ │ │ │ ├── PreviewFormat.cs.meta │ │ │ │ │ │ │ ├── PreviewGenerationResult.cs │ │ │ │ │ │ │ ├── PreviewGenerationResult.cs.meta │ │ │ │ │ │ │ ├── PreviewGenerationSettings.cs │ │ │ │ │ │ │ ├── PreviewGenerationSettings.cs.meta │ │ │ │ │ │ │ ├── PreviewMetadata.cs │ │ │ │ │ │ │ └── PreviewMetadata.cs.meta │ │ │ │ │ │ ├── Data.meta │ │ │ │ │ │ ├── Generators/ │ │ │ │ │ │ │ ├── Custom/ │ │ │ │ │ │ │ │ ├── AudioChannel.cs │ │ │ │ │ │ │ │ ├── AudioChannel.cs.meta │ │ │ │ │ │ │ │ ├── AudioChannelCoordinate.cs │ │ │ │ │ │ │ │ ├── AudioChannelCoordinate.cs.meta │ │ │ │ │ │ │ │ ├── Screenshotters/ │ │ │ │ │ │ │ │ │ ├── ISceneScreenshotter.cs │ │ │ │ │ │ │ │ │ ├── ISceneScreenshotter.cs.meta │ │ │ │ │ │ │ │ │ ├── MaterialScreenshotter.cs │ │ │ │ │ │ │ │ │ ├── MaterialScreenshotter.cs.meta │ │ │ │ │ │ │ │ │ ├── MeshScreenshotter.cs │ │ │ │ │ │ │ │ │ ├── MeshScreenshotter.cs.meta │ │ │ │ │ │ │ │ │ ├── SceneScreenshotterBase.cs │ │ │ │ │ │ │ │ │ ├── SceneScreenshotterBase.cs.meta │ │ │ │ │ │ │ │ │ ├── SceneScreenshotterSettings.cs │ │ │ │ │ │ │ │ │ └── SceneScreenshotterSettings.cs.meta │ │ │ │ │ │ │ │ ├── Screenshotters.meta │ │ │ │ │ │ │ │ ├── TypeGenerators/ │ │ │ │ │ │ │ │ │ ├── AudioTypeGeneratorSettings.cs │ │ │ │ │ │ │ │ │ ├── AudioTypeGeneratorSettings.cs.meta │ │ │ │ │ │ │ │ │ ├── AudioTypePreviewGenerator.cs │ │ │ │ │ │ │ │ │ ├── AudioTypePreviewGenerator.cs.meta │ │ │ │ │ │ │ │ │ ├── ITypePreviewGenerator.cs │ │ │ │ │ │ │ │ │ ├── ITypePreviewGenerator.cs.meta │ │ │ │ │ │ │ │ │ ├── MaterialTypePreviewGenerator.cs │ │ │ │ │ │ │ │ │ ├── MaterialTypePreviewGenerator.cs.meta │ │ │ │ │ │ │ │ │ ├── ModelTypePreviewGenerator.cs │ │ │ │ │ │ │ │ │ ├── ModelTypePreviewGenerator.cs.meta │ │ │ │ │ │ │ │ │ ├── PrefabTypePreviewGenerator.cs │ │ │ │ │ │ │ │ │ ├── PrefabTypePreviewGenerator.cs.meta │ │ │ │ │ │ │ │ │ ├── TextureTypeGeneratorSettings.cs │ │ │ │ │ │ │ │ │ ├── TextureTypeGeneratorSettings.cs.meta │ │ │ │ │ │ │ │ │ ├── TextureTypePreviewGenerator.cs │ │ │ │ │ │ │ │ │ ├── TextureTypePreviewGenerator.cs.meta │ │ │ │ │ │ │ │ │ ├── TypeGeneratorSettings.cs │ │ │ │ │ │ │ │ │ ├── TypeGeneratorSettings.cs.meta │ │ │ │ │ │ │ │ │ ├── TypePreviewGeneratorBase.cs │ │ │ │ │ │ │ │ │ ├── TypePreviewGeneratorBase.cs.meta │ │ │ │ │ │ │ │ │ ├── TypePreviewGeneratorFromScene.cs │ │ │ │ │ │ │ │ │ ├── TypePreviewGeneratorFromScene.cs.meta │ │ │ │ │ │ │ │ │ ├── TypePreviewGeneratorFromSceneSettings.cs │ │ │ │ │ │ │ │ │ └── TypePreviewGeneratorFromSceneSettings.cs.meta │ │ │ │ │ │ │ │ └── TypeGenerators.meta │ │ │ │ │ │ │ ├── Custom.meta │ │ │ │ │ │ │ ├── CustomPreviewGenerator.cs │ │ │ │ │ │ │ ├── CustomPreviewGenerator.cs.meta │ │ │ │ │ │ │ ├── IPreviewGenerator.cs │ │ │ │ │ │ │ ├── IPreviewGenerator.cs.meta │ │ │ │ │ │ │ ├── NativePreviewGenerator.cs │ │ │ │ │ │ │ ├── NativePreviewGenerator.cs.meta │ │ │ │ │ │ │ ├── PreviewGeneratorBase.cs │ │ │ │ │ │ │ └── PreviewGeneratorBase.cs.meta │ │ │ │ │ │ ├── Generators.meta │ │ │ │ │ │ ├── Services/ │ │ │ │ │ │ │ ├── Caching/ │ │ │ │ │ │ │ │ ├── CachingService.cs │ │ │ │ │ │ │ │ ├── CachingService.cs.meta │ │ │ │ │ │ │ │ ├── ICachingService.cs │ │ │ │ │ │ │ │ └── ICachingService.cs.meta │ │ │ │ │ │ │ ├── Caching.meta │ │ │ │ │ │ │ ├── IPreviewService.cs │ │ │ │ │ │ │ ├── IPreviewService.cs.meta │ │ │ │ │ │ │ ├── PreviewServiceProvider.cs │ │ │ │ │ │ │ └── PreviewServiceProvider.cs.meta │ │ │ │ │ │ ├── Services.meta │ │ │ │ │ │ ├── UI/ │ │ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ │ │ ├── AssetPreview.cs │ │ │ │ │ │ │ │ ├── AssetPreview.cs.meta │ │ │ │ │ │ │ │ ├── AssetPreviewCollection.cs │ │ │ │ │ │ │ │ ├── AssetPreviewCollection.cs.meta │ │ │ │ │ │ │ │ ├── IAssetPreview.cs │ │ │ │ │ │ │ │ ├── IAssetPreview.cs.meta │ │ │ │ │ │ │ │ ├── IAssetPreviewCollection.cs │ │ │ │ │ │ │ │ ├── IAssetPreviewCollection.cs.meta │ │ │ │ │ │ │ │ ├── IPreviewGeneratorSettings.cs │ │ │ │ │ │ │ │ ├── IPreviewGeneratorSettings.cs.meta │ │ │ │ │ │ │ │ ├── PreviewGeneratorSettings.cs │ │ │ │ │ │ │ │ └── PreviewGeneratorSettings.cs.meta │ │ │ │ │ │ │ ├── Data.meta │ │ │ │ │ │ │ ├── Elements/ │ │ │ │ │ │ │ │ ├── AssetPreviewElement.cs │ │ │ │ │ │ │ │ ├── AssetPreviewElement.cs.meta │ │ │ │ │ │ │ │ ├── GridListElement.cs │ │ │ │ │ │ │ │ ├── GridListElement.cs.meta │ │ │ │ │ │ │ │ ├── PreviewCollectionElement.cs │ │ │ │ │ │ │ │ ├── PreviewCollectionElement.cs.meta │ │ │ │ │ │ │ │ ├── PreviewGenerateButtonElement.cs │ │ │ │ │ │ │ │ ├── PreviewGenerateButtonElement.cs.meta │ │ │ │ │ │ │ │ ├── PreviewGeneratorPathsElement.cs │ │ │ │ │ │ │ │ ├── PreviewGeneratorPathsElement.cs.meta │ │ │ │ │ │ │ │ ├── PreviewGeneratorSettingsElement.cs │ │ │ │ │ │ │ │ ├── PreviewGeneratorSettingsElement.cs.meta │ │ │ │ │ │ │ │ ├── PreviewWindowDescriptionElement.cs │ │ │ │ │ │ │ │ └── PreviewWindowDescriptionElement.cs.meta │ │ │ │ │ │ │ ├── Elements.meta │ │ │ │ │ │ │ ├── PreviewGeneratorWindow.cs │ │ │ │ │ │ │ ├── PreviewGeneratorWindow.cs.meta │ │ │ │ │ │ │ ├── Views/ │ │ │ │ │ │ │ │ ├── PreviewListView.cs │ │ │ │ │ │ │ │ └── PreviewListView.cs.meta │ │ │ │ │ │ │ └── Views.meta │ │ │ │ │ │ ├── UI.meta │ │ │ │ │ │ ├── Utility/ │ │ │ │ │ │ │ ├── GraphicsUtility.cs │ │ │ │ │ │ │ ├── GraphicsUtility.cs.meta │ │ │ │ │ │ │ ├── PreviewConvertUtility.cs │ │ │ │ │ │ │ ├── PreviewConvertUtility.cs.meta │ │ │ │ │ │ │ ├── PreviewSceneUtility.cs │ │ │ │ │ │ │ ├── PreviewSceneUtility.cs.meta │ │ │ │ │ │ │ ├── RenderPipeline.cs │ │ │ │ │ │ │ ├── RenderPipeline.cs.meta │ │ │ │ │ │ │ ├── RenderPipelineUtility.cs │ │ │ │ │ │ │ └── RenderPipelineUtility.cs.meta │ │ │ │ │ │ └── Utility.meta │ │ │ │ │ ├── Scripts.meta │ │ │ │ │ ├── Styles/ │ │ │ │ │ │ ├── Style.uss │ │ │ │ │ │ ├── Style.uss.meta │ │ │ │ │ │ ├── ThemeDark.uss │ │ │ │ │ │ ├── ThemeDark.uss.meta │ │ │ │ │ │ ├── ThemeLight.uss │ │ │ │ │ │ └── ThemeLight.uss.meta │ │ │ │ │ └── Styles.meta │ │ │ │ ├── Previews.meta │ │ │ │ ├── Unity.AssetStoreTools.Editor.asmdef │ │ │ │ ├── Unity.AssetStoreTools.Editor.asmdef.meta │ │ │ │ ├── Uploader/ │ │ │ │ │ ├── Icons/ │ │ │ │ │ │ ├── account-dark.png.meta │ │ │ │ │ │ ├── account-light.png.meta │ │ │ │ │ │ ├── open-in-browser.png.meta │ │ │ │ │ │ ├── publisher-portal-dark.png.meta │ │ │ │ │ │ └── publisher-portal-light.png.meta │ │ │ │ │ ├── Icons.meta │ │ │ │ │ ├── Scripts/ │ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ │ ├── Abstractions/ │ │ │ │ │ │ │ │ ├── IPackage.cs │ │ │ │ │ │ │ │ ├── IPackage.cs.meta │ │ │ │ │ │ │ │ ├── IPackageContent.cs │ │ │ │ │ │ │ │ ├── IPackageContent.cs.meta │ │ │ │ │ │ │ │ ├── IPackageGroup.cs │ │ │ │ │ │ │ │ ├── IPackageGroup.cs.meta │ │ │ │ │ │ │ │ ├── IWorkflow.cs │ │ │ │ │ │ │ │ ├── IWorkflow.cs.meta │ │ │ │ │ │ │ │ ├── IWorkflowServices.cs │ │ │ │ │ │ │ │ ├── IWorkflowServices.cs.meta │ │ │ │ │ │ │ │ ├── WorkflowBase.cs │ │ │ │ │ │ │ │ └── WorkflowBase.cs.meta │ │ │ │ │ │ │ ├── Abstractions.meta │ │ │ │ │ │ │ ├── AssetsWorkflow.cs │ │ │ │ │ │ │ ├── AssetsWorkflow.cs.meta │ │ │ │ │ │ │ ├── HybridPackageWorkflow.cs │ │ │ │ │ │ │ ├── HybridPackageWorkflow.cs.meta │ │ │ │ │ │ │ ├── Package.cs │ │ │ │ │ │ │ ├── Package.cs.meta │ │ │ │ │ │ │ ├── PackageContent.cs │ │ │ │ │ │ │ ├── PackageContent.cs.meta │ │ │ │ │ │ │ ├── PackageGroup.cs │ │ │ │ │ │ │ ├── PackageGroup.cs.meta │ │ │ │ │ │ │ ├── PackageSorting.cs │ │ │ │ │ │ │ ├── PackageSorting.cs.meta │ │ │ │ │ │ │ ├── Serialization/ │ │ │ │ │ │ │ │ ├── AssetPath.cs │ │ │ │ │ │ │ │ ├── AssetPath.cs.meta │ │ │ │ │ │ │ │ ├── AssetsWorkflowStateData.cs │ │ │ │ │ │ │ │ ├── AssetsWorkflowStateData.cs.meta │ │ │ │ │ │ │ │ ├── HybridPackageWorkflowState.cs │ │ │ │ │ │ │ │ ├── HybridPackageWorkflowState.cs.meta │ │ │ │ │ │ │ │ ├── UnityPackageWorkflowStateData.cs │ │ │ │ │ │ │ │ ├── UnityPackageWorkflowStateData.cs.meta │ │ │ │ │ │ │ │ ├── WorkflowStateData.cs │ │ │ │ │ │ │ │ └── WorkflowStateData.cs.meta │ │ │ │ │ │ │ ├── Serialization.meta │ │ │ │ │ │ │ ├── UnityPackageWorkflow.cs │ │ │ │ │ │ │ ├── UnityPackageWorkflow.cs.meta │ │ │ │ │ │ │ ├── WorkflowServices.cs │ │ │ │ │ │ │ └── WorkflowServices.cs.meta │ │ │ │ │ │ ├── Data.meta │ │ │ │ │ │ ├── Services/ │ │ │ │ │ │ │ ├── Analytics/ │ │ │ │ │ │ │ │ ├── AnalyticsService.cs │ │ │ │ │ │ │ │ ├── AnalyticsService.cs.meta │ │ │ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ │ │ │ ├── AuthenticationAnalytic.cs │ │ │ │ │ │ │ │ │ ├── AuthenticationAnalytic.cs.meta │ │ │ │ │ │ │ │ │ ├── BaseAnalytic.cs │ │ │ │ │ │ │ │ │ ├── BaseAnalytic.cs.meta │ │ │ │ │ │ │ │ │ ├── IAssetStoreAnalytic.cs │ │ │ │ │ │ │ │ │ ├── IAssetStoreAnalytic.cs.meta │ │ │ │ │ │ │ │ │ ├── IAssetStoreAnalyticData.cs │ │ │ │ │ │ │ │ │ ├── IAssetStoreAnalyticData.cs.meta │ │ │ │ │ │ │ │ │ ├── PackageUploadAnalytic.cs │ │ │ │ │ │ │ │ │ ├── PackageUploadAnalytic.cs.meta │ │ │ │ │ │ │ │ │ ├── ValidationResultsSerializer.cs │ │ │ │ │ │ │ │ │ └── ValidationResultsSerializer.cs.meta │ │ │ │ │ │ │ │ ├── Data.meta │ │ │ │ │ │ │ │ ├── IAnalyticsService.cs │ │ │ │ │ │ │ │ └── IAnalyticsService.cs.meta │ │ │ │ │ │ │ ├── Analytics.meta │ │ │ │ │ │ │ ├── Api/ │ │ │ │ │ │ │ │ ├── AuthenticationService.cs │ │ │ │ │ │ │ │ ├── AuthenticationService.cs.meta │ │ │ │ │ │ │ │ ├── IAuthenticationService.cs │ │ │ │ │ │ │ │ ├── IAuthenticationService.cs.meta │ │ │ │ │ │ │ │ ├── IPackageDownloadingService.cs │ │ │ │ │ │ │ │ ├── IPackageDownloadingService.cs.meta │ │ │ │ │ │ │ │ ├── IPackageUploadingService.cs │ │ │ │ │ │ │ │ ├── IPackageUploadingService.cs.meta │ │ │ │ │ │ │ │ ├── PackageDownloadingService.cs │ │ │ │ │ │ │ │ ├── PackageDownloadingService.cs.meta │ │ │ │ │ │ │ │ ├── PackageUploadingService.cs │ │ │ │ │ │ │ │ └── PackageUploadingService.cs.meta │ │ │ │ │ │ │ ├── Api.meta │ │ │ │ │ │ │ ├── Caching/ │ │ │ │ │ │ │ │ ├── CachingService.cs │ │ │ │ │ │ │ │ ├── CachingService.cs.meta │ │ │ │ │ │ │ │ ├── ICachingService.cs │ │ │ │ │ │ │ │ └── ICachingService.cs.meta │ │ │ │ │ │ │ ├── Caching.meta │ │ │ │ │ │ │ ├── IUploaderService.cs │ │ │ │ │ │ │ ├── IUploaderService.cs.meta │ │ │ │ │ │ │ ├── PackageFactory/ │ │ │ │ │ │ │ │ ├── IPackageFactoryService.cs │ │ │ │ │ │ │ │ ├── IPackageFactoryService.cs.meta │ │ │ │ │ │ │ │ ├── PackageFactoryService.cs │ │ │ │ │ │ │ │ └── PackageFactoryService.cs.meta │ │ │ │ │ │ │ ├── PackageFactory.meta │ │ │ │ │ │ │ ├── UploaderServiceProvider.cs │ │ │ │ │ │ │ └── UploaderServiceProvider.cs.meta │ │ │ │ │ │ ├── Services.meta │ │ │ │ │ │ ├── UI/ │ │ │ │ │ │ │ ├── Elements/ │ │ │ │ │ │ │ │ ├── Abstractions/ │ │ │ │ │ │ │ │ │ ├── ValidationElementBase.cs │ │ │ │ │ │ │ │ │ ├── ValidationElementBase.cs.meta │ │ │ │ │ │ │ │ │ ├── WorkflowElementBase.cs │ │ │ │ │ │ │ │ │ └── WorkflowElementBase.cs.meta │ │ │ │ │ │ │ │ ├── Abstractions.meta │ │ │ │ │ │ │ │ ├── AccountToolbar.cs │ │ │ │ │ │ │ │ ├── AccountToolbar.cs.meta │ │ │ │ │ │ │ │ ├── AssetsWorkflowElement.cs │ │ │ │ │ │ │ │ ├── AssetsWorkflowElement.cs.meta │ │ │ │ │ │ │ │ ├── CurrentProjectValidationElement.cs │ │ │ │ │ │ │ │ ├── CurrentProjectValidationElement.cs.meta │ │ │ │ │ │ │ │ ├── ExternalProjectValidationElement.cs │ │ │ │ │ │ │ │ ├── ExternalProjectValidationElement.cs.meta │ │ │ │ │ │ │ │ ├── HybridPackageWorkflowElement.cs │ │ │ │ │ │ │ │ ├── HybridPackageWorkflowElement.cs.meta │ │ │ │ │ │ │ │ ├── LoadingSpinner.cs │ │ │ │ │ │ │ │ ├── LoadingSpinner.cs.meta │ │ │ │ │ │ │ │ ├── MultiToggleSelectionElement.cs │ │ │ │ │ │ │ │ ├── MultiToggleSelectionElement.cs.meta │ │ │ │ │ │ │ │ ├── PackageContentElement.cs │ │ │ │ │ │ │ │ ├── PackageContentElement.cs.meta │ │ │ │ │ │ │ │ ├── PackageElement.cs │ │ │ │ │ │ │ │ ├── PackageElement.cs.meta │ │ │ │ │ │ │ │ ├── PackageGroupElement.cs │ │ │ │ │ │ │ │ ├── PackageGroupElement.cs.meta │ │ │ │ │ │ │ │ ├── PackageListToolbar.cs │ │ │ │ │ │ │ │ ├── PackageListToolbar.cs.meta │ │ │ │ │ │ │ │ ├── PackageUploadElement.cs │ │ │ │ │ │ │ │ ├── PackageUploadElement.cs.meta │ │ │ │ │ │ │ │ ├── PathSelectionElement.cs │ │ │ │ │ │ │ │ ├── PathSelectionElement.cs.meta │ │ │ │ │ │ │ │ ├── PreviewGenerationElement.cs │ │ │ │ │ │ │ │ ├── PreviewGenerationElement.cs.meta │ │ │ │ │ │ │ │ ├── UnityPackageWorkflowElement.cs │ │ │ │ │ │ │ │ └── UnityPackageWorkflowElement.cs.meta │ │ │ │ │ │ │ ├── Elements.meta │ │ │ │ │ │ │ ├── Views/ │ │ │ │ │ │ │ │ ├── LoginView.cs │ │ │ │ │ │ │ │ ├── LoginView.cs.meta │ │ │ │ │ │ │ │ ├── PackageListView.cs │ │ │ │ │ │ │ │ └── PackageListView.cs.meta │ │ │ │ │ │ │ └── Views.meta │ │ │ │ │ │ └── UI.meta │ │ │ │ │ ├── Scripts.meta │ │ │ │ │ ├── Styles/ │ │ │ │ │ │ ├── LoginView/ │ │ │ │ │ │ │ ├── Style.uss │ │ │ │ │ │ │ ├── Style.uss.meta │ │ │ │ │ │ │ ├── ThemeDark.uss │ │ │ │ │ │ │ ├── ThemeDark.uss.meta │ │ │ │ │ │ │ ├── ThemeLight.uss │ │ │ │ │ │ │ └── ThemeLight.uss.meta │ │ │ │ │ │ ├── LoginView.meta │ │ │ │ │ │ ├── PackageListView/ │ │ │ │ │ │ │ ├── Style.uss │ │ │ │ │ │ │ ├── Style.uss.meta │ │ │ │ │ │ │ ├── ThemeDark.uss │ │ │ │ │ │ │ ├── ThemeDark.uss.meta │ │ │ │ │ │ │ ├── ThemeLight.uss │ │ │ │ │ │ │ └── ThemeLight.uss.meta │ │ │ │ │ │ ├── PackageListView.meta │ │ │ │ │ │ ├── Style.uss │ │ │ │ │ │ ├── Style.uss.meta │ │ │ │ │ │ ├── ThemeDark.uss │ │ │ │ │ │ ├── ThemeDark.uss.meta │ │ │ │ │ │ ├── ThemeLight.uss │ │ │ │ │ │ └── ThemeLight.uss.meta │ │ │ │ │ ├── Styles.meta │ │ │ │ │ ├── UploaderWindow.cs │ │ │ │ │ └── UploaderWindow.cs.meta │ │ │ │ ├── Uploader.meta │ │ │ │ ├── Utility/ │ │ │ │ │ ├── ASDebug.cs │ │ │ │ │ ├── ASDebug.cs.meta │ │ │ │ │ ├── ASToolsPreferences.cs │ │ │ │ │ ├── ASToolsPreferences.cs.meta │ │ │ │ │ ├── ASToolsUpdater.cs │ │ │ │ │ ├── ASToolsUpdater.cs.meta │ │ │ │ │ ├── CacheUtil.cs │ │ │ │ │ ├── CacheUtil.cs.meta │ │ │ │ │ ├── FileUtility.cs │ │ │ │ │ ├── FileUtility.cs.meta │ │ │ │ │ ├── LegacyToolsRemover.cs │ │ │ │ │ ├── LegacyToolsRemover.cs.meta │ │ │ │ │ ├── PackageUtility.cs │ │ │ │ │ ├── PackageUtility.cs.meta │ │ │ │ │ ├── ServiceProvider.cs │ │ │ │ │ ├── ServiceProvider.cs.meta │ │ │ │ │ ├── StyleSelector.cs │ │ │ │ │ ├── StyleSelector.cs.meta │ │ │ │ │ ├── Styles/ │ │ │ │ │ │ ├── Updater/ │ │ │ │ │ │ │ ├── Style.uss │ │ │ │ │ │ │ ├── Style.uss.meta │ │ │ │ │ │ │ ├── ThemeDark.uss │ │ │ │ │ │ │ ├── ThemeDark.uss.meta │ │ │ │ │ │ │ ├── ThemeLight.uss │ │ │ │ │ │ │ └── ThemeLight.uss.meta │ │ │ │ │ │ └── Updater.meta │ │ │ │ │ ├── Styles.meta │ │ │ │ │ ├── SymlinkUtil.cs │ │ │ │ │ └── SymlinkUtil.cs.meta │ │ │ │ ├── Utility.meta │ │ │ │ ├── Validator/ │ │ │ │ │ ├── Icons/ │ │ │ │ │ │ ├── error.png.meta │ │ │ │ │ │ ├── error_d.png.meta │ │ │ │ │ │ ├── success.png.meta │ │ │ │ │ │ ├── success_d.png.meta │ │ │ │ │ │ ├── undefined.png.meta │ │ │ │ │ │ ├── undefined_d.png.meta │ │ │ │ │ │ ├── warning.png.meta │ │ │ │ │ │ └── warning_d.png.meta │ │ │ │ │ ├── Icons.meta │ │ │ │ │ ├── Scripts/ │ │ │ │ │ │ ├── Categories/ │ │ │ │ │ │ │ ├── CategoryEvaluator.cs │ │ │ │ │ │ │ ├── CategoryEvaluator.cs.meta │ │ │ │ │ │ │ ├── ValidatorCategory.cs │ │ │ │ │ │ │ └── ValidatorCategory.cs.meta │ │ │ │ │ │ ├── Categories.meta │ │ │ │ │ │ ├── CurrentProjectValidator.cs │ │ │ │ │ │ ├── CurrentProjectValidator.cs.meta │ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ │ ├── CurrentProjectValidationSettings.cs │ │ │ │ │ │ │ ├── CurrentProjectValidationSettings.cs.meta │ │ │ │ │ │ │ ├── ExternalProjectValidationSettings.cs │ │ │ │ │ │ │ ├── ExternalProjectValidationSettings.cs.meta │ │ │ │ │ │ │ ├── MessageActions/ │ │ │ │ │ │ │ │ ├── HighlightObjectAction.cs │ │ │ │ │ │ │ │ ├── HighlightObjectAction.cs.meta │ │ │ │ │ │ │ │ ├── IMessageAction.cs │ │ │ │ │ │ │ │ ├── IMessageAction.cs.meta │ │ │ │ │ │ │ │ ├── OpenAssetAction.cs │ │ │ │ │ │ │ │ └── OpenAssetAction.cs.meta │ │ │ │ │ │ │ ├── MessageActions.meta │ │ │ │ │ │ │ ├── TestResult.cs │ │ │ │ │ │ │ ├── TestResult.cs.meta │ │ │ │ │ │ │ ├── TestResultMessage.cs │ │ │ │ │ │ │ ├── TestResultMessage.cs.meta │ │ │ │ │ │ │ ├── TestResultObject.cs │ │ │ │ │ │ │ ├── TestResultObject.cs.meta │ │ │ │ │ │ │ ├── TestResultStatus.cs │ │ │ │ │ │ │ ├── TestResultStatus.cs.meta │ │ │ │ │ │ │ ├── ValidationResult.cs │ │ │ │ │ │ │ ├── ValidationResult.cs.meta │ │ │ │ │ │ │ ├── ValidationSettings.cs │ │ │ │ │ │ │ ├── ValidationSettings.cs.meta │ │ │ │ │ │ │ ├── ValidationStatus.cs │ │ │ │ │ │ │ ├── ValidationStatus.cs.meta │ │ │ │ │ │ │ ├── ValidationType.cs │ │ │ │ │ │ │ └── ValidationType.cs.meta │ │ │ │ │ │ ├── Data.meta │ │ │ │ │ │ ├── ExternalProjectValidator.cs │ │ │ │ │ │ ├── ExternalProjectValidator.cs.meta │ │ │ │ │ │ ├── IValidator.cs │ │ │ │ │ │ ├── IValidator.cs.meta │ │ │ │ │ │ ├── Services/ │ │ │ │ │ │ │ ├── CachingService/ │ │ │ │ │ │ │ │ ├── CachingService.cs │ │ │ │ │ │ │ │ ├── CachingService.cs.meta │ │ │ │ │ │ │ │ ├── ICachingService.cs │ │ │ │ │ │ │ │ ├── ICachingService.cs.meta │ │ │ │ │ │ │ │ ├── PreviewDatabaseContractResolver.cs │ │ │ │ │ │ │ │ └── PreviewDatabaseContractResolver.cs.meta │ │ │ │ │ │ │ ├── CachingService.meta │ │ │ │ │ │ │ ├── IValidatorService.cs │ │ │ │ │ │ │ ├── IValidatorService.cs.meta │ │ │ │ │ │ │ ├── Validation/ │ │ │ │ │ │ │ │ ├── Abstractions/ │ │ │ │ │ │ │ │ │ ├── IAssetUtilityService.cs │ │ │ │ │ │ │ │ │ ├── IAssetUtilityService.cs.meta │ │ │ │ │ │ │ │ │ ├── IFileSignatureUtilityService.cs │ │ │ │ │ │ │ │ │ ├── IFileSignatureUtilityService.cs.meta │ │ │ │ │ │ │ │ │ ├── IMeshUtilityService.cs │ │ │ │ │ │ │ │ │ ├── IMeshUtilityService.cs.meta │ │ │ │ │ │ │ │ │ ├── IModelUtilityService.cs │ │ │ │ │ │ │ │ │ ├── IModelUtilityService.cs.meta │ │ │ │ │ │ │ │ │ ├── ISceneUtilityService.cs │ │ │ │ │ │ │ │ │ ├── ISceneUtilityService.cs.meta │ │ │ │ │ │ │ │ │ ├── IScriptUtilityService.cs │ │ │ │ │ │ │ │ │ └── IScriptUtilityService.cs.meta │ │ │ │ │ │ │ │ ├── Abstractions.meta │ │ │ │ │ │ │ │ ├── AssetUtilityService.cs │ │ │ │ │ │ │ │ ├── AssetUtilityService.cs.meta │ │ │ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ │ │ │ ├── ArchiveType.cs │ │ │ │ │ │ │ │ │ ├── ArchiveType.cs.meta │ │ │ │ │ │ │ │ │ ├── AssetEnumerator.cs │ │ │ │ │ │ │ │ │ ├── AssetEnumerator.cs.meta │ │ │ │ │ │ │ │ │ ├── AssetType.cs │ │ │ │ │ │ │ │ │ ├── AssetType.cs.meta │ │ │ │ │ │ │ │ │ ├── LogEntry.cs │ │ │ │ │ │ │ │ │ └── LogEntry.cs.meta │ │ │ │ │ │ │ │ ├── Data.meta │ │ │ │ │ │ │ │ ├── FileSignatureUtilityService.cs │ │ │ │ │ │ │ │ ├── FileSignatureUtilityService.cs.meta │ │ │ │ │ │ │ │ ├── MeshUtilityService.cs │ │ │ │ │ │ │ │ ├── MeshUtilityService.cs.meta │ │ │ │ │ │ │ │ ├── ModelUtilityService.cs │ │ │ │ │ │ │ │ ├── ModelUtilityService.cs.meta │ │ │ │ │ │ │ │ ├── SceneUtilityService.cs │ │ │ │ │ │ │ │ ├── SceneUtilityService.cs.meta │ │ │ │ │ │ │ │ ├── ScriptUtilityService.cs │ │ │ │ │ │ │ │ └── ScriptUtilityService.cs.meta │ │ │ │ │ │ │ ├── Validation.meta │ │ │ │ │ │ │ ├── ValidatorServiceProvider.cs │ │ │ │ │ │ │ └── ValidatorServiceProvider.cs.meta │ │ │ │ │ │ ├── Services.meta │ │ │ │ │ │ ├── Test Definitions/ │ │ │ │ │ │ │ ├── AutomatedTest.cs │ │ │ │ │ │ │ ├── AutomatedTest.cs.meta │ │ │ │ │ │ │ ├── GenericTestConfig.cs │ │ │ │ │ │ │ ├── GenericTestConfig.cs.meta │ │ │ │ │ │ │ ├── ITestConfig.cs │ │ │ │ │ │ │ ├── ITestConfig.cs.meta │ │ │ │ │ │ │ ├── ITestScript.cs │ │ │ │ │ │ │ ├── ITestScript.cs.meta │ │ │ │ │ │ │ ├── Scriptable Objects/ │ │ │ │ │ │ │ │ ├── AutomatedTestScriptableObject.cs │ │ │ │ │ │ │ │ ├── AutomatedTestScriptableObject.cs.meta │ │ │ │ │ │ │ │ ├── Editor/ │ │ │ │ │ │ │ │ │ ├── ValidationTestScriptableObjectInspector.cs │ │ │ │ │ │ │ │ │ └── ValidationTestScriptableObjectInspector.cs.meta │ │ │ │ │ │ │ │ ├── Editor.meta │ │ │ │ │ │ │ │ ├── ValidationTestScriptableObject.cs │ │ │ │ │ │ │ │ └── ValidationTestScriptableObject.cs.meta │ │ │ │ │ │ │ ├── Scriptable Objects.meta │ │ │ │ │ │ │ ├── ValidationTest.cs │ │ │ │ │ │ │ └── ValidationTest.cs.meta │ │ │ │ │ │ ├── Test Definitions.meta │ │ │ │ │ │ ├── Test Methods/ │ │ │ │ │ │ │ ├── Generic/ │ │ │ │ │ │ │ │ ├── CheckAnimationClips.cs │ │ │ │ │ │ │ │ ├── CheckAnimationClips.cs.meta │ │ │ │ │ │ │ │ ├── CheckAudioClipping.cs │ │ │ │ │ │ │ │ ├── CheckAudioClipping.cs.meta │ │ │ │ │ │ │ │ ├── CheckColliders.cs │ │ │ │ │ │ │ │ ├── CheckColliders.cs.meta │ │ │ │ │ │ │ │ ├── CheckCompressedFiles.cs │ │ │ │ │ │ │ │ ├── CheckCompressedFiles.cs.meta │ │ │ │ │ │ │ │ ├── CheckEmptyPrefabs.cs │ │ │ │ │ │ │ │ ├── CheckEmptyPrefabs.cs.meta │ │ │ │ │ │ │ │ ├── CheckFileMenuNames.cs │ │ │ │ │ │ │ │ ├── CheckFileMenuNames.cs.meta │ │ │ │ │ │ │ │ ├── CheckLODs.cs │ │ │ │ │ │ │ │ ├── CheckLODs.cs.meta │ │ │ │ │ │ │ │ ├── CheckLineEndings.cs │ │ │ │ │ │ │ │ ├── CheckLineEndings.cs.meta │ │ │ │ │ │ │ │ ├── CheckMeshPrefabs.cs │ │ │ │ │ │ │ │ ├── CheckMeshPrefabs.cs.meta │ │ │ │ │ │ │ │ ├── CheckMissingComponentsinAssets.cs │ │ │ │ │ │ │ │ ├── CheckMissingComponentsinAssets.cs.meta │ │ │ │ │ │ │ │ ├── CheckMissingComponentsinScenes.cs │ │ │ │ │ │ │ │ ├── CheckMissingComponentsinScenes.cs.meta │ │ │ │ │ │ │ │ ├── CheckModelImportLogs.cs │ │ │ │ │ │ │ │ ├── CheckModelImportLogs.cs.meta │ │ │ │ │ │ │ │ ├── CheckModelOrientation.cs │ │ │ │ │ │ │ │ ├── CheckModelOrientation.cs.meta │ │ │ │ │ │ │ │ ├── CheckModelTypes.cs │ │ │ │ │ │ │ │ ├── CheckModelTypes.cs.meta │ │ │ │ │ │ │ │ ├── CheckNormalMapTextures.cs │ │ │ │ │ │ │ │ ├── CheckNormalMapTextures.cs.meta │ │ │ │ │ │ │ │ ├── CheckPackageNaming.cs │ │ │ │ │ │ │ │ ├── CheckPackageNaming.cs.meta │ │ │ │ │ │ │ │ ├── CheckParticleSystems.cs │ │ │ │ │ │ │ │ ├── CheckParticleSystems.cs.meta │ │ │ │ │ │ │ │ ├── CheckPathLengths.cs │ │ │ │ │ │ │ │ ├── CheckPathLengths.cs.meta │ │ │ │ │ │ │ │ ├── CheckPrefabTransforms.cs │ │ │ │ │ │ │ │ ├── CheckPrefabTransforms.cs.meta │ │ │ │ │ │ │ │ ├── CheckScriptCompilation.cs │ │ │ │ │ │ │ │ ├── CheckScriptCompilation.cs.meta │ │ │ │ │ │ │ │ ├── CheckShaderCompilation.cs │ │ │ │ │ │ │ │ ├── CheckShaderCompilation.cs.meta │ │ │ │ │ │ │ │ ├── CheckTextureDimensions.cs │ │ │ │ │ │ │ │ ├── CheckTextureDimensions.cs.meta │ │ │ │ │ │ │ │ ├── CheckTypeNamespaces.cs │ │ │ │ │ │ │ │ ├── CheckTypeNamespaces.cs.meta │ │ │ │ │ │ │ │ ├── RemoveExecutableFiles.cs │ │ │ │ │ │ │ │ ├── RemoveExecutableFiles.cs.meta │ │ │ │ │ │ │ │ ├── RemoveJPGFiles.cs │ │ │ │ │ │ │ │ ├── RemoveJPGFiles.cs.meta │ │ │ │ │ │ │ │ ├── RemoveJavaScriptFiles.cs │ │ │ │ │ │ │ │ ├── RemoveJavaScriptFiles.cs.meta │ │ │ │ │ │ │ │ ├── RemoveLossyAudioFiles.cs │ │ │ │ │ │ │ │ ├── RemoveLossyAudioFiles.cs.meta │ │ │ │ │ │ │ │ ├── RemoveMixamoFiles.cs │ │ │ │ │ │ │ │ ├── RemoveMixamoFiles.cs.meta │ │ │ │ │ │ │ │ ├── RemoveSpeedTreeFiles.cs │ │ │ │ │ │ │ │ ├── RemoveSpeedTreeFiles.cs.meta │ │ │ │ │ │ │ │ ├── RemoveVideoFiles.cs │ │ │ │ │ │ │ │ └── RemoveVideoFiles.cs.meta │ │ │ │ │ │ │ ├── Generic.meta │ │ │ │ │ │ │ ├── UnityPackage/ │ │ │ │ │ │ │ │ ├── CheckDemoScenes.cs │ │ │ │ │ │ │ │ ├── CheckDemoScenes.cs.meta │ │ │ │ │ │ │ │ ├── CheckDocumentation.cs │ │ │ │ │ │ │ │ ├── CheckDocumentation.cs.meta │ │ │ │ │ │ │ │ ├── CheckPackageSize.cs │ │ │ │ │ │ │ │ ├── CheckPackageSize.cs.meta │ │ │ │ │ │ │ │ ├── CheckProjectTemplateAssets.cs │ │ │ │ │ │ │ │ └── CheckProjectTemplateAssets.cs.meta │ │ │ │ │ │ │ └── UnityPackage.meta │ │ │ │ │ │ ├── Test Methods.meta │ │ │ │ │ │ ├── UI/ │ │ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ │ │ ├── Abstractions/ │ │ │ │ │ │ │ │ │ ├── IValidatorResults.cs │ │ │ │ │ │ │ │ │ ├── IValidatorResults.cs.meta │ │ │ │ │ │ │ │ │ ├── IValidatorSettings.cs │ │ │ │ │ │ │ │ │ ├── IValidatorSettings.cs.meta │ │ │ │ │ │ │ │ │ ├── IValidatorTest.cs │ │ │ │ │ │ │ │ │ ├── IValidatorTest.cs.meta │ │ │ │ │ │ │ │ │ ├── IValidatorTestGroup.cs │ │ │ │ │ │ │ │ │ └── IValidatorTestGroup.cs.meta │ │ │ │ │ │ │ │ ├── Abstractions.meta │ │ │ │ │ │ │ │ ├── Serialization/ │ │ │ │ │ │ │ │ │ ├── ValidatorStateData.cs │ │ │ │ │ │ │ │ │ ├── ValidatorStateData.cs.meta │ │ │ │ │ │ │ │ │ ├── ValidatorStateDataContractResolver.cs │ │ │ │ │ │ │ │ │ ├── ValidatorStateDataContractResolver.cs.meta │ │ │ │ │ │ │ │ │ ├── ValidatorStateResults.cs │ │ │ │ │ │ │ │ │ ├── ValidatorStateResults.cs.meta │ │ │ │ │ │ │ │ │ ├── ValidatorStateSettings.cs │ │ │ │ │ │ │ │ │ └── ValidatorStateSettings.cs.meta │ │ │ │ │ │ │ │ ├── Serialization.meta │ │ │ │ │ │ │ │ ├── ValidatorResults.cs │ │ │ │ │ │ │ │ ├── ValidatorResults.cs.meta │ │ │ │ │ │ │ │ ├── ValidatorSettings.cs │ │ │ │ │ │ │ │ ├── ValidatorSettings.cs.meta │ │ │ │ │ │ │ │ ├── ValidatorTest.cs │ │ │ │ │ │ │ │ ├── ValidatorTest.cs.meta │ │ │ │ │ │ │ │ ├── ValidatorTestGroup.cs │ │ │ │ │ │ │ │ └── ValidatorTestGroup.cs.meta │ │ │ │ │ │ │ ├── Data.meta │ │ │ │ │ │ │ ├── Elements/ │ │ │ │ │ │ │ │ ├── ValidatorButtonElement.cs │ │ │ │ │ │ │ │ ├── ValidatorButtonElement.cs.meta │ │ │ │ │ │ │ │ ├── ValidatorDescriptionElement.cs │ │ │ │ │ │ │ │ ├── ValidatorDescriptionElement.cs.meta │ │ │ │ │ │ │ │ ├── ValidatorPathsElement.cs │ │ │ │ │ │ │ │ ├── ValidatorPathsElement.cs.meta │ │ │ │ │ │ │ │ ├── ValidatorResultsElement.cs │ │ │ │ │ │ │ │ ├── ValidatorResultsElement.cs.meta │ │ │ │ │ │ │ │ ├── ValidatorSettingsElement.cs │ │ │ │ │ │ │ │ ├── ValidatorSettingsElement.cs.meta │ │ │ │ │ │ │ │ ├── ValidatorTestElement.cs │ │ │ │ │ │ │ │ ├── ValidatorTestElement.cs.meta │ │ │ │ │ │ │ │ ├── ValidatorTestGroupElement.cs │ │ │ │ │ │ │ │ └── ValidatorTestGroupElement.cs.meta │ │ │ │ │ │ │ ├── Elements.meta │ │ │ │ │ │ │ ├── ValidatorWindow.cs │ │ │ │ │ │ │ ├── ValidatorWindow.cs.meta │ │ │ │ │ │ │ ├── Views/ │ │ │ │ │ │ │ │ ├── ValidatorTestsView.cs │ │ │ │ │ │ │ │ └── ValidatorTestsView.cs.meta │ │ │ │ │ │ │ └── Views.meta │ │ │ │ │ │ ├── UI.meta │ │ │ │ │ │ ├── Utility/ │ │ │ │ │ │ │ ├── ValidatorUtility.cs │ │ │ │ │ │ │ └── ValidatorUtility.cs.meta │ │ │ │ │ │ ├── Utility.meta │ │ │ │ │ │ ├── ValidatorBase.cs │ │ │ │ │ │ └── ValidatorBase.cs.meta │ │ │ │ │ ├── Scripts.meta │ │ │ │ │ ├── Styles/ │ │ │ │ │ │ ├── Style.uss │ │ │ │ │ │ ├── Style.uss.meta │ │ │ │ │ │ ├── ThemeDark.uss │ │ │ │ │ │ ├── ThemeDark.uss.meta │ │ │ │ │ │ ├── ThemeLight.uss │ │ │ │ │ │ └── ThemeLight.uss.meta │ │ │ │ │ ├── Styles.meta │ │ │ │ │ ├── Tests/ │ │ │ │ │ │ ├── Generic/ │ │ │ │ │ │ │ ├── Check Animation Clips.asset │ │ │ │ │ │ │ ├── Check Animation Clips.asset.meta │ │ │ │ │ │ │ ├── Check Audio Clipping.asset │ │ │ │ │ │ │ ├── Check Audio Clipping.asset.meta │ │ │ │ │ │ │ ├── Check Colliders.asset │ │ │ │ │ │ │ ├── Check Colliders.asset.meta │ │ │ │ │ │ │ ├── Check Compressed Files.asset │ │ │ │ │ │ │ ├── Check Compressed Files.asset.meta │ │ │ │ │ │ │ ├── Check Empty Prefabs.asset │ │ │ │ │ │ │ ├── Check Empty Prefabs.asset.meta │ │ │ │ │ │ │ ├── Check File Menu Names.asset │ │ │ │ │ │ │ ├── Check File Menu Names.asset.meta │ │ │ │ │ │ │ ├── Check LODs.asset │ │ │ │ │ │ │ ├── Check LODs.asset.meta │ │ │ │ │ │ │ ├── Check Line Endings.asset │ │ │ │ │ │ │ ├── Check Line Endings.asset.meta │ │ │ │ │ │ │ ├── Check Mesh Prefabs.asset │ │ │ │ │ │ │ ├── Check Mesh Prefabs.asset.meta │ │ │ │ │ │ │ ├── Check Missing Components in Assets.asset │ │ │ │ │ │ │ ├── Check Missing Components in Assets.asset.meta │ │ │ │ │ │ │ ├── Check Missing Components in Scenes.asset │ │ │ │ │ │ │ ├── Check Missing Components in Scenes.asset.meta │ │ │ │ │ │ │ ├── Check Model Import Logs.asset │ │ │ │ │ │ │ ├── Check Model Import Logs.asset.meta │ │ │ │ │ │ │ ├── Check Model Orientation.asset │ │ │ │ │ │ │ ├── Check Model Orientation.asset.meta │ │ │ │ │ │ │ ├── Check Model Types.asset │ │ │ │ │ │ │ ├── Check Model Types.asset.meta │ │ │ │ │ │ │ ├── Check Normal Map Textures.asset │ │ │ │ │ │ │ ├── Check Normal Map Textures.asset.meta │ │ │ │ │ │ │ ├── Check Package Naming.asset │ │ │ │ │ │ │ ├── Check Package Naming.asset.meta │ │ │ │ │ │ │ ├── Check Particle Systems.asset │ │ │ │ │ │ │ ├── Check Particle Systems.asset.meta │ │ │ │ │ │ │ ├── Check Path Lengths.asset │ │ │ │ │ │ │ ├── Check Path Lengths.asset.meta │ │ │ │ │ │ │ ├── Check Prefab Transforms.asset │ │ │ │ │ │ │ ├── Check Prefab Transforms.asset.meta │ │ │ │ │ │ │ ├── Check Script Compilation.asset │ │ │ │ │ │ │ ├── Check Script Compilation.asset.meta │ │ │ │ │ │ │ ├── Check Shader Compilation.asset │ │ │ │ │ │ │ ├── Check Shader Compilation.asset.meta │ │ │ │ │ │ │ ├── Check Texture Dimensions.asset │ │ │ │ │ │ │ ├── Check Texture Dimensions.asset.meta │ │ │ │ │ │ │ ├── Check Type Namespaces.asset │ │ │ │ │ │ │ ├── Check Type Namespaces.asset.meta │ │ │ │ │ │ │ ├── Remove Executable Files.asset │ │ │ │ │ │ │ ├── Remove Executable Files.asset.meta │ │ │ │ │ │ │ ├── Remove JPG Files.asset │ │ │ │ │ │ │ ├── Remove JPG Files.asset.meta │ │ │ │ │ │ │ ├── Remove JavaScript Files.asset │ │ │ │ │ │ │ ├── Remove JavaScript Files.asset.meta │ │ │ │ │ │ │ ├── Remove Lossy Audio Files.asset │ │ │ │ │ │ │ ├── Remove Lossy Audio Files.asset.meta │ │ │ │ │ │ │ ├── Remove Mixamo Files.asset │ │ │ │ │ │ │ ├── Remove Mixamo Files.asset.meta │ │ │ │ │ │ │ ├── Remove SpeedTree Files.asset │ │ │ │ │ │ │ ├── Remove SpeedTree Files.asset.meta │ │ │ │ │ │ │ ├── Remove Video Files.asset │ │ │ │ │ │ │ └── Remove Video Files.asset.meta │ │ │ │ │ │ ├── Generic.meta │ │ │ │ │ │ ├── UnityPackage/ │ │ │ │ │ │ │ ├── Check Demo Scenes.asset │ │ │ │ │ │ │ ├── Check Demo Scenes.asset.meta │ │ │ │ │ │ │ ├── Check Documentation.asset │ │ │ │ │ │ │ ├── Check Documentation.asset.meta │ │ │ │ │ │ │ ├── Check Package Size.asset │ │ │ │ │ │ │ ├── Check Package Size.asset.meta │ │ │ │ │ │ │ ├── Check Project Template Assets.asset │ │ │ │ │ │ │ └── Check Project Template Assets.asset.meta │ │ │ │ │ │ └── UnityPackage.meta │ │ │ │ │ └── Tests.meta │ │ │ │ └── Validator.meta │ │ │ ├── Editor.meta │ │ │ ├── LICENSE.md │ │ │ ├── LICENSE.md.meta │ │ │ ├── package.json │ │ │ └── package.json.meta │ │ ├── manifest.json │ │ └── packages-lock.json │ ├── ProjectSettings/ │ │ ├── AudioManager.asset │ │ ├── ClusterInputManager.asset │ │ ├── DynamicsManager.asset │ │ ├── EditorBuildSettings.asset │ │ ├── EditorSettings.asset │ │ ├── GraphicsSettings.asset │ │ ├── InputManager.asset │ │ ├── NavMeshAreas.asset │ │ ├── PackageManagerSettings.asset │ │ ├── Physics2DSettings.asset │ │ ├── PresetManager.asset │ │ ├── ProjectSettings.asset │ │ ├── ProjectVersion.txt │ │ ├── QualitySettings.asset │ │ ├── TagManager.asset │ │ ├── TimeManager.asset │ │ ├── UnityConnectSettings.asset │ │ ├── VFXManager.asset │ │ └── XRSettings.asset │ ├── ShadergraphMarkdown 2019.4.sln │ ├── Unity.CollabProxy.Editor.csproj │ ├── Unity.PlasticSCM.Editor.csproj │ ├── Unity.Rider.Editor.csproj │ ├── Unity.TextMeshPro.Editor.csproj │ ├── Unity.TextMeshPro.csproj │ ├── Unity.Timeline.Editor.csproj │ ├── Unity.Timeline.csproj │ ├── Unity.VSCode.Editor.csproj │ ├── Unity.VisualStudio.Editor.csproj │ ├── UnityEditor.TestRunner.csproj │ ├── UnityEditor.UI.csproj │ ├── UnityEngine.TestRunner.csproj │ ├── UnityEngine.UI.csproj │ └── asset-store-tools-editor.csproj └── ShadergraphMarkdown 6000.2/ ├── .vscode/ │ └── settings.json ├── Assembly-CSharp-Editor.csproj ├── Assembly-CSharp.csproj ├── DocCodeExamples.csproj ├── Needle.ShaderGraphMarkdown.Internal.csproj ├── Needle.ShaderGraphMarkdown.csproj ├── Packages/ │ ├── com.unity.asset-store-tools/ │ │ ├── CHANGELOG.md │ │ ├── CHANGELOG.md.meta │ │ ├── Editor/ │ │ │ ├── Api/ │ │ │ │ ├── Abstractions/ │ │ │ │ │ ├── AuthenticationBase.cs │ │ │ │ │ ├── AuthenticationBase.cs.meta │ │ │ │ │ ├── IAssetStoreApi.cs │ │ │ │ │ ├── IAssetStoreApi.cs.meta │ │ │ │ │ ├── IAssetStoreClient.cs │ │ │ │ │ ├── IAssetStoreClient.cs.meta │ │ │ │ │ ├── IAuthenticationType.cs │ │ │ │ │ ├── IAuthenticationType.cs.meta │ │ │ │ │ ├── IPackageUploader.cs │ │ │ │ │ ├── IPackageUploader.cs.meta │ │ │ │ │ ├── PackageUploaderBase.cs │ │ │ │ │ └── PackageUploaderBase.cs.meta │ │ │ │ ├── Abstractions.meta │ │ │ │ ├── ApiUtility.cs │ │ │ │ ├── ApiUtility.cs.meta │ │ │ │ ├── AssetStoreApi.cs │ │ │ │ ├── AssetStoreApi.cs.meta │ │ │ │ ├── AssetStoreClient.cs │ │ │ │ ├── AssetStoreClient.cs.meta │ │ │ │ ├── CloudTokenAuthentication.cs │ │ │ │ ├── CloudTokenAuthentication.cs.meta │ │ │ │ ├── CredentialsAuthentication.cs │ │ │ │ ├── CredentialsAuthentication.cs.meta │ │ │ │ ├── Models/ │ │ │ │ │ ├── Category.cs │ │ │ │ │ ├── Category.cs.meta │ │ │ │ │ ├── Package.cs │ │ │ │ │ ├── Package.cs.meta │ │ │ │ │ ├── PackageAdditionalData.cs │ │ │ │ │ ├── PackageAdditionalData.cs.meta │ │ │ │ │ ├── User.cs │ │ │ │ │ └── User.cs.meta │ │ │ │ ├── Models.meta │ │ │ │ ├── Responses/ │ │ │ │ │ ├── AssetStoreResponse.cs │ │ │ │ │ ├── AssetStoreResponse.cs.meta │ │ │ │ │ ├── AssetStoreToolsVersionResponse.cs │ │ │ │ │ ├── AssetStoreToolsVersionResponse.cs.meta │ │ │ │ │ ├── AuthenticationResponse.cs │ │ │ │ │ ├── AuthenticationResponse.cs.meta │ │ │ │ │ ├── CategoryDataResponse.cs │ │ │ │ │ ├── CategoryDataResponse.cs.meta │ │ │ │ │ ├── PackageThumbnailResponse.cs │ │ │ │ │ ├── PackageThumbnailResponse.cs.meta │ │ │ │ │ ├── PackageUploadedUnityVersionDataResponse.cs │ │ │ │ │ ├── PackageUploadedUnityVersionDataResponse.cs.meta │ │ │ │ │ ├── PackagesAdditionalDataResponse.cs │ │ │ │ │ ├── PackagesAdditionalDataResponse.cs.meta │ │ │ │ │ ├── PackagesDataResponse.cs │ │ │ │ │ ├── PackagesDataResponse.cs.meta │ │ │ │ │ ├── RefreshedPackageDataResponse.cs │ │ │ │ │ ├── RefreshedPackageDataResponse.cs.meta │ │ │ │ │ ├── UploadResponse.cs │ │ │ │ │ └── UploadResponse.cs.meta │ │ │ │ ├── Responses.meta │ │ │ │ ├── SessionAuthentication.cs │ │ │ │ ├── SessionAuthentication.cs.meta │ │ │ │ ├── UnityPackageUploader.cs │ │ │ │ ├── UnityPackageUploader.cs.meta │ │ │ │ ├── UploadStatus.cs │ │ │ │ └── UploadStatus.cs.meta │ │ │ ├── Api.meta │ │ │ ├── AssemblyInfo.cs │ │ │ ├── AssemblyInfo.cs.meta │ │ │ ├── AssetStoreTools.cs │ │ │ ├── AssetStoreTools.cs.meta │ │ │ ├── AssetStoreToolsWindow.cs │ │ │ ├── AssetStoreToolsWindow.cs.meta │ │ │ ├── Constants.cs │ │ │ ├── Constants.cs.meta │ │ │ ├── Exporter/ │ │ │ │ ├── Abstractions/ │ │ │ │ │ ├── IPackageExporter.cs │ │ │ │ │ ├── IPackageExporter.cs.meta │ │ │ │ │ ├── IPreviewInjector.cs │ │ │ │ │ ├── IPreviewInjector.cs.meta │ │ │ │ │ ├── PackageExporterBase.cs │ │ │ │ │ ├── PackageExporterBase.cs.meta │ │ │ │ │ ├── PackageExporterSettings.cs │ │ │ │ │ └── PackageExporterSettings.cs.meta │ │ │ │ ├── Abstractions.meta │ │ │ │ ├── DefaultExporterSettings.cs │ │ │ │ ├── DefaultExporterSettings.cs.meta │ │ │ │ ├── DefaultPackageExporter.cs │ │ │ │ ├── DefaultPackageExporter.cs.meta │ │ │ │ ├── LegacyExporterSettings.cs │ │ │ │ ├── LegacyExporterSettings.cs.meta │ │ │ │ ├── LegacyPackageExporter.cs │ │ │ │ ├── LegacyPackageExporter.cs.meta │ │ │ │ ├── PackageExporterResult.cs │ │ │ │ ├── PackageExporterResult.cs.meta │ │ │ │ ├── PreviewInjector.cs │ │ │ │ └── PreviewInjector.cs.meta │ │ │ ├── Exporter.meta │ │ │ ├── Previews/ │ │ │ │ ├── Scripts/ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ ├── CustomPreviewGenerationSettings.cs │ │ │ │ │ │ ├── CustomPreviewGenerationSettings.cs.meta │ │ │ │ │ │ ├── FileNameFormat.cs │ │ │ │ │ │ ├── FileNameFormat.cs.meta │ │ │ │ │ │ ├── GenerationType.cs │ │ │ │ │ │ ├── GenerationType.cs.meta │ │ │ │ │ │ ├── NativePreviewGenerationSettings.cs │ │ │ │ │ │ ├── NativePreviewGenerationSettings.cs.meta │ │ │ │ │ │ ├── PreviewDatabase.cs │ │ │ │ │ │ ├── PreviewDatabase.cs.meta │ │ │ │ │ │ ├── PreviewFormat.cs │ │ │ │ │ │ ├── PreviewFormat.cs.meta │ │ │ │ │ │ ├── PreviewGenerationResult.cs │ │ │ │ │ │ ├── PreviewGenerationResult.cs.meta │ │ │ │ │ │ ├── PreviewGenerationSettings.cs │ │ │ │ │ │ ├── PreviewGenerationSettings.cs.meta │ │ │ │ │ │ ├── PreviewMetadata.cs │ │ │ │ │ │ └── PreviewMetadata.cs.meta │ │ │ │ │ ├── Data.meta │ │ │ │ │ ├── Generators/ │ │ │ │ │ │ ├── Custom/ │ │ │ │ │ │ │ ├── AudioChannel.cs │ │ │ │ │ │ │ ├── AudioChannel.cs.meta │ │ │ │ │ │ │ ├── AudioChannelCoordinate.cs │ │ │ │ │ │ │ ├── AudioChannelCoordinate.cs.meta │ │ │ │ │ │ │ ├── Screenshotters/ │ │ │ │ │ │ │ │ ├── ISceneScreenshotter.cs │ │ │ │ │ │ │ │ ├── ISceneScreenshotter.cs.meta │ │ │ │ │ │ │ │ ├── MaterialScreenshotter.cs │ │ │ │ │ │ │ │ ├── MaterialScreenshotter.cs.meta │ │ │ │ │ │ │ │ ├── MeshScreenshotter.cs │ │ │ │ │ │ │ │ ├── MeshScreenshotter.cs.meta │ │ │ │ │ │ │ │ ├── SceneScreenshotterBase.cs │ │ │ │ │ │ │ │ ├── SceneScreenshotterBase.cs.meta │ │ │ │ │ │ │ │ ├── SceneScreenshotterSettings.cs │ │ │ │ │ │ │ │ └── SceneScreenshotterSettings.cs.meta │ │ │ │ │ │ │ ├── Screenshotters.meta │ │ │ │ │ │ │ ├── TypeGenerators/ │ │ │ │ │ │ │ │ ├── AudioTypeGeneratorSettings.cs │ │ │ │ │ │ │ │ ├── AudioTypeGeneratorSettings.cs.meta │ │ │ │ │ │ │ │ ├── AudioTypePreviewGenerator.cs │ │ │ │ │ │ │ │ ├── AudioTypePreviewGenerator.cs.meta │ │ │ │ │ │ │ │ ├── ITypePreviewGenerator.cs │ │ │ │ │ │ │ │ ├── ITypePreviewGenerator.cs.meta │ │ │ │ │ │ │ │ ├── MaterialTypePreviewGenerator.cs │ │ │ │ │ │ │ │ ├── MaterialTypePreviewGenerator.cs.meta │ │ │ │ │ │ │ │ ├── ModelTypePreviewGenerator.cs │ │ │ │ │ │ │ │ ├── ModelTypePreviewGenerator.cs.meta │ │ │ │ │ │ │ │ ├── PrefabTypePreviewGenerator.cs │ │ │ │ │ │ │ │ ├── PrefabTypePreviewGenerator.cs.meta │ │ │ │ │ │ │ │ ├── TextureTypeGeneratorSettings.cs │ │ │ │ │ │ │ │ ├── TextureTypeGeneratorSettings.cs.meta │ │ │ │ │ │ │ │ ├── TextureTypePreviewGenerator.cs │ │ │ │ │ │ │ │ ├── TextureTypePreviewGenerator.cs.meta │ │ │ │ │ │ │ │ ├── TypeGeneratorSettings.cs │ │ │ │ │ │ │ │ ├── TypeGeneratorSettings.cs.meta │ │ │ │ │ │ │ │ ├── TypePreviewGeneratorBase.cs │ │ │ │ │ │ │ │ ├── TypePreviewGeneratorBase.cs.meta │ │ │ │ │ │ │ │ ├── TypePreviewGeneratorFromScene.cs │ │ │ │ │ │ │ │ ├── TypePreviewGeneratorFromScene.cs.meta │ │ │ │ │ │ │ │ ├── TypePreviewGeneratorFromSceneSettings.cs │ │ │ │ │ │ │ │ └── TypePreviewGeneratorFromSceneSettings.cs.meta │ │ │ │ │ │ │ └── TypeGenerators.meta │ │ │ │ │ │ ├── Custom.meta │ │ │ │ │ │ ├── CustomPreviewGenerator.cs │ │ │ │ │ │ ├── CustomPreviewGenerator.cs.meta │ │ │ │ │ │ ├── IPreviewGenerator.cs │ │ │ │ │ │ ├── IPreviewGenerator.cs.meta │ │ │ │ │ │ ├── NativePreviewGenerator.cs │ │ │ │ │ │ ├── NativePreviewGenerator.cs.meta │ │ │ │ │ │ ├── PreviewGeneratorBase.cs │ │ │ │ │ │ └── PreviewGeneratorBase.cs.meta │ │ │ │ │ ├── Generators.meta │ │ │ │ │ ├── Services/ │ │ │ │ │ │ ├── Caching/ │ │ │ │ │ │ │ ├── CachingService.cs │ │ │ │ │ │ │ ├── CachingService.cs.meta │ │ │ │ │ │ │ ├── ICachingService.cs │ │ │ │ │ │ │ └── ICachingService.cs.meta │ │ │ │ │ │ ├── Caching.meta │ │ │ │ │ │ ├── IPreviewService.cs │ │ │ │ │ │ ├── IPreviewService.cs.meta │ │ │ │ │ │ ├── PreviewServiceProvider.cs │ │ │ │ │ │ └── PreviewServiceProvider.cs.meta │ │ │ │ │ ├── Services.meta │ │ │ │ │ ├── UI/ │ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ │ ├── AssetPreview.cs │ │ │ │ │ │ │ ├── AssetPreview.cs.meta │ │ │ │ │ │ │ ├── AssetPreviewCollection.cs │ │ │ │ │ │ │ ├── AssetPreviewCollection.cs.meta │ │ │ │ │ │ │ ├── IAssetPreview.cs │ │ │ │ │ │ │ ├── IAssetPreview.cs.meta │ │ │ │ │ │ │ ├── IAssetPreviewCollection.cs │ │ │ │ │ │ │ ├── IAssetPreviewCollection.cs.meta │ │ │ │ │ │ │ ├── IPreviewGeneratorSettings.cs │ │ │ │ │ │ │ ├── IPreviewGeneratorSettings.cs.meta │ │ │ │ │ │ │ ├── PreviewGeneratorSettings.cs │ │ │ │ │ │ │ └── PreviewGeneratorSettings.cs.meta │ │ │ │ │ │ ├── Data.meta │ │ │ │ │ │ ├── Elements/ │ │ │ │ │ │ │ ├── AssetPreviewElement.cs │ │ │ │ │ │ │ ├── AssetPreviewElement.cs.meta │ │ │ │ │ │ │ ├── GridListElement.cs │ │ │ │ │ │ │ ├── GridListElement.cs.meta │ │ │ │ │ │ │ ├── PreviewCollectionElement.cs │ │ │ │ │ │ │ ├── PreviewCollectionElement.cs.meta │ │ │ │ │ │ │ ├── PreviewGenerateButtonElement.cs │ │ │ │ │ │ │ ├── PreviewGenerateButtonElement.cs.meta │ │ │ │ │ │ │ ├── PreviewGeneratorPathsElement.cs │ │ │ │ │ │ │ ├── PreviewGeneratorPathsElement.cs.meta │ │ │ │ │ │ │ ├── PreviewGeneratorSettingsElement.cs │ │ │ │ │ │ │ ├── PreviewGeneratorSettingsElement.cs.meta │ │ │ │ │ │ │ ├── PreviewWindowDescriptionElement.cs │ │ │ │ │ │ │ └── PreviewWindowDescriptionElement.cs.meta │ │ │ │ │ │ ├── Elements.meta │ │ │ │ │ │ ├── PreviewGeneratorWindow.cs │ │ │ │ │ │ ├── PreviewGeneratorWindow.cs.meta │ │ │ │ │ │ ├── Views/ │ │ │ │ │ │ │ ├── PreviewListView.cs │ │ │ │ │ │ │ └── PreviewListView.cs.meta │ │ │ │ │ │ └── Views.meta │ │ │ │ │ ├── UI.meta │ │ │ │ │ ├── Utility/ │ │ │ │ │ │ ├── GraphicsUtility.cs │ │ │ │ │ │ ├── GraphicsUtility.cs.meta │ │ │ │ │ │ ├── PreviewConvertUtility.cs │ │ │ │ │ │ ├── PreviewConvertUtility.cs.meta │ │ │ │ │ │ ├── PreviewSceneUtility.cs │ │ │ │ │ │ ├── PreviewSceneUtility.cs.meta │ │ │ │ │ │ ├── RenderPipeline.cs │ │ │ │ │ │ ├── RenderPipeline.cs.meta │ │ │ │ │ │ ├── RenderPipelineUtility.cs │ │ │ │ │ │ └── RenderPipelineUtility.cs.meta │ │ │ │ │ └── Utility.meta │ │ │ │ ├── Scripts.meta │ │ │ │ ├── Styles/ │ │ │ │ │ ├── Style.uss │ │ │ │ │ ├── Style.uss.meta │ │ │ │ │ ├── ThemeDark.uss │ │ │ │ │ ├── ThemeDark.uss.meta │ │ │ │ │ ├── ThemeLight.uss │ │ │ │ │ └── ThemeLight.uss.meta │ │ │ │ └── Styles.meta │ │ │ ├── Previews.meta │ │ │ ├── Unity.AssetStoreTools.Editor.asmdef │ │ │ ├── Unity.AssetStoreTools.Editor.asmdef.meta │ │ │ ├── Uploader/ │ │ │ │ ├── Icons/ │ │ │ │ │ ├── account-dark.png.meta │ │ │ │ │ ├── account-light.png.meta │ │ │ │ │ ├── open-in-browser.png.meta │ │ │ │ │ ├── publisher-portal-dark.png.meta │ │ │ │ │ └── publisher-portal-light.png.meta │ │ │ │ ├── Icons.meta │ │ │ │ ├── Scripts/ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ ├── Abstractions/ │ │ │ │ │ │ │ ├── IPackage.cs │ │ │ │ │ │ │ ├── IPackage.cs.meta │ │ │ │ │ │ │ ├── IPackageContent.cs │ │ │ │ │ │ │ ├── IPackageContent.cs.meta │ │ │ │ │ │ │ ├── IPackageGroup.cs │ │ │ │ │ │ │ ├── IPackageGroup.cs.meta │ │ │ │ │ │ │ ├── IWorkflow.cs │ │ │ │ │ │ │ ├── IWorkflow.cs.meta │ │ │ │ │ │ │ ├── IWorkflowServices.cs │ │ │ │ │ │ │ ├── IWorkflowServices.cs.meta │ │ │ │ │ │ │ ├── WorkflowBase.cs │ │ │ │ │ │ │ └── WorkflowBase.cs.meta │ │ │ │ │ │ ├── Abstractions.meta │ │ │ │ │ │ ├── AssetsWorkflow.cs │ │ │ │ │ │ ├── AssetsWorkflow.cs.meta │ │ │ │ │ │ ├── HybridPackageWorkflow.cs │ │ │ │ │ │ ├── HybridPackageWorkflow.cs.meta │ │ │ │ │ │ ├── Package.cs │ │ │ │ │ │ ├── Package.cs.meta │ │ │ │ │ │ ├── PackageContent.cs │ │ │ │ │ │ ├── PackageContent.cs.meta │ │ │ │ │ │ ├── PackageGroup.cs │ │ │ │ │ │ ├── PackageGroup.cs.meta │ │ │ │ │ │ ├── PackageSorting.cs │ │ │ │ │ │ ├── PackageSorting.cs.meta │ │ │ │ │ │ ├── Serialization/ │ │ │ │ │ │ │ ├── AssetPath.cs │ │ │ │ │ │ │ ├── AssetPath.cs.meta │ │ │ │ │ │ │ ├── AssetsWorkflowStateData.cs │ │ │ │ │ │ │ ├── AssetsWorkflowStateData.cs.meta │ │ │ │ │ │ │ ├── HybridPackageWorkflowState.cs │ │ │ │ │ │ │ ├── HybridPackageWorkflowState.cs.meta │ │ │ │ │ │ │ ├── UnityPackageWorkflowStateData.cs │ │ │ │ │ │ │ ├── UnityPackageWorkflowStateData.cs.meta │ │ │ │ │ │ │ ├── WorkflowStateData.cs │ │ │ │ │ │ │ └── WorkflowStateData.cs.meta │ │ │ │ │ │ ├── Serialization.meta │ │ │ │ │ │ ├── UnityPackageWorkflow.cs │ │ │ │ │ │ ├── UnityPackageWorkflow.cs.meta │ │ │ │ │ │ ├── WorkflowServices.cs │ │ │ │ │ │ └── WorkflowServices.cs.meta │ │ │ │ │ ├── Data.meta │ │ │ │ │ ├── Services/ │ │ │ │ │ │ ├── Analytics/ │ │ │ │ │ │ │ ├── AnalyticsService.cs │ │ │ │ │ │ │ ├── AnalyticsService.cs.meta │ │ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ │ │ ├── AuthenticationAnalytic.cs │ │ │ │ │ │ │ │ ├── AuthenticationAnalytic.cs.meta │ │ │ │ │ │ │ │ ├── BaseAnalytic.cs │ │ │ │ │ │ │ │ ├── BaseAnalytic.cs.meta │ │ │ │ │ │ │ │ ├── IAssetStoreAnalytic.cs │ │ │ │ │ │ │ │ ├── IAssetStoreAnalytic.cs.meta │ │ │ │ │ │ │ │ ├── IAssetStoreAnalyticData.cs │ │ │ │ │ │ │ │ ├── IAssetStoreAnalyticData.cs.meta │ │ │ │ │ │ │ │ ├── PackageUploadAnalytic.cs │ │ │ │ │ │ │ │ ├── PackageUploadAnalytic.cs.meta │ │ │ │ │ │ │ │ ├── ValidationResultsSerializer.cs │ │ │ │ │ │ │ │ └── ValidationResultsSerializer.cs.meta │ │ │ │ │ │ │ ├── Data.meta │ │ │ │ │ │ │ ├── IAnalyticsService.cs │ │ │ │ │ │ │ └── IAnalyticsService.cs.meta │ │ │ │ │ │ ├── Analytics.meta │ │ │ │ │ │ ├── Api/ │ │ │ │ │ │ │ ├── AuthenticationService.cs │ │ │ │ │ │ │ ├── AuthenticationService.cs.meta │ │ │ │ │ │ │ ├── IAuthenticationService.cs │ │ │ │ │ │ │ ├── IAuthenticationService.cs.meta │ │ │ │ │ │ │ ├── IPackageDownloadingService.cs │ │ │ │ │ │ │ ├── IPackageDownloadingService.cs.meta │ │ │ │ │ │ │ ├── IPackageUploadingService.cs │ │ │ │ │ │ │ ├── IPackageUploadingService.cs.meta │ │ │ │ │ │ │ ├── PackageDownloadingService.cs │ │ │ │ │ │ │ ├── PackageDownloadingService.cs.meta │ │ │ │ │ │ │ ├── PackageUploadingService.cs │ │ │ │ │ │ │ └── PackageUploadingService.cs.meta │ │ │ │ │ │ ├── Api.meta │ │ │ │ │ │ ├── Caching/ │ │ │ │ │ │ │ ├── CachingService.cs │ │ │ │ │ │ │ ├── CachingService.cs.meta │ │ │ │ │ │ │ ├── ICachingService.cs │ │ │ │ │ │ │ └── ICachingService.cs.meta │ │ │ │ │ │ ├── Caching.meta │ │ │ │ │ │ ├── IUploaderService.cs │ │ │ │ │ │ ├── IUploaderService.cs.meta │ │ │ │ │ │ ├── PackageFactory/ │ │ │ │ │ │ │ ├── IPackageFactoryService.cs │ │ │ │ │ │ │ ├── IPackageFactoryService.cs.meta │ │ │ │ │ │ │ ├── PackageFactoryService.cs │ │ │ │ │ │ │ └── PackageFactoryService.cs.meta │ │ │ │ │ │ ├── PackageFactory.meta │ │ │ │ │ │ ├── UploaderServiceProvider.cs │ │ │ │ │ │ └── UploaderServiceProvider.cs.meta │ │ │ │ │ ├── Services.meta │ │ │ │ │ ├── UI/ │ │ │ │ │ │ ├── Elements/ │ │ │ │ │ │ │ ├── Abstractions/ │ │ │ │ │ │ │ │ ├── ValidationElementBase.cs │ │ │ │ │ │ │ │ ├── ValidationElementBase.cs.meta │ │ │ │ │ │ │ │ ├── WorkflowElementBase.cs │ │ │ │ │ │ │ │ └── WorkflowElementBase.cs.meta │ │ │ │ │ │ │ ├── Abstractions.meta │ │ │ │ │ │ │ ├── AccountToolbar.cs │ │ │ │ │ │ │ ├── AccountToolbar.cs.meta │ │ │ │ │ │ │ ├── AssetsWorkflowElement.cs │ │ │ │ │ │ │ ├── AssetsWorkflowElement.cs.meta │ │ │ │ │ │ │ ├── CurrentProjectValidationElement.cs │ │ │ │ │ │ │ ├── CurrentProjectValidationElement.cs.meta │ │ │ │ │ │ │ ├── ExternalProjectValidationElement.cs │ │ │ │ │ │ │ ├── ExternalProjectValidationElement.cs.meta │ │ │ │ │ │ │ ├── HybridPackageWorkflowElement.cs │ │ │ │ │ │ │ ├── HybridPackageWorkflowElement.cs.meta │ │ │ │ │ │ │ ├── LoadingSpinner.cs │ │ │ │ │ │ │ ├── LoadingSpinner.cs.meta │ │ │ │ │ │ │ ├── MultiToggleSelectionElement.cs │ │ │ │ │ │ │ ├── MultiToggleSelectionElement.cs.meta │ │ │ │ │ │ │ ├── PackageContentElement.cs │ │ │ │ │ │ │ ├── PackageContentElement.cs.meta │ │ │ │ │ │ │ ├── PackageElement.cs │ │ │ │ │ │ │ ├── PackageElement.cs.meta │ │ │ │ │ │ │ ├── PackageGroupElement.cs │ │ │ │ │ │ │ ├── PackageGroupElement.cs.meta │ │ │ │ │ │ │ ├── PackageListToolbar.cs │ │ │ │ │ │ │ ├── PackageListToolbar.cs.meta │ │ │ │ │ │ │ ├── PackageUploadElement.cs │ │ │ │ │ │ │ ├── PackageUploadElement.cs.meta │ │ │ │ │ │ │ ├── PathSelectionElement.cs │ │ │ │ │ │ │ ├── PathSelectionElement.cs.meta │ │ │ │ │ │ │ ├── PreviewGenerationElement.cs │ │ │ │ │ │ │ ├── PreviewGenerationElement.cs.meta │ │ │ │ │ │ │ ├── UnityPackageWorkflowElement.cs │ │ │ │ │ │ │ └── UnityPackageWorkflowElement.cs.meta │ │ │ │ │ │ ├── Elements.meta │ │ │ │ │ │ ├── Views/ │ │ │ │ │ │ │ ├── LoginView.cs │ │ │ │ │ │ │ ├── LoginView.cs.meta │ │ │ │ │ │ │ ├── PackageListView.cs │ │ │ │ │ │ │ └── PackageListView.cs.meta │ │ │ │ │ │ └── Views.meta │ │ │ │ │ └── UI.meta │ │ │ │ ├── Scripts.meta │ │ │ │ ├── Styles/ │ │ │ │ │ ├── LoginView/ │ │ │ │ │ │ ├── Style.uss │ │ │ │ │ │ ├── Style.uss.meta │ │ │ │ │ │ ├── ThemeDark.uss │ │ │ │ │ │ ├── ThemeDark.uss.meta │ │ │ │ │ │ ├── ThemeLight.uss │ │ │ │ │ │ └── ThemeLight.uss.meta │ │ │ │ │ ├── LoginView.meta │ │ │ │ │ ├── PackageListView/ │ │ │ │ │ │ ├── Style.uss │ │ │ │ │ │ ├── Style.uss.meta │ │ │ │ │ │ ├── ThemeDark.uss │ │ │ │ │ │ ├── ThemeDark.uss.meta │ │ │ │ │ │ ├── ThemeLight.uss │ │ │ │ │ │ └── ThemeLight.uss.meta │ │ │ │ │ ├── PackageListView.meta │ │ │ │ │ ├── Style.uss │ │ │ │ │ ├── Style.uss.meta │ │ │ │ │ ├── ThemeDark.uss │ │ │ │ │ ├── ThemeDark.uss.meta │ │ │ │ │ ├── ThemeLight.uss │ │ │ │ │ └── ThemeLight.uss.meta │ │ │ │ ├── Styles.meta │ │ │ │ ├── UploaderWindow.cs │ │ │ │ └── UploaderWindow.cs.meta │ │ │ ├── Uploader.meta │ │ │ ├── Utility/ │ │ │ │ ├── ASDebug.cs │ │ │ │ ├── ASDebug.cs.meta │ │ │ │ ├── ASToolsPreferences.cs │ │ │ │ ├── ASToolsPreferences.cs.meta │ │ │ │ ├── ASToolsUpdater.cs │ │ │ │ ├── ASToolsUpdater.cs.meta │ │ │ │ ├── CacheUtil.cs │ │ │ │ ├── CacheUtil.cs.meta │ │ │ │ ├── FileUtility.cs │ │ │ │ ├── FileUtility.cs.meta │ │ │ │ ├── LegacyToolsRemover.cs │ │ │ │ ├── LegacyToolsRemover.cs.meta │ │ │ │ ├── PackageUtility.cs │ │ │ │ ├── PackageUtility.cs.meta │ │ │ │ ├── ServiceProvider.cs │ │ │ │ ├── ServiceProvider.cs.meta │ │ │ │ ├── StyleSelector.cs │ │ │ │ ├── StyleSelector.cs.meta │ │ │ │ ├── Styles/ │ │ │ │ │ ├── Updater/ │ │ │ │ │ │ ├── Style.uss │ │ │ │ │ │ ├── Style.uss.meta │ │ │ │ │ │ ├── ThemeDark.uss │ │ │ │ │ │ ├── ThemeDark.uss.meta │ │ │ │ │ │ ├── ThemeLight.uss │ │ │ │ │ │ └── ThemeLight.uss.meta │ │ │ │ │ └── Updater.meta │ │ │ │ ├── Styles.meta │ │ │ │ ├── SymlinkUtil.cs │ │ │ │ └── SymlinkUtil.cs.meta │ │ │ ├── Utility.meta │ │ │ ├── Validator/ │ │ │ │ ├── Icons/ │ │ │ │ │ ├── error.png.meta │ │ │ │ │ ├── error_d.png.meta │ │ │ │ │ ├── success.png.meta │ │ │ │ │ ├── success_d.png.meta │ │ │ │ │ ├── undefined.png.meta │ │ │ │ │ ├── undefined_d.png.meta │ │ │ │ │ ├── warning.png.meta │ │ │ │ │ └── warning_d.png.meta │ │ │ │ ├── Icons.meta │ │ │ │ ├── Scripts/ │ │ │ │ │ ├── Categories/ │ │ │ │ │ │ ├── CategoryEvaluator.cs │ │ │ │ │ │ ├── CategoryEvaluator.cs.meta │ │ │ │ │ │ ├── ValidatorCategory.cs │ │ │ │ │ │ └── ValidatorCategory.cs.meta │ │ │ │ │ ├── Categories.meta │ │ │ │ │ ├── CurrentProjectValidator.cs │ │ │ │ │ ├── CurrentProjectValidator.cs.meta │ │ │ │ │ ├── Data/ │ │ │ │ │ │ ├── CurrentProjectValidationSettings.cs │ │ │ │ │ │ ├── CurrentProjectValidationSettings.cs.meta │ │ │ │ │ │ ├── ExternalProjectValidationSettings.cs │ │ │ │ │ │ ├── ExternalProjectValidationSettings.cs.meta │ │ │ │ │ │ ├── MessageActions/ │ │ │ │ │ │ │ ├── HighlightObjectAction.cs │ │ │ │ │ │ │ ├── HighlightObjectAction.cs.meta │ │ │ │ │ │ │ ├── IMessageAction.cs │ │ │ │ │ │ │ ├── IMessageAction.cs.meta │ │ │ │ │ │ │ ├── OpenAssetAction.cs │ │ │ │ │ │ │ └── OpenAssetAction.cs.meta │ │ │ │ │ │ ├── MessageActions.meta │ │ │ │ │ │ ├── TestResult.cs │ │ │ │ │ │ ├── TestResult.cs.meta │ │ │ │ │ │ ├── TestResultMessage.cs │ │ │ │ │ │ ├── TestResultMessage.cs.meta │ │ │ │ │ │ ├── TestResultObject.cs │ │ │ │ │ │ ├── TestResultObject.cs.meta │ │ │ │ │ │ ├── TestResultStatus.cs │ │ │ │ │ │ ├── TestResultStatus.cs.meta │ │ │ │ │ │ ├── ValidationResult.cs │ │ │ │ │ │ ├── ValidationResult.cs.meta │ │ │ │ │ │ ├── ValidationSettings.cs │ │ │ │ │ │ ├── ValidationSettings.cs.meta │ │ │ │ │ │ ├── ValidationStatus.cs │ │ │ │ │ │ ├── ValidationStatus.cs.meta │ │ │ │ │ │ ├── ValidationType.cs │ │ │ │ │ │ └── ValidationType.cs.meta │ │ │ │ │ ├── Data.meta │ │ │ │ │ ├── ExternalProjectValidator.cs │ │ │ │ │ ├── ExternalProjectValidator.cs.meta │ │ │ │ │ ├── IValidator.cs │ │ │ │ │ ├── IValidator.cs.meta │ │ │ │ │ ├── Services/ │ │ │ │ │ │ ├── CachingService/ │ │ │ │ │ │ │ ├── CachingService.cs │ │ │ │ │ │ │ ├── CachingService.cs.meta │ │ │ │ │ │ │ ├── ICachingService.cs │ │ │ │ │ │ │ ├── ICachingService.cs.meta │ │ │ │ │ │ │ ├── PreviewDatabaseContractResolver.cs │ │ │ │ │ │ │ └── PreviewDatabaseContractResolver.cs.meta │ │ │ │ │ │ ├── CachingService.meta │ │ │ │ │ │ ├── IValidatorService.cs │ │ │ │ │ │ ├── IValidatorService.cs.meta │ │ │ │ │ │ ├── Validation/ │ │ │ │ │ │ │ ├── Abstractions/ │ │ │ │ │ │ │ │ ├── IAssetUtilityService.cs │ │ │ │ │ │ │ │ ├── IAssetUtilityService.cs.meta │ │ │ │ │ │ │ │ ├── IFileSignatureUtilityService.cs │ │ │ │ │ │ │ │ ├── IFileSignatureUtilityService.cs.meta │ │ │ │ │ │ │ │ ├── IMeshUtilityService.cs │ │ │ │ │ │ │ │ ├── IMeshUtilityService.cs.meta │ │ │ │ │ │ │ │ ├── IModelUtilityService.cs │ │ │ │ │ │ │ │ ├── IModelUtilityService.cs.meta │ │ │ │ │ │ │ │ ├── ISceneUtilityService.cs │ │ │ │ │ │ │ │ ├── ISceneUtilityService.cs.meta │ │ │ │ │ │ │ │ ├── IScriptUtilityService.cs │ │ │ │ │ │ │ │ └── IScriptUtilityService.cs.meta │ │ │ │ │ │ │ ├── Abstractions.meta │ │ │ │ │ │ │ ├── AssetUtilityService.cs │ │ │ │ │ │ │ ├── AssetUtilityService.cs.meta │ │ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ │ │ ├── ArchiveType.cs │ │ │ │ │ │ │ │ ├── ArchiveType.cs.meta │ │ │ │ │ │ │ │ ├── AssetEnumerator.cs │ │ │ │ │ │ │ │ ├── AssetEnumerator.cs.meta │ │ │ │ │ │ │ │ ├── AssetType.cs │ │ │ │ │ │ │ │ ├── AssetType.cs.meta │ │ │ │ │ │ │ │ ├── LogEntry.cs │ │ │ │ │ │ │ │ └── LogEntry.cs.meta │ │ │ │ │ │ │ ├── Data.meta │ │ │ │ │ │ │ ├── FileSignatureUtilityService.cs │ │ │ │ │ │ │ ├── FileSignatureUtilityService.cs.meta │ │ │ │ │ │ │ ├── MeshUtilityService.cs │ │ │ │ │ │ │ ├── MeshUtilityService.cs.meta │ │ │ │ │ │ │ ├── ModelUtilityService.cs │ │ │ │ │ │ │ ├── ModelUtilityService.cs.meta │ │ │ │ │ │ │ ├── SceneUtilityService.cs │ │ │ │ │ │ │ ├── SceneUtilityService.cs.meta │ │ │ │ │ │ │ ├── ScriptUtilityService.cs │ │ │ │ │ │ │ └── ScriptUtilityService.cs.meta │ │ │ │ │ │ ├── Validation.meta │ │ │ │ │ │ ├── ValidatorServiceProvider.cs │ │ │ │ │ │ └── ValidatorServiceProvider.cs.meta │ │ │ │ │ ├── Services.meta │ │ │ │ │ ├── Test Definitions/ │ │ │ │ │ │ ├── AutomatedTest.cs │ │ │ │ │ │ ├── AutomatedTest.cs.meta │ │ │ │ │ │ ├── GenericTestConfig.cs │ │ │ │ │ │ ├── GenericTestConfig.cs.meta │ │ │ │ │ │ ├── ITestConfig.cs │ │ │ │ │ │ ├── ITestConfig.cs.meta │ │ │ │ │ │ ├── ITestScript.cs │ │ │ │ │ │ ├── ITestScript.cs.meta │ │ │ │ │ │ ├── Scriptable Objects/ │ │ │ │ │ │ │ ├── AutomatedTestScriptableObject.cs │ │ │ │ │ │ │ ├── AutomatedTestScriptableObject.cs.meta │ │ │ │ │ │ │ ├── Editor/ │ │ │ │ │ │ │ │ ├── ValidationTestScriptableObjectInspector.cs │ │ │ │ │ │ │ │ └── ValidationTestScriptableObjectInspector.cs.meta │ │ │ │ │ │ │ ├── Editor.meta │ │ │ │ │ │ │ ├── ValidationTestScriptableObject.cs │ │ │ │ │ │ │ └── ValidationTestScriptableObject.cs.meta │ │ │ │ │ │ ├── Scriptable Objects.meta │ │ │ │ │ │ ├── ValidationTest.cs │ │ │ │ │ │ └── ValidationTest.cs.meta │ │ │ │ │ ├── Test Definitions.meta │ │ │ │ │ ├── Test Methods/ │ │ │ │ │ │ ├── Generic/ │ │ │ │ │ │ │ ├── CheckAnimationClips.cs │ │ │ │ │ │ │ ├── CheckAnimationClips.cs.meta │ │ │ │ │ │ │ ├── CheckAudioClipping.cs │ │ │ │ │ │ │ ├── CheckAudioClipping.cs.meta │ │ │ │ │ │ │ ├── CheckColliders.cs │ │ │ │ │ │ │ ├── CheckColliders.cs.meta │ │ │ │ │ │ │ ├── CheckCompressedFiles.cs │ │ │ │ │ │ │ ├── CheckCompressedFiles.cs.meta │ │ │ │ │ │ │ ├── CheckEmptyPrefabs.cs │ │ │ │ │ │ │ ├── CheckEmptyPrefabs.cs.meta │ │ │ │ │ │ │ ├── CheckFileMenuNames.cs │ │ │ │ │ │ │ ├── CheckFileMenuNames.cs.meta │ │ │ │ │ │ │ ├── CheckLODs.cs │ │ │ │ │ │ │ ├── CheckLODs.cs.meta │ │ │ │ │ │ │ ├── CheckLineEndings.cs │ │ │ │ │ │ │ ├── CheckLineEndings.cs.meta │ │ │ │ │ │ │ ├── CheckMeshPrefabs.cs │ │ │ │ │ │ │ ├── CheckMeshPrefabs.cs.meta │ │ │ │ │ │ │ ├── CheckMissingComponentsinAssets.cs │ │ │ │ │ │ │ ├── CheckMissingComponentsinAssets.cs.meta │ │ │ │ │ │ │ ├── CheckMissingComponentsinScenes.cs │ │ │ │ │ │ │ ├── CheckMissingComponentsinScenes.cs.meta │ │ │ │ │ │ │ ├── CheckModelImportLogs.cs │ │ │ │ │ │ │ ├── CheckModelImportLogs.cs.meta │ │ │ │ │ │ │ ├── CheckModelOrientation.cs │ │ │ │ │ │ │ ├── CheckModelOrientation.cs.meta │ │ │ │ │ │ │ ├── CheckModelTypes.cs │ │ │ │ │ │ │ ├── CheckModelTypes.cs.meta │ │ │ │ │ │ │ ├── CheckNormalMapTextures.cs │ │ │ │ │ │ │ ├── CheckNormalMapTextures.cs.meta │ │ │ │ │ │ │ ├── CheckPackageNaming.cs │ │ │ │ │ │ │ ├── CheckPackageNaming.cs.meta │ │ │ │ │ │ │ ├── CheckParticleSystems.cs │ │ │ │ │ │ │ ├── CheckParticleSystems.cs.meta │ │ │ │ │ │ │ ├── CheckPathLengths.cs │ │ │ │ │ │ │ ├── CheckPathLengths.cs.meta │ │ │ │ │ │ │ ├── CheckPrefabTransforms.cs │ │ │ │ │ │ │ ├── CheckPrefabTransforms.cs.meta │ │ │ │ │ │ │ ├── CheckScriptCompilation.cs │ │ │ │ │ │ │ ├── CheckScriptCompilation.cs.meta │ │ │ │ │ │ │ ├── CheckShaderCompilation.cs │ │ │ │ │ │ │ ├── CheckShaderCompilation.cs.meta │ │ │ │ │ │ │ ├── CheckTextureDimensions.cs │ │ │ │ │ │ │ ├── CheckTextureDimensions.cs.meta │ │ │ │ │ │ │ ├── CheckTypeNamespaces.cs │ │ │ │ │ │ │ ├── CheckTypeNamespaces.cs.meta │ │ │ │ │ │ │ ├── RemoveExecutableFiles.cs │ │ │ │ │ │ │ ├── RemoveExecutableFiles.cs.meta │ │ │ │ │ │ │ ├── RemoveJPGFiles.cs │ │ │ │ │ │ │ ├── RemoveJPGFiles.cs.meta │ │ │ │ │ │ │ ├── RemoveJavaScriptFiles.cs │ │ │ │ │ │ │ ├── RemoveJavaScriptFiles.cs.meta │ │ │ │ │ │ │ ├── RemoveLossyAudioFiles.cs │ │ │ │ │ │ │ ├── RemoveLossyAudioFiles.cs.meta │ │ │ │ │ │ │ ├── RemoveMixamoFiles.cs │ │ │ │ │ │ │ ├── RemoveMixamoFiles.cs.meta │ │ │ │ │ │ │ ├── RemoveSpeedTreeFiles.cs │ │ │ │ │ │ │ ├── RemoveSpeedTreeFiles.cs.meta │ │ │ │ │ │ │ ├── RemoveVideoFiles.cs │ │ │ │ │ │ │ └── RemoveVideoFiles.cs.meta │ │ │ │ │ │ ├── Generic.meta │ │ │ │ │ │ ├── UnityPackage/ │ │ │ │ │ │ │ ├── CheckDemoScenes.cs │ │ │ │ │ │ │ ├── CheckDemoScenes.cs.meta │ │ │ │ │ │ │ ├── CheckDocumentation.cs │ │ │ │ │ │ │ ├── CheckDocumentation.cs.meta │ │ │ │ │ │ │ ├── CheckPackageSize.cs │ │ │ │ │ │ │ ├── CheckPackageSize.cs.meta │ │ │ │ │ │ │ ├── CheckProjectTemplateAssets.cs │ │ │ │ │ │ │ └── CheckProjectTemplateAssets.cs.meta │ │ │ │ │ │ └── UnityPackage.meta │ │ │ │ │ ├── Test Methods.meta │ │ │ │ │ ├── UI/ │ │ │ │ │ │ ├── Data/ │ │ │ │ │ │ │ ├── Abstractions/ │ │ │ │ │ │ │ │ ├── IValidatorResults.cs │ │ │ │ │ │ │ │ ├── IValidatorResults.cs.meta │ │ │ │ │ │ │ │ ├── IValidatorSettings.cs │ │ │ │ │ │ │ │ ├── IValidatorSettings.cs.meta │ │ │ │ │ │ │ │ ├── IValidatorTest.cs │ │ │ │ │ │ │ │ ├── IValidatorTest.cs.meta │ │ │ │ │ │ │ │ ├── IValidatorTestGroup.cs │ │ │ │ │ │ │ │ └── IValidatorTestGroup.cs.meta │ │ │ │ │ │ │ ├── Abstractions.meta │ │ │ │ │ │ │ ├── Serialization/ │ │ │ │ │ │ │ │ ├── ValidatorStateData.cs │ │ │ │ │ │ │ │ ├── ValidatorStateData.cs.meta │ │ │ │ │ │ │ │ ├── ValidatorStateDataContractResolver.cs │ │ │ │ │ │ │ │ ├── ValidatorStateDataContractResolver.cs.meta │ │ │ │ │ │ │ │ ├── ValidatorStateResults.cs │ │ │ │ │ │ │ │ ├── ValidatorStateResults.cs.meta │ │ │ │ │ │ │ │ ├── ValidatorStateSettings.cs │ │ │ │ │ │ │ │ └── ValidatorStateSettings.cs.meta │ │ │ │ │ │ │ ├── Serialization.meta │ │ │ │ │ │ │ ├── ValidatorResults.cs │ │ │ │ │ │ │ ├── ValidatorResults.cs.meta │ │ │ │ │ │ │ ├── ValidatorSettings.cs │ │ │ │ │ │ │ ├── ValidatorSettings.cs.meta │ │ │ │ │ │ │ ├── ValidatorTest.cs │ │ │ │ │ │ │ ├── ValidatorTest.cs.meta │ │ │ │ │ │ │ ├── ValidatorTestGroup.cs │ │ │ │ │ │ │ └── ValidatorTestGroup.cs.meta │ │ │ │ │ │ ├── Data.meta │ │ │ │ │ │ ├── Elements/ │ │ │ │ │ │ │ ├── ValidatorButtonElement.cs │ │ │ │ │ │ │ ├── ValidatorButtonElement.cs.meta │ │ │ │ │ │ │ ├── ValidatorDescriptionElement.cs │ │ │ │ │ │ │ ├── ValidatorDescriptionElement.cs.meta │ │ │ │ │ │ │ ├── ValidatorPathsElement.cs │ │ │ │ │ │ │ ├── ValidatorPathsElement.cs.meta │ │ │ │ │ │ │ ├── ValidatorResultsElement.cs │ │ │ │ │ │ │ ├── ValidatorResultsElement.cs.meta │ │ │ │ │ │ │ ├── ValidatorSettingsElement.cs │ │ │ │ │ │ │ ├── ValidatorSettingsElement.cs.meta │ │ │ │ │ │ │ ├── ValidatorTestElement.cs │ │ │ │ │ │ │ ├── ValidatorTestElement.cs.meta │ │ │ │ │ │ │ ├── ValidatorTestGroupElement.cs │ │ │ │ │ │ │ └── ValidatorTestGroupElement.cs.meta │ │ │ │ │ │ ├── Elements.meta │ │ │ │ │ │ ├── ValidatorWindow.cs │ │ │ │ │ │ ├── ValidatorWindow.cs.meta │ │ │ │ │ │ ├── Views/ │ │ │ │ │ │ │ ├── ValidatorTestsView.cs │ │ │ │ │ │ │ └── ValidatorTestsView.cs.meta │ │ │ │ │ │ └── Views.meta │ │ │ │ │ ├── UI.meta │ │ │ │ │ ├── Utility/ │ │ │ │ │ │ ├── ValidatorUtility.cs │ │ │ │ │ │ └── ValidatorUtility.cs.meta │ │ │ │ │ ├── Utility.meta │ │ │ │ │ ├── ValidatorBase.cs │ │ │ │ │ └── ValidatorBase.cs.meta │ │ │ │ ├── Scripts.meta │ │ │ │ ├── Styles/ │ │ │ │ │ ├── Style.uss │ │ │ │ │ ├── Style.uss.meta │ │ │ │ │ ├── ThemeDark.uss │ │ │ │ │ ├── ThemeDark.uss.meta │ │ │ │ │ ├── ThemeLight.uss │ │ │ │ │ └── ThemeLight.uss.meta │ │ │ │ ├── Styles.meta │ │ │ │ ├── Tests/ │ │ │ │ │ ├── Generic/ │ │ │ │ │ │ ├── Check Animation Clips.asset │ │ │ │ │ │ ├── Check Animation Clips.asset.meta │ │ │ │ │ │ ├── Check Audio Clipping.asset │ │ │ │ │ │ ├── Check Audio Clipping.asset.meta │ │ │ │ │ │ ├── Check Colliders.asset │ │ │ │ │ │ ├── Check Colliders.asset.meta │ │ │ │ │ │ ├── Check Compressed Files.asset │ │ │ │ │ │ ├── Check Compressed Files.asset.meta │ │ │ │ │ │ ├── Check Empty Prefabs.asset │ │ │ │ │ │ ├── Check Empty Prefabs.asset.meta │ │ │ │ │ │ ├── Check File Menu Names.asset │ │ │ │ │ │ ├── Check File Menu Names.asset.meta │ │ │ │ │ │ ├── Check LODs.asset │ │ │ │ │ │ ├── Check LODs.asset.meta │ │ │ │ │ │ ├── Check Line Endings.asset │ │ │ │ │ │ ├── Check Line Endings.asset.meta │ │ │ │ │ │ ├── Check Mesh Prefabs.asset │ │ │ │ │ │ ├── Check Mesh Prefabs.asset.meta │ │ │ │ │ │ ├── Check Missing Components in Assets.asset │ │ │ │ │ │ ├── Check Missing Components in Assets.asset.meta │ │ │ │ │ │ ├── Check Missing Components in Scenes.asset │ │ │ │ │ │ ├── Check Missing Components in Scenes.asset.meta │ │ │ │ │ │ ├── Check Model Import Logs.asset │ │ │ │ │ │ ├── Check Model Import Logs.asset.meta │ │ │ │ │ │ ├── Check Model Orientation.asset │ │ │ │ │ │ ├── Check Model Orientation.asset.meta │ │ │ │ │ │ ├── Check Model Types.asset │ │ │ │ │ │ ├── Check Model Types.asset.meta │ │ │ │ │ │ ├── Check Normal Map Textures.asset │ │ │ │ │ │ ├── Check Normal Map Textures.asset.meta │ │ │ │ │ │ ├── Check Package Naming.asset │ │ │ │ │ │ ├── Check Package Naming.asset.meta │ │ │ │ │ │ ├── Check Particle Systems.asset │ │ │ │ │ │ ├── Check Particle Systems.asset.meta │ │ │ │ │ │ ├── Check Path Lengths.asset │ │ │ │ │ │ ├── Check Path Lengths.asset.meta │ │ │ │ │ │ ├── Check Prefab Transforms.asset │ │ │ │ │ │ ├── Check Prefab Transforms.asset.meta │ │ │ │ │ │ ├── Check Script Compilation.asset │ │ │ │ │ │ ├── Check Script Compilation.asset.meta │ │ │ │ │ │ ├── Check Shader Compilation.asset │ │ │ │ │ │ ├── Check Shader Compilation.asset.meta │ │ │ │ │ │ ├── Check Texture Dimensions.asset │ │ │ │ │ │ ├── Check Texture Dimensions.asset.meta │ │ │ │ │ │ ├── Check Type Namespaces.asset │ │ │ │ │ │ ├── Check Type Namespaces.asset.meta │ │ │ │ │ │ ├── Remove Executable Files.asset │ │ │ │ │ │ ├── Remove Executable Files.asset.meta │ │ │ │ │ │ ├── Remove JPG Files.asset │ │ │ │ │ │ ├── Remove JPG Files.asset.meta │ │ │ │ │ │ ├── Remove JavaScript Files.asset │ │ │ │ │ │ ├── Remove JavaScript Files.asset.meta │ │ │ │ │ │ ├── Remove Lossy Audio Files.asset │ │ │ │ │ │ ├── Remove Lossy Audio Files.asset.meta │ │ │ │ │ │ ├── Remove Mixamo Files.asset │ │ │ │ │ │ ├── Remove Mixamo Files.asset.meta │ │ │ │ │ │ ├── Remove SpeedTree Files.asset │ │ │ │ │ │ ├── Remove SpeedTree Files.asset.meta │ │ │ │ │ │ ├── Remove Video Files.asset │ │ │ │ │ │ └── Remove Video Files.asset.meta │ │ │ │ │ ├── Generic.meta │ │ │ │ │ ├── UnityPackage/ │ │ │ │ │ │ ├── Check Demo Scenes.asset │ │ │ │ │ │ ├── Check Demo Scenes.asset.meta │ │ │ │ │ │ ├── Check Documentation.asset │ │ │ │ │ │ ├── Check Documentation.asset.meta │ │ │ │ │ │ ├── Check Package Size.asset │ │ │ │ │ │ ├── Check Package Size.asset.meta │ │ │ │ │ │ ├── Check Project Template Assets.asset │ │ │ │ │ │ └── Check Project Template Assets.asset.meta │ │ │ │ │ └── UnityPackage.meta │ │ │ │ └── Tests.meta │ │ │ └── Validator.meta │ │ ├── Editor.meta │ │ ├── LICENSE.md │ │ ├── LICENSE.md.meta │ │ ├── package.json │ │ └── package.json.meta │ ├── manifest.json │ └── packages-lock.json ├── ProjectSettings/ │ ├── AudioManager.asset │ ├── ClusterInputManager.asset │ ├── DynamicsManager.asset │ ├── EditorBuildSettings.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── InputManager.asset │ ├── MemorySettings.asset │ ├── MultiplayerManager.asset │ ├── NavMeshAreas.asset │ ├── PackageManagerSettings.asset │ ├── Physics2DSettings.asset │ ├── PresetManager.asset │ ├── ProjectSettings.asset │ ├── ProjectVersion.txt │ ├── QualitySettings.asset │ ├── ShaderGraphMarkdownSettings.asset │ ├── TagManager.asset │ ├── TimeManager.asset │ ├── UnityConnectSettings.asset │ ├── VFXManager.asset │ ├── VersionControlSettings.asset │ └── XRSettings.asset ├── ShadergraphMarkdown 2019.4.sln ├── ShadergraphMarkdown 6000.2.sln ├── Unity.AI.Navigation.Editor.ConversionSystem.csproj ├── Unity.AI.Navigation.Editor.Tests.csproj ├── Unity.AI.Navigation.Editor.csproj ├── Unity.AI.Navigation.LegacyOffMeshLink.Tests.csproj ├── Unity.AI.Navigation.Tests.EditorInPlaymode.csproj ├── Unity.AI.Navigation.Tests.csproj ├── Unity.AI.Navigation.Updater.csproj ├── Unity.AI.Navigation.csproj ├── Unity.CollabProxy.Editor.csproj ├── Unity.PlasticSCM.Editor.csproj ├── Unity.Rider.Editor.csproj ├── Unity.TextMeshPro.Editor.csproj ├── Unity.TextMeshPro.csproj ├── Unity.Timeline.Editor.csproj ├── Unity.Timeline.csproj ├── Unity.VSCode.Editor.csproj ├── Unity.VisualStudio.Editor.csproj ├── UnityEditor.TestRunner.csproj ├── UnityEditor.UI.csproj ├── UnityEngine.TestRunner.csproj ├── UnityEngine.UI.csproj ├── UserSettings/ │ ├── EditorUserSettings.asset │ ├── Layouts/ │ │ └── default-6000.dwlt │ └── Search.settings └── asset-store-tools-editor.csproj ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/Release.yml ================================================ name: Release Workflow on: push: branches: - 'release/package' jobs: run-release-script: runs-on: ubuntu-latest timeout-minutes: 5 defaults: run: working-directory: package steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22' - name: Publish to npm id: publish run: npx --yes needle-publish-helper@next publish "." --webhook "${{ secrets.WEBHOOK_URL }}" --registry https://packages.needle.tools --access-token "${{ secrets.NPM_TOKEN }}" --create-tag release/ --llm-api-key "${{ secrets.LLM_API_KEY }}" ================================================ FILE: .gitignore ================================================ # for more stuff, see https://github.com/github/gitignore/blob/master/Unity.gitignore # Jetbrain Rider Cache .idea/ */Plugins/Editor/JetBrains* # Project specific # add all your development/MyUnityProject folders here! # Unity specific [Ll]ibrary/ [Tt]emp/ [Oo]bj/ [Bb]uild/ [Bb]uilds/ [Ll]ogs/ [Mm]emoryCaptures/ .vs/ .gradle/ *.pidb.meta *.pdb.meta *.mdb.meta sysinfo.txt crashlytics-build.properties ProBuilderSettings.json # vvvv specific # Dynamic Plugins bin/Dynamic/ *._dynamic_.* *~.xml # OS junk files [Dd]esktop.ini [Dd]esktop.ini.meta [Tt]humbs.db [Tt]humbs.db.meta ehthumbs.db *.BridgeSort .DS_Store* .Spotlight-V100 .Trashes # Visual Studio files ExportedObj/ .consulo/ *.unityproj *.suo *.tmp *.user *.userprefs *.pidb *.booproj *.svd *.pdb *.mdb *.opendb *.VC.db *.aps *.pch *.vspscc *.vssscc *_i.c *_p.c *.ncb *.suo *.tlb *.tlh *.bak *.[Cc]ache *.ilk *.log *.lib *.sbr *.sdf *.opensdf ipch/ [Bb]in [Rr]elease*/ [Dd]ynamic*/ Ankh.NoLoad #Subversion files .svn # Office Temp Files ~$* ================================================ FILE: README.md ================================================ # Shader Graph Markdown ![Unity Version Compatibility](https://img.shields.io/badge/Unity-2019.4%20%E2%80%94%206000.2-brightgreen) ## License Shader Graph Markdown is [available on the Asset Store](http://u3d.as/2was) for commercial use. Other versions (git, OpenUPM) are only allowed to be used **non-commercially** and **only if you're entitled to use Unity Personal** (the same restrictions apply). For all other uses, **please buy a commercial license** to ensure continued support! Thank you. ## What's this? **Shader Graph Markdown** supercharges your creativity by allowing you to create great-looking, easy-to-use custom shader editors right from Unity's Shader Graph. It uses "dummy properties" to draw a nice inspector for your materials, and decorates the Blackboard (where you edit properties) so that the markdown is readable and looks good. The property naming syntax is inspired by the simplicity of markdown. ![Workflow](https://github.com/needle-tools/shadergraph-markdown/wiki/Images/04_Workflow.gif) This doesn't affect your shader's functionality - it just makes it much nicer to work with it! (if someone doesn't have the package, then you just have some extra "dummy" properties) ![ShaderGUI](https://github.com/needle-tools/shadergraph-markdown/wiki/Images/01_ShaderGUI.gif) You can make properties display conditionally, that is, only if a specific keyword option is set: ![Conditional Properties](https://github.com/needle-tools/shadergraph-markdown/wiki/Images/02_ConditionalProperties.gif) The Shader Graph UI and blackboard are modified to render all "markdown dummy properties" differently, to increase legibility. ![Shader Graph Blackboard](https://github.com/needle-tools/shadergraph-markdown/wiki/Images/03_ShaderGraphUI.png) ## Install [Get on the Asset Store](http://u3d.as/2was) ## How To Use 💡 Please open the Package Readme for more information. ## Contact [needle — tools for unity](https://needle.tools) • [Discord Community](https://discord.gg/UHwvwjs9Vp) • [@NeedleTools](https://twitter.com/NeedleTools) • [@marcel_wiessler](https://twitter.com/marcel_wiessler) • [@hybridherbst](https://twitter.com/hybridherbst) ================================================ FILE: package/.npmignore ================================================ .github ================================================ FILE: package/Changelog.md ================================================ # Changelog All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [1.5.2] 2025-08-28 - fixed: Unity 6000.2+ API changes ## [1.5.1] 2022-06-24 - fixed weird crash when text displayed in DialogueWindowComplex was too long - added: refactor multiple properties in one go to the PropertyRefactor window - added: copy list of properties to be refactored from/to clipboard as plain text format - added ability to refactor property names in scripts as well ## [1.5.0] 2022-05-12 - fixed indenting issue with `VectorSlider` drawer - fixed property name warnings showing in SubGraphs - fixed `MinMax` drawer showing property swizzles by default, can now be turned on for debugging with "Show Reference Names" - fixed reflection access error due to internal API change - fixed incorrect display properties that are both inlined and conditional in the blackboard - added Markdown Tools option to "Show Reference Names" of all properties, useful for scripting and animation - added shortcut to quickly show reference names, use SHIFT while having the material inspector focussed - added an optional parameter to specify a display name for MinMax drawers - added local and global keyword state toggles to quickly see what's on - added support for `LocalKeyword` and `GlobalKeyword` in 2021.2+ to get the new info available there - added support for global keywords in conditional properties - added support for global keywords in `!REF` - added `!DRAWER` documentation to the Readme, added more info to the attribute reference - changed Markdown Tools inspector order to better reflect common workflows ## [1.4.0] 2022-04-19 - added new markup: `!TOOLTIP` or short `!TIP` will add a tooltip to the next property ## [1.3.2] 2022-03-28 - fixed generated gradient textures being compressed, now uncompressed (better quality) ## [1.3.1] 2021-12-13 - fixed issue with Surface Options not showing in some package combinations - verified basic compatiblity with Unity 2022 ## [1.3.0] 2021-11-26 - added Global Illumination mode dropdown when shader uses `_EmissionColor` and `_EmissionMap` and/or declares the `_EMISSION` keyword - add Shader Property Refactoring and Global Illumination to the Readme - updated known Unity bugs in the Readme to include version where issues are known to be resolved ## [1.3.0-pre.2] 2021-11-09 - fixed an issue with shaders on 2021.2+ that don't have blackboard categories (e.g. Amplify shaders used in URP) ## [1.3.0-pre] 2021-11-01 - fixed properties in an old default reference format not displayed in red - fixed categories not being displayed correctly in some Editor versions - fixed foldout header state for categories with duplicate names - fixed compilation errors on 2019.4 and 2021.1 - fixed material validation not always being called in 2021.2+ - changed "Debug" category to be called "Markdown Tools" - added Shader Property Refactor window (experimental) - added toggle for development options into the Markdown Tools category - added help link for Markdown Tools in foldout header - added context menu for shader property refactoring ## [1.2.0] - 2021-10-02 - bumped version to stable for AssetStore release after testing in production ## [1.2.0-pre.2] - 2021-09-15 - fixed usage with Amplify on Built-In only (no SRPs in project) - changed Amplify sample shader to use Built-In, not URP ## [1.2.0-pre] - 2021-09-15 - added support for blackboard categories in 2021.2+ - added support for keywords in subgraphs (first-level only, same as ShaderGraph itself supports) - added settings option to show/hide Markdown in the blackboard - improved performance for parsing some ShaderGraph data blocks - fixed potential errors with HDRP being in the project but not used for shaders - fixed a number of warnings - fixed various SRP compatibility and versioning issues ## [1.1.4] - 2021-06-28 - added explicitly specified inlined properties (`Base Map && _BaseColor`) to allow e.g. virtual texture slots to use inlined properties - added callback `MarkdownSGExtensions.RegisterCustomBaseShaderGUI` to specify a base shader GUI - for HDRP ShaderGraph shaders the base shader GUI was already automatically added - for Amplify, you can now use the above callback to register one per shader (e.g. `UnityEditor.Rendering.HighDefinition.LitShaderGraphGUI`) - the base shader GUI is called with all properties that haven't been processed by `MarkdownShaderGUI` ## [1.1.3] - 2021-06-15 - fixed some issues with conditions parsing and exceptions - fixed log message when editing gradients - fixed applying gradients didn't work in some cases due to UI overlaps - updated samples ## [1.1.2] - 2021-06-05 - fixed regression where gradient always saved texture on change (press Apply to see changes) - fixed gradient texture picker sometimes showing texture preview instead - added Uber shader sample with complex conditions - added GradientDrawer script (same as GradientGeneratorDrawer) and removed legacy GradientDrawer ScriptableObjects ## [1.1.0] - 2021-06-05 - fixed some inline drawing issues - fixed GradientGenerator drawer sometimes losing gradient reference - added: experimental support for Shader Graph 12.x, needs to be updated once 2021.2 beta comes out. - added: complex conditions (e.g. `[_Value > 0]`, `[_OPTION_A || _OPTION_B]`) - added: generate gradients from arbitrary textures to GradientGenerator drawer - added: quickly switch to other gradients based on generated textures - added: conditions can now be used for entire foldout groups as well ## [1.0.3] - 2021-05-18 - fixed rare NullReference exception when changing shaders on a closed material that uses MarkdownShaderGUI - fixed samples in AssetStore version ## [1.0.2] - 2021-05-03 - fixed short-named drawers going amiss after leaving Play mode with domain reload disabled - fixed indentation issues with inline drawers - improved string trimming for drawer parameters and vector slider names - added ability to use duplicated empty headers `## (1)` ## [1.0.1] - 2021-05-03 - fixed Undo not showing changed values in some cases - fixed disappearing custom drawers when exiting play mode with Domain Reload disabled - fixed vector sliders not showing animation state (red/blue overlay) - fixed hidden lightmap texture array being displayed as texture property in some case - added `### Label` to show a non-bold label without space above, useful for indented properties coming afterwards - added ability to use inline textures with custom drawers - added MultiPropertyDrawer that can draw multiple properties on a single line (experimental) ## [1.0.0] - 2021-04-28 - fixed settings location which had an incorrect space in the path - added more samples for SRP 10+, Amplify, Built-In RP - added ability to collapse/expand all foldouts in SRP 7 with alt+click - added texture property parameter to GradientGenerator - added 2021.1 / SRP 11+ support - removed Shader Graph/Core RP dependencies, works with Built-in as well - adjusted colors - changed license to Asset Store License, if SG Markdown is aquired elsewhere you many only use it for non-commercial purposes. ## [0.5.1-exp] - 2021-02-10 - fixed texture field not being square in some cases - added tiling/offset inline drawer. Usage: ``` _MyTexture && (Texture2D) _MyTexture_ST (Vector) ``` - added texture keyword helpers - bool keywords with the same name as a texture will automatically be set on texture changes. This helps with performance improvements (making variants without texture access). Usage: ``` _BumpMap (Texture2D) _BUMPMAP (Bool Keyword) // will be set automatically depending on texture being set ``` ## [0.5.0-exp] - 2021-02-02 - fixed errors on non-ShaderGraph HDRP shaders (e.g. from Amplify) - fixed UI not refreshing when changing some shader properties - fixed duplicate split line before Additional Options - changed inline texture format to "&" for inline texture and "&&" for inline texture + property - added ability to make foldouts closed by default, just append "-" ## [0.4.5-exp] - 2021-01-30 - added shorthand for InlineTexture and VectorSliders - append & to property name - fixes and better error display for incorrect drawer usage ## [0.4.4-exp] - 2021-01-18 - added auto-generation of drawer ScriptableObjects from their type name if no ScriptableObject with that name is found - added ability for drawers to specify which properties are used by them / should be hidden - added InlineTexture drawer - added MinMax drawer - added VectorSlider drawer - added samples for all of the above in the SRP10-ShaderGraph - improved performance of MarkdownShaderGUI by caching some things ## [0.4.3-exp] - 2021-01-17 - fixed confusing reference naming of bool keywords - added support for bool values in conditional properties - added indent support by prefixing properties with one or more '-' characters ## [0.4.2-exp] - 2021-01-08 - added warnings/hints in Blackboard for default reference names and names not starting with "_" - added SettingsProvider to configure blackboard modifications - added an experimental Property Wizard to bulk add ShaderGraph properties (this is useful if you want to create a ShaderGraph version of an existing shader/material) ## [0.4.1-exp] - 2021-01-04 - fixed warnings on Unity 2021 for empty asmdef - updated Readme with notes on conditional compilation ## [0.4.0-exp] - 2021-01-03 - fixed HDRP 11 compatibility - fixed multi-target samples (SRP 10+), added HDRP sample - changed foldout syntax to #, header syntax to ## and #REF/#NOTE/#DRAWER to !REF/!NOTE/!DRAWER to align better with markdown - added foldout state persistance via SessionState, defaults to expanded ## [0.3.0-exp] - 2021-01-02 - added HDRP support - added context menu helper to toggle MarkdownShaderGUI - added debug option to show original property list - changed link syntax (removed #LINK prefix, is now the same as markdown) ## [0.2.0-exp] - 2020-12-30 - added ShaderGraph UI modifications - fixed editor compatibility from 2019.4—2020.2 and URP7—URP11 ## [0.1.0-preview] - 2020-12-29 - initial version ================================================ FILE: package/Changelog.md.meta ================================================ fileFormatVersion: 2 guid: c7d45fc9f73c63d418fb011469490538 TextScriptImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Documentation/Shader Graph Markdown - Documentation.pdf.meta ================================================ fileFormatVersion: 2 guid: cec35121b0f1282428cda709b96a3a59 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Documentation.meta ================================================ fileFormatVersion: 2 guid: ae760d4113117f345b4d4d14970d35e3 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Drawers/GradientDrawer.cs ================================================ namespace Needle.ShaderGraphMarkdown { public class GradientDrawer : GradientGeneratorDrawer { // empty } } ================================================ FILE: package/Editor/Drawers/GradientDrawer.cs.meta ================================================ fileFormatVersion: 2 guid: a28cccbde1d68ef47b16bce11a507127 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Drawers/GradientGeneratorDrawer.cs ================================================ using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; namespace Needle.ShaderGraphMarkdown { public class GradientGeneratorDrawer : MarkdownMaterialPropertyDrawer { private const string DefaultTexturePropertyName = "_RampTexture"; public string texturePropertyName = DefaultTexturePropertyName; private Dictionary> mappedGradients; public override void OnDrawerGUI(MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { var targetProperty = parameters.Get(0, properties); var targetPropertyName = texturePropertyName; #if UNITY_6000_2_OR_NEWER if(targetProperty != null && targetProperty.propertyType == UnityEngine.Rendering.ShaderPropertyType.Texture) #else if(targetProperty != null && targetProperty.type == MaterialProperty.PropType.Texture) #endif { targetPropertyName = targetProperty.name; if (string.IsNullOrEmpty(targetPropertyName)) targetPropertyName = texturePropertyName; } var targetMat = materialEditor.target as Material; if (!targetMat) return; if (mappedGradients == null) mappedGradients = new Dictionary>(); // already cached bool gradientWasFound = mappedGradients.ContainsKey(targetMat) && mappedGradients[targetMat].ContainsKey(targetPropertyName); void AddToCache(Gradient gradient) { if(!mappedGradients.ContainsKey(targetMat)) mappedGradients.Add(targetMat, new Dictionary()); mappedGradients[targetMat][targetPropertyName] = gradient; gradientWasFound = true; } // check for user data in importer if (!gradientWasFound && targetProperty?.textureValue is Texture2D tex) { if (AssetDatabase.Contains(tex)) { var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(tex)); var userData = importer.userData; if (!string.IsNullOrEmpty(userData)) { var metaData = new GradientUserData(); JsonUtility.FromJsonOverwrite(userData, metaData); if (metaData.isSet && metaData.gradient != null) AddToCache(metaData.gradient); } } } // fallback: generate gradient from texture if (!gradientWasFound && targetProperty?.textureValue is Texture2D texture) { var gradient = GenerateFromTexture(texture); if(gradient != null) AddToCache(gradient); } EditorGUILayout.BeginHorizontal(); var displayName = targetProperty != null ? targetProperty.displayName : "Ramp"; // strip condition var lastIndex = displayName.LastIndexOf('['); if (lastIndex > 0) displayName = displayName.Substring(0, lastIndex); EditorGUI.BeginChangeCheck(); var existingGradient = gradientWasFound ? mappedGradients[targetMat][targetPropertyName] : new Gradient(); var newGradient = EditorGUILayout.GradientField(new GUIContent(parameters.ShowPropertyNames ? targetPropertyName : displayName, parameters.Tooltip), existingGradient); if (EditorGUI.EndChangeCheck()) AddToCache(newGradient); var placeholderContent = new GUIContent(targetProperty?.textureValue ? "ApplyMM" : "CreateMM"); var buttonRect = GUILayoutUtility.GetRect(placeholderContent, GUI.skin.button); // draw texture picker next to button // TODO renders incorrectly on 2019.4 and prevents clicking the actual button. // var haveFixedOverlayBug = false; // if(haveFixedOverlayBug && targetProperty != null && buttonRect.width > 46 + 18) // { // var controlRect = buttonRect; // controlRect.height = 16; // controlRect.y += 2; // var newObj = (Texture2D) EditorGUI.ObjectField(controlRect, targetProperty.textureValue, typeof(Texture2D), true); // if(newObj != targetProperty.textureValue) // { // Undo.RecordObjects(new Object[] { this, targetMat }, "Applied Gradient"); // targetProperty.textureValue = newObj; // mappedGradients[targetMat].Remove(targetPropertyName); // GUIUtility.ExitGUI(); // reset GUI loop so the gradient can be picked up properly // } // buttonRect.xMax -= 18; // } if (GUI.Button(buttonRect, targetProperty?.textureValue ? "Apply" : "Create")) ApplyRampTexture(targetMat, parameters.Get(0, targetPropertyName)); EditorGUILayout.EndHorizontal(); // show gradient fallback when no gradient was found - // this shouldn't happen, ever, since we're generating the gradient from the texture if (!gradientWasFound && targetProperty != null) { if(targetProperty.textureValue) { var rect = EditorGUILayout.GetControlRect(true, 8); rect.xMin += EditorGUIUtility.labelWidth + 3; rect.height -= 2; var existingTexture = targetProperty.textureValue; GUI.DrawTexture(rect, existingTexture); } else { EditorGUILayout.LabelField(" ", "None Assigned", EditorStyles.miniLabel); } } } public override IEnumerable GetReferencedProperties(MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { var textureProperty = parameters.Get(0, properties); #if UNITY_6000_2_OR_NEWER if (textureProperty != null && textureProperty.propertyType == UnityEngine.Rendering.ShaderPropertyType.Texture) #else if (textureProperty != null && textureProperty.type == MaterialProperty.PropType.Texture) #endif yield return textureProperty; } private void ApplyRampTexture(Material targetMat, string propertyName) { targetMat.SetTexture(propertyName, CreateGradientTexture(targetMat, mappedGradients[targetMat][propertyName], propertyName)); } private Gradient GenerateFromTexture(Texture2D texture) { try { // sample texture in 8 places and make a gradient from that as fallback. var rt = new RenderTexture(texture.width, texture.height, 0); var prev = RenderTexture.active; RenderTexture.active = rt; var tex2 = new Texture2D(texture.width, texture.height); Graphics.Blit(texture, rt); tex2.wrapMode = TextureWrapMode.Clamp; tex2.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0); tex2.Apply(); RenderTexture.active = prev; rt.Release(); var grad = new Gradient(); var colorKeys = new GradientColorKey[8]; var alphaKeys = new GradientAlphaKey[8]; for (int i = 0; i < 8; i++) { var x = i / (8.0f - 1); var color = tex2.GetPixelBilinear(x, 0.5f, 0); colorKeys[i] = new GradientColorKey(color, x); alphaKeys[i] = new GradientAlphaKey(color.a, x); } grad.SetKeys(colorKeys, alphaKeys); return grad; } catch { return null; } } private const int GradientTexturePixelWidth = 256; private const int GradientTexturePixelHeight = 4; // needs to be multiple of 4 for DXT1 format compression [System.Serializable] private class GradientUserData { public Gradient gradient; public bool isSet; } private static Texture2D CreateGradientTexture(Material targetMaterial, Gradient gradient, string propertyName) { Texture2D gradientTexture = new Texture2D(GradientTexturePixelWidth, GradientTexturePixelHeight, TextureFormat.ARGB32, false, false) { name = propertyName + "_Gradient", filterMode = FilterMode.Bilinear, wrapMode = TextureWrapMode.Clamp, alphaIsTransparency = true, }; for (int j = 0; j < GradientTexturePixelHeight; j++) { for (int i = 0; i < GradientTexturePixelWidth; i++) gradientTexture.SetPixel(i, j, gradient.Evaluate((float) i / GradientTexturePixelWidth)); } gradientTexture.Apply(false); gradientTexture = SaveAndGetTexture(targetMaterial, gradientTexture); // store gradient meta-info as user data var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(gradientTexture)); importer.userData = JsonUtility.ToJson(new GradientUserData() { gradient = gradient, isSet = true }, false); if (importer is TextureImporter textureImporter) textureImporter.textureCompression = TextureImporterCompression.Uncompressed; EditorUtility.SetDirty(gradientTexture); importer.SaveAndReimport(); return gradientTexture; } private static Texture2D SaveAndGetTexture(Material targetMaterial, Texture2D sourceTexture) { string targetFolder = AssetDatabase.GetAssetPath(targetMaterial); targetFolder = targetFolder.Replace(targetMaterial.name + ".mat", string.Empty); targetFolder += "Gradient Textures/"; if (!Directory.Exists(targetFolder)) { Directory.CreateDirectory(targetFolder); AssetDatabase.Refresh(); } string path = targetFolder + targetMaterial.name + sourceTexture.name + ".png"; File.WriteAllBytes(path, sourceTexture.EncodeToPNG()); AssetDatabase.Refresh(); AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceSynchronousImport); sourceTexture = (Texture2D) AssetDatabase.LoadAssetAtPath(path, typeof(Texture2D)); return sourceTexture; } } } ================================================ FILE: package/Editor/Drawers/GradientGeneratorDrawer.cs.meta ================================================ fileFormatVersion: 2 guid: 7ea34db0be65fad46b00838dbe3c23f7 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Drawers/InlineTextureDrawer.cs ================================================ using System; using System.Collections.Generic; using System.Reflection; using UnityEditor; using UnityEngine; namespace Needle.ShaderGraphMarkdown { public class InlineTextureDrawer : MarkdownMaterialPropertyDrawer { public override void OnDrawerGUI(MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { if (parameters.Count < 1) throw new ArgumentException("No parameters to " + nameof(InlineTextureDrawer) + ". Please provide _TextureProperty and optional _Float or _Color property names."); var textureProperty = parameters.Get(0, properties); if (textureProperty == null) throw new ArgumentNullException("No property named " + parameters.Get(0, "")); var extraProperty = parameters.Get(1, properties); var displayName = textureProperty.displayName; // strip condition var lastIndex = displayName.LastIndexOf('['); if (lastIndex > 0) displayName = displayName.Substring(0, lastIndex); // strip inlining var inliningIndex = displayName.IndexOf("&&", StringComparison.Ordinal); if (inliningIndex > 0) displayName = displayName.Substring(0, inliningIndex); OnDrawerGUI(materialEditor, properties, textureProperty, new GUIContent(parameters.ShowPropertyNames ? textureProperty.name : displayName, parameters.Tooltip), extraProperty); } private static Rect lastInlineTextureRect; internal static Rect LastInlineTextureRect => lastInlineTextureRect; private static MethodInfo _GetPropertyRect = null; internal void OnDrawerGUI(MaterialEditor materialEditor, MaterialProperty[] properties, MaterialProperty textureProperty, GUIContent displayContent, MaterialProperty extraProperty) { lastInlineTextureRect = Rect.zero; if(extraProperty == null) { var rect = materialEditor.TexturePropertySingleLine(displayContent, textureProperty); lastInlineTextureRect = rect; lastInlineTextureRect.x += EditorGUIUtility.labelWidth; lastInlineTextureRect.width -= EditorGUIUtility.labelWidth; } #if UNITY_6000_2_OR_NEWER else if(extraProperty.propertyType == UnityEngine.Rendering.ShaderPropertyType.Vector && (extraProperty.name.Equals(textureProperty.name + "_ST", StringComparison.Ordinal))) #else else if(extraProperty.type == MaterialProperty.PropType.Vector && (extraProperty.name.Equals(textureProperty.name + "_ST", StringComparison.Ordinal))) #endif { if (_GetPropertyRect == null) { _GetPropertyRect = typeof(MaterialEditor).GetMethod("GetPropertyRect", (BindingFlags)(-1), null, new[] { typeof(MaterialProperty), typeof(string), typeof(bool) }, null); if (_GetPropertyRect == null) { EditorGUILayout.HelpBox("Oh no, looks like an API change for MaterialEditor.GetPropertyRect – please report a bug! Thanks!", MessageType.Error); } } if (_GetPropertyRect == null) { materialEditor.TexturePropertyWithTooltip(textureProperty, displayContent, true); } else { var rect = (Rect) _GetPropertyRect.Invoke(materialEditor, new object[] { textureProperty, displayContent.text, true }); materialEditor.TextureProperty(rect, textureProperty, displayContent.text, displayContent.tooltip, true); } } else { materialEditor.TexturePropertySingleLine(displayContent, textureProperty, extraProperty); } // workaround for Unity being weird #if UNITY_6000_2_OR_NEWER if(extraProperty != null && extraProperty.propertyType == UnityEngine.Rendering.ShaderPropertyType.Texture) #else if(extraProperty != null && extraProperty.type == MaterialProperty.PropType.Texture) #endif { EditorGUILayout.Space(45); } } public override IEnumerable GetReferencedProperties(MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { var textureProperty = parameters.Get(0, properties); var extraProperty = parameters.Get(1, properties); return new[] { textureProperty, extraProperty }; } } } ================================================ FILE: package/Editor/Drawers/InlineTextureDrawer.cs.meta ================================================ fileFormatVersion: 2 guid: 75a75a904b795ef4594207826494755b MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Drawers/MarkdownMaterialPropertyDrawer.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; namespace Needle.ShaderGraphMarkdown { public abstract class MarkdownMaterialPropertyDrawer : ScriptableObject { public readonly struct DrawerParameters { public override string ToString() { return string.Join(",", parts); } private readonly string tooltip; private readonly string[] parts; private readonly bool showPropertyNames; // TODO needs handling of conditions public DrawerParameters(string[] parts) { this.parts = parts; this.tooltip = null; this.showPropertyNames = false; } public DrawerParameters(string[] parts, string tooltip) : this(parts) { this.tooltip = tooltip; } public DrawerParameters(string[] parts, string tooltip, bool showPropertyNames) : this(parts, tooltip) { this.showPropertyNames = showPropertyNames; } public int Count => parts.Length - 2; public string Tooltip => tooltip; public bool ShowPropertyNames => showPropertyNames; public string Get(int i, string defaultValue) { // first 2 parts are "!DRAWER" and the drawer object name if (parts.Length > 2 + i) return parts[2 + i]; return defaultValue; } public MaterialProperty Get(int i, MaterialProperty[] properties) { if (parts.Length < 2 + i + 1) return null; var propertyName = parts[2 + i]; var property = properties.FirstOrDefault(x => x.name.Equals(propertyName, StringComparison.Ordinal)); return property; } } /// Renders a custom drawer. Parts 0 and 1 of the parts array are "!DRAWER" and the drawer name, /// the others are optional parameters passed to the drawer. public abstract void OnDrawerGUI(MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters); public virtual void OnInlineDrawerGUI(Rect rect, MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { OnDrawerGUI(materialEditor, properties, parameters); } public virtual bool SupportsInlineDrawing => false; public virtual IEnumerable GetReferencedProperties(MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { return null; } public override string ToString() { return this.GetType().ToString(); } } } ================================================ FILE: package/Editor/Drawers/MarkdownMaterialPropertyDrawer.cs.meta ================================================ fileFormatVersion: 2 guid: 933f374647815984aab9358ae68885aa MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Drawers/MinMaxDrawer.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; namespace Needle.ShaderGraphMarkdown { public class MinMaxDrawer : MarkdownMaterialPropertyDrawer { #if UNITY_6000_2_OR_NEWER float GetValue(MaterialProperty property, char swizzle) { switch (property.propertyType) { case UnityEngine.Rendering.ShaderPropertyType.Float: case UnityEngine.Rendering.ShaderPropertyType.Range: return property.floatValue; case UnityEngine.Rendering.ShaderPropertyType.Vector: return GetValue(property.vectorValue, swizzle); default: return 0; } } void SetValue(MaterialProperty property, char swizzle, float value) { switch (property.propertyType) { case UnityEngine.Rendering.ShaderPropertyType.Float: case UnityEngine.Rendering.ShaderPropertyType.Range: property.floatValue = value; break; case UnityEngine.Rendering.ShaderPropertyType.Vector: var val = property.vectorValue; SetValue(ref val, swizzle, value); property.vectorValue = val; break; default: return; } } #else float GetValue(MaterialProperty property, char swizzle) { switch (property.type) { case MaterialProperty.PropType.Float: case MaterialProperty.PropType.Range: return property.floatValue; case MaterialProperty.PropType.Vector: return GetValue(property.vectorValue, swizzle); default: return 0; } } void SetValue(MaterialProperty property, char swizzle, float value) { switch (property.type) { case MaterialProperty.PropType.Float: case MaterialProperty.PropType.Range: property.floatValue = value; break; case MaterialProperty.PropType.Vector: var val = property.vectorValue; SetValue(ref val, swizzle, value); property.vectorValue = val; break; default: return; } } #endif float GetValue(Vector4 vector, char swizzle) { switch (swizzle) { case 'x': case 'r': return vector.x; case 'y': case 'g': return vector.y; case 'z': case 'b': return vector.z; case 'w': case 'a': return vector.w; default: return 0; } } void SetValue(ref Vector4 vector, char swizzle, float value) { switch (swizzle) { case 'x': case 'r': vector.x = value; return; case 'y': case 'g': vector.y = value; return; case 'z': case 'b': vector.z = value; return; case 'w': case 'a': vector.w = value; return; default: return; } } void GetPropertyNameAndSwizzle(string parameterName, out string propertyName, out char swizzle) { var indexOfDot = parameterName.LastIndexOf('.'); if (indexOfDot > 0 && indexOfDot == parameterName.Length - 2) { swizzle = parameterName[parameterName.Length - 1]; propertyName = parameterName.Substring(0, parameterName.Length - 2); } else { swizzle = ' '; propertyName = parameterName; } } private bool isInline = false; private Rect inlineRect; public override void OnDrawerGUI(MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { // check if these are vectors, and get the "base property" var parameterName1 = parameters.Get(0, (string) null); var parameterName2 = parameters.Get(1, (string) null); var displayName = parameters.Get(2, (string) null); if (parameterName2 != null && parameterName2.StartsWith("[", StringComparison.Ordinal)) parameterName2 = null; if (displayName != null && displayName.StartsWith("[", StringComparison.Ordinal)) displayName = null; // allow third parameter to be used for display name override // use second parameter if it's not referencing a property if (parameterName2 != null && displayName == null) { GetPropertyNameAndSwizzle(parameterName2, out var prop, out var sw); var p2 = properties.FirstOrDefault(x => x.name.Equals(prop, StringComparison.Ordinal)); if (p2 == null) { displayName = parameterName2; parameterName2 = null; } } if (parameterName2 == null) { // parameter 1 must be a vector, no swizzles // we're using zw as limits var vectorProp = properties.FirstOrDefault(x => x.name.Equals(parameterName1, StringComparison.Ordinal)); #if UNITY_6000_2_OR_NEWER if (vectorProp == null || vectorProp.propertyType != UnityEngine.Rendering.ShaderPropertyType.Vector) #else if (vectorProp == null || vectorProp.type != MaterialProperty.PropType.Vector) #endif { if(!isInline) EditorGUILayout.HelpBox(nameof(MinMaxDrawer) + ": Parameter is not a Vector property (" + parameterName1 + ")", MessageType.Error); return; } EditorGUI.showMixedValue = vectorProp.hasMixedValue; var vec = vectorProp.vectorValue; EditorGUI.BeginChangeCheck(); if (displayName == null) displayName = vectorProp.displayName; if(isInline) EditorGUI.MinMaxSlider(inlineRect, ref vec.x, ref vec.y, vec.z, vec.w); else EditorGUILayout.MinMaxSlider(new GUIContent(parameters.ShowPropertyNames ? vectorProp.name + ".xy (z-w)" : displayName, parameters.Tooltip), ref vec.x, ref vec.y, vec.z, vec.w); if (EditorGUI.EndChangeCheck()) { vectorProp.vectorValue = vec; } EditorGUI.showMixedValue = false; return; } if (parameterName1 == null || parameterName2 == null) { if(!isInline) EditorGUILayout.HelpBox(nameof(MinMaxDrawer) + ": Parameter names are incorrect (" + parameterName1 + ", " + parameterName2 + "), all: " + string.Join(",", parameters), MessageType.Error); return; } GetPropertyNameAndSwizzle(parameterName1, out var propertyName1, out var swizzle1); GetPropertyNameAndSwizzle(parameterName2, out var propertyName2, out var swizzle2); var param1 = properties.FirstOrDefault(x => x.name.Equals(propertyName1, StringComparison.Ordinal)); var param2 = properties.FirstOrDefault(x => x.name.Equals(propertyName2, StringComparison.Ordinal)); if (param1 == null || param2 == null) { if(!isInline) EditorGUILayout.HelpBox(nameof(MinMaxDrawer) + ": Parameter names are incorrect (" + propertyName1 + ", " + propertyName2 + "), all: " + string.Join(",", parameters), MessageType.Error); return; } float value1 = GetValue(param1, swizzle1); float value2 = GetValue(param2, swizzle2); if (displayName == null) displayName = param1 == param2 ? param1.displayName : param1.displayName + "-" + param2.displayName; EditorGUI.BeginChangeCheck(); if(isInline) EditorGUI.MinMaxSlider(inlineRect, ref value1, ref value2, 0.0f, 1.0f); else EditorGUILayout.MinMaxSlider(new GUIContent(parameters.ShowPropertyNames ? (param1 == param2 ? param1.name + "." + swizzle1 + swizzle2 : param1.name + "." + swizzle1 + "-" + param2.name + "." + swizzle2) : displayName, parameters.Tooltip), ref value1, ref value2, 0.0f, 1.0f); if (EditorGUI.EndChangeCheck()) { SetValue(param1, swizzle1, value1); SetValue(param2, swizzle2, value2); } } public override bool SupportsInlineDrawing => true; public override void OnInlineDrawerGUI(Rect rect, MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { isInline = true; inlineRect = rect; OnDrawerGUI(materialEditor, properties, parameters); isInline = false; } public override IEnumerable GetReferencedProperties(MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { var parameterName1 = parameters.Get(0, (string) null); var parameterName2 = parameters.Get(1, (string) null); if (parameterName2 != null && parameterName2.StartsWith("[", StringComparison.Ordinal)) parameterName2 = null; if (parameterName1 != null && parameterName2 == null) { var vectorProp = properties.FirstOrDefault(x => x.name.Equals(parameterName1, StringComparison.Ordinal)); #if UNITY_6000_2_OR_NEWER if (vectorProp == null || vectorProp.propertyType != UnityEngine.Rendering.ShaderPropertyType.Vector) #else if (vectorProp == null || vectorProp.type != MaterialProperty.PropType.Vector) #endif return null; return new [] { vectorProp }; } if (parameterName1 == null) return null; GetPropertyNameAndSwizzle(parameterName1, out var propertyName1, out _); GetPropertyNameAndSwizzle(parameterName2, out var propertyName2, out _); var param1 = properties.FirstOrDefault(x => x.name.Equals(propertyName1, StringComparison.Ordinal)); var param2 = properties.FirstOrDefault(x => x.name.Equals(propertyName2, StringComparison.Ordinal)); if (param1 != null && param2 != null) return new[] { param1, param2 }; if (param1 != null) return new[] { param1 }; if (param2 != null) return new[] { param2 }; return null; } } } ================================================ FILE: package/Editor/Drawers/MinMaxDrawer.cs.meta ================================================ fileFormatVersion: 2 guid: f014211d17b98a7429b185cd4fc08d94 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Drawers/MultiPropertyDrawer.cs ================================================ using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace Needle.ShaderGraphMarkdown { public class MultiPropertyDrawer : MarkdownMaterialPropertyDrawer { public override void OnDrawerGUI(MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { var controlRect = EditorGUILayout.GetControlRect(false, 18f); controlRect.xMin += EditorGUI.indentLevel * 15f; var previousIndent = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; OnInlineDrawerGUI(controlRect, materialEditor, properties, parameters); EditorGUI.indentLevel = previousIndent; } public override IEnumerable GetReferencedProperties(MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { for (int i = 0; i < parameters.Count; i++) { yield return parameters.Get(i, properties); } } public override bool SupportsInlineDrawing => true; public override void OnInlineDrawerGUI(Rect rect, MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { // GUI.DrawTexture(rect, Texture2D.whiteTexture); var entryCount = parameters.Count; var partRect = rect; partRect.width = partRect.width / entryCount - 5; var previousWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = partRect.width / 3; for(int i = 0; i < parameters.Count; i++) { var param = parameters.Get(i, properties); if (param == null) { throw new System.ArgumentException("Parameter " + i + " is invalid: " + parameters.Get(i, "")); } #if UNITY_6000_2_OR_NEWER if (param.propertyType == UnityEngine.Rendering.ShaderPropertyType.Texture) #else if (param.type == MaterialProperty.PropType.Texture) #endif { var miniTexRect = partRect; miniTexRect.width += 100; var prevWidth2 = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = partRect.width; materialEditor.TexturePropertyMiniThumbnail(miniTexRect, param, parameters.ShowPropertyNames ? param.name : param.displayName, null); EditorGUIUtility.labelWidth = prevWidth2; } else { materialEditor.ShaderProperty(partRect, param, parameters.ShowPropertyNames ? param.name : param.displayName); } partRect.x += partRect.width + 5; } EditorGUIUtility.labelWidth = previousWidth; } } } ================================================ FILE: package/Editor/Drawers/MultiPropertyDrawer.cs.meta ================================================ fileFormatVersion: 2 guid: 66870b09573a3704f9601c94967f7865 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Drawers/VectorSliderDrawer.cs ================================================ using System.Collections.Generic; using System.Reflection; using UnityEditor; using UnityEngine; namespace Needle.ShaderGraphMarkdown { public class VectorSliderDrawer : MarkdownMaterialPropertyDrawer { public float minValue = 0, maxValue = 1; static string[] defaultParts = new[] {"X", "Y", "Z", "W"}; public override void OnDrawerGUI(MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { if (parameters.Count < 1) throw new System.ArgumentException("No parameters to " + nameof(VectorSliderDrawer) + ". Please provide a vector property name or one or more float property names."); var vectorProperty = parameters.Get(0, properties); if (vectorProperty == null) throw new System.ArgumentNullException("No property named " + parameters.Get(0, "")); #if UNITY_6000_2_OR_NEWER switch (vectorProperty.propertyType) #else switch (vectorProperty.type) #endif { #if UNITY_6000_2_OR_NEWER case UnityEngine.Rendering.ShaderPropertyType.Vector: #else case MaterialProperty.PropType.Vector: #endif OnDrawerGUI(materialEditor, vectorProperty, new GUIContent(parameters.ShowPropertyNames ? vectorProperty.name : vectorProperty.displayName, parameters.Tooltip)); break; #if UNITY_6000_2_OR_NEWER case UnityEngine.Rendering.ShaderPropertyType.Float: case UnityEngine.Rendering.ShaderPropertyType.Range: #else case MaterialProperty.PropType.Float: case MaterialProperty.PropType.Range: #endif EditorGUILayout.LabelField("Vector Group"); EditorGUI.indentLevel++; for (int i = 0; i < parameters.Count; i++) { var param = parameters.Get(i, properties); if (param == null) { EditorGUILayout.HelpBox("Parameter " + parameters.Get(i, (string)null) + " does not exist.", MessageType.Error); continue; } materialEditor.ShaderProperty(param, parameters.ShowPropertyNames ? param.name : param.displayName); } EditorGUI.indentLevel--; break; default: throw new System.ArgumentException("Property " + vectorProperty + " isn't a vector or float property, can't draw sliders for it."); } } public void OnDrawerGUI(MaterialEditor materialEditor, MaterialProperty vectorProperty, GUIContent display) { if (vectorProperty.name.StartsWith("_Tile", System.StringComparison.Ordinal) || vectorProperty.name.StartsWith("_Tiling", System.StringComparison.Ordinal) || vectorProperty.name.EndsWith("_ST", System.StringComparison.Ordinal)) { var rect = EditorGUILayout.GetControlRect(true, 18 * 2); materialEditor.BeginAnimatedCheck(rect, vectorProperty); EditorGUI.BeginChangeCheck(); var newVector = MaterialEditor.TextureScaleOffsetProperty(rect, vectorProperty.vectorValue); if (EditorGUI.EndChangeCheck()) vectorProperty.vectorValue = newVector; materialEditor.EndAnimatedCheck(); EditorGUILayout.Space(1); return; } var firstParen = display.text.IndexOf('('); var lastParen = display.text.LastIndexOf(')'); string[] parts; if (firstParen >= 0 && lastParen >= 0 && lastParen > firstParen) { var betweenParens = display.text.Substring(firstParen + 1, lastParen - firstParen - 1); parts = betweenParens.Split(new []{',', ';'}, System.StringSplitOptions.RemoveEmptyEntries); display.text = display.text.Substring(0, firstParen).Trim(); } else { parts = defaultParts; } EditorGUILayout.LabelField(display); EditorGUI.indentLevel++; materialEditor.BeginAnimatedCheck(vectorProperty); // we need to do our own OverridePropertyColor check as this seems to be broken for Vector4 properties in Unity. if (OverridePropertyColor(materialEditor, "material." + vectorProperty.name + ".x", out var bgColor)) GUI.backgroundColor = bgColor; EditorGUI.BeginChangeCheck(); var value = vectorProperty.vectorValue; for (int i = 0; i < Mathf.Min(parts.Length, 4); i++) { materialEditor.BeginAnimatedCheck(vectorProperty); var rect = EditorGUILayout.GetControlRect(true, 18f); value[i] = EditorGUI.Slider(rect, parts[i].Trim(), value[i], minValue, maxValue); materialEditor.EndAnimatedCheck(); } if (EditorGUI.EndChangeCheck()) vectorProperty.vectorValue = value; materialEditor.EndAnimatedCheck(); EditorGUI.indentLevel--; } // ReSharper disable InconsistentNaming private static MethodInfo InAnimationRecording, IsPropertyCandidate; // ReSharper restore InconsistentNaming private static PropertyInfo rendererForAnimationMode; // This is incredibly ugly, but seems Vector4 animation colors are broken in Unity, // this gets them to work for our custom editor here. bool OverridePropertyColor(MaterialEditor editor, string path, out Color color) { if (rendererForAnimationMode == null) rendererForAnimationMode = typeof(MaterialEditor).GetProperty("rendererForAnimationMode", (BindingFlags) (-1)); if (InAnimationRecording == null) InAnimationRecording = typeof(AnimationMode).GetMethod("InAnimationRecording", (BindingFlags) (-1)); if (IsPropertyCandidate == null) IsPropertyCandidate = typeof(AnimationMode).GetMethod("IsPropertyCandidate", (BindingFlags) (-1)); if (rendererForAnimationMode == null || InAnimationRecording == null || IsPropertyCandidate == null) { color = Color.white; return false; } var target = (Object) rendererForAnimationMode.GetValue(editor); if (AnimationMode.IsPropertyAnimated(target, path)) { color = AnimationMode.animatedPropertyColor; if ((bool) InAnimationRecording.Invoke(null, null)) color = AnimationMode.recordedPropertyColor; else if ((bool) IsPropertyCandidate.Invoke(null, new object[]{ target, path })) color = AnimationMode.candidatePropertyColor; return true; } color = Color.white; return false; } public override IEnumerable GetReferencedProperties(MaterialEditor materialEditor, MaterialProperty[] properties, DrawerParameters parameters) { var vectorProperty = parameters.Get(0, properties); if (vectorProperty == null) return null; #if UNITY_6000_2_OR_NEWER switch (vectorProperty.propertyType) #else switch (vectorProperty.type) #endif { #if UNITY_6000_2_OR_NEWER case UnityEngine.Rendering.ShaderPropertyType.Vector: #else case MaterialProperty.PropType.Vector: #endif return new[] { vectorProperty }; #if UNITY_6000_2_OR_NEWER case UnityEngine.Rendering.ShaderPropertyType.Float: case UnityEngine.Rendering.ShaderPropertyType.Range: #else case MaterialProperty.PropType.Float: case MaterialProperty.PropType.Range: #endif var parameterList = new List(); for (int i = 0; i < parameters.Count; i++) { var param = parameters.Get(i, properties); if (param != null) parameterList.Add(param); } return parameterList; default: return null; } } } } ================================================ FILE: package/Editor/Drawers/VectorSliderDrawer.cs.meta ================================================ fileFormatVersion: 2 guid: 759779a26ce11b74bb58bd6adea31d07 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Drawers.meta ================================================ fileFormatVersion: 2 guid: 03d050738f62cc243826b116f6493d1c folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Extensions/LogicExpressionParser.cs ================================================ #region License and Information /***** * LogicExpressionParser.cs * * This is basically a rewrite and improvement of the ExpressionParser i wrote * in 2014 (http://wiki.unity3d.com/index.php/ExpressionParser). It's main goal * is to allow to parse logic expressions which evaluate to true or false. * * "Parse" - returns a LogicExpression instance. This should be used when * the expression should evaluate to a boolean expression. * "ParseNumber" - returns a NumberExpression instance. This should be used * when the expression should evaluate to a numeric value. * * Multiple expression objects can share one ExpressionContext which is * responsible for resolving variables and constants. * * * [History] * 2016.11.24 - project start * 2017.08.20 - first release version. * * [License] * Copyright (c) 2017 Markus Göbel (Bunny83) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * *****/ #endregion License and Information using System; using System.Collections.Generic; namespace Needle.ShaderGraphMarkdown.LogicExpressionParser { public interface ILogicResult { bool GetResult(); } public interface INumberProvider { double GetNumber(); } public interface ICommandParser { bool Parse(Parser aParser, string aCommand, out ValueProvider aResult); } #region logic gates public class CombineAnd : ILogicResult { public List inputs = new List(); public bool GetResult() { for (int i = 0; i < inputs.Count; i++) if (!inputs[i].GetResult()) return false; return true; } } public class CombineOr : ILogicResult { public List inputs = new List(); public bool GetResult() { for (int i = 0; i < inputs.Count; i++) if (inputs[i].GetResult()) return true; return false; } } public class CombineXor : ILogicResult { public List inputs = new List(); public bool GetResult() { bool res = false; for (int i = 0; i < inputs.Count; i++) res ^= inputs[i].GetResult(); return res; } } public class CombineNot : ILogicResult { public ILogicResult input; public bool GetResult() { return !input.GetResult(); } } #endregion logic gates #region Arithmetic gates public class OperationAdd : INumberProvider { public List inputs = new List(); public double GetNumber() { double res = 0d; for(int i = 0; i < inputs.Count; i++) res += inputs[i].GetNumber(); return res; } public OperationAdd(params INumberProvider[] aInputs) { for(int i = 0;i < aInputs.Length; i++) { var add = aInputs[i] as OperationAdd; if (add != null) inputs.AddRange(add.inputs); else inputs.Add(aInputs[i]); } } } public class OperationNegate : INumberProvider { public INumberProvider input; public double GetNumber() { return -input.GetNumber(); } public OperationNegate(INumberProvider aInput) { input = aInput; } } public class OperationProduct : INumberProvider { public List inputs = new List(); public double GetNumber() { double res = 1d; for (int i = 0; i < inputs.Count; i++) res *= inputs[i].GetNumber(); return res; } public OperationProduct(params INumberProvider[] aInputs) { for (int i = 0; i < aInputs.Length; i++) { var add = aInputs[i] as OperationProduct; if (add != null) inputs.AddRange(add.inputs); else inputs.Add(aInputs[i]); } } } public class OperationReciprocal : INumberProvider { public INumberProvider input; public double GetNumber() { return 1d / input.GetNumber(); } public OperationReciprocal(INumberProvider aInput) { input = aInput; } } public class OperationPower : INumberProvider { public INumberProvider value; public INumberProvider power; public double GetNumber() { return Math.Pow(value.GetNumber(), power.GetNumber()); } public OperationPower(INumberProvider aValue, INumberProvider aPower) { value = aValue; power = aPower; } } public class CustomFunction : INumberProvider { private Func m_Func; private ParameterList m_Params; public CustomFunction(Func aFunc, ParameterList aParams) { m_Func = aFunc; m_Params = aParams; } public double GetNumber() { return m_Func(m_Params); } } #endregion Arithmetic gates #region compare gates public abstract class CompareStatement : ILogicResult { public bool GetResult() { return Compare(op1.GetNumber(), op2.GetNumber()); } public INumberProvider op1; public INumberProvider op2; protected abstract bool Compare(double aOp1, double aOp2); } public class CompareEqual : CompareStatement { protected override bool Compare(double aOp1, double aOp2) { return aOp1 == aOp2; } } public class CompareNotEqual : CompareStatement { protected override bool Compare(double aOp1, double aOp2) { return aOp1 != aOp2; } } public class CompareGreater : CompareStatement { protected override bool Compare(double aOp1, double aOp2) { return aOp1 > aOp2; } } public class CompareGreaterOrEqual : CompareStatement { protected override bool Compare(double aOp1, double aOp2) { return aOp1 >= aOp2; } } public class CompareLower : CompareStatement { protected override bool Compare(double aOp1, double aOp2) { return aOp1 < aOp2; } } public class CompareLowerOrEqual : CompareStatement { protected override bool Compare(double aOp1, double aOp2) { return aOp1 <= aOp2; } } #endregion compare gates #region value providers public class ConstantNumber : INumberProvider { public double constantValue; public double GetNumber() { return constantValue; } } public class ConstantBool : ILogicResult { public bool constantValue; public bool GetResult() { return constantValue; } } public class DelegateNumber : INumberProvider { public System.Func callback; public double GetNumber() { return callback(); } } public class DelegateBool : ILogicResult { public Func callback; public bool GetResult() { return callback(); } } public class NumberToBool : ILogicResult { public INumberProvider val; public bool GetResult() { return val.GetNumber() > 0; } } public class BoolToNumber : INumberProvider { public ILogicResult val; public double GetNumber() { return val.GetResult() ? 1d : 0d; } } public class ValueProvider : INumberProvider, ILogicResult { protected ILogicResult m_BoolVal = null; protected INumberProvider m_NumberVal = null; public virtual double GetNumber() { if (m_NumberVal != null) return m_NumberVal.GetNumber(); if (m_BoolVal != null) return m_BoolVal.GetResult() ? 1 : 0; return 0d; } public virtual bool GetResult() { if (m_BoolVal != null) return m_BoolVal.GetResult(); if (m_NumberVal != null) return m_NumberVal.GetNumber() > 0d; return false; } public virtual void Set(bool aValue) { var tmp = m_BoolVal as ConstantBool; if (tmp == null) tmp = new ConstantBool(); tmp.constantValue = aValue; m_BoolVal = tmp; m_NumberVal = null; } public virtual void Set(double aValue) { var tmp = m_NumberVal as ConstantNumber; if (tmp == null) tmp = new ConstantNumber(); tmp.constantValue = aValue; m_NumberVal = tmp; m_BoolVal = null; } public virtual void Set(Func aValue) { var tmp = m_BoolVal as DelegateBool; if (tmp == null) tmp = new DelegateBool(); tmp.callback = aValue; m_BoolVal = tmp; m_NumberVal = null; } public virtual void Set(Func aValue) { var tmp = m_NumberVal as DelegateNumber; if (tmp == null) tmp = new DelegateNumber(); tmp.callback = aValue; m_NumberVal = tmp; m_BoolVal = null; } } public class ExpressionVariable : ValueProvider { public string Name { get; private set; } public ExpressionVariable(string aName) { Name = aName; } } public class ParameterList : INumberProvider { public List inputs = new List(); public ParameterList() { } public ParameterList(INumberProvider aNumber) { inputs.Add(aNumber); } public double GetNumber() { if (inputs.Count > 0) return inputs[0].GetNumber(); return 0d; } public double this[int aIndex] { get { if (aIndex < 0 || aIndex >= inputs.Count) return 0d; return inputs[aIndex].GetNumber(); } } public bool Exists(int aIndex) { return aIndex < inputs.Count && aIndex >= 0; } } public class LogicExpression : ILogicResult { public bool GetResult() { return expressionTree.GetResult(); } public ExpressionContext Context { get { return context; } } protected ILogicResult expressionTree; protected ExpressionContext context; public ExpressionVariable this[string aVarName] { get { return context[aVarName]; } } public LogicExpression(ILogicResult aExpressionTree, ExpressionContext aContext) { expressionTree = aExpressionTree; context = aContext; } } public class NumberExpression : INumberProvider { public double GetNumber() { return expressionTree.GetNumber(); } public ExpressionContext Context { get { return context; } } protected INumberProvider expressionTree; protected ExpressionContext context; public ExpressionVariable this[string aVarName] { get { return context[aVarName]; } } public NumberExpression(INumberProvider aExpressionTree, ExpressionContext aContext) { expressionTree = aExpressionTree; context = aContext; } } #endregion value providers #region Expression & Parsing Context public class ExpressionContext { public Dictionary Variables => variables; protected Dictionary variables = new Dictionary(); public ExpressionContext() : this(true) { } public ExpressionContext(bool aAddDefaultConstants) { if (aAddDefaultConstants) AddMathConstants(); } public void AddMathConstants() { this["e"].Set(Math.E); this["pi"].Set(Math.PI); this["r2d"].Set(180d / Math.PI); this["d2r"].Set(Math.PI / 180d); } public virtual ExpressionVariable FindVariable(string aVarName) { ExpressionVariable res; if (variables.TryGetValue(aVarName, out res)) { return res; } return null; } public virtual ExpressionVariable GetVariable(string aVarName) { ExpressionVariable res; if (!variables.TryGetValue(aVarName, out res)) { res = new ExpressionVariable(aVarName); variables.Add(res.Name, res); } return res; } public virtual ExpressionVariable this[string aVarName] { get { return GetVariable(aVarName); } } } public class ParsingContext { private List m_BracketHeap = new List(); private List m_Commands = new List(); private List m_CommandParser = new List(); private Dictionary> m_Functions = new Dictionary>(); public ParsingContext() : this(true) { } public ParsingContext(bool aAddMathMethods) { if (aAddMathMethods) AddMathFunctions(); } public void AddMathFunctions() { var rnd = new System.Random(); AddFunction("sin", (p) => Math.Sin(p[0])); AddFunction("cos", (p) => Math.Cos(p[0])); AddFunction("tan", (p) => Math.Tan(p[0])); AddFunction("asin", (p) => Math.Asin(p[0])); AddFunction("acos", (p) => Math.Acos(p[0])); AddFunction("atan", (p) => Math.Atan(p[0])); AddFunction("atan2", (p) => Math.Atan2(p[0], p[1])); AddFunction("abs", (p) => Math.Abs(p[0])); AddFunction("floor", (p) => Math.Floor(p[0])); AddFunction("ceil", (p) => Math.Ceiling(p[0])); AddFunction("round", (p) => Math.Round(p[0])); AddFunction("min", (p) => Math.Min(p[0], p[1])); AddFunction("max", (p) => Math.Max(p[0], p[1])); AddFunction("exp", (p) => Math.Exp(p[0])); AddFunction("ln", (p) => Math.Log(p[0])); AddFunction("log10", (p) => Math.Log10(p[0])); AddFunction("sqrt", (p) => Math.Sqrt(p[0])); AddFunction("pow", (p) => Math.Pow(p[0], p[1])); AddFunction("lerp", (p) => { var s = p[0]; return s + (p[1] - s) * p[2]; }); AddFunction("rand", (p) => { var p0 = p[0]; if (p.Exists(1)) return p0 + rnd.NextDouble()*(p[1]-p0); else return rnd.NextDouble()*p[0]; }); AddFunction("clamp", (p) => { var p0 = p[0]; var p1 = p[0]; var p2 = p[0]; return p0 < p1 ? p1 : (p0 > p2) ? p2 : p0; }); AddFunction("clamp01", (p) => { var p0 = p[0]; return p0 < 0d ? 0d : (p0 > 1d) ? 1d : p0; }); } public static int FindClosingBracket(string aText, int aStart, char aOpen, char aClose) { int counter = 0; for (int i = aStart; i < aText.Length; i++) { if (aText[i] == aOpen) counter++; if (aText[i] == aClose) counter--; if (counter == 0) return i; } return -1; } private void SubstitudeBracket(ref string aExpression, int aIndex) { int closing = FindClosingBracket(aExpression, aIndex, '(', ')'); if (closing > aIndex + 1) { string inner = aExpression.Substring(aIndex + 1, closing - aIndex - 1); m_BracketHeap.Add(inner); string sub = "$B" + (m_BracketHeap.Count - 1) + ";"; aExpression = aExpression.Substring(0, aIndex) + sub + aExpression.Substring(closing + 1); } else throw new ParseException("Bracket not closed!"); } private void SubstitudeCommand(Parser aParser, ref string aExpression, int aIndex) { int closing = FindClosingBracket(aExpression, aIndex, '{', '}'); if (closing > aIndex + 1) { string inner = aExpression.Substring(aIndex + 1, closing - aIndex - 1).Trim(); string sub = "$C" + (m_Commands.Count) + ";"; m_Commands.Add(ParseCommand(aParser, inner)); aExpression = aExpression.Substring(0, aIndex) + sub + aExpression.Substring(closing + 1); } else throw new ParseException("Bracket not closed!"); } protected virtual ValueProvider ParseCommand(Parser aParser, string aCommand) { for (int i = 0; i < m_CommandParser.Count; i++) { ValueProvider result; if (m_CommandParser[i].Parse(aParser, aCommand, out result)) return result; } return new ValueProvider(); } public virtual void Preprocess(Parser aParser, ref string aExpression) { aExpression = aExpression.Trim(); int index = aExpression.IndexOf('{'); while (index >= 0) { SubstitudeCommand(aParser, ref aExpression, index); index = aExpression.IndexOf('{'); } index = aExpression.IndexOf('('); while (index >= 0) { SubstitudeBracket(ref aExpression, index); index = aExpression.IndexOf('('); } } private bool ParseToken(ref string aExpression, out char aTokenType, out int aIndex) { int index2a = aExpression.IndexOf('$'); int index2b = aExpression.IndexOf(';'); if (index2a >= 0 && index2b >= 3) { aTokenType = aExpression[index2a + 1]; var inner = aExpression.Substring(index2a + 2, index2b - index2a - 2); if (int.TryParse(inner, out aIndex) && aIndex >= 0) { return true; } else throw new ParseException("Can't parse bracket substitution token"); } aTokenType = '\0'; aIndex = -1; return false; } public string GetBracket(ref string aExpression) { char type; int index; if (ParseToken(ref aExpression, out type, out index) && type == 'B' && index < m_BracketHeap.Count) return m_BracketHeap[index]; return null; } public ValueProvider GetCommand(ref string aExpression) { char type; int index; if (ParseToken(ref aExpression, out type, out index) && type == 'C' && index < m_Commands.Count) return m_Commands[index]; return null; } public Func GetFunction(string fName) { Func f; if (m_Functions.TryGetValue(fName, out f)) return f; return null; } public void AddFunction(string aName, Func aFunc) { if (m_Functions.ContainsKey(aName)) m_Functions[aName] = aFunc; else m_Functions.Add(aName, aFunc); } } #endregion Expression & Parsing Context public class Parser { private ParsingContext m_ParsingContext; private ExpressionContext context; public ParsingContext ParsingContext { get { return m_ParsingContext; } set { m_ParsingContext = value; } } public ExpressionContext ExpressionContext { get { return context; } set { context = value; } } public Parser() : this(new ParsingContext(), new ExpressionContext()) { } public Parser(ParsingContext aParsingContext) : this(aParsingContext, new ExpressionContext()) { } public Parser(ParsingContext aParsingContext, ExpressionContext context) { this.context = context; m_ParsingContext = aParsingContext; } private ILogicResult ParseLogicResult(string aExpression, int aMaxRecursion) { --aMaxRecursion; m_ParsingContext.Preprocess(this, ref aExpression); aExpression = aExpression.Trim(); if (aExpression.Contains(" or ")) { string[] parts = aExpression.Split(new string[] { " or " }, StringSplitOptions.None); List exp = new List(parts.Length); for (int i = 0; i < parts.Length; i++) { string s = parts[i].Trim(); if (!string.IsNullOrEmpty(s)) exp.Add(ParseLogicResult(s, aMaxRecursion)); } if (exp.Count == 1) return exp[0]; return new CombineOr { inputs = exp }; } else if (aExpression.Contains("||")) { string[] parts = aExpression.Split(new string[] { "||" }, StringSplitOptions.None); List exp = new List(parts.Length); for (int i = 0; i < parts.Length; i++) { string s = parts[i].Trim(); if (!string.IsNullOrEmpty(s)) exp.Add(ParseLogicResult(s, aMaxRecursion)); } if (exp.Count == 1) return exp[0]; return new CombineOr { inputs = exp }; } if (aExpression.Contains(" xor ")) { string[] parts = aExpression.Split(new string[] { " xor " }, StringSplitOptions.None); List exp = new List(parts.Length); for (int i = 0; i < parts.Length; i++) { string s = parts[i].Trim(); if (!string.IsNullOrEmpty(s)) exp.Add(ParseLogicResult(s, aMaxRecursion)); } if (exp.Count == 1) return exp[0]; return new CombineXor { inputs = exp }; } else if (aExpression.Contains("^")) { string[] parts = aExpression.Split(new string[] { "^" }, StringSplitOptions.None); List exp = new List(parts.Length); for (int i = 0; i < parts.Length; i++) { string s = parts[i].Trim(); if (!string.IsNullOrEmpty(s)) exp.Add(ParseLogicResult(s, aMaxRecursion)); } if (exp.Count == 1) return exp[0]; return new CombineXor { inputs = exp }; } else if (aExpression.Contains(" and ")) { string[] parts = aExpression.Split(new string[] { " and " }, StringSplitOptions.None); List exp = new List(parts.Length); for (int i = 0; i < parts.Length; i++) { string s = parts[i].Trim(); if (!string.IsNullOrEmpty(s)) exp.Add(ParseLogicResult(s, aMaxRecursion)); } if (exp.Count == 1) return exp[0]; return new CombineAnd { inputs = exp }; } else if (aExpression.Contains("&&")) { string[] parts = aExpression.Split(new string[] { "&&" }, StringSplitOptions.None); List exp = new List(parts.Length); for (int i = 0; i < parts.Length; i++) { string s = parts[i].Trim(); if (!string.IsNullOrEmpty(s)) exp.Add(ParseLogicResult(s, aMaxRecursion)); } if (exp.Count == 1) return exp[0]; return new CombineAnd { inputs = exp }; } else if (aExpression.Contains("==")) { string[] parts = aExpression.Split(new string[] { "==" }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 2) throw new ParseException("== operator needs an expression on either side"); return new CompareEqual { op1 = ParseNumber(parts[0].Trim(), aMaxRecursion), op2 = ParseNumber(parts[1].Trim(), aMaxRecursion) }; } else if (aExpression.Contains("!=")) { string[] parts = aExpression.Split(new string[] { "!=" }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 2) throw new ParseException("!= operator needs an expression on either side"); return new CompareNotEqual { op1 = ParseNumber(parts[0].Trim(), aMaxRecursion), op2 = ParseNumber(parts[1].Trim(), aMaxRecursion) }; } else if (aExpression.Contains(">=")) { string[] parts = aExpression.Split(new string[] { ">=" }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 2) throw new ParseException(">= operator needs an expression on either side"); return new CompareGreaterOrEqual { op1 = ParseNumber(parts[0].Trim(), aMaxRecursion), op2 = ParseNumber(parts[1].Trim(), aMaxRecursion) }; } else if (aExpression.Contains(">")) { string[] parts = aExpression.Split(new string[] { ">" }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 2) throw new ParseException("> operator needs an expression on either side"); return new CompareGreater { op1 = ParseNumber(parts[0].Trim(), aMaxRecursion), op2 = ParseNumber(parts[1].Trim(), aMaxRecursion) }; } else if (aExpression.Contains("<=")) { string[] parts = aExpression.Split(new string[] { "<=" }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 2) throw new ParseException("<= operator needs an expression on either side"); return new CompareLowerOrEqual { op1 = ParseNumber(parts[0].Trim(), aMaxRecursion), op2 = ParseNumber(parts[1].Trim(), aMaxRecursion) }; } else if (aExpression.Contains("<")) { string[] parts = aExpression.Split(new string[] { "<" }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 2) throw new ParseException("< operator needs an expression on either side"); return new CompareLower { op1 = ParseNumber(parts[0].Trim(), aMaxRecursion), op2 = ParseNumber(parts[1].Trim(), aMaxRecursion) }; } else if (aExpression.StartsWith("not ")) { return new CombineNot { input = ParseLogicResult(aExpression.Substring(4), aMaxRecursion) }; } else if (aExpression.StartsWith("!")) { return new CombineNot { input = ParseLogicResult(aExpression.Substring(1), aMaxRecursion) }; } else if (aExpression == "true") { return new ConstantBool { constantValue = true }; } else if (aExpression == "false") { return new ConstantBool { constantValue = false }; } string bracketContent = m_ParsingContext.GetBracket(ref aExpression); if (!string.IsNullOrEmpty(bracketContent)) { return ParseLogicResult(bracketContent, aMaxRecursion); } ValueProvider value = m_ParsingContext.GetCommand(ref aExpression); if (value != null) { return value; } if (ValidIdentifier(aExpression)) { return context.GetVariable(aExpression.Trim()); } if (aMaxRecursion > 0) return new NumberToBool { val = ParseNumber(aExpression, aMaxRecursion) }; throw new ParseException("Unexpected end / expression"); } // ParseLogicResult(string, int) private INumberProvider ParseNumber(string aExpression, int aMaxRecursion) { --aMaxRecursion; m_ParsingContext.Preprocess(this, ref aExpression); aExpression = aExpression.Trim(); if (aExpression.Contains(",")) { string[] parts = aExpression.Split(','); var paramList = new ParameterList(); paramList.inputs.Capacity = parts.Length; for (int i = 0; i < parts.Length; i++) { string s = parts[i].Trim(); if (!string.IsNullOrEmpty(s)) paramList.inputs.Add(ParseNumber(s, aMaxRecursion)); } if (paramList.inputs.Count == 1) return paramList.inputs[0]; return paramList; } else if (aExpression.Contains("+")) { string[] parts = aExpression.Split('+'); List exp = new List(parts.Length); for (int i = 0; i < parts.Length; i++) { string s = parts[i].Trim(); if (!string.IsNullOrEmpty(s)) exp.Add(ParseNumber(s, aMaxRecursion)); } if (exp.Count == 1) return exp[0]; return new OperationAdd(exp.ToArray()); } else if (aExpression.Contains("-")) { string[] parts = aExpression.Split('-'); List exp = new List(parts.Length); if (!string.IsNullOrEmpty(parts[0].Trim())) exp.Add(ParseNumber(parts[0], aMaxRecursion)); for (int i = 1; i < parts.Length; i++) { string s = parts[i].Trim(); if (!string.IsNullOrEmpty(s)) exp.Add(new OperationNegate(ParseNumber(s, aMaxRecursion))); } if (exp.Count == 1) return exp[0]; return new OperationAdd(exp.ToArray()); } else if (aExpression.Contains("*")) { string[] parts = aExpression.Split('*'); List exp = new List(parts.Length); for (int i = 0; i < parts.Length; i++) { exp.Add(ParseNumber(parts[i], aMaxRecursion)); } if (exp.Count == 1) return exp[0]; return new OperationProduct(exp.ToArray()); } else if (aExpression.Contains("/")) { string[] parts = aExpression.Split('/'); List exp = new List(parts.Length); if (!string.IsNullOrEmpty(parts[0].Trim())) exp.Add(ParseNumber(parts[0], aMaxRecursion)); for (int i = 1; i < parts.Length; i++) { string s = parts[i].Trim(); if (!string.IsNullOrEmpty(s)) exp.Add(new OperationReciprocal(ParseNumber(s, aMaxRecursion))); } return new OperationProduct(exp.ToArray()); } else if (aExpression.Contains("^")) { int pos = aExpression.IndexOf('^'); var val = ParseNumber(aExpression.Substring(0, pos), aMaxRecursion); var pow = ParseNumber(aExpression.Substring(pos + 1), aMaxRecursion); return new OperationPower(val, pow); } int p = aExpression.IndexOf("$B", StringComparison.Ordinal); if (p > 0) { string fName = aExpression.Substring(0, p).Trim(); Func func = m_ParsingContext.GetFunction(fName); if (func != null) { aExpression = aExpression.Substring(p); string inner = m_ParsingContext.GetBracket(ref aExpression); var param = ParseNumber(inner, aMaxRecursion); if (param is ParameterList) return new CustomFunction(func, (ParameterList)param); return new CustomFunction(func, new ParameterList(param)); } } string bracketContent = m_ParsingContext.GetBracket(ref aExpression); if (!string.IsNullOrEmpty(bracketContent)) { return ParseNumber(bracketContent, aMaxRecursion); } ValueProvider value = m_ParsingContext.GetCommand(ref aExpression); if (value != null) { return value; } double doubleValue; if (double.TryParse(aExpression.Trim(), out doubleValue)) { return new ConstantNumber { constantValue = doubleValue }; } if (ValidIdentifier(aExpression)) { return context.GetVariable(aExpression.Trim()); } if (aMaxRecursion > 0) return new BoolToNumber { val = ParseLogicResult(aExpression, aMaxRecursion) }; throw new ParseException("Unexpected end / expression"); } //ParseNumber(string, int) public LogicExpression Parse(string aExpressionString, ExpressionContext aContext = null) { var old = context; if (aContext != null) context = aContext; ILogicResult tree = ParseLogicResult(aExpressionString, 20); LogicExpression res = new LogicExpression(tree, context); if (aContext != null) context = old; return res; } public NumberExpression ParseNumber(string aExpressionString, ExpressionContext aContext = null) { var old = context; if (aContext != null) context = aContext; INumberProvider tree = ParseNumber(aExpressionString, 20); NumberExpression res = new NumberExpression(tree, context); if (aContext != null) context = old; return res; } private static bool ValidIdentifier(string aExpression) { aExpression = aExpression.Trim(); if (string.IsNullOrEmpty(aExpression)) return false; if (aExpression.Length < 1) return false; if (aExpression.Contains(" ")) return false; //if (!"abcdefghijklmnopqrstuvwxyz_".Contains(aExpression.Substring(0, 1).ToLower())) // return false; // this is better for performance and garbage generation char firstLetter = char.ToLower(aExpression[0]); if (firstLetter != '_' && (firstLetter < 'a' || firstLetter > 'z')) return false; return true; } } public class ParseException : Exception { public ParseException(string aMessage) : base(aMessage) { } } } ================================================ FILE: package/Editor/Extensions/LogicExpressionParser.cs.meta ================================================ fileFormatVersion: 2 guid: 71cf27b2ba1037a4f8f154d1914b490a MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Extensions/ShaderRefactoringWindow.cs ================================================ #if UNITY_2020_3_OR_NEWER #define HAVE_UITOOLKIT_HELPBOX #endif using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UIElements; namespace Needle.ShaderGraphMarkdown { public class ShaderRefactoringWindow : EditorWindow { public ShaderRefactoringData data; [MenuItem("Window/Needle/Refactor Shader Properties")] private static void ShowWindow() { Show("", ""); } private static Texture2D iconHelp; private void ShowButton(Rect rect) { if(!iconHelp) iconHelp = EditorGUIUtility.IconContent("icons/_help" + (EditorGUIUtility.pixelsPerPoint > 1.0 ? "@2x" : "") + ".png").image as Texture2D; if (GUI.Button(rect, new GUIContent(iconHelp), "IconButton")) EditorUtility.OpenWithDefaultApp(MarkdownShaderGUI.PropertyRefactorDocumentationUrl); } [MenuItem("CONTEXT/Material/Refactor Shader Properties", false, 701)] [MenuItem("CONTEXT/Shader/Refactor Shader Properties", false, 701)] private static void ShowFromContextMenu(MenuCommand command) { // TODO can we figure out which material property is currently selected? // Debug.Log("Keyboard Control: " + GUIUtility.keyboardControl); // TODO can we add a context menu to each property? if(command.context is Material mat0) Show(AssetDatabase.GetAssetPath(mat0.shader), ""); if(command.context is Shader shader0) Show(AssetDatabase.GetAssetPath(shader0), ""); if (Selection.activeObject is Material material1) Show(AssetDatabase.GetAssetPath(material1.shader), ""); if (Selection.activeObject is Shader shader1) Show(AssetDatabase.GetAssetPath(shader1), ""); } public static void Show(string shaderAssetPath, string inputReferenceName) { var wnd = GetWindow(); wnd.data = new ShaderRefactoringData() { shaderPath = shaderAssetPath, shader = AssetDatabase.LoadAssetAtPath(shaderAssetPath), refactoringData = new List() { new ShaderPropertyRefactoringData() { sourceReferenceName = inputReferenceName, targetReferenceName = inputReferenceName } }, }; wnd.minSize = new Vector2(800, 200); wnd.Show(); } private static List GetAllAssets() where T:UnityEngine.Object { return AssetDatabase .FindAssets("t:" + typeof(T).Name) .Select(AssetDatabase.GUIDToAssetPath) .SelectMany(AssetDatabase.LoadAllAssetsAtPath) .Where(x => x && x.GetType() == typeof(T)) .Select(x => (T) x) .ToList(); } class StringPropertyFieldWithDropdown : VisualElement { public StringPropertyFieldWithDropdown(ShaderRefactoringWindow window, SerializedObject so, SerializedProperty prop, string label, Action dropdownEvent, Action changeEvent) { style.flexDirection = FlexDirection.Row; var refactorFrom = new PropertyField(prop, label) { style = { flexGrow = 1 } }; #if UNITY_2020_2_OR_NEWER refactorFrom.RegisterValueChangeCallback(evt => changeEvent?.Invoke()); #else refactorFrom.RegisterCallback>(evt => changeEvent?.Invoke()); #endif refactorFrom.Bind(so); Add(refactorFrom); var btn = new Button(dropdownEvent) { tooltip = "Select Property", style = { paddingLeft = 1, paddingRight = 1 } }; var btnContent = new VisualElement(); btnContent.AddToClassList("unity-base-popup-field__arrow"); btn.Add(btnContent); Add(btn); } } private VisualElement popupFieldContainer; private SerializedObject so; private static readonly List<(string referenceName, string displayName)> commonNamePairs = new List<(string, string)>() { ("BiRP/_MainTex", "Albedo"), ("BiRP/_BumpMap", "Normal Map"), ("BiRP/_Color", "Color"), ("URP/_BaseMap", "Albedo"), ("URP/_BumpMap", "Normal Map"), ("URP/_BaseColor", "Color"), ("HDRP/_BaseColorMap", "Albedo"), ("HDRP/_BumpMap", "Normal Map"), ("HDRP/_BaseColor", "Color"), ("_Smoothness", "Smoothness"), ("_Metallic", "Metallic"), ("_Cutoff", "Alpha Cutoff"), }; private void AddPropertyItem() { } private void CreateGUI() { titleContent = new GUIContent("Refactor Shader Properties"); var splitter = new VisualElement() { style = { flexDirection = FlexDirection.Row } }; var left = new VisualElement() { style = { width = Length.Percent(50), marginRight = 20 } }; var right = new VisualElement() { style = { width = Length.Percent(50), marginLeft = 20 } }; var rightActionsContainer = new VisualElement(); var leftActionsContainer = new VisualElement(); if (so == null || so.targetObject != this) so = new SerializedObject(this); so.Update(); var prop = so.FindProperty(nameof(data)); var propField = new PropertyField(prop.FindPropertyRelative(nameof(ShaderRefactoringData.shader))); // #if UNITY_2020_2_OR_NEWER // propField.RegisterValueChangeCallback(evt => UpdatePopupField()); // #else // propField.RegisterCallback>(evt => UpdatePopupField()); // #endif propField.Bind(so); var splitter2 = new VisualElement() { style = { flexDirection = FlexDirection.Row } }; splitter2.Add(propField); // List implementation var list = new ListView(data.refactoringData, 24, makeItem: () => { var splitter3 = new VisualElement() { style = { flexDirection = FlexDirection.Row } }; var left2 = new VisualElement() { style = { width = Length.Percent(50), marginRight = 20 }, name = "LeftSection" }; var right2 = new VisualElement() { style = { width = Length.Percent(50), marginLeft = 20 }, name = "RightSection" }; // left.Add(new TextField() { name = "From" }); // right.Add(new TextField() { name = "To" }); splitter3.Add(left2); splitter3.Add(right2); return splitter3; }, bindItem: (element, i) => { while (data.refactoringData.Count < i) data.refactoringData.Add(new ShaderPropertyRefactoringData()); var el = data.refactoringData[i]; if (el == null) { el = new ShaderPropertyRefactoringData() { }; data.refactoringData[i] = el; so.Update(); } var from = element.Q("LeftSection"); var to = element.Q("RightSection"); var p1 = prop.FindPropertyRelative(nameof(ShaderRefactoringData.refactoringData)).GetArrayElementAtIndex(i); var refactorFrom = new StringPropertyFieldWithDropdown(this, so, p1.FindPropertyRelative(nameof(ShaderPropertyRefactoringData.sourceReferenceName)), "Source Property",(() => { var properties = new List<(string referenceName, string displayName)>(); if(data.shader) { var propertyCount = data.shader.GetPropertyCount(); for (int j = 0; j < propertyCount; j++) { #if UNITY_6000_2_OR_NEWER if (data.shader.GetPropertyFlags(j).HasFlag(ShaderPropertyFlags.HideInInspector)) continue; #else if(ShaderUtil.IsShaderPropertyHidden(data.shader, j)) continue; #endif properties.Add((data.shader.GetPropertyName(j), data.shader.GetPropertyDescription(j))); } } var menu = new GenericMenu(); if (!data.shader) menu.AddItem(new GUIContent("Set a Shader to pick properties from"), false, null); else menu.AddItem(new GUIContent("Shader Properties"), false, null); foreach(var tuple in properties) { menu.AddItem(new GUIContent(tuple.referenceName + " (" + tuple.displayName + ")"), tuple.referenceName == el.sourceReferenceName, o => { var t = o as string; el.sourceReferenceName = t; }, tuple.referenceName); } menu.ShowAsContext(); }), () => { leftActionsContainer.SetEnabled( !string.IsNullOrWhiteSpace(el.sourceReferenceName) && el.sourceReferenceName.Length >= 2); }); var refactorTo = new StringPropertyFieldWithDropdown(this, so, p1.FindPropertyRelative(nameof(ShaderPropertyRefactoringData.targetReferenceName)), "Replace With", () => { var shaderInfo = ShaderUtil.GetAllShaderInfo(); List<(string referenceName, string displayName)> properties = shaderInfo.SelectMany(x => { // exclude hidden shaders? if (x.name.StartsWith("Hidden/")) return Enumerable.Empty<(string, string)>(); var shader = Shader.Find(x.name); #if UNITY_6000_2_OR_NEWER var propertyCount = shader.GetPropertyCount(); #else var propertyCount = ShaderUtil.GetPropertyCount(shader); #endif return Enumerable.Range(0, propertyCount) #if UNITY_6000_2_OR_NEWER .Where(idx => !shader.GetPropertyFlags(idx).HasFlag(ShaderPropertyFlags.HideInInspector)) #else .Where(idx => !ShaderUtil.IsShaderPropertyHidden(shader, idx)) #endif // .Where(x => // { // if (ShaderUtil.GetPropertyName(shader, x) == "_A") // { // Debug.Log("Shader has property " + "_A" + ": " + shader, shader); // } // return true; // }) #if UNITY_6000_2_OR_NEWER .Select(idx => (shader.GetPropertyName(idx), shader.GetPropertyDescription(idx))); #else .Select(idx => (ShaderUtil.GetPropertyName(shader, idx), ShaderUtil.GetPropertyDescription(shader, idx))); #endif }) .ToLookup(x => x.Item1) .OrderByDescending(x => x.Count()) // .Where(x => // { // if(x.Count() > 10) // Debug.Log("Property " + x.Key + " found in " + string.Join("\n", x)); // return true; // }) .Select(x => (x.Key, x.Count().ToString())) .ToList(); const int ItemsInMainSection = 25; var menu = new GenericMenu(); menu.AddItem(new GUIContent("Common Names"), false, null); foreach (var tuple in commonNamePairs) { menu.AddItem(new GUIContent(tuple.referenceName + " (" + tuple.displayName + ")"), tuple.referenceName == el.targetReferenceName, o => { var t = o as string; el.targetReferenceName = t; }, tuple.referenceName); } menu.AddSeparator(""); menu.AddItem(new GUIContent("Most Used Properties"), false, null); foreach(var tuple in properties.Take(ItemsInMainSection)) { menu.AddItem(new GUIContent(tuple.referenceName + "\t(" + tuple.displayName + ")"), tuple.referenceName == el.targetReferenceName, o => { var t = o as string; el.targetReferenceName = t; }, tuple.referenceName); } menu.AddSeparator(""); menu.AddItem(new GUIContent("All Properties"), false, null); foreach (var tuple in properties.Select(x => (char.ToUpperInvariant(x.referenceName.TrimStart('_').FirstOrDefault()),x)).OrderBy(x => x.Item1)) { menu.AddItem(new GUIContent("Alphabetic/" + tuple.Item1 + "/" + tuple.x.referenceName + "\t(" + tuple.x.displayName + ")"), tuple.x.referenceName == el.targetReferenceName, o => { var t = o as string; el.targetReferenceName = t; }, tuple.x.referenceName); } menu.ShowAsContext(); }, () => { rightActionsContainer.SetEnabled( !string.IsNullOrWhiteSpace(el.sourceReferenceName) && el.sourceReferenceName.Length >= 2 && !string.IsNullOrWhiteSpace(el.targetReferenceName) && el.targetReferenceName.Length >= 2); }); from.Clear(); to.Clear(); from.Add(refactorFrom); to.Add(refactorTo); }) { style = { flexShrink = 500, flexGrow = 1 }}; #if UNITY_2021_2_OR_NEWER list.showAddRemoveFooter = true; list.showAlternatingRowBackgrounds = AlternatingRowBackground.None; list.reorderMode = ListViewReorderMode.Simple; list.showBoundCollectionSize = true; list.showBorder = true; #else var addRemove = new VisualElement() { style = { flexDirection = FlexDirection.Row } }; addRemove.Add(new Button(() => { #if UNITY_2020_1_OR_NEWER var selected = list.selectedItems; foreach (ShaderPropertyRefactoringData sel in selected) data.refactoringData.Remove(sel); #else data.refactoringData.Remove(list.selectedItem as ShaderPropertyRefactoringData); #endif so.Update(); list.Bind(so); list.Refresh(); }) { text = "-" }); addRemove.Add(new Button(() => { data.refactoringData.Add(new ShaderPropertyRefactoringData()); so.Update(); list.Bind(so); list.Refresh(); }) { text = "+" }); #endif list.Bind(so); var toolbar = new Toolbar(); toolbar.Add(new ToolbarButton(() => { GUIUtility.systemCopyBuffer = string.Join("\n", data.refactoringData.Select(x => x.sourceReferenceName + ", " + x.targetReferenceName)); }) { text = "Copy Property List to Clipboard" }); toolbar.Add(new ToolbarButton(() => { var str = GUIUtility.systemCopyBuffer; var lines = str.Split('\n'); data.refactoringData.Clear(); foreach (var t in lines) { var parts = t.Split(new char[] { ',', ';' }); if (parts.Length != 2) continue; data.refactoringData.Add(new ShaderPropertyRefactoringData() { sourceReferenceName = parts[0].Trim(), targetReferenceName = parts[1].Trim() }); } so.Update(); #if UNITY_2021_2_OR_NEWER list.Rebuild(); #else list.Bind(so); #endif }) { text = "Paste Property List from Clipboard", tooltip = "Plain text with comma separation: \"source, target\""}); rootVisualElement.Add(toolbar); rootVisualElement.Add(splitter2); rootVisualElement.Add(list); #if !UNITY_2021_2_OR_NEWER rootVisualElement.Add(addRemove); #endif rootVisualElement.Add(splitter); splitter.Add(left); splitter.Add(right); so.ApplyModifiedProperties(); rightActionsContainer.Add(new Label("Replace") { style = { marginTop = 10, marginLeft = 3, unityFontStyleAndWeight = FontStyle.Bold}}); rightActionsContainer.Add(new Button(() => { FixMaterialsAndShaders(); FixAnimationClips(); FixScripts(); }) { text = "Replace all Usages", tooltip = "Replace in materials, shaders and animation clips", style = { height = 30 } }); rightActionsContainer.Add(new Button(FixMaterialsAndShaders) { text = "Replace in materials and shaders" }); rightActionsContainer.Add(new Button(FixAnimationClips) { text = "Replace in animations" }); rightActionsContainer.Add(new Button(FixScripts) { text = "Replace in scripts" }); #if HAVE_UITOOLKIT_HELPBOX rightActionsContainer.Add(new HelpBox("Please make sure to have a backup before running these operations!", HelpBoxMessageType.Warning)); #endif right.Add(rightActionsContainer); leftActionsContainer.Add(new Label("Find") { style = { marginTop = 10, marginLeft = 3, unityFontStyleAndWeight = FontStyle.Bold }}); leftActionsContainer.Add(new Button(() => { FindMaterials(); FindAnimationClips(); FindScripts(); }) { text = "Find all Usages", tooltip = "Find materials, animation clips and scripts using this property", style = { height = 30 } } ); leftActionsContainer.Add(new Button(FindMaterials) { text = "Find materials and shaders using this property" }); leftActionsContainer.Add(new Button(FindAnimationClips) { text = "Find animations targeting this property" }); leftActionsContainer.Add(new Button(FindScripts) { text = "Find scripts targeting this property" }); #if HAVE_UITOOLKIT_HELPBOX leftActionsContainer.Add(new HelpBox("Non-Shadergraph shaders and scripts cannot be updated automatically. Please use the Find buttons and update them manually.", HelpBoxMessageType.None)); #endif left.Add(leftActionsContainer); } private void FindAnimationClips() { FindAnimationClips(data.refactoringData); } private void FindAnimationClips(List dat) { var clipsThatNeedUpdating = new List(); var allClips = GetAllAssets(); var i = 0; var count = allClips.Count; foreach (var clip in allClips) { i++; EditorUtility.DisplayProgressBar("Parsing AnimationClips", "Clip " + i + "/" + count + ": " + clip.name, (float)i / count); foreach (var binding in AnimationUtility.GetCurveBindings(clip)) { foreach (var data in dat) { if (binding.propertyName.StartsWith("material." + data.sourceReferenceName, StringComparison.Ordinal)) // TODO what if the animated material is not at index 0? { if(clipsThatNeedUpdating.Contains(clip)) continue; clipsThatNeedUpdating.Add(clip); Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, clip, $"AnimationClip targets this shader property: {clip.name} ({binding.path})"); } } } } EditorUtility.ClearProgressBar(); } private void FindScripts() { foreach (var dat in data.refactoringData) { FindScripts(dat); } } private void FindScripts(ShaderPropertyRefactoringData data) { // find all scripts var scripts = GetAllAssets(); var i = 0; var count = scripts.Count; foreach (var script in scripts) { i++; if(!AssetDatabase.IsOpenForEdit(script)) continue; EditorUtility.DisplayProgressBar("Parsing Scripts", "Script " + i + "/" + count + ": " + script.name, (float)i / count); var fullText = File.ReadAllLines(AssetDatabase.GetAssetPath(script)); for(int line = 0; line < fullText.Length; line++) { var text = fullText[line]; var scriptPath = $"{AssetDatabase.GetAssetPath(script)}:{line}"; if (text.Contains("\"" + data.sourceReferenceName + "\"")) { Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, script, "{0}", "Script contains reference to this property: " + scriptPath); } else if (text.Contains(data.sourceReferenceName)) { // Debug.Log($"Script may contain reference to this property: ) (at {Path.GetFullPath(AssetDatabase.GetAssetPath(script)).Replace("\\","/")}:{line})", script); Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, script, "{0}", "Script may contain reference to this property: " + scriptPath); } } } EditorUtility.ClearProgressBar(); } void FindMaterials() { foreach (var dat in data.refactoringData) { FindMaterials(dat); } } private void FindMaterials(ShaderPropertyRefactoringData data) { var allMaterials = GetAllAssets(); var i = 0; var count = allMaterials.Count; foreach (var material in allMaterials) { i++; EditorUtility.DisplayProgressBar("Parsing Material and Shader", "Material " + i + "/" + count + ": " + material.name, (float)i / count); if(!material) continue; var shader = material.shader; if(!shader) continue; var propertyIndex = shader.FindPropertyIndex(data.sourceReferenceName); if (propertyIndex >= 0) { Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, material, $"Material uses shader with property {data.sourceReferenceName}: {material.name} ({shader.name})"); } } EditorUtility.ClearProgressBar(); } private void FixMaterialsAndShaders() { FixMaterialsAndShaders(data.shader, data.refactoringData); } private void FixMaterialsAndShaders(Shader sourceShader, List propertyList) { var allShaders = GetAllAssets(); var shadersThatNeedUpdating = new List(); var shadersThatCannotBeUpdated = new List(); var shadersThatWouldNeedUpdatingButAreExcluded = new List(); var i = 0; var count = allShaders.Count; foreach (var shader in allShaders) { i++; EditorUtility.DisplayProgressBar("Parsing Shader", "Shader " + i + "/" + count + ": " + shader.name, (float)i / count); if(!shader) continue; foreach(var data in propertyList) { if (string.IsNullOrWhiteSpace(data.sourceReferenceName)) { // Debug.LogWarning("Can't refactor: source reference name is empty. Please select a source property."); continue; } if (string.IsNullOrWhiteSpace(data.targetReferenceName)) { // Debug.LogWarning("Can't refactor: target reference name is empty. Please set a new reference name for " + data.sourceReferenceName); continue; } var propertyIndex = shader.FindPropertyIndex(data.sourceReferenceName); if (propertyIndex >= 0) { var path = AssetDatabase.GetAssetPath(shader); // if the shader field is set, we want to explicitly only upgrade that shader; // if the shader field isn't set, we want to upgrade all shaders. var shouldUpdateThisShader = !sourceShader || shader == sourceShader; var couldUpdateThisShader = path.EndsWith(".shadergraph", StringComparison.OrdinalIgnoreCase) && AssetDatabase.IsOpenForEdit(path); if (!couldUpdateThisShader) { shadersThatCannotBeUpdated.Add(shader); } else if (shouldUpdateThisShader) { // Debug.Log($"Shader has property {data.sourceReferenceName} and will be updated: {shader.name}", shader); shadersThatNeedUpdating.Add(shader); } else { // Debug.LogWarning($"Shader has property {data.sourceReferenceName} but will NOT be updated: {shader.name}. Clear the shader field if you want to update all shaders with this property.", shader); shadersThatWouldNeedUpdatingButAreExcluded.Add(shader); } } } } EditorUtility.ClearProgressBar(); if(!shadersThatNeedUpdating.Any() && !shadersThatWouldNeedUpdatingButAreExcluded.Any() && !shadersThatCannotBeUpdated.Any()) { Debug.Log($"Properties haven't been found in any shaders. No changes necessary."); return; } if (shadersThatWouldNeedUpdatingButAreExcluded.Any()) { var selectedShadersNeedUpdating = shadersThatNeedUpdating.Any(); switch (EditorUtility.DisplayDialogComplex( "Properties also exist in other shaders!", (selectedShadersNeedUpdating ? "This property exists in this shader that is selected for updating:" + ObjectNames(shadersThatNeedUpdating) : "This property doesn't exist in the selected shader(s).") + "\n\n" + "but also exists in these shaders that won't be updated because they're not selected:" + ObjectNames(shadersThatWouldNeedUpdatingButAreExcluded) + ".\n\n" + "It also exists in " + shadersThatCannotBeUpdated.Count + " shaders that cannot be updated (read-only or not checked out)" + ".\n\n" + "Are you sure you want to update just these shaders? Press \"Update All\", or Cancel and empty the \"Shader\" field to update all shaders.", selectedShadersNeedUpdating ? "Update only " + ObjectNames(shadersThatNeedUpdating, true) : "Do nothing", "Cancel", "Update all " + (shadersThatNeedUpdating.Count + shadersThatWouldNeedUpdatingButAreExcluded.Count) + " shaders")) { case 0: break; case 1: return; case 2: shadersThatNeedUpdating.AddRange(shadersThatWouldNeedUpdatingButAreExcluded); shadersThatWouldNeedUpdatingButAreExcluded.Clear(); break; } } // find all materials that are using any of these shaders var materialsThatNeedUpdating = GetAllAssets() .Where(x => x.shader && shadersThatNeedUpdating.Contains(x.shader)) .ToList(); var materialsThatCannotBeUpdated = materialsThatNeedUpdating.Where(x => !AssetDatabase.IsMainAsset(x) || !AssetDatabase.IsOpenForEdit(x)).ToList(); var materialsThatCanBeUpdated = materialsThatNeedUpdating.Except(materialsThatCannotBeUpdated).ToList(); if (materialsThatCannotBeUpdated.Any()) { Debug.LogWarning($"Some materials couldn't be updated since they are sub assets [{materialsThatCannotBeUpdated.Count}]: {ObjectNames(materialsThatCannotBeUpdated, false, false, true)}"); } if(!shadersThatNeedUpdating.Any() && !materialsThatCanBeUpdated.Any()) { Debug.Log("Property hasn't been found in any shaders and materials. No changes necessary."); return; } if (!EditorUtility.DisplayDialog( "Refactor shader properties", "Shaders that will be changed [" + shadersThatNeedUpdating.Count + "]:" + ObjectNames(shadersThatNeedUpdating) + "\n\n" + "Materials that will be changed [" + materialsThatCanBeUpdated.Count + "]:" + ObjectNames(materialsThatCanBeUpdated) + "\n\n" + (shadersThatCannotBeUpdated.Any() ? ("Shaders that can't be changed [" + shadersThatCannotBeUpdated.Count + "]:" + ObjectNames(shadersThatCannotBeUpdated) + "\n\n") : "") + (materialsThatCannotBeUpdated.Any() ? ("Materials that can't be changed [" + materialsThatCannotBeUpdated.Count + "]:" + ObjectNames(materialsThatCannotBeUpdated)) : ""), "OK", "Cancel")) return; foreach (var shader in shadersThatNeedUpdating) { // upgrade file var path = AssetDatabase.GetAssetPath(shader); if (!path.EndsWith(".shadergraph", StringComparison.OrdinalIgnoreCase)) { Debug.LogWarning("Only .shadergraph files can be auto-updated right now. Please update the file manually: " + path, shader); continue; } var text = File.ReadAllText(path); // TODO this will only properly work for ShaderGraph files right now, as these have "" around properties. // The general case is more complex! We'd have to parse the file properly and check if something is "_VarName=" or "_VarName2" (the former would be replaced, the latter is a separate field) var lines = File.ReadAllLines(path); for (int l = 0; l < lines.Length; l++) { var start = lines[l].TrimStart(); if (start.StartsWith("\"m_DisplayName") || start.StartsWith("\"m_ShaderOutputName") || start.StartsWith("\"m_Name")) { continue; } foreach (var data in propertyList) { if (string.IsNullOrWhiteSpace(data.sourceReferenceName)) continue; if (string.IsNullOrWhiteSpace(data.targetReferenceName)) continue; lines[l] = lines[l].Replace($"\"{data.sourceReferenceName}\"", $"\"{data.targetReferenceName}\""); } } File.WriteAllLines(path, lines); } foreach (var mat in materialsThatCanBeUpdated) { var path = AssetDatabase.GetAssetPath(mat); var lines = File.ReadAllLines(path); for(int l = 0; l < lines.Length; l++) { var start = lines[l].TrimStart(); foreach (var data in propertyList) { if (string.IsNullOrWhiteSpace(data.sourceReferenceName)) continue; if (string.IsNullOrWhiteSpace(data.targetReferenceName)) continue; // migrate shader keywords if (start.StartsWith("m_ShaderKeywords: ", StringComparison.Ordinal)) { lines[l] = lines[l].Replace(" " + data.sourceReferenceName + " ", " " + data.targetReferenceName + " "); lines[l] = lines[l].Replace(" " + data.sourceReferenceName + "\n", " " + data.targetReferenceName + "\n"); // last item } else { // we're directly operating on the serialized YAML here, what could possibly go wrong lines[l] = lines[l].Replace("- " + data.sourceReferenceName + ":", "- " + data.targetReferenceName + ":"); } } } File.WriteAllLines(path, lines); } AssetDatabase.Refresh(); // UpdatePopupField(); } private void FixAnimationClips() { FixAnimationClips(data.refactoringData); } private void FixAnimationClips(List dat) { var clipsThatNeedUpdating = new List(); var allClips = GetAllAssets(); var i = 0; var count = allClips.Count; foreach (var clip in allClips) { i++; EditorUtility.DisplayProgressBar("Parsing AnimationClips", "Clip " + i + "/" + count + ": " + clip.name, (float)i / count); foreach (var binding in AnimationUtility.GetCurveBindings(clip)) { foreach(var data in dat) { if (binding.propertyName.StartsWith("material." + data.sourceReferenceName, StringComparison.Ordinal)) // TODO what if the animated material is not at index 0? { if(clipsThatNeedUpdating.Contains(clip)) continue; clipsThatNeedUpdating.Add(clip); } } } } EditorUtility.ClearProgressBar(); var clipsThatCannotBeUpdated = clipsThatNeedUpdating.Where(x => !AssetDatabase.IsOpenForEdit(x)).ToList(); foreach (var clip in clipsThatNeedUpdating) { var currentBindings = AnimationUtility.GetCurveBindings(clip); for(int j = 0; j < currentBindings.Length; j++) { var binding = currentBindings[j]; foreach (var data in dat) { if (binding.propertyName.StartsWith("material." + data.sourceReferenceName, StringComparison.Ordinal)) // TODO what if the animated material is not at index 0? { var curve = AnimationUtility.GetEditorCurve(clip, binding); AnimationUtility.SetEditorCurve(clip, binding, null); binding.propertyName = binding.propertyName.Replace("material." + data.sourceReferenceName, "material." + data.targetReferenceName); AnimationUtility.SetEditorCurve(clip, binding, curve); } } // AnimationUtility.SetEditorCurve(clip, binding, null); } // var path = AssetDatabase.GetAssetPath(clip); // var text = File.ReadAllText(path); // // we're directly operating on the serialized YAML here, what could possibly go wrong // text = text.Replace("- " + data.sourceReferenceName + ":", "- " + data.targetReferenceName + ":"); // File.WriteAllText(path, text); } if(clipsThatNeedUpdating.Any()) { Debug.Log("Clips that have been updated [" + clipsThatNeedUpdating.Count + "]: " + ObjectNames(clipsThatNeedUpdating)); if (clipsThatCannotBeUpdated.Any()) { Debug.Log("Clips that cannot be updated (read-only) [" + clipsThatCannotBeUpdated.Count + "]: " + ObjectNames(clipsThatCannotBeUpdated)); } } } private void FixScripts() { // find all scripts var scripts = GetAllAssets(); var i = 0; var count = scripts.Count; foreach (var script in scripts) { i++; if (!AssetDatabase.IsOpenForEdit(script)) continue; EditorUtility.DisplayProgressBar("Parsing Scripts", "Script " + i + "/" + count + ": " + script.name, (float)i / count); var path = AssetDatabase.GetAssetPath(script); var fullText = File.ReadAllLines(path); bool didAChange = false; for (int line = 0; line < fullText.Length; line++) { var text = fullText[line]; foreach (var data in data.refactoringData) text = text.Replace("\"" + data.sourceReferenceName + "\"", "\"" + data.targetReferenceName + "\""); if (fullText[line] != text) didAChange = true; fullText[line] = text; } if (didAChange) File.WriteAllLines(path, fullText); } AssetDatabase.Refresh(); EditorUtility.ClearProgressBar(); } private static string ObjectNames(IReadOnlyCollection objects, bool singleLine = false, bool returnShortStringIfTooManyElements = true, bool groupByName = false) where T : UnityEngine.Object { var strings = (groupByName ? objects.ToLookup(x => x.name).Select(x => $"{x.Key} [{x.Count()}]") : objects.Select(x => x.name)).OrderBy(x => x).ToList(); if (returnShortStringIfTooManyElements && strings.Count > 5) return singleLine ? " " : "\n" + "(too many items to display: " + strings.Count + ")"; const int MaxPrettyObjectsCount = 10; var makeSingleLineBecauseOfTooManyElements = returnShortStringIfTooManyElements && strings.Count > MaxPrettyObjectsCount; var firstSeparator = singleLine ? "" : makeSingleLineBecauseOfTooManyElements ? "\n" : "\n · "; var separator = singleLine || makeSingleLineBecauseOfTooManyElements ? ", " : "\n · "; return firstSeparator + string.Join(separator, strings); } } [Serializable] public class ShaderRefactoringData { public string shaderPath; public Shader shader; public List refactoringData = new List(); // public string sourceReferenceName; // public string targetReferenceName; } [Serializable] public class ShaderPropertyRefactoringData { public string sourceReferenceName; public string targetReferenceName; } } ================================================ FILE: package/Editor/Extensions/ShaderRefactoringWindow.cs.meta ================================================ fileFormatVersion: 2 guid: cc31c4ce78c591c4eb30f57032a7b454 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Extensions.meta ================================================ fileFormatVersion: 2 guid: f9d6513e81beefd43a10c3ff3600e926 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/Common/CommonInternalExtensions.cs ================================================ using System; using System.Reflection; using UnityEditor; using UnityEngine; namespace Needle.ShaderGraphMarkdown { public static class CommonInternalExtensions { public static void ShaderPropertyWithTooltip(this MaterialEditor materialEditor, MaterialProperty prop, GUIContent label) { // special case: we want to draw textures and vectors differently, since they discard tooltips in the MaterialEditor implementation... switch (prop.type) { case MaterialProperty.PropType.Vector: if (_GetPropertyRect == null) { materialEditor.VectorProperty(prop, label.text); } else { var position = (Rect)_GetPropertyRect.Invoke(materialEditor, new object[] { prop, label.text, true }); _VectorProperty(position, prop, label); } break; case MaterialProperty.PropType.Texture: bool scaleOffset = (prop.flags & MaterialProperty.PropFlags.NoScaleOffset) == MaterialProperty.PropFlags.None; materialEditor.TexturePropertyWithTooltip(prop, label, scaleOffset); break; default: materialEditor.ShaderProperty(prop, label); break; } } // from MaterialEditor.VectorProperty, plus support for GUIContent public static Vector4 _VectorProperty(Rect position, MaterialProperty prop, GUIContent label) { #if UNITY_2022_1_OR_NEWER MaterialEditor.BeginProperty(position, prop); #endif EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = prop.hasMixedValue; float labelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 0.0f; Vector4 vector4 = EditorGUI.Vector4Field(position, label, prop.vectorValue); EditorGUIUtility.labelWidth = labelWidth; EditorGUI.showMixedValue = false; if (EditorGUI.EndChangeCheck()) prop.vectorValue = vector4; #if UNITY_2022_1_OR_NEWER MaterialEditor.EndProperty(); #endif return prop.vectorValue; } private static MethodInfo _getPropertyRect = null; private static MethodInfo _GetPropertyRect { get { if (_getPropertyRect == null) _getPropertyRect = typeof(MaterialEditor).GetMethod("GetPropertyRect", (BindingFlags)(-1), null, CallingConventions.Any, new Type[] { typeof(MaterialProperty), typeof(string), typeof(bool) }, null); return _getPropertyRect; } } public static void TexturePropertyWithTooltip(this MaterialEditor materialEditor, MaterialProperty textureProperty, GUIContent displayContent, bool scaleOffset) { if (_GetPropertyRect == null) { materialEditor.TextureProperty(textureProperty, displayContent.text, scaleOffset); } else { var rect = (Rect)_GetPropertyRect.Invoke(materialEditor, new object[] { textureProperty, displayContent.text, true }); materialEditor.TextureProperty(rect, textureProperty, displayContent.text, displayContent.tooltip, scaleOffset); } } } } ================================================ FILE: package/Editor/Internal/Common/CommonInternalExtensions.cs.meta ================================================ fileFormatVersion: 2 guid: 0d353ff40ad6b6d47bd1ada5b22dcfdf MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/Common/Needle.ShaderGraphMarkdownCommonInternal.asmref ================================================ { "reference": "GUID:343deaaf83e0cee4ca978e7df0b80d21" } ================================================ FILE: package/Editor/Internal/Common/Needle.ShaderGraphMarkdownCommonInternal.asmref.meta ================================================ fileFormatVersion: 2 guid: fa3720d486f1e3b42965c20bf53121f6 AssemblyDefinitionReferenceImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/Common.meta ================================================ fileFormatVersion: 2 guid: d3aed7a7d8dbbde419dfa506a0850af6 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/CommonExtensions.cs ================================================ // using System.Collections; // using System.Collections.Generic; // using UnityEngine; // // namespace Needle.ShaderGraphMarkdown // { // public static class CommonExtensions // { // } // } ================================================ FILE: package/Editor/Internal/CommonExtensions.cs.meta ================================================ fileFormatVersion: 2 guid: a61a189b38ecce04bbf837daeefdf90e MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/HDRP/MarkdownHDExtensions.cs ================================================ #if !NO_INTERNALS_ACCESS && UNITY_2019_4_OR_NEWER using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using UnityEditor.Graphing; using UnityEditor.Rendering.HighDefinition; using UnityEditor.ShaderGraph; #if UNITY_2020_2_OR_NEWER using UnityEditor.Rendering.HighDefinition.ShaderGraph; using UnityEditor.ShaderGraph.Internal; using UnityEditor.ShaderGraph.Serialization; #endif using UnityEngine; using UnityEngine.Rendering.HighDefinition; namespace UnityEditor.Rendering.HighDefinition { public static class MarkdownHDExtensions { private static readonly Dictionary TypeToHDPropertyTypeMap = new Dictionary() { #if UNITY_2020_2_OR_NEWER { typeof(LightmapData), typeof(LightmappingShaderProperties.LightmapTextureArrayProperty) }, { typeof(DiffusionProfileSettings), typeof(DiffusionProfileShaderProperty) }, #endif }; [InitializeOnLoadMethod] static void RegisterMarkdownHelpers() { MarkdownSGExtensions.RegisterTypeMap(TypeToHDPropertyTypeMap); MarkdownSGExtensions.RegisterCustomInspectorGetter(GetDefaultCustomInspectorFromGraphData); #if UNITY_2020_2_OR_NEWER MarkdownSGExtensions.RegisterCustomInspectorSetter(SetDefaultCustomInspector); #endif } #if UNITY_2020_2_OR_NEWER static bool SetDefaultCustomInspector(Target target, string customInspector) { if (target is HDTarget hdTarget) { if (hdTarget.customEditorGUI.Equals(customInspector, StringComparison.Ordinal)) hdTarget.customEditorGUI = null; // GetDefaultCustomInspectorFromShader(mat.shader); else hdTarget.customEditorGUI = customInspector; return true; } return false; } #endif static string GetDefaultCustomInspectorFromGraphData(GraphData graphData) { string customInspector = null; #if UNITY_2020_2_OR_NEWER foreach(var target in graphData.activeTargets) { // Debug.Log("Active Target: " + target.displayName); if (target is HDTarget hdTarget) { var activeSubTarget = ((JsonData) typeof(HDTarget).GetField("m_ActiveSubTarget", (BindingFlags)(-1)).GetValue(hdTarget)).value; if (activeSubTarget is HDSubTarget hdSubTarget) { customInspector = (string) typeof(HDSubTarget).GetProperty("customInspector", (BindingFlags) (-1))?.GetValue(hdSubTarget); break; // Debug.Log($"Active SubTarget: {activeSubTarget.displayName}, Custom Editor: " + customInspector); } } } #else if (graphData.outputNode is MasterNode masterNode) { switch (masterNode) { case HDLitMasterNode hdLitMasterNode: customInspector = "UnityEditor.Rendering.HighDefinition.HDLitGUI"; break; case HDUnlitMasterNode hdUnlitMasterNode: customInspector = "UnityEditor.Rendering.HighDefinition.HDUnlitGUI"; break; case DecalMasterNode sub: customInspector = "UnityEditor.Rendering.HighDefinition.DecalGUI"; break; case EyeMasterNode sub: customInspector = "UnityEditor.Rendering.HighDefinition.EyeGUI"; break; case FabricMasterNode sub: customInspector = "UnityEditor.Rendering.HighDefinition.FabricGUI"; break; case HairMasterNode sub: customInspector = "UnityEditor.Rendering.HighDefinition.HairGUI"; break; case PBRMasterNode sub: customInspector = "UnityEditor.Rendering.HighDefinition.HDPBRLitGUI"; break; case StackLitMasterNode sub: customInspector = "UnityEditor.Rendering.HighDefinition.StackLitGUI"; break; case UnlitMasterNode sub: customInspector = "UnityEditor.Rendering.HighDefinition.UnlitUI"; break; } } #endif return customInspector; } public static void RemoveShaderGraphUIBlock(ShaderGUI baseShaderGui) { MaterialUIBlockList blockList = null; #if UNITY_2020_2_OR_NEWER if (baseShaderGui is LightingShaderGraphGUI hdGui) { blockList = (MaterialUIBlockList) typeof(LightingShaderGraphGUI).GetProperty("uiBlocks", (BindingFlags) (-1))?.GetValue(hdGui); } #else if (baseShaderGui is HDLitGUI hdLitGUI) { blockList = (MaterialUIBlockList) typeof(HDLitGUI).GetField("uiBlocks", (BindingFlags) (-1))?.GetValue(hdLitGUI); } else if (baseShaderGui is HDUnlitGUI hdUnlitGUI) { blockList = (MaterialUIBlockList) typeof(HDUnlitGUI).GetField("uiBlocks", (BindingFlags) (-1))?.GetValue(hdUnlitGUI); } #endif #if !UNITY_2021_1_OR_NEWER else if (baseShaderGui is DecalGUI decalGUI) { blockList = (MaterialUIBlockList) typeof(DecalGUI).GetField("uiBlocks", (BindingFlags) (-1))?.GetValue(decalGUI); } #endif else { blockList = (MaterialUIBlockList) baseShaderGui.GetType().GetField("uiBlocks", (BindingFlags) (-1))?.GetValue(baseShaderGui); if(blockList == null) blockList = (MaterialUIBlockList) baseShaderGui.GetType().GetProperty("uiBlocks", (BindingFlags) (-1))?.GetValue(baseShaderGui); } if (blockList == null) return; var shaderGraphBlock = blockList.FetchUIBlock(); // // check if there are special features active and so we need to keep the block // var m_Features = typeof(ShaderGraphUIBlock).GetField("m_Features", (BindingFlags) (-1)); // var shaderGraphFeatures = (ShaderGraphUIBlock.Features) (m_Features?.GetValue(shaderGraphBlock) ?? 0); // if(shaderGraphFeatures == 0) blockList?.Remove(shaderGraphBlock); // Debug.Log(shaderGraphFeatures); } } } #endif ================================================ FILE: package/Editor/Internal/HDRP/MarkdownHDExtensions.cs.meta ================================================ fileFormatVersion: 2 guid: 9bd2e12169b536b4fa16cd8592363617 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/HDRP/Needle.ShaderGraphMarkdown.HDInternal.asmref ================================================ { "reference": "GUID:78bd2ddd6e276394a9615c203e574844" } ================================================ FILE: package/Editor/Internal/HDRP/Needle.ShaderGraphMarkdown.HDInternal.asmref.meta ================================================ fileFormatVersion: 2 guid: f958dcb7312678b47b40503dd4056043 AssemblyDefinitionReferenceImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/HDRP.meta ================================================ fileFormatVersion: 2 guid: fd6180dc2a4d07949b204ff770b9dcaa folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/Needle.ShaderGraphMarkdown.Internal.asmdef ================================================ { "name": "Needle.ShaderGraphMarkdown.Internal", "rootNamespace": "", "references": [], "includePlatforms": [ "Editor" ], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": true, "precompiledReferences": [], "autoReferenced": false, "defineConstraints": [], "versionDefines": [ { "name": "com.unity.render-pipelines.universal", "expression": "7.0.0", "define": "URP_7_OR_NEWER" }, { "name": "com.unity.render-pipelines.high-definition", "expression": "7.0.0", "define": "HDRP_7_OR_NEWER" }, { "name": "com.unity.modules.ui", "expression": "0.0.0", "define": "NO_INTERNALS_ACCESS" } ], "noEngineReferences": false } ================================================ FILE: package/Editor/Internal/Needle.ShaderGraphMarkdown.Internal.asmdef.meta ================================================ fileFormatVersion: 2 guid: bf7b384417358f74ca38b924ce380c6d AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/ShaderGraph/Experimental/MarkdownBlackboardProperties.cs ================================================ #if !NO_INTERNALS_ACCESS && UNITY_2019_4_OR_NEWER // using System; // using System.Collections.Generic; // using System.IO; // using System.Linq; // using System.Reflection; // using System.Text; // using UnityEngine; // using UnityEditor.ShaderGraph.Internal; // #if UNITY_2020_2_OR_NEWER // using UnityEditor.ShaderGraph.Serialization; // #else // #endif namespace UnityEditor.ShaderGraph { #region Custom Blackboard Properties - Experimental // [Serializable] // [BlackboardInputInfo(30000, name = "Markdown/Foldout Header")] // public class MarkdownFoldout : MarkdownShaderProperty // { // internal override string DisplayName => "# My Foldout"; // } // // [Serializable] // [BlackboardInputInfo(30001, name = "Markdown/Header")] // public class MarkdownHeader : MarkdownShaderProperty // { // internal override string DisplayName => "## My Header"; // } // // [Serializable] // [BlackboardInputInfo(30002, name = "Markdown/Note")] // public class MarkdownNote : MarkdownShaderProperty // { // internal override string DisplayName => "!NOTE My Note"; // } // // public abstract class MarkdownShaderProperty : AbstractShaderProperty // { // internal abstract string DisplayName { get; } // // internal MarkdownShaderProperty() // { // displayName = DisplayName; // } // // public override PropertyType propertyType => PropertyType.Boolean; // // internal override bool isExposable => true; // internal override bool isRenamable => true; // // internal override string GetPropertyAsArgumentString() // { // return $"{concreteShaderValueType.ToShaderString(concretePrecision.ToShaderString())} {referenceName}"; // } // // internal override void ForeachHLSLProperty(Action action) // { // HLSLDeclaration decl = GetDefaultHLSLDeclaration(); // action(new HLSLProperty(HLSLType._float, referenceName, decl, concretePrecision)); // } // // internal override string GetPropertyBlockString() // { // return $"{hideTagString}[ToggleUI]{referenceName}(\"{displayName}\", Float) = {(value == true ? 1 : 0)}"; // } // // internal override AbstractMaterialNode ToConcreteNode() // { // return new BooleanNode() { value = new ToggleData(value) }; // } // // internal override PreviewProperty GetPreviewMaterialProperty() // { // return new PreviewProperty(propertyType) // { // name = referenceName, // booleanValue = value // }; // } // // internal override ShaderInput Copy() // { // return new BooleanShaderProperty() // { // displayName = displayName, // hidden = hidden, // value = value, // precision = precision, // }; // } // } #endregion } #endif ================================================ FILE: package/Editor/Internal/ShaderGraph/Experimental/MarkdownBlackboardProperties.cs.meta ================================================ fileFormatVersion: 2 guid: c7fa19444126deb4085d2b4bff709070 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/ShaderGraph/Experimental/PropertyManipulation.cs ================================================ #if !NO_INTERNALS_ACCESS && UNITY_2019_4_OR_NEWER using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEngine; using UnityEditor.ShaderGraph.Internal; #if UNITY_2020_2_OR_NEWER using UnityEditor.ShaderGraph.Serialization; #else #endif namespace UnityEditor.ShaderGraph { public static partial class MarkdownSGExtensions { #region ShaderGraph Property Manipulation API [MenuItem("internal:CONTEXT/Shader/Remove All Properties", true)] static bool RemoveAllPropertiesValidate(MenuCommand command) { return GetGraphData((Shader) command.context) != null; } [MenuItem("internal:CONTEXT/Shader/Remove All Properties", false)] static void RemoveAllProperties(MenuCommand command) { var shader = (Shader) command.context; var graphData = GetGraphData(shader); if (graphData == null) return; if (m_Keywords == null) m_Keywords = typeof(GraphData).GetField("m_Keywords", (BindingFlags) (-1)); if (m_Properties == null) m_Properties = typeof(GraphData).GetField("m_Properties", (BindingFlags) (-1)); #if UNITY_2020_2_OR_NEWER var keywords = (List>) m_Keywords.GetValue(graphData); var properties = (List>) m_Properties.GetValue(graphData); #else var keywords = (List) m_Keywords.GetValue(graphData); var properties = (List) m_Properties.GetValue(graphData); #endif keywords.Clear(); properties.Clear(); WriteShaderGraphToDisk(shader, graphData); } [MenuItem("internal:CONTEXT/Shader/Add Test Property", true)] static bool AddTestPropertyValidate(MenuCommand command) { return GetGraphData((Shader) command.context) != null; } [MenuItem("internal:CONTEXT/Shader/Add Test Properties", false)] static void AddTestProperty(MenuCommand command) { var shader = (Shader) command.context; AddMaterialProperty(shader, "My Bool"); AddMaterialProperty(shader, "My Color"); AddMaterialProperty(shader, "My Texture"); AddMaterialProperty(shader, "My Vector"); AddMaterialProperty(shader, "My Float"); AddMaterialKeyword(shader, "Some Boolean Keyword"); AddMaterialKeyword(shader, "Some Enum Keyword"); } [MenuItem("CONTEXT/Shader/Add Properties Wizard", true)] static bool AddPropertiesWizardValidate(MenuCommand command) { return GetGraphData((Shader) command.context) != null; } [MenuItem("CONTEXT/Shader/Add Properties Wizard", false)] static void AddPropertiesWizard(MenuCommand command) { // open wizard window var wizard = ScriptableWizard.DisplayWizard("Add Properties from Material", "Add Properties"); wizard.targetShader = (Shader) command.context; } [InitializeOnLoadMethod] static void RegisterBaseTypeMap() { RegisterTypeMap(TypeToPropertyTypeMap); } private static readonly List> TypeMaps = new List>(); public static void RegisterTypeMap(Dictionary typeToPropertyTypeMap) { if (!TypeMaps.Contains(typeToPropertyTypeMap)) TypeMaps.Add(typeToPropertyTypeMap); } // commented out entries are either duplicates or don't have matching Unity types private static readonly Dictionary TypeToPropertyTypeMap = new Dictionary() { { typeof(Gradient), typeof(GradientShaderProperty) }, // { typeof(Matrix), typeof(Matrix2ShaderProperty) }, // { typeof(null), typeof(Matrix3ShaderProperty) }, { typeof(Matrix4x4), typeof(Matrix4ShaderProperty) }, // { typeof(Matrix4x4), typeof(MatrixShaderProperty) }, { typeof(TextureSamplerState), typeof(SamplerStateShaderProperty) }, // { typeof(VirtualTexture), typeof(VirtualTextureShaderProperty) }, // { typeof(null), typeof(AbstractShaderProperty) }, { typeof(bool), typeof(BooleanShaderProperty) }, { typeof(Color), typeof(ColorShaderProperty) }, { typeof(Cubemap), typeof(CubemapShaderProperty) }, { typeof(Texture2DArray), typeof(Texture2DArrayShaderProperty) }, { typeof(Texture2D), typeof(Texture2DShaderProperty) }, { typeof(Texture3D), typeof(Texture3DShaderProperty) }, { typeof(float), typeof(Vector1ShaderProperty) }, { typeof(Vector2), typeof(Vector2ShaderProperty) }, { typeof(Vector3), typeof(Vector3ShaderProperty) }, { typeof(Vector4), typeof(Vector4ShaderProperty) }, // { typeof(float), typeof(VectorShaderProperty) }, // { typeof(null), typeof(MultiJsonInternal.UnknownShaderPropertyType) }, }; private static FieldInfo m_Properties, m_Keywords; public static void AddMaterialKeyword(Shader shader, string displayName, string referenceName = null) { var keywordType = typeof(T); var graphData = GetGraphData(shader); if (graphData == null) return; if (m_Keywords == null) m_Keywords = typeof(GraphData).GetField("m_Keywords", (BindingFlags) (-1)); var keywords = #if UNITY_2020_2_OR_NEWER (List>) #else (List) #endif m_Keywords.GetValue(graphData); ShaderKeyword keyword = null; if(keywordType == typeof(Enum)) keyword = new ShaderKeyword(KeywordType.Enum); else if (keywordType == typeof(bool)) keyword = new ShaderKeyword(KeywordType.Boolean); else { Debug.LogError($"Can't create keyword of type {keywordType}, allowed types are Enum and bool"); return; } keyword.displayName = displayName; if (!string.IsNullOrEmpty(referenceName)) keyword.overrideReferenceName = referenceName; // JsonData has an implicit conversion operator keywords.Add(keyword); WriteShaderGraphToDisk(shader, graphData); } private static #if UNITY_2020_2_OR_NEWER List> #else List #endif GetProperties(GraphData graphData) { if (m_Properties == null) m_Properties = typeof(GraphData).GetField("m_Properties", (BindingFlags) (-1)); var properties = #if UNITY_2020_2_OR_NEWER (List>) #else (List) #endif m_Properties.GetValue(graphData); return properties; } internal static void AddMaterialPropertyInternal(GraphData graphData, Type propertyType, string displayName, string referenceName = null) { var properties = GetProperties(graphData); var prop = (AbstractShaderProperty) Activator.CreateInstance(propertyType, true); prop.displayName = displayName; if (!string.IsNullOrEmpty(referenceName)) prop.overrideReferenceName = referenceName; // JsonData has an implicit conversion operator properties.Add(prop); } public static void RemoveMaterialProperty(Shader shader, string referenceName) { var graphData = GetGraphData(shader); if (graphData == null) return; RemoveMaterialProperty(graphData, referenceName); WriteShaderGraphToDisk(shader, graphData); } internal static void RemoveMaterialProperty(GraphData graphData, string referenceName) { var properties = GetProperties(graphData); properties.RemoveAll(x => ((AbstractShaderProperty) x).referenceName.Equals(referenceName, StringComparison.Ordinal)); } public static void AddMaterialPropertyInternal(Shader shader, Type propertyType, string displayName, string referenceName = null) { var graphData = GetGraphData(shader); if (graphData == null) return; if (propertyType == null) return; AddMaterialPropertyInternal(graphData, propertyType, displayName, referenceName); WriteShaderGraphToDisk(shader, graphData); } public static void AddMaterialPropertyInternal(Shader shader, string displayName, string referenceName = null) where T: AbstractShaderProperty { AddMaterialPropertyInternal(shader, typeof(T), displayName, referenceName); } internal static void AddMaterialProperty(GraphData graphData, Type propertyType, string displayName, string referenceName = null) { AddMaterialPropertyInternal(graphData, FindTypeInTypeMap(propertyType), displayName, referenceName); } private static Type FindTypeInTypeMap(Type propertyType) { Type foundType = null; foreach(var dict in TypeMaps) { if (dict == null) continue; if (dict.ContainsKey(propertyType)) { foundType = dict[propertyType]; break; } } if(foundType == null) { Debug.LogError($"Can't add property of type {propertyType}: not found in type map. Allowed types: {string.Join("\n", TypeToPropertyTypeMap.Select(x => x.Key + " (" + x.Value + ")"))}"); return null; } return foundType; } public static void AddMaterialProperty(Shader shader, Type propertyType, string displayName, string referenceName = null) { AddMaterialPropertyInternal(shader, FindTypeInTypeMap(propertyType), displayName, referenceName); } public static void AddMaterialProperty(Shader shader, string displayName, string referenceName = null) { AddMaterialProperty(shader, typeof(T), displayName, referenceName); } } [Serializable] class PropertyWizard : ScriptableWizard { public Shader targetShader; public Material sourceMaterial; public Shader sourceShader; public bool hideMatchingProperties = false; private SerializedProperty _targetShader; private SerializedProperty _sourceMaterial, _sourceShader; private SerializedObject serializedObject; private void OnEnable() { serializedObject = new SerializedObject(this); createButtonName = "Apply Properties"; _targetShader = serializedObject.FindProperty("targetShader"); _sourceMaterial = serializedObject.FindProperty("sourceMaterial"); _sourceShader = serializedObject.FindProperty("sourceShader"); } [Serializable] public class ShaderProperty { public string name; public ShaderUtil.ShaderPropertyType propertyType; public string description; public bool isHidden = false; } private Dictionary sourceProperties = new Dictionary(); private Dictionary targetProperties = new Dictionary(); enum DrawMode { AddToTarget, RemoveFromTarget } void OnGUI() { serializedObject.Update(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginVertical(); EditorGUILayout.PropertyField(_sourceMaterial); EditorGUILayout.PropertyField(_sourceShader); EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(); EditorGUILayout.PropertyField(_targetShader); EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); void AddProperty(GraphData graphData, ShaderUtil.ShaderPropertyType shaderPropertyType, string displayName, string referenceName) { Type ShaderPropertyTypeToType(ShaderUtil.ShaderPropertyType propertyType) { switch (propertyType) { case ShaderUtil.ShaderPropertyType.Color: return typeof(Color); case ShaderUtil.ShaderPropertyType.Float: return typeof(float); case ShaderUtil.ShaderPropertyType.Range: return typeof(float); case ShaderUtil.ShaderPropertyType.TexEnv: return typeof(Texture2D); case ShaderUtil.ShaderPropertyType.Vector: return typeof(Vector4); default: return null; } } MarkdownSGExtensions.AddMaterialProperty(graphData, ShaderPropertyTypeToType(shaderPropertyType), displayName, referenceName); } void FillPropertyList(Shader shader, ref Dictionary props) { if (props == null) props = new Dictionary(); props.Clear(); var propertyCount = ShaderUtil.GetPropertyCount(shader); for (int i = 0; i < propertyCount; i++) { var propName = ShaderUtil.GetPropertyName(shader, i); if (props.ContainsKey(propName)) { Debug.LogWarning("Duplicate property: " + propName, shader); return; } props.Add(propName, new ShaderProperty() { name = propName, description = ShaderUtil.GetPropertyDescription(shader, i), propertyType = ShaderUtil.GetPropertyType(shader, i), isHidden = ShaderUtil.IsShaderPropertyHidden(shader, i) }); } } void Refresh() { if (!sourceMaterial && !sourceShader) return; FillPropertyList(sourceMaterial ? sourceMaterial.shader : sourceShader, ref sourceProperties); FillPropertyList(targetShader, ref targetProperties); } void DrawElement(Rect rect, ShaderProperty property, bool existsInSource, DrawMode mode) { var c = GUI.color; var baseColor = existsInSource ? Color.green : Color.white; if (property.isHidden) baseColor.a = 0.5f; GUI.color = baseColor; rect.height = 20; property.name = EditorGUI.TextField(rect, property.name); rect.y += rect.height; property.description = EditorGUI.TextField(rect, property.description); rect.y += rect.height; property.propertyType = (ShaderUtil.ShaderPropertyType) EditorGUI.EnumPopup(rect, property.propertyType); rect.y += rect.height; // property.isHidden = EditorGUI.Toggle(rect, property.isHidden); // rect.y += rect.height; // rect.width *= 0.5f; switch (mode) { case DrawMode.AddToTarget: if (GUI.Button(rect, "Add to Target")) { if (!targetProperties.ContainsKey(property.name)) { var graphData = MarkdownSGExtensions.GetGraphData(targetShader); AddProperty(graphData, property.propertyType, property.description, property.name); MarkdownSGExtensions.WriteShaderGraphToDisk(targetShader, graphData); } else { Debug.LogWarning("Property already exists: " + property.name + ", can't add again."); } } break; case DrawMode.RemoveFromTarget: if (GUI.Button(rect, "Remove from Target")) { var graphData = MarkdownSGExtensions.GetGraphData(targetShader); MarkdownSGExtensions.RemoveMaterialProperty(graphData, property.name); MarkdownSGExtensions.WriteShaderGraphToDisk(targetShader, graphData); } break; } GUI.color = c; } GUILayout.Label("Property Wizard"); if (GUILayout.Button("Refresh")) Refresh(); if(GUILayout.Button("Add All Source Properties to Target")) { var graphData = MarkdownSGExtensions.GetGraphData(targetShader); foreach (var kvp in sourceProperties) { var prop = kvp.Value; if(!targetProperties.ContainsKey(prop.name)) AddProperty(graphData, prop.propertyType, prop.description, prop.name); } MarkdownSGExtensions.WriteShaderGraphToDisk(targetShader, graphData); } hideMatchingProperties = EditorGUILayout.Toggle("Hide Matching Properties", hideMatchingProperties); sp = EditorGUILayout.BeginScrollView(sp); var draw = GUILayoutUtility.GetRect(position.width, Mathf.Max(sourceProperties.Count, targetProperties.Count) * 110); draw.width /= 2; int j = 0; foreach(var prop in sourceProperties) { if(hideMatchingProperties && targetProperties.ContainsKey(prop.Key)) continue; DrawElement(new Rect(draw.x, j * 110, draw.width, 110), prop.Value, targetProperties.ContainsKey(prop.Key), DrawMode.AddToTarget); j++; } draw.x += draw.width; j = 0; foreach(var prop in targetProperties) { if(hideMatchingProperties && sourceProperties.ContainsKey(prop.Key)) continue; DrawElement(new Rect(draw.x, j * 110, draw.width, 110), prop.Value, sourceProperties.ContainsKey(prop.Key), DrawMode.RemoveFromTarget); j++; } EditorGUILayout.EndScrollView(); if (serializedObject.hasModifiedProperties) { serializedObject.ApplyModifiedProperties(); Refresh(); } } private Vector2 sp; } #endregion } #endif ================================================ FILE: package/Editor/Internal/ShaderGraph/Experimental/PropertyManipulation.cs.meta ================================================ fileFormatVersion: 2 guid: 142b6f14013f84d48bec529090658508 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/ShaderGraph/Experimental.meta ================================================ fileFormatVersion: 2 guid: 6e925d24a18428c4997205c5c61beccc folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/ShaderGraph/MarkdownSGExtensions.cs ================================================ #if !NO_INTERNALS_ACCESS && UNITY_2019_4_OR_NEWER #if UNITY_2021_2_OR_NEWER #define SRP12_SG_REFACTORED #endif using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using UnityEditor.Experimental.GraphView; using UnityEditor.ShaderGraph.Drawing; using UnityEditor.ShaderGraph.Internal; using UnityEngine; using UnityEngine.UIElements; using Object = UnityEngine.Object; #if UNITY_2020_2_OR_NEWER #if SRP12_SG_REFACTORED using UnityEditor.Rendering.BuiltIn.ShaderGraph; #else #if !UNITY_2021_2_OR_NEWER using UnityEditor.ShaderGraph.Drawing.Views.Blackboard; #endif #endif using UnityEditor.ShaderGraph.Serialization; #else #endif namespace UnityEditor.ShaderGraph { public static partial class MarkdownSGExtensions { #if SRP12_SG_REFACTORED [InitializeOnLoadMethod] static void RegisterMarkdownHelpers() { MarkdownSGExtensions.RegisterCustomInspectorSetter(SetDefaultCustomInspector); } static bool SetDefaultCustomInspector(Target target, string customInspector) { if (target is BuiltInTarget builtInTarget) { if (builtInTarget.customEditorGUI.Equals(customInspector, StringComparison.Ordinal)) builtInTarget.customEditorGUI = null; else builtInTarget.customEditorGUI = customInspector; return true; } return false; } #endif internal static GraphData GetGraphData(AssetImporter importer, bool initializeGraph = false) { try { var path = importer.assetPath; var textGraph = File.ReadAllText(path, Encoding.UTF8); #if UNITY_2020_2_OR_NEWER var graph = new GraphData { assetGuid = AssetDatabase.AssetPathToGUID(path) }; MultiJson.Deserialize(graph, textGraph); #else var graph = JsonUtility.FromJson(textGraph); #endif if(initializeGraph) { graph.OnEnable(); graph.ValidateGraph(); } return graph; } catch (ArgumentException e) { Debug.LogError("Couldn't get graph data for " + importer.assetPath + ": " + e); return null; } } internal static GraphData GetGraphData(Shader shader, bool initializeGraph = false) { if (!AssetDatabase.Contains(shader)) return null; var assetPath = AssetDatabase.GetAssetPath(shader); var assetImporter = AssetImporter.GetAtPath(assetPath); if (assetImporter is ShaderGraphImporter shaderGraphImporter) return GetGraphData(shaderGraphImporter, initializeGraph); return null; } internal static void WriteShaderGraphToDisk(Shader shader, GraphData graphData) { #if UNITY_2020_2_OR_NEWER File.WriteAllText(AssetDatabase.GetAssetPath(shader), MultiJson.Serialize(graphData)); #else File.WriteAllText(AssetDatabase.GetAssetPath(shader), JsonUtility.ToJson(graphData)); #endif AssetDatabase.Refresh(); } public static string GetDefaultCustomInspectorFromShader(Shader shader) { var graphData = GetGraphData(shader); string customInspector = null; if (graphData != null) { foreach (var getter in CustomInspectorGetters) { var inspector = getter(graphData); if (!string.IsNullOrEmpty(inspector)) { customInspector = inspector; break; } } } foreach (var customGetter in CustomBaseShaderGUIGetters) { if(customGetter == null) continue; var inspector = customGetter(shader); if (!string.IsNullOrEmpty(inspector)) { customInspector = inspector; break; } } // foreach (var target in graphData.allPotentialTargets) // Debug.Log("Potential Target: " + target.displayName); // HDShaderUtils.IsHDRPShader // var shaderID = HDShaderUtils.GetShaderEnumFromShader(shader); // var hdMetadata = AssetDatabase.LoadAssetAtPath(assetPath); // Debug.Log("Shader ID: " + shaderID); // HDSubTarget.Setup return customInspector; } private const string CustomEditorGUI = "Needle.MarkdownShaderGUI"; [MenuItem("CONTEXT/Material/Toggle ShaderGraph Markdown", true)] static bool ToggleShaderGraphMarkdownValidate(MenuCommand command) { if (command.context is Material mat) return MarkdownSGExtensions.GetGraphData(mat.shader) != null; return false; } [MenuItem("CONTEXT/Material/Toggle ShaderGraph Markdown", false, 700)] static void ToggleShaderGraphMarkdown(MenuCommand command) { if (command.context is Material mat) { var graphData = MarkdownSGExtensions.GetGraphData(mat.shader); #if UNITY_2020_2_OR_NEWER foreach (var target in graphData.activeTargets) { foreach (var setter in CustomInspectorSetters) { setter(target, CustomEditorGUI); } } #else if (graphData.outputNode is UnityEditor.ShaderGraph.MasterNode masterNode) { var canChangeShaderGUI = masterNode as UnityEditor.Graphing.ICanChangeShaderGUI; // GenerationUtils.FinalCustomEditorString(canChangeShaderGUI); if (canChangeShaderGUI.OverrideEnabled) { canChangeShaderGUI.OverrideEnabled = false; canChangeShaderGUI.ShaderGUIOverride = null; } else { canChangeShaderGUI.OverrideEnabled = true; canChangeShaderGUI.ShaderGUIOverride = CustomEditorGUI; } } #endif MarkdownSGExtensions.WriteShaderGraphToDisk(mat.shader, graphData); } } private static readonly List> CustomInspectorGetters = new List>(); internal static void RegisterCustomInspectorGetter(Func func) { CustomInspectorGetters.Add(func); } private static readonly List> CustomBaseShaderGUIGetters = new List>(); public static void RegisterCustomBaseShaderGUI(Func func) { CustomBaseShaderGUIGetters.Add(func); } #if UNITY_2020_2_OR_NEWER private static readonly List> CustomInspectorSetters = new List>(); internal static void RegisterCustomInspectorSetter(Func func) { CustomInspectorSetters.Add(func); } #endif #region Blackboard Access Helpers private static FieldInfo m_InputRows; public static Dictionary GetInputRowDictionaryFromController(object controllerObject) { #if !UNITY_2021_2_OR_NEWER var provider = (BlackboardProvider) controllerObject; if (provider != null) { if(m_InputRows == null) m_InputRows = typeof(BlackboardProvider).GetField("m_InputRows", (BindingFlags) (-1)); var inputRows = (Dictionary) m_InputRows?.GetValue(provider); return inputRows?.ToDictionary(x => x.Key, x => (VisualElement) x.Value); } #endif #if UNITY_2021_2_OR_NEWER var controller = (BlackboardController) controllerObject; if(controller != null) { return controller.Model.properties.ToDictionary(x => (ShaderInput) x, x => (VisualElement) controller.GetBlackboardRow(x)); } #endif return null; } public static List GetBlackboardElements(VisualElement blackboard) { var blackboardFieldQuery = blackboard.Query().Visible().ToList(); if(blackboardFieldQuery.Count > 0) return blackboardFieldQuery.Cast().ToList(); #if SRP12_SG_REFACTORED return blackboard.Query().Visible().ToList().Cast().ToList(); #else return new List(); #endif } public static string GetBlackboardFieldText(VisualElement fieldView) { if (fieldView is BlackboardField field1) return field1.text; #if SRP12_SG_REFACTORED if (fieldView is SGBlackboardField field2) return field2.text; #endif return "unknown"; } public static void SetBlackboardFieldTypeText(VisualElement fieldView, string toString) { if (fieldView is BlackboardField field1) field1.typeText = toString; #if SRP12_SG_REFACTORED if (fieldView is SGBlackboardField field2) field2.typeText = toString; #endif } public static bool ElementIsBlackboardRow(VisualElement element) { if (element is BlackboardRow) return true; #if UNITY_2021_2_OR_NEWER if (element is SGBlackboardRow) return true; #endif return false; } #endregion public static ShaderGUI CreateShaderGUI(string defaultCustomInspector) { var type = typeof(EditorWindow).Assembly.GetType("UnityEditor.ShaderGUIUtility"); var method = type?.GetMethod("CreateShaderGUI", (BindingFlags)(-1)); if (method != null) return (ShaderGUI) method.Invoke(null, new object[]{defaultCustomInspector}); return null; } #if SRP12_SG_REFACTORED public static IEnumerable<(string categoryName, int categoryHash, IEnumerable properties)> CollectCategories(Shader s) { string path = AssetDatabase.GetAssetPath(s); ShaderGraphMetadata metadata = null; foreach (var obj in AssetDatabase.LoadAllAssetsAtPath(path)) { if (obj is ShaderGraphMetadata meta) { metadata = meta; var categories = metadata.categoryDatas .Select(x => (x.categoryName, x.GetHashCode(), x.propertyDatas.Select(x => x.referenceName))); return categories; } } return null; } #endif public class WrappedShaderKeyword { internal ShaderKeyword keyword; } public static WrappedShaderKeyword FindKeywordData(Shader shader, string keywordRef) { // collect keywords from graph and first level of child sub graphs // (shader graph only generates shader_feature / multi_compile for that first level it seems) var graphData = GetGraphData(shader); if (graphData == null) return null; var shaderKeywords = new KeywordCollector(); foreach (var node in graphData.GetNodes()) node.CollectShaderKeywords(shaderKeywords, GenerationMode.ForReals); graphData.CollectShaderKeywords(shaderKeywords, GenerationMode.ForReals); var keyword = shaderKeywords.keywords.FirstOrDefault(x => x.referenceName == keywordRef); if (keyword == null) return null; return new WrappedShaderKeyword() { keyword = keyword }; } public static void DrawShaderKeywordProperty(MaterialEditor editor, WrappedShaderKeyword keyword, string tooltip, bool showPropertyNames = false) { var mat = (Material) editor.target; switch (keyword.keyword.keywordType) { case KeywordType.Boolean: var isSet = mat.IsKeywordEnabled(keyword.keyword.referenceName); var newValue = EditorGUILayout.Toggle(new GUIContent(showPropertyNames ? keyword.keyword.referenceName : keyword.keyword.displayName, tooltip), isSet); if (isSet != newValue) { Undo.RegisterCompleteObjectUndo(mat, "Set " + keyword.keyword.displayName + " to " + newValue); if (newValue) mat.EnableKeyword(keyword.keyword.referenceName); else mat.DisableKeyword(keyword.keyword.referenceName); } break; case KeywordType.Enum: var keywordEntries = keyword.keyword.entries; var keywordReferenceName = keyword.keyword.referenceName + "_"; // get selected index based on set keywords var currentIndex = 0; for (int i = 0; i < keywordEntries.Count; i++) { if (mat.IsKeywordEnabled(keywordReferenceName + keywordEntries[i].referenceName)) { currentIndex = i; break; } } var newIndex = EditorGUILayout.Popup(new GUIContent(showPropertyNames ? keyword.keyword.referenceName : keyword.keyword.displayName, tooltip), currentIndex, keywordEntries.Select(x => x.displayName).ToArray()); if (newIndex != currentIndex) { Undo.RegisterCompleteObjectUndo(mat, "Set " + keyword.keyword.displayName + " to " + keywordReferenceName + keywordEntries[newIndex].referenceName); for (int i = 0; i < keywordEntries.Count; i++) { if (i == newIndex) mat.EnableKeyword(keywordReferenceName + keywordEntries[i].referenceName); else mat.DisableKeyword(keywordReferenceName + keywordEntries[i].referenceName); } } break; } } public static string GetShaderPathForWindow(EditorWindow wnd) { if (!(wnd is MaterialGraphEditWindow editWindow)) return null; return AssetDatabase.GUIDToAssetPath(editWindow.selectedGuid); } public static bool IsSubGraph(EditorWindow wnd) { if (!(wnd is MaterialGraphEditWindow editWindow)) return false; #if UNITY_2020_3_OR_NEWER return editWindow.graphObject != null && editWindow.graphObject.graph != null && editWindow.graphObject.graph.isSubGraph; #else return false; // graphObject not easily accessible (would need reflection) #endif } } } #endif ================================================ FILE: package/Editor/Internal/ShaderGraph/MarkdownSGExtensions.cs.meta ================================================ fileFormatVersion: 2 guid: c0665c0edaae93a488e613a5eba6cca8 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/ShaderGraph/Needle.ShaderGraphMarkdown.SGInternal.asmref ================================================ { "reference": "GUID:be0903cd8e1546f498710afdc59db5eb" } ================================================ FILE: package/Editor/Internal/ShaderGraph/Needle.ShaderGraphMarkdown.SGInternal.asmref.meta ================================================ fileFormatVersion: 2 guid: 2b229227776b1064893d170fadd807fa AssemblyDefinitionReferenceImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/ShaderGraph.meta ================================================ fileFormatVersion: 2 guid: fcc7194c6b4ab234380210378dde895c folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/URP/MarkdownURPExtensions.cs ================================================ #if !NO_INTERNALS_ACCESS && UNITY_2019_4_OR_NEWER using System; using System.Collections.Generic; using System.Reflection; using UnityEditor; using UnityEditor.ShaderGraph; using UnityEngine; #if UNITY_2020_2_OR_NEWER using UnityEditor.Rendering.Universal.ShaderGraph; #endif namespace UnityEditor.Rendering.Universal { public static class MarkdownURPExtensions { [InitializeOnLoadMethod] static void RegisterMarkdownHelpers() { MarkdownSGExtensions.RegisterCustomInspectorGetter(GetDefaultCustomInspectorFromGraphData); #if UNITY_2020_2_OR_NEWER MarkdownSGExtensions.RegisterCustomInspectorSetter(SetDefaultCustomInspector); #endif } #if UNITY_2021_2_OR_NEWER internal struct MaterialHeaderScopeItem { public GUIContent headerTitle { get; set; } public uint expandable { get; set; } public Action drawMaterialScope { get; set; } } internal class MarkdownShaderGraphLitGUI : ShaderGraphLitGUI { // needs to stay in sync with BaseShaderGUI:240.OnOpenGUI public override void OnOpenGUI(Material material, MaterialEditor materialEditor) { var m_MaterialScopeList = (MaterialHeaderScopeList) typeof(BaseShaderGUI).GetField("m_MaterialScopeList", (BindingFlags)(-1))?.GetValue(this); m_MaterialScopeList.RegisterHeaderScope(Styles.SurfaceOptions, (uint)Expandable.SurfaceOptions, DrawSurfaceOptions); // m_MaterialScopeList.RegisterHeaderScope(Styles.SurfaceInputs, (uint)Expandable.SurfaceInputs, DrawSurfaceInputs); FillAdditionalFoldouts(m_MaterialScopeList); m_MaterialScopeList.RegisterHeaderScope(Styles.AdvancedLabel, (uint)Expandable.Advanced, DrawAdvancedOptions); } } internal class MarkdownShaderGraphUnlitGUI : ShaderGraphUnlitGUI { // needs to stay in sync with BaseShaderGUI:240.OnOpenGUI public override void OnOpenGUI(Material material, MaterialEditor materialEditor) { var m_MaterialScopeList = (MaterialHeaderScopeList) typeof(BaseShaderGUI).GetField("m_MaterialScopeList", (BindingFlags)(-1))?.GetValue(this); m_MaterialScopeList.RegisterHeaderScope(Styles.SurfaceOptions, (uint)Expandable.SurfaceOptions, DrawSurfaceOptions); // m_MaterialScopeList.RegisterHeaderScope(Styles.SurfaceInputs, (uint)Expandable.SurfaceInputs, DrawSurfaceInputs); FillAdditionalFoldouts(m_MaterialScopeList); m_MaterialScopeList.RegisterHeaderScope(Styles.AdvancedLabel, (uint)Expandable.Advanced, DrawAdvancedOptions); } } #endif private static string GetDefaultCustomInspectorFromGraphData(GraphData arg) { #if UNITY_2021_2_OR_NEWER foreach (var target in arg.activeTargets) { if (target is UniversalTarget universalTarget) { switch (universalTarget.activeSubTarget) { case UniversalLitSubTarget litSubTarget: return typeof(MarkdownShaderGraphLitGUI).FullName; case UniversalUnlitSubTarget unlitSubTarget: return typeof(MarkdownShaderGraphUnlitGUI).FullName; } } } #endif return null; } #if UNITY_2020_2_OR_NEWER static bool SetDefaultCustomInspector(Target target, string customInspector) { if (target is UniversalTarget universalTarget) { if (universalTarget.customEditorGUI.Equals(customInspector, StringComparison.Ordinal)) universalTarget.customEditorGUI = null; else universalTarget.customEditorGUI = customInspector; return true; } return false; } #endif } } #endif ================================================ FILE: package/Editor/Internal/URP/MarkdownURPExtensions.cs.meta ================================================ fileFormatVersion: 2 guid: 4766915697c6c8f47bdb5a20c7439f8f MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/URP/Needle.ShaderGraphMarkdown.URPInternal.asmref ================================================ { "reference": "GUID:c579267770062bf448e75eb160330b7f" } ================================================ FILE: package/Editor/Internal/URP/Needle.ShaderGraphMarkdown.URPInternal.asmref.meta ================================================ fileFormatVersion: 2 guid: 077b6e44fa2bd90488894948876acda9 AssemblyDefinitionReferenceImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal/URP.meta ================================================ fileFormatVersion: 2 guid: 3b3c336cbcc76b44f8f5876ef83b3d84 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Internal.meta ================================================ fileFormatVersion: 2 guid: 1fd8cc8fa6e9b5c4f9bdccf086a8f883 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/MarkdownShaderGUI.cs ================================================ #if UNITY_2021_2_OR_NEWER #define HAVE_VALIDATE_MATERIAL #if RP_CORE_7_OR_NEWER #define HAVE_HEADER_FOLDOUT_WITH_DOCS #endif #if SHADERGRAPH_7_OR_NEWER #define SRP12_SG_REFACTORED #endif #endif using System; using System.Linq; using System.Collections.Generic; using System.Reflection; using Needle.Editors; using UnityEngine; using UnityEditor; using UnityEditor.Rendering; using Needle.ShaderGraphMarkdown; using Needle.ShaderGraphMarkdown.LogicExpressionParser; using Unity.Profiling; using UnityEditorInternal; #if SHADERGRAPH_7_OR_NEWER using UnityEditor.ShaderGraph; #endif using UnityEngine.Rendering; #if HDRP_7_OR_NEWER using UnityEditor.Rendering.HighDefinition; #endif // we're making an exception here: only Needle namespace, because full namespace // has to be included in the ShaderGraph custom ui field. namespace Needle { [InitializeOnLoadAttribute] static class EditorHeaderNeedleIcon { static EditorHeaderNeedleIcon() { Editor.finishedDefaultHeaderGUI += DisplayLogoInHeader; } static void DisplayLogoInHeader(Editor editor) { if (editor is MaterialEditor matEditor && matEditor.customShaderGUI is MarkdownShaderGUI) { var logo = MarkdownNeedleIcons.LogoButton; if (logo) { var rect = new Rect(); rect.width = 20; rect.height = 20; rect.y = 2; rect.x = EditorGUIUtility.currentViewWidth - 70; if (Event.current.type == EventType.Repaint) GUI.DrawTexture(rect, logo, ScaleMode.ScaleToFit); EditorGUIUtility.AddCursorRect(rect, MouseCursor.Link); if (Event.current.type == EventType.MouseUp && rect.Contains(Event.current.mousePosition)) Application.OpenURL("https://needle.tools"); } } } } // ReSharper disable once ClassNeverInstantiated.Global public class MarkdownShaderGUI : ShaderGUI { private class HeaderGroup { public readonly string name; public readonly string condition; public List properties; public Action customDrawer = null; public bool expandedByDefault = true; private int hash = 0; public string FoldoutStateKeyName => $"{nameof(MarkdownShaderGUI)}.{name}.{hash}"; public HeaderGroup(string displayName, string condition = null, int hash = 0) { if (!string.IsNullOrEmpty(displayName) && displayName.EndsWith("-", StringComparison.Ordinal)) { expandedByDefault = false; name = displayName.Substring(0, displayName.Length - 1); } else { name = displayName; } this.condition = condition; this.hash = hash; } } private static GUIStyle centeredGreyMiniLabel; private static GUIStyle CenteredGreyMiniLabel { get { if (centeredGreyMiniLabel == null) { centeredGreyMiniLabel = new GUIStyle(GUI.skin.FindStyle("MiniLabel") ?? EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector).FindStyle("MiniLabel")) { alignment = TextAnchor.MiddleLeft, normal = { textColor = Color.gray } }; } return centeredGreyMiniLabel; } } private static readonly Dictionary drawerCache = new Dictionary(); private readonly List referencedProperties = new List(); internal enum MarkdownProperty { None, Reference, Link, Note, Drawer, Header, Foldout, Tooltip, } private static readonly string refFormat = "!REF"; private static readonly string noteFormat = "!NOTE"; private static readonly string alternateNoteFormat = "* "; private static readonly string tooltipFormat = "!TIP"; private static readonly string alternateTooltipFormat = "!TOOLTIP"; private static readonly string drawerFormat = "!DRAWER"; private static readonly string foldoutHeaderFormat = "#"; private static readonly string foldoutHeaderFormatStart = foldoutHeaderFormat + " "; private static readonly string headerFormat = "##"; private static readonly string headerFormatStart = headerFormat + " "; private static readonly string headerFormatStartLabel = headerFormat + "# "; private static MarkdownProperty GetMarkdownTypeUncached(string display) { if (display.StartsWith(refFormat)) return MarkdownProperty.Reference; if (display.StartsWith("[") && display.IndexOf("]", StringComparison.Ordinal) > -1 && display.IndexOf("(", StringComparison.Ordinal) > -1 && display.EndsWith(")")) return MarkdownProperty.Link; if (display.StartsWith(noteFormat) || display.StartsWith(alternateNoteFormat)) return MarkdownProperty.Note; if (display.StartsWith(tooltipFormat) || display.StartsWith(alternateTooltipFormat)) return MarkdownProperty.Tooltip; if (display.StartsWith(drawerFormat)) return MarkdownProperty.Drawer; if (display.StartsWith(foldoutHeaderFormatStart) || display.Equals(foldoutHeaderFormat, StringComparison.Ordinal)) return MarkdownProperty.Foldout; if (display.StartsWith(headerFormatStart) || display.StartsWith(headerFormatStartLabel) || display.Equals(headerFormat, StringComparison.Ordinal)) return MarkdownProperty.Header; return MarkdownProperty.None; } private static Dictionary propertyTypeCache = new Dictionary(); internal static MarkdownProperty GetMarkdownType(string display) { if (propertyTypeCache.ContainsKey(display)) return propertyTypeCache[display]; var type = GetMarkdownTypeUncached(display); propertyTypeCache.Add(display, type); return type; } internal static int GetIndentLevel(string display) { int indent = 0; for(int i = 0; i < display.Length; i++) { if (display[i].Equals('-')) indent++; else break; } return indent; } private bool showOriginalPropertyList = false, _showPropertyNames = false, debugConditionalProperties = false, debugReferencedProperties = false; private bool shortcutShowPropertyNames = false; private bool showPropertyNames => _showPropertyNames || shortcutShowPropertyNames; private bool debugPropertyDrawers = false, debugMarkdownGroups = false, showBaseShaderGuiOptions = true; // Reflection Access // ReSharper disable InconsistentNaming private static Type MaterialPropertyHandler; private static MethodInfo GetHandler; private static FieldInfo m_PropertyDrawer, m_DecoratorDrawers; private bool debugLocalAndGlobalKeywords = false; #if UNITY_2021_2_OR_NEWER private Dictionary localKeywords; private Dictionary globalKeywords; #else private List localKeywords; private List globalKeywords; private static MethodInfo getShaderLocalKeywords; private static MethodInfo GetShaderLocalKeywords { get { if (getShaderLocalKeywords == null) getShaderLocalKeywords = typeof(ShaderUtil).GetMethod("GetShaderLocalKeywords", (BindingFlags) (-1)); return getShaderLocalKeywords; } } private static MethodInfo getShaderGlobalKeywords; private static MethodInfo GetShaderGlobalKeywords { get { if (getShaderGlobalKeywords == null) getShaderGlobalKeywords = typeof(ShaderUtil).GetMethod("GetShaderGlobalKeywords", (BindingFlags) (-1)); return getShaderGlobalKeywords; } } #endif // ReSharper restore InconsistentNaming private static MaterialProperty FindKeywordProperty(string keywordRef, MaterialProperty[] properties) { var keywordProp = ShaderGUI.FindProperty(keywordRef, properties, false); // special case: bool properties have to be named MY_PROP_ON in ShaderGraph to be exposed, // but the actual property is named "MY_PROP" and the keyword is still "MY_PROP_ON". if (keywordProp == null && keywordRef.EndsWith("_ON", StringComparison.Ordinal)) keywordProp = ShaderGUI.FindProperty(keywordRef.Substring(0, keywordRef.Length - "_ON".Length), properties, false); return keywordProp; } internal static MarkdownMaterialPropertyDrawer GetCachedDrawer(string objectName) { if(!drawerCache.ContainsKey(objectName) || !drawerCache[objectName]) { var objectPath = AssetDatabase.FindAssets($"t:{nameof(MarkdownMaterialPropertyDrawer)} {objectName}").Select(AssetDatabase.GUIDToAssetPath).FirstOrDefault(); var scriptableObject = default(MarkdownMaterialPropertyDrawer); if(!string.IsNullOrEmpty(objectPath)) scriptableObject = AssetDatabase.LoadAssetAtPath(objectPath); if (!scriptableObject) { // create a drawer instance in memory var drawerType = TypeCache .GetTypesDerivedFrom() .FirstOrDefault(x => x.Name.Equals(objectName, StringComparison.Ordinal)); scriptableObject = (MarkdownMaterialPropertyDrawer) ScriptableObject.CreateInstance(drawerType); if (!scriptableObject && !objectName.EndsWith("Drawer", StringComparison.Ordinal)) { var longName = objectName + "Drawer"; if (drawerCache.ContainsKey(longName) && drawerCache[longName]) scriptableObject = drawerCache[longName]; else scriptableObject = GetCachedDrawer(longName); } } if (drawerCache.ContainsKey(objectName)) drawerCache[objectName] = scriptableObject; else drawerCache.Add(objectName, scriptableObject); } return drawerCache[objectName]; } public override void OnClosed(Material material) { headerGroups?.Clear(); headerGroups = null; lastHash = -1; base.OnClosed(material); } private int lastHash; private List headerGroups = null; #if SRP12_SG_REFACTORED private Dictionary propertyToCategory = null; private Dictionary categoryToHeaderGroup = null; #endif private string nextTooltip = null; private string UseTooltip() { if (nextTooltip == null) return null; var val = nextTooltip; nextTooltip = null; return val; } public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { var targetMat = materialEditor.target as Material; if (!targetMat) return; OnGUIMarker.Begin(); // Use default labelWidth EditorGUIUtility.labelWidth = 0f; // proper widths for texture and label fields, same as ShaderGUI EditorGUIUtility.fieldWidth = 64f; // keyboard shortcut overrides shortcutShowPropertyNames = Event.current.shift; int GetTargetMaterialHashCode() { var hashCode = targetMat.name.GetHashCode(); hashCode = (hashCode * 397) ^ (targetMat.shader ? targetMat.shader.name.GetHashCode() : 0); foreach(var prop in properties) { hashCode = (hashCode * 397) ^ prop.name.GetHashCode(); hashCode = (hashCode * 397) ^ prop.displayName.GetHashCode(); hashCode = (hashCode * 397) ^ prop.rangeLimits.GetHashCode(); #if UNITY_6000_2_OR_NEWER hashCode = (hashCode * 397) ^ prop.propertyFlags.GetHashCode(); #else hashCode = (hashCode * 397) ^ prop.flags.GetHashCode(); #endif // need to hash values as well since it seems MaterialProperties are changing whenever a value is changed/undone etc. hashCode = (hashCode * 397) ^ prop.floatValue.GetHashCode(); hashCode = (hashCode * 397) ^ prop.colorValue.GetHashCode(); hashCode = (hashCode * 397) ^ prop.vectorValue.GetHashCode(); if(prop.textureValue) hashCode = (hashCode * 397) ^ prop.textureValue.GetHashCode(); hashCode = (hashCode * 397) ^ prop.textureScaleAndOffset.GetHashCode(); } return hashCode; } GetHashCodeMarker.Begin(); int currentHash = GetTargetMaterialHashCode(); if (lastHash != currentHash) { lastHash = currentHash; MaterialChanged(materialEditor, properties); headerGroups?.Clear(); } GetHashCodeMarker.End(); if (headerGroups == null) headerGroups = new List(); // split by header properties if(headerGroups.Count < 1) { GenerateHeaderGroupsMarker.Begin(); headerGroups.Add(new HeaderGroup("Default")); #if SRP12_SG_REFACTORED // on 2021.2+, we need to also collect Blackboard Categories, which are stored in a sub asset var blackboardCategories = MarkdownSGExtensions.CollectCategories(targetMat.shader)?.ToList(); propertyToCategory = new Dictionary(); categoryToHeaderGroup = new Dictionary(); if(blackboardCategories != null) { foreach (var cat in blackboardCategories) { var firstPropertyInCategory = cat.properties.FirstOrDefault(); if(firstPropertyInCategory != null) propertyToCategory.Add(firstPropertyInCategory, cat.categoryHash); } foreach (var cat in blackboardCategories) { var display = cat.categoryName; var condition = GetBetween(display, '[', ']', true); if (!string.IsNullOrWhiteSpace(condition)) display = display.Substring(0, display.LastIndexOf('[')).TrimEnd(); var group = new HeaderGroup(display, condition, cat.categoryHash); categoryToHeaderGroup.Add(cat.categoryHash, group); } } #endif foreach (var prop in properties) { var display = prop.displayName; if(display.StartsWith(foldoutHeaderFormatStart) || display.Equals(foldoutHeaderFormat, StringComparison.Ordinal)) { var condition = GetBetween(display, '[', ']', true); if (!string.IsNullOrWhiteSpace(condition)) display = display.Substring(0, display.LastIndexOf('[')).TrimEnd(); if (display.Equals(foldoutHeaderFormat, StringComparison.Ordinal) || (display.StartsWith(foldoutHeaderFormatStart + "(", StringComparison.Ordinal) && display.EndsWith(")", StringComparison.Ordinal))) // for multiple # (1) foldout breakers) headerGroups.Add(new HeaderGroup(null, condition)); else { // remove "# " from start display = display.Substring(display.IndexOf(' ') + 1); headerGroups.Add(new HeaderGroup(display, condition)); } } else { #if SRP12_SG_REFACTORED if (propertyToCategory.ContainsKey(prop.name)) { var categoryHeaderGroup = categoryToHeaderGroup[propertyToCategory[prop.name]]; headerGroups.Add(categoryHeaderGroup); } #endif var headerGroup = headerGroups.Last(); if (headerGroup.properties == null) headerGroup.properties = new List(); // need to process REF/DRAWER properties early so we can hide them properly if needed display = display.TrimStart('-'); var searchForReferencedProperties = false; // don't collect properties for drawers/references that are conditionally excluded var markdownType = GetMarkdownType(display); switch (markdownType) { case MarkdownProperty.Foldout: case MarkdownProperty.Header: case MarkdownProperty.Link: case MarkdownProperty.Note: case MarkdownProperty.Tooltip: case MarkdownProperty.None: break; case MarkdownProperty.Reference: case MarkdownProperty.Drawer: searchForReferencedProperties = true; break; } if(searchForReferencedProperties) { // TODO unsure about this: there are cases where both excluding and including such properties seems desired. // var condition = GetBetween(display, '[', ']', true); // if (!string.IsNullOrEmpty(condition) && !ConditionIsFulfilled(targetMat, condition)) // continue; var split = display.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); if (split.Length < 2) continue; switch (markdownType) { case MarkdownProperty.Reference: var keywordRef = split[1]; var keywordProp = FindKeywordProperty(keywordRef, properties); if(keywordProp != null) referencedProperties.Add(keywordProp); break; case MarkdownProperty.Drawer: var objectName = split[1]; var drawer = GetCachedDrawer(objectName); if (drawer != null) { var referencedProps = drawer.GetReferencedProperties(materialEditor, properties, new MarkdownMaterialPropertyDrawer.DrawerParameters(split)); if(referencedProps != null) referencedProperties.AddRange(referencedProps); } break; } } headerGroup.properties.Add(prop); } } headerGroups.Add(new HeaderGroup(null) { properties = null, customDrawer = DrawCustomGUI }); headerGroups.Add(new HeaderGroup(MarkdownToolsLabel) { properties = null, customDrawer = DrawDebugGroupContent, expandedByDefault = false}); GenerateHeaderGroupsMarker.End(); } void DrawDebugGroupContent() { DrawDebugGroupContentMarker.Begin(); EditorGUILayout.LabelField("Utilities", EditorStyles.boldLabel); if (shortcutShowPropertyNames) { EditorGUI.showMixedValue = true; EditorGUILayout.Toggle(ShowPropertyNames, _showPropertyNames); EditorGUI.showMixedValue = false; } else { _showPropertyNames = EditorGUILayout.Toggle(ShowPropertyNames, _showPropertyNames); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Refactoring"); if (GUILayout.Button(RenameShaderProperties, EditorStyles.miniButton)) ShowRefactoringWindow(AssetDatabase.GetAssetPath(targetMat.shader), ""); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.LabelField("Markdown Debugging", EditorStyles.boldLabel); EditorGUI.BeginChangeCheck(); showOriginalPropertyList = EditorGUILayout.Toggle(ShowOriginalProperties, showOriginalPropertyList); if (EditorGUI.EndChangeCheck()) InitializeCustomGUI(targetMat); debugConditionalProperties = EditorGUILayout.Toggle(DebugConditionalProperties, debugConditionalProperties); debugReferencedProperties = EditorGUILayout.Toggle(DebugReferencedProperties, debugReferencedProperties); EditorGUILayout.Space(); EditorGUILayout.LabelField(ShaderKeywords, EditorStyles.boldLabel); foreach (var kw in targetMat.shaderKeywords) { EditorGUILayout.TextField(kw, EditorStyles.miniLabel); } EditorGUILayout.Space(); var newVal = EditorGUILayout.Foldout(debugLocalAndGlobalKeywords, LocalAndGlobalKeywords); if (newVal != debugLocalAndGlobalKeywords) { debugLocalAndGlobalKeywords = newVal; // enforce refresh when opening / closing the foldout if (debugLocalAndGlobalKeywords) CollectLocalAndGlobalKeywords(targetMat); } if(debugLocalAndGlobalKeywords) { EditorGUILayout.LabelField(LocalKeywords, EditorStyles.boldLabel); EditorGUI.indentLevel++; EditorGUI.indentLevel++; foreach (var kw in localKeywords) { #if UNITY_2021_2_OR_NEWER var title = kw.Value.name + " " + (kw.Value.type != ShaderKeywordType.UserDefined ? "[" + kw.Value.type + "]" : "") + (kw.Value.isOverridable ? " [Overridable]" : ""); var isOn = targetMat.shaderKeywords.Contains(kw.Value.name); #else var title = kw; var isOn = targetMat.shaderKeywords.Contains(kw); #endif EditorGUILayout.TextField(title, EditorStyles.miniLabel); var lastRect = GUILayoutUtility.GetLastRect(); lastRect.xMin -= 32; lastRect.xMax = lastRect.xMin += 16; EditorGUI.BeginDisabledGroup(true); EditorGUI.ToggleLeft(lastRect, GUIContent.none, isOn); EditorGUI.EndDisabledGroup(); } EditorGUI.indentLevel--; EditorGUI.indentLevel--; EditorGUILayout.LabelField(GlobalKeywords, EditorStyles.boldLabel); EditorGUI.indentLevel++; EditorGUI.indentLevel++; foreach (var kw in globalKeywords) { #if UNITY_2021_2_OR_NEWER var title = kw.Value.name; var isOn = Shader.IsKeywordEnabled(kw.Value); #else var title = kw; var isOn = Shader.IsKeywordEnabled(kw); #endif EditorGUILayout.TextField(title, EditorStyles.miniLabel); var lastRect = GUILayoutUtility.GetLastRect(); lastRect.xMin -= 32; lastRect.xMax = lastRect.xMin += 16; EditorGUI.BeginDisabledGroup(true); EditorGUI.ToggleLeft(lastRect, GUIContent.none, isOn); EditorGUI.EndDisabledGroup(); } EditorGUI.indentLevel--; EditorGUI.indentLevel--; } GUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Reset"); if (GUILayout.Button(ClearKeywords)) { foreach (var kw in targetMat.shaderKeywords) targetMat.DisableKeyword(kw); #if HDRP_7_OR_NEWER try { HDShaderUtils.ResetMaterialKeywords(targetMat); } catch (ArgumentException) { // ignore, not a HDRP shader probably } #endif ValidateMaterial(targetMat); // resetting the shader seems to trigger keyword sanitization for ShaderGraph shaders and some other Unity shaders baseShaderGui?.AssignNewShaderToMaterial(targetMat, targetMat.shader, targetMat.shader); } GUILayout.EndHorizontal(); if (IsDeveloperMode()) { DrawDeveloperModeContent(); } EditorGUILayout.Space(); // #if HDRP_7_OR_NEWER // EditorGUILayout.LabelField("ShaderGraph Info", EditorStyles.boldLabel); // if (GUILayout.Button("Refresh")) // { // Debug.Log(MarkdownHDExtensions.GetDefaultCustomInspectorFromShader(targetMat.shader)); // } // #endif DrawDebugGroupContentMarker.End(); } void DrawDeveloperModeContent() { EditorGUILayout.Space(); EditorGUILayout.LabelField(DevelopmentOptionsLabel, EditorStyles.boldLabel); showBaseShaderGuiOptions = EditorGUILayout.Foldout(showBaseShaderGuiOptions, BaseShaderGUIOptions); if (showBaseShaderGuiOptions) { EditorGUI.indentLevel++; EditorGUILayout.TextField("Type", baseShaderGui != null ? baseShaderGui.GetType().ToString() : "none", EditorStyles.miniLabel); if(baseShaderGui != null) { if (GUILayout.Button("Validate Material")) ValidateMaterial(targetMat); if (GUILayout.Button("Re-Assign Shader")) baseShaderGui.AssignNewShaderToMaterial(targetMat, targetMat.shader, targetMat.shader); } EditorGUI.indentLevel--; } debugPropertyDrawers = EditorGUILayout.Foldout(debugPropertyDrawers, PropertyDrawersAndDecorators); if (debugPropertyDrawers) { foreach (var prop in properties) { if (MaterialPropertyHandler == null) MaterialPropertyHandler = typeof(MaterialProperty).Assembly.GetType("UnityEditor.MaterialPropertyHandler"); if (MaterialPropertyHandler != null) { if(GetHandler == null) GetHandler = MaterialPropertyHandler.GetMethod("GetHandler", (BindingFlags) (-1)); var handler = GetHandler.Invoke(null, new object[] {((Material) materialEditor.target).shader, prop.name}); if(m_PropertyDrawer == null) m_PropertyDrawer = MaterialPropertyHandler.GetField("m_PropertyDrawer", (BindingFlags) (-1)); if(m_DecoratorDrawers == null) m_DecoratorDrawers = MaterialPropertyHandler.GetField("m_DecoratorDrawers", (BindingFlags) (-1)); if(handler != null) { MaterialPropertyDrawer propertyDrawer = (MaterialPropertyDrawer) m_PropertyDrawer.GetValue(handler); List decoratorDrawers = (List) m_DecoratorDrawers.GetValue(handler); if (propertyDrawer != null || decoratorDrawers != null) { #if UNITY_6000_2_OR_NEWER EditorGUILayout.LabelField($@"{prop.name}(""{prop.displayName}"", {prop.propertyType})"); #else EditorGUILayout.LabelField($@"{prop.name}(""{prop.displayName}"", {prop.type})"); #endif } EditorGUI.indentLevel++; if(propertyDrawer != null) { EditorGUILayout.LabelField("Property Drawer", EditorStyles.miniLabel); EditorGUILayout.LabelField(propertyDrawer.GetType() + " (height: " + propertyDrawer.GetPropertyHeight(prop, prop.displayName, materialEditor) + "px)"); } if(decoratorDrawers != null) { EditorGUILayout.LabelField("Decorator Drawers", EditorStyles.miniLabel); foreach (var d in decoratorDrawers) { EditorGUILayout.LabelField(d.GetType() + " (height: " + d.GetPropertyHeight(prop, prop.displayName, materialEditor) + "px)"); } } EditorGUI.indentLevel--; } } // MaterialPropertyHandler handler = MaterialPropertyHandler.GetHandler(((Material) materialEditor.target).shader, prop.name); } } debugMarkdownGroups = EditorGUILayout.Foldout(debugMarkdownGroups, GroupsAndCategories); if (debugMarkdownGroups) { EditorGUI.BeginDisabledGroup(true); foreach (var group in headerGroups) { EditorGUILayout.LabelField(group.name == null ? "" : string.IsNullOrWhiteSpace(group.name) ? "" : group.name, EditorStyles.boldLabel); EditorGUILayout.LabelField("Condition", group.condition == null ? "" : group.condition); EditorGUILayout.LabelField("Custom Drawer", group.customDrawer?.Method?.Name ?? ""); EditorGUILayout.LabelField("Property Count", "" + (group.properties?.Count ?? 0)); EditorGUILayout.Space(); } EditorGUI.EndDisabledGroup(); } EditorGUILayout.Space(); GUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Inspector"); if (GUILayout.Button(RedrawInspector)) { drawerCache.Clear(); headerGroups.Clear(); GUIUtility.ExitGUI(); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Foldouts"); if (GUILayout.Button(ResetFoldoutSessionState)) { foreach(var group in headerGroups) { SessionState.EraseBool(group.FoldoutStateKeyName); } } GUILayout.EndHorizontal(); } void DrawCustomGUI() { DrawCustomGUIMarker.Begin(); if (!haveSearchedForCustomGUI) InitializeCustomGUI(targetMat); // This assumes that all base shader GUIs draw the advanced options themselves (e.g. instancing, queue, ...) if(baseShaderGui == null) { EditorGUILayout.Space(); EditorGUILayout.LabelField(AdditionalOptions, EditorStyles.boldLabel); EditorGUILayout.Space(); // from MaterialEditor.PropertiesDefaultGUI(properties); if (SupportedRenderingFeatures.active.editableMaterialRenderQueue) materialEditor.RenderQueueField(); materialEditor.EnableInstancingField(); materialEditor.DoubleSidedGIField(); // if the material exposes _EMISSION as user-changeable property, we only want to draw the lightmap settings when its on. // otherwise, we always draw it when the known emission properties exist if (targetMat.HasProperty(EmissionColorPropertyName) || targetMat.HasProperty(EmissionMapPropertyName) || targetMat.HasProperty(EmissionColorPropertyName2) || targetMat.HasProperty(EmissionMapPropertyName2)) if (!(targetMat.HasProperty(EmissionKeyword) || targetMat.HasProperty("_Emission")) || targetMat.IsKeywordEnabled(EmissionKeyword)) materialEditor.LightmapEmissionFlagsProperty(0, true, true); EditorGUILayout.Space(); CoreEditorUtils.DrawSplitter(); } try { if (baseShaderGui != null) { // only pass in the properties that are hidden - this allows us to render all custom HDRP ShaderGraph UIs. baseShaderGui?.OnGUI(materialEditor, showOriginalPropertyList ? properties : properties #if UNITY_6000_2_OR_NEWER .Where(x => x.propertyFlags.HasFlag(ShaderPropertyFlags.HideInInspector)) #else .Where(x => x.flags.HasFlag(MaterialProperty.PropFlags.HideInInspector)) #endif .ToArray()); CoreEditorUtils.DrawSplitter(); } } catch (Exception e) { EditorGUILayout.HelpBox("Exception when drawing base shader GUI of type " + baseShaderGui?.GetType() + ":\n" + e, MessageType.Error); } DrawCustomGUIMarker.End(); } void DrawGroup(HeaderGroup group) { DrawGroupMarker.Begin(); bool previousPropertyWasDrawn = true; bool nextPropertyDrawerShouldBeInline = false; for(int i = 0; i < group.properties.Count; i++) { var prop = group.properties[i]; var display = prop.displayName; var isDisabled = false; var hasCondition = !display.StartsWith("[", StringComparison.Ordinal) && display.Contains('[') && display.EndsWith("]", StringComparison.Ordinal); if (hasCondition) { var condition = GetBetween(display, '[', ']', true); if (!string.IsNullOrEmpty(condition)) { if (!ConditionIsFulfilled(targetMat, condition)) { // consume tooltip for conditionally excluded properties UseTooltip(); if(!debugConditionalProperties) { previousPropertyWasDrawn = false; continue; } isDisabled = true; EditorGUI.BeginDisabledGroup(true); } if(!debugConditionalProperties) display = display.Substring(0, display.IndexOf('[') - 1); } } var currentIndent = EditorGUI.indentLevel; EditorGUI.indentLevel = currentIndent + GetIndentLevel(display); display = display.TrimStart('-'); switch (GetMarkdownType(display)) { case MarkdownProperty.Link: if (!previousPropertyWasDrawn) continue; var linkText = GetBetween(display, '[', ']'); var linkHref = GetBetween(display, '(', ')'); if (GUILayout.Button(new GUIContent(linkText, UseTooltip()), CenteredGreyMiniLabel)) Application.OpenURL(linkHref); break; case MarkdownProperty.Note: if (!previousPropertyWasDrawn) continue; var noteText = display.Substring(display.IndexOf(' ') + 1); EditorGUILayout.LabelField(new GUIContent(noteText, UseTooltip()), CenteredGreyMiniLabel); break; case MarkdownProperty.Tooltip: nextTooltip = display.Substring(display.IndexOf(' ') + 1); break; case MarkdownProperty.Drawer: var parts = display.Split(' '); if(parts.Length > 1) { var objectName = parts[1]; var drawer = GetCachedDrawer(objectName); if(drawer) { try { if (nextPropertyDrawerShouldBeInline) { if(drawer.SupportsInlineDrawing) { var rect = InlineTextureDrawer.LastInlineTextureRect; rect.xMin += EditorGUI.indentLevel * 15f; var previousIndent = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; drawer.OnInlineDrawerGUI(InlineTextureDrawer.LastInlineTextureRect, materialEditor, properties, new MarkdownMaterialPropertyDrawer.DrawerParameters(parts, UseTooltip())); EditorGUI.indentLevel = previousIndent; } else { EditorGUI.LabelField(InlineTextureDrawer.LastInlineTextureRect, drawer + " doesn't support inline drawing", EditorStyles.miniLabel); drawer.OnDrawerGUI(materialEditor, properties, new MarkdownMaterialPropertyDrawer.DrawerParameters(parts, UseTooltip())); } } else { drawer.OnDrawerGUI(materialEditor, properties, new MarkdownMaterialPropertyDrawer.DrawerParameters(parts, UseTooltip(), showPropertyNames)); } } catch (Exception e) { EditorGUILayout.HelpBox("Error in Custom Drawer \"" + objectName + "\":\n" + e.Message, MessageType.Error); } finally { nextPropertyDrawerShouldBeInline = false; } } else EditorGUILayout.HelpBox("Custom Drawer \"" + objectName + "\" not found.", MessageType.Error); } else { EditorGUILayout.HelpBox("No Drawer specified.", MessageType.Warning); } previousPropertyWasDrawn = true; break; case MarkdownProperty.Header: var stringIndex = display.IndexOf(' ') + 1; if(stringIndex > 0 && !(display.StartsWith(headerFormatStart + "(", StringComparison.Ordinal) && display.EndsWith(")", StringComparison.Ordinal))) { var labelName = display.Substring(stringIndex); if(display.StartsWith(headerFormatStartLabel)) { EditorGUILayout.LabelField(new GUIContent(labelName, UseTooltip())); } else { EditorGUILayout.Space(); EditorGUILayout.LabelField(new GUIContent(labelName, UseTooltip()), EditorStyles.boldLabel); } } else { EditorGUILayout.Space(); } previousPropertyWasDrawn = true; break; case MarkdownProperty.Reference: var split = display.Split(' '); if (split.Length > 1) { var keywordRef = split[1]; var keywordProp = FindKeywordProperty(keywordRef, properties); var foundKeywordToDraw = false; #if SHADERGRAPH_7_OR_NEWER // special case: the keyword might be defined in a subgraph if (keywordProp == null) { var keyword = MarkdownSGExtensions.FindKeywordData(targetMat.shader, keywordRef); if (keyword != null) { MarkdownSGExtensions.DrawShaderKeywordProperty(materialEditor, keyword, UseTooltip(), showPropertyNames); foundKeywordToDraw = true; } } #endif if (!foundKeywordToDraw) { if (keywordProp == null) { #if UNITY_2021_2_OR_NEWER if (globalKeywords.ContainsKey(keywordRef)) #else if (globalKeywords.Contains(keywordRef)) #endif { EditorGUI.BeginChangeCheck(); var newVal = EditorGUILayout.Toggle(keywordRef, Shader.IsKeywordEnabled(keywordRef)); if (EditorGUI.EndChangeCheck()) { if (newVal) Shader.EnableKeyword(keywordRef); else Shader.DisableKeyword(keywordRef); } } else { EditorGUILayout.HelpBox("Could not find MaterialProperty: '" + keywordRef, MessageType.Error); } } else materialEditor.ShaderProperty(keywordProp, new GUIContent(showPropertyNames ? keywordProp.name : keywordProp.displayName, UseTooltip())); } } previousPropertyWasDrawn = true; break; // case MarkdownProperty.None: default: if(referencedProperties.Contains(prop)) { if (debugReferencedProperties) { EditorGUI.BeginDisabledGroup(true); materialEditor.ShaderProperty(prop, display); EditorGUI.EndDisabledGroup(); } previousPropertyWasDrawn = false; break; } #if UNITY_6000_2_OR_NEWER if(prop.propertyFlags.HasFlag(ShaderPropertyFlags.HideInInspector)) break; if(prop.propertyFlags.HasFlag(ShaderPropertyFlags.PerRendererData)) break; #else if(prop.flags.HasFlag(MaterialProperty.PropFlags.HideInInspector)) break; if(prop.flags.HasFlag(MaterialProperty.PropFlags.PerRendererData)) break; #endif // check if drawer shorthand + parameters var indexOfShorthand = display.IndexOf("&&", StringComparison.Ordinal); var isShorthandWithParameters = indexOfShorthand > 0 && indexOfShorthand < display.Length - 2; // drawer shorthands if (isShorthandWithParameters || display.EndsWith("&", StringComparison.Ordinal) || #if UNITY_6000_2_OR_NEWER (prop.propertyType == ShaderPropertyType.Texture && prop.propertyFlags.HasFlag(ShaderPropertyFlags.NonModifiableTextureData))) // display non-modifiable textures inlined by default #else (prop.type == MaterialProperty.PropType.Texture && prop.flags.HasFlag(MaterialProperty.PropFlags.NonModifiableTextureData))) // display non-modifiable textures inlined by default #endif { bool shouldDrawMultiplePropertiesInline = display.EndsWith("&&", StringComparison.Ordinal); var parameters = default(MarkdownMaterialPropertyDrawer.DrawerParameters); if (isShorthandWithParameters) { // extract parameters var parameterRemainder = display.Substring(indexOfShorthand + 2).Trim(); parameters = new MarkdownMaterialPropertyDrawer.DrawerParameters(("!DRAWER Dummy " + prop.name + " " + parameterRemainder) .Split(new [] {' '}, StringSplitOptions.RemoveEmptyEntries), UseTooltip()); display = display.Substring(0, indexOfShorthand); } var trimmedDisplay = display.Trim(' ', '&'); #if UNITY_6000_2_OR_NEWER if(prop.propertyType == ShaderPropertyType.Texture) #else if(prop.type == MaterialProperty.PropType.Texture) #endif { var drawer = (InlineTextureDrawer) GetCachedDrawer(nameof(InlineTextureDrawer)); if (drawer) { if (isShorthandWithParameters) { drawer.OnDrawerGUI(materialEditor, properties, parameters); // exclude parameter properties from further regular rendering for (int k = 1; k < parameters.Count; k++) { var j = k; var referencedProperty = properties.FirstOrDefault(x => x.name == parameters.Get(j, "")); if (referencedProperty != null && !referencedProperties.Contains(referencedProperty)) referencedProperties.Add(referencedProperty); } } else { MaterialProperty extraProperty = null; if (shouldDrawMultiplePropertiesInline) { extraProperty = (i + 1 < group.properties.Count) ? group.properties[i + 1] : null; if (extraProperty != null) { #if UNITY_6000_2_OR_NEWER if (extraProperty.propertyFlags.HasFlag(ShaderPropertyFlags.HideInInspector)) #else if (extraProperty.flags.HasFlag(MaterialProperty.PropFlags.HideInInspector)) #endif { extraProperty = null; } else if (GetMarkdownType(extraProperty.displayName) == MarkdownProperty.Drawer) { extraProperty = null; nextPropertyDrawerShouldBeInline = true; } // check if this is a special property, not supported right now // also we don't want to draw if hidden (e.g. unity_Lightmaps) else if (GetMarkdownType(extraProperty.displayName) != MarkdownProperty.None) { extraProperty = null; } // add this to the referenced list so we don't draw it twice if (extraProperty != null && !referencedProperties.Contains(extraProperty)) { referencedProperties.Add(extraProperty); } } } drawer.OnDrawerGUI(materialEditor, properties, prop, new GUIContent(showPropertyNames ? prop.name + (extraProperty != null ? " & " + extraProperty.name : "") : trimmedDisplay, UseTooltip()), extraProperty); } } } #if UNITY_6000_2_OR_NEWER else if (prop.propertyType == ShaderPropertyType.Vector) #else else if (prop.type == MaterialProperty.PropType.Vector) #endif { var drawer = (VectorSliderDrawer) GetCachedDrawer(nameof(VectorSliderDrawer)); if (drawer) { drawer.OnDrawerGUI(materialEditor, prop, new GUIContent(showPropertyNames ? prop.name : trimmedDisplay, UseTooltip())); } } } else { materialEditor.ShaderPropertyWithTooltip(prop, new GUIContent(showPropertyNames ? prop.name : display, UseTooltip())); } previousPropertyWasDrawn = true; break; } EditorGUI.indentLevel = currentIndent; if(isDisabled) EditorGUI.EndDisabledGroup(); } EditorGUILayout.Space(); DrawGroupMarker.End(); } foreach(var group in headerGroups) { if (group.properties == null && group.customDrawer == null) continue; bool isDisabled = false; if (group.condition != null) { if (!ConditionIsFulfilled(targetMat, group.condition)) { if (debugConditionalProperties) isDisabled = true; else continue; } } DrawHeaderGroupMarker.Begin(); if (isDisabled) EditorGUI.BeginDisabledGroup(true); EditorGUI.BeginChangeCheck(); if (string.IsNullOrEmpty(group.name) || group.name.Equals("Default", StringComparison.OrdinalIgnoreCase)) { if(group.customDrawer != null) { group.customDrawer.Invoke(); } else { DrawGroup(group); CoreEditorUtils.DrawSplitter(); } } else { var keyName = group.FoldoutStateKeyName; var state = SessionState.GetBool(keyName, group.expandedByDefault); bool newState; if (group.customDrawer == DrawDebugGroupContent || group.name == MarkdownToolsLabel) { newState = DrawHeaderFoldout(new GUIContent(group.name), state, false, () => true, null, AttributeDocumentationUrl, pos => { var menu = new GenericMenu(); menu.AddItem(new GUIContent("Show Development Options"), ShowDevelopmentOptions, () => { ShowDevelopmentOptions = !ShowDevelopmentOptions; }); menu.DropDown(new Rect(pos, Vector2.zero)); }); } else { newState = CoreEditorUtils.DrawHeaderFoldout(group.name, state); } if(newState != state) SessionState.SetBool(keyName, newState); state = newState; if (state) { EditorGUI.indentLevel++; EditorGUILayout.Space(); if (group.customDrawer != null) { group.customDrawer.Invoke(); if(group != headerGroups.Last()) CoreEditorUtils.DrawSplitter(); } else DrawGroup(group); EditorGUI.indentLevel--; } if(group != headerGroups.Last()) CoreEditorUtils.DrawSplitter(); } if (EditorGUI.EndChangeCheck()) MaterialChanged(materialEditor, properties); if(isDisabled) EditorGUI.EndDisabledGroup(); DrawHeaderGroupMarker.End(); } OnGUIMarker.End(); MarkdownNeedleIcons.DrawGUILogo(); } private bool IsDeveloperMode() { return Unsupported.IsDeveloperMode() || ShowDevelopmentOptions; } public static string GetBetween(string str, char start, char end, bool last = false) { var i0 = last ? str.LastIndexOf(start) : str.IndexOf(start); var i1 = last ? str.LastIndexOf(end) : str.IndexOf(end); if (i0 < 0 || i1 < 0) return null; return str.Substring(i0 + 1, i1 - i0 - 1); } private static Parser parser; // private static Dictionary expressionCache = new Dictionary(); private static bool ConditionIsFulfilled(Material targetMaterial, string conditionExpression) { bool SetValueForCondition(ExpressionVariable variable, string condition) { // check if keyword is set var keywordIsSet = Array.IndexOf(targetMaterial.shaderKeywords, condition) >= 0; if (keywordIsSet) { variable.Set(true); return true; } // also check for global keywords if (Shader.IsKeywordEnabled(condition)) { variable.Set(true); return true; } var propertyIndex = targetMaterial.shader.FindPropertyIndex(condition); if (propertyIndex <= -1) { // property not found - // we could search harder (e.g. assume Vector.x/y/z/w or Color.r/g/b/a) // or we warn. // we should still set the value here // variable.Set(0); return false; } var propertyType = targetMaterial.shader.GetPropertyType(propertyIndex); switch (propertyType) { case ShaderPropertyType.Float: case ShaderPropertyType.Range: variable.Set(targetMaterial.GetFloat(condition)); break; case ShaderPropertyType.Texture: variable.Set(targetMaterial.GetTexture(condition)); break; case ShaderPropertyType.Vector: variable.Set(targetMaterial.GetVector(condition).magnitude); break; case ShaderPropertyType.Color: variable.Set(targetMaterial.GetColor(condition).maxColorComponent); break; default: // weird property type in comparison return false; } return true; } ConditionCheckMarker.Begin(); // TODO Cache properly bool result; try { parser = new Parser(new ParsingContext(false), new ExpressionContext(false)); var expression = parser.Parse(conditionExpression); bool allConditionsResolved = true; foreach (var v in expression.Context.Variables) { allConditionsResolved &= SetValueForCondition(v.Value, v.Key); } if (!allConditionsResolved) { // TODO show warning // TODO how to see which expressions are actually needed? } result = expression.GetResult(); } catch (ParseException) { Debug.LogWarning("Expression parse error for Shader condition: " + conditionExpression + " on " + targetMaterial, targetMaterial); result = false; } ConditionCheckMarker.End(); return result; } private void CollectLocalAndGlobalKeywords(Material material) { // TODO properly support keywordSpace and local/global keywords and overrides on 2021.2+ #if UNITY_2021_2_OR_NEWER localKeywords = material.shader.keywordSpace.keywords.ToDictionary(x => x.name, x => x); globalKeywords = Shader.globalKeywords.ToDictionary(x => x.name, x => x); #else localKeywords = ((string[]) GetShaderLocalKeywords.Invoke(null, new object[] { material.shader })).ToList(); globalKeywords = ((string[]) GetShaderGlobalKeywords.Invoke(null, new object[] { material.shader })).ToList(); #endif } protected virtual void MaterialChanged(MaterialEditor materialEditor, MaterialProperty[] properties) { MaterialChangedMarker.Begin(); foreach(var mat in materialEditor.targets) { var material = (Material) mat; if (!material) throw new ArgumentNullException(nameof(materialEditor), "Target material is null for " + materialEditor); if (!material.shader) return; // get keywords for this shader. CollectLocalAndGlobalKeywords(material); // loop through texture properties #if UNITY_6000_2_OR_NEWER foreach (var materialProperty in properties.Where(x => x.propertyType == ShaderPropertyType.Texture)) #else foreach (var materialProperty in properties.Where(x => x.type == MaterialProperty.PropType.Texture)) #endif { var uppercaseName = materialProperty.name.ToUpperInvariant(); #if UNITY_2021_2_OR_NEWER if (localKeywords.ContainsKey(uppercaseName)) #else if (localKeywords.Contains(uppercaseName)) #endif { if (material.GetTexture(materialProperty.name)) material.EnableKeyword(uppercaseName); else material.DisableKeyword(uppercaseName); } } } MaterialChangedMarker.End(); } private ShaderGUI baseShaderGui = null; private bool haveSearchedForCustomGUI = false; // ReSharper disable InconsistentNaming private static readonly GUIContent AdditionalOptions = new GUIContent("Additional Options", "Options from the shader that are not part of the usual shader properties, e.g. instancing."); private static readonly GUIContent ShowOriginalProperties = new GUIContent("Show Original Properties", "Enable this option to show all properties, even those marked with [HideInInspector]."); private static readonly GUIContent ShowPropertyNames = new GUIContent("Show Reference Names", "Shortcut: Hold the key.\nEnable this option to see the actual reference names of all properties. This helps with access from code or animation."); private static readonly GUIContent RenameShaderProperties = new GUIContent("Rename Shader Properties", "Change shader property names across your project. Finds and renames property names in existing materials, animations, and scripts using that shader."); private static readonly GUIContent DebugConditionalProperties = new GUIContent("Debug Conditional Properties", "Enable this option to show properties that are conditionally filtered out with [SOME_CONDITION]."); private static readonly GUIContent DebugReferencedProperties = new GUIContent("Debug Referenced Properties", "Enable this option to show properties that are filtered out because they are referenced by drawers (!DRAWER) or inline properties (&&)."); private static readonly GUIContent RedrawInspector = new GUIContent("Reset Inspector", "After updating some properties and drawers, Unity caches some editor/inspector details. Clicking this button forces a regeneration of the Shader Inspector in such cases."); private static readonly GUIContent ShaderKeywords = new GUIContent("Enabled Keywords"); private static readonly GUIContent ClearKeywords = new GUIContent("Reset Keywords", "Reset keywords currently set on this shader."); private static readonly GUIContent LocalKeywords = new GUIContent("Local Keywords"); private static readonly GUIContent GlobalKeywords = new GUIContent("Global Keywords"); private static readonly GUIContent DevelopmentOptionsLabel = new GUIContent("Development Options"); private static readonly GUIContent BaseShaderGUIOptions = new GUIContent("Base Shader GUI"); private static readonly GUIContent PropertyDrawersAndDecorators = new GUIContent("Property Drawers and Decorators"); private static readonly GUIContent GroupsAndCategories = new GUIContent("Groups and Categories"); private static readonly GUIContent ResetFoldoutSessionState = new GUIContent("Reset Foldout SessionState"); private static readonly GUIContent LocalAndGlobalKeywords = new GUIContent("All Keywords", "All keywords that are defined/used by this shader and globally."); private static readonly string MarkdownToolsLabel = "Markdown Tools"; private static readonly string AttributeDocumentationUrl = "https://github.com/needle-tools/shadergraph-markdown#attribute-reference"; internal static readonly string PropertyRefactorDocumentationUrl = "https://github.com/needle-tools/shadergraph-markdown#refactoring-shader-properties"; private static ProfilerMarker OnGUIMarker = new ProfilerMarker("OnGUI"); private static ProfilerMarker GetHashCodeMarker = new ProfilerMarker("Get Hash Code"); private static ProfilerMarker GenerateHeaderGroupsMarker = new ProfilerMarker("Generate Header Groups"); private static ProfilerMarker DrawGroupMarker = new ProfilerMarker("Draw Group"); private static ProfilerMarker DrawCustomGUIMarker = new ProfilerMarker("Draw Custom GUI"); private static ProfilerMarker DrawDebugGroupContentMarker = new ProfilerMarker("Draw Debug Group Content"); private static ProfilerMarker DrawHeaderGroupMarker = new ProfilerMarker("Draw Header Groups"); private static ProfilerMarker MaterialChangedMarker = new ProfilerMarker("Material Changed"); private static ProfilerMarker ConditionCheckMarker = new ProfilerMarker("Condition Check"); private const string ShowDevelopmentOptionsKey = nameof(MarkdownShaderGUI) + "." + nameof(ShowDevelopmentOptions); private const string EmissionKeyword = "_EMISSION"; private const string EmissionColorPropertyName = "_EmissionColor"; private const string EmissionMapPropertyName = "_EmissionMap"; private const string EmissionColorPropertyName2 = "emissiveFactor"; private const string EmissionMapPropertyName2 = "emissiveTexture"; private bool ShowDevelopmentOptions { get => SessionState.GetBool(ShowDevelopmentOptionsKey, false); set => SessionState.SetBool(ShowDevelopmentOptionsKey, value); } // ReSharper restore InconsistentNaming private void InitializeCustomGUI(Material targetMat) { // instead of calling base, we need to draw the right inspector here. // to figure out which one that is, we need to get info from the ShaderGraph directly - // at least in HDRP, different custom editors are used depending on what modes are selected in ShaderGraph. // also see HDShaderUtils.cs //// HDRP 10 // Decal Rendering.HighDefinition.DecalGUI // Eye Rendering.HighDefinition.LightingShaderGraphGUI // Fabric Rendering.HighDefinition.LightingShaderGraphGUI // Hair Rendering.HighDefinition.LightingShaderGraphGUI // Lit Rendering.HighDefinition.LitShaderGraphGUI // StackLit Rendering.HighDefinition.LightingShaderGraphGUI // LayeredLit Rendering.HighDefinition.LayeredLitGUI // Unlit Rendering.HighDefinition.HDUnlitGUI // TerrainLitGUI // AxFGUI //// HDRP 8 // UnityEditor.Rendering.HighDefinition.HDLitGUI // UnityEditor.Rendering.HighDefinition.HDUnlitGUI // UnityEditor.Rendering.HighDefinition.DecalGUI #if SHADERGRAPH_7_OR_NEWER var defaultCustomInspector = MarkdownSGExtensions.GetDefaultCustomInspectorFromShader(targetMat.shader); if(!string.IsNullOrEmpty(defaultCustomInspector)) { baseShaderGui = MarkdownSGExtensions.CreateShaderGUI(defaultCustomInspector); } // remove the "ShaderGraphUIBlock" uiBlock ("Exposed Properties") as we're rendering that ourselves // if(!showOriginalPropertyList) // MarkdownHDExtensions.RemoveShaderGraphUIBlock(baseShaderGui); #endif haveSearchedForCustomGUI = true; } #if HAVE_VALIDATE_MATERIAL public override void ValidateMaterial(Material material) { base.ValidateMaterial(material); if(baseShaderGui != null) baseShaderGui.ValidateMaterial(material); #else public void ValidateMaterial(Material material) { #endif // if the material has an emission keyword, this is explicitly controlled; // if it doesn't, GI only works when _EmissionColor is present if (!(material.HasProperty(EmissionKeyword) || material.HasProperty("_Emission")) && (material.HasProperty(EmissionColorPropertyName) || material.HasProperty(EmissionColorPropertyName2))) { MaterialEditor.FixupEmissiveFlag(material); bool state = (material.globalIlluminationFlags & MaterialGlobalIlluminationFlags.EmissiveIsBlack) == MaterialGlobalIlluminationFlags.None; if (state) material.EnableKeyword(EmissionKeyword); else material.DisableKeyword(EmissionKeyword); } } public static void ShowRefactoringWindow(string shaderAssetPath, string inputReferenceName) { ShaderRefactoringWindow.Show(shaderAssetPath, inputReferenceName); } internal static bool DrawHeaderFoldout(GUIContent title, bool state, bool isBoxed = false, Func hasMoreOptions = null, Action toggleMoreOptions = null, string documentationURL = "", Action contextAction = null) { #if HAVE_HEADER_FOLDOUT_WITH_DOCS #if UNITY_2023_1_OR_NEWER return CoreEditorUtils.DrawHeaderFoldout(title, state, isBoxed, hasMoreOptions, toggleMoreOptions, false, documentationURL, contextAction); #else return CoreEditorUtils.DrawHeaderFoldout(title, state, isBoxed, hasMoreOptions, toggleMoreOptions, documentationURL, contextAction); #endif #else return CoreEditorUtilsShim.DrawHeaderFoldout(title, state, isBoxed, hasMoreOptions, toggleMoreOptions, documentationURL, contextAction); #endif } } } ================================================ FILE: package/Editor/MarkdownShaderGUI.cs.meta ================================================ fileFormatVersion: 2 guid: 139b97e2a9f906f44921cc2415c30ccd MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Needle/MarkdownNeedleIcons.cs ================================================ using UnityEditor; using UnityEngine; namespace Needle.Editors { internal static class MarkdownNeedleIcons { private const string _iconGuid = "f4f8d9f0d70d0d447b9083d86319ca3d"; private static Texture2D _logo; private const string _iconDarkModeGuid = "c581d781060cfa4479726b9c6009399d"; private static Texture2D _logoDarkMode; public static Texture2D Logo { get { if (_logo) return _logo; var path = AssetDatabase.GUIDToAssetPath(_iconGuid); if (path != null) { return _logo = AssetDatabase.LoadAssetAtPath(path); } _logo = Texture2D.blackTexture; return _logo; } } public static Texture2D LogoDarkMode { get { if (_logoDarkMode) return _logoDarkMode; var path = AssetDatabase.GUIDToAssetPath(_iconDarkModeGuid); if (path != null) { return _logoDarkMode = AssetDatabase.LoadAssetAtPath(path); } _logoDarkMode = Texture2D.blackTexture; return _logoDarkMode; } } private const string _iconButtonGuid = "6d6418d8e5b6a4c44955be2c5c84ffa5"; private static Texture2D _logo_button; public static Texture2D LogoButton { get { if (_logo) return _logo; var path = AssetDatabase.GUIDToAssetPath(_iconButtonGuid); if (path != null) { return _logo_button = AssetDatabase.LoadAssetAtPath(path); } _logo_button = Texture2D.blackTexture; return _logo_button; } } public static void DrawGUILogo() { var logo = EditorGUIUtility.isProSkin ? LogoDarkMode : Logo; if (logo) { EditorGUILayout.Space(20f); EditorGUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); EditorGUILayout.BeginVertical(); var rect = GUILayoutUtility.GetRect(80, 40); if (Event.current.type == EventType.Repaint) GUI.DrawTexture(rect, logo, ScaleMode.ScaleToFit); GUILayout.Label(new GUIContent("ShaderGraph Markdown by Needle"), EditorStyles.miniLabel); var linkRect = new Rect(rect); linkRect.height += EditorGUIUtility.singleLineHeight; EditorGUIUtility.AddCursorRect(linkRect, MouseCursor.Link); if (Event.current.type == EventType.MouseUp && linkRect.Contains(Event.current.mousePosition)) Application.OpenURL("https://needle.tools"); EditorGUILayout.EndVertical(); GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); } } } } ================================================ FILE: package/Editor/Needle/MarkdownNeedleIcons.cs.meta ================================================ fileFormatVersion: 2 guid: 768c7bcaca6c9d144978fa63c8b02ab1 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Needle/logo-button.png.meta ================================================ fileFormatVersion: 2 guid: 6d6418d8e5b6a4c44955be2c5c84ffa5 TextureImporter: internalIDToNameTable: [] externalObjects: {} serializedVersion: 13 mipmaps: mipMapMode: 0 enableMipMap: 1 sRGBTexture: 1 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 flipGreenChannel: 0 isReadable: 0 streamingMipmaps: 0 streamingMipmapsPriority: 0 vTOnly: 0 ignoreMipmapLimit: 0 grayScaleToAlpha: 0 generateCubemap: 6 cubemapConvolution: 0 seamlessCubemap: 0 textureFormat: 1 maxTextureSize: 2048 textureSettings: serializedVersion: 2 filterMode: 1 aniso: 1 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: 1 alphaIsTransparency: 0 spriteTessellationDetail: -1 textureType: 0 textureShape: 1 singleChannelComponent: 0 flipbookRows: 1 flipbookColumns: 1 maxTextureSizeSet: 0 compressionQualitySet: 0 textureFormatSet: 0 ignorePngGamma: 0 applyGammaDecoding: 0 swizzle: 50462976 cookieLightType: 0 platformSettings: - serializedVersion: 4 buildTarget: DefaultTexturePlatform maxTextureSize: 2048 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 - serializedVersion: 4 buildTarget: Standalone maxTextureSize: 2048 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 sprites: [] outline: [] customData: physicsShape: [] bones: [] spriteID: internalID: 0 vertices: [] indices: edges: [] weights: [] secondaryTextures: [] spriteCustomMetadata: entries: [] nameFileIdTable: {} mipmapLimitGroupName: pSDRemoveMatte: 0 userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Needle/needlelogo.png.meta ================================================ fileFormatVersion: 2 guid: f4f8d9f0d70d0d447b9083d86319ca3d TextureImporter: internalIDToNameTable: [] externalObjects: {} serializedVersion: 13 mipmaps: mipMapMode: 0 enableMipMap: 0 sRGBTexture: 1 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 flipGreenChannel: 0 isReadable: 0 streamingMipmaps: 0 streamingMipmapsPriority: 0 vTOnly: 0 ignoreMipmapLimit: 0 grayScaleToAlpha: 0 generateCubemap: 6 cubemapConvolution: 0 seamlessCubemap: 0 textureFormat: 1 maxTextureSize: 2048 textureSettings: serializedVersion: 2 filterMode: 1 aniso: 1 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: 1 alphaIsTransparency: 1 spriteTessellationDetail: -1 textureType: 0 textureShape: 1 singleChannelComponent: 0 flipbookRows: 1 flipbookColumns: 1 maxTextureSizeSet: 0 compressionQualitySet: 0 textureFormatSet: 0 ignorePngGamma: 0 applyGammaDecoding: 0 swizzle: 50462976 cookieLightType: 0 platformSettings: - serializedVersion: 4 buildTarget: DefaultTexturePlatform maxTextureSize: 256 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 - serializedVersion: 4 buildTarget: Standalone maxTextureSize: 2048 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 sprites: [] outline: [] customData: physicsShape: [] bones: [] spriteID: internalID: 0 vertices: [] indices: edges: [] weights: [] secondaryTextures: [] spriteCustomMetadata: entries: [] nameFileIdTable: {} mipmapLimitGroupName: pSDRemoveMatte: 0 userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Needle/needlelogo_white.png.meta ================================================ fileFormatVersion: 2 guid: c581d781060cfa4479726b9c6009399d TextureImporter: internalIDToNameTable: [] externalObjects: {} serializedVersion: 13 mipmaps: mipMapMode: 0 enableMipMap: 1 sRGBTexture: 1 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 flipGreenChannel: 0 isReadable: 0 streamingMipmaps: 0 streamingMipmapsPriority: 0 vTOnly: 0 ignoreMipmapLimit: 0 grayScaleToAlpha: 0 generateCubemap: 6 cubemapConvolution: 0 seamlessCubemap: 0 textureFormat: 1 maxTextureSize: 2048 textureSettings: serializedVersion: 2 filterMode: 1 aniso: 1 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: 1 alphaIsTransparency: 0 spriteTessellationDetail: -1 textureType: 0 textureShape: 1 singleChannelComponent: 0 flipbookRows: 1 flipbookColumns: 1 maxTextureSizeSet: 0 compressionQualitySet: 0 textureFormatSet: 0 ignorePngGamma: 0 applyGammaDecoding: 0 swizzle: 50462976 cookieLightType: 0 platformSettings: - serializedVersion: 4 buildTarget: DefaultTexturePlatform maxTextureSize: 2048 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 - serializedVersion: 4 buildTarget: Standalone maxTextureSize: 2048 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 sprites: [] outline: [] customData: physicsShape: [] bones: [] spriteID: internalID: 0 vertices: [] indices: edges: [] weights: [] secondaryTextures: [] spriteCustomMetadata: entries: [] nameFileIdTable: {} mipmapLimitGroupName: pSDRemoveMatte: 0 userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Needle.ShaderGraphMarkdown.asmdef ================================================ { "name": "Needle.ShaderGraphMarkdown", "rootNamespace": "", "references": [ "GUID:3eae0364be2026648bf74846acb8a731", "GUID:be0903cd8e1546f498710afdc59db5eb", "GUID:c579267770062bf448e75eb160330b7f", "GUID:78bd2ddd6e276394a9615c203e574844" ], "includePlatforms": [ "Editor" ], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": true, "precompiledReferences": [], "autoReferenced": false, "defineConstraints": [], "versionDefines": [ { "name": "com.unity.shadergraph", "expression": "7.0.0", "define": "SHADERGRAPH_7_OR_NEWER" }, { "name": "com.unity.shadergraph", "expression": "10.0.0", "define": "SHADERGRAPH_10_OR_NEWER" }, { "name": "com.unity.shadergraph", "expression": "12.0.0", "define": "SHADERGRAPH_12_OR_NEWER" }, { "name": "com.unity.render-pipelines.high-definition", "expression": "7.0.0", "define": "HDRP_7_OR_NEWER" }, { "name": "com.unity.render-pipelines.high-definition", "expression": "10.0.0", "define": "HDRP_10_OR_NEWER" }, { "name": "com.unity.render-pipelines.universal", "expression": "7.0.0", "define": "URP_7_OR_NEWER" }, { "name": "com.unity.render-pipelines.core", "expression": "7.0.0", "define": "RP_CORE_7_OR_NEWER" } ], "noEngineReferences": false } ================================================ FILE: package/Editor/Needle.ShaderGraphMarkdown.asmdef.meta ================================================ fileFormatVersion: 2 guid: 381ea92d5c3a57b43bf4b68aab88232d AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Needle.meta ================================================ fileFormatVersion: 2 guid: 1564ae2af7f5112499e9da048cc63011 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Resources/Styles/ShaderGraphMarkdown.uss ================================================ .__markdown #pill #node-border { background-image: none; border-bottom-width: 0; border-top-width: 0; border-left-width: 0; border-right-width: 0; } .__markdown .pill.has-icon #contents #top #icon { display:none; } .__markdown .__markdown_Reference #contents > #top > #title-label { -unity-font-style: italic; } .__markdown .__markdown_Link #contents > #top > #title-label { font-size:11px; color: #6BBDEE; max-width:350px; white-space:normal; } .__markdown .__markdown_Note #contents > #top { width:auto; } .__markdown .__markdown_Note #contents > #top > #title-label { color: #fdc64d; max-width:350px; white-space:normal; } .__markdown .__markdown_Tooltip #contents > #top { width:auto; } .__markdown .__markdown_Tooltip #contents > #top > #title-label { color: #9e9e9e; max-width:350px; white-space:normal; } .__markdown .__markdown_Drawer #contents > #top > #title-label { -unity-font-style: italic; } .__markdown .__markdown_Header #contents > #top > #title-label { -unity-font-style: bold; } .__markdown .__markdown_Foldout #contents > #top > #title-label { -unity-font-style: bold; font-size: 16px; } .__markdown #contentItem > Label#typeLabel { /* -unity-font-style: italic; */ opacity: 0.5; } .__markdown_DefaultReferenceWarning > #typeLabel { padding-right:8px; border-right-width: 2px; border-right-color: rgba(255,0,0,1); /*-unity-font-style: italic;*/ } .__markdown_NonRecommendedReferenceHint > #typeLabel { padding-right:8px; border-right-width: 2px; border-right-color: rgb(241, 192, 14); /*-unity-font-style: italic;*/ } .__markdown_indent_1 { margin-left:10px; } .__markdown_indent_2 { margin-left:20px; } .__markdown_indent_3 { margin-left:30px; } .__markdown_indent_4 { margin-left:40px; } .__markdown_indent_5 { margin-left:50px; } .__markdown_indent_6 { margin-left:60px; } ================================================ FILE: package/Editor/Resources/Styles/ShaderGraphMarkdown.uss.meta ================================================ fileFormatVersion: 2 guid: fa1c4dec95c31ee4d8503ad0f5b238e3 ScriptedImporter: internalIDToNameTable: [] externalObjects: {} serializedVersion: 2 userData: assetBundleName: assetBundleVariant: script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} disableValidation: 0 ================================================ FILE: package/Editor/Resources/Styles.meta ================================================ fileFormatVersion: 2 guid: 2da5712904e05bd4690f8b8688cecb41 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Resources.meta ================================================ fileFormatVersion: 2 guid: 417c8a87efefaa64e874a9b10da3c472 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Settings/ShaderGraphMarkdownSettings.cs ================================================ using System.Collections.Generic; using UnityEditor; using UnityEngine; #if !UNITY_2020_2_OR_NEWER using System.IO; using System.Linq; using UnityEditorInternal; #endif namespace Needle.ShaderGraphMarkdown { internal class ShaderGraphMarkdownSettingsProvider : SettingsProvider { private SerializedObject m_SerializedObject; private SerializedProperty showDefaultReferenceNameWarning, showNamingRecommendationHint, showMarkdownInBlackboard; public override void OnGUI(string searchContext) { if (m_SerializedObject == null) { ShaderGraphMarkdownSettings.instance.hideFlags = HideFlags.DontSave; m_SerializedObject = new SerializedObject(ShaderGraphMarkdownSettings.instance); showMarkdownInBlackboard = m_SerializedObject.FindProperty(nameof(ShaderGraphMarkdownSettings.instance.showMarkdownInBlackboard)); showDefaultReferenceNameWarning = m_SerializedObject.FindProperty(nameof(ShaderGraphMarkdownSettings.instance.showDefaultReferenceNameWarning)); showNamingRecommendationHint = m_SerializedObject.FindProperty(nameof(ShaderGraphMarkdownSettings.instance.showNamingRecommendationHint)); } EditorGUILayout.LabelField("Blackboard Hints", EditorStyles.boldLabel); EditorGUILayout.PropertyField(showMarkdownInBlackboard, new GUIContent("Show Markdown in Blackboard")); EditorGUILayout.PropertyField(showDefaultReferenceNameWarning, new GUIContent("Default Name Warning")); EditorGUILayout.PropertyField(showNamingRecommendationHint, new GUIContent("Recommendations Hint")); if(m_SerializedObject.hasModifiedProperties) { m_SerializedObject.ApplyModifiedProperties(); } } // we don't need the SettingsProvider before 2020.2 as the only setting for now // are the blackboard hints, which are available on 2020.2+ only #if UNITY_2020_2_OR_NEWER [SettingsProvider] public static SettingsProvider CreateShaderGraphMarkdownSettingsProvider() { ShaderGraphMarkdownSettings.instance.Save(); return new ShaderGraphMarkdownSettingsProvider("Project/Graphics/ShaderGraph Markdown", SettingsScope.Project); } #endif public ShaderGraphMarkdownSettingsProvider(string path, SettingsScope scopes, IEnumerable keywords = null) : base(path, scopes, keywords) { } } [FilePath("ProjectSettings/ShaderGraphMarkdownSettings.asset", FilePathAttribute.Location.ProjectFolder)] internal class ShaderGraphMarkdownSettings : ScriptableSingleton { public bool showMarkdownInBlackboard = true; [Tooltip("Shows a red bar next to properties that still have the default reference name. This is not recommended, as it will be hard to change to another shader in the future. Try to align your property names to Unity conventions (e.g. \"_BaseColor\")")] public bool showDefaultReferenceNameWarning = true; [Tooltip("Shows a yellow bar next to properties that don't follow recommended reference naming. Reference names should start with \"_\" and be in CamelCase.")] public bool showNamingRecommendationHint = true; public void Save() => Save(true); } #if !UNITY_2020_1_OR_NEWER #region FilePath/ScriptableSingleton Shim internal class ScriptableSingleton : ScriptableObject where T : ScriptableObject { static T s_Instance; public static T instance { get { if (!s_Instance) CreateAndLoad(); return s_Instance; } } protected ScriptableSingleton() { if (s_Instance) Debug.LogError("ScriptableSingleton already exists. Did you query the singleton in a constructor?"); else { object casted = this; s_Instance = casted as T; System.Diagnostics.Debug.Assert(s_Instance != null); } } private static void CreateAndLoad() { System.Diagnostics.Debug.Assert(s_Instance == null); // Load string filePath = GetFilePath(); #if UNITY_EDITOR if (!string.IsNullOrEmpty(filePath)) InternalEditorUtility.LoadSerializedFileAndForget(filePath); #endif if (s_Instance == null) { T t = CreateInstance(); t.hideFlags = HideFlags.HideAndDontSave; } System.Diagnostics.Debug.Assert(s_Instance != null); } protected virtual void Save(bool saveAsText) { if (!s_Instance) { Debug.Log("Cannot save ScriptableSingleton: no instance!"); return; } string filePath = GetFilePath(); if (!string.IsNullOrEmpty(filePath)) { string folderPath = Path.GetDirectoryName(filePath); if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath); #if UNITY_EDITOR InternalEditorUtility.SaveToSerializedFileAndForget(new[] {s_Instance}, filePath, saveAsText); #endif } } protected static string GetFilePath() { System.Type type = typeof(T); object[] attributes = type.GetCustomAttributes(true); return attributes.OfType() .Select(f => f.filepath) .FirstOrDefault(); } } [System.AttributeUsage(System.AttributeTargets.Class)] internal sealed class FilePathAttribute : System.Attribute { public enum Location { PreferencesFolder, ProjectFolder } public string filepath { get; set; } public FilePathAttribute(string relativePath, FilePathAttribute.Location location) { if (string.IsNullOrEmpty(relativePath)) { Debug.LogError("Invalid relative path! (its null or empty)"); return; } if (relativePath[0] == '/') relativePath = relativePath.Substring(1); #if UNITY_EDITOR if (location == FilePathAttribute.Location.PreferencesFolder) this.filepath = InternalEditorUtility.unityPreferencesFolder + "/" + relativePath; else #endif this.filepath = relativePath; } } #endregion #endif } ================================================ FILE: package/Editor/Settings/ShaderGraphMarkdownSettings.cs.meta ================================================ fileFormatVersion: 2 guid: 1fc47d9f999cc8f4ab4b4aa3f01dc7b3 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/Settings.meta ================================================ fileFormatVersion: 2 guid: cdf365a60a8b3a945a151b2a6326fc82 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package/Editor/ShaderGraphPropertyUI.cs ================================================ #if SHADERGRAPH_7_OR_NEWER using System; using System.Globalization; using System.Linq; using UnityEngine; using UnityEngine.UIElements; using UnityEditor; using UnityEditor.Experimental.GraphView; using System.Reflection; using UnityEditor.ShaderGraph; using UnityEditor.ShaderGraph.Internal; namespace Needle.ShaderGraphMarkdown { internal static class ShaderGraphPropertyUI { [InitializeOnLoadMethod] static void Init() { EditorApplication.update += EditorUpdate; } private static int i = 0; private static StyleSheet styleSheet; private static PropertyInfo _graphEditorView, _blackboardProvider, _blackboard; private static void EditorUpdate() { if(!ShaderGraphMarkdownSettings.instance.showMarkdownInBlackboard) return; i = (i + 1) % 100; // only run every 100 frames for now if (i != 0) return; #if SHADERGRAPH_10_OR_NEWER var shaderGraphAssembly = typeof(UnityEditor.ShaderGraph.PositionControl).Assembly; // does not exist pre-2020.2 #else var shaderGraphAssembly = typeof(UnityEditor.ShaderGraph.KeywordDefinition).Assembly; // went internal in 2020.2+ #endif var windowType = shaderGraphAssembly.GetType("UnityEditor.ShaderGraph.Drawing.MaterialGraphEditWindow"); var windows = Resources.FindObjectsOfTypeAll(windowType); foreach (var windowObject in windows) { var wnd = (EditorWindow) windowObject; if (_graphEditorView == null) _graphEditorView = windowType.GetProperty("graphEditorView", (BindingFlags) (-1)); var graphEditorView = _graphEditorView?.GetValue(wnd); if (graphEditorView == null) continue; var GraphEditorView = graphEditorView.GetType(); if (_blackboardProvider == null) _blackboardProvider = GraphEditorView.GetProperty("blackboardProvider", (BindingFlags) (-1)); var blackboardProvider = _blackboardProvider?.GetValue(graphEditorView); if (blackboardProvider == null) { _blackboardProvider = GraphEditorView.GetProperty("blackboardController", (BindingFlags) (-1)); blackboardProvider = _blackboardProvider?.GetValue(graphEditorView); if(blackboardProvider == null) continue; } var BlackboardProvider = blackboardProvider.GetType(); if (_blackboard == null) _blackboard = BlackboardProvider.GetProperty("blackboard", (BindingFlags) (-1)); var blackboard = (VisualElement) _blackboard?.GetValue(blackboardProvider); if (blackboard == null) continue; #if UNITY_2020_2_OR_NEWER // get inputRows as well so we can access actual shader property data, not just UI var inputRows = MarkdownSGExtensions.GetInputRowDictionaryFromController(blackboardProvider); if(inputRows == null) continue; var shaderInputs = inputRows .Where(x => x.Value != null) .ToDictionary(x => x.Value, x => x.Key); #endif if (!styleSheet) styleSheet = Resources.Load("Styles/ShaderGraphMarkdown"); var blackboardElements = blackboard.Query().Visible().ToList().Cast().ToList(); if (!blackboardElements.Any()) blackboardElements = MarkdownSGExtensions.GetBlackboardElements(blackboard); #if !UNITY_2020_2_OR_NEWER BlackboardRow GetRowForElement(VisualElement fieldView) { if (fieldView == null) return null; var p = fieldView.parent; while (p != null && !(p is BlackboardRow)) p = p.parent; if (p is BlackboardRow row) return row; return null; } void ToggleExpand(EventBase eventBase) { var target = eventBase.target as Button; var currentRow = GetRowForElement(target); if (target == null || currentRow == null || blackboardElements == null) return; // expanded state has already been updated at this point since our event is always appended later as the default event var currentState = currentRow.expanded; // only react to Alt click if (!(eventBase is MouseUpEvent downEvent) || !downEvent.altKey) return; foreach (var fieldView in blackboardElements) { var row = GetRowForElement(fieldView); if(row != null) row.expanded = currentState; } } #endif bool nextFieldShouldBeIndented = false; foreach(var fieldView in blackboardElements) { #if UNITY_2020_2_OR_NEWER bool usesDefaultReferenceName = false; bool usesRecommendedReferenceName = true; bool usesInlineTextureDrawerShorthand = false; bool isTextureProperty = false; // get shaderInput for this field var element = (VisualElement) fieldView; while (!MarkdownSGExtensions.ElementIsBlackboardRow(element) && element.parent != null) element = element.parent; if(MarkdownSGExtensions.ElementIsBlackboardRow(element)) // element is BlackboardRow blackboardRow) { var blackboardRow = element; if (shaderInputs.ContainsKey(blackboardRow)) { var shaderInput = shaderInputs[blackboardRow]; #if UNITY_2021_1_OR_NEWER if (shaderInput.IsUsingNewDefaultRefName() || shaderInput.IsUsingOldDefaultRefName()) #else if (shaderInput.referenceName.Equals(shaderInput.GetDefaultReferenceName(), StringComparison.Ordinal)) #endif usesDefaultReferenceName = true; if (!usesDefaultReferenceName && ReferenceNameLooksLikeADefaultReference(shaderInput.referenceName)) usesDefaultReferenceName = true; // some properties already have "good" default reference names, we shouldn't warn in that case. if (usesDefaultReferenceName && shaderInput.referenceName.StartsWith("_", StringComparison.Ordinal)) usesDefaultReferenceName = false; if (!shaderInput.referenceName.StartsWith("_", StringComparison.Ordinal)) usesRecommendedReferenceName = false; if ((shaderInput is AbstractShaderProperty abstractShaderProperty && (abstractShaderProperty.propertyType == PropertyType.Texture2D || abstractShaderProperty.propertyType == PropertyType.Texture3D || abstractShaderProperty.propertyType == PropertyType.Texture2DArray || abstractShaderProperty.propertyType == PropertyType.VirtualTexture))) isTextureProperty = true; var display = shaderInput.displayName; var hasCondition = !display.StartsWith("[", StringComparison.Ordinal) && display.Contains('[') && display.EndsWith("]", StringComparison.Ordinal); if (hasCondition) { display = display.Substring(0, display.IndexOf('[') - 1).TrimEnd(); } if (display.EndsWith("&&", StringComparison.Ordinal)) { usesInlineTextureDrawerShorthand = true; } // make sure our context menu handler is registered element.UnregisterCallback(MenuPopulateEvent); element.RegisterCallback(MenuPopulateEvent); // refactoring if (markedForRefactor != null && markedForRefactor == blackboardRow) { markedForRefactor = null; MarkdownShaderGUI.ShowRefactoringWindow(MarkdownSGExtensions.GetShaderPathForWindow(wnd), shaderInput.referenceName); } } } #endif var displayName = MarkdownSGExtensions.GetBlackboardFieldText(fieldView); var contentItem = fieldView.Q("contentItem"); var indentLevel = MarkdownShaderGUI.GetIndentLevel(displayName); if (nextFieldShouldBeIndented) { indentLevel += 2; nextFieldShouldBeIndented = false; } displayName = displayName.TrimStart('-'); var markdownType = MarkdownShaderGUI.GetMarkdownType(displayName); contentItem.ClearClassList(); if (!fieldView.styleSheets.Contains(styleSheet) && styleSheet) { fieldView.styleSheets.Add(styleSheet); #if !UNITY_2020_2_OR_NEWER // attach to the Blackboard expandButton for each item and add the typical Alt+Click handler // to expand/collapse all items. var row = GetRowForElement(fieldView); if (row != null) { var expand = row.Q