using System; using System.Collections.Generic; using System.Globalization; using System.Xml; using Oni.Imaging; namespace Oni.Xml { using Oni.Particles; internal class ParticleXmlExporter : ParticleXml { #region Private data private Particle particle; private XmlWriter xml; #endregion private ParticleXmlExporter(Particle particle, XmlWriter writer) { this.particle = particle; this.xml = writer; } public static void Export(string name, BinaryReader rawReader, XmlWriter xml) { var particle = new Oni.Particles.Particle(rawReader); xml.WriteStartElement("Particle"); xml.WriteAttributeString("Name", name); var exporter = new ParticleXmlExporter(particle, xml); exporter.Write(); xml.WriteEndElement(); } public void Write() { WriteOptions(); WriteProperties(); WriteAppearance(); WriteAttractor(); WriteVariables(); WriteEmitters(); WriteEvents(); } private void WriteProperties() { xml.WriteStartElement("Properties"); int flagValue = 0x00001000; for (int i = 0; i < 13; i++, flagValue <<= 1) WriteFlag1((ParticleFlags1)flagValue); xml.WriteEndElement(); } private void WriteOptions() { xml.WriteStartElement("Options"); WriteValue(particle.Lifetime, "Lifetime"); xml.WriteElementString("DisableDetailLevel", particle.DisableDetailLevel.ToString()); foreach (ParticleFlags1 flag in optionFlags1) WriteFlag1(flag); foreach (ParticleFlags2 flag in optionFlags2) WriteFlag2(flag); WriteValue(particle.CollisionRadius, "CollisionRadius"); xml.WriteElementString("AIDodgeRadius", XmlConvert.ToString(particle.AIDodgeRadius)); xml.WriteElementString("AIAlertRadius", XmlConvert.ToString(particle.AIAlertRadius)); xml.WriteElementString("FlyBySoundName", particle.FlyBySoundName); xml.WriteEndElement(); } private void WriteAttractor() { var attractor = particle.Attractor; xml.WriteStartElement("Attractor"); xml.WriteElementString("Target", attractor.Target.ToString()); xml.WriteElementString("Selector", attractor.Selector.ToString()); xml.WriteElementString("Class", attractor.ClassName); WriteValue(attractor.MaxDistance, "MaxDistance"); WriteValue(attractor.MaxAngle, "MaxAngle"); WriteValue(attractor.AngleSelectMin, "AngleSelectMin"); WriteValue(attractor.AngleSelectMax, "AngleSelectMax"); WriteValue(attractor.AngleSelectWeight, "AngleSelectWeight"); xml.WriteEndElement(); } private void WriteAppearance() { var appearance = particle.Appearance; xml.WriteStartElement("Appearance"); if ((particle.Flags1 & ParticleFlags1.Geometry) != 0) { xml.WriteElementString("DisplayType", "Geometry"); xml.WriteElementString("TexGeom", appearance.TextureName); } else if ((particle.Flags2 & ParticleFlags2.Vector) != 0) { xml.WriteElementString("DisplayType", "Vector"); } else if ((particle.Flags2 & ParticleFlags2.Decal) != 0) { xml.WriteElementString("DisplayType", "Decal"); xml.WriteElementString("TexGeom", appearance.TextureName); } else { xml.WriteElementString("DisplayType", particle.SpriteType.ToString()); xml.WriteElementString("TexGeom", appearance.TextureName); } foreach (ParticleFlags1 flag in appearanceFlags1) WriteFlag1(flag); foreach (ParticleFlags2 flag in appearanceFlags2) WriteFlag2(flag); WriteFlag1(ParticleFlags1.ScaleToVelocity); WriteValue(appearance.Scale, "Scale"); WriteFlag1(ParticleFlags1.UseSeparateYScale); WriteValue(appearance.YScale, "YScale"); WriteValue(appearance.Rotation, "Rotation"); WriteValue(appearance.Alpha, "Alpha"); WriteValue(appearance.XOffset, "XOffset"); WriteValue(appearance.XShorten, "XShorten"); WriteFlag2(ParticleFlags2.UseSpecialTint); WriteValue(appearance.Tint, "Tint"); WriteFlag2(ParticleFlags2.FadeOutOnEdge); WriteFlag2(ParticleFlags2.OneSidedEdgeFade); WriteValue(appearance.EdgeFadeMin, "EdgeFadeMin"); WriteValue(appearance.EdgeFadeMax, "EdgeFadeMax"); WriteValue(appearance.MaxContrail, "MaxContrailDistance"); WriteFlag2(ParticleFlags2.LensFlare); WriteValue(appearance.LensFlareDistance, "LensFlareDistance"); xml.WriteElementString("LensFlareFadeInFrames", XmlConvert.ToString(appearance.LensFlareFadeInFrames)); xml.WriteElementString("LensFlareFadeOutFrames", XmlConvert.ToString(appearance.LensFlareFadeOutFrames)); WriteFlag2(ParticleFlags2.DecalFullBrightness); xml.WriteElementString("MaxDecals", XmlConvert.ToString(appearance.MaxDecals)); xml.WriteElementString("DecalFadeFrames", XmlConvert.ToString(appearance.DecalFadeFrames)); WriteValue(appearance.DecalWrapAngle, "DecalWrapAngle"); xml.WriteEndElement(); } private void WriteFlag1(ParticleFlags1 flag) { xml.WriteElementString(flag.ToString(), ((particle.Flags1 & flag) != 0) ? "true" : "false"); } private void WriteFlag2(ParticleFlags2 flag) { xml.WriteElementString(flag.ToString(), ((particle.Flags2 & flag) != 0) ? "true" : "false"); } private void WriteVariables() { xml.WriteStartElement("Variables"); foreach (Variable variable in particle.Variables) WriteVariable(variable); xml.WriteEndElement(); } private void WriteVariable(Variable variable) { switch (variable.StorageType) { case StorageType.Float: xml.WriteStartElement("Float"); break; case StorageType.Color: xml.WriteStartElement("Color"); break; case StorageType.PingPongState: xml.WriteStartElement("PingPongState"); break; } xml.WriteAttributeString("Name", variable.Name); WriteValue(variable.Value); xml.WriteEndElement(); } private void WriteEmitters() { xml.WriteStartElement("Emitters"); foreach (Emitter e in particle.Emitters) WriteEmitter(e); xml.WriteEndElement(); } private void WriteEmitter(Emitter emitter) { xml.WriteStartElement("Emitter"); xml.WriteElementString("Class", emitter.ParticleClass); xml.WriteElementString("Flags", (emitter.Flags == EmitterFlags.None) ? string.Empty : emitter.Flags.ToString().Replace(",", string.Empty)); xml.WriteElementString("TurnOffTreshold", XmlConvert.ToString(emitter.TurnOffTreshold)); xml.WriteElementString("Probability", XmlConvert.ToString(emitter.Probability / 65535.0f)); xml.WriteElementString("Copies", XmlConvert.ToString(emitter.Copies)); switch (emitter.LinkTo) { case 0: xml.WriteElementString("LinkTo", string.Empty); break; case 1: xml.WriteElementString("LinkTo", "this"); break; case 10: xml.WriteElementString("LinkTo", "link"); break; default: xml.WriteElementString("LinkTo", XmlConvert.ToString(emitter.LinkTo - 2)); break; } WriteEmitterRate(emitter); WriteEmitterPosition(emitter); WriteEmitterDirection(emitter); WriteEmitterSpeed(emitter); xml.WriteElementString("Orientation", emitter.OrientationDir.ToString()); xml.WriteElementString("OrientationUp", emitter.OrientationUp.ToString()); xml.WriteEndElement(); } private void WriteEmitterRate(Emitter emitter) { xml.WriteStartElement("Rate"); xml.WriteStartElement(emitter.Rate.ToString()); switch (emitter.Rate) { case EmitterRate.Continous: WriteValue(emitter.Parameters[0], "Interval"); break; case EmitterRate.Random: WriteValue(emitter.Parameters[0], "MinInterval"); WriteValue(emitter.Parameters[1], "MaxInterval"); break; case EmitterRate.Distance: WriteValue(emitter.Parameters[0], "Distance"); break; case EmitterRate.Attractor: WriteValue(emitter.Parameters[0], "RechargeTime"); WriteValue(emitter.Parameters[1], "CheckInterval"); break; } xml.WriteEndElement(); xml.WriteEndElement(); } private void WriteEmitterPosition(Emitter emitter) { xml.WriteStartElement("Position"); xml.WriteStartElement(emitter.Position.ToString()); switch (emitter.Position) { case EmitterPosition.Line: WriteValue(emitter.Parameters[2], "Radius"); break; case EmitterPosition.Circle: case EmitterPosition.Sphere: WriteValue(emitter.Parameters[2], "InnerRadius"); WriteValue(emitter.Parameters[3], "OuterRadius"); break; case EmitterPosition.Offset: WriteValue(emitter.Parameters[2], "X"); WriteValue(emitter.Parameters[3], "Y"); WriteValue(emitter.Parameters[4], "Z"); break; case EmitterPosition.Cylinder: WriteValue(emitter.Parameters[2], "Height"); WriteValue(emitter.Parameters[3], "InnerRadius"); WriteValue(emitter.Parameters[4], "OuterRadius"); break; case EmitterPosition.BodySurface: case EmitterPosition.BodyBones: WriteValue(emitter.Parameters[2], "OffsetRadius"); break; } xml.WriteEndElement(); xml.WriteEndElement(); } private void WriteEmitterDirection(Emitter emitter) { xml.WriteStartElement("Direction"); xml.WriteStartElement(emitter.Direction.ToString()); switch (emitter.Direction) { case EmitterDirection.Cone: WriteValue(emitter.Parameters[5], "Angle"); WriteValue(emitter.Parameters[6], "CenterBias"); break; case EmitterDirection.Ring: WriteValue(emitter.Parameters[5], "Angle"); WriteValue(emitter.Parameters[6], "Offset"); break; case EmitterDirection.Offset: WriteValue(emitter.Parameters[5], "X"); WriteValue(emitter.Parameters[6], "Y"); WriteValue(emitter.Parameters[7], "Z"); break; case EmitterDirection.Inaccurate: WriteValue(emitter.Parameters[5], "BaseAngle"); WriteValue(emitter.Parameters[6], "Inaccuracy"); WriteValue(emitter.Parameters[7], "CenterBias"); break; } xml.WriteEndElement(); xml.WriteEndElement(); } private void WriteEmitterSpeed(Emitter emitter) { xml.WriteStartElement("Speed"); xml.WriteStartElement(emitter.Speed.ToString()); switch (emitter.Speed) { case EmitterSpeed.Uniform: WriteValue(emitter.Parameters[8], "Speed"); break; case EmitterSpeed.Stratified: WriteValue(emitter.Parameters[8], "Speed1"); WriteValue(emitter.Parameters[9], "Speed2"); break; } xml.WriteEndElement(); xml.WriteEndElement(); } private void WriteEvents() { xml.WriteStartElement("Events"); foreach (Event e in particle.Events) WriteEvent(e); xml.WriteEndElement(); } private void WriteEvent(Event e) { if (e.Actions.Count == 0) return; xml.WriteStartElement(e.Type.ToString()); foreach (EventAction action in e.Actions) WriteAction(action); xml.WriteEndElement(); } private void WriteAction(EventAction action) { int actionTypeIndex = (int)action.Type; if (actionTypeIndex >= eventActionInfoTable.Length || eventActionInfoTable[actionTypeIndex] == null) { Console.Error.WriteLine("ParticleXmlExporter: Unknown event action type {0}, ignoring", actionTypeIndex); return; } xml.WriteStartElement(action.Type.ToString()); EventActionInfo info = eventActionInfoTable[actionTypeIndex]; int i = 0; foreach (VariableReference variable in action.Variables) { if (i < info.Parameters.Length) { xml.WriteElementString(info.Parameters[i++].Name, variable.Name); } } foreach (Value value in action.Parameters) { if (i < info.Parameters.Length) { xml.WriteStartElement(info.Parameters[i++].Name); WriteValue(value); xml.WriteEndElement(); } } xml.WriteEndElement(); } private void WriteValue(Value value, string name) { xml.WriteStartElement(name); WriteValue(value); xml.WriteEndElement(); } private void WriteValue(Value value) { if (value == null) return; switch (value.Type) { case ValueType.Variable: case ValueType.InstanceName: xml.WriteString(value.Name); break; case ValueType.Float: xml.WriteString(XmlConvert.ToString(value.Float1)); break; case ValueType.FloatRandom: xml.WriteStartElement("Random"); xml.WriteAttributeString("Min", XmlConvert.ToString(value.Float1)); xml.WriteAttributeString("Max", XmlConvert.ToString(value.Float2)); xml.WriteEndElement(); break; case ValueType.FloatBellCurve: xml.WriteStartElement("BellCurve"); xml.WriteAttributeString("Mean", XmlConvert.ToString(value.Float1)); xml.WriteAttributeString("StdDev", XmlConvert.ToString(value.Float2)); xml.WriteEndElement(); break; case ValueType.Color: WriteColor(value.Color1); break; case ValueType.ColorRandom: xml.WriteStartElement("Random"); WriteColorAttribute("Min", value.Color1); WriteColorAttribute("Max", value.Color2); xml.WriteEndElement(); break; case ValueType.ColorBellCurve: xml.WriteStartElement("BellCurve"); WriteColorAttribute("Mean", value.Color1); WriteColorAttribute("StdDev", value.Color2); xml.WriteEndElement(); break; case ValueType.Int32: xml.WriteString(XmlConvert.ToString(value.Int)); break; case ValueType.TimeCycle: xml.WriteStartElement("TimeCycle"); xml.WriteAttributeString("Length", XmlConvert.ToString(value.Float1)); xml.WriteAttributeString("Scale", XmlConvert.ToString(value.Float2)); xml.WriteEndElement(); break; } } private void WriteColor(Color color) { if (color.A == 255) xml.WriteValue(string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", color.R, color.G, color.B)); else xml.WriteValue(string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", color.R, color.G, color.B, color.A)); } private void WriteColorAttribute(string name, Color color) { if (color.A == 255) xml.WriteAttributeString(name, string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", color.R, color.G, color.B)); else xml.WriteAttributeString(name, string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", color.R, color.G, color.B, color.A)); } } }