/// <summary> /// Generate code of new spriteGroup class. /// </summary> /// <param name="spriteGroup"> Name of new class/spriteGroup. </param> /// <returns> /// <code> /// [<see cref="NutPackerLib.OriginalNameAttribute"/>("original name of folder")] /// public class <paramref name="spriteGroup"/> : <see cref="ISpriteGroup"/> { } /// </code> /// </returns> public static CodeTypeDeclaration GenerateSpriteGroupClass( string spriteGroup) { /// New public class with <param name="spriteGroup"></param> name. var spriteGroupClass = new CodeTypeDeclaration() { Name = Walkthrough.VariableName(spriteGroup) , IsClass = true , TypeAttributes = TypeAttributes.Public }; /// Inherited from <see cref="ISpriteGroup"/>. spriteGroupClass.BaseTypes.Add(new CodeTypeReference(typeof(ISpriteGroup))); /// Create attribute. /// [<see cref="NutPackerLib.OriginalNameAttribute"/>("original name of folder")] CodeAttributeDeclaration attribute = new CodeAttributeDeclaration( typeof(NutPackerLib.OriginalNameAttribute).FullName , new CodeAttributeArgument(new CodePrimitiveExpression(spriteGroup)) ); /// Add attribute. spriteGroupClass.CustomAttributes.Add(attribute); return(spriteGroupClass); }
/// <summary> /// Generate new property <see cref="Rectangle"/>. /// </summary> /// <param name="picture"> Name of new property. </param> /// <param name="rectangle"> Location in texture atlas. </param> /// <returns> /// <code> /// [<see cref="NutPackerLib.OriginalNameAttribute"/>(name of picture)] /// public static <see cref="Rectangle"/> <paramref name="picture"/> { /// get { /// // r - <paramref name="rectangle"/>. /// return new <see cref="Rectangle"/>(r.X, r.Y, r.Width, r.Height); /// } /// } /// </code> /// </returns> public static CodeMemberProperty GenerateTileProperty( string picture , Rectangle rectangle) { /// New public static class, with getter and type <see cref="Rectangle"/>. CodeMemberProperty pic = new CodeMemberProperty() { Attributes = MemberAttributes.Public | MemberAttributes.Static , Name = Walkthrough.VariableName(Path.GetFileNameWithoutExtension(picture)) , HasGet = true , Type = new CodeTypeReference(typeof(Xna.Rectangle)) }; /// Add expression to getter: return new <see cref="Rectangle"/>(r.X, r.Y, r.Width, r.Height); /// r - <param name="rectangle"></param> pic.GetStatements.Add( new CodeMethodReturnStatement( new CodeObjectCreateExpression(typeof(Xna.Rectangle) , new CodePrimitiveExpression(rectangle.X) , new CodePrimitiveExpression(rectangle.Y) , new CodePrimitiveExpression(rectangle.Width) , new CodePrimitiveExpression(rectangle.Height) ))); /// Create attribute. /// [<see cref="NutPackerLib.OriginalNameAttribute"/>("name of picture")] CodeAttributeDeclaration attribute = new CodeAttributeDeclaration( typeof(NutPackerLib.OriginalNameAttribute).FullName , new CodeAttributeArgument(new CodePrimitiveExpression(picture)) ); /// Add attribute. pic.CustomAttributes.Add(attribute); return(pic); }
/// <summary> /// Generate code of new SpriteSheet class. /// </summary> /// <remarks> /// Ya, all this code will be generated for every class, /// but it's only way to use indexer with static field. /// And ya, we need static field, cause we don't wanna create instance of array /// each time when we use it. /// So, it's some kinda singleton. /// </remarks> /// <param name="spriteSheetName"> Name of new class/spriteSheet. </param> /// <param name="rectangles"> Array of rectangles. </param> /// <returns> /// <code> /// public class <paramref name="spriteSheetName"/> : <see cref="ISpriteSheet"/> /// { /// private static <see cref="Xna.Rectangle"/>[] Frames = /// new <see cref="Xna.Rectangle"/>[] { } /// /// public int Length { /// get { /// return Frames.Length; // Here will be just number. /// } /// } /// /// public int this[int index] { get { return Frames[index] } } /// } /// </code> /// </returns> public static CodeTypeDeclaration GenerateSpriteSheetClass( string spriteSheetName , params Rectangle[] rectangles) { /// New public class with <param name="spriteSheetName"></param> name. CodeTypeDeclaration spriteSheetClass = new CodeTypeDeclaration() { Name = Walkthrough.VariableName(spriteSheetName) , IsClass = true , TypeAttributes = TypeAttributes.Public }; /// Inherited from <see cref="ISpriteSheet"/>. spriteSheetClass.BaseTypes.Add(new CodeTypeReference(typeof(ISpriteSheet))); /// Create attribute. /// [<see cref="NutPackerLib.OriginalNameAttribute"/>("original name of folder")] CodeAttributeDeclaration attribute = new CodeAttributeDeclaration( typeof(NutPackerLib.OriginalNameAttribute).FullName , new CodeAttributeArgument(new CodePrimitiveExpression(spriteSheetName)) ); /// Add attribute. spriteSheetClass.CustomAttributes.Add(attribute); /// Array of expressions which create rectangles. CodeExpression[] createRectangles = new CodeExpression[rectangles.Length]; for (int i = 0; i < rectangles.Length; i++) { /// One rectangle /// new Rectangle(X, Y, Width, Height); CodeExpression rectangle = new CodeObjectCreateExpression(typeof(Xna.Rectangle) , new CodePrimitiveExpression(rectangles[i].X) , new CodePrimitiveExpression(rectangles[i].Y) , new CodePrimitiveExpression(rectangles[i].Width) , new CodePrimitiveExpression(rectangles[i].Height) ); createRectangles[i] = rectangle; } /// Array of rectangles. /// new Rectangles[rectangles.Length] { <param name="rectangles"></param> }; CodeArrayCreateExpression createArray = new CodeArrayCreateExpression( new CodeTypeReference(typeof(Xna.Rectangle)) , createRectangles); /// private static field with array of <see cref="Xna.Rectangle"/> which called Frames. CodeMemberField frames = new CodeMemberField() { Attributes = MemberAttributes.Private | MemberAttributes.Static | MemberAttributes.Final , Type = new CodeTypeReference(typeof(Xna.Rectangle[])) , Name = "Frames" , InitExpression = createArray }; /// Length property, /// only one getter which return number of rectangles in Frames field. CodeMemberProperty length = new CodeMemberProperty() { Attributes = MemberAttributes.Public | MemberAttributes.Final , Type = new CodeTypeReference(typeof(int)) , Name = "Length" , HasGet = true }; /// Expression which return number of rectangles. CodeMethodReturnStatement retn = new CodeMethodReturnStatement(new CodePrimitiveExpression(rectangles.Length)); /// Add return expression to length getter. length.GetStatements.Add(retn); /// Indexer. /// I don't know why, but it's only way to create indexer. /// Ya, we MUST create PROPERTY which called "Item", not "item" or "indexer" or somthing else, /// just f*****g "Item". /// And after thar, we MUST add declaration of variable to PROPERTY parameters. /// Microsoft what the f**k? /// Documentation says we should use <see cref="CodeIndexerExpression"/>, /// but it's not work. /// Anyway, I found ONE post from 2003 with correct example /// source: https://forums.asp.net/post/354445.aspx /// thanks man, this is gonna suck when your post will be deleted. CodeMemberProperty indexer = new CodeMemberProperty() { Attributes = MemberAttributes.Public | MemberAttributes.Final , Type = new CodeTypeReference(typeof(Xna.Rectangle)) , Name = "Item" , HasGet = true }; /// Declaration of variable. /// int index CodeParameterDeclarationExpression index = new CodeParameterDeclarationExpression( new CodeTypeReference(typeof(int)) , "index" ); /// Magic continues. indexer.Parameters.Add(index); /// Create getter. /// get { return Frames[index]; } indexer.GetStatements.Add(new CodeMethodReturnStatement( new CodeArrayIndexerExpression( new CodeVariableReferenceExpression("Frames") , new CodeVariableReferenceExpression("index")) )); /// Add Frames field, Length property, and indexer to the class. spriteSheetClass.Members.Add(frames); spriteSheetClass.Members.Add(length); spriteSheetClass.Members.Add(indexer); return(spriteSheetClass); }
/// <summary> /// Create atlas, /// and generate .dll or(and) source code /// with classes which contains rectangles. /// </summary> public static void Pack(IPackOptions opt) { if (opt.Sprites.Length == 0 && opt.Tiles.Length == 0) { throw new ApplicationException("Nothing to pack here."); } /// Delete previous atlas. File.Delete(Path.Combine(opt.Output, String.Concat(opt.Name, ".png"))); /// Paths to all dirs. var allDirs = opt.Sprites.Concat(opt.Tiles); /// Paths to all images. var images = new List <string>(); foreach (var dir in allDirs) { var dirInfo = new DirectoryInfo(dir); images.AddRange(Walkthrough.GetPictures(dirInfo).Select(file => file.FullName)); } /// Find same paths. var groups = images.GroupBy(name => name).Where(group => group.Count() != 1); if (groups.Count() != 0) { throw new ApplicationException( "Found nested paths. Check input parameters." ); } /// Packer from sspack. var imagePacker = new sspack.ImagePacker(); /// Create sprite and dictionary. /// map - Dictionary: full filename -> rectangle in output image. /// imageBitmap - Texture atlas. imagePacker.PackImage( images , opt.PowerOfTwo , opt.Square , opt.MaxWidth , opt.MaxHeight , opt.Padding , true /// Generate dictionary, , out var imageBitmap , out var map ); var codeUnit = new CodeCompileUnit(); var codeNameSpace = new CodeNamespace("NutPacker.Content"); codeUnit.Namespaces.Add(codeNameSpace); /// Generate code. if (opt.Sprites.Length != 0) { foreach (var sprites in opt.Sprites) { var spritesDirectory = new DirectoryInfo(sprites); codeNameSpace.Types.Add(Walkthrough.GenerateSpriteCodeDom(spritesDirectory, map)); } } if (opt.Tiles.Length != 0) { foreach (var pics in opt.Tiles) { var picturesDirectory = new DirectoryInfo(pics); codeNameSpace.Types.Add(Walkthrough.GenerateTileCodeDom(picturesDirectory, map)); } } var codeDomProvider = CodeDomProvider.CreateProvider("CSharp"); var generatorOptions = new CodeGeneratorOptions { BracingStyle = "C" , BlankLinesBetweenMembers = false , VerbatimOrder = true }; /// Create file with source code. if (opt.GenerateSource) { using (var sourceWriter = new StreamWriter(Path.Combine(opt.Output, String.Concat(Walkthrough.VariableName(opt.Name), ".cs")))) { codeDomProvider.GenerateCodeFromCompileUnit( codeUnit , sourceWriter , generatorOptions); } } var assemblyNames = new string[] { "NutPackerLib.dll" , "MonoGame.Framework.dll" , "System.Runtime.dll" }; CompilerParameters cp; if (opt.GenerateLib) { cp = new CompilerParameters( assemblyNames , Path.Combine(opt.Output, String.Concat(Walkthrough.VariableName(opt.Name), ".dll")) , false ) { GenerateInMemory = false }; } else { cp = new CompilerParameters(assemblyNames) { GenerateInMemory = true }; } /// Compile the CodeDom. var compile = codeDomProvider.CompileAssemblyFromDom(cp, codeUnit); /// Print errors. foreach (var e in compile.Errors) { Console.Error.WriteLine(e.ToString()); } /// If no error - save the sprite. if (compile.Errors.Count == 0) { using (var streamWriter = new StreamWriter(Path.Combine(opt.Output, String.Concat(Walkthrough.VariableName(opt.Name), ".png")))) { imageBitmap.Save( streamWriter.BaseStream , System.Drawing.Imaging.ImageFormat.Png ); } } }