425 lines
12 KiB
C#
425 lines
12 KiB
C#
|
//----------------------------------------------
|
||
|
// MeshBaker
|
||
|
// Copyright © 2011-2012 Ian Deane
|
||
|
//----------------------------------------------
|
||
|
using UnityEngine;
|
||
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using System.IO;
|
||
|
using System;
|
||
|
|
||
|
namespace DigitalOpus.MB.Core{
|
||
|
public class MB_Utility{
|
||
|
|
||
|
public static bool DO_INTEGRITY_CHECKS = false;
|
||
|
|
||
|
public struct MeshAnalysisResult{
|
||
|
public Rect uvRect;
|
||
|
public bool hasOutOfBoundsUVs;
|
||
|
public bool hasOverlappingSubmeshVerts;
|
||
|
public bool hasOverlappingSubmeshTris;
|
||
|
public bool hasUVs;
|
||
|
public float submeshArea;
|
||
|
}
|
||
|
|
||
|
public static Texture2D createTextureCopy(Texture2D source){
|
||
|
Texture2D newTex = new Texture2D(source.width,source.height,TextureFormat.ARGB32,true);
|
||
|
newTex.SetPixels(source.GetPixels());
|
||
|
return newTex;
|
||
|
}
|
||
|
|
||
|
public static bool ArrayBIsSubsetOfA(System.Object[] a, System.Object[] b){
|
||
|
for (int i = 0; i < b.Length; i++){
|
||
|
bool foundBinA = false;
|
||
|
for (int j = 0; j < a.Length; j++){
|
||
|
if (a[j] == b[i]){
|
||
|
foundBinA = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (foundBinA == false) return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public static Material[] GetGOMaterials(GameObject go){
|
||
|
if (go == null) return new Material[0];
|
||
|
Material[] sharedMaterials = null;
|
||
|
Mesh mesh = null;
|
||
|
MeshRenderer mr = go.GetComponent<MeshRenderer>();
|
||
|
if (mr != null){
|
||
|
sharedMaterials = mr.sharedMaterials;
|
||
|
MeshFilter mf = go.GetComponent<MeshFilter>();
|
||
|
if (mf == null){
|
||
|
throw new Exception("Object " + go + " has a MeshRenderer but no MeshFilter.");
|
||
|
}
|
||
|
mesh = mf.sharedMesh;
|
||
|
}
|
||
|
|
||
|
SkinnedMeshRenderer smr = go.GetComponent<SkinnedMeshRenderer>();
|
||
|
if (smr != null){
|
||
|
sharedMaterials = smr.sharedMaterials;
|
||
|
mesh = smr.sharedMesh;
|
||
|
}
|
||
|
|
||
|
if (sharedMaterials == null){
|
||
|
Debug.LogError("Object " + go.name + " does not have a MeshRenderer or a SkinnedMeshRenderer component");
|
||
|
return new Material[0];
|
||
|
} else if (mesh == null){
|
||
|
Debug.LogError("Object " + go.name + " has a MeshRenderer or SkinnedMeshRenderer but no mesh.");
|
||
|
return new Material[0];
|
||
|
} else {
|
||
|
if (mesh.subMeshCount < sharedMaterials.Length){
|
||
|
Debug.LogWarning("Object " + go + " has only " + mesh.subMeshCount + " submeshes and has " + sharedMaterials.Length + " materials. Extra materials do nothing.");
|
||
|
Material[] newSharedMaterials = new Material[mesh.subMeshCount];
|
||
|
Array.Copy(sharedMaterials,newSharedMaterials,newSharedMaterials.Length);
|
||
|
sharedMaterials = newSharedMaterials;
|
||
|
}
|
||
|
return sharedMaterials;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static Mesh GetMesh(GameObject go){
|
||
|
if (go == null) return null;
|
||
|
MeshFilter mf = go.GetComponent<MeshFilter>();
|
||
|
if (mf != null){
|
||
|
return mf.sharedMesh;
|
||
|
}
|
||
|
|
||
|
SkinnedMeshRenderer smr = go.GetComponent<SkinnedMeshRenderer>();
|
||
|
if (smr != null){
|
||
|
return smr.sharedMesh;
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
public static void SetMesh(GameObject go, Mesh m)
|
||
|
{
|
||
|
if (go == null) return;
|
||
|
MeshFilter mf = go.GetComponent<MeshFilter>();
|
||
|
if (mf != null)
|
||
|
{
|
||
|
mf.sharedMesh = m;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SkinnedMeshRenderer smr = go.GetComponent<SkinnedMeshRenderer>();
|
||
|
if (smr != null)
|
||
|
{
|
||
|
smr.sharedMesh = m;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static Renderer GetRenderer(GameObject go){
|
||
|
if (go == null) return null;
|
||
|
MeshRenderer mr = go.GetComponent<MeshRenderer>();
|
||
|
if (mr != null) return mr;
|
||
|
|
||
|
|
||
|
SkinnedMeshRenderer smr = go.GetComponent<SkinnedMeshRenderer>();
|
||
|
if (smr != null) return smr;
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
public static void DisableRendererInSource(GameObject go){
|
||
|
if (go == null) return;
|
||
|
MeshRenderer mf = go.GetComponent<MeshRenderer>();
|
||
|
if (mf != null){
|
||
|
mf.enabled = false;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
SkinnedMeshRenderer smr = go.GetComponent<SkinnedMeshRenderer>();
|
||
|
if (smr != null){
|
||
|
smr.enabled = false;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static bool hasOutOfBoundsUVs(Mesh m, ref Rect uvBounds){
|
||
|
MeshAnalysisResult mar = new MeshAnalysisResult();
|
||
|
bool outVal = hasOutOfBoundsUVs(m, ref mar);
|
||
|
uvBounds = mar.uvRect;
|
||
|
return outVal;
|
||
|
}
|
||
|
|
||
|
public static bool hasOutOfBoundsUVs(Mesh m, ref MeshAnalysisResult putResultHere, int submeshIndex = -1, int uvChannel = 0)
|
||
|
{
|
||
|
if (m == null)
|
||
|
{
|
||
|
putResultHere.hasOutOfBoundsUVs = false;
|
||
|
return putResultHere.hasOutOfBoundsUVs;
|
||
|
}
|
||
|
Vector2[] uvs;
|
||
|
if (uvChannel == 0) {
|
||
|
uvs = m.uv;
|
||
|
} else if (uvChannel == 1)
|
||
|
{
|
||
|
uvs = m.uv2;
|
||
|
} else if (uvChannel == 2)
|
||
|
{
|
||
|
uvs = m.uv3;
|
||
|
} else {
|
||
|
|
||
|
uvs = m.uv4;
|
||
|
}
|
||
|
return hasOutOfBoundsUVs(uvs, m, ref putResultHere, submeshIndex);
|
||
|
}
|
||
|
|
||
|
public static bool hasOutOfBoundsUVs(Vector2[] uvs, Mesh m, ref MeshAnalysisResult putResultHere, int submeshIndex = -1)
|
||
|
{
|
||
|
putResultHere.hasUVs = true;
|
||
|
if (uvs.Length == 0)
|
||
|
{
|
||
|
putResultHere.hasUVs = false;
|
||
|
putResultHere.hasOutOfBoundsUVs = false;
|
||
|
putResultHere.uvRect = new Rect();
|
||
|
return putResultHere.hasOutOfBoundsUVs;
|
||
|
}
|
||
|
float minx, miny, maxx, maxy;
|
||
|
if (submeshIndex >= m.subMeshCount)
|
||
|
{
|
||
|
putResultHere.hasOutOfBoundsUVs = false;
|
||
|
putResultHere.uvRect = new Rect();
|
||
|
return putResultHere.hasOutOfBoundsUVs;
|
||
|
}
|
||
|
else if (submeshIndex >= 0)
|
||
|
{
|
||
|
//checking specific submesh
|
||
|
int[] tris = m.GetTriangles(submeshIndex);
|
||
|
if (tris.Length == 0)
|
||
|
{
|
||
|
putResultHere.hasOutOfBoundsUVs = false;
|
||
|
putResultHere.uvRect = new Rect();
|
||
|
return putResultHere.hasOutOfBoundsUVs;
|
||
|
}
|
||
|
minx = maxx = uvs[tris[0]].x;
|
||
|
miny = maxy = uvs[tris[0]].y;
|
||
|
for (int idx = 0; idx < tris.Length; idx++)
|
||
|
{
|
||
|
int i = tris[idx];
|
||
|
if (uvs[i].x < minx) minx = uvs[i].x;
|
||
|
if (uvs[i].x > maxx) maxx = uvs[i].x;
|
||
|
if (uvs[i].y < miny) miny = uvs[i].y;
|
||
|
if (uvs[i].y > maxy) maxy = uvs[i].y;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
//checking all UVs
|
||
|
minx = maxx = uvs[0].x;
|
||
|
miny = maxy = uvs[0].y;
|
||
|
for (int i = 0; i < uvs.Length; i++)
|
||
|
{
|
||
|
if (uvs[i].x < minx) minx = uvs[i].x;
|
||
|
if (uvs[i].x > maxx) maxx = uvs[i].x;
|
||
|
if (uvs[i].y < miny) miny = uvs[i].y;
|
||
|
if (uvs[i].y > maxy) maxy = uvs[i].y;
|
||
|
}
|
||
|
}
|
||
|
Rect uvBounds = new Rect();
|
||
|
uvBounds.x = minx;
|
||
|
uvBounds.y = miny;
|
||
|
uvBounds.width = maxx - minx;
|
||
|
uvBounds.height = maxy - miny;
|
||
|
if (maxx > 1f || minx < 0f || maxy > 1f || miny < 0f)
|
||
|
{
|
||
|
putResultHere.hasOutOfBoundsUVs = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
putResultHere.hasOutOfBoundsUVs = false;
|
||
|
}
|
||
|
putResultHere.uvRect = uvBounds;
|
||
|
return putResultHere.hasOutOfBoundsUVs;
|
||
|
}
|
||
|
|
||
|
public static void setSolidColor(Texture2D t, Color c)
|
||
|
{
|
||
|
Color[] cs = t.GetPixels();
|
||
|
for (int i = 0; i < cs.Length; i++)
|
||
|
{
|
||
|
cs[i] = c;
|
||
|
}
|
||
|
t.SetPixels(cs);
|
||
|
t.Apply();
|
||
|
}
|
||
|
|
||
|
public static Texture2D resampleTexture(Texture2D source, int newWidth, int newHeight){
|
||
|
TextureFormat f = source.format;
|
||
|
if (f == TextureFormat.ARGB32 ||
|
||
|
f == TextureFormat.RGBA32 ||
|
||
|
f == TextureFormat.BGRA32 ||
|
||
|
f == TextureFormat.RGB24 ||
|
||
|
f == TextureFormat.Alpha8 ||
|
||
|
f == TextureFormat.DXT1)
|
||
|
{
|
||
|
Texture2D newTex = new Texture2D(newWidth,newHeight,TextureFormat.ARGB32,true);
|
||
|
float w = newWidth;
|
||
|
float h = newHeight;
|
||
|
for (int i = 0; i < newWidth; i++){
|
||
|
for (int j = 0; j < newHeight; j++){
|
||
|
float u = i/w;
|
||
|
float v = j/h;
|
||
|
newTex.SetPixel(i,j,source.GetPixelBilinear(u,v));
|
||
|
}
|
||
|
}
|
||
|
newTex.Apply();
|
||
|
return newTex;
|
||
|
} else {
|
||
|
Debug.LogError("Can only resize textures in formats ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. texture:" + source + " was in format: " + source.format);
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class MB_Triangle{
|
||
|
int submeshIdx;
|
||
|
int[] vs = new int[3];
|
||
|
|
||
|
public bool isSame(object obj){
|
||
|
MB_Triangle tobj = (MB_Triangle) obj;
|
||
|
if (vs[0] == tobj.vs[0] &&
|
||
|
vs[1] == tobj.vs[1] &&
|
||
|
vs[2] == tobj.vs[2] &&
|
||
|
submeshIdx != tobj.submeshIdx){
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool sharesVerts(MB_Triangle obj){
|
||
|
if (vs[0] == obj.vs[0] ||
|
||
|
vs[0] == obj.vs[1] ||
|
||
|
vs[0] == obj.vs[2]){
|
||
|
if (submeshIdx != obj.submeshIdx) return true;
|
||
|
}
|
||
|
if (vs[1] == obj.vs[0] ||
|
||
|
vs[1] == obj.vs[1] ||
|
||
|
vs[1] == obj.vs[2]){
|
||
|
if (submeshIdx != obj.submeshIdx) return true;
|
||
|
}
|
||
|
if (vs[2] == obj.vs[0] ||
|
||
|
vs[2] == obj.vs[1] ||
|
||
|
vs[2] == obj.vs[2]){
|
||
|
if (submeshIdx != obj.submeshIdx) return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public void Initialize(int[] ts, int idx, int sIdx){
|
||
|
vs[0] = ts[idx];
|
||
|
vs[1] = ts[idx + 1];
|
||
|
vs[2] = ts[idx + 2];
|
||
|
submeshIdx = sIdx;
|
||
|
Array.Sort(vs);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static bool AreAllSharedMaterialsDistinct(Material[] sharedMaterials){
|
||
|
for (int i = 0; i < sharedMaterials.Length; i++){
|
||
|
for (int j = i + 1; j < sharedMaterials.Length; j++){
|
||
|
if (sharedMaterials[i] == sharedMaterials[j]){
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public static int doSubmeshesShareVertsOrTris(Mesh m, ref MeshAnalysisResult mar){
|
||
|
MB_Triangle consider = new MB_Triangle();
|
||
|
MB_Triangle other = new MB_Triangle();
|
||
|
//cache all triangles
|
||
|
int[][] tris = new int[m.subMeshCount][];
|
||
|
for (int i = 0; i < m.subMeshCount; i++){
|
||
|
tris[i] = m.GetTriangles(i);
|
||
|
}
|
||
|
bool sharesVerts = false;
|
||
|
bool sharesTris = false;
|
||
|
for (int i = 0; i < m.subMeshCount; i++){
|
||
|
int[] smA = tris[i];
|
||
|
for (int j = i+1; j < m.subMeshCount; j++){
|
||
|
int[] smB = tris[j];
|
||
|
for (int k = 0; k < smA.Length; k+=3){
|
||
|
consider.Initialize(smA,k,i);
|
||
|
for (int l = 0; l < smB.Length; l+=3){
|
||
|
other.Initialize(smB,l,j);
|
||
|
if (consider.isSame(other)){
|
||
|
sharesTris = true;
|
||
|
break;
|
||
|
}
|
||
|
if (consider.sharesVerts(other)){
|
||
|
sharesVerts = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (sharesTris){
|
||
|
mar.hasOverlappingSubmeshVerts = true;
|
||
|
mar.hasOverlappingSubmeshTris = true;
|
||
|
return 2;
|
||
|
} else if (sharesVerts){
|
||
|
mar.hasOverlappingSubmeshVerts = true;
|
||
|
mar.hasOverlappingSubmeshTris = false;
|
||
|
return 1;
|
||
|
} else {
|
||
|
mar.hasOverlappingSubmeshTris = false;
|
||
|
mar.hasOverlappingSubmeshVerts = false;
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static bool GetBounds(GameObject go, out Bounds b){
|
||
|
if (go == null){
|
||
|
Debug.LogError("go paramater was null");
|
||
|
b = new Bounds(Vector3.zero,Vector3.zero);
|
||
|
return false;
|
||
|
}
|
||
|
Renderer r = GetRenderer(go);
|
||
|
if (r == null){
|
||
|
Debug.LogError("GetBounds must be called on an object with a Renderer");
|
||
|
b = new Bounds(Vector3.zero,Vector3.zero);
|
||
|
return false;
|
||
|
}
|
||
|
if (r is MeshRenderer){
|
||
|
b = r.bounds;
|
||
|
return true;
|
||
|
} else if (r is SkinnedMeshRenderer){
|
||
|
b = r.bounds;
|
||
|
return true;
|
||
|
}
|
||
|
Debug.LogError("GetBounds must be called on an object with a MeshRender or a SkinnedMeshRenderer.");
|
||
|
b = new Bounds(Vector3.zero,Vector3.zero);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public static void Destroy(UnityEngine.Object o){
|
||
|
if (Application.isPlaying){
|
||
|
MonoBehaviour.Destroy(o);
|
||
|
} else {
|
||
|
// string p = AssetDatabase.GetAssetPath(o);
|
||
|
// if (p != null && p.Equals("")) // don't try to destroy assets
|
||
|
MonoBehaviour.DestroyImmediate(o,false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static string ConvertAssetsRelativePathToFullSystemPath(string pth)
|
||
|
{
|
||
|
string aPth = Application.dataPath.Replace("Assets", "");
|
||
|
return aPth + pth;
|
||
|
}
|
||
|
|
||
|
public static bool IsSceneInstance(GameObject go)
|
||
|
{
|
||
|
// go.scene.name
|
||
|
// - is the name of the scene if in a scene
|
||
|
// - is the name of the prefab if in prefab edit scene
|
||
|
// - is null if is a prefab assigned from the project folder
|
||
|
return go.scene.name != null;
|
||
|
}
|
||
|
}
|
||
|
}
|