using System; using System.Collections.Generic; using System.IO; using System.Xml; namespace Oni.Level { using Akira; using Metadata; using Motoko; using Physics; partial class LevelImporter { private List roomScenes; private PolygonMesh model; private AkiraDaeReader daeReader; private void ReadModel(XmlReader xml, string basePath) { xml.ReadStartElement("Environment"); xml.ReadStartElement("Model"); daeReader = new AkiraDaeReader(); model = daeReader.Mesh; level.model = model; info.WriteLine("Reading environment..."); while (xml.IsStartElement()) { switch (xml.LocalName) { case "Import": case "Scene": ImportModelScene(xml, basePath); break; case "Object": xml.Skip(); break; case "Camera": ReadCamera(xml, basePath); break; case "Texture": textureImporter.ReadOptions(xml, basePath); break; default: error.WriteLine("Unknown element {0}", xml.LocalName); xml.Skip(); break; } } info.WriteLine("Reading rooms..."); roomScenes = new List(); xml.ReadEndElement(); xml.ReadStartElement("Rooms"); while (xml.IsStartElement("Import")) { string filePath = xml.GetAttribute("Path"); if (filePath == null) filePath = xml.ReadElementContentAsString(); else xml.Skip(); filePath = Path.Combine(basePath, filePath); roomScenes.Add(LoadScene(filePath)); } xml.ReadEndElement(); if (xml.IsStartElement("Textures")) ReadTextures(xml, basePath); xml.ReadEndElement(); } private class NodePropertiesReader { private readonly string basePath; private readonly TextWriter error; public readonly Dictionary properties = new Dictionary(StringComparer.Ordinal); public NodePropertiesReader(string basePath, TextWriter error) { this.basePath = basePath; this.error = error; } public Dictionary Properties { get { return properties; } } public void ReadScene(XmlReader xml, Dae.Node scene) { var nodeProperties = new ObjectDaeNodeProperties(); properties.Add(scene.Id, nodeProperties); while (xml.IsStartElement()) { switch (xml.LocalName) { case "GunkFlags": nodeProperties.GunkFlags = xml.ReadElementContentAsEnum(); break; case "ScriptId": nodeProperties.ScriptId = xml.ReadElementContentAsInt(); break; case "Node": ReadNode(xml, scene, nodeProperties); break; default: xml.Skip(); break; } } } private void ReadNode(XmlReader xml, Dae.Node parentNode, ObjectDaeNodeProperties parentNodeProperties) { string id = xml.GetAttribute("Id"); if (string.IsNullOrEmpty(id)) { error.Write("Each import node must have an Id attribute"); xml.Skip(); return; } var nodeProperties = new ObjectDaeNodeProperties { GunkFlags = parentNodeProperties.GunkFlags, ScriptId = parentNodeProperties.ScriptId, HasPhysics = parentNodeProperties.HasPhysics }; properties.Add(id, nodeProperties); xml.ReadStartElement("Node"); while (xml.IsStartElement()) { switch (xml.LocalName) { case "GunkFlags": nodeProperties.GunkFlags |= xml.ReadElementContentAsEnum(); break; case "ScriptId": nodeProperties.ScriptId = xml.ReadElementContentAsInt(); break; case "Physics": nodeProperties.PhysicsType = xml.ReadElementContentAsEnum(); nodeProperties.HasPhysics = true; break; case "ObjectFlags": nodeProperties.ObjectFlags = xml.ReadElementContentAsEnum(); nodeProperties.HasPhysics = true; break; case "Animation": nodeProperties.Animations.Add(ReadAnimationClip(xml)); nodeProperties.HasPhysics = true; break; case "Particles": nodeProperties.Particles.AddRange(ReadParticles(xml, basePath)); nodeProperties.HasPhysics = true; break; default: error.WriteLine("Unknown physics object element {0}", xml.LocalName); xml.Skip(); break; } } xml.ReadEndElement(); } private ObjectAnimationClip ReadAnimationClip(XmlReader xml) { var animClip = new ObjectAnimationClip(xml.GetAttribute("Name")); if (!xml.SkipEmpty()) { xml.ReadStartElement(); while (xml.IsStartElement()) { switch (xml.LocalName) { case "Start": animClip.Start = xml.ReadElementContentAsInt(); break; case "Stop": animClip.Stop = xml.ReadElementContentAsInt(); break; case "End": animClip.End = xml.ReadElementContentAsInt(); break; case "Flags": animClip.Flags = xml.ReadElementContentAsEnum(); break; default: error.WriteLine("Unknown object animation property {0}", xml.LocalName); xml.Skip(); break; } } xml.ReadEndElement(); } return animClip; } } private void ImportModelScene(XmlReader xml, string basePath) { var filePath = Path.Combine(basePath, xml.GetAttribute("Path")); var scene = LoadScene(filePath); var propertiesReader = new NodePropertiesReader(basePath, error); if (!xml.SkipEmpty()) { xml.ReadStartElement(); propertiesReader.ReadScene(xml, scene); xml.ReadEndElement(); } daeReader.ReadScene(scene, propertiesReader.Properties); if (propertiesReader.Properties.Values.Any(p => p.HasPhysics)) { var imp = new ObjectDaeImporter(textureImporter, propertiesReader.Properties); imp.Import(scene); foreach (var node in imp.Nodes.Where(n => n.Geometries.Length > 0)) { var setup = new ObjectSetup { Name = node.Name, FileName = node.FileName, ScriptId = node.ScriptId, Flags = node.Flags, PhysicsType = ObjectPhysicsType.Animated }; setup.Geometries = node.Geometries .Where(n => (n.Flags & GunkFlags.Invisible) == 0) .Select(n => n.Geometry.Name).ToArray(); foreach (var nodeGeometry in node.Geometries.Where(g => (g.Flags & GunkFlags.Invisible) == 0)) { var writer = new DatWriter(); GeometryDatWriter.Write(nodeGeometry.Geometry, writer.ImporterFile); writer.Write(outputDirPath); } setup.Position = Vector3.Zero; setup.Orientation = Quaternion.Identity; setup.Scale = 1.0f; setup.Origin = Matrix.CreateFromQuaternion(setup.Orientation) * Matrix.CreateScale(setup.Scale) * Matrix.CreateTranslation(setup.Position); //int i = 0; foreach (var animation in node.Animations) { //if (nodes.Count > 1) // animation.Name += i.ToString("d2", CultureInfo.InvariantCulture); if ((animation.Flags & ObjectAnimationFlags.Local) == 0) { //animation.Scale = Matrix.CreateScale(setup.Scale); foreach (var key in animation.Keys) { key.Rotation = setup.Orientation * key.Rotation; key.Translation += setup.Position; } } if ((animation.Flags & ObjectAnimationFlags.AutoStart) != 0) { setup.Animation = animation; setup.PhysicsType = ObjectPhysicsType.Animated; } var writer = new DatWriter(); writer.BeginImport(); ObjectDatWriter.WriteAnimation(animation, writer); writer.Write(outputDirPath); } if (setup.Animation == null && node.Animations.Length > 0) { setup.Animation = node.Animations[0]; } if (setup.Animation != null) { var frame0 = setup.Animation.Keys[0]; setup.Scale = frame0.Scale.X; setup.Orientation = frame0.Rotation; setup.Position = frame0.Translation; } level.physics.Add(setup); } } } private void ImportModel(string basePath) { info.WriteLine("Importing objects..."); ImportGunkObjects(); info.WriteLine("Importing textures..."); ImportModelTextures(); info.WriteLine("Generating grids..."); string gridFilePath = Path.Combine(basePath, string.Format("temp/grids/{0}_grids.dae", level.name)); var gridBuilder = new RoomGridBuilder(roomScenes[0], model); gridBuilder.Build(); AkiraDaeWriter.WriteRooms(gridBuilder.Mesh, gridFilePath); daeReader.ReadScene(Dae.Reader.ReadFile(gridFilePath), new Dictionary()); info.WriteLine("Writing environment..."); var writer = new DatWriter(); AkiraDatWriter.Write(model, writer, level.name, debug); writer.Write(outputDirPath); } private void ImportGunkNode(int gunkId, Matrix transform, GunkFlags flags, Geometry geometry) { ImportGunk(gunkId, transform, flags, geometry, null); } private void ImportGunk(int gunkId, Matrix transform, GunkFlags flags, Geometry geometry, string textureName) { TextureFormat? textureFormat = null; if (geometry.Texture != null) { Texture texture = null; if (!geometry.Texture.IsPlaceholder) { texture = TextureDatReader.ReadInfo(geometry.Texture); } else { var txmp = FindSharedInstance(TemplateTag.TXMP, geometry.Texture.Name); if (txmp != null) texture = TextureDatReader.ReadInfo(txmp); } if (texture != null) textureFormat = texture.Format; } else { if (geometry.TextureName != null) { var options = textureImporter.GetOptions(geometry.TextureName, false); if (options != null) textureFormat = options.Format; } } switch (textureFormat) { case TextureFormat.BGRA4444: case TextureFormat.BGRA5551: case TextureFormat.RGBA: flags |= GunkFlags.Transparent | GunkFlags.TwoSided | GunkFlags.NoOcclusion; break; } Material material; if (!string.IsNullOrEmpty(textureName)) material = model.Materials.GetMaterial(textureName); else if (!string.IsNullOrEmpty(geometry.TextureName)) material = model.Materials.GetMaterial(geometry.TextureName); else if (geometry.Texture != null) material = model.Materials.GetMaterial(geometry.Texture.Name); else material = model.Materials.GetMaterial("NONE"); int pointIndexBase = model.Points.Count; int texCoordIndexBase = model.TexCoords.Count; model.Points.AddRange(Vector3.Transform(geometry.Points, ref transform)); model.TexCoords.AddRange(geometry.TexCoords); foreach (var quad in Quadify.Do(geometry)) { var pointIndices = new int[quad.Length]; var texCoordIndices = new int[quad.Length]; var colors = new Imaging.Color[quad.Length]; for (int j = 0; j < quad.Length; j++) { pointIndices[j] = pointIndexBase + quad[j]; texCoordIndices[j] = texCoordIndexBase + quad[j]; colors[j] = new Imaging.Color(207, 207, 207); } var poly = new Polygon(model, pointIndices) { TexCoordIndices = texCoordIndices, Colors = colors, Material = material, ObjectId = gunkId & 0xffffff, ObjectType = gunkId >> 24 }; poly.Flags |= flags; model.Polygons.Add(poly); } } private void ImportModelTextures() { int imported = 0; int copied = 0; var copy = new List(); foreach (var material in model.Polygons.Select(p => p.Material).Distinct()) { //if (material.IsMarker) // continue; if (File.Exists(material.ImageFilePath)) { var options = textureImporter.AddMaterial(material); if (options != null) material.Flags |= options.GunkFlags; imported++; } else { var txmp = FindSharedInstance(TemplateTag.TXMP, material.Name); if (txmp != null) copy.Add(txmp); } } Parallel.ForEach(copy, txmp => { var texture = TextureDatReader.Read(txmp); if ((texture.Flags & TextureFlags.HasMipMaps) == 0) { texture.GenerateMipMaps(); TextureDatWriter.Write(texture, outputDirPath); System.Threading.Interlocked.Increment(ref imported); } else { string sourceFilePath = txmp.File.FilePath; File.Copy(sourceFilePath, Path.Combine(outputDirPath, Path.GetFileName(sourceFilePath)), true); System.Threading.Interlocked.Increment(ref copied); } }); error.WriteLine("Imported {0} textures, copied {1} textures", imported, copied); } private ObjectAnimationClip ReadAnimationClip(XmlReader xml) { var anim = new ObjectAnimationClip(xml.GetAttribute("Name")); if (!xml.SkipEmpty()) { xml.ReadStartElement(); while (xml.IsStartElement()) { switch (xml.LocalName) { case "Start": anim.Start = xml.ReadElementContentAsInt(); break; case "Stop": anim.Stop = xml.ReadElementContentAsInt(); break; case "End": anim.End = xml.ReadElementContentAsInt(); break; case "Flags": anim.Flags = xml.ReadElementContentAsEnum(); break; default: error.WriteLine("Unknown object animation parameter {0}", xml.LocalName); xml.Skip(); break; } } xml.ReadEndElement(); } return anim; } } }