using Godot; using System.Reflection; using System.Collections.Generic; namespace Rokojori { [Tool] [GlobalClass] public partial class CSShaderClassGenerator:Node { [Export] public string resourcePath; [Export] public bool generate; public override void _Process( double delta ) { if ( generate ) { generate = false; Generate(); } } public static readonly string ShaderName = "${ShaderName}"; public static readonly string ShaderResourcePath = "${ShaderResourcePath}"; public static readonly string ShaderStaticPropertyNames = "${ShaderStaticPropertyNames}"; public static readonly string ShaderInstancePropertiesDeclarations = "${ShaderInstancePropertiesDeclarations}"; public static readonly string ShaderInstancePropertiesInitializers = "${ShaderInstancePropertiesInitializers}"; public static readonly string MaterialPresets = "${MaterialPresets}"; public void Generate() { var shader = ResourceLoader.Load( resourcePath ); var resourceParentPath = RegexUtility.ParentPath( resourcePath ); var absoluteFilePath = FilePath.Absolute( ProjectSettings.GlobalizePath( resourcePath ) ); var absoluteParentPath = absoluteFilePath.CreateAbsoluteParent(); RJLog.Log( resourceParentPath ); var shaderName = absoluteFilePath.fileName; var members = Shaders.GetUniformMembers( shader ); var templatePath = ProjectSettings.GlobalizePath( "res://addons/rokojori_action_library/Runtime/Shading/Tools/CSShaderClassGenerator/CSShaderClassTemplate.txt" ); var template = FilesSync.LoadUTF8( templatePath ); var staticProps = new List(); var instanceProps = new List(); var instanceInitializers = new List(); members.ForEach( ( m )=> { var type = m.GetPropertyNameType(); var name = m.name; var csName = name.ToCamelCase(); var declaration = "public static readonly ${type}PropertyName ${csName} = ${type}PropertyName.Create( \"${name}\" );"; var instance = "public readonly CustomMaterialProperty<${csType}> ${csName};"; var init = "${csName} = new CustomMaterialProperty<${csType}>( this, ${ShaderName}.${csName} );"; var csType = type; if ( csType == "Float" ) { csType = "float"; } else if ( csType == "Int" ) { csType = "float"; } var map = new StringMap(); map[ "${type}" ] = type; map[ "${csType}"] = csType; map[ "${name}" ] = name; map[ "${csName}" ] = csName; map[ "${ShaderName}"] = shaderName; staticProps.Add( map.ReplaceAll( declaration ) ); instanceProps.Add( map.ReplaceAll( instance ) ); instanceInitializers.Add( map.ReplaceAll( init ) ); } ); var materials = FilesSync.GetFiles( absoluteParentPath.fullPath, ( f ) => { return f.hasFileExtension( "material" ); } ); var presets = new List(); materials.ForEach( m => { var materialName = m.fileName; var shortName = materialName; if ( materialName.ToLower().StartsWith( shaderName.ToLower() ) ) { shortName = shortName.Substring( shaderName.Length ); } var declaration = "public static readonly CachedResource<${ShaderName}Material> ${csName} = CustomMaterial.Cached<${ShaderName}Material>(\n" + " \"${materialPath}\"\n" + " );"; var mMap = new StringMap(); mMap[ "${materialPath}" ] = resourceParentPath + "/" + materialName + ".material"; mMap[ "${ShaderName}" ] = shaderName; mMap[ "${csName}" ] = RegexUtility.ToValidCSName( shortName ).ToPascalCase(); presets.Add( mMap.ReplaceAll( declaration ) ); } ); var variables = new StringMap(); variables[ CSShaderClassGenerator.ShaderName ] = shaderName; variables[ CSShaderClassGenerator.ShaderResourcePath ] = resourcePath; variables[ CSShaderClassGenerator.ShaderStaticPropertyNames ] = Lists.Join( staticProps, "\n " ); variables[ CSShaderClassGenerator.ShaderInstancePropertiesDeclarations ] = Lists.Join( instanceProps, "\n " ); variables[ CSShaderClassGenerator.ShaderInstancePropertiesInitializers ] = Lists.Join( instanceInitializers, "\n " ); variables[ CSShaderClassGenerator.MaterialPresets ] = Lists.Join( presets, "\n " ); var shaderClass = variables.ReplaceAll( template ); var outputPath = absoluteFilePath.WithExtension( "cs" ); FilesSync.SaveUTF8( outputPath.fullPath, shaderClass ); this.LogInfo( "\nGenerated >> ", outputPath.fullPath, "\n" + shaderClass ); } } }