389 lines
17 KiB
C#
389 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Xml.Linq;
|
|
using System.Xml.XPath;
|
|
using JD.Asset.Template.Utils;
|
|
using JD.Collections.Extensions;
|
|
using TeaBox.Macros;
|
|
using JD.Core;
|
|
using JD.Serialization;
|
|
using JD.Timeline.Model;
|
|
using JDSongConverter.Utils;
|
|
|
|
namespace JDSongConverter.Tasks
|
|
{
|
|
/// <summary>
|
|
/// Converts different deprecated timeline files to dance tape file
|
|
/// </summary>
|
|
public class ConvertTimelineToDtapeTask : Task
|
|
{
|
|
public string TimelineFile { get; set; }
|
|
public string DanceTapeFile { get; set; }
|
|
|
|
public void Init(string timelineFile, string danceTapeFile)
|
|
{
|
|
TimelineFile = timelineFile;
|
|
DanceTapeFile = danceTapeFile;
|
|
}
|
|
|
|
public override string Name
|
|
{
|
|
get { return "Convert timeline to dance tape"; }
|
|
set { }
|
|
}
|
|
|
|
public override string ProgressText
|
|
{
|
|
get { return "Converting timeline to dance tape"; }
|
|
}
|
|
|
|
public override TaskResult Process(IExecutionContext executionContext)
|
|
{
|
|
var tape = new Tape();
|
|
if (TimelineFile.EndsWith(".tpl", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
ImportTpl(tape, TimelineFile);
|
|
}
|
|
else if (TimelineFile.EndsWith(".tml", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
ImportTml(tape, TimelineFile);
|
|
}
|
|
tape.Save(DanceTapeFile);
|
|
|
|
return TaskResult.Ok;
|
|
}
|
|
|
|
private void ImportTpl(Tape tape, string fileName)
|
|
{
|
|
var serializer = new LuaSerializer();
|
|
var actorTemplate = (Actor_Template)serializer.LoadObjectFromFile(fileName);
|
|
if (actorTemplate == null)
|
|
return;
|
|
|
|
JD_Timeline_Template timeline = actorTemplate.COMPONENTS.OfType<JD_Timeline_Template>().FirstOrDefault();
|
|
if (timeline == null)
|
|
return;
|
|
|
|
tape.Id = DanceTapeFile;
|
|
tape.IsLoading = true;
|
|
tape.TapeClock = Tape.TapeClock_Enum.TapeClock_ConductorGameplay;
|
|
tape.MapName = Context.Song.Name;
|
|
|
|
//Import of tracks
|
|
foreach (var layer in timeline.Layers)
|
|
{
|
|
if (MoveTrack.NameValidationRegexHandOnly.IsMatch(layer.LayerName))
|
|
{
|
|
var moveTrack = tape.GetOrCreateTrack<MoveTrack>((uint)layer.LayerID);
|
|
moveTrack.MoveType = MotionClip.MoveType_Enum.MoveType_HandOnly;
|
|
moveTrack.CoachId = (MotionClip.CoachId_Enum)(Int32.Parse(MoveTrack.NameValidationRegexHandOnly.Match(layer.LayerName).Groups[2].Value) - 1);
|
|
moveTrack.Name = layer.LayerName;
|
|
}
|
|
else if (MoveTrack.NameValidationRegexTplKinect.IsMatch(layer.LayerName))
|
|
{
|
|
var moveTrack = tape.GetOrCreateTrack<MoveTrack>((uint)layer.LayerID);
|
|
moveTrack.MoveType = MotionClip.MoveType_Enum.MoveType_FullBody;
|
|
moveTrack.CoachId = (MotionClip.CoachId_Enum)(Int32.Parse(MoveTrack.NameValidationRegexTplKinect.Match(layer.LayerName).Groups[2].Value) - 1);
|
|
moveTrack.Name = "CameraMoves" + (int)(moveTrack.CoachId + 1);
|
|
}
|
|
else if (PictoTrack.NameValidationRegex.IsMatch(layer.LayerName))
|
|
{
|
|
var pictoTrack = tape.GetOrCreateTrack<PictoTrack>((uint)layer.LayerID);
|
|
pictoTrack.Name = layer.LayerName;
|
|
}
|
|
}
|
|
|
|
var goldMoveEventsTrack = tape.GetOrCreateTrack<GoldEffectTrack>(GoldEffectTrack.DefaultName);
|
|
|
|
//Import of move colors
|
|
var moveColors = new Dictionary<string, Color>();
|
|
foreach (var moveModel in timeline.MoveModels)
|
|
{
|
|
var color = Color.FromArgb((int)moveModel.Color);
|
|
moveColors[moveModel.Name] = Color.FromArgb(color.R, color.G, color.B);
|
|
}
|
|
|
|
uint clipId = 0;
|
|
|
|
//Import of standard moves
|
|
foreach (var move in timeline.Moves)
|
|
{
|
|
var clip = new MotionClip
|
|
{
|
|
Id = clipId++,
|
|
TrackId = (uint)move.LayerID,
|
|
StartBeat = move.StartPosition,
|
|
BeatDuration = move.StopPosition - move.StartPosition,
|
|
ClassifierPath = move.ClassifierPath,
|
|
MoveType = MotionClip.MoveType_Enum.MoveType_HandOnly,
|
|
GoldMove = move.GoldMove,
|
|
CoachId = tape.GetOrCreateTrack<MoveTrack>((uint)move.LayerID).CoachId,
|
|
Color = moveColors.ContainsKey(move.MoveName) ? moveColors[move.MoveName] : Color.WhiteSmoke
|
|
};
|
|
tape.Clips.Add(clip);
|
|
}
|
|
|
|
//Import of kinect moves
|
|
foreach (var move in timeline.MovesKinect)
|
|
{
|
|
var clip = new MotionClip
|
|
{
|
|
Id = clipId++,
|
|
TrackId = (uint)move.LayerID,
|
|
StartBeat = move.StartPosition,
|
|
BeatDuration = move.StopPosition - move.StartPosition,
|
|
ClassifierPath = move.ClassifierPath,
|
|
MoveType = MotionClip.MoveType_Enum.MoveType_FullBody,
|
|
GoldMove = move.GoldMove,
|
|
CoachId = tape.GetOrCreateTrack<MoveTrack>((uint)move.LayerID).CoachId,
|
|
Color = moveColors.ContainsKey(move.MoveName) ? moveColors[move.MoveName] : Color.WhiteSmoke
|
|
};
|
|
clip.MotionPlatformSpecifics[Platform.X360] = new MotionClip.MotionPlatformSpecific();
|
|
clip.MotionPlatformSpecifics[Platform.X360].ScoreSmoothing = 2;
|
|
tape.Clips.Add(clip);
|
|
}
|
|
|
|
//Import of kinect models
|
|
foreach (var kinectModel in timeline.KinectModelMoves)
|
|
{
|
|
var model = tape.MoveModels().FirstOrDefault(x => x.MoveType == MotionClip.MoveType_Enum.MoveType_FullBody &&
|
|
x.Name.Equals(kinectModel.Name, StringComparison.OrdinalIgnoreCase));
|
|
if (model != null)
|
|
{
|
|
//TODO
|
|
//model.ScoreScale = kinectModel.ScoreScale;
|
|
//model.Kinect.ScoreSmoothing = kinectModel.ScoreSmoothing;
|
|
//model.Kinect.ScoringMode = kinectModel.ScoringMode;
|
|
//model.Kinect2.ScoreScale = kinectModel.ScoreScaleDurango;
|
|
//model.Kinect2.ScoreSmoothing = kinectModel.ScoreSmoothingDurango;
|
|
//model.Kinect2.ScoringMode = kinectModel.ScoringModeDurango;
|
|
}
|
|
}
|
|
|
|
//Import of pictos
|
|
foreach (var picto in timeline.Pictos)
|
|
{
|
|
var clip = new PictogramClip
|
|
{
|
|
Id = clipId++,
|
|
TrackId = (uint)picto.LayerID,
|
|
StartBeat = picto.Position,
|
|
BeatDuration = 1,
|
|
PictoPath = picto.TexturePath
|
|
};
|
|
tape.Clips.Add(clip);
|
|
}
|
|
|
|
//Import of gold move effects
|
|
foreach (var block in timeline.Block)
|
|
{
|
|
if (block.ModelName == "goldmove")
|
|
{
|
|
var clip = new GoldEffectClip
|
|
{
|
|
Id = clipId++,
|
|
TrackId = goldMoveEventsTrack.Id,
|
|
StartBeat = block.StartPosition,
|
|
BeatDuration = 1,
|
|
EffectType = GoldEffectClip.EffectType_Enum.GoldEffect_Auto
|
|
};
|
|
tape.Clips.Add(clip);
|
|
}
|
|
else if (block.ModelName == "goldmovecascade")
|
|
{
|
|
var clip = new GoldEffectClip
|
|
{
|
|
Id = clipId++,
|
|
TrackId = goldMoveEventsTrack.Id,
|
|
StartBeat = block.StartPosition,
|
|
BeatDuration = 1,
|
|
EffectType = GoldEffectClip.EffectType_Enum.GoldEffect_Cascade
|
|
};
|
|
tape.Clips.Add(clip);
|
|
}
|
|
}
|
|
|
|
foreach (var clip in tape.Clips.OfType<MotionClip>())
|
|
{
|
|
var movePathName = Context.OutputMap.Folder.RelativeUri + "/timeline/moves/" + Context.Song.Name + "_" + clip.Model.Name;
|
|
movePathName += clip.MoveType == MotionClip.MoveType_Enum.MoveType_HandOnly ? ".msm" : ".gesture";
|
|
clip.ClassifierPath = new Path(movePathName);
|
|
}
|
|
foreach (var clip in tape.Clips.OfType<PictogramClip>())
|
|
{
|
|
var pictoPathName = Context.OutputMap.Folder.RelativeUri + "/timeline/pictos/" + clip.Model.Name + ".tga";
|
|
clip.PictoPath = new Path(pictoPathName);
|
|
}
|
|
|
|
tape.Tracks.RemoveAll(track => track.Clips().IsEmpty());
|
|
tape.Tracks.ForEach(track => track.Tape = tape);
|
|
|
|
tape.IsLoading = false;
|
|
}
|
|
|
|
private void ImportTml(Tape tape, string fileName)
|
|
{
|
|
tape.Id = DanceTapeFile;
|
|
tape.IsLoading = true;
|
|
tape.TapeClock = Tape.TapeClock_Enum.TapeClock_ConductorGameplay;
|
|
tape.MapName = Context.Song.Name;
|
|
|
|
XDocument xmlFile = XDocument.Load(fileName);
|
|
|
|
var moveModels = xmlFile.XPathSelectElements("/JustDanceToolLD/partition/databank/MoveBank/Move").ToList();
|
|
var kinectmoveModels = xmlFile.XPathSelectElements("/JustDanceToolLD/partition/databank/KinectBank/KinectMove").ToList();
|
|
var pictoModels = xmlFile.XPathSelectElements("/JustDanceToolLD/partition/databank/PictoBank/Picto").ToList();
|
|
|
|
var layers = xmlFile.XPathSelectElements("/JustDanceToolLD/partition/layer").ToList();
|
|
|
|
//We create Tracks
|
|
foreach (var layer in layers)
|
|
{
|
|
var layerName = layer.Attribute("name").Value;
|
|
if (MoveTrack.NameValidationRegexHandOnly.IsMatch(layerName))
|
|
{
|
|
var track = tape.GetOrCreateTrack<MoveTrack>(IdGenerator.Instance.Generate());
|
|
track.Name = layerName;
|
|
track.MoveType = MotionClip.MoveType_Enum.MoveType_HandOnly;
|
|
int layerId;
|
|
if (Int32.TryParse(MoveTrack.NameValidationRegexHandOnly.Match(layerName).Groups[2].Value, out layerId))
|
|
{
|
|
track.CoachId = (MotionClip.CoachId_Enum) (layerId - 1);
|
|
}
|
|
else
|
|
{
|
|
track.CoachId = MotionClip.CoachId_Enum.COACH_0;
|
|
}
|
|
|
|
track.Extensions.Layer = layer;
|
|
}
|
|
else if (MoveTrack.NameValidationRegexTplKinect.IsMatch(layerName))
|
|
{
|
|
var track = tape.GetOrCreateTrack<MoveTrack>(IdGenerator.Instance.Generate());
|
|
track.MoveType = MotionClip.MoveType_Enum.MoveType_FullBody;
|
|
int layerId;
|
|
if (Int32.TryParse(MoveTrack.NameValidationRegexTplKinect.Match(layerName).Groups[2].Value, out layerId))
|
|
{
|
|
track.CoachId = (MotionClip.CoachId_Enum)(layerId - 1);
|
|
}
|
|
else
|
|
{
|
|
track.CoachId = MotionClip.CoachId_Enum.COACH_0;
|
|
}
|
|
track.Name = "CameraMoves" + (int)(track.CoachId + 1);
|
|
track.Extensions.Layer = layer;
|
|
}
|
|
else if (PictoTrack.NameValidationRegex.IsMatch(layerName))
|
|
{
|
|
var track = tape.GetOrCreateTrack<PictoTrack>(IdGenerator.Instance.Generate());
|
|
track.Name = layerName;
|
|
track.Extensions.Layer = layer;
|
|
}
|
|
else if (layerName.StartsWith("Events"))
|
|
{
|
|
if (layer.Descendants("instance").Any(x => x.Attribute("model").Value == "GoldMove"))
|
|
{
|
|
var track = tape.GetOrCreateTrack<GoldEffectTrack>(GoldEffectTrack.DefaultName);
|
|
track.Extensions.Layer = layer;
|
|
}
|
|
}
|
|
}
|
|
|
|
//We parse move layers
|
|
foreach (var track in tape.Tracks.OfType<MoveTrack>())
|
|
{
|
|
foreach (var instance in track.Extensions.Layer.Descendants("instance"))
|
|
{
|
|
int position = Int32.Parse(instance.Attribute("position").Value)-1;
|
|
string modelName = instance.Attribute("model").Value;
|
|
|
|
var model = track.MoveType == MotionClip.MoveType_Enum.MoveType_HandOnly ?
|
|
moveModels.FirstOrDefault(x => x.Attribute("name").Value == modelName) :
|
|
kinectmoveModels.FirstOrDefault(x => x.Attribute("name").Value == modelName);
|
|
if (model == null)
|
|
continue;
|
|
|
|
int duration = XmlUtils.ReadIntValue(model.Element("duration"), 0);
|
|
int subdivisionInBeat = XmlUtils.ReadIntValue(model.Element("SubdivisionsInBeat"), 1);
|
|
bool isGoldenMove = XmlUtils.ReadBoolValue(model.Element("GoldenMove"), false) ||
|
|
XmlUtils.ReadBoolValue(instance.Element("GoldenMove"), false);
|
|
var moveColor = XmlUtils.ReadColorValue(model.Element("color"), Color.Orange);
|
|
moveColor = Color.FromArgb(255, moveColor.B, moveColor.G, moveColor.R);
|
|
|
|
var offsetInSubdivision = XmlUtils.ReadIntValue(instance.Element("OffsetInSubdivisions"), 0);
|
|
|
|
var movePathName = Context.OutputMap.Folder.RelativeUri + "/timeline/moves/" + Context.Song.Name + "_" + modelName;
|
|
movePathName += track.MoveType == MotionClip.MoveType_Enum.MoveType_HandOnly ? ".msm" : ".gesture";
|
|
|
|
var clip = new MotionClip
|
|
{
|
|
Id = IdGenerator.Instance.Generate(),
|
|
TrackId = track.Id,
|
|
StartBeat = position + offsetInSubdivision/(float)subdivisionInBeat,
|
|
BeatDuration = duration / (float)subdivisionInBeat,
|
|
ClassifierPath = new Path(movePathName),
|
|
MoveType = track.MoveType,
|
|
CoachId = track.CoachId,
|
|
GoldMove = isGoldenMove,
|
|
Color = moveColor
|
|
};
|
|
|
|
tape.Clips.Add(clip);
|
|
}
|
|
}
|
|
|
|
//We parse picto layers
|
|
foreach (var track in tape.Tracks.OfType<PictoTrack>())
|
|
{
|
|
foreach (var instance in track.Extensions.Layer.Descendants("instance"))
|
|
{
|
|
int position = Int32.Parse(instance.Attribute("position").Value)-1;
|
|
|
|
string modelName = instance.Attribute("model").Value;
|
|
|
|
var model = pictoModels.FirstOrDefault(x => x.Attribute("name").Value == modelName);
|
|
if (model == null)
|
|
continue;
|
|
|
|
var offset = (float)XmlUtils.ReadDoubleValue(instance.Element("Offset"), 0);
|
|
|
|
var pictoPathName = Context.OutputMap.Folder.RelativeUri + "/timeline/pictos/" + modelName + ".tga";
|
|
|
|
var clip = new PictogramClip
|
|
{
|
|
Id = IdGenerator.Instance.Generate(),
|
|
TrackId = track.Id,
|
|
StartBeat = tape.TimeInBeats(position, offset),
|
|
BeatDuration = 1,
|
|
PictoPath = new Path(pictoPathName)
|
|
};
|
|
tape.Clips.Add(clip);
|
|
}
|
|
}
|
|
|
|
|
|
//We parse gold effects layers
|
|
foreach (var track in tape.Tracks.OfType<GoldEffectTrack>())
|
|
{
|
|
XElement layer = track.Extensions.Layer;
|
|
var goldEffects = layer.Descendants("instance").Where(x => x.Attribute("model").Value.StartsWith("GoldMove"));
|
|
foreach (var instance in goldEffects)
|
|
{
|
|
int position = Int32.Parse(instance.Attribute("position").Value)-1;
|
|
var offset = (float)XmlUtils.ReadDoubleValue(instance.Element("Offset"), 0);
|
|
var clip = new GoldEffectClip
|
|
{
|
|
Id = IdGenerator.Instance.Generate(),
|
|
TrackId = track.Id,
|
|
StartBeat = tape.TimeInBeats(position, offset),
|
|
BeatDuration = 1,
|
|
EffectType = GoldEffectClip.EffectType_Enum.GoldEffect_Auto
|
|
};
|
|
tape.Clips.Add(clip);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|