-
Notifications
You must be signed in to change notification settings - Fork 170
/
AlembicBuildPostProcess.cs
175 lines (152 loc) · 6.62 KB
/
AlembicBuildPostProcess.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEditor.Callbacks;
using UnityEngine;
using UnityEngine.Formats.Alembic.Importer;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
namespace UnityEditor.Formats.Alembic.Importer
{
static class AlembicBuildPostProcess
{
internal const string kUnsupportedTarget = "Alembic only supports the following build targets: Windows 64-bit, macOS X, and Linux 64-bit.";
internal static readonly HashSet<KeyValuePair<string, string>> FilesToCopy = new HashSet<KeyValuePair<string, string>>();
internal static bool HaveAlembicInstances = false;
[PostProcessBuild]
public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
{
if (HaveAlembicInstances)
{
AlembicBuildAnalytics.SendAnalytics(target);
}
if (!TargetIsSupported(target))
{
if (HaveAlembicInstances)
{
Debug.LogWarning(kUnsupportedTarget);
}
HaveAlembicInstances = false;
return;
}
foreach (var files in FilesToCopy)
{
if (!File.Exists(EditorHelper.BuildPathIfNecessary(files.Key)))
{
continue;
}
var dir = Path.GetDirectoryName(files.Value);
if (dir != null && !Directory.Exists(EditorHelper.BuildPathIfNecessary(dir)))
{
Directory.CreateDirectory(dir);
}
if (File.Exists(EditorHelper.BuildPathIfNecessary(files.Value)))
{
var attrs = File.GetAttributes(files.Value);
attrs &= ~FileAttributes.ReadOnly;
File.SetAttributes(files.Value, attrs);
}
File.Copy(EditorHelper.BuildPathIfNecessary(files.Key), files.Value, true);
}
FilesToCopy.Clear();
}
public static bool TargetIsSupported(BuildTarget target)
{
return target == BuildTarget.StandaloneOSX || target == BuildTarget.StandaloneWindows64 || target == BuildTarget.StandaloneLinux64;
}
}
class AlembicScenePlayerStripper : IProcessSceneWithReport
{
static readonly Type[] kStrippableBehaviourTypes =
{
typeof(AlembicStreamPlayer),
typeof(AlembicCustomData),
typeof(AlembicCurvesRenderer), typeof(AlembicCurves),
typeof(AlembicPointsRenderer), typeof(AlembicPointsCloud),
};
public int callbackOrder => -9999; // Run early to strip behaviours before other callbacks try to interact with them.
public void OnProcessScene(Scene scene, BuildReport report)
{
if (report == null || AlembicBuildPostProcess.TargetIsSupported(report.summary.platform))
return;
var sceneRoots = scene.GetRootGameObjects();
var alembicBehaviours = new List<Component>();
foreach (var type in kStrippableBehaviourTypes)
{
alembicBehaviours.AddRange(sceneRoots.SelectMany(root => root.GetComponentsInChildren(type, true)));
}
if (alembicBehaviours.Count > 0)
{
Debug.Log($"{AlembicBuildPostProcess.kUnsupportedTarget} Stripping {alembicBehaviours.Count} alembic behaviour instances from scene '{scene.name}'.");
alembicBehaviours.ForEach(Object.DestroyImmediate);
}
}
}
class AlembicProcessScene : IProcessSceneWithReport
{
public int callbackOrder => 9999; // Run late to catch potential Alembics that were created during a Scene post process.
public void OnProcessScene(Scene scene, BuildReport report)
{
AlembicBuildPostProcess.HaveAlembicInstances |= scene.GetRootGameObjects()
.SelectMany(root => root.GetComponentsInChildren<AlembicStreamPlayer>(true)).Any();
if (report == null || !AlembicBuildPostProcess.TargetIsSupported(report.summary.platform))
{
return;
}
var activeScene = SceneManager.GetActiveScene();
SceneManager.SetActiveScene(scene);
var players = scene.GetRootGameObjects().SelectMany(root => root.GetComponentsInChildren<AlembicStreamPlayer>(true));
var pathToStreamingAssets = GetStreamingAssetsPath(report.summary);
foreach (var p in players)
{
ProcessAlembicStreamPlayerAssets(p, pathToStreamingAssets);
}
SceneManager.SetActiveScene(activeScene);
}
static void ProcessAlembicStreamPlayerAssets(AlembicStreamPlayer streamPlayer, string streamingAssetsPath)
{
streamPlayer.StreamDescriptor = streamPlayer.StreamDescriptor.Clone();// make a copy
var srcPath = streamPlayer.StreamDescriptor.PathToAbc;
if (string.IsNullOrEmpty(srcPath))
{
return;
}
// Avoid name collisions by hashing the full path
var hashedFilename = HashSha1(srcPath) + ".abc";
var dstPath = Path.Combine(streamingAssetsPath, hashedFilename);
AlembicBuildPostProcess.FilesToCopy.Add(new KeyValuePair<string, string>(srcPath, dstPath));
streamPlayer.StreamDescriptor.PathToAbc = hashedFilename;
}
static string GetStreamingAssetsPath(BuildSummary summary)
{
switch (summary.platform)
{
case BuildTarget.StandaloneOSX:
return Path.Combine(summary.outputPath, "Contents/Resources/Data/StreamingAssets");
case BuildTarget.StandaloneLinux64:
case BuildTarget.StandaloneWindows64:
var name = Path.ChangeExtension(summary.outputPath, null);
return name + "_Data/StreamingAssets";
default:
throw new NotImplementedException();
}
}
static string HashSha1(string value)
{
var sha1 = SHA1.Create();
var inputBytes = Encoding.ASCII.GetBytes(value);
var hash = sha1.ComputeHash(inputBytes);
var sb = new StringBuilder();
foreach (var t in hash)
{
sb.Append(t.ToString("X2"));
}
return sb.ToString();
}
}
}