using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Xml; using Oni.Imaging; using Oni.Metadata; using Oni.Xml; namespace Oni.Motoko { internal class TextureXmlImporter { private readonly XmlImporter importer; private readonly XmlReader xml; private readonly string filePath; public TextureXmlImporter(XmlImporter importer, XmlReader xml, string filePath) { this.importer = importer; this.xml = xml; this.filePath = filePath; } public void Import() { xml.ReadStartElement(); var name = Importer.DecodeFileName(Path.GetFileNameWithoutExtension(filePath)); var flags = MetaEnum.Parse(xml.ReadElementContentAsString("Flags", "")); var format = MetaEnum.Parse(xml.ReadElementContentAsString("Format", "")); int width = 0; int height = 0; int speed = 1; if (xml.IsStartElement("Width")) width = xml.ReadElementContentAsInt(); if (xml.IsStartElement("Height")) height = xml.ReadElementContentAsInt(); string envMapName = null; if (xml.IsStartElement("EnvMap")) { envMapName = xml.ReadElementContentAsString(); if (envMapName != null && envMapName.Length == 0) envMapName = null; } if (xml.IsStartElement("Speed")) speed = xml.ReadElementContentAsInt(); var imageFilePaths = new List(); var inputDirPath = Path.GetDirectoryName(filePath); while (xml.IsStartElement("Image")) { string imageFilePath = xml.ReadElementContentAsString(); if (!Path.IsPathRooted(imageFilePath)) imageFilePath = Path.Combine(inputDirPath, imageFilePath); if (!File.Exists(imageFilePath)) throw new IOException(string.Format("Could not find image file '{0}'", imageFilePath)); imageFilePaths.Add(imageFilePath); } xml.ReadEndElement(); var surfaces = new List(); foreach (string imageFilePath in imageFilePaths) surfaces.Add(TgaReader.Read(imageFilePath)); if (surfaces.Count == 0) throw new InvalidDataException("No images found. A texture must have at least one image."); int imageWidth = 0; int imageHeight = 0; foreach (Surface surface in surfaces) { if (imageWidth == 0) imageWidth = surface.Width; else if (imageWidth != surface.Width) throw new NotSupportedException("All animation frames must have the same size."); if (imageHeight == 0) imageHeight = surface.Height; else if (imageHeight != surface.Height) throw new NotSupportedException("All animation frames must have the same size."); } if (width == 0) width = imageWidth; else if (width > imageWidth) throw new NotSupportedException("Cannot upscale images."); if (height == 0) height = imageHeight; else if (height > imageHeight) throw new NotSupportedException("Cannot upscale images."); //if (envMapName != null && surfaces.Count > 1) // throw new NotSupportedException("Animated textures cannot have an environment map."); if (width != imageWidth || height != imageHeight) { for (int i = 0; i < surfaces.Count; i++) surfaces[i] = surfaces[i].Resize(width, height); } flags |= InstanceMetadata.TXMPFlags.SwapBytes; if (envMapName != null) flags |= InstanceMetadata.TXMPFlags.HasEnvMap; for (int i = 0; i < surfaces.Count; i++) { BinaryWriter writer; if (i == 0) writer = importer.BeginXmlInstance(TemplateTag.TXMP, name, i.ToString(CultureInfo.InvariantCulture)); else writer = importer.BeginXmlInstance(TemplateTag.TXMP, null, i.ToString(CultureInfo.InvariantCulture)); writer.Skip(128); writer.Write((int)flags); writer.WriteUInt16(width); writer.WriteUInt16(height); writer.Write((int)format); if (i == 0 && surfaces.Count > 1) writer.WriteInstanceId(surfaces.Count); else writer.Write(0); if (envMapName != null) writer.WriteInstanceId(surfaces.Count + ((surfaces.Count > 1) ? 1 : 0)); else writer.Write(0); writer.Write(importer.RawWriter.Align32()); writer.Skip(12); var mainSurface = surfaces[i]; var levels = new List { mainSurface }; if ((flags & InstanceMetadata.TXMPFlags.HasMipMaps) != 0) { int mipWidth = width; int mipHeight = height; while (mipWidth > 1 || mipHeight > 1) { mipWidth = Math.Max(mipWidth >> 1, 1); mipHeight = Math.Max(mipHeight >> 1, 1); levels.Add(mainSurface.Resize(mipWidth, mipHeight)); } } foreach (var level in levels) { var surface = level.Convert(TextureFormatToSurfaceFormat(format)); importer.RawWriter.Write(surface.Data); } importer.EndXmlInstance(); } if (surfaces.Count > 1) { var txan = importer.CreateInstance(TemplateTag.TXAN); using (var writer = txan.OpenWrite(12)) { writer.WriteInt16(speed); writer.WriteInt16(speed); writer.Write(0); writer.Write(surfaces.Count); writer.Write(0); for (int i = 1; i < surfaces.Count; i++) writer.WriteInstanceId(i); } } if (envMapName != null) { importer.CreateInstance(TemplateTag.TXMP, envMapName); } } private static SurfaceFormat TextureFormatToSurfaceFormat(InstanceMetadata.TXMPFormat format) { switch (format) { case InstanceMetadata.TXMPFormat.BGRA4444: return SurfaceFormat.BGRA4444; case InstanceMetadata.TXMPFormat.BGR555: return SurfaceFormat.BGRX5551; case InstanceMetadata.TXMPFormat.BGRA5551: return SurfaceFormat.BGRA5551; case InstanceMetadata.TXMPFormat.RGBA: return SurfaceFormat.RGBA; case InstanceMetadata.TXMPFormat.BGR: return SurfaceFormat.BGRX; case InstanceMetadata.TXMPFormat.DXT1: return SurfaceFormat.DXT1; default: throw new NotSupportedException(string.Format("Texture format {0} is not supported", format)); } } } }