Update custom pass to global custom pass
This commit is contained in:
229
Editor/NormalBaker/ModelOutlineJobs.cs
Normal file
229
Editor/NormalBaker/ModelOutlineJobs.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
|
||||
// Todo: Change this into a editor window
|
||||
|
||||
namespace Unity.Toonshader.Editor
|
||||
{
|
||||
public class ModelOutlineJobs
|
||||
{
|
||||
public struct CollectNormalJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<Vector3> normals, vertrx;
|
||||
[NativeDisableContainerSafetyRestriction]
|
||||
public NativeArray<UnsafeParallelHashMap<Vector3, Vector3>.ParallelWriter> result;
|
||||
public CollectNormalJob(NativeArray<Vector3> normals, NativeArray<Vector3> vertrx, NativeArray<UnsafeParallelHashMap<Vector3, Vector3>.ParallelWriter> result)
|
||||
{
|
||||
this.normals = normals;
|
||||
this.vertrx = vertrx;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
void IJobParallelFor.Execute(int index)
|
||||
{
|
||||
for (var i = 0; i < result.Length + 1; i++)
|
||||
{
|
||||
if (i == result.Length)
|
||||
{
|
||||
Debug.LogError($"Overlapping vertices count({i})has out of bound!");
|
||||
break;
|
||||
}
|
||||
|
||||
if (result[i].TryAdd(vertrx[index], normals[index]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct BakeNormalJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<Vector3> vertrx, normals;
|
||||
[ReadOnly] public NativeArray<Vector4> tangents;
|
||||
[NativeDisableContainerSafetyRestriction]
|
||||
[ReadOnly] public NativeArray<UnsafeParallelHashMap<Vector3, Vector3>> result;
|
||||
[WriteOnly] public NativeArray<Vector2> uv2;
|
||||
|
||||
public BakeNormalJob(NativeArray<Vector3> vertrx, NativeArray<Vector3> normals, NativeArray<Vector4> tangents, NativeArray<UnsafeParallelHashMap<Vector3, Vector3>> result, NativeArray<Vector2> uv2)
|
||||
{
|
||||
this.vertrx = vertrx;
|
||||
this.normals = normals;
|
||||
this.tangents = tangents;
|
||||
this.result = result;
|
||||
this.uv2 = uv2;
|
||||
}
|
||||
|
||||
void IJobParallelFor.Execute(int index)
|
||||
{
|
||||
var smoothedNormals = Vector3.zero;
|
||||
for (var i = 0; i < result.Length; i++)
|
||||
{
|
||||
if (result[i][vertrx[index]] != Vector3.zero)
|
||||
smoothedNormals += result[i][vertrx[index]];
|
||||
else
|
||||
break;
|
||||
}
|
||||
smoothedNormals = smoothedNormals.normalized;
|
||||
|
||||
var bitangent = (Vector3.Cross(normals[index], tangents[index]) * tangents[index].w).normalized;
|
||||
|
||||
var tbn = new Matrix4x4(
|
||||
tangents[index],
|
||||
bitangent,
|
||||
normals[index],
|
||||
Vector4.zero);
|
||||
tbn = tbn.transpose;
|
||||
|
||||
var bakedNormal = tbn.MultiplyVector(smoothedNormals).normalized;
|
||||
var newUV = new Vector2(bakedNormal.x, bakedNormal.y);
|
||||
uv2[index] = newUV;
|
||||
}
|
||||
}
|
||||
|
||||
//[ContextMenu("Generate smooth normal to uv2")]
|
||||
//void ProcessingModel()
|
||||
//{
|
||||
// var go = Selection.activeGameObject;
|
||||
// if (go == null)
|
||||
// {
|
||||
// Debug.LogError("Select a GameObject first!");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Dictionary<string, Mesh> originalMesh = GetMesh(go), smoothedMesh = GetMesh(go);
|
||||
|
||||
// foreach (var item in originalMesh)
|
||||
// {
|
||||
// var m = item.Value;
|
||||
// ComputeSmoothedNormalByJob(smoothedMesh[item.Key], m);
|
||||
// }
|
||||
//}
|
||||
|
||||
//void OnPreprocessModel()
|
||||
//{
|
||||
// if (assetPath.Contains("@@@"))
|
||||
// {
|
||||
// ModelImporter model = assetImporter as ModelImporter;
|
||||
// model.importNormals = ModelImporterNormals.Calculate;
|
||||
// model.normalCalculationMode = ModelImporterNormalCalculationMode.AngleWeighted;
|
||||
// model.normalSmoothingAngle = 180.0f;
|
||||
// model.importAnimation = false;
|
||||
// model.materialImportMode = ModelImporterMaterialImportMode.None;
|
||||
// }
|
||||
//}
|
||||
|
||||
//void OnPostprocessModel(GameObject g)
|
||||
//{
|
||||
// if (!g.name.Contains("_ol") || g.name.Contains("@@@"))
|
||||
// return;
|
||||
|
||||
// ModelImporter model = assetImporter as ModelImporter;
|
||||
|
||||
// string src = model.assetPath;
|
||||
// string dst = Path.GetDirectoryName(src) + "/@@@" + Path.GetFileName(src);
|
||||
|
||||
// if (!File.Exists(Application.dataPath + "/" + dst.Substring(7)))
|
||||
// {
|
||||
// AssetDatabase.CopyAsset(src, dst);
|
||||
// AssetDatabase.ImportAsset(dst);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// var go = AssetDatabase.LoadAssetAtPath<GameObject>(dst);
|
||||
|
||||
// Dictionary<string, Mesh> originalMesh = GetMesh(g), smoothedMesh = GetMesh(go);
|
||||
|
||||
// foreach (var item in originalMesh)
|
||||
// {
|
||||
// var m = item.Value;
|
||||
// ComputeSmoothedNormalByJob(smoothedMesh[item.Key], m);
|
||||
// }
|
||||
|
||||
// AssetDatabase.DeleteAsset(dst);
|
||||
// }
|
||||
// AssetDatabase.Refresh();
|
||||
//}
|
||||
}
|
||||
|
||||
public static class UTSNormalBakerHelper
|
||||
{
|
||||
public static Dictionary<string, Mesh> GetMesh(GameObject go)
|
||||
{
|
||||
static void AddMesh(Dictionary<string, Mesh> dictionary, string name, Mesh mesh)
|
||||
{
|
||||
if (dictionary.ContainsKey(name))
|
||||
Debug.LogWarning($"Model:'{name}'is duplicate!");
|
||||
else
|
||||
dictionary.Add(name, mesh);
|
||||
}
|
||||
|
||||
var dic = new Dictionary<string, Mesh>();
|
||||
foreach (var item in go.GetComponentsInChildren<MeshFilter>())
|
||||
AddMesh(dic, item.name, item.sharedMesh);
|
||||
|
||||
if (go.TryGetComponent<MeshFilter>(out var mf))
|
||||
{
|
||||
AddMesh(dic, mf.name.Replace("@", ""), mf.sharedMesh);
|
||||
}
|
||||
|
||||
foreach (var item in go.GetComponentsInChildren<SkinnedMeshRenderer>())
|
||||
{
|
||||
AddMesh(dic, item.name, item.sharedMesh);
|
||||
}
|
||||
|
||||
if (go.TryGetComponent<SkinnedMeshRenderer>(out var smr))
|
||||
{
|
||||
AddMesh(dic, smr.name.Replace("@", ""), smr.sharedMesh);
|
||||
}
|
||||
|
||||
return dic;
|
||||
}
|
||||
|
||||
public static void ComputeSmoothedNormalByJob(Mesh smoothedMesh, Mesh originalMesh, int maxOverlapvertices = 20)
|
||||
{
|
||||
int svc = smoothedMesh.vertexCount, ovc = originalMesh.vertexCount;
|
||||
|
||||
// CollectNormalJob Data
|
||||
var normals = new NativeArray<Vector3>(smoothedMesh.normals, Allocator.Persistent);
|
||||
var vertrx = new NativeArray<Vector3>(smoothedMesh.vertices, Allocator.Persistent);
|
||||
var smoothedNormals = new NativeArray<Vector3>(svc, Allocator.Persistent);
|
||||
var result = new NativeArray<UnsafeParallelHashMap<Vector3, Vector3>>(maxOverlapvertices, Allocator.Persistent);
|
||||
var resultParallel = new NativeArray<UnsafeParallelHashMap<Vector3, Vector3>.ParallelWriter>(result.Length, Allocator.Persistent);
|
||||
|
||||
// NormalBakeJob Data
|
||||
NativeArray<Vector3> normalsO = new NativeArray<Vector3>(originalMesh.normals, Allocator.Persistent),
|
||||
vertrxO = new NativeArray<Vector3>(originalMesh.vertices, Allocator.Persistent);
|
||||
var tangents = new NativeArray<Vector4>(originalMesh.tangents, Allocator.Persistent);
|
||||
var uv2 = new NativeArray<Vector2>(ovc, Allocator.Persistent);
|
||||
|
||||
for (var i = 0; i < result.Length; i++)
|
||||
{
|
||||
result[i] = new UnsafeParallelHashMap<Vector3, Vector3>(svc, Allocator.Persistent);
|
||||
resultParallel[i] = result[i].AsParallelWriter();
|
||||
}
|
||||
|
||||
var collectNormalJob = new ModelOutlineJobs.CollectNormalJob(normals, vertrx, resultParallel);
|
||||
var normalBakeJob = new ModelOutlineJobs.BakeNormalJob(vertrxO, normalsO, tangents, result, uv2);
|
||||
|
||||
normalBakeJob.Schedule(ovc, 8, collectNormalJob.Schedule(svc, 100)).Complete();
|
||||
|
||||
var _uv2 = new Vector2[ovc];
|
||||
uv2.CopyTo(_uv2);
|
||||
originalMesh.uv2 = _uv2;
|
||||
|
||||
normals.Dispose();
|
||||
vertrx.Dispose();
|
||||
result.Dispose();
|
||||
smoothedNormals.Dispose();
|
||||
resultParallel.Dispose();
|
||||
normalsO.Dispose();
|
||||
vertrxO.Dispose();
|
||||
tangents.Dispose();
|
||||
uv2.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Editor/NormalBaker/ModelOutlineJobs.cs.meta
Normal file
2
Editor/NormalBaker/ModelOutlineJobs.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d919b8f4574ad484a893adf0e159c92c
|
||||
77
Editor/NormalBaker/SmoothNormalBakerWindow.cs
Normal file
77
Editor/NormalBaker/SmoothNormalBakerWindow.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Unity.Toonshader.Editor
|
||||
{
|
||||
public class SmoothNormalBakerWindow : EditorWindow
|
||||
{
|
||||
[MenuItem("Tools/UTS/Smooth Normal Baker")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<SmoothNormalBakerWindow>("Smooth Normal Baker");
|
||||
window.ShowUtility();
|
||||
}
|
||||
|
||||
public void CreateGUI()
|
||||
{
|
||||
var objectField = new ObjectField("Input Object")
|
||||
{
|
||||
allowSceneObjects = false,
|
||||
objectType = typeof(Mesh)
|
||||
};
|
||||
|
||||
var directBakeToggle = new Toggle("Direct Bake");
|
||||
|
||||
var bakeButton = new Button()
|
||||
{
|
||||
text = "Bake"
|
||||
};
|
||||
|
||||
bakeButton.clicked += () =>
|
||||
{
|
||||
if (objectField.value is not GameObject inputObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var outputObject = inputObject;
|
||||
if (!directBakeToggle.value)
|
||||
{
|
||||
var saveLocation = EditorUtility.OpenFolderPanel("Save location", "", "");
|
||||
|
||||
if (!Directory.Exists(saveLocation))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var inputMeshLocation = AssetDatabase.GetAssetPath(inputObject);
|
||||
var outputMeshLocation = Path.Combine(saveLocation, $"{Path.GetFileNameWithoutExtension(inputMeshLocation)}_UtsSmoothedNormal{Path.GetExtension(inputMeshLocation)}");
|
||||
|
||||
if (!File.Exists(outputMeshLocation))
|
||||
{
|
||||
AssetDatabase.CopyAsset(inputMeshLocation, outputMeshLocation);
|
||||
AssetDatabase.ImportAsset(outputMeshLocation);
|
||||
}
|
||||
|
||||
outputObject = AssetDatabase.LoadAssetAtPath<GameObject>(outputMeshLocation);
|
||||
}
|
||||
|
||||
var originalMesh = UTSNormalBakerHelper.GetMesh(inputObject);
|
||||
var smoothedMesh = UTSNormalBakerHelper.GetMesh(outputObject);
|
||||
|
||||
foreach (var item in originalMesh)
|
||||
{
|
||||
var m = item.Value;
|
||||
UTSNormalBakerHelper.ComputeSmoothedNormalByJob(smoothedMesh[item.Key], m);
|
||||
}
|
||||
};
|
||||
|
||||
rootVisualElement.Add(objectField);
|
||||
rootVisualElement.Add(directBakeToggle);
|
||||
rootVisualElement.Add(bakeButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Editor/NormalBaker/SmoothNormalBakerWindow.cs.meta
Normal file
2
Editor/NormalBaker/SmoothNormalBakerWindow.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 714b5c28c6434f34182ee10216e2b979
|
||||
Reference in New Issue
Block a user