Skip to content

Instantly share code, notes, and snippets.

@aarthificial
Last active May 26, 2024 04:30
Show Gist options
  • Save aarthificial/a77bf477c6bfa51507ffc1e8f18c5635 to your computer and use it in GitHub Desktop.
Save aarthificial/a77bf477c6bfa51507ffc1e8f18c5635 to your computer and use it in GitHub Desktop.
MIT License
Copyright (c) 2023 aarthificial
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
using Common;
using UnityEngine;
namespace Editor
{
public class PixelMap : ScriptableObject
{
public SerializedDictionary<Color32, Vector2Int> lookup = new();
public Color32[] data;
}
}
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Common
{
[Serializable]
public class SerializedDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
{
[Serializable]
public struct Pair
{
public TKey key;
public TValue value;
public static implicit operator KeyValuePair<TKey, TValue>(Pair pair)
{
return new KeyValuePair<TKey, TValue>(pair.key, pair.value);
}
public static implicit operator Pair(KeyValuePair<TKey, TValue> pair)
{
return new Pair
{
key = pair.Key,
value = pair.Value
};
}
}
[SerializeField] private List<Pair> entries = new();
public SerializedDictionary()
{
}
public SerializedDictionary(IDictionary<TKey, TValue> dictionary) : base(dictionary)
{
}
public void OnBeforeSerialize()
{
entries.Clear();
foreach (var pair in this)
entries.Add(pair);
}
public void OnAfterDeserialize()
{
Clear();
foreach (var entry in entries)
this[entry.key] = entry.value;
}
}
}
using System.IO;
using UnityEditor;
using UnityEngine;
namespace Editor
{
public class TexturePostprocessor : AssetPostprocessor
{
private const string ExtensionPNG = ".png";
private const string PixelMapSuffix = ".map";
private const string PixelWeightsPrefix = "weights.";
private void OnPostprocessTexture(Texture2D texture)
{
var fileName = Path.GetFileNameWithoutExtension(assetPath);
var extension = Path.GetExtension(assetPath);
if (extension != ExtensionPNG)
return;
if (fileName.EndsWith(PixelMapSuffix))
ProcessPixelMap(texture, fileName);
else if (fileName.StartsWith(PixelWeightsPrefix))
ProcessPixelWeights(texture, fileName.Replace(PixelWeightsPrefix, ""));
}
private void ProcessPixelWeights(Texture2D texture, string fileName)
{
if (!fileName.Contains('_'))
return;
var mapName = fileName.Split('_')[0] + PixelMapSuffix;
var map = FindPixelMap(mapName);
if (map == null)
return;
var skinData = texture.GetPixels32();
for (var i = 0; i < skinData.Length; i++)
{
if (map.lookup.TryGetValue(skinData[i], out var position))
{
skinData[i].r = (byte)position.x;
skinData[i].g = (byte)position.y;
skinData[i].b = 0;
skinData[i].a = 255;
}
else
{
skinData[i] = Color.clear;
}
}
var path = assetPath.Replace(PixelWeightsPrefix, "");
var skinTexture = new Texture2D(texture.width, texture.height);
skinTexture.SetPixels32(skinData);
File.WriteAllBytes(path, skinTexture.EncodeToPNG());
AssetDatabase.ImportAsset(path);
}
private void ProcessPixelMap(Texture2D texture, string fileName)
{
var map = FindPixelMap(fileName);
if (map == null)
{
map = ScriptableObject.CreateInstance<PixelMap>();
map.name = fileName;
AssetDatabase.CreateAsset(
map,
Path.Combine(Path.GetDirectoryName(assetPath), $"{fileName}.asset")
);
}
map.data = texture.GetPixels32();
map.lookup.Clear();
for (var i = 0; i < map.data.Length; i++)
if (map.data[i].a > 0)
{
map.lookup[map.data[i]] = new Vector2Int(
i % texture.width,
i / texture.width
);
}
EditorUtility.SetDirty(map);
AssetDatabase.SaveAssets();
}
private static PixelMap FindPixelMap(string fileName)
{
var guids = AssetDatabase.FindAssets("t:" + nameof(PixelMap));
foreach (var guid in guids)
{
var asset = AssetDatabase.LoadMainAssetAtPath(
AssetDatabase.GUIDToAssetPath(guid)
) as PixelMap;
if (asset != null && asset.name == fileName)
return asset;
}
return null;
}
}
}
@MakarovIncorporated
Copy link

Yo, that looks so complicated, but it saves your time exponentially). i love it

@MrvPumpkin
Copy link

Assets\Resources\Scripts\PixelPerfect\PixelMap.cs(8,71): error CS8124: Tuple must contain at least two elements.
Assets\Resources\Scripts\PixelPerfect\PixelMap.cs(8,72): error CS1526: A new expression requires (), [], or {} after type
Assets\Resources\Scripts\PixelPerfect\SerializedDictionary.cs(31,59): error CS8124: Tuple must contain at least two elements.
Assets\Resources\Scripts\PixelPerfect\SerializedDictionary.cs(31,60): error CS1526: A new expression requires (), [], or {} after type

@ttvedt
Copy link

ttvedt commented Oct 13, 2022

I'm getting the same errors as MrvPumpkin, has anyone found a fix for this yet?

Edit: I figured it out, he has "new()" but you need it to be "new [typename]()"

@Honuluau
Copy link

No reanimator.cs 😭

@SpellMender
Copy link

SpellMender commented Sep 29, 2023

No reanimator.cs 😭

Here's the link to his reanimator package: https://github.com/aarthificial/reanimation/tree/master

--
To install the package you have to install Git: https://git-scm.com/

At time of writing this worked for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment