501 lines
16 KiB
C#
501 lines
16 KiB
C#
//////////////////////////////////////////////////////
|
|
// MicroSplat
|
|
// Copyright (c) Jason Booth
|
|
//////////////////////////////////////////////////////
|
|
|
|
using UnityEngine;
|
|
using System.Collections;
|
|
using UnityEditor;
|
|
using System.Collections.Generic;
|
|
|
|
#if __MICROSPLAT__ && __MICROSPLAT_MESH__
|
|
using JBooth.MicroSplat;
|
|
|
|
public partial class MicroSplatMeshEditor : Editor
|
|
{
|
|
public enum Resolutions
|
|
{
|
|
k256 = 256,
|
|
k512 = 512,
|
|
k1024 = 1024,
|
|
k2048 = 2048,
|
|
k4096 = 4096,
|
|
k8192 = 8192
|
|
};
|
|
|
|
public enum Passes
|
|
{
|
|
Albedo = 1,
|
|
Height = 2,
|
|
Normal = 4,
|
|
Metallic = 8,
|
|
Smoothness = 16,
|
|
AO = 32,
|
|
Emissive = 64,
|
|
FinalNormal = 128,
|
|
#if __MICROSPLAT_PROCTEX__
|
|
ProceduralSplat0 = 256,
|
|
ProceduralSplat1 = 512,
|
|
ProceduralSplat2 = 1024,
|
|
ProceduralSplat3 = 2048,
|
|
ProceduralSplat4 = 4096,
|
|
ProceduralSplat5 = 8192,
|
|
ProceduralSplat6 = 16384,
|
|
ProceduralSplat7 = 32768,
|
|
#endif
|
|
};
|
|
|
|
public Passes passes = 0;
|
|
public Resolutions res = Resolutions.k1024;
|
|
|
|
bool needsBake = false;
|
|
void BakingGUI(MicroSplatMesh job)
|
|
{
|
|
if (needsBake && Event.current.type == EventType.Repaint)
|
|
{
|
|
needsBake = false;
|
|
Bake(job);
|
|
}
|
|
res = (Resolutions)EditorGUILayout.EnumPopup(new GUIContent("Resolution"), res);
|
|
#if UNITY_2017_3_OR_NEWER
|
|
passes = (Passes)EditorGUILayout.EnumFlagsField(new GUIContent("Features"), passes);
|
|
#else
|
|
passes = (Passes)EditorGUILayout.EnumMaskPopup(new GUIContent("Features"), passes);
|
|
#endif
|
|
if (GUILayout.Button("Export Selected"))
|
|
{
|
|
needsBake = true;
|
|
}
|
|
}
|
|
|
|
bool IsEnabled(Passes p)
|
|
{
|
|
return ((int)passes & (int)p) == (int)p;
|
|
}
|
|
|
|
|
|
class MeshDef
|
|
{
|
|
public Vector3[] verts;
|
|
public int[] faces;
|
|
public Color[] color;
|
|
public Vector4[] uv0;
|
|
public Vector4[] uv1;
|
|
public Vector4[] uv2;
|
|
public Vector4[] uv3;
|
|
|
|
public static int kMaxVert = 60000;
|
|
|
|
public MeshDef(int triCount)
|
|
{
|
|
verts = new Vector3[triCount];
|
|
faces = new int[triCount];
|
|
color = new Color[triCount];
|
|
uv0 = new Vector4[triCount];
|
|
uv1 = new Vector4[triCount];
|
|
uv2 = new Vector4[triCount];
|
|
uv3 = new Vector4[triCount];
|
|
}
|
|
}
|
|
|
|
static void RemoveKeyword(List<string> keywords, string keyword)
|
|
{
|
|
if (keywords.Contains(keyword))
|
|
{
|
|
keywords.Remove(keyword);
|
|
}
|
|
}
|
|
|
|
static Material SetupMaterial(Material template, Material mat, MicroSplatBaseFeatures.DebugOutput debugOutput)
|
|
{
|
|
MicroSplatShaderGUI.MicroSplatCompiler comp = new MicroSplatShaderGUI.MicroSplatCompiler();
|
|
var kwds = MicroSplatUtilities.FindOrCreateKeywords(template);
|
|
List<string> keywords = new List<string>(kwds.keywords);
|
|
|
|
RemoveKeyword(keywords, "_SNOW");
|
|
RemoveKeyword(keywords, "_PARALLAX");
|
|
RemoveKeyword(keywords, "_TESSDISTANCE");
|
|
RemoveKeyword(keywords, "_WINDPARTICULATE");
|
|
RemoveKeyword(keywords, "_SNOWPARTICULATE");
|
|
RemoveKeyword(keywords, "_GLITTER");
|
|
RemoveKeyword(keywords, "_SNOWGLITTER");
|
|
RemoveKeyword(keywords, "_WORLDSPACEUV");
|
|
RemoveKeyword (keywords, "_SPECULARFROMMETALLIC");
|
|
RemoveKeyword (keywords, "_USESPECULARWORKFLOW");
|
|
RemoveKeyword (keywords, "_BDRFLAMBERT");
|
|
RemoveKeyword (keywords, "_BDRF1");
|
|
RemoveKeyword (keywords, "_BDRF2");
|
|
RemoveKeyword (keywords, "_BDRF3");
|
|
|
|
keywords.Add(FeatureFromOutput(debugOutput).ToString());
|
|
keywords.Add ("_RENDERBAKE");
|
|
keywords.Add ("_UNLIT");
|
|
|
|
|
|
// bool blendable = keywords.Contains("_MESHOVERLAYSPLATS") || keywords.Contains("_MESHCOMBINED");
|
|
string shader = comp.Compile(keywords.ToArray(), "RenderBake_" + debugOutput.ToString(), null, null); // blendable?
|
|
System.IO.File.WriteAllText ("Assets/shader" + debugOutput.ToString () + ".shader", shader);
|
|
Shader s = ShaderUtil.CreateShaderAsset(shader);
|
|
Material renderMat = new Material(mat);
|
|
renderMat.shader = s;
|
|
renderMat.CopyPropertiesFromMaterial (mat);
|
|
renderMat.enableInstancing = false;
|
|
return renderMat;
|
|
}
|
|
|
|
List<GameObject> BuildRenderObjects(MicroSplatMesh job,MicroSplatBaseFeatures.DebugOutput debugOutput)
|
|
{
|
|
List<GameObject> goes = new List<GameObject>();
|
|
|
|
|
|
Material renderMat = SetupMaterial(job.templateMaterial, job.matInstance, debugOutput);
|
|
Mesh src = job.GetComponent<MeshFilter>().sharedMesh;
|
|
var srcTri = src.triangles;
|
|
List<Vector4> srcUV0 = new List<Vector4>();
|
|
List<Vector4> srcUV1 = new List<Vector4>();
|
|
List<Vector4> srcUV2 = new List<Vector4>();
|
|
List<Vector4> srcUV3 = new List<Vector4>();
|
|
src.GetUVs(0, srcUV0);
|
|
src.GetUVs(1, srcUV1);
|
|
src.GetUVs(2, srcUV2);
|
|
src.GetUVs(3, srcUV3);
|
|
|
|
var srcColor = src.colors;
|
|
|
|
int srcVCount = src.vertexCount;
|
|
|
|
List<MeshDef> defs = new List<MeshDef>();
|
|
|
|
int triCount = srcTri.Length;
|
|
int left = triCount;
|
|
while (left > 0)
|
|
{
|
|
defs.Add(new MeshDef(Mathf.Clamp(left, 0, MeshDef.kMaxVert)));
|
|
left -= MeshDef.kMaxVert;
|
|
}
|
|
|
|
for (int i = 0; i < triCount; i++)
|
|
{
|
|
int defIdx = (int)(i / MeshDef.kMaxVert);
|
|
int idxOffset = i - (defIdx * MeshDef.kMaxVert);
|
|
var d = defs[defIdx];
|
|
d.faces[idxOffset] = idxOffset;
|
|
int vIdx = srcTri[i];
|
|
d.verts[idxOffset] = new Vector3(srcUV0[vIdx].x, srcUV0[vIdx].y, 0);
|
|
d.uv0[idxOffset] = srcUV0[vIdx];
|
|
if (idxOffset < d.uv1.Length && vIdx < srcUV1.Count)
|
|
{
|
|
d.uv1 [idxOffset] = srcUV1 [vIdx];
|
|
}
|
|
if (idxOffset < d.uv2.Length && vIdx < srcUV2.Count)
|
|
{
|
|
d.uv2[idxOffset] = srcUV2[vIdx];
|
|
}
|
|
if (idxOffset < d.uv3.Length && vIdx < srcUV3.Count)
|
|
{
|
|
d.uv3[idxOffset] = srcUV3[vIdx];
|
|
}
|
|
if (idxOffset < d.color.Length && vIdx < srcColor.Length)
|
|
{
|
|
d.color[idxOffset] = srcColor[vIdx];
|
|
}
|
|
}
|
|
for (int i = 0; i < defs.Count; ++i)
|
|
{
|
|
var d = defs[i];
|
|
Mesh renderMesh = new Mesh();
|
|
renderMesh.vertices = d.verts;
|
|
renderMesh.triangles = d.faces;
|
|
renderMesh.colors = d.color;
|
|
renderMesh.SetUVs(0, new List<Vector4>(d.uv0));
|
|
renderMesh.SetUVs(1, new List<Vector4>(d.uv1));
|
|
renderMesh.SetUVs(2, new List<Vector4>(d.uv2));
|
|
renderMesh.SetUVs(3, new List<Vector4>(d.uv3));
|
|
renderMesh.RecalculateBounds();
|
|
renderMesh.UploadMeshData(false);
|
|
|
|
|
|
GameObject go = new GameObject();
|
|
go.AddComponent<MeshRenderer>().sharedMaterial = renderMat;
|
|
go.AddComponent<MeshFilter>().sharedMesh = renderMesh;
|
|
go.transform.position = new Vector3(0, 10000, 0);
|
|
goes.Add(go);
|
|
}
|
|
|
|
return goes;
|
|
}
|
|
|
|
MicroSplatBaseFeatures.DebugOutput OutputFromPass(Passes p)
|
|
{
|
|
if (p == Passes.Albedo)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.Albedo;
|
|
}
|
|
else if (p == Passes.AO)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.AO;
|
|
}
|
|
else if (p == Passes.Emissive)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.Emission;
|
|
}
|
|
else if (p == Passes.Height)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.Height;
|
|
}
|
|
else if (p == Passes.Metallic)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.Metallic;
|
|
}
|
|
else if (p == Passes.Normal)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.Normal;
|
|
}
|
|
else if (p == Passes.Smoothness)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.Smoothness;
|
|
}
|
|
else if (p == Passes.FinalNormal)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.FinalNormalTangent;
|
|
}
|
|
#if __MICROSPLAT_PROCTEX__
|
|
else if (p == Passes.ProceduralSplat0)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput0;
|
|
}
|
|
else if (p == Passes.ProceduralSplat1)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput1;
|
|
}
|
|
else if (p == Passes.ProceduralSplat2)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput2;
|
|
}
|
|
else if (p == Passes.ProceduralSplat3)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput3;
|
|
}
|
|
else if (p == Passes.ProceduralSplat4)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput4;
|
|
}
|
|
else if (p == Passes.ProceduralSplat5)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput5;
|
|
}
|
|
else if (p == Passes.ProceduralSplat6)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput6;
|
|
}
|
|
else if (p == Passes.ProceduralSplat7)
|
|
{
|
|
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput7;
|
|
}
|
|
#endif
|
|
|
|
return MicroSplatBaseFeatures.DebugOutput.Albedo;
|
|
}
|
|
|
|
static MicroSplatBaseFeatures.DefineFeature FeatureFromOutput(MicroSplatBaseFeatures.DebugOutput p)
|
|
{
|
|
if (p == MicroSplatBaseFeatures.DebugOutput.Albedo)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_ALBEDO;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.AO)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_AO;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.Emission)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_EMISSION;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.Height)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_HEIGHT;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.Metallic)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_METAL;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.Normal)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_NORMAL;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.Smoothness)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SMOOTHNESS;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.FinalNormalTangent)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_FINALNORMALTANGENT;
|
|
}
|
|
#if __MICROSPLAT_PROCTEX__
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput0)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT0;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput1)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT1;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput2)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT2;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput3)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT3;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput4)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT4;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput5)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT5;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput6)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT6;
|
|
}
|
|
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput7)
|
|
{
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT7;
|
|
}
|
|
#endif
|
|
|
|
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_ALBEDO;
|
|
}
|
|
|
|
Camera SetupCamera()
|
|
{
|
|
Camera cam = new GameObject("cam").AddComponent<Camera>();
|
|
cam.orthographic = true;
|
|
cam.orthographicSize = 0.5f;
|
|
cam.transform.position = new Vector3(0.5f, 10000.5f, -1);
|
|
cam.nearClipPlane = 0.1f;
|
|
cam.farClipPlane = 2.0f;
|
|
cam.enabled = false;
|
|
cam.depthTextureMode = DepthTextureMode.None;
|
|
cam.clearFlags = CameraClearFlags.Color;
|
|
cam.backgroundColor = Color.grey;
|
|
return cam;
|
|
}
|
|
|
|
void Bake(MicroSplatMesh job)
|
|
{
|
|
Camera cam = SetupCamera();
|
|
string baseDir = MicroSplatUtilities.RelativePathFromAsset (job.templateMaterial);
|
|
// for each pass
|
|
int pass = 1;
|
|
Passes lastPass = Passes.Emissive;
|
|
#if __MICROSPLAT_PROCTEX__
|
|
lastPass = Passes.ProceduralSplat7;
|
|
#endif
|
|
while (pass <= (int)(lastPass))
|
|
{
|
|
Passes p = (Passes)pass;
|
|
pass *= 2;
|
|
if (!IsEnabled(p))
|
|
{
|
|
continue;
|
|
}
|
|
MicroSplatBaseFeatures.DebugOutput debugOutput = OutputFromPass(p);
|
|
|
|
var readWrite = (p == Passes.Albedo || p == Passes.Emissive) ?
|
|
RenderTextureReadWrite.sRGB : RenderTextureReadWrite.Linear;
|
|
|
|
RenderTexture rt = RenderTexture.GetTemporary((int)res, (int)res, 0, RenderTextureFormat.ARGB32, readWrite);
|
|
RenderTexture.active = rt;
|
|
cam.targetTexture = rt;
|
|
|
|
var meshes = BuildRenderObjects(job, debugOutput);
|
|
|
|
bool fog = RenderSettings.fog;
|
|
if (p == Passes.Normal)
|
|
{
|
|
cam.backgroundColor = new Color (0.5f, 0.5f, 1);
|
|
}
|
|
else if (p == Passes.Smoothness || p == Passes.Emissive || p == Passes.Metallic)
|
|
{
|
|
cam.backgroundColor = Color.black;
|
|
}
|
|
else if (p == Passes.AO)
|
|
{
|
|
cam.backgroundColor = Color.white;
|
|
}
|
|
else
|
|
{
|
|
cam.backgroundColor = Color.gray;
|
|
}
|
|
var ambInt = RenderSettings.ambientIntensity;
|
|
var reflectInt = RenderSettings.reflectionIntensity;
|
|
RenderSettings.ambientIntensity = 0;
|
|
RenderSettings.reflectionIntensity = 0;
|
|
Unsupported.SetRenderSettingsUseFogNoDirty(false);
|
|
cam.Render();
|
|
Unsupported.SetRenderSettingsUseFogNoDirty(fog);
|
|
|
|
RenderSettings.ambientIntensity = ambInt;
|
|
RenderSettings.reflectionIntensity = reflectInt;
|
|
|
|
Texture2D tex = new Texture2D((int)res, (int)res, TextureFormat.ARGB32, false, (p != Passes.Albedo && p != Passes.Emissive));
|
|
tex.ReadPixels(new Rect(0, 0, (int)res, (int)res), 0, 0);
|
|
RenderTexture.active = null;
|
|
RenderTexture.ReleaseTemporary(rt);
|
|
|
|
for (int x = 0; x < tex.width; ++x)
|
|
{
|
|
for (int y = 0; y < tex.height; ++y)
|
|
{
|
|
Color c = tex.GetPixel(x, y);
|
|
c.a = 1;
|
|
tex.SetPixel(x, y, c);
|
|
}
|
|
}
|
|
|
|
tex.Apply();
|
|
|
|
var bytes = tex.EncodeToTGA();
|
|
string texPath = baseDir + "/" + job.gameObject.name + "_" + debugOutput.ToString();
|
|
System.IO.File.WriteAllBytes(texPath + ".tga", bytes);
|
|
|
|
|
|
for (int i = 0; i < meshes.Count; ++i)
|
|
{
|
|
if (meshes[i] == null)
|
|
continue;
|
|
MeshRenderer mr = meshes[i].GetComponent<MeshRenderer>();
|
|
MeshFilter mf = meshes[i].GetComponent<MeshFilter>();
|
|
|
|
if (mr != null && mr.sharedMaterial != null)
|
|
{
|
|
if (mr.sharedMaterial.shader != null)
|
|
{
|
|
GameObject.DestroyImmediate(meshes[i].GetComponent<MeshRenderer>().sharedMaterial.shader);
|
|
}
|
|
GameObject.DestroyImmediate(meshes[i].GetComponent<MeshRenderer>().sharedMaterial);
|
|
}
|
|
|
|
if (mf != null && mf.sharedMesh != null)
|
|
{
|
|
GameObject.DestroyImmediate(meshes[i].GetComponent<MeshFilter>().sharedMesh);
|
|
}
|
|
|
|
GameObject.DestroyImmediate(meshes[i]);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
GameObject.DestroyImmediate(cam.gameObject);
|
|
AssetDatabase.Refresh();
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
#endif |