public List<ExtraMesh> GetSceneMeshes (Bounds bounds) { if ( (tagMask != null && tagMask.Count > 0) || mask != 0 ) { MeshFilter[] filters = GameObject.FindObjectsOfType(typeof(MeshFilter)) as MeshFilter[]; List<MeshFilter> filteredFilters = new List<MeshFilter> (filters.Length/3); for (int i=0;i<filters.Length;i++) { MeshFilter filter = filters[i]; if (filter.renderer != null && filter.sharedMesh != null && filter.renderer.enabled && (((1 << filter.gameObject.layer) & mask) == (1 << filter.gameObject.layer) || tagMask.Contains (filter.tag))) { if (filter.GetComponent<RecastMeshObj>() == null) { filteredFilters.Add (filter); } } } List<ExtraMesh> meshes = new List<ExtraMesh>(); Dictionary<Mesh, Vector3[]> cachedVertices = new Dictionary<Mesh, Vector3[]>(); Dictionary<Mesh, int[]> cachedTris = new Dictionary<Mesh, int[]>(); #if UNITY_4 bool containedStatic = false; #else HashSet<Mesh> staticMeshes = new HashSet<Mesh>(); #endif foreach (MeshFilter filter in filteredFilters) { //Workaround for statically batched meshes if (filter.renderer.isPartOfStaticBatch) { #if UNITY_4 containedStatic = true; #else Mesh mesh = filter.sharedMesh; if (!staticMeshes.Contains (mesh)) { staticMeshes.Add (mesh); ExtraMesh smesh = new ExtraMesh(); smesh.original = filter; if (cachedVertices.ContainsKey (mesh)) { smesh.vertices = cachedVertices[mesh]; smesh.triangles = cachedTris[mesh]; } else { smesh.vertices = mesh.vertices; smesh.triangles = mesh.triangles; cachedVertices[mesh] = smesh.vertices; cachedTris[mesh] = smesh.triangles; } //This is not the real bounds, it will be expanded later as more renderers are found smesh.bounds = filter.renderer.bounds; smesh.matrix = Matrix4x4.identity; meshes.Add (smesh); } else { ExtraMesh smesh; for (int i=0;i<meshes.Count;i++) { if (meshes[i].original == mesh) { smesh = meshes[i]; break; } } smesh.bounds.Encapsulate (filter.renderer.bounds); } #endif } else { //Only include it if it intersects with the graph if (filter.renderer.bounds.Intersects (bounds)) { Mesh mesh = filter.sharedMesh; ExtraMesh smesh = new ExtraMesh(); smesh.matrix = filter.renderer.localToWorldMatrix; smesh.original = filter; if (cachedVertices.ContainsKey (mesh)) { smesh.vertices = cachedVertices[mesh]; smesh.triangles = cachedTris[mesh]; } else { smesh.vertices = mesh.vertices; smesh.triangles = mesh.triangles; cachedVertices[mesh] = smesh.vertices; cachedTris[mesh] = smesh.triangles; } smesh.bounds = filter.renderer.bounds; meshes.Add (smesh); } } #if UNITY_4 if (containedStatic) Debug.LogWarning ("Some meshes were statically batched. These meshes can not be used for navmesh calculation" + " due to technical constraints."); #endif } #if ASTARDEBUG int y = 0; foreach (SceneMesh smesh in meshes) { y++; Vector3[] vecs = smesh.mesh.vertices; int[] tris = smesh.mesh.triangles; for (int i=0;i<tris.Length;i+=3) { Vector3 p1 = smesh.matrix.MultiplyPoint3x4(vecs[tris[i+0]]); Vector3 p2 = smesh.matrix.MultiplyPoint3x4(vecs[tris[i+1]]); Vector3 p3 = smesh.matrix.MultiplyPoint3x4(vecs[tris[i+2]]); Debug.DrawLine (p1,p2,Color.red,1); Debug.DrawLine (p2,p3,Color.red,1); Debug.DrawLine (p3,p1,Color.red,1); } } #endif return meshes; } else { return new System.Collections.Generic.List<Pathfinding.Voxels.ExtraMesh>(); } }
public void VoxelizeInput() { AstarProfiler.StartProfile("Build Navigation Mesh"); AstarProfiler.StartProfile("Voxelizing - Step 1"); Vector3 min = forcedBounds.min; voxelOffset = min; // Scale factor from world space to voxel space float ics = 1F / cellSize; float ich = 1F / cellHeight; AstarProfiler.EndProfile("Voxelizing - Step 1"); AstarProfiler.StartProfile("Voxelizing - Step 2 - Init"); float slopeLimit = Mathf.Cos(Mathf.Atan(Mathf.Tan(maxSlope * Mathf.Deg2Rad) * (ich * cellSize))); // Temporary arrays used for rasterization float[] vTris = new float[3 * 3]; float[] vOut = new float[7 * 3]; float[] vRow = new float[7 * 3]; float[] vCellOut = new float[7 * 3]; float[] vCell = new float[7 * 3]; if (inputExtraMeshes == null) { throw new System.NullReferenceException("inputExtraMeshes not set"); } //Find the largest lengths of vertex arrays and check for meshes which can be skipped int maxVerts = 0; for (int m = 0; m < inputExtraMeshes.Count; m++) { if (!inputExtraMeshes[m].bounds.Intersects(forcedBounds)) { continue; } maxVerts = System.Math.Max(inputExtraMeshes[m].vertices.Length, maxVerts); } //Create buffer, here vertices will be stored multiplied with the local-to-voxel-space matrix Vector3[] verts = new Vector3[maxVerts]; // First subtract min, then scale to voxel space (one unit equals one voxel along the x and z axes) // then subtract half a voxel to fix rounding Matrix4x4 voxelMatrix = Matrix4x4.TRS(-new Vector3(0.5f, 0, 0.5f), Quaternion.identity, Vector3.one) * Matrix4x4.Scale(new Vector3(ics, ich, ics)) * Matrix4x4.TRS(-min, Quaternion.identity, Vector3.one); AstarProfiler.EndProfile("Voxelizing - Step 2 - Init"); AstarProfiler.StartProfile("Voxelizing - Step 2"); for (int m = 0; m < inputExtraMeshes.Count; m++) { ExtraMesh mesh = inputExtraMeshes[m]; if (!mesh.bounds.Intersects(forcedBounds)) { continue; } Matrix4x4 matrix = mesh.matrix; matrix = voxelMatrix * matrix; // Flip the orientation of all faces if the mesh is scaled in such a way // that the face orientations would change // This happens for example if a mesh has a negative scale along an odd number of axes // e.g it happens for the scale (-1, 1, 1) but not for (-1, -1, 1) or (1,1,1) var flipOrientation = VectorMath.ReversesFaceOrientations(matrix); Vector3[] vs = mesh.vertices; int[] tris = mesh.triangles; int trisLength = tris.Length; for (int i = 0; i < vs.Length; i++) { verts[i] = matrix.MultiplyPoint3x4(vs[i]); } //AstarProfiler.StartFastProfile(0); int mesharea = mesh.area; for (int i = 0; i < trisLength; i += 3) { Vector3 p1; Vector3 p2; Vector3 p3; int minX; int minZ; int maxX; int maxZ; p1 = verts[tris[i]]; p2 = verts[tris[i + 1]]; p3 = verts[tris[i + 2]]; if (flipOrientation) { var tmp = p1; p1 = p3; p3 = tmp; } minX = (int)(Utility.Min(p1.x, p2.x, p3.x)); minZ = (int)(Utility.Min(p1.z, p2.z, p3.z)); maxX = (int)System.Math.Ceiling(Utility.Max(p1.x, p2.x, p3.x)); maxZ = (int)System.Math.Ceiling(Utility.Max(p1.z, p2.z, p3.z)); minX = Mathf.Clamp(minX, 0, voxelArea.width - 1); maxX = Mathf.Clamp(maxX, 0, voxelArea.width - 1); minZ = Mathf.Clamp(minZ, 0, voxelArea.depth - 1); maxZ = Mathf.Clamp(maxZ, 0, voxelArea.depth - 1); if (minX >= voxelArea.width || minZ >= voxelArea.depth || maxX <= 0 || maxZ <= 0) { continue; } // Debug code //Debug.DrawLine (p1*cellSize+min+Vector3.up*0.2F,p2*cellSize+voxelOffset+Vector3.up*0.1F,Color.red); //Debug.DrawLine (p1*cellSize+min,p2*cellSize+voxelOffset,Color.red, 1); //Debug.DrawLine (p2*cellSize+min,p3*cellSize+voxelOffset,Color.red, 1); //Debug.DrawLine (p3*cellSize+min,p1*cellSize+voxelOffset,Color.red, 1); Vector3 normal; int area; //AstarProfiler.StartProfile ("Rasterize..."); normal = Vector3.Cross(p2 - p1, p3 - p1); float dot = Vector3.Dot(normal.normalized, Vector3.up); if (dot < slopeLimit) { area = UnwalkableArea; } else { area = 1 + mesharea; } Utility.CopyVector(vTris, 0, p1); Utility.CopyVector(vTris, 3, p2); Utility.CopyVector(vTris, 6, p3); for (int x = minX; x <= maxX; x++) { int nrow = Utility.ClipPolygon(vTris, 3, vOut, 1F, -x + 0.5F, 0); if (nrow < 3) { continue; } nrow = Utility.ClipPolygon(vOut, nrow, vRow, -1F, x + 0.5F, 0); if (nrow < 3) { continue; } float clampZ1 = vRow[2]; float clampZ2 = vRow[2]; for (int q = 1; q < nrow; q++) { float val = vRow[q * 3 + 2]; clampZ1 = System.Math.Min(clampZ1, val); clampZ2 = System.Math.Max(clampZ2, val); } int clampZ1I = Mathf.Clamp((int)System.Math.Round(clampZ1), 0, voxelArea.depth - 1); int clampZ2I = Mathf.Clamp((int)System.Math.Round(clampZ2), 0, voxelArea.depth - 1); for (int z = clampZ1I; z <= clampZ2I; z++) { //AstarProfiler.StartFastProfile(1); int ncell = Utility.ClipPolygon(vRow, nrow, vCellOut, 1F, -z + 0.5F, 2); if (ncell < 3) { //AstarProfiler.EndFastProfile(1); continue; } ncell = Utility.ClipPolygonY(vCellOut, ncell, vCell, -1F, z + 0.5F, 2); if (ncell < 3) { //AstarProfiler.EndFastProfile(1); continue; } //AstarProfiler.EndFastProfile(1); //AstarProfiler.StartFastProfile(2); float sMin = vCell[1]; float sMax = vCell[1]; for (int q = 1; q < ncell; q++) { float val = vCell[q * 3 + 1]; sMin = System.Math.Min(sMin, val); sMax = System.Math.Max(sMax, val); } //AstarProfiler.EndFastProfile(2); int maxi = (int)System.Math.Ceiling(sMax); // Skip span if below the bounding box if (maxi >= 0) { // Make sure mini >= 0 int mini = System.Math.Max(0, (int)sMin); // Make sure the span is at least 1 voxel high maxi = System.Math.Max(mini + 1, maxi); voxelArea.AddLinkedSpan(z * voxelArea.width + x, (uint)mini, (uint)maxi, area, voxelWalkableClimb); } } } } //AstarProfiler.EndFastProfile(0); //AstarProfiler.EndProfile ("Rasterize..."); } AstarProfiler.EndProfile("Voxelizing - Step 2"); }
/** Rasterizes a collider to a mesh assuming it's vertices should be multiplied with the matrix. * Note that the bounds of the returned ExtraMesh is based on collider.bounds. So you might want to * call myExtraMesh.RecalculateBounds on the returned mesh to recalculate it if the collider.bounds would * not give the correct value. * */ ExtraMesh RasterizeCollider (Collider col, Matrix4x4 localToWorldMatrix) { if (col is BoxCollider) { BoxCollider collider = col as BoxCollider; Matrix4x4 matrix = Matrix4x4.TRS (collider.center, Quaternion.identity, collider.size*0.5f); matrix = localToWorldMatrix * matrix; Bounds b = collider.bounds; ExtraMesh m = new ExtraMesh(BoxColliderVerts,BoxColliderTris, b, matrix); #if ASTARDEBUG Vector3[] verts = BoxColliderVerts; int[] tris = BoxColliderTris; for (int i=0;i<tris.Length;i+=3) { Debug.DrawLine (matrix.MultiplyPoint3x4(verts[tris[i]]),matrix.MultiplyPoint3x4(verts[tris[i+1]]), Color.yellow); Debug.DrawLine (matrix.MultiplyPoint3x4(verts[tris[i+2]]),matrix.MultiplyPoint3x4(verts[tris[i+1]]), Color.yellow); Debug.DrawLine (matrix.MultiplyPoint3x4(verts[tris[i]]),matrix.MultiplyPoint3x4(verts[tris[i+2]]), Color.yellow); //Normal debug /*Vector3 va = matrix.MultiplyPoint3x4(verts[tris[i]]); Vector3 vb = matrix.MultiplyPoint3x4(verts[tris[i+1]]); Vector3 vc = matrix.MultiplyPoint3x4(verts[tris[i+2]]); Debug.DrawRay ((va+vb+vc)/3, Vector3.Cross(vb-va,vc-va).normalized,Color.blue);*/ } #endif return m; } else if (col is SphereCollider || col is CapsuleCollider) { SphereCollider scollider = col as SphereCollider; CapsuleCollider ccollider = col as CapsuleCollider; float radius = (scollider != null ? scollider.radius : ccollider.radius); float height = scollider != null ? 0 : (ccollider.height*0.5f/radius) - 1; Matrix4x4 matrix = Matrix4x4.TRS (scollider != null ? scollider.center : ccollider.center, Quaternion.identity, Vector3.one*radius); matrix = localToWorldMatrix * matrix; //Calculate the number of rows to use //grows as sqrt(x) to the radius of the sphere/capsule which I have found works quite good int rows = Mathf.Max (4,Mathf.RoundToInt(colliderRasterizeDetail*Mathf.Sqrt(matrix.MultiplyVector(Vector3.one).magnitude))); if (rows > 100) { Debug.LogWarning ("Very large detail for some collider meshes. Consider decreasing Collider Rasterize Detail (RecastGraph)"); } int cols = rows; Vector3[] verts; int[] trisArr; //Check if we have already calculated a similar capsule CapsuleCache cached = null; for (int i=0;i<capsuleCache.Count;i++) { CapsuleCache c = capsuleCache[i]; if (c.rows == rows && Mathf.Approximately (c.height, height)) { cached = c; } } if (cached == null) { //Generate a sphere/capsule mesh verts = new Vector3[(rows)*cols + 2]; List<int> tris = new List<int>(); verts[verts.Length-1] = Vector3.up; for (int r=0;r<rows;r++) { for (int c=0;c<cols;c++) { verts[c + r*cols] = new Vector3 (Mathf.Cos (c*Mathf.PI*2/cols)*Mathf.Sin ((r*Mathf.PI/(rows-1))), Mathf.Cos ((r*Mathf.PI/(rows-1))) + (r < rows/2 ? height : -height) , Mathf.Sin (c*Mathf.PI*2/cols)*Mathf.Sin ((r*Mathf.PI/(rows-1)))); } } verts[verts.Length-2] = Vector3.down; for (int i=0, j = cols-1;i<cols; j = i++) { tris.Add (verts.Length-1); tris.Add (0*cols + j); tris.Add (0*cols + i); } for (int r=1;r<rows;r++) { for (int i=0, j = cols-1;i<cols; j = i++) { tris.Add (r*cols + i); tris.Add (r*cols + j); tris.Add ((r-1)*cols + i); tris.Add ((r-1)*cols + j); tris.Add ((r-1)*cols + i); tris.Add (r*cols + j); } } for (int i=0, j = cols-1;i<cols; j = i++) { tris.Add (verts.Length-2); tris.Add ((rows-1)*cols + j); tris.Add ((rows-1)*cols + i); } //Add calculated mesh to the cache cached = new CapsuleCache (); cached.rows = rows; cached.height = height; cached.verts = verts; cached.tris = tris.ToArray(); capsuleCache.Add (cached); } //Read from cache verts = cached.verts; trisArr = cached.tris; Bounds b = col.bounds; ExtraMesh m = new ExtraMesh(verts,trisArr, b, matrix); #if ASTARDEBUG for (int i=0;i<trisArr.Length;i+=3) { Debug.DrawLine (matrix.MultiplyPoint3x4(verts[trisArr[i]]),matrix.MultiplyPoint3x4(verts[trisArr[i+1]]), Color.yellow); Debug.DrawLine (matrix.MultiplyPoint3x4(verts[trisArr[i+2]]),matrix.MultiplyPoint3x4(verts[trisArr[i+1]]), Color.yellow); Debug.DrawLine (matrix.MultiplyPoint3x4(verts[trisArr[i]]),matrix.MultiplyPoint3x4(verts[trisArr[i+2]]), Color.yellow); //Normal debug /*Vector3 va = matrix.MultiplyPoint3x4(verts[trisArr[i]]); Vector3 vb = matrix.MultiplyPoint3x4(verts[trisArr[i+1]]); Vector3 vc = matrix.MultiplyPoint3x4(verts[trisArr[i+2]]); Debug.DrawRay ((va+vb+vc)/3, Vector3.Cross(vb-va,vc-va).normalized,Color.blue);*/ } #endif return m; } else if (col is MeshCollider) { MeshCollider collider = col as MeshCollider; if ( collider.sharedMesh != null ) { ExtraMesh m = new ExtraMesh(collider.sharedMesh.vertices, collider.sharedMesh.triangles, collider.bounds, localToWorldMatrix); return m; } } return new ExtraMesh(); }
/** Find all relevant RecastMeshObj components and add ExtraMeshes for them */ public void GetRecastMeshObjs (Bounds bounds, List<ExtraMesh> buffer) { List<RecastMeshObj> buffer2 = Util.ListPool<RecastMeshObj>.Claim (); RecastMeshObj.GetAllInBounds (buffer2, bounds); Dictionary<Mesh, Vector3[]> cachedVertices = new Dictionary<Mesh, Vector3[]>(); Dictionary<Mesh, int[]> cachedTris = new Dictionary<Mesh, int[]>(); for (int i=0;i<buffer2.Count;i++) { MeshFilter filter = buffer2[i].GetMeshFilter(); if (filter != null) { Mesh mesh = filter.sharedMesh; ExtraMesh smesh = new ExtraMesh(); smesh.matrix = filter.renderer.localToWorldMatrix; smesh.original = filter; smesh.area = buffer2[i].area; if (cachedVertices.ContainsKey (mesh)) { smesh.vertices = cachedVertices[mesh]; smesh.triangles = cachedTris[mesh]; } else { smesh.vertices = mesh.vertices; smesh.triangles = mesh.triangles; cachedVertices[mesh] = smesh.vertices; cachedTris[mesh] = smesh.triangles; } smesh.bounds = filter.renderer.bounds; buffer.Add (smesh); } else { Collider coll = buffer2[i].GetCollider(); if (coll == null) { Debug.LogError ("RecastMeshObject ("+buffer2[i].gameObject.name +") didn't have a collider or MeshFilter attached"); continue; } ExtraMesh smesh = RasterizeCollider (coll); smesh.area = buffer2[i].area; //Make sure a valid ExtraMesh was returned if (smesh.vertices != null) buffer.Add(smesh); } } //Clear cache to avoid memory leak capsuleCache.Clear(); Util.ListPool<RecastMeshObj>.Release (buffer2); }
void CollectTreeMeshes (List<ExtraMesh> extraMeshes, Terrain terrain) { TerrainData data = terrain.terrainData; for (int i=0;i<data.treeInstances.Length;i++) { TreeInstance instance = data.treeInstances[i]; TreePrototype prot = data.treePrototypes[instance.prototypeIndex]; if (prot.prefab.collider == null) { Bounds b = new Bounds(terrain.transform.position + Vector3.Scale(instance.position,data.size), new Vector3(instance.widthScale,instance.heightScale,instance.widthScale)); Matrix4x4 matrix = Matrix4x4.TRS (terrain.transform.position + Vector3.Scale(instance.position,data.size), Quaternion.identity, new Vector3(instance.widthScale,instance.heightScale,instance.widthScale)*0.5f); ExtraMesh m = new ExtraMesh(BoxColliderVerts,BoxColliderTris, b, matrix); #if ASTARDEBUG Debug.DrawRay (instance.position, Vector3.up, Color.red,1); #endif extraMeshes.Add (m); } else { //The prefab has a collider, use that instead Vector3 pos = terrain.transform.position + Vector3.Scale(instance.position,data.size); Vector3 scale = new Vector3(instance.widthScale,instance.heightScale,instance.widthScale); //Generate a mesh from the collider ExtraMesh m = RasterizeCollider (prot.prefab.collider,Matrix4x4.TRS (pos,Quaternion.identity,scale)); //Make sure a valid mesh was generated if (m.vertices != null) { #if ASTARDEBUG Debug.DrawRay (pos, Vector3.up, Color.yellow,1); #endif //The bounds are incorrectly based on collider.bounds m.RecalculateBounds (); extraMeshes.Add (m); } } } }
static void GetSceneMeshes (Bounds bounds, List<string> tagMask, LayerMask layerMask, List<ExtraMesh> meshes) { if ( (tagMask != null && tagMask.Count > 0) || layerMask != 0 ) { var filters = GameObject.FindObjectsOfType(typeof(MeshFilter)) as MeshFilter[]; var filteredFilters = new List<MeshFilter> (filters.Length/3); for (int i=0;i<filters.Length;i++) { MeshFilter filter = filters[i]; Renderer rend = filter.GetComponent<Renderer>(); if (rend != null && filter.sharedMesh != null && rend.enabled && (((1 << filter.gameObject.layer) & layerMask) != 0 || tagMask.Contains (filter.tag))) { if (filter.GetComponent<RecastMeshObj>() == null) { filteredFilters.Add (filter); } } } var cachedVertices = new Dictionary<Mesh, Vector3[]>(); var cachedTris = new Dictionary<Mesh, int[]>(); bool containedStatic = false; for (int i=0;i<filteredFilters.Count;i++) { MeshFilter filter = filteredFilters[i]; // Note, guaranteed to have a renderer Renderer rend = filter.GetComponent<Renderer>(); //Workaround for statically batched meshes if (rend.isPartOfStaticBatch) { containedStatic = true; } else { //Only include it if it intersects with the graph if (rend.bounds.Intersects (bounds)) { Mesh mesh = filter.sharedMesh; var smesh = new ExtraMesh(); smesh.matrix = rend.localToWorldMatrix; smesh.original = filter; if (cachedVertices.ContainsKey (mesh)) { smesh.vertices = cachedVertices[mesh]; smesh.triangles = cachedTris[mesh]; } else { smesh.vertices = mesh.vertices; smesh.triangles = mesh.triangles; cachedVertices[mesh] = smesh.vertices; cachedTris[mesh] = smesh.triangles; } smesh.bounds = rend.bounds; meshes.Add (smesh); } } if (containedStatic) Debug.LogWarning ("Some meshes were statically batched. These meshes can not be used for navmesh calculation" + " due to technical constraints.\nDuring runtime scripts cannot access the data of meshes which have been statically batched.\n" + "One way to solve this problem is to use cached startup (Save & Load tab in the inspector) to only calculate the graph when the game is not playing."); } #if ASTARDEBUG int y = 0; foreach (ExtraMesh smesh in meshes) { y++; Vector3[] vecs = smesh.vertices; int[] tris = smesh.triangles; for (int i=0;i<tris.Length;i+=3) { Vector3 p1 = smesh.matrix.MultiplyPoint3x4(vecs[tris[i+0]]); Vector3 p2 = smesh.matrix.MultiplyPoint3x4(vecs[tris[i+1]]); Vector3 p3 = smesh.matrix.MultiplyPoint3x4(vecs[tris[i+2]]); Debug.DrawLine (p1,p2,Color.red,1); Debug.DrawLine (p2,p3,Color.red,1); Debug.DrawLine (p3,p1,Color.red,1); } } #endif } }
/** Find all relevant RecastMeshObj components and create ExtraMeshes for them */ public void GetRecastMeshObjs (Bounds bounds, List<ExtraMesh> buffer) { List<RecastMeshObj> buffer2 = Util.ListPool<RecastMeshObj>.Claim (); // Get all recast mesh objects inside the bounds RecastMeshObj.GetAllInBounds (buffer2, bounds); var cachedVertices = new Dictionary<Mesh, Vector3[]>(); var cachedTris = new Dictionary<Mesh, int[]>(); // Create an ExtraMesh object // for each RecastMeshObj for (int i=0;i<buffer2.Count;i++) { MeshFilter filter = buffer2[i].GetMeshFilter(); Renderer rend = filter != null ? filter.GetComponent<Renderer>() : null; if (filter != null && rend != null) { Mesh mesh = filter.sharedMesh; var smesh = new ExtraMesh(); smesh.matrix = rend.localToWorldMatrix; smesh.original = filter; smesh.area = buffer2[i].area; // Don't read the vertices and triangles from the // mesh if we have seen the same mesh previously if (cachedVertices.ContainsKey (mesh)) { smesh.vertices = cachedVertices[mesh]; smesh.triangles = cachedTris[mesh]; } else { smesh.vertices = mesh.vertices; smesh.triangles = mesh.triangles; cachedVertices[mesh] = smesh.vertices; cachedTris[mesh] = smesh.triangles; } smesh.bounds = rend.bounds; buffer.Add (smesh); } else { Collider coll = buffer2[i].GetCollider(); if (coll == null) { Debug.LogError ("RecastMeshObject ("+buffer2[i].gameObject.name +") didn't have a collider or MeshFilter+Renderer attached"); continue; } ExtraMesh smesh = RasterizeCollider (coll); smesh.area = buffer2[i].area; //Make sure a valid ExtraMesh was returned if (smesh.vertices != null) buffer.Add(smesh); } } //Clear cache to avoid memory leak capsuleCache.Clear(); Util.ListPool<RecastMeshObj>.Release (buffer2); }
private ExtraMesh RasterizeCollider(Collider col, Matrix4x4 localToWorldMatrix) { if (col is BoxCollider) { BoxCollider boxCollider = col as BoxCollider; Matrix4x4 matrix4x = Matrix4x4.TRS(boxCollider.center, Quaternion.identity, boxCollider.size * 0.5f); matrix4x = localToWorldMatrix * matrix4x; Bounds bounds = boxCollider.bounds; ExtraMesh result = new ExtraMesh(this.BoxColliderVerts, this.BoxColliderTris, bounds, matrix4x); return result; } if (col is SphereCollider || col is CapsuleCollider) { SphereCollider sphereCollider = col as SphereCollider; CapsuleCollider capsuleCollider = col as CapsuleCollider; float num = (!(sphereCollider != null)) ? capsuleCollider.radius : sphereCollider.radius; float num2 = (!(sphereCollider != null)) ? (capsuleCollider.height * 0.5f / num - 1f) : 0f; Matrix4x4 matrix4x2 = Matrix4x4.TRS((!(sphereCollider != null)) ? capsuleCollider.center : sphereCollider.center, Quaternion.identity, Vector3.one * num); matrix4x2 = localToWorldMatrix * matrix4x2; int num3 = Mathf.Max(4, Mathf.RoundToInt(this.colliderRasterizeDetail * Mathf.Sqrt(matrix4x2.MultiplyVector(Vector3.one).magnitude))); if (num3 > 100) { UnityEngine.Debug.LogWarning("Very large detail for some collider meshes. Consider decreasing Collider Rasterize Detail (RecastGraph)"); } int num4 = num3; RecastGraph.CapsuleCache capsuleCache = null; for (int i = 0; i < this.capsuleCache.Count; i++) { RecastGraph.CapsuleCache capsuleCache2 = this.capsuleCache[i]; if (capsuleCache2.rows == num3 && Mathf.Approximately(capsuleCache2.height, num2)) { capsuleCache = capsuleCache2; } } Vector3[] array; if (capsuleCache == null) { array = new Vector3[num3 * num4 + 2]; List<int> list = new List<int>(); array[array.Length - 1] = Vector3.up; for (int j = 0; j < num3; j++) { for (int k = 0; k < num4; k++) { array[k + j * num4] = new Vector3(Mathf.Cos((float)k * 3.14159274f * 2f / (float)num4) * Mathf.Sin((float)j * 3.14159274f / (float)(num3 - 1)), Mathf.Cos((float)j * 3.14159274f / (float)(num3 - 1)) + ((j >= num3 / 2) ? (-num2) : num2), Mathf.Sin((float)k * 3.14159274f * 2f / (float)num4) * Mathf.Sin((float)j * 3.14159274f / (float)(num3 - 1))); } } array[array.Length - 2] = Vector3.down; int l = 0; int num5 = num4 - 1; while (l < num4) { list.Add(array.Length - 1); list.Add(0 * num4 + num5); list.Add(0 * num4 + l); num5 = l++; } for (int m = 1; m < num3; m++) { int n = 0; int num6 = num4 - 1; while (n < num4) { list.Add(m * num4 + n); list.Add(m * num4 + num6); list.Add((m - 1) * num4 + n); list.Add((m - 1) * num4 + num6); list.Add((m - 1) * num4 + n); list.Add(m * num4 + num6); num6 = n++; } } int num7 = 0; int num8 = num4 - 1; while (num7 < num4) { list.Add(array.Length - 2); list.Add((num3 - 1) * num4 + num8); list.Add((num3 - 1) * num4 + num7); num8 = num7++; } capsuleCache = new RecastGraph.CapsuleCache(); capsuleCache.rows = num3; capsuleCache.height = num2; capsuleCache.verts = array; capsuleCache.tris = list.ToArray(); this.capsuleCache.Add(capsuleCache); } array = capsuleCache.verts; int[] tris = capsuleCache.tris; Bounds bounds2 = col.bounds; ExtraMesh result2 = new ExtraMesh(array, tris, bounds2, matrix4x2); return result2; } if (col is MeshCollider) { MeshCollider meshCollider = col as MeshCollider; if (meshCollider.sharedMesh != null) { ExtraMesh result3 = new ExtraMesh(meshCollider.sharedMesh.vertices, meshCollider.sharedMesh.triangles, meshCollider.bounds, localToWorldMatrix); return result3; } } return default(ExtraMesh); }
private void CollectTreeMeshes(Terrain terrain, List<ExtraMesh> extraMeshes) { TerrainData terrainData = terrain.terrainData; for (int i = 0; i < terrainData.treeInstances.Length; i++) { TreeInstance treeInstance = terrainData.treeInstances[i]; TreePrototype treePrototype = terrainData.treePrototypes[treeInstance.prototypeIndex]; if (!(treePrototype.prefab == null)) { Collider component = treePrototype.prefab.GetComponent<Collider>(); if (component == null) { Bounds b = new Bounds(terrain.transform.position + Vector3.Scale(treeInstance.position, terrainData.size), new Vector3(treeInstance.widthScale, treeInstance.heightScale, treeInstance.widthScale)); Matrix4x4 matrix = Matrix4x4.TRS(terrain.transform.position + Vector3.Scale(treeInstance.position, terrainData.size), Quaternion.identity, new Vector3(treeInstance.widthScale, treeInstance.heightScale, treeInstance.widthScale) * 0.5f); ExtraMesh item = new ExtraMesh(this.BoxColliderVerts, this.BoxColliderTris, b, matrix); extraMeshes.Add(item); } else { Vector3 pos = terrain.transform.position + Vector3.Scale(treeInstance.position, terrainData.size); Vector3 s = new Vector3(treeInstance.widthScale, treeInstance.heightScale, treeInstance.widthScale); ExtraMesh item2 = this.RasterizeCollider(component, Matrix4x4.TRS(pos, Quaternion.identity, s)); if (item2.vertices != null) { item2.RecalculateBounds(); extraMeshes.Add(item2); } } } } }