10using System.Collections.Generic;
38 private Rect[] _nullRects =
new Rect[4];
49 return _nullRects[channel];
64 _nullRects[channel] = rect;
66 getChannel(channel)[key] = rect;
70 private TextureToRect getChannel(
int channel) {
81 throw new Exception();
89 [Tooltip(
"When non-zero, extends each texture by a certain number of pixels, filling in the space with texture data based on the texture wrap mode.")]
92 private int _border = 0;
94 [Tooltip(
"When non-zero, adds an amount of empty space between each texture.")]
97 private int _padding = 0;
99 [Tooltip(
"Should the atlas have mip maps?")]
101 private bool _mipMap =
true;
103 [Tooltip(
"The filter mode that should be used for the atlas.")]
105 private FilterMode _filterMode = FilterMode.Bilinear;
107 [Tooltip(
"The texture format that should be used for the atlas.")]
109 private TextureFormat _format = TextureFormat.ARGB32;
111 [Tooltip(
"The maximum atlas size in pixels.")]
115 private int _maxAtlasSize = 4096;
117 [Tooltip(
"Add textures to this array to ensure that they are always present in the atlas.")]
126 return _currHash != _atlasHash;
130 private static Material _cachedBlitMaterial =
null;
131 private static void enableBlitPass(Texture tex) {
132 if (_cachedBlitMaterial ==
null) {
133 _cachedBlitMaterial =
new Material(Shader.Find(
"Hidden/Leap Motion/Graphic Renderer/InternalPack"));
134 _cachedBlitMaterial.hideFlags = HideFlags.HideAndDontSave;
136 _cachedBlitMaterial.mainTexture = tex;
137 _cachedBlitMaterial.SetPass(0);
140 private List<LeapTextureFeature> _features =
new List<LeapTextureFeature>();
141 private Hash _atlasHash = 1;
142 private Hash _currHash = 0;
151 _features.AddRange(textureFeatures);
153 _currHash =
new Hash() {
162 if (_extraTextures ==
null) {
166 for (
int i = 0; i < _extraTextures.Length; i++) {
167 switch (_extraTextures[i].channel) {
168 case UVChannelFlags.UV0:
169 case UVChannelFlags.UV1:
170 case UVChannelFlags.UV2:
171 case UVChannelFlags.UV3:
174 _extraTextures[i].
channel = UVChannelFlags.UV0;
179 foreach (var extra
in _extraTextures) {
180 _currHash.Add(extra.texture);
181 _currHash.Add(extra.channel);
184 foreach (var feature
in _features) {
185 _currHash.Add(feature.channel);
186 foreach (var dataObj
in feature.featureData) {
187 _currHash.Add(dataObj.texture);
199 if (!Utils.IsCompressible(_format)) {
200 Debug.LogWarning(
"Format " + _format +
" is not compressible! Using ARGB32 instead.");
201 _format = TextureFormat.ARGB32;
204 _atlasHash = _currHash;
206 packedTextures =
new Texture2D[_features.Count];
209 mainProgressLoop(progress, packedTextures, channelMapping);
212 foreach (var texture
in _cachedProcessedTextures.Values) {
215 _cachedProcessedTextures.Clear();
217 foreach (var texture
in _cachedDefaultTextures.Values) {
220 _cachedDefaultTextures.Clear();
223 private void mainProgressLoop(
ProgressBar progress, Texture2D[] packedTextures,
AtlasUvs channelMapping) {
224 progress.
Begin(5,
"",
"", () => {
225 foreach (var channel
in MeshUtil.allUvChannels) {
226 progress.Begin(1,
"", channel +
": ", () => {
227 doPerChannelPack(progress, channel, packedTextures, channelMapping);
231 finalizeTextures(progress, packedTextures);
235 private void doPerChannelPack(ProgressBar progress, UVChannelFlags channel, Texture2D[] packedTextures, AtlasUvs channelMapping) {
236 var mainTextureFeature = _features.Query().FirstOrDefault(f => f.channel == channel);
237 if (mainTextureFeature ==
null)
return;
239 Texture2D defaultTexture, packedTexture;
240 Texture2D[] rawTextureArray, processedTextureArray;
242 progress.Step(
"Prepare " + channel);
243 prepareForPacking(mainTextureFeature, out defaultTexture,
246 out processedTextureArray);
248 progress.Step(
"Pack " + channel);
249 var packedRects = packedTexture.PackTextures(processedTextureArray,
252 makeNoLongerReadable:
false);
254 packedTexture.Apply(updateMipmaps:
true, makeNoLongerReadable:
true);
255 packedTextures[_features.IndexOf(mainTextureFeature)] = packedTexture;
257 packSecondaryTextures(progress, channel, mainTextureFeature, packedTexture, packedRects, packedTextures);
260 for (
int i = 0; i < packedRects.Length; i++) {
261 float dx = 1.0f / packedTexture.width;
262 float dy = 1.0f / packedTexture.height;
263 Rect r = packedRects[i];
265 if (processedTextureArray[i] != defaultTexture) {
277 for (
int i = 0; i < rawTextureArray.Length; i++) {
278 channelMapping.SetRect(channel.Index(), rawTextureArray[i], packedRects[i]);
282 private void packSecondaryTextures(ProgressBar progress, UVChannelFlags channel, LeapTextureFeature mainFeature, Texture2D packedTexture, Rect[] packedRects, Texture2D[] packedTextures) {
285 var nonMainFeatures = _features.Query().Where(f => f.channel == channel).Skip(1).ToList();
287 progress.Begin(nonMainFeatures.Count,
"",
"Copying secondary textures: ", () => {
288 foreach (var secondaryFeature in nonMainFeatures) {
290 RenderTexture secondaryRT = new RenderTexture(packedTexture.width, packedTexture.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
292 RenderTexture.active = secondaryRT;
293 GL.Clear(clearDepth: false, clearColor: true, backgroundColor: Color.black);
294 GL.LoadPixelMatrix(0, 1, 0, 1);
296 progress.Begin(secondaryFeature.featureData.Count,
"", secondaryFeature.propertyName, () => {
297 for (int i = 0; i < secondaryFeature.featureData.Count; i++) {
298 var mainTexture = mainFeature.featureData[i].texture;
299 if (mainTexture == null) {
304 var secondaryTexture = secondaryFeature.featureData[i].texture;
305 if (secondaryTexture == null) {
310 progress.Step(secondaryTexture.name);
312 Rect rect = packedRects[i];
315 float borderDX = _border / (float)mainTexture.width;
316 float borderDY = _border / (float)mainTexture.height;
318 drawTexture(secondaryTexture, secondaryRT, rect, borderDX, borderDY);
322 packedTextures[_features.IndexOf(secondaryFeature)] = convertToTexture2D(secondaryRT, mipmap: false);
327 private void finalizeTextures(ProgressBar progress, Texture2D[] packedTextures) {
328 progress.Begin(packedTextures.Length,
"",
"Finalizing ", () => {
329 for (int i = 0; i < packedTextures.Length; i++) {
330 progress.Begin(2,
"", _features[i].propertyName +
": ", () => {
331 Texture2D tex = packedTextures[i];
332 RenderTexture rt = new RenderTexture(tex.width, tex.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
334 GL.LoadPixelMatrix(0, 1, 0, 1);
335 drawTexture(tex, rt, new Rect(0, 0, 1, 1), 0, 0);
337 progress.Step(
"Copying Texture");
338 tex = convertToTexture2D(rt, _mipMap);
340 progress.Step(
"Compressing Texture");
343#if UNITY_2018_3_OR_NEWER
344 UnityEditor.EditorUtility.CompressTexture(tex, _format, UnityEditor.TextureCompressionQuality.Best);
346 UnityEditor.EditorUtility.CompressTexture(tex, _format, TextureCompressionQuality.Best);
349 tex.filterMode = _filterMode;
351 progress.Step(
"Updating Texture");
353 tex.Apply(updateMipmaps: true, makeNoLongerReadable: false);
355 packedTextures[i] = tex;
361 private void prepareForPacking(LeapTextureFeature feature,
362 out Texture2D defaultTexture,
363 out Texture2D packedTexture,
364 out Texture2D[] rawTextureArray,
365 out Texture2D[] processedTextureArray) {
366 if (_extraTextures ==
null) {
367 _extraTextures =
new TextureReference[0];
370 rawTextureArray = feature.featureData.Query().
371 Select(dataObj => dataObj.texture).
372 Concat(_extraTextures.Query().
373 Where(p => p.channel == feature.channel).
374 Select(p => p.texture)).
377 processedTextureArray = rawTextureArray.Query().
378 Select(t => processTexture(t)).
381 defaultTexture = getDefaultTexture(
Color.white);
382 for (
int i = 0; i < processedTextureArray.Length; i++) {
383 if (processedTextureArray[i] ==
null) {
384 processedTextureArray[i] = defaultTexture;
388 #if UNITY_2018_2_OR_NEWER
389 packedTexture =
new Texture2D(1, 1, TextureFormat.ARGB32, mipChain:
false,
392 packedTexture =
new Texture2D(1, 1, TextureFormat.ARGB32, mipmap:
false,
395 packedTexture.filterMode = _filterMode;
398 private Dictionary<Texture2D, Texture2D> _cachedProcessedTextures =
new Dictionary<Texture2D, Texture2D>();
399 private Texture2D processTexture(Texture2D source) {
400 using (
new ProfilerSample(
"Process Texture")) {
401 if (source ==
null) {
406 if (_cachedProcessedTextures.TryGetValue(source, out processed)) {
410 RenderTexture destRT =
new RenderTexture(source.width + _border * 2, source.height + _border * 2, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
412 GL.LoadPixelMatrix(0, 1, 0, 1);
413 drawTexture(source, destRT,
new Rect(0, 0, 1, 1), _border / (
float)source.width, _border / (
float)source.height);
415 processed = convertToTexture2D(destRT, mipmap:
false);
416 _cachedProcessedTextures[source] = processed;
421 private void drawTexture(Texture2D source, RenderTexture dst, Rect rect,
float borderDX,
float borderDY) {
422 enableBlitPass(source);
423 RenderTexture.active = dst;
426 GL.TexCoord(
new Vector2(0 - borderDX, 0 - borderDY));
427 GL.Vertex(rect.Corner00());
428 GL.TexCoord(
new Vector2(1 + borderDX, 0 - borderDY));
429 GL.Vertex(rect.Corner10());
430 GL.TexCoord(
new Vector2(1 + borderDX, 1 + borderDY));
431 GL.Vertex(rect.Corner11());
432 GL.TexCoord(
new Vector2(0 - borderDX, 1 + borderDY));
433 GL.Vertex(rect.Corner01());
437 private Texture2D convertToTexture2D(RenderTexture source,
bool mipmap) {
438 Texture2D tex =
new Texture2D(source.width, source.height, TextureFormat.ARGB32, mipmap, linear:
true);
440 RenderTexture.active = source;
441 tex.ReadPixels(
new Rect(0, 0, tex.width, tex.height), 0, 0);
442 tex.Apply(updateMipmaps:
false, makeNoLongerReadable:
false);
443 RenderTexture.active =
null;
451 private Dictionary<Color, Texture2D> _cachedDefaultTextures =
new Dictionary<Color, Texture2D>();
452 private Texture2D getDefaultTexture(
Color color) {
454 if (!_cachedDefaultTextures.TryGetValue(color, out texture)) {
456 #if UNITY_2018_2_OR_NEWER
457 texture =
new Texture2D(3, 3, TextureFormat.ARGB32, mipChain:
false);
459 texture =
new Texture2D(3, 3, TextureFormat.ARGB32, mipmap:
false);
462 texture.SetPixels(
new Color[3 * 3].Fill(color));
463 _cachedDefaultTextures[color] = texture;
471 public UVChannelFlags channel = UVChannelFlags.UV0;
void RebuildAtlas(ProgressBar progress, out Texture2D[] packedTextures, out AtlasUvs channelMapping)
Actually perform the build for the atlas. This method outputs the atlas textures, and the atlas uvs t...
bool isDirty
Returns whether or not the results built by this atlas have become invalid.
void UpdateTextureList(List< LeapTextureFeature > textureFeatures)
Updates the internal list of textures given some texture features to build an atlas for....
A class that contains mapping information that specifies how a texture is packed into an atlas.
Rect GetRect(int channel, UnityEngine.Object key)
Given a texture object and a uv channel, return the rect that this texture occupies within the atlas....
void SetRect(int channel, UnityEngine.Object key, Rect rect)
Given a texture object and a uv channel, store into this data structure the rect that the texture tak...
This class allows you to easily give feedback of an action as it completes.
void Begin(int sections, string title, string info, Action action)
Begins a new chunk. If this call is made from within a chunk it will generate a sub-chunk that repres...
In order to have this class be serialized, you will always need to create your own non-generic versio...
bool TryGetValue(TKey key, out TValue value)