using SanAndreasUnity.Importing.Collision; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using Object = UnityEngine.Object; using UnityEngine.Profiling; namespace SanAndreasUnity.Importing.Conversion { public class CollisionModel { private static UnityEngine.Vector3 Convert(Vector3 vec) { return new UnityEngine.Vector3(vec.X, vec.Z, vec.Y); } private static Mesh Convert(IEnumerable faces, int numFaces, IEnumerable vertices, int numVertices) { Profiler.BeginSample ("Convert mesh"); // create vertices array for the mesh UnityEngine.Vector3[] meshVertices = new UnityEngine.Vector3[numVertices]; int i = 0; foreach (var v in vertices) { meshVertices [i] = Convert (v.Position); i++; } var mesh = new Mesh { vertices = meshVertices, subMeshCount = 1 }; // indices //var indices = faces.SelectMany(x => x.GetIndices()).ToArray(); // each face has 3 indices which form a single triangle, and we should also add another one // which faces the opposite direction int[] indices = new int[numFaces * 3 * 2]; i = 0; foreach (var f in faces) { indices [i++] = f.A; indices [i++] = f.B; indices [i++] = f.C; // triangle with opposite direction indices [i++] = f.B; indices [i++] = f.A; indices [i++] = f.C; } mesh.SetIndices(indices, MeshTopology.Triangles, 0); Profiler.EndSample (); return mesh; } private static Mesh Convert(FaceGroup group, ICollection faces, ICollection vertices) { int numFaces = 1 + group.EndFace - group.StartFace; return Convert(faces.Skip(group.StartFace).Take(numFaces), numFaces, vertices, vertices.Count); } private static GameObject _sTemplateParent; private static readonly Dictionary _sLoaded = new Dictionary(); public static void Load(string name, Transform destParent, bool forceConvex = false) { Load(name, null, destParent, forceConvex); } public static void Load(CollisionFile file, Transform destParent, bool forceConvex = false) { Load(file.Name, file, destParent, forceConvex); } private static void Load(string name, CollisionFile file, Transform destParent, bool forceConvex) { CollisionModel col; if (_sLoaded.ContainsKey(name)) { col = _sLoaded[name]; if (col == null) return; col.Spawn(destParent, forceConvex); return; } file = file ?? CollisionFile.FromName(name); if (file == null || (file.Flags & Flags.NotEmpty) != Flags.NotEmpty) { _sLoaded.Add(name, null); return; } col = new CollisionModel(file); _sLoaded.Add(name, col); col.Spawn(destParent, forceConvex); } public static void LoadAsync(string name, CollisionFile file, Transform destParent, bool forceConvex, System.Action onFinish) { // load collision file asyncly, and when it's ready just call the other function if (file != null) { // collision file already loaded // just call other function Utilities.F.RunExceptionSafe( () => Load(file, destParent, forceConvex) ); onFinish (); return; } // load collision file asyncly CollisionFile.FromNameAsync (name, (cf) => { // loading finished // call other function if(cf != null) Utilities.F.RunExceptionSafe( () => Load( cf, destParent, forceConvex ) ); onFinish (); }); } private readonly GameObject _template; private readonly Dictionary _flagGroups; private void Add(Surface surface, Action setup) where TCollider : Collider { Profiler.BeginSample ("Add<" + typeof(TCollider).Name + ">"); if (!_flagGroups.ContainsKey(surface.Flags)) { var group = new GameObject(string.Format("Group {0}", (int)surface.Flags)); group.transform.SetParent(_template.transform); _flagGroups.Add(surface.Flags, group.transform); } var type = typeof(TCollider); var obj = new GameObject(type.Name, type); obj.transform.SetParent(_flagGroups[surface.Flags]); setup(obj.GetComponent()); Profiler.EndSample (); } private CollisionModel(CollisionFile file) { Profiler.BeginSample ("CollisionModel()"); if (_sTemplateParent == null) { _sTemplateParent = new GameObject("Collision Templates"); _sTemplateParent.SetActive(false); } _template = new GameObject(file.Name); _template.transform.SetParent(_sTemplateParent.transform); _flagGroups = new Dictionary(); foreach (var box in file.Boxes) { Add(box.Surface, x => { var min = Convert(box.Min); var max = Convert(box.Max); x.center = (min + max) * .5f; x.size = (max - min); }); } foreach (var sphere in file.Spheres) { Add(sphere.Surface, x => { x.center = Convert(sphere.Center); x.radius = sphere.Radius; }); } if (file.FaceGroups.Length > 0) { foreach (var group in file.FaceGroups) { Add(file.Faces[group.StartFace].Surface, x => { x.sharedMesh = Convert(group, file.Faces, file.Vertices); }); } } else if (file.Faces.Length > 0) { Add(file.Faces[0].Surface, x => { x.sharedMesh = Convert(file.Faces, file.Faces.Length, file.Vertices, file.Vertices.Length); }); } // TODO: MeshCollider Profiler.EndSample (); } public void Spawn(Transform destParent, bool forceConvex) { var clone = Object.Instantiate(_template.gameObject); clone.name = "Collision"; clone.transform.SetParent(destParent, false); // Debug.Log ("Setting parent (" + destParent.name + ") for " + clone.name); if (!forceConvex) return; Profiler.BeginSample ("Adjust colliders"); foreach (var collider in clone.GetComponentsInChildren()) { var meshCollider = collider as MeshCollider; collider.gameObject.layer = 2; if (meshCollider != null) { meshCollider.convex = true; } } Profiler.EndSample (); } } }