static public FrameImageContent Tween(this FrameImageContent prev, FrameImageContent next, float pct) { FrameImageContent result = new FrameImageContent(); result.TextureFolder = prev.TextureFolder; result.TextureFile = prev.TextureFile; result.Clockwise = prev.Clockwise; result.Pivot = prev.Pivot; result.Parent = prev.Parent; result.TimelineId = prev.TimelineId; result.Scale.X = MathHelper.Lerp(prev.Scale.X, next.Scale.X, pct); result.Scale.Y = MathHelper.Lerp(prev.Scale.Y, next.Scale.Y, pct); result.Position.X = MathHelper.Lerp(prev.Position.X, next.Position.X, pct); result.Position.Y = MathHelper.Lerp(prev.Position.Y, next.Position.Y, pct); double angleB = prev.Clockwise ? ((next.Angle - prev.Angle < 0) ? (next.Angle + MathHelper.TwoPi) : next.Angle) : ((next.Angle - prev.Angle > 0) ? (next.Angle - MathHelper.TwoPi) : next.Angle); result.Angle = MathHelper.Lerp(prev.Angle, (float)angleB, pct); return(result); }
public static FrameImageContent FromObjectXml(XElement xml, ImageContent[][] textures, bool tweened) { int folderId = int.Parse(xml.Attribute("folder").Value); int fileId = int.Parse(xml.Attribute("file").Value); Vector2 pivot = Vector2.Zero; if (xml.Attribute("pivot_x") != null || xml.Attribute("pivot_y") != null) { pivot = new Vector2( GetFloatAttribute(xml, "pivot_x", 0) * textures[folderId][fileId].Dimensions.X, (1 - GetFloatAttribute(xml, "pivot_y", 1)) * textures[folderId][fileId].Dimensions.Y ); } else { try { pivot = textures[int.Parse(xml.Attribute("folder").Value)][int.Parse(xml.Attribute("file").Value)].Pivot; } catch { } } Vector2 position = new Vector2( GetFloatAttribute(xml, "x", 0), -GetFloatAttribute(xml, "y", 0) ); Vector2 scale = new Vector2( GetFloatAttribute(xml, "scale_x", 1), GetFloatAttribute(xml, "scale_y", 1) ); FrameImageContent frameImage = new FrameImageContent() { Angle = -MathHelper.ToRadians(GetFloatAttribute(xml, "angle", 0)), Pivot = pivot, Position = position, TextureFolder = folderId, TextureFile = fileId, TextureName = textures[folderId][fileId].TextureName, Clockwise = xml.Parent.Attribute("spin") != null && xml.Parent.Attribute("spin").Value == "-1", Tweened = tweened, ZIndex = GetInt32Attribute(xml, "z_index", 0), Scale = scale }; return(frameImage); }
static public FrameImageContent CloneFrameImage(this FrameImageContent fimg, int?zindex, int?timelineId, int?parent) { return(new FrameImageContent() { Angle = fimg.Angle, Clockwise = fimg.Clockwise, Pivot = fimg.Pivot, Position = fimg.Position, TextureFile = fimg.TextureFile, TextureFolder = fimg.TextureFolder, TextureName = fimg.TextureName, Tweened = fimg.Tweened, ZIndex = zindex.HasValue ? zindex.Value : fimg.ZIndex, TimelineId = timelineId.HasValue ? timelineId.Value : fimg.TimelineId, Parent = parent.HasValue ? parent.Value : fimg.Parent, Scale = fimg.Scale }); }
public override CharacterContent Process(XDocument input, ContentProcessorContext context) { // Prevent parsing issues based on culture (i.e., decimals vs. commas in numbers) CultureInfo culture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; CharacterContent character = new CharacterContent(); // Internal character.FramesPerSecond = AnimationFPS; // Textures // <folder> and <file> should be numbered in sequential order, so we shouldn't need // to get the id values here; <file> name attributes should also contain folder names //TODO: Future versions will support atlas images and sounds List <List <string> > textures = (from fld in input.Root.Elements("folder") select ( from file in fld.Elements("file") select file.Attribute("name").Value ).ToList() ).ToList(); character.Textures = (from fld in input.Root.Elements("folder") select ( from file in fld.Elements("file") select XmlHelpers.FromImageXml(file, Path.GetDirectoryName(context.OutputFilename.Remove(0, context.OutputDirectory.Length))) ).ToArray()).ToArray(); // Entities // Currently one entity per file, with no ID or name //TODO: Future versions may include more than one entity, with names // Animations List <AnimationContent> animations = new List <AnimationContent>(); foreach (XElement xanim in input.Root.Element("entity").Elements("animation")) { // Make a dictionary of external timelines which get pulled into the mainline Dictionary <int, Dictionary <int, List <FrameImageContent> > > objCache = new Dictionary <int, Dictionary <int, List <FrameImageContent> > >(); Dictionary <int, Dictionary <int, List <Bone> > > boneCache = new Dictionary <int, Dictionary <int, List <Bone> > >(); foreach (XElement tl in xanim.Elements("timeline")) { int tlId = int.Parse(tl.Attribute("id").Value); if (!objCache.ContainsKey(tlId)) { objCache[tlId] = new Dictionary <int, List <FrameImageContent> >(); } if (!boneCache.ContainsKey(tlId)) { boneCache[tlId] = new Dictionary <int, List <Bone> >(); } foreach (XElement key in tl.Elements("key")) { int objId = int.Parse(key.Attribute("id").Value); objCache[tlId][objId] = (from obj in key.Elements("object") select XmlHelpers.FromObjectXml(obj, character.Textures, true)).ToList(); boneCache[tlId][objId] = (from bone in key.Elements("bone") select XmlHelpers.FromBoneXml(bone)).ToList(); } } // Build the animation itself AnimationContent anim = new AnimationContent() { Name = xanim.Attribute("name").Value, Length = long.Parse(xanim.Attribute("length").Value), Looping = XmlHelpers.GetBoolAttribute(xanim, "looping", true), }; // Retrieve a list of keyframes from the mainline List <Keyframe> keyframes = new List <Keyframe>(); foreach (XElement xkey in xanim.Element("mainline").Elements("key")) { Keyframe key = new Keyframe(); key.Time = XmlHelpers.GetInt64Attribute(xkey, "time", 0); List <FrameImageContent> objs = new List <FrameImageContent>(); List <Bone> bones = new List <Bone>(); foreach (XElement xobj in xkey.DescendantNodes()) { if (xobj.Name == "object") { objs.Add(XmlHelpers.FromObjectXml(xobj, character.Textures, false)); } else if (xobj.Name == "bone") { bones.Add(XmlHelpers.FromBoneXml(xobj)); } else if (xobj.Name == "object_ref") { int tlId = int.Parse(xobj.Attribute("timeline").Value); int keyId = int.Parse(xobj.Attribute("key").Value); int zindex = int.Parse(xobj.Attribute("z_index").Value); int parentId = XmlHelpers.GetInt32Attribute(xobj, "parent", -1); foreach (FrameImageContent fimg in objCache[tlId][keyId]) { objs.Add(fimg.CloneFrameImage(zindex, tlId, parentId)); } } else if (xobj.Name == "bone_ref") { int tlId = int.Parse(xobj.Attribute("timeline").Value); int keyId = int.Parse(xobj.Attribute("key").Value); int parentId = XmlHelpers.GetInt32Attribute(xobj, "parent", -1); foreach (Bone bone in boneCache[tlId][keyId]) { bones.Add(bone.CloneBone(tlId, parentId)); } } } // Save to object key.Objects = objs.ToArray(); key.Bones = bones.OrderBy(x => x.Id).ToArray(); keyframes.Add(key); } if (keyframes[keyframes.Count - 1].Time < anim.Length) { keyframes.Add(keyframes[0].Clone(anim.Length)); } // Process into frames Int32 frameTimeStep = 1000 / AnimationFPS; List <FrameContent> frames = new List <FrameContent>(); for (int frameTime = 0; frameTime <= anim.Length; frameTime += frameTimeStep) { Keyframe currentFrame = (from kf in keyframes where kf.Time <= frameTime orderby kf.Time descending select kf). FirstOrDefault(); Keyframe nextFrame = (from kf in keyframes where kf.Time > frameTime orderby kf.Time ascending select kf). FirstOrDefault(); float timePct = (nextFrame != null) ? ((frameTime - currentFrame.Time) / (float)(nextFrame.Time - currentFrame.Time)) : 0; List <FrameImageContent> frameImages = new List <FrameImageContent>(); foreach (FrameImageContent currentFrameImage in currentFrame.Objects) { FrameImageContent nextFrameImage = (currentFrameImage.Tweened && nextFrame != null && nextFrame.Objects.Where(x => x.TimelineId == currentFrameImage.TimelineId).Any()) ? nextFrame.Objects.Where(x => x.TimelineId == currentFrameImage.TimelineId).Select(x => x).First() : null; // Tweening //TODO: Future versions will use tweening methods other than linear if (nextFrameImage != null) { frameImages.Add(currentFrameImage.Tween(nextFrameImage, timePct)); } // Select last frame else { frameImages.Add(currentFrameImage.CloneFrameImage()); } } List <Bone> bones = new List <Bone>(); foreach (Bone currentBone in currentFrame.Bones) { Bone nextBone = (nextFrame != null && nextFrame.Bones.Where(x => x.TimelineId == currentBone.TimelineId).Any()) ? nextFrame.Bones.Where(x => x.TimelineId == currentBone.TimelineId) .Select(x => x) .First() : null; // Tweening if (nextBone != null) { bones.Add(currentBone.Tween(nextBone, timePct)); } else { bones.Add(currentBone.CloneBone()); } } FrameContent frame = new FrameContent() { Objects = frameImages.OrderBy(x => x.ZIndex).ToArray(), Bones = bones.OrderBy(x => x.Id).ToArray() }; frames.Add(frame); } anim.Frames = frames.ToArray(); // Build timeline/texture lookup anim.TextureTimelines = new Dictionary <string, int>(); foreach (List <string> folder in textures) { foreach (string file in folder) { var textimeline = frames.SelectMany(x => x.Objects) .Where(x => x.TextureName != null && x.TextureName.Equals(file)) .Select(x => x.TimelineId); if (textimeline.Any()) { anim.TextureTimelines[file] = textimeline.First(); } } } // Build timeline/bone lookup anim.BoneTimelines = new Dictionary <string, int>(); foreach (Bone bone in frames.SelectMany(x => x.Bones)) { if (!String.IsNullOrEmpty(bone.Name) && !anim.BoneTimelines.ContainsKey(bone.Name)) { anim.BoneTimelines.Add(bone.Name, bone.TimelineId); } } animations.Add(anim); } character.Animations = new List <AnimationContent>(); character.Animations.AddRange(animations); // Build external textures foreach (List <string> folder in textures) { foreach (string txfile in folder) { String assetName = Path.Combine( Path.GetDirectoryName(context.OutputFilename.Remove(0, context.OutputDirectory.Length)), Path.GetFileNameWithoutExtension(context.OutputFilename), textures.IndexOf(folder).ToString("00"), folder.IndexOf(txfile).ToString("00")); String sourceName = Path.Combine( Path.GetDirectoryName(context.OutputFilename.Remove(0, context.OutputDirectory.Length)), txfile ); OpaqueDataDictionary data = new OpaqueDataDictionary(); data.Add("GenerateMipmaps", false); data.Add("ResizeToPowerOfTwo", false); data.Add("PremultiplyAlpha", PremultiplyAlpha); data.Add("TextureFormat", TextureFormat); context.BuildAsset <TextureContent, TextureContent>( new ExternalReference <TextureContent>(sourceName), "TextureProcessor", data, "TextureImporter", assetName); } } Thread.CurrentThread.CurrentCulture = culture; return(character); }
static public FrameImageContent CloneFrameImage(this FrameImageContent fimg) { return(fimg.CloneFrameImage(null, null, null)); }