| 1 | using System; | 
 
 
 
 
 | 2 | using System.Collections.Generic; | 
 
 
 
 
 | 3 | using System.IO; | 
 
 
 
 
 | 4 | using Oni.Imaging; | 
 
 
 
 
 | 5 |  | 
 
 
 
 
 | 6 | namespace Oni.Motoko | 
 
 
 
 
 | 7 | { | 
 
 
 
 
 | 8 | internal class TextureImporter : Importer | 
 
 
 
 
 | 9 | { | 
 
 
 
 
 | 10 | private static readonly int[] powersOf2 = new[] { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }; | 
 
 
 
 
 | 11 | private static readonly Dictionary<string, TextureFormat> formatNames = new Dictionary<string, TextureFormat>(StringComparer.OrdinalIgnoreCase) | 
 
 
 
 
 | 12 | { | 
 
 
 
 
 | 13 | { "bgr32", TextureFormat.BGR }, | 
 
 
 
 
 | 14 | { "bgr", TextureFormat.BGR }, | 
 
 
 
 
 | 15 | { "bgra32", TextureFormat.RGBA }, | 
 
 
 
 
 | 16 | { "rgba", TextureFormat.RGBA }, | 
 
 
 
 
 | 17 | { "bgra4444", TextureFormat.BGRA4444 }, | 
 
 
 
 
 | 18 | { "bgr555", TextureFormat.BGR555 }, | 
 
 
 
 
 | 19 | { "bgra5551", TextureFormat.BGRA5551 }, | 
 
 
 
 
 | 20 | { "dxt1", TextureFormat.DXT1 } | 
 
 
 
 
 | 21 | }; | 
 
 
 
 
 | 22 |  | 
 
 
 
 
 | 23 | private readonly bool allowLargeTextures; | 
 
 
 
 
 | 24 | private readonly bool noMipMaps; | 
 
 
 
 
 | 25 | private readonly TextureFlags defaultFlags; | 
 
 
 
 
 | 26 | private readonly TextureFormat? defaultFormat; | 
 
 
 
 
 | 27 | private readonly string envmapName; | 
 
 
 
 
 | 28 |  | 
 
 
 
 
 | 29 | public static TextureFormat ParseTextureFormat(string name) | 
 
 
 
 
 | 30 | { | 
 
 
 
 
 | 31 | TextureFormat format; | 
 
 
 
 
 | 32 |  | 
 
 
 
 
 | 33 | if (!formatNames.TryGetValue(name, out format)) | 
 
 
 
 
 | 34 | throw new FormatException(string.Format("Invalid texture format '{0}'", name)); | 
 
 
 
 
 | 35 |  | 
 
 
 
 
 | 36 | return format; | 
 
 
 
 
 | 37 | } | 
 
 
 
 
 | 38 |  | 
 
 
 
 
 | 39 | public TextureImporter(TextureImporterOptions options) | 
 
 
 
 
 | 40 | { | 
 
 
 
 
 | 41 | if (options != null) | 
 
 
 
 
 | 42 | { | 
 
 
 
 
 | 43 | defaultFormat = options.Format; | 
 
 
 
 
 | 44 | envmapName = options.EnvironmentMap; | 
 
 
 
 
 | 45 | defaultFlags = options.Flags & ~TextureFlags.HasMipMaps; | 
 
 
 
 
 | 46 | noMipMaps = (options.Flags & TextureFlags.HasMipMaps) == 0; | 
 
 
 
 
 | 47 | allowLargeTextures = true; | 
 
 
 
 
 | 48 | } | 
 
 
 
 
 | 49 | } | 
 
 
 
 
 | 50 |  | 
 
 
 
 
 | 51 | public TextureImporter(string[] args) | 
 
 
 
 
 | 52 | { | 
 
 
 
 
 | 53 | foreach (string arg in args) | 
 
 
 
 
 | 54 | { | 
 
 
 
 
 | 55 | if (arg.StartsWith("-format:", StringComparison.Ordinal)) | 
 
 
 
 
 | 56 | { | 
 
 
 
 
 | 57 | TextureFormat formatArg; | 
 
 
 
 
 | 58 | string formatName = arg.Substring(8); | 
 
 
 
 
 | 59 |  | 
 
 
 
 
 | 60 | if (!formatNames.TryGetValue(formatName, out formatArg)) | 
 
 
 
 
 | 61 | throw new NotSupportedException(string.Format("Unknown texture format {0}", formatName)); | 
 
 
 
 
 | 62 |  | 
 
 
 
 
 | 63 | defaultFormat = formatArg; | 
 
 
 
 
 | 64 | } | 
 
 
 
 
 | 65 | else if (arg.StartsWith("-envmap:", StringComparison.Ordinal)) | 
 
 
 
 
 | 66 | { | 
 
 
 
 
 | 67 | string envmap = arg.Substring(8); | 
 
 
 
 
 | 68 |  | 
 
 
 
 
 | 69 | if (envmap.Length > 0) | 
 
 
 
 
 | 70 | envmapName = envmap; | 
 
 
 
 
 | 71 | } | 
 
 
 
 
 | 72 | else if (arg == "-large") | 
 
 
 
 
 | 73 | { | 
 
 
 
 
 | 74 | allowLargeTextures = true; | 
 
 
 
 
 | 75 | } | 
 
 
 
 
 | 76 | else if (arg == "-nouwrap") | 
 
 
 
 
 | 77 | { | 
 
 
 
 
 | 78 | defaultFlags |= TextureFlags.NoUWrap; | 
 
 
 
 
 | 79 | } | 
 
 
 
 
 | 80 | else if (arg == "-novwrap") | 
 
 
 
 
 | 81 | { | 
 
 
 
 
 | 82 | defaultFlags |= TextureFlags.NoVWrap; | 
 
 
 
 
 | 83 | } | 
 
 
 
 
 | 84 | else if (arg == "-nomipmaps") | 
 
 
 
 
 | 85 | { | 
 
 
 
 
 | 86 | noMipMaps = true; | 
 
 
 
 
 | 87 | } | 
 
 
 
 
 | 88 | } | 
 
 
 
 
 | 89 | } | 
 
 
 
 
 | 90 |  | 
 
 
 
 
 | 91 | public override void Import(string filePath, string outputDirPath) | 
 
 
 
 
 | 92 | { | 
 
 
 
 
 | 93 | var texture = new Texture | 
 
 
 
 
 | 94 | { | 
 
 
 
 
 | 95 | Name = DecodeFileName(filePath), | 
 
 
 
 
 | 96 | Flags = defaultFlags | 
 
 
 
 
 | 97 | }; | 
 
 
 
 
 | 98 |  | 
 
 
 
 
 | 99 | LoadImage(texture, filePath); | 
 
 
 
 
 | 100 |  | 
 
 
 
 
 | 101 | if (envmapName != null) | 
 
 
 
 
 | 102 | { | 
 
 
 
 
 | 103 | texture.EnvMap = new Texture(); | 
 
 
 
 
 | 104 | texture.EnvMap.Name = envmapName; | 
 
 
 
 
 | 105 | } | 
 
 
 
 
 | 106 |  | 
 
 
 
 
 | 107 | BeginImport(); | 
 
 
 
 
 | 108 | TextureDatWriter.Write(texture, this); | 
 
 
 
 
 | 109 | Write(outputDirPath); | 
 
 
 
 
 | 110 | } | 
 
 
 
 
 | 111 |  | 
 
 
 
 
 | 112 | private void LoadImage(Texture texture, string filePath) | 
 
 
 
 
 | 113 | { | 
 
 
 
 
 | 114 | var surfaces = new List<Surface>(); | 
 
 
 
 
 | 115 |  | 
 
 
 
 
 | 116 | switch (Path.GetExtension(filePath).ToLowerInvariant()) | 
 
 
 
 
 | 117 | { | 
 
 
 
 
 | 118 | case ".tga": | 
 
 
 
 
 | 119 | surfaces.Add(TgaReader.Read(filePath)); | 
 
 
 
 
 | 120 | break; | 
 
 
 
 
 | 121 | case ".dds": | 
 
 
 
 
 | 122 | surfaces.AddRange(DdsReader.Read(filePath, noMipMaps)); | 
 
 
 
 
 | 123 | break; | 
 
 
 
 
 | 124 | default: | 
 
 
 
 
 | 125 | #if !NETCORE | 
 
 
 
 
 | 126 | surfaces.Add(SysReader.Read(filePath)); | 
 
 
 
 
 | 127 | #endif | 
 
 
 
 
 | 128 | break; | 
 
 
 
 
 | 129 | } | 
 
 
 
 
 | 130 |  | 
 
 
 
 
 | 131 | if (surfaces.Count == 0) | 
 
 
 
 
 | 132 | throw new InvalidDataException(string.Format("Could not load image '{0}'", filePath)); | 
 
 
 
 
 | 133 |  | 
 
 
 
 
 | 134 | var mainSurface = surfaces[0]; | 
 
 
 
 
 | 135 |  | 
 
 
 
 
 | 136 | if (Array.IndexOf(powersOf2, mainSurface.Width) == -1) | 
 
 
 
 
 | 137 | Console.Error.WriteLine("Warning: Texture '{0}' width is not a power of 2", filePath); | 
 
 
 
 
 | 138 |  | 
 
 
 
 
 | 139 | if (Array.IndexOf(powersOf2, mainSurface.Height) == -1) | 
 
 
 
 
 | 140 | Console.Error.WriteLine("Warning: Texture '{0}' height is not a power of 2", filePath); | 
 
 
 
 
 | 141 |  | 
 
 
 
 
 | 142 | if (surfaces.Count == 1) | 
 
 
 
 
 | 143 | { | 
 
 
 
 
 | 144 | mainSurface.CleanupAlpha(); | 
 
 
 
 
 | 145 | } | 
 
 
 
 
 | 146 |  | 
 
 
 
 
 | 147 | if (mainSurface.Format == SurfaceFormat.DXT1 && defaultFormat != null && defaultFormat != TextureFormat.DXT1) | 
 
 
 
 
 | 148 | { | 
 
 
 
 
 | 149 | // | 
 
 
 
 
 | 150 | // If the source image is DXT1 but the target is not then we can convert here | 
 
 
 
 
 | 151 | // to a more flexible image format. | 
 
 
 
 
 | 152 | // | 
 
 
 
 
 | 153 |  | 
 
 
 
 
 | 154 | for (int i = 0; i < surfaces.Count; i++) | 
 
 
 
 
 | 155 | surfaces[i] = surfaces[i].Convert(SurfaceFormat.RGBA); | 
 
 
 
 
 | 156 |  | 
 
 
 
 
 | 157 | mainSurface = surfaces[0]; | 
 
 
 
 
 | 158 | } | 
 
 
 
 
 | 159 |  | 
 
 
 
 
 | 160 | if (!allowLargeTextures && (mainSurface.Width > 256 || mainSurface.Height > 256)) | 
 
 
 
 
 | 161 | { | 
 
 
 
 
 | 162 | if (surfaces.Count == 1) | 
 
 
 
 
 | 163 | { | 
 
 
 
 
 | 164 | int sx = mainSurface.Width / 256; | 
 
 
 
 
 | 165 | int sy = mainSurface.Height / 256; | 
 
 
 
 
 | 166 |  | 
 
 
 
 
 | 167 | int s = Math.Max(sx, sy); | 
 
 
 
 
 | 168 |  | 
 
 
 
 
 | 169 | mainSurface = mainSurface.Resize(mainSurface.Width / s, mainSurface.Height / s); | 
 
 
 
 
 | 170 | surfaces[0] = mainSurface; | 
 
 
 
 
 | 171 | } | 
 
 
 
 
 | 172 | else | 
 
 
 
 
 | 173 | { | 
 
 
 
 
 | 174 | while (surfaces.Count > 0 && (surfaces[0].Width > 256 || surfaces[0].Height > 256)) | 
 
 
 
 
 | 175 | surfaces.RemoveAt(0); | 
 
 
 
 
 | 176 |  | 
 
 
 
 
 | 177 | mainSurface = surfaces[0]; | 
 
 
 
 
 | 178 | } | 
 
 
 
 
 | 179 | } | 
 
 
 
 
 | 180 |  | 
 
 
 
 
 | 181 | if (surfaces.Count == 1 && !noMipMaps && mainSurface.Format != SurfaceFormat.DXT1) | 
 
 
 
 
 | 182 | { | 
 
 
 
 
 | 183 | var surface = mainSurface; | 
 
 
 
 
 | 184 |  | 
 
 
 
 
 | 185 | while (surface.Width > 1 || surface.Height > 1) | 
 
 
 
 
 | 186 | { | 
 
 
 
 
 | 187 | int width = Math.Max(surface.Width >> 1, 1); | 
 
 
 
 
 | 188 | int height = Math.Max(surface.Height >> 1, 1); | 
 
 
 
 
 | 189 |  | 
 
 
 
 
 | 190 | surface = surface.Resize(width, height); | 
 
 
 
 
 | 191 |  | 
 
 
 
 
 | 192 | surfaces.Add(surface); | 
 
 
 
 
 | 193 | } | 
 
 
 
 
 | 194 | } | 
 
 
 
 
 | 195 |  | 
 
 
 
 
 | 196 | var convertToFormat = mainSurface.Format; | 
 
 
 
 
 | 197 |  | 
 
 
 
 
 | 198 | if (defaultFormat != null) | 
 
 
 
 
 | 199 | { | 
 
 
 
 
 | 200 | texture.Format = defaultFormat.Value; | 
 
 
 
 
 | 201 | convertToFormat = texture.Format.ToSurfaceFormat(); | 
 
 
 
 
 | 202 | } | 
 
 
 
 
 | 203 | else | 
 
 
 
 
 | 204 | { | 
 
 
 
 
 | 205 | switch (mainSurface.Format) | 
 
 
 
 
 | 206 | { | 
 
 
 
 
 | 207 | case SurfaceFormat.BGRA4444: | 
 
 
 
 
 | 208 | texture.Format = TextureFormat.BGRA4444; | 
 
 
 
 
 | 209 | break; | 
 
 
 
 
 | 210 |  | 
 
 
 
 
 | 211 | case SurfaceFormat.BGRX5551: | 
 
 
 
 
 | 212 | texture.Format = TextureFormat.BGR555; | 
 
 
 
 
 | 213 | break; | 
 
 
 
 
 | 214 |  | 
 
 
 
 
 | 215 | case SurfaceFormat.BGR565: | 
 
 
 
 
 | 216 | texture.Format = TextureFormat.BGR555; | 
 
 
 
 
 | 217 | break; | 
 
 
 
 
 | 218 |  | 
 
 
 
 
 | 219 | case SurfaceFormat.BGRA5551: | 
 
 
 
 
 | 220 | texture.Format = TextureFormat.BGRA5551; | 
 
 
 
 
 | 221 | break; | 
 
 
 
 
 | 222 |  | 
 
 
 
 
 | 223 | case SurfaceFormat.BGRX: | 
 
 
 
 
 | 224 | case SurfaceFormat.RGBX: | 
 
 
 
 
 | 225 | texture.Format = TextureFormat.BGR; | 
 
 
 
 
 | 226 | convertToFormat = SurfaceFormat.BGRX; | 
 
 
 
 
 | 227 | break; | 
 
 
 
 
 | 228 |  | 
 
 
 
 
 | 229 | case SurfaceFormat.BGRA: | 
 
 
 
 
 | 230 | case SurfaceFormat.RGBA: | 
 
 
 
 
 | 231 | // | 
 
 
 
 
 | 232 | // We don't do RGBA32 unless specifically required because | 
 
 
 
 
 | 233 | // Oni needs a patch to support it. | 
 
 
 
 
 | 234 | // | 
 
 
 
 
 | 235 | texture.Format = TextureFormat.BGRA4444; | 
 
 
 
 
 | 236 | convertToFormat = SurfaceFormat.BGRA4444; | 
 
 
 
 
 | 237 | break; | 
 
 
 
 
 | 238 |  | 
 
 
 
 
 | 239 | case SurfaceFormat.DXT1: | 
 
 
 
 
 | 240 | texture.Format = TextureFormat.DXT1; | 
 
 
 
 
 | 241 | break; | 
 
 
 
 
 | 242 |  | 
 
 
 
 
 | 243 | default: | 
 
 
 
 
 | 244 | throw new NotSupportedException(string.Format("Image format {0} cannot be imported", mainSurface.Format)); | 
 
 
 
 
 | 245 | } | 
 
 
 
 
 | 246 | } | 
 
 
 
 
 | 247 |  | 
 
 
 
 
 | 248 | if (convertToFormat != mainSurface.Format) | 
 
 
 
 
 | 249 | { | 
 
 
 
 
 | 250 | for (int i = 0; i < surfaces.Count; i++) | 
 
 
 
 
 | 251 | surfaces[i] = surfaces[i].Convert(convertToFormat); | 
 
 
 
 
 | 252 |  | 
 
 
 
 
 | 253 | mainSurface = surfaces[0]; | 
 
 
 
 
 | 254 | } | 
 
 
 
 
 | 255 |  | 
 
 
 
 
 | 256 | if (texture.Format != TextureFormat.RGBA) | 
 
 
 
 
 | 257 | texture.Flags |= TextureFlags.SwapBytes; | 
 
 
 
 
 | 258 |  | 
 
 
 
 
 | 259 | texture.Width = mainSurface.Width; | 
 
 
 
 
 | 260 | texture.Height = mainSurface.Height; | 
 
 
 
 
 | 261 | texture.Surfaces.AddRange(surfaces); | 
 
 
 
 
 | 262 | } | 
 
 
 
 
 | 263 | } | 
 
 
 
 
 | 264 | } |