using System; using System.Collections.Generic; using System.IO; using UnityEngine; [RequireComponent(typeof(MeshRenderer))] [AddComponentMenu("MegaFiers/OBJ Cache")] [ExecuteInEditMode] [RequireComponent(typeof(MeshFilter))] public class MegaCacheOBJ : MonoBehaviour { public List meshes = new List(); public int frame; public bool animate; public float time; public float speed = 1f; public float looptime = 5f; public float fps = 25f; public MegaCacheRepeatMode loopmode; public int firstframe; public int lastframe = 1; public int skip; public string lastpath = string.Empty; public string cachefile = string.Empty; public int framevertcount; public int frametricount; public float scale = 1f; public bool adjustcoord = true; public bool buildtangents; public bool updatecollider; public bool saveuvs = true; public bool savenormals = true; public bool savetangents = true; public bool optimize = true; public bool recalcnormals; public bool update; public bool loadmtls; public MegaCacheData datasource; public MegaCacheImage cacheimage; public MeshFilter mf; public int framecount; public Vector3[] vertcache; public Vector3[] normcache; public Vector4[] tangentcache; public Vector2[] uvcache; public MegaCacheImageFace[] subs; public int decformat; public bool shownormals; public bool showextras; public float normallen = 1f; public bool showdataimport = true; public bool showanimation = true; public bool showdata; public string namesplit = string.Empty; public string runtimefolder = string.Empty; private bool optimized; private int lastreadframe = -1; private Mesh lastmesh; private int maxv; private int maxsm; private int[] maxsmfc; private FileStream fs; private BinaryReader br; private long[] meshoffs; public Mesh imagemesh; private static byte[] buffer; public bool meshchanged; private MeshCollider meshCol; [ContextMenu("Help")] public void Help() { Application.OpenURL("http://www.west-racing.com/mf/?page_id=6226"); } private void Start() { mf = GetComponent(); if (!Application.isEditor && datasource == MegaCacheData.File && fs == null) { string fileName = Path.GetFileName(cachefile); string text = Application.dataPath + "/"; if (runtimefolder.Length > 0) { text = text + runtimefolder + "/"; } text += fileName; OpenCache(text); } } public void ChangeSource(MegaCacheData src) { if (src != datasource) { CloseCache(); datasource = src; if (Application.isEditor) { UnityEngine.Object.DestroyImmediate(imagemesh); } else { UnityEngine.Object.Destroy(imagemesh); } switch (datasource) { case MegaCacheData.File: OpenCache(cachefile); break; case MegaCacheData.Image: MountImage(cacheimage); break; } update = true; } } private void Update() { int num = 0; switch (datasource) { case MegaCacheData.Mesh: num = meshes.Count - 1; break; case MegaCacheData.File: num = framecount - 1; break; case MegaCacheData.Image: if ((bool)cacheimage && cacheimage.frames != null) { num = cacheimage.frames.Count - 1; } break; } if (num > 0) { if (animate) { looptime = (float)num / fps; time += Time.deltaTime * speed; float num2 = time; switch (loopmode) { case MegaCacheRepeatMode.Loop: num2 = Mathf.Repeat(time, Mathf.Abs(looptime)); if (looptime < 0f) { num2 = looptime - num2; } break; case MegaCacheRepeatMode.PingPong: num2 = Mathf.PingPong(time, looptime); break; case MegaCacheRepeatMode.Clamp: num2 = Mathf.Clamp(time, 0f, looptime); break; } frame = (int)(num2 / looptime * (float)num); } frame = Mathf.Clamp(frame, 0, num); if (frame != lastframe) { meshchanged = true; } if (datasource == MegaCacheData.Image && (bool)cacheimage) { if (imagemesh == null) { imagemesh = new Mesh(); } if (mf.sharedMesh != imagemesh) { ClearMesh(); mf.sharedMesh = imagemesh; } cacheimage.GetMesh(imagemesh, frame, this); } if (datasource == MegaCacheData.File) { GetFrame(frame); } if (datasource == MegaCacheData.Mesh && (bool)mf && meshes.Count > 0 && (mf.sharedMesh != meshes[frame] || update)) { mf.sharedMesh = meshes[frame]; framevertcount = meshes[frame].vertexCount; } if (updatecollider && meshchanged) { if (meshCol == null) { meshCol = GetComponent(); } if (meshCol != null) { meshCol.sharedMesh = null; meshCol.sharedMesh = mf.sharedMesh; } } } update = false; meshchanged = false; } private void Reset() { } public void AddMesh(Mesh ms) { if ((bool)ms) { meshes.Add(ms); } } public void DestroyMeshes() { for (int i = 0; i < meshes.Count; i++) { if (Application.isPlaying) { UnityEngine.Object.Destroy(meshes[i]); } else { UnityEngine.Object.DestroyImmediate(meshes[i]); } } meshes.Clear(); meshes.TrimExcess(); GC.Collect(); ClearMesh(); mf.sharedMesh = new Mesh(); } public void DestroyImage() { if ((bool)cacheimage) { if (Application.isEditor) { UnityEngine.Object.DestroyImmediate(cacheimage); } else { UnityEngine.Object.Destroy(cacheimage); } cacheimage = null; } } public void ClearMesh() { if (Application.isEditor) { UnityEngine.Object.DestroyImmediate(mf.sharedMesh); } else { UnityEngine.Object.Destroy(mf.sharedMesh); } mf.sharedMesh = null; } public void InitImport() { MegaCacheObjImporter.Init(); } public Mesh LoadFrame(string filename, int frame) { Mesh result = null; char[] separator = new char[10] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; string directoryName = Path.GetDirectoryName(filename); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filename); string[] array; if (namesplit.Length > 0) { array = fileNameWithoutExtension.Split(namesplit[0]); // string[] array2; array[0] = array[0] + namesplit[0]; } else { array = fileNameWithoutExtension.Split(separator); } if (array.Length > 0) { string filename2 = directoryName + "/" + array[0] + frame.ToString("D" + decformat) + ".obj"; result = LoadFrame(filename2); } return result; } public void LoadMtl(string filename, int frame) { char[] separator = new char[10] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; string directoryName = Path.GetDirectoryName(filename); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filename); string[] array; if (namesplit.Length > 0) { array = fileNameWithoutExtension.Split(namesplit[0]); // string[] array2; array[0] = array[0] + namesplit[0]; } else { array = fileNameWithoutExtension.Split(separator); } if (array.Length > 0) { string filename2 = directoryName + "/" + array[0] + frame.ToString("D" + decformat) + ".mtl"; LoadMtl(filename2); } } public void LoadMtl(string filename) { if (File.Exists(filename)) { MegaCacheObjImporter.ImportMtl(filename); } } public Mesh LoadFrame(string filename) { Mesh result = null; if (File.Exists(filename)) { result = MegaCacheObjImporter.ImportFile(filename, scale, adjustcoord, buildtangents, loadmtls, optimize, recalcnormals); } return result; } public void MountImage(MegaCacheImage image) { if ((bool)image) { subs = new MegaCacheImageFace[image.maxsm]; for (int i = 0; i < image.maxsm; i++) { MegaCacheImageFace megaCacheImageFace = new MegaCacheImageFace(); megaCacheImageFace.max = image.smfc[i]; megaCacheImageFace.tris = new int[megaCacheImageFace.max]; subs[i] = megaCacheImageFace; } vertcache = new Vector3[image.maxv]; normcache = new Vector3[image.maxv]; tangentcache = new Vector4[image.maxv]; uvcache = new Vector2[image.maxv]; } } public void OpenCache(string filename) { if (filename.Length == 0) { return; } fs = new FileStream(filename, FileMode.Open); if (fs == null) { return; } br = new BinaryReader(fs); if (br == null) { return; } if (br.ReadInt32() == 0) { framecount = br.ReadInt32(); optimized = br.ReadBoolean(); maxv = br.ReadInt32(); br.ReadInt32(); maxsm = br.ReadInt32(); subs = new MegaCacheImageFace[maxsm]; for (int i = 0; i < maxsm; i++) { MegaCacheImageFace megaCacheImageFace = new MegaCacheImageFace(); megaCacheImageFace.max = br.ReadInt32(); megaCacheImageFace.tris = new int[megaCacheImageFace.max]; subs[i] = megaCacheImageFace; } } vertcache = new Vector3[maxv]; normcache = new Vector3[maxv]; tangentcache = new Vector4[maxv]; uvcache = new Vector2[maxv]; if (buffer == null || buffer.Length < maxv * 16) { buffer = new byte[maxv * 16]; } meshoffs = new long[framecount]; for (int j = 0; j < framecount; j++) { meshoffs[j] = br.ReadInt64(); } ClearMesh(); Mesh sharedMesh = new Mesh(); mf.sharedMesh = sharedMesh; update = true; } private void OnDestroy() { CloseCache(); } private void OnDrawGizmosSelected() { if (shownormals) { Vector3[] vertices = mf.sharedMesh.vertices; Vector3[] normals = mf.sharedMesh.normals; Gizmos.color = Color.red; Gizmos.matrix = base.transform.localToWorldMatrix; float num = normallen * 0.01f; Color black = Color.black; for (int i = 0; i < framevertcount; i++) { black.r = normals[i].x; black.g = normals[i].y; black.b = normals[i].z; Gizmos.color = black; Gizmos.DrawRay(vertices[i], normals[i] * num); } Gizmos.matrix = Matrix4x4.identity; } } private void GetFrame(int fnum) { if (br == null) { OpenCache(cachefile); } GetFrame(fnum, mf.sharedMesh); } public void GetFrameRef(int fnum, Mesh _mesh) { if (br == null) { OpenCache(cachefile); update = true; } GetFrame(fnum, _mesh); } public void GetFrame(int fnum, Mesh mesh) { if (fnum != lastreadframe || update || lastmesh == null) { MakeMeshFromFrame(fnum, mesh); lastreadframe = fnum; lastmesh = mesh; } else if (fnum == lastreadframe && mesh != lastmesh && lastmesh != null) { mesh.bounds = lastmesh.bounds; mesh.subMeshCount = lastmesh.subMeshCount; mesh.vertices = lastmesh.vertices; mesh.normals = lastmesh.normals; mesh.uv = lastmesh.uv; mesh.tangents = lastmesh.tangents; for (int i = 0; i < lastmesh.subMeshCount; i++) { mesh.SetTriangles(lastmesh.GetTriangles(i), i); } } } public void MakeMeshFromFrame(int fnum, Mesh mesh) { if (br == null) { return; } fs.Position = meshoffs[fnum]; int num = br.ReadInt32(); int num2 = br.ReadInt32(); int num3 = br.ReadInt32(); int num4 = br.ReadInt32(); Vector3 vector = default(Vector3); vector.x = br.ReadSingle(); vector.y = br.ReadSingle(); vector.z = br.ReadSingle(); Vector3 vector2 = default(Vector3); vector2.x = br.ReadSingle(); vector2.y = br.ReadSingle(); vector2.z = br.ReadSingle(); Vector3 vector3 = (vector2 - vector) * 1.5259022E-05f; mesh.bounds.SetMinMax(vector, vector2); float num5 = 1f / 127f; if (!optimized) { br.Read(buffer, 0, num * 12); for (int i = 0; i < num; i++) { int num6 = i * 12; vertcache[i].x = BitConverter.ToSingle(buffer, num6); vertcache[i].y = BitConverter.ToSingle(buffer, num6 + 4); vertcache[i].z = BitConverter.ToSingle(buffer, num6 + 8); } } else { br.Read(buffer, 0, num * 6); for (int j = 0; j < num; j++) { int num7 = j * 6; vertcache[j].x = vector.x + (float)(int)BitConverter.ToUInt16(buffer, num7) * vector3.x; vertcache[j].y = vector.y + (float)(int)BitConverter.ToUInt16(buffer, num7 + 2) * vector3.y; vertcache[j].z = vector.z + (float)(int)BitConverter.ToUInt16(buffer, num7 + 4) * vector3.z; } } if (!optimized) { br.Read(buffer, 0, num2 * 12); for (int k = 0; k < num2; k++) { int num8 = k * 12; normcache[k].x = BitConverter.ToSingle(buffer, num8); normcache[k].y = BitConverter.ToSingle(buffer, num8 + 4); normcache[k].z = BitConverter.ToSingle(buffer, num8 + 8); } } else { br.Read(buffer, 0, num2 * 3); for (int l = 0; l < num2; l++) { int num9 = l * 3; normcache[l].x = (float)(sbyte)buffer[num9] * num5; normcache[l].y = (float)(sbyte)buffer[num9 + 1] * num5; normcache[l].z = (float)(sbyte)buffer[num9 + 2] * num5; } } if (!optimized) { br.Read(buffer, 0, num4 * 16); for (int m = 0; m < num4; m++) { int num10 = m * 16; tangentcache[m].x = BitConverter.ToSingle(buffer, num10); tangentcache[m].y = BitConverter.ToSingle(buffer, num10 + 4); tangentcache[m].z = BitConverter.ToSingle(buffer, num10 + 8); tangentcache[m].w = BitConverter.ToSingle(buffer, num10 + 12); } } else { br.Read(buffer, 0, num4 * 4); for (int n = 0; n < num4; n++) { tangentcache[n].x = (float)(sbyte)buffer[n * 4] * num5; tangentcache[n].y = (float)(sbyte)buffer[n * 4 + 1] * num5; tangentcache[n].z = (float)(sbyte)buffer[n * 4 + 2] * num5; tangentcache[n].w = (float)(sbyte)buffer[n * 4 + 3] * num5; } } if (!optimized) { br.Read(buffer, 0, num3 * 8); for (int num11 = 0; num11 < num3; num11++) { int num12 = num11 * 8; uvcache[num11].x = BitConverter.ToSingle(buffer, num12); uvcache[num11].y = BitConverter.ToSingle(buffer, num12 + 4); } } else { Vector2 vector4 = default(Vector2); vector4.x = br.ReadSingle(); vector4.y = br.ReadSingle(); Vector2 vector5 = default(Vector2); vector5.x = br.ReadSingle(); vector5.y = br.ReadSingle(); Vector2 vector6 = (vector5 - vector4) * 0.003921569f; br.Read(buffer, 0, num3 * 2); for (int num13 = 0; num13 < num3; num13++) { int num14 = num13 * 2; uvcache[num13].x = vector4.x + (float)(int)buffer[num14] * vector6.x; uvcache[num13].y = vector4.y + (float)(int)buffer[num14 + 1] * vector6.y; } } byte b = (byte)(mesh.subMeshCount = br.ReadByte()); mesh.vertices = vertcache; if (num2 > 0) { mesh.normals = normcache; } if (num3 > 0) { mesh.uv = uvcache; } if (num4 > 0) { mesh.tangents = tangentcache; } for (int num16 = 0; num16 < b; num16++) { int num17 = br.ReadInt32(); br.Read(buffer, 0, num17 * 2); for (int num18 = 0; num18 < num17; num18++) { subs[num16].tris[num18] = BitConverter.ToUInt16(buffer, num18 * 2); } for (int num19 = num17; num19 < subs[num16].max; num19++) { subs[num16].tris[num19] = subs[num16].tris[num17]; } } for (int num20 = 0; num20 < b; num20++) { mesh.SetTriangles(subs[num20].tris, num20); } mesh.RecalculateBounds(); } public void CloseCache() { if (br != null) { br = null; } if (fs != null) { fs = null; } buffer = null; GC.Collect(); } public void CreateImageFromCacheFile() { if (br == null) { OpenCache(cachefile); } if (br != null) { if ((bool)cacheimage) { DestroyImage(); } MegaCacheImage megaCacheImage = ScriptableObject.CreateInstance(); megaCacheImage.maxv = maxv; megaCacheImage.maxsm = maxsm; megaCacheImage.smfc = new int[maxsm]; for (int i = 0; i < maxsm; i++) { megaCacheImage.smfc[i] = subs[i].max; } Mesh mesh = new Mesh(); for (int j = 0; j < framecount; j++) { MakeMeshFromFrame(j, mesh); MegaCacheImageFrame item = MegaCacheImage.CreateImageFrame(mesh); megaCacheImage.frames.Add(item); } cacheimage = megaCacheImage; ChangeSource(MegaCacheData.Image); if (Application.isEditor) { UnityEngine.Object.DestroyImmediate(mesh); } else { UnityEngine.Object.Destroy(mesh); } mesh = null; GC.Collect(); } } }