Skip to content

Commit a8b7cda

Browse files
authored
Merge pull request #17 from nasshu2916/feature/convert-anim-to-dmx-binary
AnimationClip から DMXのバイナリデータに変換できるようにする
2 parents eff0fe2 + 20a767d commit a8b7cda

File tree

3 files changed

+155
-4
lines changed

3 files changed

+155
-4
lines changed

Assets/ArtNet/Editor/DmxRecorder/ConvertAnimInspector.cs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.IO;
12
using UnityEditor;
23
using UnityEngine;
34

@@ -16,15 +17,22 @@ public override void OnInspectorGUI()
1617

1718
EditorGUILayout.BeginVertical(GUI.skin.box);
1819
{
19-
if (GUILayout.Button("Convert"))
20+
if (GUILayout.Button("ConvertAnim"))
2021
{
21-
Convert(convertAnim);
22+
ConvertAnim(convertAnim);
23+
}
24+
25+
GUILayout.Space(5);
26+
27+
if (GUILayout.Button("ConvertPacket"))
28+
{
29+
ConvertPacket(convertAnim);
2230
}
2331
}
2432
EditorGUILayout.EndVertical();
2533
}
2634

27-
private static void Convert(ConvertAnim convertAnim)
35+
private static void ConvertAnim(ConvertAnim convertAnim)
2836
{
2937
var binary = convertAnim.binary;
3038
if (!binary)
@@ -44,5 +52,41 @@ private static void Convert(ConvertAnim convertAnim)
4452

4553
Debug.Log("Conversion complete");
4654
}
55+
56+
private static void ConvertPacket(ConvertAnim convertAnim)
57+
{
58+
var binary = convertAnim.binary;
59+
if (!binary)
60+
{
61+
Debug.LogError("Binary is null");
62+
return;
63+
}
64+
if (string.IsNullOrEmpty(convertAnim.OutputDirectory))
65+
{
66+
Debug.LogError("Output directory is null or empty");
67+
return;
68+
}
69+
70+
var timelineSettingPath = convertAnim.OutputDirectory + "/DmxTimeline.asset";
71+
var dmxTimelineSetting = AssetDatabase.LoadAssetAtPath(timelineSettingPath, typeof(DmxTimelineSetting)) as DmxTimelineSetting;
72+
if (dmxTimelineSetting is null)
73+
{
74+
Debug.LogError("DmxTimelineSetting is null");
75+
return;
76+
}
77+
78+
Debug.Log($"ArtNet Recorder: {dmxTimelineSetting.DmxTimelines.Count} timelines found");
79+
var timelineConverter = new TimelineConverter(dmxTimelineSetting);
80+
var dmxPackets = timelineConverter.ToDmxPackets();
81+
var storeData = RecordData.Serialize(dmxPackets);
82+
83+
84+
var path = convertAnim.OutputDirectory + "/DmxPackets.bytes";
85+
var exists = File.Exists(path);
86+
File.WriteAllBytes(path, storeData);
87+
var message = exists ? "Data updated" : "Data stored";
88+
Debug.Log($"ArtNet Recorder: {message} at {path}");
89+
Debug.Log("Conversion complete");
90+
}
4791
}
4892
}

Assets/ArtNet/Editor/DmxRecorder/RecordData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public static class RecordData
1515
private static readonly byte[] ReservedBuffer = new byte[11];
1616
private static byte Version = 0x01;
1717

18-
public static byte[] Serialize(List<(int time, DmxPacket packet)> dmxPackets)
18+
public static byte[] Serialize(IReadOnlyList<(int time, DmxPacket packet)> dmxPackets)
1919
{
2020
var startTime = dmxPackets.Select(x => x.time).OrderBy(x => x).First();
2121
using var memoryStream = new MemoryStream();

Assets/ArtNet/Editor/DmxRecorder/TimelineConverter.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ public TimelineConverter(IReadOnlyCollection<(int time, DmxPacket packet)> packe
2121
}
2222
}
2323

24+
public TimelineConverter(DmxTimelineSetting dmxTimelineSetting)
25+
{
26+
foreach (var timelineElement in dmxTimelineSetting.DmxTimelines)
27+
{
28+
Timelines.Add(new TimelineUniverse(timelineElement.Universe, timelineElement.DmxTimelineClip));
29+
}
30+
}
31+
2432
public void SaveDmxTimelineClips(string directory)
2533
{
2634
if (System.IO.Directory.Exists(directory) == false)
@@ -51,6 +59,11 @@ public void SaveDmxTimelineClips(string directory)
5159
AssetDatabase.Refresh();
5260
}
5361

62+
public List<(int time, DmxPacket packet)> ToDmxPackets()
63+
{
64+
return Timelines.SelectMany(x => x.ToDmxPackets()).OrderBy(x => x.time).ToList();
65+
}
66+
5467
private static void SaveAsset<T>(T asset, string directory, string fileName) where T : UnityEngine.Object
5568
{
5669
var path = $"{directory}/{fileName}";
@@ -77,6 +90,71 @@ public TimelineUniverse(int universe, IReadOnlyCollection<(int time, DmxPacket p
7790
}
7891
}
7992

93+
public TimelineUniverse(int universe, AnimationClip clip)
94+
{
95+
Universe = universe;
96+
var curveBindings = AnimationUtility.GetCurveBindings(clip);
97+
ChannelDmxFrameData = new List<DmxFrameData>[512];
98+
for (var i = 0; i < ChannelDmxFrameData.Length; i++)
99+
{
100+
var propertyName = $"Ch{i + 1:D3}";
101+
102+
var curve = curveBindings
103+
.Where(binding => binding.propertyName == propertyName)
104+
.Select(binding => AnimationUtility.GetEditorCurve(clip, binding))
105+
.FirstOrDefault();
106+
107+
if (curve is null) continue;
108+
109+
ChannelDmxFrameData[i] = curve.keys.Select(x => new DmxFrameData((int) (x.time * 1000), (byte) x.value)).ToList();
110+
}
111+
}
112+
113+
public IEnumerable<int> AllFrameTimes()
114+
{
115+
return ChannelDmxFrameData.SelectMany(x => x.Select(frameData => frameData.Millisecond)).Distinct();
116+
}
117+
118+
public byte FrameValue(int channel, int time)
119+
{
120+
var dmxFrameData = ChannelDmxFrameData[channel];
121+
122+
// If there is a frame data at the exact time, return it
123+
foreach (var frameData in dmxFrameData.Where(frameData => frameData.Millisecond == time))
124+
{
125+
return frameData.Value;
126+
}
127+
128+
// if there is no frame data, return 0
129+
if (dmxFrameData.Count == 0) return 0;
130+
131+
// if time is out of range, return the first or last value
132+
if (time < dmxFrameData[0].Millisecond) return dmxFrameData[0].Value;
133+
if (time > dmxFrameData[^1].Millisecond) return dmxFrameData[^1].Value;
134+
135+
// return the estimated value from frames around the specified time.
136+
137+
// Find the frame data before and after the specified time
138+
var prev = dmxFrameData[0];
139+
var next = dmxFrameData[0];
140+
foreach (var frameData in dmxFrameData)
141+
{
142+
if (frameData.Millisecond > time)
143+
{
144+
next = frameData;
145+
break;
146+
}
147+
148+
prev = frameData;
149+
}
150+
151+
// Calculate the estimated value
152+
var prevDiff = next.Value - prev.Value;
153+
var prevDiffTime = next.Millisecond - prev.Millisecond;
154+
var timeDiff = time - prev.Millisecond;
155+
return (byte) (prev.Value + (prevDiff * timeDiff / prevDiffTime));
156+
}
157+
80158
public AnimationClip ToAnimationClip()
81159
{
82160
var curves = ConvertAnimationCurves();
@@ -144,6 +222,35 @@ private static bool IsOmittedFrame(
144222

145223
return Math.Abs((float) prevDiff / prevDiffTime - (float) nextDiff / nextDiffTime) <= tolerance;
146224
}
225+
226+
public IEnumerable<(int time, DmxPacket packet)> ToDmxPackets()
227+
{
228+
byte sequence = 0;
229+
var packets = new List<(int time, DmxPacket packet)>();
230+
var allFrameTimes = AllFrameTimes().OrderBy(x => x).ToList();
231+
232+
foreach (var time in allFrameTimes)
233+
{
234+
var dmx = new byte[512];
235+
for (var i = 0; i < ChannelDmxFrameData.Length; i++)
236+
{
237+
dmx[i] = FrameValue(i, time);
238+
}
239+
240+
var packet = new DmxPacket { Sequence = sequence, Universe = (ushort) Universe, Dmx = dmx };
241+
packets.Add((time, packet));
242+
if (sequence >= 255)
243+
{
244+
sequence = 0;
245+
}
246+
else
247+
{
248+
sequence++;
249+
}
250+
}
251+
252+
return packets;
253+
}
147254
}
148255

149256
public struct DmxFrameData

0 commit comments

Comments
 (0)