| 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 |
} |