/****************************************
|
Simple Mesh Combine v1.61
|
Copyright 2015 Unluck Software
|
www.chemicalbliss.com
|
|
Change Log
|
v1.1
|
Added naming and prefab save option
|
|
v1.2
|
Added lightmap support
|
|
v1.3
|
Added multiple material support
|
v1.301
|
Fixed compile error trying to unwrap UV in game mode
|
|
v1.4
|
Added C# scripts
|
|
v1.41 - 22.01.2015
|
Changed from using SharedMaterial.Name to SharedMaterial directly to identify different materials
|
Fixed error when combining meshes with more submeshes than materials
|
|
v1.5 -24.01.2015
|
Improved editor layout, added more info and tips
|
Lightmap option as own function
|
Now sets UV2 to null to reduce mesh size
|
|
v1.53 -31.03.2015
|
Fixed lightmapping for Unity 5
|
|
v1.54 -01.05.2015
|
Fixed build error Unity 5
|
|
v1.6 & v1.61 - 28.05.2015
|
Added Export to OBJ (Beta)
|
- Used to fix/optimize combined meshes in external 3D sofware
|
- Submesh/mulitple material support limited
|
Improved Save Mesh asset
|
- Save to custom folder
|
- Overwrite keeps prefab
|
Added Copy functionions
|
- Duplicates gameObject then removes all components and empty gameObjects exept Colliders
|
- Used to create prefabs with combined mesh + colliders
|
Fixed: Deleting combined gameObject no longer gives null error
|
|
*****************************************/
|
import System.IO;
|
import System.Text;
|
import System.Collections.Generic;
|
@CustomEditor(SimpleMeshCombine)
|
public class SimpleMeshCombineEditor extends Editor {
|
function ExportMesh(meshFilter: MeshFilter, folder: String, filename: String) {
|
var path: String = SaveFile(folder, filename, "obj");
|
if (path != null) {
|
var sw: StreamWriter = new StreamWriter(path);
|
sw.Write(MeshToString(meshFilter));
|
sw.Flush();
|
sw.Close();
|
AssetDatabase.Refresh();
|
Debug.Log("Exported OBJ file to folder: " + path);
|
}
|
}
|
|
function MeshToString(meshFilter: MeshFilter): String {
|
var sMesh: Mesh = meshFilter.sharedMesh;
|
var stringBuilder: StringBuilder = new StringBuilder();
|
stringBuilder.Append("g ").Append(meshFilter.name).Append("\n");
|
for (var vert: Vector3 in sMesh.vertices) {
|
var tPoint: Vector3 = meshFilter.transform.TransformPoint(vert);
|
stringBuilder.Append(String.Format("v {0} {1} {2}\n", -tPoint.x, tPoint.y, tPoint.z));
|
}
|
stringBuilder.Append("\n");
|
for (var norm: Vector3 in sMesh.normals) {
|
var tDir: Vector3 = meshFilter.transform.TransformDirection(norm);
|
stringBuilder.Append(String.Format("vn {0} {1} {2}\n", -tDir.x, tDir.y, tDir.z));
|
}
|
stringBuilder.Append("\n");
|
for (var uv: Vector3 in sMesh.uv) {
|
stringBuilder.Append(String.Format("vt {0} {1}\n", uv.x, uv.y));
|
}
|
for (var material: int = 0; material < sMesh.subMeshCount; material++) {
|
stringBuilder.Append("\n");
|
var tris: int[] = sMesh.GetTriangles(material);
|
for (var i: int = 0; i < tris.Length; i += 3) {
|
stringBuilder.Append(String.Format("f {1}/{1}/{1} {0}/{0}/{0} {2}/{2}/{2}\n", tris[i] + 1, tris[i + 1] + 1, tris[i + 2] + 1));
|
}
|
}
|
return stringBuilder.ToString();
|
}
|
|
function SaveFile(folder: String, name: String, type: String) {
|
var path = EditorUtility.SaveFilePanel("Select Folder ", folder, name, type);
|
if (path.Length > 0) {
|
if (path.Contains("" + Application.dataPath)) {
|
var s: String = "" + path + "";
|
var d: String = "" + Application.dataPath + "/";
|
var p: String = "Assets/" + s.Remove(0, d.Length);
|
var cancel: boolean;
|
return p;
|
} else {
|
Debug.LogError("Prefab Save Failed: Can't save outside project: " + path);
|
}
|
}
|
}
|
|
var tex: Texture = Resources.Load("SMC_Title");
|
override function OnInspectorGUI() {
|
//
|
// STYLE AND COLOR
|
//
|
var color2: Color = Color.yellow;
|
var color1: Color = Color.cyan;
|
var buttonStyle = new GUIStyle(GUI.skin.button);
|
var buttonStyle2 = new GUIStyle(GUI.skin.button);
|
var titleStyle = new GUIStyle(GUI.skin.label);
|
buttonStyle.fontStyle = FontStyle.Bold;
|
buttonStyle.fixedWidth = 150;
|
buttonStyle.fixedHeight = 35;
|
buttonStyle.fontSize = 15;
|
buttonStyle2.fixedWidth = 200;
|
buttonStyle2.fixedHeight = 25;
|
buttonStyle2.margin = RectOffset((Screen.width - 200) * .5, (Screen.width - 200) * .5, 0, 0);
|
buttonStyle.margin = RectOffset((Screen.width - 150) * .5, (Screen.width - 150) * .5, 0, 0);
|
titleStyle.fixedWidth = 256;
|
titleStyle.fixedHeight = 64;
|
titleStyle.margin = RectOffset((Screen.width - 256) * .5, (Screen.width - 256) * .5, 0, 0);
|
var infoStyle = new GUIStyle(GUI.skin.label);
|
infoStyle.fontSize = 10;
|
infoStyle.margin.top = 0;
|
infoStyle.margin.bottom = 0;
|
GUILayout.Label(tex, titleStyle);
|
|
if (!Application.isPlaying) {
|
GUI.enabled = true;
|
} else {
|
GUILayout.Label("Editor can't combine in play-mode", infoStyle);
|
GUILayout.Label("Use SimpleMeshCombine.CombineMeshes();", infoStyle);
|
GUI.enabled = false;
|
}
|
GUILayout.Space(15);
|
//
|
// COMBINE MESH AREA
|
//
|
GUI.color = color1;
|
if (target.combinedGameOjects == null || target.combinedGameOjects.Length == 0) {
|
if (GUILayout.Button("Combine", buttonStyle)) {
|
if (target.transform.childCount > 1) target.CombineMeshes();
|
target.combined.isStatic = true;
|
}
|
} else {
|
if (GUILayout.Button("Release", buttonStyle)) {
|
target.EnableRenderers(true);
|
if (target.combined) DestroyImmediate(target.combined);
|
target.combinedGameOjects = null;
|
}
|
}
|
GUILayout.Space(5);
|
//
|
// SAVE MESH AREA
|
//
|
if (target.combined) {
|
if (!target._canGenerateLightmapUV) {
|
GUILayout.Label("Warning: Mesh has too high vertex count", EditorStyles.boldLabel);
|
GUI.enabled = false;
|
}
|
|
if (target.combined.GetComponent(MeshFilter).sharedMesh.name != "") {
|
GUI.enabled = false;
|
} else if(!Application.isPlaying){
|
GUI.enabled = true;
|
}
|
|
if (GUILayout.Button("Save Mesh", buttonStyle2)) {
|
var path: String = SaveFile("Assets/", target.transform.name + " [SMC Asset]", "asset");
|
if (path != null) {
|
var asset = AssetDatabase.LoadAssetAtPath(path, Object);
|
if (asset == null) {
|
AssetDatabase.CreateAsset(target.combined.GetComponent(MeshFilter).sharedMesh, path);
|
} else {
|
asset.Clear();
|
EditorUtility.CopySerialized(target.combined.GetComponent(MeshFilter).sharedMesh, asset);
|
AssetDatabase.SaveAssets();
|
}
|
target.combined.GetComponent("MeshFilter").sharedMesh = AssetDatabase.LoadAssetAtPath(path, Object);
|
Debug.Log("Saved mesh asset: " + path);
|
}
|
}
|
GUILayout.Space(5);
|
}
|
|
if(!Application.isPlaying){
|
GUI.enabled = true;
|
}
|
|
if (target.combined) {
|
if (GUILayout.Button("Export OBJ", buttonStyle2)) {
|
if (target.combined) {
|
ExportMesh(target.combined.GetComponent(MeshFilter), "Assets/", target.transform.name + " [SMC Mesh]" + ".obj");
|
}
|
}
|
GUILayout.Space(15);
|
//
|
// COPY
|
//
|
GUI.color = color2;
|
var bText:String = "Create Copy";
|
if (target.combined.GetComponent(MeshFilter).sharedMesh.name == "") {
|
bText = bText + " (Saved mesh)";
|
GUI.enabled = false;
|
} else if(!Application.isPlaying){
|
GUI.enabled = true;
|
}
|
|
if (GUILayout.Button(bText, buttonStyle2)) {
|
var newCopy = new GameObject();
|
var newCopy2 = new GameObject();
|
newCopy2.transform.parent = newCopy.transform;
|
newCopy2.transform.localPosition = target.combined.transform.localPosition;
|
newCopy2.transform.localRotation = target.combined.transform.localRotation;
|
newCopy.name = target.name + " [SMC Copy]";
|
newCopy2.name = "Mesh [SMC]";
|
newCopy.transform.position = target.transform.position;
|
newCopy.transform.rotation = target.transform.rotation;
|
|
|
var mf = newCopy2.AddComponent(MeshFilter);
|
newCopy2.AddComponent(MeshRenderer);
|
mf.sharedMesh = target.combined.GetComponent(MeshFilter).sharedMesh;
|
|
target.copyTarget = newCopy;
|
CopyMaterials(newCopy2.transform);
|
CopyColliders();
|
Selection.activeTransform = newCopy.transform;
|
|
}
|
|
|
GUILayout.Space(5);
|
if (!target.copyTarget) {
|
GUI.enabled = false;
|
} else if(!Application.isPlaying){
|
GUI.enabled = true;
|
}
|
if (GUILayout.Button("Copy Colliders", buttonStyle2)) {
|
CopyColliders();
|
}
|
GUILayout.Space(5);
|
if (GUILayout.Button("Copy Materials", buttonStyle2)) {
|
CopyMaterials(target.copyTarget.transform.FindChild("Mesh [SMC]"));
|
}
|
|
if (!Application.isPlaying) {
|
GUI.enabled = true;
|
}
|
target.destroyOldColliders = EditorGUILayout.Toggle("Destroy old colliders", target.destroyOldColliders);
|
target.keepStructure = EditorGUILayout.Toggle("Keep collider structure", target.keepStructure);
|
target.copyTarget = EditorGUILayout.ObjectField("Copy to: ", target.copyTarget, GameObject, true);
|
}
|
|
if(!target.combined){
|
target.generateLightmapUV = EditorGUILayout.Toggle("Create Lightmap UV", target.generateLightmapUV);
|
}
|
|
GUILayout.Space(5);
|
GUI.color = color1;
|
EditorGUILayout.BeginVertical("Box");
|
if (target.combined) {
|
GUILayout.Label("Vertex count: " + target.vCount + " / 65536", infoStyle);
|
GUILayout.Label("Material count: " + target.combined.GetComponent(Renderer).sharedMaterials.length, infoStyle);
|
}else{
|
GUILayout.Label("Vertex count: - / 65536", infoStyle);
|
GUILayout.Label("Material count: -", infoStyle);
|
|
}
|
EditorGUILayout.EndVertical();
|
|
|
|
if (GUI.changed) {
|
EditorUtility.SetDirty(target);
|
}
|
}
|
|
function DestroyComponentsExeptColliders(t:Transform){
|
var transforms = t.GetComponentsInChildren(typeof(Transform));
|
for(var trans : Transform in transforms){
|
if(!target.keepStructure && trans.transform.parent != t && trans.transform != t && trans.GetComponent(Collider)){
|
trans.transform.name = ""+ GetParentStructure(t, trans.transform);
|
trans.transform.parent = t;
|
}
|
}
|
var components = t.GetComponentsInChildren(typeof(Component));
|
for(var comp : Component in components){
|
if( !( comp instanceof Collider) && !( comp instanceof GameObject) && !( comp instanceof Transform) ){
|
DestroyImmediate(comp);
|
}
|
}
|
}
|
|
function GetParentStructure(root:Transform, t:Transform):String{
|
var ct:Transform = t;
|
var s:String = "";
|
while(ct !=root ){
|
s = s.Insert(0, ct.name + " - ");
|
ct = ct.parent;
|
|
}
|
s = s.Remove(s.Length-3, 3);
|
return s;
|
}
|
|
function DestroyEmptyGameObjects(t:Transform){
|
var components = t.GetComponentsInChildren(typeof(Transform));
|
for(var comp : Transform in components){
|
if(comp && (comp.childCount == 0 || !CheckChildrenForColliders(comp))){
|
var col:Collider = comp.GetComponent(Collider);
|
if(!col){
|
DestroyImmediate(comp.gameObject);
|
}
|
}
|
}
|
}
|
function CheckChildrenForColliders(t:Transform):boolean{
|
var components = t.GetComponentsInChildren(typeof(Collider));
|
if(components.Length > 0){
|
return true;
|
}
|
return false;
|
}
|
|
function CopyMaterials(t:Transform){
|
var r = t.GetComponent(Renderer);
|
r.sharedMaterials = target.combined.transform.GetComponent(Renderer).sharedMaterials;
|
}
|
|
function CopyColliders(){
|
var clone:GameObject = Instantiate(target.gameObject, target.copyTarget.transform.position, target.copyTarget.transform.rotation);
|
if(target.destroyOldColliders){
|
var o = target.copyTarget.transform.FindChild("Colliders [SMC]");
|
if(o){
|
DestroyImmediate(o.gameObject);
|
}
|
}
|
clone.transform.name = "Colliders [SMC]";
|
clone.transform.parent = target.copyTarget.transform;
|
DestroyComponentsExeptColliders(clone.transform);
|
DestroyEmptyGameObjects(clone.transform);
|
}
|
}
|