CSharpGL(12)用T4模板生成CSSL及其renderer代码
严格来说本篇不直接涉及OpenGL,但磨刀不误砍柴工,本篇仍算CSharpGL里。
上一篇制作了CSSL。CSSL和Renderer配合,就定义了一个渲染OpenGL元素的完整流程。本篇用T4模板来帮助开发者自动生成这个简单的代码框架。
本篇是CSharpGL的一部分,CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL)
设计一个系统前,要想好用户会期望如何使用这个系统。当我想创建一个新的OpenGL元素时,只需在一处提供此元素的名字,就足以确定它的CSSL+Renderer框架。
所以,我需要写一个T4模板,提供此名字,然后自动生成2个文件,分别存放CSSL和Renderer代码。
为T4重定向输出位置即可。步骤如下。
创建一个以ttinclude为扩展名的文件,输入以下内容。后面将在tt文件中引用此文件。
1 <#@ assembly name="System.Core"#> 2 <#@ assembly name="EnvDTE"#> 3 <#@ import namespace="System.Collections.Generic"#> 4 <#@ import namespace="System.IO"#> 5 <#@ import namespace="System.Text"#> 6 <#@ import namespace="Microsoft.VisualStudio.TextTemplating"#> 7 8 <#+ 9 10 // T4 Template Block manager for handling multiple file outputs more easily. 11 // Copyright (c) Microsoft Corporation. All rights reserved. 12 // This source code is made available under the terms of the Microsoft Public License (MS-PL) 13 14 // Manager class records the various blocks so it can split them up 15 class Manager 16 { 17 public struct Block { 18 public String Name; 19 public int Start, Length; 20 } 21 22 public List<Block> blocks = new List<Block>(); 23 public Block currentBlock; 24 public Block footerBlock = new Block(); 25 public Block headerBlock = new Block(); 26 public ITextTemplatingEngineHost host; 27 public ManagementStrategy strategy; 28 public StringBuilder template; 29 public String OutputPath { get; set; } 30 31 public Manager(ITextTemplatingEngineHost host, StringBuilder template, bool commonHeader) { 32 this.host = host; 33 this.template = template; 34 OutputPath = String.Empty; 35 strategy = ManagementStrategy.Create(host); 36 } 37 38 public void StartBlock(String name) { 39 currentBlock = new Block { Name = name, Start = template.Length }; 40 } 41 42 public void StartFooter() { 43 footerBlock.Start = template.Length; 44 } 45 46 public void EndFooter() { 47 footerBlock.Length = template.Length - footerBlock.Start; 48 } 49 50 public void StartHeader() { 51 headerBlock.Start = template.Length; 52 } 53 54 public void EndHeader() { 55 headerBlock.Length = template.Length - headerBlock.Start; 56 } 57 58 public void EndBlock() { 59 currentBlock.Length = template.Length - currentBlock.Start; 60 blocks.Add(currentBlock); 61 } 62 63 public void Process(bool split) { 64 String header = template.ToString(headerBlock.Start, headerBlock.Length); 65 String footer = template.ToString(footerBlock.Start, footerBlock.Length); 66 blocks.Reverse(); 67 foreach(Block block in blocks) { 68 String fileName = Path.Combine(OutputPath, block.Name); 69 if (split) { 70 String content = header + template.ToString(block.Start, block.Length) + footer; 71 strategy.CreateFile(fileName, content); 72 template.Remove(block.Start, block.Length); 73 } else { 74 strategy.DeleteFile(fileName); 75 } 76 } 77 } 78 } 79 80 class ManagementStrategy 81 { 82 internal static ManagementStrategy Create(ITextTemplatingEngineHost host) { 83 return (host is IServiceProvider) ? new VSManagementStrategy(host) : new ManagementStrategy(host); 84 } 85 86 internal ManagementStrategy(ITextTemplatingEngineHost host) { } 87 88 internal virtual void CreateFile(String fileName, String content) { 89 File.WriteAllText(fileName, content); 90 } 91 92 internal virtual void DeleteFile(String fileName) { 93 if (File.Exists(fileName)) 94 File.Delete(fileName); 95 } 96 } 97 98 class VSManagementStrategy : ManagementStrategy 99 { 100 private EnvDTE.ProjectItem templateProjectItem; 101 102 internal VSManagementStrategy(ITextTemplatingEngineHost host) : base(host) { 103 IServiceProvider hostServiceProvider = (IServiceProvider)host; 104 if (hostServiceProvider == null) 105 throw new ArgumentNullException("Could not obtain hostServiceProvider"); 106 107 EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE)); 108 if (dte == null) 109 throw new ArgumentNullException("Could not obtain DTE from host"); 110 111 templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile); 112 } 113 114 internal override void CreateFile(String fileName, String content) { 115 base.CreateFile(fileName, content); 116 ((EventHandler)delegate { templateProjectItem.ProjectItems.AddFromFile(fileName); }).BeginInvoke(null, null, null, null); 117 } 118 119 internal override void DeleteFile(String fileName) { 120 ((EventHandler)delegate { FindAndDeleteFile(fileName); }).BeginInvoke(null, null, null, null); 121 } 122 123 private void FindAndDeleteFile(String fileName) { 124 foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems) { 125 if (projectItem.get_FileNames(0) == fileName) { 126 projectItem.Delete(); 127 return; 128 } 129 } 130 } 131 }#>
像下面这样,即可指定多个输出文件了。
1 <#@ template debug="false" hostspecific="true" language="C#" #> 2 <#@ output extension=".cs" #> 3 <#@ include file=".\Dump2MultipleFiles.ttinclude" #> 4 <# string ShaderName = "SomeShader"; #> 5 <# string ClassName = ShaderName + "Renderer"; #> 6 <# string VertexShaderName = ShaderName + "Vert"; #> 7 <# string FragmentShaderName = ShaderName + "Frag"; #> 8 9 <# var manager = new Manager(Host, GenerationEnvironment, true) { OutputPath = Path.GetDirectoryName(Host.TemplateFile)}; #> 10 <# 11 // 下述内容写入ShaderName + ".cs"文件 12 manager.StartBlock(ShaderName + ".cs"); 13 #> 14 namespace CSharpShaders 15 { 16 class <#= VertexShaderName #> : VertexCSShaderCode 17 { 18 } 19 } 20 <# 21 // 上述内容写入ShaderName + ".cs"文件 22 manager.EndBlock(); 23 #> 24 <# 25 // 下述内容写入ClassName + ".cs"文件 26 manager.StartBlock(ClassName + ".cs"); 27 #> 28 namespace ShaderLab 29 { 30 public class <#= ClassName #> : RendererBase 31 { 32 } 33 } 34 35 <# 36 // 上述内容写入ClassName + ".cs"文件 37 manager.EndBlock(); 38 39 // 最后要写上这句话 40 manager.Process(true); 41 #>
本篇使用T4模板生成多个代码文件,其方法是通用的。
CSharpGL(12)用T4模板生成CSSL及其renderer代码
原文:http://www.cnblogs.com/bitzhuwei/p/CSharpGL-12-T4-generated-CSSL-and-Renderer.html