Files
Zombie/Assets/MantisLODEditor/Editor/InnerMantisLODEditorProfessional.cs

628 lines
24 KiB
C#

/*--------------------------------------------------------
InnerMantisLODEditorProfessional.cs
Created by MINGFEN WANG on 13-12-26.
Copyright (c) 2013 MINGFEN WANG. All rights reserved.
http://www.mesh-online.net/
--------------------------------------------------------*/
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
/*
* If you want to use the managed plugin, you must use this namespace.
*/
using MantisLOD;
using UnityEditor;
using UnityEngine;
namespace MantisLODEditor
{
[CustomEditor(typeof(MantisLODEditorProfessional))]
public class InnerMantisLODEditorProfessional: Editor {
// run when focused
[DllImport("MantisLOD")]
private static extern int create_progressive_mesh(Vector3[] vertex_array, int vertex_count, int[] triangle_array, int triangle_count, Vector3[] normal_array, int normal_count, Color[] color_array, int color_count, Vector2[] uv_array, int uv_count, int protect_boundary, int protect_detail, int protect_symmetry, int protect_normal, int protect_shape);
[DllImport("MantisLOD")]
private static extern int get_triangle_list(int index, float goal, int[] triangles, ref int triangle_count);
[DllImport("MantisLOD")]
private static extern int delete_progressive_mesh(int index);
/*
* If you want to simplify meshes at runtime, please use the managed plugin.
* The managed plugin is provided as c# script, which can run on all platforms,
* but it is slower than the native plugin.
* To use the managed plugin, You just need to add the prefix of
* 'MantisLODSimpler.' before the native APIs.
*/
private bool use_managed_plugin = false;
private int max_lod_count = 8;
private int origin_face_number = 0;
private int face_number = 0;
private float quality = 100.0f;
private float save_quality = 100.0f;
private bool protect_boundary = true;
private bool save_protect_boundary = true;
private bool protect_detail = false;
private bool save_protect_detail = false;
private bool protect_symmetry = false;
private bool save_protect_symmetry = false;
private bool protect_normal = false;
private bool save_protect_normal = false;
private bool protect_shape = true;
private bool save_protect_shape = true;
private Mantis_Mesh[] Mantis_Meshes = null;
private bool show_title = true;
private bool show_help = true;
private bool save_show_help = true;
private bool optimized = false;
override public void OnInspectorGUI() {
DrawDefaultInspector();
if(target) {
if(Mantis_Meshes != null) {
show_title = EditorGUILayout.Foldout(show_title, "Mantis LOD Editor - Professional Edition V6.3");
if(show_title) {
// A decent style. Light grey text inside a border.
GUIStyle helpStyle = new GUIStyle(GUI.skin.box);
helpStyle.wordWrap = true;
helpStyle.alignment = TextAnchor.UpperLeft;
show_help = EditorGUILayout.Foldout(show_help, show_help?"Hide Help":"Show Help");
// save all triangle lists as progressive mesh
if(GUILayout.Button("Save Progressive Mesh", GUILayout.ExpandWidth(true), GUILayout.ExpandWidth(true))) {
if (!optimized) {
optimize();
optimized = true;
}
ProgressiveMesh pm = (ProgressiveMesh)ScriptableObject.CreateInstance(typeof(ProgressiveMesh));
fill_progressive_mesh(pm);
string filePath = EditorUtility.SaveFilePanelInProject (
"Save Progressive Mesh",
((Component)target).gameObject.name + "_Progressive_Mesh.asset",
"asset",
"Choose a file location"
);
if(filePath != "") {
AssetDatabase.CreateAsset(pm, filePath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
if(show_help) {
GUILayout.Label(
"When Clicked, save the progressive meshes as a single asset file for runtime use."
, helpStyle
, GUILayout.ExpandWidth(true));
}
EditorGUILayout.Space();
// save current mesh as a lod asset file
if(GUILayout.Button("Save Current Mesh", GUILayout.ExpandWidth(true), GUILayout.ExpandWidth(true))) {
if (!optimized) {
optimize();
optimized = true;
}
foreach (Mantis_Mesh child in Mantis_Meshes) {
// clone the mesh
Mesh new_mesh = (Mesh)Instantiate(child.mesh);
// remove unused vertices
if(new_mesh.blendShapeCount == 0) {
shrink_mesh(new_mesh);
}
MeshUtility.Optimize(new_mesh);
string filePath = EditorUtility.SaveFilePanelInProject (
"Save Current Mesh",
new_mesh.name + "_Quality_" + quality.ToString() + ".asset",
"asset",
"Choose a file location"
);
if(filePath != "") {
AssetDatabase.CreateAsset(new_mesh, filePath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
}
if(show_help) {
GUILayout.Label(
"When Clicked, save the meshes of current quality as LOD asset files."
, helpStyle
, GUILayout.ExpandWidth(true));
}
max_lod_count = EditorGUILayout.IntField("Max LOD Count", max_lod_count);
if(show_help) {
GUILayout.Label(
"The maximum LOD count we want to generate."
, helpStyle
, GUILayout.ExpandWidth(true));
}
protect_boundary = EditorGUILayout.Toggle ("Protect Boundary", protect_boundary);
if(show_help) {
GUILayout.Label(
"When checked, all open boundaries will be protected; Otherwise, some smooth parts of open boundaries will be smartly merged. Both way, uv boundaries and material boundaries will be protected."
, helpStyle
, GUILayout.ExpandWidth(true));
}
protect_detail = EditorGUILayout.Toggle ("More Details", protect_detail);
if(show_help) {
GUILayout.Label(
"When checked, more details will be preserved, toggle it only if when making the highest LOD, otherwise, please leave it unchecked to get best results."
, helpStyle
, GUILayout.ExpandWidth(true));
}
protect_symmetry = EditorGUILayout.Toggle ("Protect Symmetry", protect_symmetry);
if(show_help) {
GUILayout.Label(
"When checked, all symmetric uv mapping will be preserved, you should check it only if you are making the higher LODs; Otherwise, please leave it unchecked to get best results."
, helpStyle
, GUILayout.ExpandWidth(true));
}
protect_normal = EditorGUILayout.Toggle ("Protect Hard Edge", protect_normal);
if(show_help) {
GUILayout.Label(
"When checked, all hard edges will be preserved; If you want to get maximum decimation, please leave it unchecked."
, helpStyle
, GUILayout.ExpandWidth(true));
}
protect_shape = EditorGUILayout.Toggle ("Beautiful Triangles", protect_shape);
if(show_help) {
GUILayout.Label(
"When checked, it generates beautiful triangles, but the result may not be better than the tradition method, if this happens, please leave it unchecked."
, helpStyle
, GUILayout.ExpandWidth(true));
}
EditorGUILayout.LabelField("Triangles", face_number.ToString() + "/" + origin_face_number.ToString());
if(show_help) {
GUILayout.Label(
"Display current triangle number and total triangle number."
, helpStyle
, GUILayout.ExpandWidth(true));
}
quality = EditorGUILayout.Slider ("Quality", quality, 0.0f, 100.0f);
if(show_help) {
GUILayout.Label(
"Drag to change the quality, the mesh will change in real time."
, helpStyle
, GUILayout.ExpandWidth(true));
}
if (GUI.changed) {
if(save_show_help != show_help) {
string filename = "mantis_not_show_help";
if(show_help) {
if(System.IO.File.Exists(filename)) System.IO.File.Delete(filename);
} else {
if(!System.IO.File.Exists(filename)) System.IO.File.Create(filename);
}
save_show_help = show_help;
}
if(save_protect_boundary != protect_boundary) {
quality = 100.0f;
// delete old progressive mesh
clean_all();
init_all();
// create new progressive mesh
optimize();
optimized = true;
save_protect_boundary = protect_boundary;
}
if(save_protect_detail != protect_detail) {
quality = 100.0f;
// delete old progressive mesh
clean_all();
init_all();
// create new progressive mesh
optimize();
optimized = true;
save_protect_detail = protect_detail;
}
if(save_protect_symmetry != protect_symmetry) {
quality = 100.0f;
// delete old progressive mesh
clean_all();
init_all();
// create new progressive mesh
optimize();
optimized = true;
save_protect_symmetry = protect_symmetry;
}
if(save_protect_normal != protect_normal) {
quality = 100.0f;
// delete old progressive mesh
clean_all();
init_all();
// create new progressive mesh
optimize();
optimized = true;
save_protect_normal = protect_normal;
}
if(save_protect_shape != protect_shape) {
quality = 100.0f;
// delete old progressive mesh
clean_all();
init_all();
// create new progressive mesh
optimize();
optimized = true;
save_protect_shape = protect_shape;
}
if(save_quality != quality) {
if (!optimized) {
optimize();
optimized = true;
}
face_number = 0;
foreach (Mantis_Mesh child in Mantis_Meshes) {
// get triangle list by quality value
if(child.index != -1 && (!use_managed_plugin ? get_triangle_list(child.index, quality, child.out_triangles, ref child.out_count) : MantisLODSimpler.get_triangle_list(child.index, quality, child.out_triangles, ref child.out_count)) == 1) {
if(child.out_count > 0) {
int counter = 0;
int mat = 0;
while(counter < child.out_count) {
int len = child.out_triangles[counter];
counter++;
if(len > 0) {
int[] new_triangles = new int[len];
Array.Copy(child.out_triangles, counter, new_triangles, 0, len);
child.mesh.SetTriangles(new_triangles, mat);
counter += len;
} else {
child.mesh.SetTriangles((int[])null, mat);
}
mat++;
}
face_number += child.mesh.triangles.Length/3;
// refresh normals and bounds
//child.mesh.RecalculateNormals();
//child.mesh.RecalculateBounds();
EditorUtility.SetDirty (target);
}
}
}
save_quality = quality;
}
}
}
}
}
}
private void fill_progressive_mesh(ProgressiveMesh pm) {
int triangle_count = 0;
int[][][][] temp_triangles;
temp_triangles = new int[max_lod_count][][][];
// max lod count
triangle_count++;
for (int lod=0; lod<temp_triangles.Length; lod++) {
float quality = 100.0f * (temp_triangles.Length - lod) / temp_triangles.Length;
temp_triangles[lod] = new int[Mantis_Meshes.Length][][];
// mesh count
triangle_count++;
int mesh_count = 0;
pm.uuids = new string[Mantis_Meshes.Length];
foreach (Mantis_Mesh child in Mantis_Meshes) {
// get triangle list by quality value
if(child.index != -1 && (!use_managed_plugin ? get_triangle_list(child.index, quality, child.out_triangles, ref child.out_count) : MantisLODSimpler.get_triangle_list(child.index, quality, child.out_triangles, ref child.out_count)) == 1) {
if(child.out_count > 0) {
int counter = 0;
int mat = 0;
temp_triangles[lod][mesh_count] = new int[child.mesh.subMeshCount][];
// sub mesh count
triangle_count++;
while(counter < child.out_count) {
int len = child.out_triangles[counter];
// triangle count
triangle_count++;
// triangle list count
triangle_count += len;
counter++;
int[] new_triangles = new int[len];
Array.Copy(child.out_triangles, counter, new_triangles, 0, len);
temp_triangles[lod][mesh_count][mat] = new_triangles;
counter += len;
mat++;
}
} else {
temp_triangles[lod][mesh_count] = new int[child.mesh.subMeshCount][];
// sub mesh count
triangle_count++;
for (int mat=0; mat<temp_triangles[lod][mesh_count].Length; mat++) {
temp_triangles[lod][mesh_count][mat] = new int[0];
// triangle count
triangle_count++;
}
}
}
pm.uuids[mesh_count] = child.uuid;
mesh_count++;
}
}
// create fix size array
pm.triangles = new int[triangle_count];
// reset the counter
triangle_count = 0;
// max lod count
pm.triangles[triangle_count] = temp_triangles.Length;
triangle_count++;
for (int lod=0; lod<temp_triangles.Length; lod++) {
// mesh count
pm.triangles[triangle_count] = temp_triangles[lod].Length;
triangle_count++;
for (int mesh_count=0; mesh_count<temp_triangles[lod].Length; mesh_count++) {
// sub mesh count
pm.triangles[triangle_count] = temp_triangles[lod][mesh_count].Length;
triangle_count++;
for (int mat=0; mat<temp_triangles[lod][mesh_count].Length; mat++) {
// triangle count
pm.triangles[triangle_count] = temp_triangles[lod][mesh_count][mat].Length;
triangle_count++;
Array.Copy(temp_triangles[lod][mesh_count][mat], 0, pm.triangles, triangle_count, temp_triangles[lod][mesh_count][mat].Length);
// triangle list count
triangle_count += temp_triangles[lod][mesh_count][mat].Length;
}
}
}
}
private void shrink_mesh(Mesh mesh) {
// get all origin data
Vector3[] origin_vertices = mesh.vertices;
Vector3[] vertices = null;
if(origin_vertices != null && origin_vertices.Length > 0) vertices = new Vector3[origin_vertices.Length];
BoneWeight[] origin_boneWeights = mesh.boneWeights;
BoneWeight[] boneWeights = null;
if(origin_boneWeights != null && origin_boneWeights.Length > 0) boneWeights = new BoneWeight[origin_boneWeights.Length];
Color[] origin_colors = mesh.colors;
Color[] colors = null;
if(origin_colors != null && origin_colors.Length > 0) colors = new Color[origin_colors.Length];
Color32[] origin_colors32 = mesh.colors32;
Color32[] colors32 = null;
if(origin_colors32 != null && origin_colors32.Length > 0) colors32 = new Color32[origin_colors32.Length];
Vector4[] origin_tangents = mesh.tangents;
Vector4[] tangents = null;
if(origin_tangents != null && origin_tangents.Length > 0) tangents = new Vector4[origin_tangents.Length];
Vector3[] origin_normals = mesh.normals;
Vector3[] normals = null;
if(origin_normals != null && origin_normals.Length > 0) normals = new Vector3[origin_normals.Length];
Vector2[] origin_uv = mesh.uv;
Vector2[] uv = null;
if(origin_uv != null && origin_uv.Length > 0) uv = new Vector2[origin_uv.Length];
Vector2[] origin_uv2 = mesh.uv2;
Vector2[] uv2 = null;
if(origin_uv2 != null && origin_uv2.Length > 0) uv2 = new Vector2[origin_uv2.Length];
int[][] origin_triangles = new int[mesh.subMeshCount][];
for (int i=0; i<mesh.subMeshCount; i++) {
origin_triangles[i] = mesh.GetTriangles(i);
}
// make permutation
Dictionary<int, int> imap = new Dictionary<int, int>();
int vertex_count = 0;
for (int i=0; i<mesh.subMeshCount; i++) {
int[] triangles = mesh.GetTriangles(i);
for(int j=0; j<triangles.Length; j += 3) {
if(!imap.ContainsKey(triangles[j])) {
if(vertices != null) vertices[vertex_count] = origin_vertices[triangles[j]];
if(boneWeights != null) boneWeights[vertex_count] = origin_boneWeights[triangles[j]];
if(colors != null) colors[vertex_count] = origin_colors[triangles[j]];
if(colors32 != null) colors32[vertex_count] = origin_colors32[triangles[j]];
if(tangents != null) tangents[vertex_count] = origin_tangents[triangles[j]];
if(normals != null) normals[vertex_count] = origin_normals[triangles[j]];
if(uv != null) uv[vertex_count] = origin_uv[triangles[j]];
if(uv2 != null) uv2[vertex_count] = origin_uv2[triangles[j]];
imap.Add(triangles[j], vertex_count);
vertex_count++;
}
if(!imap.ContainsKey(triangles[j+1])) {
if(vertices != null) vertices[vertex_count] = origin_vertices[triangles[j+1]];
if(boneWeights != null) boneWeights[vertex_count] = origin_boneWeights[triangles[j+1]];
if(colors != null) colors[vertex_count] = origin_colors[triangles[j+1]];
if(colors32 != null) colors32[vertex_count] = origin_colors32[triangles[j+1]];
if(tangents != null) tangents[vertex_count] = origin_tangents[triangles[j+1]];
if(normals != null) normals[vertex_count] = origin_normals[triangles[j+1]];
if(uv != null) uv[vertex_count] = origin_uv[triangles[j+1]];
if(uv2 != null) uv2[vertex_count] = origin_uv2[triangles[j+1]];
imap.Add(triangles[j+1], vertex_count);
vertex_count++;
}
if(!imap.ContainsKey(triangles[j+2])) {
if(vertices != null) vertices[vertex_count] = origin_vertices[triangles[j+2]];
if(boneWeights != null) boneWeights[vertex_count] = origin_boneWeights[triangles[j+2]];
if(colors != null) colors[vertex_count] = origin_colors[triangles[j+2]];
if(colors32 != null) colors32[vertex_count] = origin_colors32[triangles[j+2]];
if(tangents != null) tangents[vertex_count] = origin_tangents[triangles[j+2]];
if(normals != null) normals[vertex_count] = origin_normals[triangles[j+2]];
if(uv != null) uv[vertex_count] = origin_uv[triangles[j+2]];
if(uv2 != null) uv2[vertex_count] = origin_uv2[triangles[j+2]];
imap.Add(triangles[j+2], vertex_count);
vertex_count++;
}
}
}
// set data back to mesh
mesh.Clear (false);
if (vertices != null) {
Vector3[] new_vertices = new Vector3[vertex_count];
Array.Copy(vertices, new_vertices, vertex_count);
mesh.vertices = new_vertices;
}
if (boneWeights != null) {
BoneWeight[] new_boneWeights = new BoneWeight[vertex_count];
Array.Copy(boneWeights, new_boneWeights, vertex_count);
mesh.boneWeights = new_boneWeights;
}
if (colors != null) {
Color[] new_colors = new Color[vertex_count];
Array.Copy(colors, new_colors, vertex_count);
mesh.colors = new_colors;
}
if (colors32 != null) {
Color32[] new_colors32 = new Color32[vertex_count];
Array.Copy(colors32, new_colors32, vertex_count);
mesh.colors32 = new_colors32;
}
if (tangents != null) {
Vector4[] new_tangents = new Vector4[vertex_count];
Array.Copy(tangents, new_tangents, vertex_count);
mesh.tangents = new_tangents;
}
if (normals != null) {
Vector3[] new_normals = new Vector3[vertex_count];
Array.Copy(normals, new_normals, vertex_count);
mesh.normals = new_normals;
}
if (uv != null) {
Vector2[] new_uv = new Vector2[vertex_count];
Array.Copy(uv, new_uv, vertex_count);
mesh.uv = new_uv;
}
if (uv2 != null) {
Vector2[] new_uv2 = new Vector2[vertex_count];
Array.Copy(uv2, new_uv2, vertex_count);
mesh.uv2 = new_uv2;
}
mesh.subMeshCount = origin_triangles.Length;
for (int i=0; i<mesh.subMeshCount; i++) {
int[] new_triangles = new int[origin_triangles[i].Length];
for(int j=0; j<new_triangles.Length; j += 3) {
new_triangles[j] = (int)imap[origin_triangles[i][j]];
new_triangles[j+1] = (int)imap[origin_triangles[i][j+1]];
new_triangles[j+2] = (int)imap[origin_triangles[i][j+2]];
}
mesh.SetTriangles(new_triangles, i);
}
// refresh normals and bounds
//mesh.RecalculateNormals();
//mesh.RecalculateBounds();
}
private string get_uuid_from_mesh(Mesh mesh) {
string uuid = mesh.name + "_" + mesh.vertexCount.ToString() + "_" + mesh.subMeshCount.ToString();
for (int i=0; i<mesh.subMeshCount; ++i) {
uuid += "_" + mesh.GetIndexCount(i).ToString();
}
return uuid;
}
private void get_all_meshes() {
Component[] allFilters = (Component[])((Component)target).gameObject.GetComponentsInChildren (typeof(MeshFilter));
Component[] allRenderers = (Component[])((Component)target).gameObject.GetComponentsInChildren (typeof(SkinnedMeshRenderer));
int mesh_count = allFilters.Length + allRenderers.Length;
if (mesh_count > 0) {
Mantis_Meshes = new Mantis_Mesh[mesh_count];
int counter = 0;
foreach (Component child in allFilters) {
Mantis_Meshes[counter] = new Mantis_Mesh();
Mantis_Meshes[counter].mesh = ((MeshFilter)child).sharedMesh;
Mantis_Meshes[counter].uuid = get_uuid_from_mesh(((MeshFilter)child).sharedMesh);
counter++;
}
foreach (Component child in allRenderers) {
Mantis_Meshes[counter] = new Mantis_Mesh();
Mantis_Meshes[counter].mesh = ((SkinnedMeshRenderer)child).sharedMesh;
Mantis_Meshes[counter].uuid = get_uuid_from_mesh(((SkinnedMeshRenderer)child).sharedMesh);
counter++;
}
}
}
void Awake() {
init_all();
}
private void init_all() {
if (Mantis_Meshes == null) {
if(target) {
get_all_meshes();
if (Mantis_Meshes != null) {
face_number = 0;
foreach (Mantis_Mesh child in Mantis_Meshes) {
int triangle_number = child.mesh.triangles.Length;
child.origin_triangles = new int[child.mesh.subMeshCount][];
// out data is large than origin data
child.out_triangles = new int[triangle_number+child.mesh.subMeshCount];
for(int i=0; i<child.mesh.subMeshCount; i++) {
int[] sub_triangles = child.mesh.GetTriangles(i);
face_number += sub_triangles.Length/3;
// save origin triangle list
child.origin_triangles[i] = new int[sub_triangles.Length];
Array.Copy(sub_triangles, child.origin_triangles[i], sub_triangles.Length);
}
child.index = -1;
}
origin_face_number = face_number;
string filename2 = "mantis_not_show_help";
if(System.IO.File.Exists(filename2)) {
show_help = false;
save_show_help = false;
} else {
show_help = true;
save_show_help = true;
}
}
}
}
}
private void optimize() {
if(target) {
if (Mantis_Meshes != null) {
foreach (Mantis_Mesh child in Mantis_Meshes) {
int triangle_number = child.mesh.triangles.Length;
Vector3[] vertices = child.mesh.vertices;
// in data is large than origin data
int[] triangles = new int[triangle_number+child.mesh.subMeshCount];
// we need normal data to protect normal boundary
Vector3[] normals = child.mesh.normals;
// we need color data to protect color boundary
Color[] colors = child.mesh.colors;
// we need uv data to protect uv boundary
Vector2[] uvs = child.mesh.uv;
int counter = 0;
for(int i=0; i<child.mesh.subMeshCount; i++) {
int[] sub_triangles = child.mesh.GetTriangles(i);
triangles[counter] = sub_triangles.Length;
counter++;
Array.Copy(sub_triangles, 0, triangles, counter, sub_triangles.Length);
counter += sub_triangles.Length;
}
// create progressive mesh
if (!use_managed_plugin)
{
child.index = create_progressive_mesh(vertices, vertices.Length, triangles, counter, normals, normals.Length, colors, colors.Length, uvs, uvs.Length, protect_boundary ? 1 : 0, protect_detail ? 1 : 0, protect_symmetry ? 1 : 0, protect_normal ? 1 : 0, protect_shape ? 1 : 0);
}
else
{
child.index = MantisLODSimpler.create_progressive_mesh(vertices, vertices.Length, triangles, counter, normals, normals.Length, colors, colors.Length, uvs, uvs.Length, protect_boundary ? 1 : 0, protect_detail ? 1 : 0, protect_symmetry ? 1 : 0, protect_normal ? 1 : 0, protect_shape ? 1 : 0);
}
}
}
}
}
private void clean_all() {
// restore triangle list
if (Mantis_Meshes != null) {
if(target) {
foreach (Mantis_Mesh child in Mantis_Meshes) {
if(child.index != -1) {
for(int i=0; i<child.mesh.subMeshCount; i++) {
child.mesh.SetTriangles(child.origin_triangles[i], i);
}
//child.mesh.RecalculateNormals();
//child.mesh.RecalculateBounds();
// do not need it
if (!use_managed_plugin)
{
delete_progressive_mesh(child.index);
}
else
{
MantisLODSimpler.delete_progressive_mesh(child.index);
}
child.index = -1;
}
}
}
Mantis_Meshes = null;
}
}
public void OnDisable() {
clean_all();
}
public void OnDestroy() {
clean_all ();
}
}
}