using System; using System.Collections.Generic; using System.Globalization; using System.IO; using Oni.Collections; using Oni.Imaging; namespace Oni.Akira { internal class RoomDaeWriter { #region Private data private readonly PolygonMesh source; private DaeSceneBuilder world; private static readonly string[] objectTypeNames = new[] { "", "char", "patr", "door", "flag", "furn", "", "", "part", "pwru", "sndg", "trgv", "weap", "trig", "turr", "cons", "cmbt", "mele", "neut" }; #endregion #region private class DaePolygon private class DaePolygon { private readonly Polygon source; private readonly Material material; private readonly int[] pointIndices; private readonly int[] texCoordIndices; private readonly int[] colorIndices; public DaePolygon(Polygon source, int[] pointIndices, int[] texCoordIndices, int[] colorIndices) { this.source = source; this.material = source.Material; this.pointIndices = pointIndices; this.texCoordIndices = texCoordIndices; this.colorIndices = colorIndices; } public DaePolygon(Material material, int[] pointIndices, int[] texCoordIndices) { this.material = material; this.pointIndices = pointIndices; this.texCoordIndices = texCoordIndices; } public Polygon Source => source; public Material Material => material; public int[] PointIndices => pointIndices; public int[] TexCoordIndices => texCoordIndices; public int[] ColorIndices => colorIndices; } #endregion #region private class DaeMeshBuilder private class DaeMeshBuilder { private readonly List polygons = new List(); private readonly List points = new List(); private readonly Dictionary uniquePoints = new Dictionary(); private readonly List texCoords = new List(); private readonly Dictionary uniqueTexCoords = new Dictionary(); private readonly List colors = new List(); private readonly Dictionary uniqueColors = new Dictionary(); private string name; private Vector3 translation; private Dae.Geometry geometry; public DaeMeshBuilder(string name) { this.name = name; } public string Name { get { return name; } set { name = value; } } public Vector3 Translation => translation; public void ResetTransform() { // // Attempt to un-bake the translation of the furniture // Vector3 center = BoundingSphere.CreateFromPoints(points).Center; BoundingBox bbox = BoundingBox.CreateFromPoints(points); center.Y = bbox.Min.Y; translation = center; for (int i = 0; i < points.Count; i++) points[i] -= center; } public void AddPolygon(Polygon polygon) { polygons.Add(new DaePolygon( polygon, Remap(polygon.Mesh.Points, polygon.PointIndices, points, uniquePoints), null, null)); } public IEnumerable Polygons => from p in polygons where p.Source != null select p.Source; private static int[] Remap(IList values, int[] indices, List list, Dictionary unique) where T : struct { var result = new int[indices.Length]; for (int i = 0; i < indices.Length; i++) result[i] = AddUnique(list, unique, values[indices[i]]); return result; } private static int[] Remap(IList values, List list, Dictionary unique) where T : struct { var result = new int[values.Count]; for (int i = 0; i < values.Count; i++) result[i] = AddUnique(list, unique, values[i]); return result; } private static int AddUnique(List list, Dictionary unique, T value) where T : struct { int index; if (!unique.TryGetValue(value, out index)) { index = list.Count; unique.Add(value, index); list.Add(value); } return index; } public void Build() { var positionSource = new Dae.Source(points); var primitives = new Dae.MeshPrimitives(Dae.MeshPrimitiveType.Polygons); var posInput = new Dae.IndexedInput(Dae.Semantic.Position, positionSource); primitives.Inputs.Add(posInput); foreach (var poly in polygons) { primitives.VertexCounts.Add(poly.PointIndices.Length); posInput.Indices.AddRange(poly.PointIndices); } geometry = new Dae.Geometry { Name = Name + "_geo", Vertices = { new Dae.Input(Dae.Semantic.Position, positionSource) }, Primitives = { primitives } }; } public Dae.Geometry Geometry { get { return geometry; } } } #endregion #region private class DaeSceneBuilder private class DaeSceneBuilder { private readonly Dae.Scene scene; private readonly Dictionary nameMeshBuilder; private readonly List meshBuilders; private readonly Dictionary materials; private string imagesFolder = "images"; public DaeSceneBuilder() { scene = new Dae.Scene(); nameMeshBuilder = new Dictionary(StringComparer.Ordinal); meshBuilders = new List(); materials = new Dictionary(); } public string ImagesFolder { get { return imagesFolder; } set { imagesFolder = value; } } public DaeMeshBuilder GetMeshBuilder(string name) { DaeMeshBuilder result; if (!nameMeshBuilder.TryGetValue(name, out result)) { result = new DaeMeshBuilder(name); nameMeshBuilder.Add(name, result); meshBuilders.Add(result); } return result; } public IEnumerable MeshBuilders { get { return meshBuilders; } } public Dae.Material GetMaterial(Material material) { Dae.Material result; if (!materials.TryGetValue(material, out result)) { result = new Dae.Material(); materials.Add(material, result); } return result; } public void Build() { BuildNodes(); BuildMaterials(); } private void BuildNodes() { foreach (var meshBuilder in meshBuilders) { meshBuilder.Build(); var inst = new Dae.GeometryInstance(meshBuilder.Geometry); var node = new Dae.Node(); node.Name = meshBuilder.Name; node.Instances.Add(inst); if (meshBuilder.Translation != Vector3.Zero) node.Transforms.Add(new Dae.TransformTranslate(meshBuilder.Translation)); scene.Nodes.Add(node); } } private void BuildMaterials() { foreach (KeyValuePair pair in materials) { var material = pair.Key; var image = new Dae.Image { FilePath = "./" + GetImageFileName(material).Replace('\\', '/'), Name = material.Name + "_img" }; var effectSurface = new Dae.EffectSurface(image); var effectSampler = new Dae.EffectSampler(effectSurface) { //WrapS = texture.WrapU ? Dae.EffectSamplerWrap.Wrap : Dae.EffectSamplerWrap.None, //WrapT = texture.WrapV ? Dae.EffectSamplerWrap.Wrap : Dae.EffectSamplerWrap.None }; var effectTexture = new Dae.EffectTexture(effectSampler, "diffuse_TEXCOORD"); var effect = new Dae.Effect { Name = material.Name + "_fx", AmbientValue = Vector4.One, SpecularValue = Vector4.Zero, DiffuseValue = effectTexture, TransparentValue = material.Image.HasAlpha ? effectTexture : null, Parameters = { new Dae.EffectParameter("surface", effectSurface), new Dae.EffectParameter("sampler", effectSampler) } }; var daeMaterial = pair.Value; daeMaterial.Name = material.Name; daeMaterial.Effect = effect; } } private string GetImageFileName(Material material) { if (material.IsMarker) return Path.Combine("markers", material.Name + ".tga"); return Path.Combine(imagesFolder, material.Name + ".tga"); } public void Write(string filePath) { string outputDirPath = Path.GetDirectoryName(filePath); foreach (var material in materials.Keys) TgaWriter.Write(material.Image, Path.Combine(outputDirPath, GetImageFileName(material))); Dae.Writer.WriteFile(filePath, scene); } } #endregion public static void Write(PolygonMesh mesh, string filePath) { var writer = new RoomDaeWriter(mesh); writer.WriteGeometry(); writer.world.Write(filePath); } private RoomDaeWriter(PolygonMesh source) { this.source = source; } private void WriteGeometry() { world = new DaeSceneBuilder(); for (int i = 0; i < source.Polygons.Count; i++) { var polygon = source.Polygons[i]; var name = string.Format(CultureInfo.InvariantCulture, "floor_{0}", i); var meshBuilder = world.GetMeshBuilder(name); meshBuilder.AddPolygon(polygon); } foreach (var meshBuilder in world.MeshBuilders) { meshBuilder.ResetTransform(); } world.Build(); } } }