Просмотр исходного кода

Merge pull request #17 from tainicom/Tilemap

Tilemap
Nikos Kastellanos 3 лет назад
Родитель
Сommit
f479df6c10
91 измененных файлов с 3568 добавлено и 0 удалено
  1. 18 0
      Aether.Extras.PORTABLE.sln
  2. 20 0
      Aether.Extras.WINDOWS.MG.sln
  3. 22 0
      Aether.Extras.WINDOWS.XNA.sln
  4. 174 0
      Content.Pipeline/TilemapImporters/Processors/TilePacker.cs
  5. 149 0
      Content.Pipeline/TilemapImporters/Processors/TilemapProcessor.cs
  6. 28 0
      Content.Pipeline/TilemapImporters/Properties/AssemblyInfo.cs
  7. 55 0
      Content.Pipeline/TilemapImporters/Serialization/TilemapWriter.cs
  8. 55 0
      Content.Pipeline/TilemapImporters/Tilemap/TileContent.cs
  9. 117 0
      Content.Pipeline/TilemapImporters/Tilemap/TilemapContent.cs
  10. 31 0
      Content.Pipeline/TilemapImporters/Tilemap/TilesetContent.cs
  11. 50 0
      Content.Pipeline/TilemapImporters/Tilemap/XMLExtensions.cs
  12. 66 0
      Content.Pipeline/TilemapImporters/TilemapImporter.WINDOWS.MG.csproj
  13. 18 0
      Content.Pipeline/TilemapImporters/TilemapImporter.WINDOWS.MG.csproj.user
  14. 65 0
      Content.Pipeline/TilemapImporters/TilemapImporter.WINDOWS.XNA.csproj
  15. 183 0
      Content.Pipeline/TilemapImporters/TilemapImporter.cs
  16. 27 0
      Samples.WINDOWS.MG.sln
  17. 32 0
      Samples.WINDOWS.XNA.sln
  18. BIN
      Samples/Tilemap/Game.ico
  19. 153 0
      Samples/Tilemap/Game1.cs
  20. BIN
      Samples/Tilemap/GameThumbnail.png
  21. BIN
      Samples/Tilemap/Icon.ico
  22. 19 0
      Samples/Tilemap/Program.cs
  23. 34 0
      Samples/Tilemap/Properties/AssemblyInfo.cs
  24. 89 0
      Samples/Tilemap/Samples.Tilemap.WINDOWS.MG.csproj
  25. 137 0
      Samples/Tilemap/Samples.Tilemap.WINDOWS.XNA.csproj
  26. 3 0
      Samples/Tilemap/app.config
  27. 42 0
      Samples/Tilemap/app.manifest
  28. BIN
      Samples/TilemapContent/Object/Bush_1.png
  29. BIN
      Samples/TilemapContent/Object/Bush_2.png
  30. BIN
      Samples/TilemapContent/Object/Bush_3.png
  31. BIN
      Samples/TilemapContent/Object/Bush_4.png
  32. BIN
      Samples/TilemapContent/Object/Crate.png
  33. BIN
      Samples/TilemapContent/Object/Mushroom_1.png
  34. BIN
      Samples/TilemapContent/Object/Mushroom_2.png
  35. BIN
      Samples/TilemapContent/Object/Sign_1.png
  36. BIN
      Samples/TilemapContent/Object/Sign_2.png
  37. BIN
      Samples/TilemapContent/Object/Stone.png
  38. BIN
      Samples/TilemapContent/Object/Tree_1.png
  39. BIN
      Samples/TilemapContent/Object/Tree_2.png
  40. BIN
      Samples/TilemapContent/Object/Tree_3.png
  41. 93 0
      Samples/TilemapContent/Samples.TilemapContent.contentproj
  42. 62 0
      Samples/TilemapContent/Samples.TilemapContent.mgcb
  43. BIN
      Samples/TilemapContent/Tiles/1.png
  44. BIN
      Samples/TilemapContent/Tiles/10.png
  45. BIN
      Samples/TilemapContent/Tiles/11.png
  46. BIN
      Samples/TilemapContent/Tiles/12.png
  47. BIN
      Samples/TilemapContent/Tiles/13.png
  48. BIN
      Samples/TilemapContent/Tiles/14.png
  49. BIN
      Samples/TilemapContent/Tiles/15.png
  50. BIN
      Samples/TilemapContent/Tiles/16.png
  51. BIN
      Samples/TilemapContent/Tiles/17.png
  52. BIN
      Samples/TilemapContent/Tiles/18.png
  53. BIN
      Samples/TilemapContent/Tiles/2.png
  54. BIN
      Samples/TilemapContent/Tiles/3.png
  55. BIN
      Samples/TilemapContent/Tiles/4.png
  56. BIN
      Samples/TilemapContent/Tiles/5.png
  57. BIN
      Samples/TilemapContent/Tiles/6.png
  58. BIN
      Samples/TilemapContent/Tiles/7.png
  59. BIN
      Samples/TilemapContent/Tiles/8.png
  60. BIN
      Samples/TilemapContent/Tiles/9.png
  61. 60 0
      Samples/TilemapContent/font.spritefont
  62. 1 0
      Samples/TilemapContent/free-platformer-game-tileset.txt
  63. 10 0
      Samples/TilemapContent/tilemapMipmap.tmx
  64. 10 0
      Samples/TilemapContent/tilemapMipmapPerSprite.tmx
  65. 10 0
      Samples/TilemapContent/tilemapNoMipmap.tmx
  66. 90 0
      Samples/TilemapContent/tileset.tsx
  67. 49 0
      Shaders/Tilemap/Aether.Shaders.Tilemap.PORTABLE.csproj
  68. 60 0
      Shaders/Tilemap/Aether.Shaders.Tilemap.WINDOWS.MG.csproj
  69. 61 0
      Shaders/Tilemap/Aether.Shaders.Tilemap.WINDOWS.XNA.csproj
  70. 24 0
      Shaders/Tilemap/Properties/AssemblyInfo.PORTABLE.cs
  71. 37 0
      Shaders/Tilemap/Properties/AssemblyInfo.cs
  72. BIN
      Shaders/Tilemap/Resources/TilemapEffect.dx11.mgfxo.8
  73. BIN
      Shaders/Tilemap/Resources/TilemapEffect.dx11.mgfxo.9
  74. BIN
      Shaders/Tilemap/Resources/TilemapEffect.ogl.mgfxo.8
  75. BIN
      Shaders/Tilemap/Resources/TilemapEffect.ogl.mgfxo.9
  76. BIN
      Shaders/Tilemap/Resources/TilemapEffect.xna.WinReach
  77. 25 0
      Shaders/Tilemap/Shaders/BuildShaders.bat
  78. 58 0
      Shaders/Tilemap/Shaders/Common.fxh
  79. 62 0
      Shaders/Tilemap/Shaders/Macros.fxh
  80. 161 0
      Shaders/Tilemap/Shaders/Structures.fxh
  81. 158 0
      Shaders/Tilemap/Shaders/TilemapEffect.fx
  82. 489 0
      Shaders/Tilemap/TilemapEffect.cs
  83. 54 0
      Tilemap/Aether.Tilemap.PORTABLE.csproj
  84. 69 0
      Tilemap/Aether.Tilemap.WINDOWS.MG.csproj
  85. 84 0
      Tilemap/Aether.Tilemap.WINDOWS.XNA.csproj
  86. 87 0
      Tilemap/ContentReaders/TilemapReader.cs
  87. 24 0
      Tilemap/Properties/AssemblyInfo.PORTABLE.cs
  88. 41 0
      Tilemap/Properties/AssemblyInfo.cs
  89. 34 0
      Tilemap/Tilemap/Extensions.cs
  90. 33 0
      Tilemap/Tilemap/Tile.cs
  91. 35 0
      Tilemap/Tilemap/Tilemap.cs

+ 18 - 0
Aether.Extras.PORTABLE.sln

@@ -17,6 +17,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnimationImporters.PORTABLE
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Atlas.PORTABLE", "Atlas\Aether.Atlas.PORTABLE.csproj", "{E5907795-BC19-4CAA-9909-9F7145E1BB0D}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Tilemap.PORTABLE", "Tilemap\Aether.Tilemap.PORTABLE.csproj", "{94963AFA-FAC3-42A0-A2B2-4DA3A7F604D8}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aether.Shaders", "Aether.Shaders", "{0193DE8C-8B7A-43F6-A30B-E65EC387B2C4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Shaders.Tilemap.PORTABLE", "Shaders\Tilemap\Aether.Shaders.Tilemap.PORTABLE.csproj", "{CA1564C9-D4A6-4454-A6B6-7B4E907DF8C0}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -47,6 +53,14 @@ Global
 		{E5907795-BC19-4CAA-9909-9F7145E1BB0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{E5907795-BC19-4CAA-9909-9F7145E1BB0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{E5907795-BC19-4CAA-9909-9F7145E1BB0D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{94963AFA-FAC3-42A0-A2B2-4DA3A7F604D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{94963AFA-FAC3-42A0-A2B2-4DA3A7F604D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{94963AFA-FAC3-42A0-A2B2-4DA3A7F604D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{94963AFA-FAC3-42A0-A2B2-4DA3A7F604D8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{CA1564C9-D4A6-4454-A6B6-7B4E907DF8C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{CA1564C9-D4A6-4454-A6B6-7B4E907DF8C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{CA1564C9-D4A6-4454-A6B6-7B4E907DF8C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{CA1564C9-D4A6-4454-A6B6-7B4E907DF8C0}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -55,5 +69,9 @@ Global
 		{C3218A39-5491-44BF-B820-754832E318DB} = {717F89BC-8423-4E7C-A834-460CFD62B1E3}
 		{0A079394-D331-433A-94DF-AA25B6522279} = {717F89BC-8423-4E7C-A834-460CFD62B1E3}
 		{962D579A-91F7-4FAB-940A-423E04B8465E} = {717F89BC-8423-4E7C-A834-460CFD62B1E3}
+		{CA1564C9-D4A6-4454-A6B6-7B4E907DF8C0} = {0193DE8C-8B7A-43F6-A30B-E65EC387B2C4}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {CC734668-3E0E-4826-90C3-55064F0473DB}
 	EndGlobalSection
 EndGlobal

+ 20 - 0
Aether.Extras.WINDOWS.MG.sln

@@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Graphics.WINDOWS.MG"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnimationImporters.WINDOWS.MG", "Content.Pipeline\AnimationImporters\AnimationImporters.WINDOWS.MG.csproj", "{D9A47306-DEE0-4410-BC2C-BA8FFCE682A3}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TilemapImporter.WINDOWS.MG", "Content.Pipeline\TilemapImporters\TilemapImporter.WINDOWS.MG.csproj", "{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89}"
+EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Animation.WINDOWS.MG", "Animation\Aether.Animation.WINDOWS.MG.csproj", "{F08D6D4C-60FB-4543-8D81-594080EB8051}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aether.Shaders", "Aether.Shaders", "{13D47E11-4A7C-49C8-942E-2543E9C0098A}"
@@ -29,10 +31,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Atlas.WINDOWS.MG", "
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Shaders.InfiniteGrid.WINDOWS.MG", "Shaders\InfiniteGrid\Aether.Shaders.InfiniteGrid.WINDOWS.MG.csproj", "{08296A2E-BECF-4DB9-A518-8A613D07B80E}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Shaders.Tilemap.WINDOWS.MG", "Shaders\Tilemap\Aether.Shaders.Tilemap.WINDOWS.MG.csproj", "{6721CD07-0880-4154-85AF-EBE6043EF178}"
+EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SLMCImporter.WINDOWS.MG", "Content.Pipeline\SLMCImporter\SLMCImporter.WINDOWS.MG.csproj", "{C9FA924B-DA91-4641-8B7D-CBC09E5560AE}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VoxelImporters.WINDOWS.MG", "Content.Pipeline\VoxelImporters\VoxelImporters.WINDOWS.MG.csproj", "{AA15CF95-0AF2-4346-9465-CB6EDBE22ED9}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Tilemap.WINDOWS.MG", "Tilemap\Aether.Tilemap.WINDOWS.MG.csproj", "{7BD9F460-F3F9-40A0-B298-066EA975C06C}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -59,6 +65,10 @@ Global
 		{D9A47306-DEE0-4410-BC2C-BA8FFCE682A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{D9A47306-DEE0-4410-BC2C-BA8FFCE682A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{D9A47306-DEE0-4410-BC2C-BA8FFCE682A3}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89}.Release|Any CPU.Build.0 = Release|Any CPU
 		{F08D6D4C-60FB-4543-8D81-594080EB8051}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{F08D6D4C-60FB-4543-8D81-594080EB8051}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{F08D6D4C-60FB-4543-8D81-594080EB8051}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -83,6 +93,10 @@ Global
 		{08296A2E-BECF-4DB9-A518-8A613D07B80E}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{08296A2E-BECF-4DB9-A518-8A613D07B80E}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{08296A2E-BECF-4DB9-A518-8A613D07B80E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6721CD07-0880-4154-85AF-EBE6043EF178}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6721CD07-0880-4154-85AF-EBE6043EF178}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6721CD07-0880-4154-85AF-EBE6043EF178}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6721CD07-0880-4154-85AF-EBE6043EF178}.Release|Any CPU.Build.0 = Release|Any CPU
 		{C9FA924B-DA91-4641-8B7D-CBC09E5560AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{C9FA924B-DA91-4641-8B7D-CBC09E5560AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{C9FA924B-DA91-4641-8B7D-CBC09E5560AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -91,6 +105,10 @@ Global
 		{AA15CF95-0AF2-4346-9465-CB6EDBE22ED9}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{AA15CF95-0AF2-4346-9465-CB6EDBE22ED9}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{AA15CF95-0AF2-4346-9465-CB6EDBE22ED9}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7BD9F460-F3F9-40A0-B298-066EA975C06C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7BD9F460-F3F9-40A0-B298-066EA975C06C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7BD9F460-F3F9-40A0-B298-066EA975C06C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7BD9F460-F3F9-40A0-B298-066EA975C06C}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -100,10 +118,12 @@ Global
 		{48E4029A-115C-4DC2-AF3A-0AB94F36BFC0} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
 		{400DC7B2-739D-4156-916D-2F2E1920310D} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
 		{D9A47306-DEE0-4410-BC2C-BA8FFCE682A3} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
+		{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
 		{FBBDE2BA-F9F3-4041-8584-2C912C235E26} = {13D47E11-4A7C-49C8-942E-2543E9C0098A}
 		{96105100-20DB-4187-9BCA-0A20AC9F1298} = {13D47E11-4A7C-49C8-942E-2543E9C0098A}
 		{E710FBEA-8C75-405D-B6B4-CFC82CB48FB5} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
 		{08296A2E-BECF-4DB9-A518-8A613D07B80E} = {13D47E11-4A7C-49C8-942E-2543E9C0098A}
+		{6721CD07-0880-4154-85AF-EBE6043EF178} = {13D47E11-4A7C-49C8-942E-2543E9C0098A}
 		{C9FA924B-DA91-4641-8B7D-CBC09E5560AE} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
 		{AA15CF95-0AF2-4346-9465-CB6EDBE22ED9} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
 	EndGlobalSection

+ 22 - 0
Aether.Extras.WINDOWS.XNA.sln

@@ -9,6 +9,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aether.Content.Pipeline", "
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnimationImporters.WINDOWS.XNA", "Content.Pipeline\AnimationImporters\AnimationImporters.WINDOWS.XNA.csproj", "{E22F02E7-6799-4C14-B9B3-B461D6E9AB6E}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TilemapImporter.WINDOWS.XNA", "Content.Pipeline\TilemapImporters\TilemapImporter.WINDOWS.XNA.csproj", "{B5FAF2D1-7164-4956-9474-0F4B19706DE8}"
+EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Animation.WINDOWS.XNA", "Animation\Aether.Animation.WINDOWS.XNA.csproj", "{1BD2DBC0-D366-42F7-9369-F566CCD01C03}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aether.Shaders", "Aether.Shaders", "{EFC7A27C-C20B-4BE7-8B3A-2B109991D704}"
@@ -23,10 +25,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Atlas.WINDOWS.XNA",
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Shaders.InfiniteGrid.WINDOWS.XNA", "Shaders\InfiniteGrid\Aether.Shaders.InfiniteGrid.WINDOWS.XNA.csproj", "{908EC8FF-B0ED-478A-B486-702E578AF64A}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Shaders.Tilemap.WINDOWS.XNA", "Shaders\Tilemap\Aether.Shaders.Tilemap.WINDOWS.XNA.csproj", "{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}"
+EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SLMCImporter.WINDOWS.XNA", "Content.Pipeline\SLMCImporter\SLMCImporter.WINDOWS.XNA.csproj", "{497B1130-EB63-4057-BC40-F60D6FFA50CA}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VoxelImporters.WINDOWS.XNA", "Content.Pipeline\VoxelImporters\VoxelImporters.WINDOWS.XNA.csproj", "{0690782F-0000-0000-0000-000000000000}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Tilemap.WINDOWS.XNA", "Tilemap\Aether.Tilemap.WINDOWS.XNA.csproj", "{746551BA-FB64-43B0-8479-0506B930D3E5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x86 = Debug|x86
@@ -69,14 +75,28 @@ Global
 		{908EC8FF-B0ED-478A-B486-702E578AF64A}.Debug|x86.Build.0 = Debug|x86
 		{908EC8FF-B0ED-478A-B486-702E578AF64A}.Release|x86.ActiveCfg = Release|x86
 		{908EC8FF-B0ED-478A-B486-702E578AF64A}.Release|x86.Build.0 = Release|x86
+		{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}.Debug|x86.ActiveCfg = Debug|x86
+		{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}.Debug|x86.Build.0 = Debug|x86
+		{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}.Release|x86.ActiveCfg = Release|x86
+		{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}.Release|x86.Build.0 = Release|x86
 		{497B1130-EB63-4057-BC40-F60D6FFA50CA}.Debug|x86.ActiveCfg = Debug|x86
 		{497B1130-EB63-4057-BC40-F60D6FFA50CA}.Debug|x86.Build.0 = Debug|x86
 		{497B1130-EB63-4057-BC40-F60D6FFA50CA}.Release|x86.ActiveCfg = Release|x86
 		{497B1130-EB63-4057-BC40-F60D6FFA50CA}.Release|x86.Build.0 = Release|x86
+		{B5FAF2D1-7164-4956-9474-0F4B19706DE8}.Debug|x86.ActiveCfg = Debug|x86
+		{B5FAF2D1-7164-4956-9474-0F4B19706DE8}.Debug|x86.Build.0 = Debug|x86
+		{B5FAF2D1-7164-4956-9474-0F4B19706DE8}.Release|x86.ActiveCfg = Release|x86
+		{B5FAF2D1-7164-4956-9474-0F4B19706DE8}.Release|x86.Build.0 = Release|x86
 		{0690782F-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86
 		{0690782F-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86
 		{0690782F-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86
 		{0690782F-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86
+		{746551BA-FB64-43B0-8479-0506B930D3E5}.Debug|x86.ActiveCfg = Debug|x86
+		{746551BA-FB64-43B0-8479-0506B930D3E5}.Debug|x86.Build.0 = Debug|x86
+		{746551BA-FB64-43B0-8479-0506B930D3E5}.Release|x86.ActiveCfg = Release|x86
+		{746551BA-FB64-43B0-8479-0506B930D3E5}.Release|x86.Build.0 = Release|x86
+		
+		
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -86,9 +106,11 @@ Global
 		{E22F02E7-6799-4C14-B9B3-B461D6E9AB6E} = {F6B6E505-6037-49E4-9060-8B29A7B99BC1}
 		{90E6017D-198B-4470-BF9B-8B8791C295CC} = {F6B6E505-6037-49E4-9060-8B29A7B99BC1}
 		{497B1130-EB63-4057-BC40-F60D6FFA50CA} = {F6B6E505-6037-49E4-9060-8B29A7B99BC1}
+		{B5FAF2D1-7164-4956-9474-0F4B19706DE8} = {F6B6E505-6037-49E4-9060-8B29A7B99BC1}
 		{0690782F-0000-0000-0000-000000000000} = {F6B6E505-6037-49E4-9060-8B29A7B99BC1}
 		{89E0198E-7298-411A-B5C1-61F2754A3F80} = {EFC7A27C-C20B-4BE7-8B3A-2B109991D704}
 		{B82B862D-C728-4A10-8A56-65D688E022C8} = {EFC7A27C-C20B-4BE7-8B3A-2B109991D704}
 		{908EC8FF-B0ED-478A-B486-702E578AF64A} = {EFC7A27C-C20B-4BE7-8B3A-2B109991D704}
+		{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6} = {EFC7A27C-C20B-4BE7-8B3A-2B109991D704}
 	EndGlobalSection
 EndGlobal

+ 174 - 0
Content.Pipeline/TilemapImporters/Processors/TilePacker.cs

@@ -0,0 +1,174 @@
+#region License
+//   Copyright 2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+
+namespace tainicom.Aether.Content.Pipeline
+{
+	internal static class TilePacker
+	{
+		internal static IList<TileContent> ArrangeGlyphs(IList<TileContent> sourceTiles,
+            int tileWidth, int tileHeight,
+            bool requirePOT, bool requireSquare)
+		{
+            // copy tiles to destTiles
+            var destTiles = new List<TileContent>();
+            for (int i = 0; i < sourceTiles.Count; i++)
+            {
+                var srcTile = sourceTiles[i];
+                var dstTile = new TileContent(srcTile);
+                destTiles.Add(dstTile);
+            }
+            
+            for (int i = 0; i < destTiles.Count; i++)
+			{
+                var dstTile = destTiles[i];
+                dstTile.DstBounds.Width = tileWidth;
+                dstTile.DstBounds.Height = tileHeight;
+            }
+            
+			// Sort so the largest glyphs get arranged first.
+			destTiles.Sort(CompareTileSizes);
+
+			// Work out how big the output bitmap should be.
+			int outputWidth = EstimateOutputWidth(destTiles);
+            outputWidth = MakeValidTextureSize(outputWidth, requirePOT);
+            int outputHeight = 0;
+
+			// Choose positions for each glyph, one at a time.
+			for (int i = 0; i < destTiles.Count; i++)
+			{
+				PositionGlyph(destTiles, i, outputWidth);
+                outputHeight = Math.Max(outputHeight, destTiles[i].DstBounds.Y + destTiles[i].DstBounds.Height);
+            }
+
+			// Create the merged output bitmap.
+			outputHeight = MakeValidTextureSize(outputHeight, requirePOT);
+			if (requireSquare)
+            {
+				outputHeight = Math.Max(outputWidth, outputHeight);
+				outputWidth = outputHeight;
+			}
+
+            return destTiles;
+
+        }
+        
+		static void PositionGlyph(List<TileContent> glyphs, int index, int outputWidth)
+		{
+			int x = 0;
+			int y = 0;
+
+			while (true)
+			{
+				// Is this position free for us to use?
+				int intersects = FindIntersectingTile(glyphs, index, x, y);
+
+				if (intersects < 0)
+				{
+                    glyphs[index].DstBounds.X = x;
+                    glyphs[index].DstBounds.Y = y;
+                    return;
+				}
+
+				// Skip past the existing glyph that we collided with.
+                x = glyphs[intersects].DstBounds.X + glyphs[intersects].DstBounds.Width;
+
+                // If we ran out of room to move to the right, try the next line down instead.
+                //if (x + glyphs[index].SrcBounds.Width > outputWidth)
+                if (x + glyphs[index].DstBounds.Width > outputWidth)
+                {
+					x = 0;
+					y++;
+				}
+			}
+		}
+        
+		// Checks if a proposed glyph position collides with anything that we already arranged.
+		static int FindIntersectingTile(List<TileContent> glyphs, int index, int x, int y)
+		{
+            var bounds = glyphs[index].DstBounds;
+
+			for (int i = 0; i < index; i++)
+			{
+                var other = glyphs[i].DstBounds;
+
+                if (other.X >= x + bounds.Width)
+					continue;
+				if (other.X + other.Width <= x)
+					continue;
+				if (other.Y >= y + bounds.Height)
+					continue;
+				if (other.Y + other.Height <= y)
+					continue;
+
+				return i;
+			}
+
+			return -1;
+		}
+
+		static int CompareTileSizes(TileContent a, TileContent b)
+		{
+			var res = b.DstBounds.Height.CompareTo(a.DstBounds.Height);
+            if (res == 0)
+                res = b.DstBounds.Width.CompareTo(a.DstBounds.Width);
+  		    return res;
+		}
+
+		static int EstimateOutputWidth(IList<TileContent> sourceGlyphs)
+		{
+			int maxWidth = 0;
+			int totalSize = 0;
+
+			foreach (var sourceGlyph in sourceGlyphs)
+			{
+				maxWidth = Math.Max(maxWidth, sourceGlyph.DstBounds.Width);
+				totalSize += sourceGlyph.DstBounds.Width * sourceGlyph.DstBounds.Height;
+			}
+
+			int width = Math.Max((int)Math.Sqrt(totalSize), maxWidth);
+            return width;
+		}
+
+		// Rounds a value up to the next larger valid texture size.
+		static int MakeValidTextureSize(int value, bool requirePowerOfTwo)
+		{
+			// In case we want to compress the texture, make sure the size is a multiple of 4.
+			const int blockSize = 4;
+
+			if (requirePowerOfTwo)
+			{
+				// Round up to a power of two.
+				int powerOfTwo = blockSize;
+
+				while (powerOfTwo < value)
+					powerOfTwo <<= 1;
+
+				return powerOfTwo;
+			}
+			else
+			{
+				// Round up to the specified block size.
+				return (value + blockSize - 1) & ~(blockSize - 1);
+			}
+		}
+	}
+
+}
+

+ 149 - 0
Content.Pipeline/TilemapImporters/Processors/TilemapProcessor.cs

@@ -0,0 +1,149 @@
+#region License
+//   Copyright 2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+using System;
+using System.ComponentModel;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content.Pipeline;
+using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
+using Microsoft.Xna.Framework.Content.Pipeline.Processors;
+
+namespace tainicom.Aether.Content.Pipeline
+{
+    [ContentProcessor(DisplayName = "Tilemap Processor - Aether")]
+    public class TilemapProcessor : TextureProcessor, IContentProcessor
+    {        
+        private bool _mipmapsPerSprite = true;
+
+#if WINDOWS
+        // override InputType
+        [Browsable(false)]
+#endif
+        Type IContentProcessor.InputType { get { return typeof(TilemapContent); } }
+
+#if WINDOWS
+        // override OutputType
+        [Browsable(false)]
+#endif
+        Type IContentProcessor.OutputType { get { return typeof(TilemapContent); } }
+        
+
+        [DefaultValue(true)]
+        public bool MipmapsPerSprite
+        {
+            get { return _mipmapsPerSprite; }
+            set { _mipmapsPerSprite = value; }
+        }
+
+        public TilemapProcessor()
+        {
+        }
+        
+        object IContentProcessor.Process(object input, ContentProcessorContext context)
+        {
+            return Process((TilemapContent)input, context);
+        }
+
+        public TilemapContent Process(TilemapContent input, ContentProcessorContext context)
+        {
+            if (MipmapsPerSprite && GenerateMipmaps)
+                foreach (var texture in input.DestinationTiles)
+                    texture.SrcTexture.GenerateMipmaps(false);
+
+            var output = input;
+            
+            if (GenerateMipmaps)
+            {
+                if (MipmapsPerSprite)
+                {
+                    var maxTileWidth = 1;
+                    var maxTileHeight = 1;
+                    foreach (var tile in input.DestinationTiles)
+                    {
+                        maxTileWidth = Math.Max(maxTileWidth, tile.DstBounds.Width);
+                        maxTileHeight = Math.Max(maxTileHeight, tile.DstBounds.Height);
+                    }
+
+                    for (int mipLevel = 1; ; mipLevel++)
+                    {
+                        int mipLevel2 = (int)Math.Pow(2, mipLevel);
+                        Rectangle size = new Rectangle(0, 0, output.TextureAtlas.Faces[0][0].Width, output.TextureAtlas.Faces[0][0].Height);
+                        size.Width /= mipLevel2;
+                        size.Height /= mipLevel2;
+
+                        if ((maxTileWidth / mipLevel2) < 1 && (maxTileHeight / mipLevel2) < 1)
+                            break;
+
+                        var mipmapBmp = new PixelBitmapContent<Color>(size.Width, size.Height);
+                        foreach (var tile in input.DestinationTiles)
+                        {
+                            if (mipLevel >= tile.SrcTexture.Faces[0].Count) continue;
+                            var srcBmp = tile.SrcTexture.Faces[0][mipLevel];
+                            var srcBounds = new Rectangle(0, 0, srcBmp.Width, srcBmp.Height);
+                            var dstBounds = tile.DstBounds;
+                            dstBounds.X = (int)Math.Ceiling((float)dstBounds.X / mipLevel2);
+                            dstBounds.Y = (int)Math.Ceiling((float)dstBounds.Y / mipLevel2);
+                            dstBounds.Width = (int)(dstBounds.Width / mipLevel2);
+                            dstBounds.Height = (int)(dstBounds.Height / mipLevel2);
+                            // snap image to bottom
+                            dstBounds.Width  = srcBounds.Width;
+                            dstBounds.Y     += (dstBounds.Height - srcBounds.Height);
+                            dstBounds.Height = srcBounds.Height;
+
+                            if (dstBounds.Width == 0 || dstBounds.Height == 0)
+                                continue;
+                                
+                            //if (dstBounds.Width > 0 && dstBounds.Height > 0)
+                                BitmapContent.Copy(srcBmp, srcBounds, mipmapBmp, dstBounds);
+                        }
+                        output.TextureAtlas.Mipmaps.Add(mipmapBmp);
+                    }
+
+                    var outputFace0 = output.TextureAtlas.Faces[0];
+                    while (outputFace0[outputFace0.Count - 1].Width > 1 || outputFace0[outputFace0.Count - 1].Height > 1)
+                    {
+                        var lastMipmap = outputFace0[outputFace0.Count - 1];
+                        var w = Math.Max(1, lastMipmap.Width/2);
+                        var h = Math.Max(1, lastMipmap.Height/2);
+                        var mipmapBmp = new PixelBitmapContent<Color>(w, h);
+                        //PixelBitmapContent<Color>.Copy(lastMipmap, mipmapBmp);
+                        output.TextureAtlas.Mipmaps.Add(mipmapBmp);
+                    }
+                }
+                else
+                {
+                    output.TextureAtlas.GenerateMipmaps(false);
+                }
+            }
+            
+            // Workaround MonoGame TextureProcessor bug.
+            // MonoGame TextureProcessor overwrites existing mipmaps.
+            if (GenerateMipmaps && MipmapsPerSprite)
+            {
+                GenerateMipmaps = false;
+                base.Process(output.TextureAtlas, context);
+                GenerateMipmaps = true;
+            }
+            else
+            {
+                base.Process(output.TextureAtlas, context);
+            }
+            
+            return output;
+        }
+        
+    }
+}

+ 28 - 0
Content.Pipeline/TilemapImporters/Properties/AssemblyInfo.cs

@@ -0,0 +1,28 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Aether.Content.Pipeline.TilemapImporter")]
+[assembly: AssemblyProduct("TilemapImporter")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyCopyright("Copyright © Kastellanos Nikolaos  2021")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 55 - 0
Content.Pipeline/TilemapImporters/Serialization/TilemapWriter.cs

@@ -0,0 +1,55 @@
+#region License
+//   Copyright 2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+using Microsoft.Xna.Framework.Content.Pipeline;
+using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
+using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler;
+using Microsoft.Xna.Framework.Graphics;
+using System;
+
+namespace tainicom.Aether.Content.Pipeline.Serialization
+{
+    [ContentTypeWriter]
+    class TextureAtlasWriter : ContentTypeWriter<TilemapContent>
+    {
+        protected override void Write(ContentWriter output, TilemapContent atlas)
+        {
+            output.WriteRawObject((Texture2DContent)atlas.TextureAtlas);
+
+            output.WriteRawObject((Texture2DContent)atlas.TextureMap);
+
+            // write Sprites
+            output.Write(atlas.DestinationTiles.Count);
+            foreach(var name in atlas.Tiles.Keys)
+            {
+                var sprite = atlas.Tiles[name];
+                output.Write(name);
+                output.Write(sprite.DstBounds.X);
+                output.Write(sprite.DstBounds.Y);
+                output.Write(sprite.DstBounds.Width);
+                output.Write(sprite.DstBounds.Height);
+            }
+            
+            return;
+        }
+        
+        public override string GetRuntimeReader(TargetPlatform targetPlatform)
+        {
+            return "tainicom.Aether.Graphics.Content.TilemapReader, Aether.Tilemap";
+        }
+        
+    }
+}

+ 55 - 0
Content.Pipeline/TilemapImporters/Tilemap/TileContent.cs

@@ -0,0 +1,55 @@
+#region License
+//   Copyright 2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
+
+namespace tainicom.Aether.Content.Pipeline
+{
+    public class TileContent
+    {
+        internal TilesetContent Tileset;
+        internal int Id;
+        public TextureContent SrcTexture;
+        public Rectangle SrcBounds;
+
+        public Rectangle DstBounds;
+
+        public TileContent()
+        {
+            this.Tileset = null;
+            this.Id = -1;
+            this.SrcTexture = null;
+            this.SrcBounds = Rectangle.Empty;
+            this.DstBounds = Rectangle.Empty;
+        }
+
+        public TileContent(TileContent other)
+        {
+            this.Tileset = other.Tileset;
+            this.Id = other.Id;
+            this.SrcTexture = other.SrcTexture;
+            this.SrcBounds = other.SrcBounds;
+            this.DstBounds = other.DstBounds;
+        }
+
+        public override string ToString()
+        {
+            return string.Format("{{SrcBounds:{0}, DstBounds{1}}}", SrcBounds, DstBounds);
+        }
+    }
+}

+ 117 - 0
Content.Pipeline/TilemapImporters/Tilemap/TilemapContent.cs

@@ -0,0 +1,117 @@
+#region License
+//   Copyright 2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content.Pipeline;
+using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
+
+namespace tainicom.Aether.Content.Pipeline
+{
+    public class TilemapContent : Texture2DContent
+    {
+        public Texture2DContent TextureAtlas { get { return this; } }
+        public Texture2DContent TextureMap;
+        public readonly Dictionary<string, TileContent> Tiles = new Dictionary<string, TileContent>();
+        
+        public TilesetContent Tileset;
+        internal readonly List<TileContent> DestinationTiles = new List<TileContent>();
+
+        internal int MapColumns, MapRows;
+        internal int TileWidth, TileHeight;
+        internal int Width, Height;
+
+        internal string Renderorder;
+        internal int Firstgid;
+        internal int LayerColumns, LayerRows;
+        internal int[] MapData;
+
+
+
+        internal static void PackTiles(TilemapContent output, int tileWidth, int tileHeight)
+        {
+            var dstTiles = TilePacker.ArrangeGlyphs(output.Tileset.SourceTiles, tileWidth, tileHeight, true, true);
+
+            foreach (var dstTile in dstTiles)
+            {
+                output.DestinationTiles.Add(dstTile);
+                var name = dstTile.SrcTexture.Name;
+                if (output.Tiles.ContainsKey(name))
+                    name = name + dstTile.Id;
+                output.Tiles.Add(name, dstTile);
+            }
+        }
+        
+        internal static void RenderAtlas(TilemapContent output)
+        {
+            Rectangle s = new Rectangle(0,0,1,1);
+            foreach (var dstTile in output.DestinationTiles)
+            {
+                s = Rectangle.Union(s,dstTile.DstBounds);
+            }
+
+            var outputBmp = new PixelBitmapContent<Color>(s.Width, s.Height);
+
+            foreach (var dstTile in output.DestinationTiles)
+            {
+                var srcBounds = dstTile.SrcBounds;
+                var dstBounds = new Rectangle(dstTile.DstBounds.X, dstTile.DstBounds.Y, srcBounds.Width, srcBounds.Height);
+                var offsetX = 0;
+                var offsetY = dstTile.DstBounds.Height - srcBounds.Height;
+                dstBounds.X += offsetX;
+                dstBounds.Y += offsetY;
+                var srcBmp = dstTile.SrcTexture.Faces[0][0];
+                BitmapContent.Copy(srcBmp, srcBounds, outputBmp, dstBounds);
+            }
+            var mipmapChain = new MipmapChain(outputBmp);
+            output.TextureAtlas.Mipmaps = mipmapChain;
+        }
+
+        internal static void RenderMap(TilemapContent output)
+        {
+            var mp = new byte[output.MapData.Length * 4];
+            for (int i = 0; i < output.MapData.Length; i++)
+            {
+                var id = output.MapData[i] - output.Firstgid;
+                TileContent tile = null;
+                foreach (var t in output.DestinationTiles)
+                {
+                    if (t.Id == id)
+                    {
+                        tile = t;
+                        break;
+                    }
+                }
+
+                byte x = (byte)(tile.DstBounds.X / output.TileWidth);
+                byte y = (byte)(tile.DstBounds.Y / output.TileHeight);
+
+                mp[i * 4 + 0] = x;
+                mp[i * 4 + 1] = y;
+                mp[i * 4 + 2] = 0;
+                mp[i * 4 + 3] = 255;
+            }
+
+            BitmapContent bm = new PixelBitmapContent<Color>(output.MapColumns, output.MapRows);
+            bm.SetPixelData(mp);
+            Texture2DContent mt = new Texture2DContent();
+            mt.Faces[0].Add(bm);
+            output.TextureMap = mt;
+        }
+
+    }
+}

+ 31 - 0
Content.Pipeline/TilemapImporters/Tilemap/TilesetContent.cs

@@ -0,0 +1,31 @@
+#region License
+//   Copyright 2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+using System.Collections.Generic;
+using Microsoft.Xna.Framework.Content.Pipeline;
+using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
+
+namespace tainicom.Aether.Content.Pipeline
+{
+    public class TilesetContent : Texture2DContent
+    {
+        public int TileWidth;
+        public int tileHeight;
+
+        public readonly List<TileContent> SourceTiles = new List<TileContent>();
+        
+    }
+}

+ 50 - 0
Content.Pipeline/TilemapImporters/Tilemap/XMLExtensions.cs

@@ -0,0 +1,50 @@
+#region License
+//   Copyright 2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+using System.Globalization;
+using Microsoft.Xna.Framework;
+
+namespace System.Xml
+{
+    public static class XMLExtensions
+    {
+
+        public static string GetAttribute(this XmlNode xmlNode, string attributeName)
+        {
+            var attribute = xmlNode.Attributes[attributeName];
+            if (attribute == null) return null;
+            return attribute.Value;
+        }
+
+        public static int? GetAttributeAsInt(this XmlNode xmlNode, string attributeName)
+        {
+            var attribute = xmlNode.Attributes[attributeName];
+            if (attribute == null) return null;
+            return Int32.Parse(attribute.Value, CultureInfo.InvariantCulture);
+        }
+
+        public static Color? GetAttributeAsColor(this XmlNode xmlNode, string attributeName)
+        {
+            var attribute = xmlNode.Attributes[attributeName];
+            if (attribute == null) return null;
+            attribute.Value = attribute.Value.TrimStart(new char[] { '#' });
+            return new Color(
+                Int32.Parse(attribute.Value.Substring(0, 2), System.Globalization.NumberStyles.HexNumber),
+                Int32.Parse(attribute.Value.Substring(2, 2), System.Globalization.NumberStyles.HexNumber),
+                Int32.Parse(attribute.Value.Substring(4, 2), System.Globalization.NumberStyles.HexNumber));
+        }
+    }
+}

+ 66 - 0
Content.Pipeline/TilemapImporters/TilemapImporter.WINDOWS.MG.csproj

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>tainicom.Aether.Content.Pipeline</RootNamespace>
+    <AssemblyName>Aether.Content.Pipeline.TilemapImporter</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\..\bin\Debug\Windows\</OutputPath>
+    <DefineConstants>TRACE;DEBUG;WINDOWS MG</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\..\bin\Release\Windows\</OutputPath>
+    <DefineConstants>TRACE;WINDOWS MG</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <PackageReference Include="MonoGame.Framework.Portable.9000" Version="3.8.9008">
+      <PrivateAssets>all</PrivateAssets>
+      <ExcludeAssets>runtime</ExcludeAssets>
+    </PackageReference>
+    <PackageReference Include="MonoGame.Framework.Content.Pipeline.Portable.9000" Version="3.8.9008">
+      <PrivateAssets>all</PrivateAssets>
+      <ExcludeAssets>runtime</ExcludeAssets>
+    </PackageReference>
+    <Reference Include="System.XML" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="TilemapImporter.cs" />
+    <Compile Include="Tilemap\TileContent.cs" />
+    <Compile Include="Tilemap\TilemapContent.cs" />
+    <Compile Include="Tilemap\TilesetContent.cs" />
+    <Compile Include="Tilemap\XMLExtensions.cs" />
+    <Compile Include="Processors\TilePacker.cs" />
+    <Compile Include="Processors\TilemapProcessor.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Serialization\TilemapWriter.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 18 - 0
Content.Pipeline/TilemapImporters/TilemapImporter.WINDOWS.MG.csproj.user

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
+    <StartArguments>
+    </StartArguments>
+    <StartAction>Project</StartAction>
+    <StartProgram>
+    </StartProgram>
+    <StartWorkingDirectory>
+    </StartWorkingDirectory>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
+    <StartAction>Program</StartAction>
+    <StartProgram>C:\Program Files %28x86%29\MSBuild\MonoGame\v3.0\Tools\MGCB.exe</StartProgram>
+    <StartArguments>/reference:..\..\bin\Debug\Windows\Aether.Content.Pipeline.TilemapImporter.dll /importer:TilemapImporter /processor:TilemapProcessor /build:tilemapNoMipmap.tmx</StartArguments>
+    <StartWorkingDirectory>P:\tainicom\Aether.Extras\Samples\TilemapContent</StartWorkingDirectory>
+  </PropertyGroup>
+</Project>

+ 65 - 0
Content.Pipeline/TilemapImporters/TilemapImporter.WINDOWS.XNA.csproj

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
+  <PropertyGroup>
+    <ProjectGuid>{B5FAF2D1-7164-4956-9474-0F4B19706DE8}</ProjectGuid>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>tainicom.Aether.Content.Pipeline</RootNamespace>
+    <AssemblyName>Aether.Content.Pipeline.TilemapImporter</AssemblyName>
+    <XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
+    <XnaPlatform>Windows</XnaPlatform>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\..\bin\Debug\Windows.XNA\</OutputPath>
+    <DefineConstants>TRACE;DEBUG;WINDOWS XNA</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <PlatformTarget>x86</PlatformTarget>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <DebugType>none</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\..\bin\Release\Windows.XNA\</OutputPath>
+    <DefineConstants>TRACE;WINDOWS XNA</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <PlatformTarget>x86</PlatformTarget>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.TextureImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
+    <Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
+    <Reference Include="System" />
+    <Reference Include="System.Core">
+      <RequiredTargetFramework>4.0</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.XML" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="TilemapImporter.cs" />
+    <Compile Include="Tilemap\TileContent.cs" />
+    <Compile Include="Tilemap\TilemapContent.cs" />
+    <Compile Include="Tilemap\TilesetContent.cs" />
+    <Compile Include="Tilemap\XMLExtensions.cs" />
+    <Compile Include="Processors\TilePacker.cs" />
+    <Compile Include="Processors\TilemapProcessor.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Serialization\TilemapWriter.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.ContentPipelineExtensions.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+     Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 183 - 0
Content.Pipeline/TilemapImporters/TilemapImporter.cs

@@ -0,0 +1,183 @@
+#region License
+//   Copyright 2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Xml;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content.Pipeline;
+using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace tainicom.Aether.Content.Pipeline
+{
+    [ContentImporter(".tmx", DisplayName = "Tilemap Importer - Aether", DefaultProcessor = "TilemapProcessor")]
+    public class TilemapImporter : ContentImporter<TilemapContent>
+    {
+        public override TilemapContent Import(string filename, ContentImporterContext context)
+        {
+            TilemapContent output;
+
+            if (Path.GetExtension(filename) == ".tmx")
+                output = ImportTMX(filename, context);
+            else
+                throw new InvalidContentException("File type not supported");
+
+            TilemapContent.PackTiles(output, output.TileWidth, output.TileHeight);
+            TilemapContent.RenderAtlas(output);
+            TilemapContent.RenderMap(output);
+
+            return output;
+        }
+        
+        private static TilemapContent ImportTMX(string filename, ContentImporterContext context)
+        {
+            TilemapContent output = new TilemapContent();
+            output.Identity = new ContentIdentity(filename);
+
+            XmlDocument xmlDoc = new XmlDocument();
+            xmlDoc.Load(filename);
+
+            var map = xmlDoc.DocumentElement;
+            var orientation = map.GetAttribute("orientation");
+            if (orientation != "orthogonal")
+                throw new InvalidContentException("Invalid orientation. Only 'orthogonal' is supported for atlases.");
+            output.Renderorder = map.GetAttribute("renderorder");
+            output.MapColumns = map.GetAttributeAsInt("width").Value;
+            output.MapRows = map.GetAttributeAsInt("height").Value;
+            output.TileWidth = map.GetAttributeAsInt("tilewidth").Value;
+            output.TileHeight = map.GetAttributeAsInt("tileheight").Value;
+            output.Width = output.MapColumns * output.TileWidth;
+            output.Height = output.MapRows * output.TileHeight;
+
+            XmlNode tilesetNode = map["tileset"];
+            output.Firstgid = tilesetNode.GetAttributeAsInt("firstgid").Value;
+
+            if (tilesetNode.Attributes["source"] != null)
+            {
+                var tsxFilename = tilesetNode.Attributes["source"].Value;
+                var baseDirectory = Path.GetDirectoryName(filename);
+                tsxFilename = Path.Combine(baseDirectory, tsxFilename);
+                output.Tileset = ImportTSX(tsxFilename, context);
+                context.AddDependency(tsxFilename);
+            }
+            else
+            {
+                var rootDirectory = Path.GetDirectoryName(filename);
+                output.Tileset = ImportTileset(tilesetNode, context, rootDirectory);
+            }
+
+            XmlNode layerNode = map["layer"];
+            var layerColumns = Convert.ToInt32(map.Attributes["width"].Value, CultureInfo.InvariantCulture);
+            var layerRows = Convert.ToInt32(map.Attributes["height"].Value, CultureInfo.InvariantCulture);
+            output.LayerColumns = layerColumns;
+            output.LayerRows = layerRows;
+
+            XmlNode layerDataNode = layerNode["data"];
+            var encoding = layerDataNode.Attributes["encoding"].Value;
+            if (encoding != "csv")
+                throw new InvalidContentException("Invalid encoding. Only 'csv' is supported for data.");
+            var data = layerDataNode.InnerText;
+            var dataStringList = data.Split(',');
+            var mapData = new int[dataStringList.Length];
+            for (int i = 0; i < dataStringList.Length; i++)
+                mapData[i] = Convert.ToInt32(dataStringList[i].Trim(), CultureInfo.InvariantCulture);
+            output.MapData = mapData;
+
+            return output;
+        }
+
+        private static TilesetContent ImportTSX(string tsxFilename, ContentImporterContext context)
+        {
+            XmlDocument xmlDoc = new XmlDocument();
+            xmlDoc.Load(tsxFilename);
+            XmlNode tileset = xmlDoc.DocumentElement;
+            var baseDirectory = Path.GetDirectoryName(tsxFilename);
+            return ImportTileset(tileset, context, baseDirectory);
+        }
+
+        private static TilesetContent ImportTileset(XmlNode tilesetNode, ContentImporterContext context, string baseDirectory)
+        {
+            TilesetContent tileset = new TilesetContent();
+
+            if (tilesetNode["tileoffset"] != null)
+                throw new InvalidContentException("tileoffset is not supported.");
+
+            tileset.TileWidth = tilesetNode.GetAttributeAsInt("tilewidth").Value;
+            tileset.tileHeight = tilesetNode.GetAttributeAsInt("tileheight").Value;
+
+            BitmapContent bm = new PixelBitmapContent<Color>(tileset.TileWidth, tileset.tileHeight);
+            Texture2DContent mt = new Texture2DContent();
+            mt.Faces[0].Add(bm);
+            mt.Name = "null";            
+            var nullTile = new TileContent();
+            nullTile.Tileset = tileset;
+            nullTile.Id = -1;
+            nullTile.SrcTexture = mt;
+            nullTile.SrcBounds.Location = Point.Zero;
+            nullTile.SrcBounds.Width = mt.Mipmaps[0].Width;
+            nullTile.SrcBounds.Height = mt.Mipmaps[0].Height;
+            nullTile.DstBounds.Location = Point.Zero;
+            nullTile.DstBounds.Width = tileset.TileWidth;
+            nullTile.DstBounds.Height = tileset.tileHeight;
+
+            tileset.SourceTiles.Add(nullTile);
+
+            foreach (XmlNode tileNode in tilesetNode.ChildNodes)
+            {
+                if (tileNode.Name != "tile") continue;
+                var tileId = tileNode.GetAttributeAsInt("id").Value;
+
+                XmlNode imageNode = tileNode["image"];
+                
+                //var format = GetAttribute(imageNode, "format");
+                var imageSource = imageNode.GetAttribute("source");
+                var fullImageSource = Path.Combine(baseDirectory, imageSource);
+                TextureImporter txImporter = new TextureImporter();
+                var textureContent = (Texture2DContent)txImporter.Import(fullImageSource, context);
+                textureContent.Name = Path.GetFileNameWithoutExtension(fullImageSource);
+
+                var source = new TileContent();
+                source.Tileset = tileset;
+                source.Id = tileId;
+                source.SrcTexture = textureContent;
+                source.SrcBounds.Location = Point.Zero;
+                source.SrcBounds.Width  = textureContent.Mipmaps[0].Width;
+                source.SrcBounds.Height = textureContent.Mipmaps[0].Height;
+                source.DstBounds.Location = Point.Zero;
+                source.DstBounds.Width = textureContent.Mipmaps[0].Width;
+                source.DstBounds.Height = textureContent.Mipmaps[0].Height;
+
+
+                var transKeyColor = imageNode.GetAttributeAsColor("trans");
+                if (transKeyColor != null)
+                    foreach (var mips in textureContent.Faces)
+                        foreach (var mip in mips)
+                            ((PixelBitmapContent<Color>)mip).ReplaceColor(transKeyColor.Value, Color.Transparent);
+
+                if (tileId != tileset.SourceTiles.Count-1)
+                    throw new InvalidContentException("Invalid id");
+                tileset.SourceTiles.Add(source);
+            }
+
+            return tileset;
+        }
+        
+    }
+
+}

+ 27 - 0
Samples.WINDOWS.MG.sln

@@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Graphics.WINDOWS.MG"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnimationImporters.WINDOWS.MG", "Content.Pipeline\AnimationImporters\AnimationImporters.WINDOWS.MG.csproj", "{D9A47306-DEE0-4410-BC2C-BA8FFCE682A3}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TilemapImporter.WINDOWS.MG", "Content.Pipeline\TilemapImporters\TilemapImporter.WINDOWS.MG.csproj", "{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89}"
+EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Animation.WINDOWS.MG", "Animation\Aether.Animation.WINDOWS.MG.csproj", "{F08D6D4C-60FB-4543-8D81-594080EB8051}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aether.Shaders", "Aether.Shaders", "{13D47E11-4A7C-49C8-942E-2543E9C0098A}"
@@ -23,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Shaders.FXAA.WINDOWS
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Shaders.Deferred.WINDOWS.MG", "Shaders\Deferred\Aether.Shaders.Deferred.WINDOWS.MG.csproj", "{96105100-20DB-4187-9BCA-0A20AC9F1298}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Shaders.Tilemap.WINDOWS.MG", "Shaders\Tilemap\Aether.Shaders.Tilemap.WINDOWS.MG.csproj", "{6721CD07-0880-4154-85AF-EBE6043EF178}"
+EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtlasImporter.WINDOWS.MG", "Content.Pipeline\AtlasImporter\AtlasImporter.WINDOWS.MG.csproj", "{E710FBEA-8C75-405D-B6B4-CFC82CB48FB5}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Atlas.WINDOWS.MG", "Atlas\Aether.Atlas.WINDOWS.MG.csproj", "{C7522B5E-B255-4FB6-A17A-9E6D217AEBD2}"
@@ -49,6 +53,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.SLMC.WINDOWS.MG", "
 		{C9FA924B-DA91-4641-8B7D-CBC09E5560AE} = {C9FA924B-DA91-4641-8B7D-CBC09E5560AE}
 	EndProjectSection
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.Tilemap.WINDOWS.MG", "Samples\Tilemap\Samples.Tilemap.WINDOWS.MG.csproj", "{9B99798B-72F3-4E4B-9B99-DE15F7C955E9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Tilemap.WINDOWS.MG", "Tilemap\Aether.Tilemap.WINDOWS.MG.csproj", "{7BD9F460-F3F9-40A0-B298-066EA975C06C}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -75,6 +83,10 @@ Global
 		{D9A47306-DEE0-4410-BC2C-BA8FFCE682A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{D9A47306-DEE0-4410-BC2C-BA8FFCE682A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{D9A47306-DEE0-4410-BC2C-BA8FFCE682A3}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89}.Release|Any CPU.Build.0 = Release|Any CPU
 		{F08D6D4C-60FB-4543-8D81-594080EB8051}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{F08D6D4C-60FB-4543-8D81-594080EB8051}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{F08D6D4C-60FB-4543-8D81-594080EB8051}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -87,6 +99,10 @@ Global
 		{96105100-20DB-4187-9BCA-0A20AC9F1298}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{96105100-20DB-4187-9BCA-0A20AC9F1298}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{96105100-20DB-4187-9BCA-0A20AC9F1298}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6721CD07-0880-4154-85AF-EBE6043EF178}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6721CD07-0880-4154-85AF-EBE6043EF178}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6721CD07-0880-4154-85AF-EBE6043EF178}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6721CD07-0880-4154-85AF-EBE6043EF178}.Release|Any CPU.Build.0 = Release|Any CPU
 		{E710FBEA-8C75-405D-B6B4-CFC82CB48FB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{E710FBEA-8C75-405D-B6B4-CFC82CB48FB5}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{E710FBEA-8C75-405D-B6B4-CFC82CB48FB5}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -123,6 +139,14 @@ Global
 		{DC1BD888-3D11-4557-AC59-8271DC991ED1}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{DC1BD888-3D11-4557-AC59-8271DC991ED1}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{DC1BD888-3D11-4557-AC59-8271DC991ED1}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9B99798B-72F3-4E4B-9B99-DE15F7C955E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9B99798B-72F3-4E4B-9B99-DE15F7C955E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9B99798B-72F3-4E4B-9B99-DE15F7C955E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9B99798B-72F3-4E4B-9B99-DE15F7C955E9}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7BD9F460-F3F9-40A0-B298-066EA975C06C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7BD9F460-F3F9-40A0-B298-066EA975C06C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7BD9F460-F3F9-40A0-B298-066EA975C06C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7BD9F460-F3F9-40A0-B298-066EA975C06C}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -132,8 +156,10 @@ Global
 		{48E4029A-115C-4DC2-AF3A-0AB94F36BFC0} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
 		{400DC7B2-739D-4156-916D-2F2E1920310D} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
 		{D9A47306-DEE0-4410-BC2C-BA8FFCE682A3} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
+		{9B0F9C6B-3C43-472D-B0C1-91E11A9FDE89} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
 		{FBBDE2BA-F9F3-4041-8584-2C912C235E26} = {13D47E11-4A7C-49C8-942E-2543E9C0098A}
 		{96105100-20DB-4187-9BCA-0A20AC9F1298} = {13D47E11-4A7C-49C8-942E-2543E9C0098A}
+		{6721CD07-0880-4154-85AF-EBE6043EF178} = {13D47E11-4A7C-49C8-942E-2543E9C0098A}
 		{E710FBEA-8C75-405D-B6B4-CFC82CB48FB5} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
 		{08296A2E-BECF-4DB9-A518-8A613D07B80E} = {13D47E11-4A7C-49C8-942E-2543E9C0098A}
 		{53EA1090-FEE4-4F16-96C2-57DD6B75FA75} = {584E505E-859A-4DA8-9235-EE6C61C03479}
@@ -142,5 +168,6 @@ Global
 		{E605A7E1-EAE9-4EBF-941D-D88A6C1DC568} = {584E505E-859A-4DA8-9235-EE6C61C03479}
 		{C9FA924B-DA91-4641-8B7D-CBC09E5560AE} = {A921886B-C6F7-4FF8-8668-EC20004C464A}
 		{DC1BD888-3D11-4557-AC59-8271DC991ED1} = {584E505E-859A-4DA8-9235-EE6C61C03479}
+		{9B99798B-72F3-4E4B-9B99-DE15F7C955E9} = {584E505E-859A-4DA8-9235-EE6C61C03479}
 	EndGlobalSection
 EndGlobal

+ 32 - 0
Samples.WINDOWS.XNA.sln

@@ -9,6 +9,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aether.Content.Pipeline", "
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnimationImporters.WINDOWS.XNA", "Content.Pipeline\AnimationImporters\AnimationImporters.WINDOWS.XNA.csproj", "{E22F02E7-6799-4C14-B9B3-B461D6E9AB6E}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TilemapImporter.WINDOWS.XNA", "Content.Pipeline\TilemapImporters\TilemapImporter.WINDOWS.XNA.csproj", "{B5FAF2D1-7164-4956-9474-0F4B19706DE8}"
+EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Animation.WINDOWS.XNA", "Animation\Aether.Animation.WINDOWS.XNA.csproj", "{1BD2DBC0-D366-42F7-9369-F566CCD01C03}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aether.Shaders", "Aether.Shaders", "{EFC7A27C-C20B-4BE7-8B3A-2B109991D704}"
@@ -17,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Shaders.FXAA.WINDOWS
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Shaders.Deferred.WINDOWS.XNA", "Shaders\Deferred\Aether.Shaders.Deferred.WINDOWS.XNA.csproj", "{B82B862D-C728-4A10-8A56-65D688E022C8}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Shaders.Tilemap.WINDOWS.XNA", "Shaders\Tilemap\Aether.Shaders.Tilemap.WINDOWS.XNA.csproj", "{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}"
+EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtlasImporter.WINDOWS.XNA", "Content.Pipeline\AtlasImporter\AtlasImporter.WINDOWS.XNA.csproj", "{90E6017D-198B-4470-BF9B-8B8791C295CC}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Atlas.WINDOWS.XNA", "Atlas\Aether.Atlas.WINDOWS.XNA.csproj", "{644629BB-1A4E-4932-8E50-4F93BC938794}"
@@ -47,6 +51,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.SLMC.WINDOWS.XNA",
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.SLMCContent", "Samples\SLMCContent\Samples.SLMCContent.contentproj", "{99A5C5A9-03E3-41C6-A121-EF423F6CEB23}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.Tilemap.WINDOWS.XNA", "Samples\Tilemap\Samples.Tilemap.WINDOWS.XNA.csproj", "{0586BA88-7CBC-493B-9B9F-B1AE7089ECDC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.TilemapContent", "Samples\TilemapContent\Samples.TilemapContent.contentproj", "{CC95FC77-31DC-4120-895C-4764ED092F4E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aether.Tilemap.WINDOWS.XNA", "Tilemap\Aether.Tilemap.WINDOWS.XNA.csproj", "{746551BA-FB64-43B0-8479-0506B930D3E5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x86 = Debug|x86
@@ -65,6 +75,10 @@ Global
 		{E22F02E7-6799-4C14-B9B3-B461D6E9AB6E}.Debug|x86.Build.0 = Debug|x86
 		{E22F02E7-6799-4C14-B9B3-B461D6E9AB6E}.Release|x86.ActiveCfg = Release|x86
 		{E22F02E7-6799-4C14-B9B3-B461D6E9AB6E}.Release|x86.Build.0 = Release|x86
+		{B5FAF2D1-7164-4956-9474-0F4B19706DE8}.Debug|x86.ActiveCfg = Debug|x86
+		{B5FAF2D1-7164-4956-9474-0F4B19706DE8}.Debug|x86.Build.0 = Debug|x86
+		{B5FAF2D1-7164-4956-9474-0F4B19706DE8}.Release|x86.ActiveCfg = Release|x86
+		{B5FAF2D1-7164-4956-9474-0F4B19706DE8}.Release|x86.Build.0 = Release|x86
 		{1BD2DBC0-D366-42F7-9369-F566CCD01C03}.Debug|x86.ActiveCfg = Debug|x86
 		{1BD2DBC0-D366-42F7-9369-F566CCD01C03}.Debug|x86.Build.0 = Debug|x86
 		{1BD2DBC0-D366-42F7-9369-F566CCD01C03}.Release|x86.ActiveCfg = Release|x86
@@ -77,6 +91,10 @@ Global
 		{B82B862D-C728-4A10-8A56-65D688E022C8}.Debug|x86.Build.0 = Debug|x86
 		{B82B862D-C728-4A10-8A56-65D688E022C8}.Release|x86.ActiveCfg = Release|x86
 		{B82B862D-C728-4A10-8A56-65D688E022C8}.Release|x86.Build.0 = Release|x86
+		{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}.Debug|x86.ActiveCfg = Debug|x86
+		{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}.Debug|x86.Build.0 = Debug|x86
+		{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}.Release|x86.ActiveCfg = Release|x86
+		{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}.Release|x86.Build.0 = Release|x86
 		{90E6017D-198B-4470-BF9B-8B8791C295CC}.Debug|x86.ActiveCfg = Debug|x86
 		{90E6017D-198B-4470-BF9B-8B8791C295CC}.Debug|x86.Build.0 = Debug|x86
 		{90E6017D-198B-4470-BF9B-8B8791C295CC}.Release|x86.ActiveCfg = Release|x86
@@ -123,6 +141,16 @@ Global
 		{47F16ADE-1B63-476A-81AB-454C46ABD974}.Release|x86.Build.0 = Release|x86
 		{99A5C5A9-03E3-41C6-A121-EF423F6CEB23}.Debug|x86.ActiveCfg = Debug|x86
 		{99A5C5A9-03E3-41C6-A121-EF423F6CEB23}.Release|x86.ActiveCfg = Release|x86
+		{0586BA88-7CBC-493B-9B9F-B1AE7089ECDC}.Debug|x86.ActiveCfg = Debug|x86
+		{0586BA88-7CBC-493B-9B9F-B1AE7089ECDC}.Debug|x86.Build.0 = Debug|x86
+		{0586BA88-7CBC-493B-9B9F-B1AE7089ECDC}.Release|x86.ActiveCfg = Release|x86
+		{0586BA88-7CBC-493B-9B9F-B1AE7089ECDC}.Release|x86.Build.0 = Release|x86
+		{CC95FC77-31DC-4120-895C-4764ED092F4E}.Debug|x86.ActiveCfg = Debug|x86
+		{CC95FC77-31DC-4120-895C-4764ED092F4E}.Release|x86.ActiveCfg = Release|x86
+		{746551BA-FB64-43B0-8479-0506B930D3E5}.Debug|x86.ActiveCfg = Debug|x86
+		{746551BA-FB64-43B0-8479-0506B930D3E5}.Debug|x86.Build.0 = Debug|x86
+		{746551BA-FB64-43B0-8479-0506B930D3E5}.Release|x86.ActiveCfg = Release|x86
+		{746551BA-FB64-43B0-8479-0506B930D3E5}.Release|x86.Build.0 = Release|x86
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -130,10 +158,12 @@ Global
 	GlobalSection(NestedProjects) = preSolution
 		{565ACF47-7E36-435E-8727-2AFB39E61134} = {F6B6E505-6037-49E4-9060-8B29A7B99BC1}
 		{E22F02E7-6799-4C14-B9B3-B461D6E9AB6E} = {F6B6E505-6037-49E4-9060-8B29A7B99BC1}
+		{B5FAF2D1-7164-4956-9474-0F4B19706DE8} = {F6B6E505-6037-49E4-9060-8B29A7B99BC1}
 		{90E6017D-198B-4470-BF9B-8B8791C295CC} = {F6B6E505-6037-49E4-9060-8B29A7B99BC1}
 		{497B1130-EB63-4057-BC40-F60D6FFA50CA} = {F6B6E505-6037-49E4-9060-8B29A7B99BC1}
 		{89E0198E-7298-411A-B5C1-61F2754A3F80} = {EFC7A27C-C20B-4BE7-8B3A-2B109991D704}
 		{B82B862D-C728-4A10-8A56-65D688E022C8} = {EFC7A27C-C20B-4BE7-8B3A-2B109991D704}
+		{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6} = {EFC7A27C-C20B-4BE7-8B3A-2B109991D704}
 		{908EC8FF-B0ED-478A-B486-702E578AF64A} = {EFC7A27C-C20B-4BE7-8B3A-2B109991D704}
 		{8C971E86-ACD6-45E6-A4C2-7E0C556152FE} = {45FFB286-52CE-4EB7-8ACE-8B6FBD18D616}
 		{B02966BF-88BC-41B2-80EF-8F8C5F98FA98} = {45FFB286-52CE-4EB7-8ACE-8B6FBD18D616}
@@ -145,5 +175,7 @@ Global
 		{4CA82D0E-2414-460C-9A70-2DA3C3AF96B6} = {45FFB286-52CE-4EB7-8ACE-8B6FBD18D616}
 		{47F16ADE-1B63-476A-81AB-454C46ABD974} = {45FFB286-52CE-4EB7-8ACE-8B6FBD18D616}
 		{99A5C5A9-03E3-41C6-A121-EF423F6CEB23} = {45FFB286-52CE-4EB7-8ACE-8B6FBD18D616}
+		{0586BA88-7CBC-493B-9B9F-B1AE7089ECDC} = {45FFB286-52CE-4EB7-8ACE-8B6FBD18D616}
+		{CC95FC77-31DC-4120-895C-4764ED092F4E} = {45FFB286-52CE-4EB7-8ACE-8B6FBD18D616}
 	EndGlobalSection
 EndGlobal

BIN
Samples/Tilemap/Game.ico


+ 153 - 0
Samples/Tilemap/Game1.cs

@@ -0,0 +1,153 @@
+using System;
+using System.IO;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using tainicom.Aether.Graphics;
+using tainicom.Aether.Shaders;
+
+namespace Samples.Atlas
+{
+    public class Game1 : Microsoft.Xna.Framework.Game
+    {
+        GraphicsDeviceManager graphics;
+        SpriteBatch spriteBatch;
+        SpriteFont font;
+
+        KeyboardState previousKeyboardState;
+
+        int mipLevel = 4;
+        bool showAtlas = false;
+        bool useGenerateBitmap = true;
+        bool useMipmapPerSprite = true;
+        Rectangle atlasSize = new Rectangle(0, 0, 1024, 512);
+        RenderTarget2D rt;
+
+
+        Tilemap tilemapMipmapPerSprite;
+        Tilemap tilemapMipmap;
+        Tilemap tilemapNoMipmap;
+        
+        public Game1()
+        {
+            graphics = new GraphicsDeviceManager(this);
+            Content.RootDirectory = "Content";
+
+            graphics.PreferredBackBufferWidth = atlasSize.Width;
+            graphics.PreferredBackBufferHeight = atlasSize.Height;
+        }
+
+        protected override void LoadContent()
+        {
+            spriteBatch = new SpriteBatch(GraphicsDevice);
+            font = Content.Load<SpriteFont>("font");
+
+            rt = new RenderTarget2D(GraphicsDevice, atlasSize.Width, atlasSize.Height);
+
+            // Load tilemap
+            tilemapMipmapPerSprite = Content.Load<Tilemap>("tilemapMipmapPerSprite");
+            tilemapMipmap = Content.Load<Tilemap>("tilemapMipmap");
+            tilemapNoMipmap = Content.Load<Tilemap>("tilemapNoMipmap");
+
+#if DEBUG
+            using (var fs = File.Create("tilemapNoMipmapAtlas.png"))
+                tilemapNoMipmap.TextureAtlas.SaveAsPng(fs, tilemapNoMipmap.TextureAtlas.Width, tilemapNoMipmap.TextureAtlas.Height);
+            using (var fs = File.Create("tilemapMipmapAtlas.png"))
+                tilemapNoMipmap.TextureAtlas.SaveAsPng(fs, tilemapMipmap.TextureAtlas.Width, tilemapNoMipmap.TextureAtlas.Height);
+#endif
+            
+        }
+        
+        /// <param name="gameTime">Provides a snapshot of timing values.</param>
+        protected override void Update(GameTime gameTime)
+        {
+            KeyboardState keyState = Keyboard.GetState();
+            GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);
+
+            if (keyState.IsKeyDown(Keys.Escape) || gamePadState.Buttons.Back == ButtonState.Pressed)
+                Exit();
+
+            if (keyState.IsKeyDown(Keys.F1) && !previousKeyboardState.IsKeyDown(Keys.F1))
+                useMipmapPerSprite = !useMipmapPerSprite;
+            if (keyState.IsKeyDown(Keys.F2) && !previousKeyboardState.IsKeyDown(Keys.F2))
+                useGenerateBitmap = !useGenerateBitmap;
+            if (keyState.IsKeyDown(Keys.F3) && !previousKeyboardState.IsKeyDown(Keys.F3))
+                showAtlas = !showAtlas;
+            if (keyState.IsKeyDown(Keys.OemPlus) && !previousKeyboardState.IsKeyDown(Keys.OemPlus) && mipLevel < 10)
+                mipLevel++;
+            if (keyState.IsKeyDown(Keys.Add) && !previousKeyboardState.IsKeyDown(Keys.Add) && mipLevel < 10)
+                mipLevel++;
+            if (keyState.IsKeyDown(Keys.OemMinus) && !previousKeyboardState.IsKeyDown(Keys.OemMinus) && mipLevel > 0)
+                mipLevel--;
+            if (keyState.IsKeyDown(Keys.Subtract) && !previousKeyboardState.IsKeyDown(Keys.Subtract) && mipLevel > 0)
+                mipLevel--;
+            
+            previousKeyboardState = keyState;
+            base.Update(gameTime);
+        }
+        
+        protected override void Draw(GameTime gameTime)
+        {
+            GraphicsDevice.BlendState = BlendState.Opaque;
+            GraphicsDevice.DepthStencilState = DepthStencilState.Default;
+
+            GraphicsDevice.SetRenderTarget(rt);
+            GraphicsDevice.Clear(Color.Black);
+
+            var currentTilemap = (useGenerateBitmap) 
+                ? (useMipmapPerSprite ? tilemapMipmapPerSprite : tilemapMipmap)
+                : (tilemapNoMipmap);
+
+            int mipLevel2 = (int)Math.Pow(2, mipLevel);
+            var mipSize = atlasSize;
+            mipSize.Width /= mipLevel2;
+            mipSize.Height /= mipLevel2;
+            
+
+            if (showAtlas)
+            {
+                spriteBatch.Begin();
+                spriteBatch.Draw(currentTilemap.TextureAtlas, mipSize, Color.White);
+                spriteBatch.End();
+            }
+            else
+            {
+                DrawTilemap(gameTime, currentTilemap, mipSize);
+            }
+
+
+            GraphicsDevice.SetRenderTarget(null);
+            GraphicsDevice.Clear(Color.CornflowerBlue);
+            spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointWrap, null, null);
+            spriteBatch.Draw(rt, atlasSize, mipSize, Color.White);
+            spriteBatch.End();
+
+            spriteBatch.Begin();
+            spriteBatch.DrawString(font, String.Format("[F1] MipmapPerSprite - ({0})", useMipmapPerSprite ? "ON" : "OFF"), new Vector2(20, 20), Color.White);
+            spriteBatch.DrawString(font, String.Format("[F2] GenerateMipmap - ({0})", useGenerateBitmap ? "ON" : "OFF"), new Vector2(20, 40), Color.White);
+            spriteBatch.DrawString(font, String.Format("[F3] {0}", showAtlas? "Show Tilemap" : "Show Atlas"), new Vector2(20, 60), Color.White);
+            spriteBatch.DrawString(font, String.Format("[+/-] MipLevel - ({0})", mipLevel), new Vector2(20, 80), Color.White);
+            spriteBatch.End();
+
+            base.Draw(gameTime);
+        }
+
+        private void DrawTilemap(GameTime gameTime, Tilemap tilemap, Rectangle mipSize)
+        {
+            // setup tilemapEffect
+            var viewport = GraphicsDevice.Viewport;
+            Matrix projection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, 1);
+            Matrix halfPixelOffset = Matrix.Identity;
+#if XNA
+            halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);
+#endif
+
+            tilemap.Effect.Projection = halfPixelOffset * projection;
+                        
+            // Draw tilemap
+            spriteBatch.Begin(0, BlendState.AlphaBlend, SamplerState.PointWrap, null, null, tilemap.Effect);
+            spriteBatch.Draw(tilemap.TextureMap, mipSize, Color.White);
+            spriteBatch.End();
+        }
+    }
+}

BIN
Samples/Tilemap/GameThumbnail.png


BIN
Samples/Tilemap/Icon.ico


+ 19 - 0
Samples/Tilemap/Program.cs

@@ -0,0 +1,19 @@
+using System;
+
+namespace Samples.Atlas
+{
+#if WINDOWS || XBOX || LINUX
+    public static class Program
+    {
+        [STAThread]
+        static void Main(string[] args)
+        {
+            using (Game1 game = new Game1())
+            {
+                game.Run();
+            }
+        }
+    }
+#endif
+}
+

+ 34 - 0
Samples/Tilemap/Properties/AssemblyInfo.cs

@@ -0,0 +1,34 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Samples.Tilemap")]
+[assembly: AssemblyProduct("Samples.Tilemap")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyCopyright("Copyright © Kastellanos Nikolaos 2019-2021")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type. Only Windows
+// assemblies support COM.
+[assembly: ComVisible(false)]
+
+// On Windows, the following GUID is for the ID of the typelib if this
+// project is exposed to COM. On other platforms, it unique identifies the
+// title storage container when deploying this assembly to the device.
+[assembly: Guid("F24DB7E5-82FB-497D-A9D7-9C1B22AB9728")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]

+ 89 - 0
Samples/Tilemap/Samples.Tilemap.WINDOWS.MG.csproj

@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{9B99798B-72F3-4E4B-9B99-DE15F7C955E9}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Samples.Tilemap</RootNamespace>
+    <AssemblyName>Samples.Tilemap</AssemblyName>
+    <FileAlignment>512</FileAlignment>
+    <MonoGamePlatform>Windows</MonoGamePlatform>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <TargetFrameworkProfile />
+  </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationIcon>Icon.ico</ApplicationIcon>
+  </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>TRACE;DEBUG;WINDOWS MG</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>true</Prefer32Bit>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE;WINDOWS MG</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="Game1.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="MonoGame.Framework">
+      <HintPath>$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\Windows\MonoGame.Framework.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Icon.ico" />
+  </ItemGroup>
+  <ItemGroup>
+    <MonoGameContentReference Include="..\TilemapContent\Samples.TilemapContent.mgcb">
+      <Link>Content\Content.mgcb</Link>
+    </MonoGameContentReference>
+    <None Include="app.config" />
+    <None Include="app.manifest" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Atlas\Aether.Atlas.WINDOWS.MG.csproj">
+      <Project>{c7522b5e-b255-4fb6-a17a-9e6d217aebd2}</Project>
+      <Name>Aether.Atlas.WINDOWS.MG</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Shaders\Tilemap\Aether.Shaders.Tilemap.WINDOWS.MG.csproj">
+      <Project>{6721cd07-0880-4154-85af-ebe6043ef178}</Project>
+      <Name>Aether.Shaders.Tilemap.WINDOWS.MG</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Tilemap\Aether.Tilemap.WINDOWS.MG.csproj">
+      <Project>{7bd9f460-f3f9-40a0-b298-066ea975c06c}</Project>
+      <Name>Aether.Tilemap.WINDOWS.MG</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 137 - 0
Samples/Tilemap/Samples.Tilemap.WINDOWS.XNA.csproj

@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <ProjectGuid>{0586BA88-7CBC-493B-9B9F-B1AE7089ECDC}</ProjectGuid>
+    <ProjectTypeGuids>{6D335F3A-9D43-41b4-9D22-F6F17C4BE596};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <OutputType>WinExe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Samples.Tilemap</RootNamespace>
+    <AssemblyName>Samples.Tilemap</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <TargetFrameworkProfile>Client</TargetFrameworkProfile>
+    <XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
+    <XnaPlatform>Windows</XnaPlatform>
+    <XnaProfile>Reach</XnaProfile>
+    <XnaCrossPlatformGroupID>FD62A179-B0DF-4ED3-898B-F1081CB0D364</XnaCrossPlatformGroupID>
+    <XnaOutputType>Game</XnaOutputType>
+    <ApplicationIcon>Game.ico</ApplicationIcon>
+    <Thumbnail>GameThumbnail.png</Thumbnail>
+    <PublishUrl>publish\</PublishUrl>
+    <Install>true</Install>
+    <InstallFrom>Disk</InstallFrom>
+    <UpdateEnabled>false</UpdateEnabled>
+    <UpdateMode>Foreground</UpdateMode>
+    <UpdateInterval>7</UpdateInterval>
+    <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+    <UpdatePeriodically>false</UpdatePeriodically>
+    <UpdateRequired>false</UpdateRequired>
+    <MapFileExtensions>true</MapFileExtensions>
+    <ApplicationRevision>0</ApplicationRevision>
+    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+    <IsWebBootstrapper>false</IsWebBootstrapper>
+    <UseApplicationTrust>false</UseApplicationTrust>
+    <BootstrapperEnabled>true</BootstrapperEnabled>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\Windows\XNA\</OutputPath>
+    <DefineConstants>TRACE;DEBUG;WINDOWS XNA</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <NoStdLib>true</NoStdLib>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+    <PlatformTarget>x86</PlatformTarget>
+    <XnaCompressContent>false</XnaCompressContent>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\Windows\XNA\</OutputPath>
+    <DefineConstants>TRACE;WINDOWS XNA</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <NoStdLib>true</NoStdLib>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+    <PlatformTarget>x86</PlatformTarget>
+    <XnaCompressContent>true</XnaCompressContent>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
+    <Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
+    <Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
+    <Reference Include="mscorlib" />
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Game1.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Game.ico" />
+    <Content Include="GameThumbnail.png">
+      <XnaPlatformSpecific>true</XnaPlatformSpecific>
+    </Content>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Atlas\Aether.Atlas.WINDOWS.XNA.csproj">
+      <Project>{644629BB-1A4E-4932-8E50-4F93BC938794}</Project>
+      <Name>Aether.Atlas.WINDOWS.XNA</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Shaders\Tilemap\Aether.Shaders.Tilemap.WINDOWS.XNA.csproj">
+      <Project>{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}</Project>
+      <Name>Aether.Shaders.Tilemap.WINDOWS.XNA</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Tilemap\Aether.Tilemap.WINDOWS.XNA.csproj">
+      <Project>{746551BA-FB64-43B0-8479-0506B930D3E5}</Project>
+      <Name>Aether.Tilemap.WINDOWS.XNA</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\TilemapContent\Samples.TilemapContent.contentproj">
+      <Project>{CC95FC77-31DC-4120-895C-4764ED092F4E}</Project>
+      <Name>Samples.TilemapContent %28Content%29</Name>
+      <XnaReferenceType>Content</XnaReferenceType>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
+      <Visible>False</Visible>
+      <ProductName>Microsoft .NET Framework 4 Client Profile %28x86 and x64%29</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+      <Install>false</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 3.5 SP1</ProductName>
+      <Install>false</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+      <Visible>False</Visible>
+      <ProductName>Windows Installer 3.1</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Xna.Framework.4.0">
+      <Visible>False</Visible>
+      <ProductName>Microsoft XNA Framework Redistributable 4.0</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.targets" />
+  <!--
+      To modify your build process, add your task inside one of the targets below and uncomment it. 
+      Other similar extension points exist, see Microsoft.Common.targets.
+      <Target Name="BeforeBuild">
+      </Target>
+      <Target Name="AfterBuild">
+      </Target>
+    -->
+</Project>

+ 3 - 0
Samples/Tilemap/app.config

@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>

+ 42 - 0
Samples/Tilemap/app.manifest

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <assemblyIdentity version="1.0.0.0" name="Graphics"/>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+    <security>
+      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- A list of the Windows versions that this application has been tested on and is
+           is designed to work with. Uncomment the appropriate elements and Windows will 
+           automatically selected the most compatible environment. -->
+
+      <!-- Windows Vista -->
+      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
+
+      <!-- Windows 7 -->
+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+
+      <!-- Windows 8 -->
+      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+
+      <!-- Windows 8.1 -->
+      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+
+    </application>
+  </compatibility>
+
+  <application xmlns="urn:schemas-microsoft-com:asm.v3">
+    <windowsSettings>
+      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
+    </windowsSettings>
+  </application>
+
+</assembly>

BIN
Samples/TilemapContent/Object/Bush_1.png


BIN
Samples/TilemapContent/Object/Bush_2.png


BIN
Samples/TilemapContent/Object/Bush_3.png


BIN
Samples/TilemapContent/Object/Bush_4.png


BIN
Samples/TilemapContent/Object/Crate.png


BIN
Samples/TilemapContent/Object/Mushroom_1.png


BIN
Samples/TilemapContent/Object/Mushroom_2.png


BIN
Samples/TilemapContent/Object/Sign_1.png


BIN
Samples/TilemapContent/Object/Sign_2.png


BIN
Samples/TilemapContent/Object/Stone.png


BIN
Samples/TilemapContent/Object/Tree_1.png


BIN
Samples/TilemapContent/Object/Tree_2.png


BIN
Samples/TilemapContent/Object/Tree_3.png


+ 93 - 0
Samples/TilemapContent/Samples.TilemapContent.contentproj

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <ProjectGuid>{CC95FC77-31DC-4120-895C-4764ED092F4E}</ProjectGuid>
+    <ProjectTypeGuids>{96E2B04D-8817-42c6-938A-82C39BA4D311};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
+    <OutputPath>bin\$(Platform)\$(Configuration)</OutputPath>
+    <ContentRootDirectory>Content</ContentRootDirectory>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <PlatformTarget>x86</PlatformTarget>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+    <PlatformTarget>x86</PlatformTarget>
+  </PropertyGroup>
+  <PropertyGroup>
+    <RootNamespace>Samples.AtlasContent</RootNamespace>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.EffectImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.FBXImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.TextureImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.XImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.AudioImporters, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.VideoImporters, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="font.spritefont">
+      <Name>font</Name>
+      <Importer>FontDescriptionImporter</Importer>
+      <Processor>FontDescriptionProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Content.Pipeline\AtlasImporter\AtlasImporter.WINDOWS.XNA.csproj">
+      <Project>{90E6017D-198B-4470-BF9B-8B8791C295CC}</Project>
+      <Name>AtlasImporter.WINDOWS.XNA</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Content.Pipeline\TilemapImporters\TilemapImporter.WINDOWS.XNA.csproj">
+      <Project>{B5FAF2D1-7164-4956-9474-0F4B19706DE8}</Project>
+      <Name>TilemapImporter.WINDOWS.XNA</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="tilemapMipmap.tmx">
+      <Name>tilemapMipmap</Name>
+      <Importer>TilemapImporter</Importer>
+      <Processor>TilemapProcessor</Processor>
+      <ProcessorParameters_ColorKeyEnabled>False</ProcessorParameters_ColorKeyEnabled>
+      <ProcessorParameters_GenerateMipmaps>True</ProcessorParameters_GenerateMipmaps>
+      <ProcessorParameters_MipmapsPerSprite>False</ProcessorParameters_MipmapsPerSprite>
+    </Compile>
+    <Compile Include="tilemapMipmapPerSprite.tmx">
+      <Name>tilemapMipmapPerSprite</Name>
+      <Importer>TilemapImporter</Importer>
+      <Processor>TilemapProcessor</Processor>
+      <ProcessorParameters_ColorKeyEnabled>False</ProcessorParameters_ColorKeyEnabled>
+      <ProcessorParameters_GenerateMipmaps>True</ProcessorParameters_GenerateMipmaps>
+    </Compile>
+    <Compile Include="tilemapNoMipmap.tmx">
+      <Name>tilemapNoMipmap</Name>
+      <Importer>TilemapImporter</Importer>
+      <Processor>TilemapProcessor</Processor>
+      <ProcessorParameters_ColorKeyEnabled>False</ProcessorParameters_ColorKeyEnabled>
+      <ProcessorParameters_MipmapsPerSprite>False</ProcessorParameters_MipmapsPerSprite>
+    </Compile>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\$(XnaFrameworkVersion)\Microsoft.Xna.GameStudio.ContentPipeline.targets" />
+  <!--  To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 62 - 0
Samples/TilemapContent/Samples.TilemapContent.mgcb

@@ -0,0 +1,62 @@
+
+#----------------------------- Global Properties ----------------------------#
+
+/outputDir:bin/$(Platform)
+/intermediateDir:obj/$(Platform)
+/platform:Windows
+/config:
+/profile:Reach
+/compress:False
+
+#-------------------------------- References --------------------------------#
+
+/reference:..\..\bin\Debug\Windows\Aether.Content.Pipeline.TilemapImporter.dll
+
+#---------------------------------- Content ---------------------------------#
+
+#begin font.spritefont
+/importer:FontDescriptionImporter
+/processor:FontDescriptionProcessor
+/processorParam:PremultiplyAlpha=True
+/processorParam:TextureFormat=Compressed
+/build:font.spritefont
+
+#begin tilemapMipmap.tmx
+/importer:TilemapImporter
+/processor:TilemapProcessor
+/processorParam:MipmapsPerSprite=False
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=False
+/processorParam:GenerateMipmaps=True
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:tilemapMipmap.tmx
+
+#begin tilemapMipmapPerSprite.tmx
+/importer:TilemapImporter
+/processor:TilemapProcessor
+/processorParam:MipmapsPerSprite=True
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=False
+/processorParam:GenerateMipmaps=True
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:tilemapMipmapPerSprite.tmx
+
+#begin tilemapNoMipmap.tmx
+/importer:TilemapImporter
+/processor:TilemapProcessor
+/processorParam:MipmapsPerSprite=False
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=False
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:tilemapNoMipmap.tmx
+

BIN
Samples/TilemapContent/Tiles/1.png


BIN
Samples/TilemapContent/Tiles/10.png


BIN
Samples/TilemapContent/Tiles/11.png


BIN
Samples/TilemapContent/Tiles/12.png


BIN
Samples/TilemapContent/Tiles/13.png


BIN
Samples/TilemapContent/Tiles/14.png


BIN
Samples/TilemapContent/Tiles/15.png


BIN
Samples/TilemapContent/Tiles/16.png


BIN
Samples/TilemapContent/Tiles/17.png


BIN
Samples/TilemapContent/Tiles/18.png


BIN
Samples/TilemapContent/Tiles/2.png


BIN
Samples/TilemapContent/Tiles/3.png


BIN
Samples/TilemapContent/Tiles/4.png


BIN
Samples/TilemapContent/Tiles/5.png


BIN
Samples/TilemapContent/Tiles/6.png


BIN
Samples/TilemapContent/Tiles/7.png


BIN
Samples/TilemapContent/Tiles/8.png


BIN
Samples/TilemapContent/Tiles/9.png


+ 60 - 0
Samples/TilemapContent/font.spritefont

@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+This file contains an xml description of a font, and will be read by the XNA
+Framework Content Pipeline. Follow the comments to customize the appearance
+of the font in your game, and to change the characters which are available to draw
+with.
+-->
+<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
+  <Asset Type="Graphics:FontDescription">
+
+    <!--
+    Modify this string to change the font that will be imported.
+    -->
+    <FontName>Segoe UI Mono</FontName>
+
+    <!--
+    Size is a float value, measured in points. Modify this value to change
+    the size of the font.
+    -->
+    <Size>14</Size>
+
+    <!--
+    Spacing is a float value, measured in pixels. Modify this value to change
+    the amount of spacing in between characters.
+    -->
+    <Spacing>0</Spacing>
+
+    <!--
+    UseKerning controls the layout of the font. If this value is true, kerning information
+    will be used when placing characters.
+    -->
+    <UseKerning>true</UseKerning>
+
+    <!--
+    Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
+    and "Bold, Italic", and are case sensitive.
+    -->
+    <Style>Regular</Style>
+
+    <!--
+    If you uncomment this line, the default character will be substituted if you draw
+    or measure text that contains characters which were not included in the font.
+    -->
+    <!-- <DefaultCharacter>*</DefaultCharacter> -->
+
+    <!--
+    CharacterRegions control what letters are available in the font. Every
+    character from Start to End will be built and made available for drawing. The
+    default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
+    character set. The characters are ordered according to the Unicode standard.
+    See the documentation for more information.
+    -->
+    <CharacterRegions>
+      <CharacterRegion>
+        <Start>&#32;</Start>
+        <End>&#126;</End>
+      </CharacterRegion>
+    </CharacterRegions>
+  </Asset>
+</XnaContent>

+ 1 - 0
Samples/TilemapContent/free-platformer-game-tileset.txt

@@ -0,0 +1 @@
+http://www.gameart2d.com/free-platformer-game-tileset.html

+ 10 - 0
Samples/TilemapContent/tilemapMipmap.tmx

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" renderorder="right-down" width="4" height="2" tilewidth="128" tileheight="128" nextobjectid="1">
+ <tileset firstgid="1" source="tileset.tsx"/>
+ <layer name="Tile Layer 1" width="4" height="2">
+  <data encoding="csv">
+29,24,25,0,
+1,2,2,18
+</data>
+ </layer>
+</map>

+ 10 - 0
Samples/TilemapContent/tilemapMipmapPerSprite.tmx

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" renderorder="right-down" width="4" height="2" tilewidth="128" tileheight="128" nextobjectid="1">
+ <tileset firstgid="1" source="tileset.tsx"/>
+ <layer name="Tile Layer 1" width="4" height="2">
+  <data encoding="csv">
+29,24,25,0,
+1,2,2,18
+</data>
+ </layer>
+</map>

+ 10 - 0
Samples/TilemapContent/tilemapNoMipmap.tmx

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" renderorder="right-down" width="4" height="2" tilewidth="128" tileheight="128" nextobjectid="1">
+ <tileset firstgid="1" source="tileset.tsx"/>
+ <layer name="Tile Layer 1" width="4" height="2">
+  <data encoding="csv">
+29,24,25,0,
+1,2,2,18
+</data>
+ </layer>
+</map>

+ 90 - 0
Samples/TilemapContent/tileset.tsx

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tileset name="1" tilewidth="133" tileheight="128" tilecount="29" columns="0">
+ <tile id="0">
+  <image width="128" height="128" source="Tiles/1.png"/>
+ </tile>
+ <tile id="1">
+  <image width="128" height="128" source="Tiles/2.png"/>
+ </tile>
+ <tile id="2">
+  <image width="128" height="128" source="Tiles/3.png"/>
+ </tile>
+ <tile id="3">
+  <image width="128" height="128" source="Tiles/4.png"/>
+ </tile>
+ <tile id="4">
+  <image width="128" height="128" source="Tiles/5.png"/>
+ </tile>
+ <tile id="5">
+  <image width="128" height="128" source="Tiles/6.png"/>
+ </tile>
+ <tile id="6">
+  <image width="128" height="128" source="Tiles/7.png"/>
+ </tile>
+ <tile id="7">
+  <image width="128" height="128" source="Tiles/8.png"/>
+ </tile>
+ <tile id="8">
+  <image width="128" height="128" source="Tiles/9.png"/>
+ </tile>
+ <tile id="9">
+  <image width="128" height="128" source="Tiles/10.png"/>
+ </tile>
+ <tile id="10">
+  <image width="128" height="128" source="Tiles/11.png"/>
+ </tile>
+ <tile id="11">
+  <image width="128" height="128" source="Tiles/12.png"/>
+ </tile>
+ <tile id="12">
+  <image width="128" height="93" source="Tiles/13.png"/>
+ </tile>
+ <tile id="13">
+  <image width="128" height="93" source="Tiles/14.png"/>
+ </tile>
+ <tile id="14">
+  <image width="128" height="93" source="Tiles/15.png"/>
+ </tile>
+ <tile id="15">
+  <image width="128" height="128" source="Tiles/16.png"/>
+ </tile>
+ <tile id="16">
+  <image width="128" height="99" source="Tiles/17.png"/>
+ </tile>
+ <tile id="17">
+  <image width="128" height="128" source="Tiles/18.png"/>
+ </tile>
+ <tile id="18">
+  <image width="133" height="65" source="Object/Bush_1.png"/>
+ </tile>
+ <tile id="19">
+  <image width="133" height="65" source="Object/Bush_2.png"/>
+ </tile>
+ <tile id="20">
+  <image width="73" height="47" source="Object/Bush_3.png"/>
+ </tile>
+ <tile id="21">
+  <image width="73" height="46" source="Object/Bush_4.png"/>
+ </tile>
+ <tile id="22">
+  <image width="77" height="77" source="Object/Crate.png"/>
+ </tile>
+ <tile id="23">
+  <image width="49" height="41" source="Object/Mushroom_1.png"/>
+ </tile>
+ <tile id="24">
+  <image width="50" height="41" source="Object/Mushroom_2.png"/>
+ </tile>
+ <tile id="25">
+  <image width="63" height="65" source="Object/Sign_1.png"/>
+ </tile>
+ <tile id="26">
+  <image width="62" height="64" source="Object/Sign_2.png"/>
+ </tile>
+ <tile id="27">
+  <image width="90" height="54" source="Object/Stone.png"/>
+ </tile>
+ <tile id="28">
+  <image width="116" height="44" source="Object/Tree_1.png"/>
+ </tile>
+</tileset>

+ 49 - 0
Shaders/Tilemap/Aether.Shaders.Tilemap.PORTABLE.csproj

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <ProjectGuid>{CA1564C9-D4A6-4454-A6B6-7B4E907DF8C0}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>tainicom.Aether.Shaders</RootNamespace>
+    <AssemblyName>Aether.Shaders.Tilemap</AssemblyName>
+    <TargetFrameworkProfile>Profile328</TargetFrameworkProfile>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\bin\Debug\Portable\</OutputPath>
+    <DefineConstants>DEBUG;PORTABLE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\bin\Release\Portable\</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+    <DefineConstants>PORTABLE</DefineConstants>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="TilemapEffect.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.PORTABLE.cs" />
+  </ItemGroup>
+  <ItemGroup>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.Portable.9000" Version="3.8.9008">
+      <PrivateAssets>all</PrivateAssets>
+      <ExcludeAssets>runtime</ExcludeAssets>
+    </PackageReference>
+  </ItemGroup>
+</Project>

+ 60 - 0
Shaders/Tilemap/Aether.Shaders.Tilemap.WINDOWS.MG.csproj

@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{6721CD07-0880-4154-85AF-EBE6043EF178}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>tainicom.Aether.Shaders</RootNamespace>
+    <AssemblyName>Aether.Shaders.Tilemap</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\..\bin\Debug\Windows\</OutputPath>
+    <DefineConstants>TRACE;DEBUG;WINDOWS MG</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\..\bin\Release\Windows\</OutputPath>
+    <DefineConstants>TRACE;WINDOWS MG</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="TilemapEffect.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Resources\TilemapEffect.dx11.mgfxo.8" />
+    <EmbeddedResource Include="Resources\TilemapEffect.dx11.mgfxo.9" />
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.Portable.9000" Version="3.8.9008">
+      <PrivateAssets>all</PrivateAssets>
+      <ExcludeAssets>runtime</ExcludeAssets>
+    </PackageReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 61 - 0
Shaders/Tilemap/Aether.Shaders.Tilemap.WINDOWS.XNA.csproj

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
+  <PropertyGroup>
+    <ProjectGuid>{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}</ProjectGuid>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>tainicom.Aether.Shaders</RootNamespace>
+    <AssemblyName>Aether.Shaders.Tilemap</AssemblyName>
+    <XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
+    <XnaPlatform>Windows</XnaPlatform>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\..\bin\Debug\Windows.XNA\</OutputPath>
+    <DefineConstants>TRACE;DEBUG;WINDOWS XNA</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <PlatformTarget>x86</PlatformTarget>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <DebugType>none</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\..\bin\Release\Windows.XNA\</OutputPath>
+    <DefineConstants>TRACE;WINDOWS XNA</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <PlatformTarget>x86</PlatformTarget>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
+    <Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
+    <Reference Include="System" />
+    <Reference Include="System.Core">
+      <RequiredTargetFramework>4.0</RequiredTargetFramework>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="TilemapEffect.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Resources\TilemapEffect.xna.WinReach" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.ContentPipelineExtensions.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+     Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 24 - 0
Shaders/Tilemap/Properties/AssemblyInfo.PORTABLE.cs

@@ -0,0 +1,24 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+[assembly: AssemblyTitle("Aether.Shaders.TileMap")]
+[assembly: AssemblyProduct("Aether.Shaders.TileMap")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyCopyright("Copyright ©  Kastellanos Nikolaos 2019-2021")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion ("1.0.*")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+

+ 37 - 0
Shaders/Tilemap/Properties/AssemblyInfo.cs

@@ -0,0 +1,37 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("Aether.Shaders.TileMap")]
+[assembly: AssemblyProduct("Aether.Shaders.TileMap")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyCopyright("Copyright ©  Kastellanos Nikolaos 2019-2021")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type. Only Windows
+// assemblies support COM.
+[assembly: ComVisible(false)]
+
+// On Windows, the following GUID is for the ID of the typelib if this
+// project is exposed to COM. On other platforms, it unique identifies the
+// title storage container when deploying this assembly to the device.
+[assembly: Guid("316AAED0-BF7E-4096-BF14-CAEC9D974C04")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+

BIN
Shaders/Tilemap/Resources/TilemapEffect.dx11.mgfxo.8


BIN
Shaders/Tilemap/Resources/TilemapEffect.dx11.mgfxo.9


BIN
Shaders/Tilemap/Resources/TilemapEffect.ogl.mgfxo.8


BIN
Shaders/Tilemap/Resources/TilemapEffect.ogl.mgfxo.9


BIN
Shaders/Tilemap/Resources/TilemapEffect.xna.WinReach


+ 25 - 0
Shaders/Tilemap/Shaders/BuildShaders.bat

@@ -0,0 +1,25 @@
+@echo off
+setlocal
+
+cd %~dp0
+
+SET MGFX="C:\Program Files (x86)\MSBuild\MonoGame\v3.0\Tools\2mgfx.exe"
+SET XNAFX="..\..\Tools\CompileEffect\CompileEffect.exe"
+
+@echo Build dx11
+@for /f %%f IN ('dir /b *.fx') do (
+    call %MGFX% %%~nf.fx ..\Resources\%%~nf.dx11.mgfxo /Profile:DirectX_11
+)
+
+@echo Build ogl
+@for /f %%f IN ('dir /b *.fx') do (
+    call %MGFX% %%~nf.fx ..\Resources\%%~nf.ogl.mgfxo
+)
+
+@echo Build dx9/xna Reach
+@for /f %%f IN ('dir /b *.fx') do (
+    call %XNAFX% Windows Reach %%~nf.fx ..\Resources\%%~nf.xna.WinReach
+)
+
+endlocal
+@pause

+ 58 - 0
Shaders/Tilemap/Shaders/Common.fxh

@@ -0,0 +1,58 @@
+//-----------------------------------------------------------------------------
+// Common.fxh
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+
+float ComputeFogFactor(float4 position)
+{
+    return saturate(dot(position, FogVector));
+}
+
+
+void ApplyFog(inout float4 color, float fogFactor)
+{
+    color.rgb = lerp(color.rgb, FogColor * color.a, fogFactor);
+}
+
+
+void AddSpecular(inout float4 color, float3 specular)
+{
+    color.rgb += specular * color.a;
+}
+
+
+struct CommonVSOutput
+{
+    float4 Pos_ps;
+    float4 Diffuse;
+    float3 Specular;
+    float  FogFactor;
+};
+
+
+CommonVSOutput ComputeCommonVSOutput(float4 position)
+{
+    CommonVSOutput vout;
+    
+    vout.Pos_ps = mul(position, WorldViewProj);
+    vout.Diffuse = DiffuseColor;
+    vout.Specular = 0;
+    vout.FogFactor = ComputeFogFactor(position);
+    
+    return vout;
+}
+
+
+#define SetCommonVSOutputParams \
+    vout.PositionPS = cout.Pos_ps; \
+    vout.Diffuse = cout.Diffuse; \
+    vout.Specular = float4(cout.Specular, cout.FogFactor);
+
+
+#define SetCommonVSOutputParamsNoFog \
+    vout.PositionPS = cout.Pos_ps; \
+    vout.Diffuse = cout.Diffuse;
+

+ 62 - 0
Shaders/Tilemap/Shaders/Macros.fxh

@@ -0,0 +1,62 @@
+//-----------------------------------------------------------------------------
+// Macros.fxh
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+#ifdef SM4
+
+
+// Macros for targetting shader model 4.0 (DX11)
+
+#define TECHNIQUE(name, vsname, psname ) \
+	technique name { pass { VertexShader = compile vs_4_0_level_9_1 vsname (); PixelShader = compile ps_4_0_level_9_1 psname(); } }
+
+#define BEGIN_CONSTANTS     cbuffer Parameters : register(b0) {
+#define MATRIX_CONSTANTS
+#define END_CONSTANTS       };
+
+#define _vs(r)
+#define _ps(r)
+#define _cb(r)
+
+#define DECLARE_TEXTURE(Name, index) \
+    Texture2D<float4> Name : register(t##index); \
+    sampler Name##Sampler : register(s##index)
+
+#define DECLARE_CUBEMAP(Name, index) \
+    TextureCube<float4> Name : register(t##index); \
+    sampler Name##Sampler : register(s##index)
+
+#define SAMPLE_TEXTURE(Name, texCoord)  Name.Sample(Name##Sampler, texCoord)
+#define SAMPLE_CUBEMAP(Name, texCoord)  Name.Sample(Name##Sampler, texCoord)
+
+
+#else
+
+
+// Macros for targetting shader model 2.0 (DX9)
+
+#define TECHNIQUE(name, vsname, psname ) \
+	technique name { pass { VertexShader = compile vs_2_0 vsname (); PixelShader = compile ps_2_0 psname(); } }
+
+#define BEGIN_CONSTANTS
+#define MATRIX_CONSTANTS
+#define END_CONSTANTS
+
+#define _vs(r)  : register(vs, r)
+#define _ps(r)  : register(ps, r)
+#define _cb(r)
+
+#define DECLARE_TEXTURE(Name, index) \
+    sampler2D Name##Sampler : register(s##index);
+
+#define DECLARE_CUBEMAP(Name, index) \
+    samplerCUBE Name : register(s##index);
+
+#define SAMPLE_TEXTURE(Name, texCoord)  tex2D(Name##Sampler, texCoord)
+#define SAMPLE_CUBEMAP(Name, texCoord)  texCUBE(Name##Sampler, texCoord)
+
+
+#endif

+ 161 - 0
Shaders/Tilemap/Shaders/Structures.fxh

@@ -0,0 +1,161 @@
+//-----------------------------------------------------------------------------
+// Structurs.fxh
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+
+// Vertex shader input structures.
+
+struct VSInput
+{
+    float4 Position : POSITION;
+};
+
+struct VSInputVc
+{
+    float4 Position : POSITION;
+    float4 Color    : COLOR;
+};
+
+struct VSInputTx
+{
+    float4 Position : POSITION;
+    float2 TexCoord : TEXCOORD;
+};
+
+struct VSInputTxVc
+{
+    float4 Position : POSITION;
+    float2 TexCoord : TEXCOORD;
+    float4 Color    : COLOR;
+};
+
+struct VSInputNm
+{
+    float4 Position : POSITION;
+    float3 Normal   : NORMAL;
+};
+
+struct VSInputNmVc
+{
+    float4 Position : POSITION;
+    float3 Normal   : NORMAL;
+    float4 Color    : COLOR;
+};
+
+struct VSInputNmTx
+{
+    float4 Position : POSITION;
+    float3 Normal   : NORMAL;
+    float2 TexCoord : TEXCOORD;
+};
+
+struct VSInputNmTxVc
+{
+    float4 Position : POSITION;
+    float3 Normal   : NORMAL;
+    float2 TexCoord : TEXCOORD;
+    float4 Color    : COLOR;
+};
+
+struct VSInputTx2
+{
+    float4 Position  : POSITION0;
+    float2 TexCoord  : TEXCOORD0;
+    float2 TexCoord2 : TEXCOORD1;
+};
+
+struct VSInputTx2Vc
+{
+    float4 Position  : POSITION0;
+    float2 TexCoord  : TEXCOORD0;
+    float2 TexCoord2 : TEXCOORD1;
+    float4 Color     : COLOR;
+};
+
+struct VSInputNmTxWeights
+{
+    float4 Position : POSITION0;
+    float3 Normal   : NORMAL0;
+    float2 TexCoord : TEXCOORD0;
+    uint4  Indices  : BLENDINDICES0;
+    float4 Weights  : BLENDWEIGHT0;
+};
+
+
+
+// Vertex shader output structures.
+
+struct VSOutput
+{
+    float4 PositionPS : SV_Position;
+    float4 Diffuse    : COLOR0;
+    float4 Specular   : COLOR1;
+};
+
+struct VSOutputNoFog
+{
+    float4 PositionPS : SV_Position;
+    float4 Diffuse    : COLOR0;
+};
+
+struct VSOutputTx
+{
+    float4 PositionPS : SV_Position;
+    float4 Diffuse    : COLOR0;
+    float4 Specular   : COLOR1;
+    float2 TexCoord   : TEXCOORD0;
+};
+
+struct VSOutputTxNoFog
+{
+    float4 PositionPS : SV_Position;
+    float4 Diffuse    : COLOR0;
+    float2 TexCoord   : TEXCOORD0;
+};
+
+struct VSOutputPixelLighting
+{
+    float4 PositionPS : SV_Position;
+    float4 PositionWS : TEXCOORD0;
+    float3 NormalWS   : TEXCOORD1;
+    float4 Diffuse    : COLOR0;
+};
+
+struct VSOutputPixelLightingTx
+{
+    float4 PositionPS : SV_Position;
+    float2 TexCoord   : TEXCOORD0;
+    float4 PositionWS : TEXCOORD1;
+    float3 NormalWS   : TEXCOORD2;
+    float4 Diffuse    : COLOR0;
+};
+
+struct VSOutputTx2
+{
+    float4 PositionPS : SV_Position;
+    float4 Diffuse    : COLOR0;
+    float4 Specular   : COLOR1;
+    float2 TexCoord   : TEXCOORD0;
+    float2 TexCoord2  : TEXCOORD1;
+};
+
+struct VSOutputTx2NoFog
+{
+    float4 PositionPS : SV_Position;
+    float4 Diffuse    : COLOR0;
+    float2 TexCoord   : TEXCOORD0;
+    float2 TexCoord2  : TEXCOORD1;
+};
+
+struct VSOutputTxEnvMap
+{
+    float4 PositionPS : SV_Position;
+    float4 Diffuse    : COLOR0;
+    float4 Specular   : COLOR1;
+    float2 TexCoord   : TEXCOORD0;
+    float3 EnvCoord   : TEXCOORD1;
+};
+

+ 158 - 0
Shaders/Tilemap/Shaders/TilemapEffect.fx

@@ -0,0 +1,158 @@
+#include "Macros.fxh"
+
+
+#ifdef SM4
+DECLARE_TEXTURE(Texture, 0);
+DECLARE_TEXTURE(TextureAtlas, 1);
+#else
+texture Texture;
+sampler TextureSampler : register(s0) = sampler_state
+{
+	Texture = (Texture);
+	MAGFILTER = POINT;
+	MINFILTER = POINT;
+	MIPFILTER = POINT;
+	AddressU = Wrap;
+	AddressV = Wrap;
+};
+texture TextureAtlas;
+sampler TextureAtlasSampler : register(s1) = sampler_state
+{
+    Texture = (TextureAtlas);
+	MAGFILTER = POINT; //LINEAR;
+	MINFILTER = POINT; //LINEAR;
+	MIPFILTER = POINT; //LINEAR;
+	AddressU = Wrap;
+	AddressV = Wrap;
+};
+#endif
+
+
+BEGIN_CONSTANTS
+
+    float4 DiffuseColor     _vs(c0) _cb(c0);
+    float3 FogColor         _ps(c0) _cb(c1);
+    float4 FogVector        _vs(c5) _cb(c2);
+    float2 MapSize;
+	float2 InvAtlasSize;
+
+MATRIX_CONSTANTS
+
+    float4x4 WorldViewProj  _vs(c1) _cb(c0);
+
+END_CONSTANTS
+
+
+#include "Structures.fxh"
+#include "Common.fxh"
+
+
+// Vertex shader: basic.
+VSOutputTx VSTilemapFog(VSInputTx vin)
+{
+    VSOutputTx vout;
+    
+    CommonVSOutput cout = ComputeCommonVSOutput(vin.Position);
+    SetCommonVSOutputParams;
+    
+    vout.TexCoord = vin.TexCoord;
+
+    return vout;
+}
+
+
+// Vertex shader: no fog.
+VSOutputTxNoFog VSTilemap(VSInputTx vin)
+{
+    VSOutputTxNoFog vout;
+    
+    CommonVSOutput cout = ComputeCommonVSOutput(vin.Position);
+    SetCommonVSOutputParamsNoFog;
+    
+    vout.TexCoord = vin.TexCoord;
+
+    return vout;
+}
+
+
+// Vertex shader: vertex color.
+VSOutputTx VSTilemapVcFog(VSInputTxVc vin)
+{
+    VSOutputTx vout;
+    
+    CommonVSOutput cout = ComputeCommonVSOutput(vin.Position);
+    SetCommonVSOutputParams;
+    
+    vout.TexCoord = vin.TexCoord;
+    vout.Diffuse *= vin.Color;
+    
+    return vout;
+}
+
+
+// Vertex shader: vertex color, no fog.
+VSOutputTxNoFog VSTilemapVc(VSInputTxVc vin)
+{
+    VSOutputTxNoFog vout;
+    
+    CommonVSOutput cout = ComputeCommonVSOutput(vin.Position);
+    SetCommonVSOutputParamsNoFog;
+    
+    vout.TexCoord = vin.TexCoord;
+    vout.Diffuse *= vin.Color;
+    
+    return vout;
+}
+
+
+// Pixel shader: basic.
+float4 PSTilemapFog(VSOutputTx pin) : SV_Target0
+{
+    float2 txCoord = pin.TexCoord * MapSize;
+    float2 txCoordi = floor(txCoord);
+    float2 tx2Coord = (txCoord - txCoordi);
+
+    float4 mapColor = SAMPLE_TEXTURE(Texture, txCoordi/MapSize);
+    float2 tileCoord = mapColor.xy * 255;
+    float alpha = mapColor.a;
+	
+    tx2Coord = (tx2Coord + tileCoord) * InvAtlasSize;
+
+    float4 color = SAMPLE_TEXTURE(TextureAtlas, tx2Coord);
+    color *= alpha;
+    color *= pin.Diffuse;
+    
+    ApplyFog(color, pin.Specular.w);
+    
+    return color;
+}
+
+
+// Pixel shader: no fog.
+float4 PSTilemap(VSOutputTxNoFog pin) : SV_Target0
+{
+    float2 txCoord = pin.TexCoord * MapSize;
+    float2 txCoordi = floor(txCoord);
+    float2 tx2Coord = (txCoord - txCoordi);
+
+    float4 mapColor = SAMPLE_TEXTURE(Texture, txCoordi/MapSize);
+	float2 tileCoord = mapColor.xy * 255;
+	float alpha = mapColor.a;
+	
+    tx2Coord = (tx2Coord + tileCoord) * InvAtlasSize;
+
+    float4 color = SAMPLE_TEXTURE(TextureAtlas, tx2Coord);
+	color *= alpha;
+    color *= pin.Diffuse;
+
+    return color;
+}
+
+
+// NOTE: The order of the techniques here are
+// defined to match the indexing in DualTextureEffect.cs.
+
+TECHNIQUE( TilemapEffectFog,             VSTilemapFog,   PSTilemapFog );
+TECHNIQUE( TilemapEffect,                VSTilemap,      PSTilemap );
+TECHNIQUE( TilemapEffect_VertexColorFog, VSTilemapVcFog, PSTilemapFog );
+TECHNIQUE( TilemapEffect_VertexColor,    VSTilemapVc,    PSTilemap );

+ 489 - 0
Shaders/Tilemap/TilemapEffect.cs

@@ -0,0 +1,489 @@
+#region License
+//   Copyright 2019-2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+using System;
+using System.IO;
+using System.Reflection;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace tainicom.Aether.Shaders
+{
+    public class TilemapEffect : Effect, IEffectMatrices
+    {
+        #region Effect Parameters
+        
+        EffectParameter textureParam;
+        EffectParameter textureAtlasParam;
+
+        EffectParameter mapSizeParam;
+        EffectParameter invAtlasSizeParam;
+        
+        EffectParameter diffuseColorParam;
+        EffectParameter fogColorParam;
+        EffectParameter fogVectorParam;
+
+        // IEffectMatrices
+        EffectParameter worldViewProjParam;
+
+        #endregion
+
+        #region Fields
+        
+        bool fogEnabled;
+        bool vertexColorEnabled;
+
+        Matrix world = Matrix.Identity;
+        Matrix view = Matrix.Identity;
+        Matrix projection = Matrix.Identity;
+
+        Matrix worldView;
+
+        Vector3 diffuseColor = Vector3.One;
+
+        float alpha = 1;
+
+        float fogStart = 0;
+        float fogEnd = 1;
+                
+        Vector2 atlasSize;
+
+        EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
+
+#if ((MG && WINDOWS) || W8_1 || W10)
+        static readonly String resourceName = "tainicom.Aether.Shaders.Resources.TilemapEffect.dx11.mgfxo";
+#else
+        static readonly String resourceName = "tainicom.Aether.Shaders.Resources.TilemapEffect.xna.WinReach";
+#endif
+
+        internal static byte[] LoadEffectResource(string name)
+        {
+            using (var stream = LoadEffectResourceStream(name))
+            using (var ms = new MemoryStream())
+            {
+                stream.CopyTo(ms);
+                return ms.ToArray();
+            }
+        }
+
+        internal static Stream LoadEffectResourceStream(string name)
+        {
+            // Detect MG version
+            var version = "";
+#if !XNA && !PORTABLE
+            version = ".9";
+            var mgVersion = GetAssembly(typeof(Effect)).GetName().Version;
+            if (mgVersion.Major == 3)
+            {
+                if (mgVersion.Minor == 4)
+                    version = ".6";
+                if (mgVersion.Minor == 5)
+                    version = ".7";
+                if (mgVersion.Minor == 6)
+                    version = ".8";
+                if (mgVersion.Minor == 7)
+                    version = ".8";
+                if (mgVersion.Minor == 8)
+                    version = ".9";
+            }
+            name = name + version;
+#endif
+
+            Stream stream = GetAssembly(typeof(TilemapEffect)).GetManifestResourceStream(name);
+            return stream;
+        }
+
+        private static Assembly GetAssembly(Type type)
+        {            
+            #if W8_1 || W10 
+            return type.GetTypeInfo().Assembly;
+            #else
+            return type.Assembly;
+            #endif
+        }
+
+        #endregion
+        
+        #region Public Properties
+
+        public Matrix Projection
+        {
+            get { return projection; }
+            set
+            {
+                projection = value;
+                dirtyFlags |= EffectDirtyFlags.WorldViewProj;
+            }
+        }
+
+        public Matrix View
+        {
+            get { return view; }
+            set
+            {
+                view = value;
+                dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.Fog;
+            }
+        }
+
+        public Matrix World
+        {
+            get { return world; }
+            set
+            {
+                world = value;
+                dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.Fog;
+            }
+        }
+                
+        /// <summary>
+        /// Gets or sets the Tilemap texture.
+        /// </summary>
+        public Texture2D Texture
+        {
+            get { return textureParam.GetValueTexture2D(); }
+            set { textureParam.SetValue(value); }
+        }
+
+
+        /// <summary>
+        /// Gets or sets the Atlas texture.
+        /// </summary>
+        public Texture2D TextureAtlas
+        {
+            get { return textureAtlasParam.GetValueTexture2D(); }
+            set { textureAtlasParam.SetValue(value); }
+        }
+
+
+        /// <summary>
+        /// Gets or sets the Map size.
+        /// </summary>
+        public Vector2 MapSize
+        {
+            get { return mapSizeParam.GetValueVector2(); }
+            set { mapSizeParam.SetValue(value); }
+        }
+
+        /// <summary>
+        /// Gets or sets the Atlas size.
+        /// </summary>
+        public Vector2 AtlasSize
+        {
+            get { return atlasSize; }
+            set 
+            { 
+                atlasSize = value;
+                value.X = 1f/value.X;
+                value.Y = 1f/value.Y;
+                invAtlasSizeParam.SetValue(value);
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the material diffuse color (range 0 to 1).
+        /// </summary>
+        public Vector3 DiffuseColor
+        {
+            get { return diffuseColor; }
+
+            set
+            {
+                diffuseColor = value;
+                dirtyFlags |= EffectDirtyFlags.MaterialColor;
+            }
+        }
+
+
+        /// <summary>
+        /// Gets or sets the material alpha.
+        /// </summary>
+        public float Alpha
+        {
+            get { return alpha; }
+
+            set
+            {
+                alpha = value;
+                dirtyFlags |= EffectDirtyFlags.MaterialColor;
+            }
+        }
+
+
+        /// <summary>
+        /// Gets or sets the fog enable flag.
+        /// </summary>
+        public bool FogEnabled
+        {
+            get { return fogEnabled; }
+
+            set
+            {
+                if (fogEnabled != value)
+                {
+                    fogEnabled = value;
+                    dirtyFlags |= EffectDirtyFlags.ShaderIndex | EffectDirtyFlags.FogEnable;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Gets or sets the fog start distance.
+        /// </summary>
+        public float FogStart
+        {
+            get { return fogStart; }
+
+            set
+            {
+                fogStart = value;
+                dirtyFlags |= EffectDirtyFlags.Fog;
+            }
+        }
+
+
+        /// <summary>
+        /// Gets or sets the fog end distance.
+        /// </summary>
+        public float FogEnd
+        {
+            get { return fogEnd; }
+
+            set
+            {
+                fogEnd = value;
+                dirtyFlags |= EffectDirtyFlags.Fog;
+            }
+        }
+
+
+        /// <summary>
+        /// Gets or sets the fog color.
+        /// </summary>
+        public Vector3 FogColor
+        {
+            get { return fogColorParam.GetValueVector3(); }
+            set { fogColorParam.SetValue(value); }
+        }
+
+
+        /// <summary>
+        /// Gets or sets whether vertex color is enabled.
+        /// </summary>
+        public bool VertexColorEnabled
+        {
+            get { return vertexColorEnabled; }
+
+            set
+            {
+                if (vertexColorEnabled != value)
+                {
+                    vertexColorEnabled = value;
+                    dirtyFlags |= EffectDirtyFlags.ShaderIndex;
+                }
+            }
+        }
+
+        #endregion
+
+        #region Methods
+
+         public TilemapEffect(GraphicsDevice graphicsDevice)
+            : base(graphicsDevice, 
+#if NETFX_CORE || WP8
+            LoadEffectResourceStream(resourceName), true
+#else
+            LoadEffectResource(resourceName)
+#endif
+           )
+        {    
+            CacheEffectParameters(null);
+        }
+
+        public TilemapEffect(GraphicsDevice graphicsDevice, byte[] Bytecode): base(graphicsDevice, Bytecode)
+        {   
+            CacheEffectParameters(null);
+        }
+
+        protected TilemapEffect(TilemapEffect cloneSource)
+            : base(cloneSource)
+        {
+            CacheEffectParameters(cloneSource);
+            
+            fogEnabled = cloneSource.fogEnabled;
+            vertexColorEnabled = cloneSource.vertexColorEnabled;
+
+            world = cloneSource.world;
+            view = cloneSource.view;
+            projection = cloneSource.projection;
+
+            diffuseColor = cloneSource.diffuseColor;
+
+            alpha = cloneSource.alpha;
+
+            fogStart = cloneSource.fogStart;
+            fogEnd = cloneSource.fogEnd;
+
+            atlasSize = cloneSource.atlasSize;
+        }
+        
+        public override Effect Clone()
+        {
+            return new TilemapEffect(this);
+        }
+
+        void CacheEffectParameters(TilemapEffect cloneSource)
+        {   
+            textureParam = Parameters["Texture"];
+            textureAtlasParam = Parameters["TextureAtlas"];
+            mapSizeParam = Parameters["MapSize"];
+            invAtlasSizeParam = Parameters["InvAtlasSize"];
+            diffuseColorParam = Parameters["DiffuseColor"];
+            fogColorParam = Parameters["FogColor"];
+            fogVectorParam = Parameters["FogVector"];
+            // IEffectMatrices
+            worldViewProjParam = Parameters["WorldViewProj"];
+        }
+
+
+        /// <summary>
+        /// Lazily computes derived parameter values immediately before applying the effect.
+        /// </summary>
+        protected override void OnApply()
+        {
+            // Recompute the world+view+projection matrix or fog vector?
+            dirtyFlags = SetWorldViewProjAndFog(dirtyFlags, ref world, ref view, ref projection, ref worldView, fogEnabled, fogStart, fogEnd, worldViewProjParam, fogVectorParam);
+
+            // Recompute the diffuse/alpha material color parameter?
+            if ((dirtyFlags & EffectDirtyFlags.MaterialColor) != 0)
+            {
+                diffuseColorParam.SetValue(new Vector4(diffuseColor * alpha, alpha));
+
+                dirtyFlags &= ~EffectDirtyFlags.MaterialColor;
+            }
+
+            // Recompute the shader index?
+            if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0)
+            {
+                int shaderIndex = 0;
+
+                if (!fogEnabled)
+                    shaderIndex += 1;
+
+                if (vertexColorEnabled)
+                    shaderIndex += 2;
+
+                dirtyFlags &= ~EffectDirtyFlags.ShaderIndex;
+
+                CurrentTechnique = Techniques[shaderIndex];
+            }
+        }
+
+
+        /// <summary>
+        /// Lazily recomputes the world+view+projection matrix and
+        /// fog vector based on the current effect parameter settings.
+        /// </summary>
+        static EffectDirtyFlags SetWorldViewProjAndFog(EffectDirtyFlags dirtyFlags,
+                                                                ref Matrix world, ref Matrix view, ref Matrix projection, ref Matrix worldView,
+                                                                bool fogEnabled, float fogStart, float fogEnd,
+                                                                EffectParameter worldViewProjParam, EffectParameter fogVectorParam)
+        {
+            // Recompute the world+view+projection matrix?
+            if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
+            {
+                Matrix worldViewProj;
+
+                Matrix.Multiply(ref world, ref view, out worldView);
+                Matrix.Multiply(ref worldView, ref projection, out worldViewProj);
+
+                worldViewProjParam.SetValue(worldViewProj);
+
+                dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
+            }
+
+            if (fogEnabled)
+            {
+                // Recompute the fog vector?
+                if ((dirtyFlags & (EffectDirtyFlags.Fog | EffectDirtyFlags.FogEnable)) != 0)
+                {
+                    SetFogVector(ref worldView, fogStart, fogEnd, fogVectorParam);
+
+                    dirtyFlags &= ~(EffectDirtyFlags.Fog | EffectDirtyFlags.FogEnable);
+                }
+            }
+            else
+            {
+                // When fog is disabled, make sure the fog vector is reset to zero.
+                if ((dirtyFlags & EffectDirtyFlags.FogEnable) != 0)
+                {
+                    fogVectorParam.SetValue(Vector4.Zero);
+
+                    dirtyFlags &= ~EffectDirtyFlags.FogEnable;
+                }
+            }
+
+            return dirtyFlags;
+        }
+        
+        /// <summary>
+        /// Sets a vector which can be dotted with the object space vertex position to compute fog amount.
+        /// </summary>
+        static void SetFogVector(ref Matrix worldView, float fogStart, float fogEnd, EffectParameter fogVectorParam)
+        {
+            if (fogStart == fogEnd)
+            {
+                // Degenerate case: force everything to 100% fogged if start and end are the same.
+                fogVectorParam.SetValue(new Vector4(0, 0, 0, 1));
+            }
+            else
+            {
+                // We want to transform vertex positions into view space, take the resulting
+                // Z value, then scale and offset according to the fog start/end distances.
+                // Because we only care about the Z component, the shader can do all this
+                // with a single dot product, using only the Z row of the world+view matrix.
+
+                float scale = 1f / (fogStart - fogEnd);
+
+                Vector4 fogVector = new Vector4();
+
+                fogVector.X = worldView.M13 * scale;
+                fogVector.Y = worldView.M23 * scale;
+                fogVector.Z = worldView.M33 * scale;
+                fogVector.W = (worldView.M43 + fogStart) * scale;
+
+                fogVectorParam.SetValue(fogVector);
+            }
+        }
+        
+        #endregion
+
+
+        enum EffectDirtyFlags
+        {
+            WorldViewProj = 1,
+            //World = 2,
+            //EyePosition = 4,
+            MaterialColor = 8,
+            Fog = 16,
+            FogEnable = 32,
+            //AlphaTest = 64,
+            ShaderIndex = 128,
+            All = -1
+        }
+    }
+}

+ 54 - 0
Tilemap/Aether.Tilemap.PORTABLE.csproj

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <ProjectGuid>{94963AFA-FAC3-42A0-A2B2-4DA3A7F604D8}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>tainicom.Aether.Graphics</RootNamespace>
+    <AssemblyName>Aether.Tilemap</AssemblyName>
+    <TargetFrameworkProfile>Profile328</TargetFrameworkProfile>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\bin\Debug\Portable\</OutputPath>
+    <DefineConstants>DEBUG;PORTABLE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\bin\Release\Portable\</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+    <DefineConstants>PORTABLE</DefineConstants>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="Tilemap\Extensions.cs" />
+    <Compile Include="Tilemap\Tile.cs" />
+    <Compile Include="Tilemap\Tilemap.cs" />
+    <Compile Include="ContentReaders\TilemapReader.cs" />
+    <Compile Include="Properties\AssemblyInfo.PORTABLE.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.Portable.9000" Version="3.8.9008">
+      <PrivateAssets>all</PrivateAssets>
+      <ExcludeAssets>runtime</ExcludeAssets>
+    </PackageReference>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Shaders\Tilemap\Aether.Shaders.Tilemap.PORTABLE.csproj">
+      <Project>{ca1564c9-d4a6-4454-a6b6-7b4e907df8c0}</Project>
+      <Name>Aether.Shaders.Tilemap.PORTABLE</Name>
+    </ProjectReference>
+  </ItemGroup>
+</Project>

+ 69 - 0
Tilemap/Aether.Tilemap.WINDOWS.MG.csproj

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <ProjectGuid>{7BD9F460-F3F9-40A0-B298-066EA975C06C}</ProjectGuid>
+    <ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>tainicom.Aether.Graphics</RootNamespace>
+    <AssemblyName>Aether.Tilemap</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <TargetFrameworkProfile>Client</TargetFrameworkProfile>
+    <TargetFrameworkProfile>
+    </TargetFrameworkProfile>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\bin\Debug\Windows\</OutputPath>
+    <DefineConstants>TRACE;DEBUG;WINDOWS MG MAPPEDMEM</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\bin\Release\Windows\</OutputPath>
+    <DefineConstants>TRACE;WINDOWS MG MAPPEDMEM</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.Portable.9000" Version="3.8.9008">
+      <PrivateAssets>all</PrivateAssets>
+      <ExcludeAssets>runtime</ExcludeAssets>
+    </PackageReference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="mscorlib" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Tilemap\Extensions.cs" />
+    <Compile Include="Tilemap\Tile.cs" />
+    <Compile Include="Tilemap\Tilemap.cs" />
+    <Compile Include="ContentReaders\TilemapReader.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Shaders\Tilemap\Aether.Shaders.Tilemap.WINDOWS.MG.csproj">
+      <Project>{6721cd07-0880-4154-85af-ebe6043ef178}</Project>
+      <Name>Aether.Shaders.Tilemap.WINDOWS.MG</Name>
+      <Private>False</Private>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+      Other similar extension points exist, see Microsoft.Common.targets.
+      <Target Name="BeforeBuild">
+      </Target>
+      <Target Name="AfterBuild">
+      </Target>
+    -->
+</Project>

+ 84 - 0
Tilemap/Aether.Tilemap.WINDOWS.XNA.csproj

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <ProjectGuid>{746551BA-FB64-43B0-8479-0506B930D3E5}</ProjectGuid>
+    <ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>tainicom.Aether.Graphics</RootNamespace>
+    <AssemblyName>Aether.Tilemap</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <TargetFrameworkProfile>Client</TargetFrameworkProfile>
+    <XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
+    <XnaPlatform>Windows</XnaPlatform>
+    <XnaProfile>Reach</XnaProfile>
+    <XnaCrossPlatformGroupID>7637B8A4-FB9C-43EC-8ABC-602C818F00B5</XnaCrossPlatformGroupID>
+    <XnaOutputType>Library</XnaOutputType>
+    <!--
+    <XnaRefreshLevel>1</XnaRefreshLevel>
+    -->
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\bin\Debug\Windows.XNA\</OutputPath>
+    <DefineConstants>TRACE;DEBUG;WINDOWS XNA</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <NoStdLib>true</NoStdLib>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+    <PlatformTarget>x86</PlatformTarget>
+    <XnaCompressContent>false</XnaCompressContent>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <DebugType>none</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\bin\Release\Windows.XNA\</OutputPath>
+    <DefineConstants>WINDOWS XNA</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <NoStdLib>true</NoStdLib>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+    <PlatformTarget>x86</PlatformTarget>
+    <XnaCompressContent>true</XnaCompressContent>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
+    <Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
+    <Reference Include="mscorlib" />
+    <Reference Include="System" />
+    <Reference Include="System.Core">
+      <RequiredTargetFramework>4.0</RequiredTargetFramework>
+      <Private>False</Private>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Tilemap\Extensions.cs" />
+    <Compile Include="Tilemap\Tile.cs" />
+    <Compile Include="Tilemap\Tilemap.cs" />
+    <Compile Include="ContentReaders\TilemapReader.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Shaders\Tilemap\Aether.Shaders.Tilemap.WINDOWS.XNA.csproj">
+      <Project>{AB2B4C7E-0FCA-4CFE-8837-E3E3374249F6}</Project>
+      <Name>Aether.Shaders.Tilemap.WINDOWS.XNA</Name>
+      <Private>False</Private>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.targets" />
+  <!--
+      To modify your build process, add your task inside one of the targets below and uncomment it. 
+      Other similar extension points exist, see Microsoft.Common.targets.
+      <Target Name="BeforeBuild">
+      </Target>
+      <Target Name="AfterBuild">
+      </Target>
+    -->
+</Project>

+ 87 - 0
Tilemap/ContentReaders/TilemapReader.cs

@@ -0,0 +1,87 @@
+#region License
+//   Copyright 2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using tainicom.Aether.Graphics;
+using tainicom.Aether.Shaders;
+
+namespace tainicom.Aether.Graphics.Content
+{
+    public class TilemapReader : ContentTypeReader<Tilemap>
+    {
+        protected override Tilemap Read(ContentReader input, Tilemap existingInstance)
+        {
+            IGraphicsDeviceService graphicsDeviceService = (IGraphicsDeviceService)input.ContentManager.ServiceProvider.GetService(typeof(IGraphicsDeviceService));
+            var device = graphicsDeviceService.GraphicsDevice;
+
+
+            Tilemap output = existingInstance ?? new Tilemap();
+
+            // read standard Texture2D
+            output.TextureAtlas = ReadTexture2D(input, output.TextureAtlas);
+
+            output.TextureMap = ReadTexture2D(input, output.TextureMap);
+
+            // read Sprites
+            var count = input.ReadInt32();
+            for (int i = 0; i < count; i++)
+            {
+                var name = input.ReadString();
+                var bounds = new Rectangle(input.ReadInt32(), input.ReadInt32(), input.ReadInt32(), input.ReadInt32());
+                output.Sprites[name] = new Tile(output.TextureAtlas, bounds);
+            }
+
+            var tilemapEffect = new TilemapEffect(device);
+            tilemapEffect.Alpha = 1f;
+            tilemapEffect.VertexColorEnabled = true;
+            tilemapEffect.DiffuseColor = new Vector3(1f, 1f, 1f);
+            tilemapEffect.World = Matrix.Identity;
+            tilemapEffect.View = Matrix.Identity;
+            tilemapEffect.Projection = Matrix.Identity;
+
+            tilemapEffect.Texture = output.TextureMap;
+            tilemapEffect.TextureAtlas = output.TextureAtlas;
+            tilemapEffect.MapSize = new Vector2(output.TextureMap.Width, output.TextureMap.Height);
+            tilemapEffect.AtlasSize = new Vector2(
+                output.TextureAtlas.Width / output.Sprites["null"].Bounds.Width,
+                output.TextureAtlas.Height / output.Sprites["null"].Bounds.Height);
+            output.Effect = tilemapEffect;
+
+            return output;
+        }
+
+        private Texture2D ReadTexture2D(ContentReader input, Texture2D existingInstance)
+        {
+            Texture2D output = null;
+            try
+            {
+                output = input.ReadRawObject<Texture2D>(existingInstance);
+            }
+            catch(NotSupportedException)
+            {
+                var assembly = typeof(Microsoft.Xna.Framework.Content.ContentTypeReader).Assembly;
+                var texture2DReaderType = assembly.GetType("Microsoft.Xna.Framework.Content.Texture2DReader");
+                var texture2DReader = (ContentTypeReader)Activator.CreateInstance(texture2DReaderType, true);
+                output = input.ReadRawObject<Texture2D>(texture2DReader, existingInstance);
+            }
+            
+            return output;
+        }
+    }
+}

+ 24 - 0
Tilemap/Properties/AssemblyInfo.PORTABLE.cs

@@ -0,0 +1,24 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+[assembly: AssemblyTitle ("Aether.Tilemap")]
+[assembly: AssemblyDescription ("")]
+[assembly: AssemblyConfiguration ("")]
+[assembly: AssemblyCompany ("")]
+[assembly: AssemblyProduct ("Aether.Tilemap")]
+[assembly: AssemblyCopyright ("Copyright ©  Kastellanos Nikolaos 2021")]
+[assembly: AssemblyTrademark ("")]
+[assembly: AssemblyCulture ("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion ("1.0.*")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+

+ 41 - 0
Tilemap/Properties/AssemblyInfo.cs

@@ -0,0 +1,41 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("Aether.Tilemap")]
+[assembly: AssemblyProduct("Aether.Tilemap")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyCopyright("Copyright ©  Kastellanos Nikolaos 2021")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type. Only Windows
+// assemblies support COM.
+[assembly: ComVisible(false)]
+
+// On Windows, the following GUID is for the ID of the typelib if this
+// project is exposed to COM. On other platforms, it unique identifies the
+// title storage container when deploying this assembly to the device.
+[assembly: Guid("38540D53-77A0-41C2-AAA2-20851B81EEF8")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+
+#if WP7_1
+// Enable NEON/SIMD
+[assembly: CodeGeneration(CodeGenerationFlags.EnableFPIntrinsicsUsingSIMD)]
+#endif

+ 34 - 0
Tilemap/Tilemap/Extensions.cs

@@ -0,0 +1,34 @@
+#region License
+//   Copyright 2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+using Microsoft.Xna.Framework;
+using tainicom.Aether.Graphics;
+
+namespace Microsoft.Xna.Framework.Graphics
+{
+    public static class Extensions
+    {
+        public static void Draw(this SpriteBatch spriteBatch, Tile sprite, Vector2 position, Color color)
+        {
+            spriteBatch.Draw(sprite.Texture, position, sprite.Bounds, color);
+        }
+        
+        public static void Draw(this SpriteBatch spriteBatch, Tile sprite, Rectangle destinationRectangle, Color color)
+        {
+            spriteBatch.Draw(sprite.Texture, destinationRectangle, sprite.Bounds, color);
+        }
+    }
+}

+ 33 - 0
Tilemap/Tilemap/Tile.cs

@@ -0,0 +1,33 @@
+#region License
+//   Copyright 2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace tainicom.Aether.Graphics
+{
+    public class Tile
+    {
+        public readonly Texture2D Texture;
+        public readonly Rectangle Bounds;
+
+        internal Tile(Texture2D texture, Rectangle bounds)
+        {
+            this.Texture = texture;
+            this.Bounds = bounds;
+        }
+    }
+}

+ 35 - 0
Tilemap/Tilemap/Tilemap.cs

@@ -0,0 +1,35 @@
+#region License
+//   Copyright 2021 Kastellanos Nikolaos
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+#endregion
+
+using System.Collections.Generic;
+using Microsoft.Xna.Framework.Graphics;
+using tainicom.Aether.Shaders;
+
+namespace tainicom.Aether.Graphics
+{
+    public class Tilemap
+    {
+        public Texture2D TextureAtlas { get; internal set; }
+        public Texture2D TextureMap { get; internal set; }
+        public TilemapEffect Effect { get; internal set;}
+        public readonly Dictionary<string, Tile> Sprites;
+
+        internal Tilemap()
+        {
+            this.Sprites = new Dictionary<string, Tile>();
+        }
+    }
+}