using System; using System.Collections.Generic; using System.IO; namespace Oni { internal class DaeExporter : Exporter { private readonly bool noAnimation; private readonly List animationNames = new List(); private readonly string geometryName; private readonly string fileType; public DaeExporter(string[] args, InstanceFileManager fileManager, string outputDirPath, string fileType) : base(fileManager, outputDirPath) { foreach (string arg in args) { if (arg == "-noanim") noAnimation = true; else if (arg.StartsWith("-anim:", StringComparison.Ordinal)) animationNames.Add(arg.Substring(6)); else if (arg.StartsWith("-geom:", StringComparison.Ordinal)) geometryName = arg.Substring(6); } this.fileType = fileType; } protected override void ExportFile(string sourceFilePath) { string extension = Path.GetExtension(sourceFilePath); if (string.Equals(extension, ".xml", StringComparison.OrdinalIgnoreCase)) { var sceneExporter = new SceneExporter(InstanceFileManager, OutputDirPath); sceneExporter.ExportScene(sourceFilePath); return; } base.ExportFile(sourceFilePath); } protected override List GetSupportedDescriptors(InstanceFile file) { var descriptors = new List(); descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.ONCC)); descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.TRBS)); descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.M3GM)); descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.AKEV)); descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.OBAN)); descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.OFGA)); descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.ONWC)); return descriptors; } protected override void ExportInstance(InstanceDescriptor descriptor) { var tag = descriptor.Template.Tag; if (tag == TemplateTag.AKEV) { var mesh = Akira.AkiraDatReader.Read(descriptor); Akira.AkiraDaeWriter.Write(mesh, descriptor.Name, OutputDirPath, fileType); return; } var scene = new Dae.Scene(); scene.Name = descriptor.Name; var textureWriter = new Motoko.TextureDaeWriter(OutputDirPath); var geometryWriter = new Motoko.GeometryDaeWriter(textureWriter); var bodyWriter = new Totoro.BodyDaeWriter(geometryWriter); if (tag == TemplateTag.OFGA) ExportObjectGeometry(descriptor, scene, geometryWriter); else if (tag == TemplateTag.OBAN) ExportObjectAnimation(descriptor, scene, geometryWriter); else if (tag == TemplateTag.ONCC) ExportCharacterBody(descriptor, scene, bodyWriter); else if (tag == TemplateTag.TRBS) ExportCharacterBodySet(descriptor, scene, bodyWriter); else if (tag == TemplateTag.M3GM) ExportGeometry(descriptor, scene, geometryWriter); else if (tag == TemplateTag.ONWC) ExportWeaponGeometry(descriptor, scene, geometryWriter); if (scene.Nodes.Count > 0) { string filePath = Path.Combine(OutputDirPath, descriptor.Name + "." + fileType); Dae.Writer.WriteFile(filePath, scene); } } private void ExportObjectGeometry(InstanceDescriptor descriptor, Dae.Scene scene, Motoko.GeometryDaeWriter geometryWriter) { var geometry = Physics.ObjectDatReader.ReadObjectGeometry(descriptor); var root = new Dae.Node(); foreach (var objNode in geometry.Geometries) root.Nodes.Add(geometryWriter.WriteNode(objNode.Geometry, objNode.Geometry.Name)); scene.Nodes.Add(root); } private void ExportObjectAnimation(InstanceDescriptor descriptor, Dae.Scene scene, Motoko.GeometryDaeWriter geometryWriter) { var animation = Physics.ObjectDatReader.ReadAnimation(descriptor); Dae.Node node; if (geometryName == "camera") { node = new Dae.Node { Name = descriptor.Name + "_camera", Instances = { new Dae.CameraInstance { Target = new Dae.Camera { XFov = 45.0f, AspectRatio = 4.0f / 3.0f, ZNear = 1.0f, ZFar = 10000.0f } }} }; } else if (geometryName != null) { var file = InstanceFileManager.OpenFile(geometryName); if (file == null) { Console.Error.WriteLine("Cannot fine file {0}", geometryName); node = new Dae.Node(); } else { var geom = Motoko.GeometryDatReader.Read(file.Descriptors[0]); node = geometryWriter.WriteNode(geom, geom.Name); } } else { node = new Dae.Node(); } scene.Nodes.Add(node); ExportAnimation(node, new List(animation.Keys)); } private void ExportGeometry(InstanceDescriptor descriptor, Dae.Scene scene, Motoko.GeometryDaeWriter geometryWriter) { var animations = new List(animationNames.Count); foreach (string animationFilePath in animationNames) { var file = InstanceFileManager.OpenFile(animationFilePath); if (file == null) { Console.Error.WriteLine("Cannot find animation {0}", animationFilePath); continue; } animations.Add(Physics.ObjectDatReader.ReadAnimation(file.Descriptors[0])); } ExportGeometry(scene, geometryWriter, descriptor, animations); } private void ExportCharacterBodySet(InstanceDescriptor descriptor, Dae.Scene scene, Totoro.BodyDaeWriter bodyWriter) { var body = Totoro.BodyDatReader.Read(descriptor); var node = bodyWriter.Write(body, noAnimation, null); scene.Nodes.Add(node); } private void ExportCharacterBody(InstanceDescriptor descriptor, Dae.Scene scene, Totoro.BodyDaeWriter bodyWriter) { var animationName = animationNames.Count > 0 ? animationNames[0] : null; var characterClass = Game.CharacterClass.Read(descriptor, animationName); var body = Totoro.BodyDatReader.Read(characterClass.Body); var textures = characterClass.Textures; var pelvis = bodyWriter.Write(body, noAnimation, textures); scene.Nodes.Add(pelvis); var animation = noAnimation ? null : characterClass.Animation; if (animation != null) { var anim = Totoro.AnimationDatReader.Read(animation); Totoro.AnimationDaeWriter.Write(pelvis, anim); } } private void ExportWeaponGeometry(InstanceDescriptor descriptor, Dae.Scene scene, Motoko.GeometryDaeWriter geometryWriter) { var weaponClass = Game.WeaponClass.Read(descriptor); if (weaponClass.Geometry != null) ExportGeometry(weaponClass.Geometry, scene, geometryWriter); } private static void ExportGeometry(Dae.Scene scene, Motoko.GeometryDaeWriter geometryWriter, InstanceDescriptor m3gm, List animations) { var geometry = Motoko.GeometryDatReader.Read(m3gm); if (animations != null && animations.Count > 0) { geometry.HasTransform = true; geometry.Transform = Matrix.CreateScale(animations[0].Keys[0].Scale); } var node = geometryWriter.WriteNode(geometry, m3gm.Name); scene.Nodes.Add(node); if (animations != null && animations.Count > 0) { var frames = new List(); int offset = 0; foreach (var animation in animations) { foreach (var key in animation.Keys) { frames.Add(new Physics.ObjectAnimationKey { Translation = key.Translation, Rotation = key.Rotation, Time = key.Time + offset }); } offset += animation.Length; } ExportAnimation(node, frames); } } private static void ExportAnimation(Dae.Node node, List frames) { var times = new float[frames.Count]; var interpolations = new string[times.Length]; var positions = new Vector3[frames.Count]; var angles = new Vector3[frames.Count]; for (int i = 0; i < times.Length; ++i) times[i] = frames[i].Time / 60.0f; for (int i = 0; i < interpolations.Length; i++) interpolations[i] = "LINEAR"; for (int i = 0; i < frames.Count; i++) positions[i] = frames[i].Translation; for (int i = 0; i < frames.Count; i++) angles[i] = frames[i].Rotation.ToEulerXYZ(); var translate = node.Transforms.Translate("translate", positions[0]); var rotateX = node.Transforms.Rotate("rotX", Vector3.UnitX, angles[0].X); var rotateY = node.Transforms.Rotate("rotY", Vector3.UnitY, angles[0].Y); var rotateZ = node.Transforms.Rotate("rotZ", Vector3.UnitZ, angles[0].Z); WriteSampler(times, interpolations, i => positions[i].X, translate, "X"); WriteSampler(times, interpolations, i => positions[i].Y, translate, "Y"); WriteSampler(times, interpolations, i => positions[i].Z, translate, "Z"); WriteSampler(times, interpolations, i => angles[i].X, rotateX, "ANGLE"); WriteSampler(times, interpolations, i => angles[i].Y, rotateY, "ANGLE"); WriteSampler(times, interpolations, i => angles[i].Z, rotateZ, "ANGLE"); } private static void WriteSampler(float[] times, string[] interpolations, Func getValue, Dae.Transform transform, string targetName) { var values = new float[times.Length]; for (int i = 0; i < values.Length; ++i) values[i] = getValue(i); transform.BindAnimation(targetName, new Dae.Sampler { Inputs = { new Dae.Input(Dae.Semantic.Input, new Dae.Source(times, 1)), new Dae.Input(Dae.Semantic.Output, new Dae.Source(values, 1)), new Dae.Input(Dae.Semantic.Interpolation, new Dae.Source(interpolations, 1)) } }); } } }