private static extern bool GetHullInfo(uint uHullIndex, ref SConvexDecompositionHullInfo infoOut);
private static bool ComputeHull(GameObject gameObject, FracturedObject.ECCAlgorithm eAlgorithm, int nMaxHulls, int nMaxHullVertices, int nLegacySteps, bool bVerbose, out int nTotalTrianglesOut) { MeshFilter theMesh = (MeshFilter)gameObject.GetComponent<MeshFilter>(); DllInit(true); SetLogFunctionPointer(Marshal.GetFunctionPointerForDelegate(new LogDelegate(Log))); SConvexDecompositionInfoInOut info = new SConvexDecompositionInfoInOut(); nTotalTrianglesOut = 0; if(theMesh) { if(theMesh.sharedMesh) { uint uLegacySteps = (uint)(Mathf.Max(1, nLegacySteps)); info.uMaxHullVertices = (uint)(Mathf.Max(3, nMaxHullVertices)); info.uMaxHulls = (uint)nMaxHulls; info.fPrecision = 0.2f; info.fBackFaceDistanceFactor = 0.2f; info.uLegacyDepth = eAlgorithm == FracturedObject.ECCAlgorithm.Legacy ? uLegacySteps : 0; info.uNormalizeInputMesh = 0; info.uUseFastVersion = eAlgorithm == FracturedObject.ECCAlgorithm.Fast ? (uint)1 : (uint)0; info.uTriangleCount = (uint)theMesh.sharedMesh.triangles.Length / 3; info.uVertexCount = (uint)theMesh.sharedMesh.vertexCount; Vector3[] av3Vertices = theMesh.sharedMesh.vertices; if(DoConvexDecomposition(ref info, av3Vertices, theMesh.sharedMesh.triangles)) { for(int nHull = 0; nHull < info.nHullsOut; nHull++) { SConvexDecompositionHullInfo hullInfo = new SConvexDecompositionHullInfo(); GetHullInfo((uint)nHull, ref hullInfo); if(hullInfo.nTriangleCount > 0) { Vector3[] hullVertices = new Vector3[hullInfo.nVertexCount]; int[] hullIndices = new int[hullInfo.nTriangleCount * 3]; float fHullVolume = -1.0f; FillHullMeshData((uint)nHull, ref fHullVolume, hullIndices, hullVertices); Mesh hullMesh = new Mesh(); hullMesh.vertices = hullVertices; hullMesh.triangles = hullIndices; hullMesh.uv = new Vector2[hullVertices.Length]; hullMesh.RecalculateNormals(); GameObject goNewHull = new GameObject("Hull " + (nHull + 1)); goNewHull.transform.position = gameObject.transform.position; goNewHull.transform.rotation = gameObject.transform.rotation; goNewHull.transform.localScale = gameObject.transform.localScale; goNewHull.transform.parent = gameObject.transform; goNewHull.layer = gameObject.layer; MeshCollider meshCollider = goNewHull.AddComponent<MeshCollider>() as MeshCollider; meshCollider.sharedMesh = null; meshCollider.sharedMesh = hullMesh; meshCollider.convex = true; nTotalTrianglesOut += hullInfo.nTriangleCount; } else { if(bVerbose) { Debug.LogWarning(gameObject.name + ": Error generating collider. ComputeHull() returned 0 triangles."); } } } if(info.nHullsOut < 0 && bVerbose) { Debug.LogWarning(gameObject.name + ": Error generating collider. ComputeHull() returned no hulls."); } } else { if(bVerbose) { Debug.LogWarning(gameObject.name + ": Error generating collider. ComputeHull() returned false."); } } } } DllClose(); return nTotalTrianglesOut > 0; }
public void ComputeHullsRuntime(LogDelegate log, ProgressDelegate progress) { string strMeshAssetPath = ""; MeshFilter theMesh = (MeshFilter)gameObject.GetComponent <MeshFilter>(); bool bForceNoMultithreading = ForceNoMultithreading; if (Algorithm == EAlgorithm.Legacy) { // Force no multithreading for the legacy method since sometimes it hangs when merging hulls bForceNoMultithreading = true; } DllInit(!bForceNoMultithreading); if (log != null) { SetLogFunctionPointer(Marshal.GetFunctionPointerForDelegate(log)); } else { SetLogFunctionPointer(IntPtr.Zero); } if (progress != null) { SetProgressFunctionPointer(Marshal.GetFunctionPointerForDelegate(progress)); } else { SetProgressFunctionPointer(IntPtr.Zero); } SConvexDecompositionInfoInOut info = new SConvexDecompositionInfoInOut(); int nMeshCount = 0; if (theMesh) { if (theMesh.sharedMesh) { info.uMaxHullVertices = (uint)(Mathf.Max(3, MaxHullVertices)); info.uMaxHulls = (uint)(Mathf.Max(1, MaxHulls)); info.fPrecision = 1.0f - Mathf.Clamp01(Precision); info.fBackFaceDistanceFactor = BackFaceDistanceFactor; info.uLegacyDepth = Algorithm == EAlgorithm.Legacy ? (uint)(Mathf.Max(1, LegacyDepth)) : 0; info.uNormalizeInputMesh = NormalizeInputMesh == true ? (uint)1 : (uint)0; info.uUseFastVersion = Algorithm == EAlgorithm.Fast ? (uint)1 : (uint)0; info.uTriangleCount = (uint)theMesh.sharedMesh.triangles.Length / 3; info.uVertexCount = (uint)theMesh.sharedMesh.vertexCount; if (DebugLog) { Debug.Log(string.Format("Processing mesh: {0} triangles, {1} vertices.", info.uTriangleCount, info.uVertexCount)); } Vector3[] av3Vertices = theMesh.sharedMesh.vertices; float fMeshRescale = 1.0f; if (NormalizeInputMesh == false && InternalScale > 0.0f) { av3Vertices = new Vector3[theMesh.sharedMesh.vertexCount]; float fMaxDistSquared = 0.0f; for (int nVertex = 0; nVertex < theMesh.sharedMesh.vertexCount; nVertex++) { float fDistSquared = theMesh.sharedMesh.vertices[nVertex].sqrMagnitude; if (fDistSquared > fMaxDistSquared) { fMaxDistSquared = fDistSquared; } } fMeshRescale = InternalScale / Mathf.Sqrt(fMaxDistSquared); if (DebugLog) { Debug.Log("Max vertex distance = " + Mathf.Sqrt(fMaxDistSquared) + ". Rescaling mesh by a factor of " + fMeshRescale); } for (int nVertex = 0; nVertex < theMesh.sharedMesh.vertexCount; nVertex++) { av3Vertices[nVertex] = theMesh.sharedMesh.vertices[nVertex] * fMeshRescale; } } if (DoConvexDecomposition(ref info, av3Vertices, theMesh.sharedMesh.triangles)) { if (info.nHullsOut > 0) { if (DebugLog) { Debug.Log(string.Format("Created {0} hulls", info.nHullsOut)); } DestroyHulls(); foreach (Collider collider in GetComponents <Collider>()) { collider.enabled = false; } m_aGoHulls = new GameObject[info.nHullsOut]; } else if (info.nHullsOut == 0) { if (log != null) { log("Error: No hulls were generated"); } } else { // -1 User cancelled } for (int nHull = 0; nHull < info.nHullsOut; nHull++) { SConvexDecompositionHullInfo hullInfo = new SConvexDecompositionHullInfo(); GetHullInfo((uint)nHull, ref hullInfo); if (hullInfo.nTriangleCount > 0) { m_aGoHulls[nHull] = new GameObject("Hull " + nHull); m_aGoHulls[nHull].transform.position = this.transform.position; m_aGoHulls[nHull].transform.rotation = this.transform.rotation; m_aGoHulls[nHull].transform.parent = this.transform; m_aGoHulls[nHull].layer = this.gameObject.layer; Vector3[] hullVertices = new Vector3[hullInfo.nVertexCount]; int[] hullIndices = new int[hullInfo.nTriangleCount * 3]; float fHullVolume = -1.0f; float fInvMeshRescale = 1.0f / fMeshRescale; FillHullMeshData((uint)nHull, ref fHullVolume, hullIndices, hullVertices); if (NormalizeInputMesh == false && InternalScale > 0.0f) { fInvMeshRescale = 1.0f / fMeshRescale; for (int nVertex = 0; nVertex < hullVertices.Length; nVertex++) { hullVertices[nVertex] *= fInvMeshRescale; hullVertices[nVertex] = Vector3.Scale(hullVertices[nVertex], transform.localScale); } } else { for (int nVertex = 0; nVertex < hullVertices.Length; nVertex++) { hullVertices[nVertex] = Vector3.Scale(hullVertices[nVertex], transform.localScale); } } Mesh hullMesh = new Mesh(); hullMesh.vertices = hullVertices; hullMesh.triangles = hullIndices; Collider hullCollider = null; fHullVolume *= Mathf.Pow(fInvMeshRescale, 3.0f); if (fHullVolume < MinHullVolume) { if (DebugLog) { Debug.Log(string.Format("Hull {0} will be approximated as a box collider (volume is {1:F2})", nHull, fHullVolume)); } MeshFilter meshf = m_aGoHulls[nHull].AddComponent <MeshFilter>(); meshf.sharedMesh = hullMesh; // Let Unity3D compute the best fitting box (it will use the meshfilter) hullCollider = m_aGoHulls[nHull].AddComponent <BoxCollider>() as BoxCollider; if (CreateHullMesh == false) { if (Application.isEditor && Application.isPlaying == false) { DestroyImmediate(meshf); DestroyImmediate(hullMesh); } else { Destroy(meshf); Destroy(hullMesh); } } else { meshf.sharedMesh.RecalculateNormals(); meshf.sharedMesh.uv = new Vector2[hullVertices.Length]; } } else { if (DebugLog) { Debug.Log(string.Format("Hull {0} collider: {1} vertices and {2} triangles. Volume = {3}", nHull, hullMesh.vertexCount, hullMesh.triangles.Length / 3, fHullVolume)); } MeshCollider meshCollider = m_aGoHulls[nHull].AddComponent <MeshCollider>() as MeshCollider; meshCollider.sharedMesh = hullMesh; meshCollider.convex = true; meshCollider.smoothSphereCollisions = SmoothSphereCollisions; hullCollider = meshCollider; if (CreateHullMesh) { MeshFilter meshf = m_aGoHulls[nHull].AddComponent <MeshFilter>(); meshf.sharedMesh = hullMesh; } nMeshCount++; } if (hullCollider) { hullCollider.material = PhysMaterial; hullCollider.isTrigger = IsTrigger; if (hullInfo.nTriangleCount > LargestHullFaces) { LargestHullFaces = hullInfo.nTriangleCount; } if (hullInfo.nVertexCount > LargestHullVertices) { LargestHullVertices = hullInfo.nVertexCount; } } } } } else { if (log != null) { log("Error: convex decomposition could not be completed due to errors"); } } } else { if (log != null) { log("Error: " + this.name + " has no mesh"); } } } else { if (log != null) { log("Error: " + this.name + " has no mesh"); } } DllClose(); }
private static bool ComputeHull(GameObject gameObject, bool isTrigger, FracturedObject.ECCAlgorithm eAlgorithm, int nMaxHulls, int nMaxHullVertices, int nLegacySteps, bool bVerbose, out int nTotalTrianglesOut) { MeshFilter theMesh = (MeshFilter)gameObject.GetComponent <MeshFilter>(); DllInit(true); SetLogFunctionPointer(Marshal.GetFunctionPointerForDelegate(new LogDelegate(Log))); SConvexDecompositionInfoInOut info = new SConvexDecompositionInfoInOut(); nTotalTrianglesOut = 0; if (theMesh) { if (theMesh.sharedMesh) { uint uLegacySteps = (uint)(Mathf.Max(1, nLegacySteps)); info.uMaxHullVertices = (uint)(Mathf.Max(3, nMaxHullVertices)); info.uMaxHulls = (uint)nMaxHulls; info.fPrecision = 0.2f; info.fBackFaceDistanceFactor = 0.2f; info.uLegacyDepth = eAlgorithm == FracturedObject.ECCAlgorithm.Legacy ? uLegacySteps : 0; info.uNormalizeInputMesh = 0; info.uUseFastVersion = eAlgorithm == FracturedObject.ECCAlgorithm.Fast ? (uint)1 : (uint)0; info.uTriangleCount = (uint)theMesh.sharedMesh.triangles.Length / 3; info.uVertexCount = (uint)theMesh.sharedMesh.vertexCount; Vector3[] av3Vertices = theMesh.sharedMesh.vertices; if (DoConvexDecomposition(ref info, av3Vertices, theMesh.sharedMesh.triangles)) { for (int nHull = 0; nHull < info.nHullsOut; nHull++) { SConvexDecompositionHullInfo hullInfo = new SConvexDecompositionHullInfo(); GetHullInfo((uint)nHull, ref hullInfo); if (hullInfo.nTriangleCount > 0) { Vector3[] hullVertices = new Vector3[hullInfo.nVertexCount]; int[] hullIndices = new int[hullInfo.nTriangleCount * 3]; float fHullVolume = -1.0f; FillHullMeshData((uint)nHull, ref fHullVolume, hullIndices, hullVertices); Mesh hullMesh = new Mesh(); hullMesh.vertices = hullVertices; hullMesh.triangles = hullIndices; hullMesh.uv = new Vector2[hullVertices.Length]; hullMesh.RecalculateNormals(); GameObject goNewHull = new GameObject("Hull " + (nHull + 1)); goNewHull.transform.position = gameObject.transform.position; goNewHull.transform.rotation = gameObject.transform.rotation; goNewHull.transform.localScale = gameObject.transform.localScale; goNewHull.transform.parent = gameObject.transform; goNewHull.layer = gameObject.layer; MeshCollider meshCollider = goNewHull.AddComponent <MeshCollider>() as MeshCollider; meshCollider.sharedMesh = null; meshCollider.sharedMesh = hullMesh; meshCollider.convex = true; meshCollider.isTrigger = isTrigger; nTotalTrianglesOut += hullInfo.nTriangleCount; } else { if (bVerbose) { Debug.LogWarning(gameObject.name + ": Error generating collider. ComputeHull() returned 0 triangles."); } } } if (info.nHullsOut < 0 && bVerbose) { Debug.LogWarning(gameObject.name + ": Error generating collider. ComputeHull() returned no hulls."); } } else { if (bVerbose) { Debug.LogWarning(gameObject.name + ": Error generating collider. ComputeHull() returned false."); } } } } DllClose(); return(nTotalTrianglesOut > 0); }
public bool ComputeHulls(LogDelegate log, ProgressDelegate progress) { bool hadError = false; string strMeshAssetPath = ""; if(CreateMeshAssets) { string uniqueID = null;; Debug.LogFormat("Obj: {0} of type {1}", gameObject.transform.FullPath(), UnityEditor.PrefabUtility.GetPrefabType(this.gameObject)); if (UnityEditor.PrefabUtility.GetPrefabParent(this.gameObject) != null) { string prefabPath = UnityEditor.AssetDatabase.GetAssetPath(UnityEditor.PrefabUtility.GetPrefabParent(this.gameObject)); int startIndex = 1 + prefabPath.LastIndexOf("/"); prefabPath = prefabPath.Substring(startIndex, prefabPath.LastIndexOf(".") - startIndex); uniqueID = string.Format("{0}_{1}", prefabPath, UnityEditor.PrefabUtility.GetPrefabParent(this.gameObject).name); } else uniqueID = string.Format("{0}_{1}", gameObject.name, this.GetInstanceID().ToString()); strMeshAssetPath = "ColliderMeshes\\" + uniqueID + " _colMesh.asset"; if (!InBatchSaveMode) strMeshAssetPath = UnityEditor.EditorUtility.SaveFilePanelInProject("Save mesh asset", strMeshAssetPath, "asset", "Save collider mesh for " + gameObject.name); else strMeshAssetPath = "Assets/" + strMeshAssetPath; if(strMeshAssetPath.Length == 0) { return false; } } MeshFilter theMesh = (MeshFilter)gameObject.GetComponent<MeshFilter>(); bool bForceNoMultithreading = ForceNoMultithreading; if(Algorithm == EAlgorithm.Legacy) { // Force no multithreading for the legacy method since sometimes it hangs when merging hulls bForceNoMultithreading = true; } SConvexDecompositionInfoInOut info = new SConvexDecompositionInfoInOut(); SConvexDecompositionInfoInOutVHACD infoVHACD = new SConvexDecompositionInfoInOutVHACD(); if(Algorithm == EAlgorithm.VHACD) { ConcaveColliderDll20.DllInit(!bForceNoMultithreading); if(log != null) { ConcaveColliderDll20.SetLogFunctionPointer(Marshal.GetFunctionPointerForDelegate(log)); } else { ConcaveColliderDll20.SetLogFunctionPointer(IntPtr.Zero); } if(progress != null) { ConcaveColliderDll20.SetProgressFunctionPointer(Marshal.GetFunctionPointerForDelegate(progress)); } else { ConcaveColliderDll20.SetProgressFunctionPointer(IntPtr.Zero); } } else { DllInit(!bForceNoMultithreading); if(log != null) { SetLogFunctionPointer(Marshal.GetFunctionPointerForDelegate(log)); } else { SetLogFunctionPointer(IntPtr.Zero); } if(progress != null) { SetProgressFunctionPointer(Marshal.GetFunctionPointerForDelegate(progress)); } else { SetProgressFunctionPointer(IntPtr.Zero); } } int nMeshCount = 0; bool bDllCloseVHACD = false; if(theMesh) { if(theMesh.sharedMesh) { if(Algorithm == EAlgorithm.VHACD) { infoVHACD.fConcavity = VHACD_Concavity; infoVHACD.fAlpha = 0.05f; infoVHACD.fBeta = 0.05f; infoVHACD.fGamma = 0.05f; infoVHACD.fDelta = 0.0005f; infoVHACD.fMinVolumePerCH = VHACD_MinVolumePerCH; infoVHACD.uResolution = (uint)VHACD_NumVoxels; infoVHACD.uMaxNumVerticesPerCH = (uint)VHACD_MaxVerticesPerCH; infoVHACD.nDepth = 20; infoVHACD.nPlaneDownsampling = 4; infoVHACD.nConvexhullDownsampling = 4; infoVHACD.nPca = VHACD_NormalizeMesh ? 1 : 0; infoVHACD.nMode = 0; infoVHACD.nConvexhullApproximation = 1; infoVHACD.nOclAcceleration = 1; infoVHACD.uTriangleCount = (uint)theMesh.sharedMesh.triangles.Length / 3; infoVHACD.uVertexCount = (uint)theMesh.sharedMesh.vertexCount; if(DebugLog) { Debug.Log(string.Format("Processing mesh: {0} triangles, {1} vertices.", infoVHACD.uTriangleCount, infoVHACD.uVertexCount)); } } else { info.uMaxHullVertices = (uint)(Mathf.Max(3, MaxHullVertices)); info.uMaxHulls = (uint)(Mathf.Max(1, MaxHulls)); info.fPrecision = 1.0f - Mathf.Clamp01(Precision); info.fBackFaceDistanceFactor = BackFaceDistanceFactor; info.uLegacyDepth = Algorithm == EAlgorithm.Legacy ? (uint)(Mathf.Max(1, LegacyDepth)) : 0; info.uNormalizeInputMesh = NormalizeInputMesh == true ? (uint)1 : (uint)0; info.uUseFastVersion = Algorithm == EAlgorithm.Fast ? (uint)1 : (uint)0; info.uTriangleCount = (uint)theMesh.sharedMesh.triangles.Length / 3; info.uVertexCount = (uint)theMesh.sharedMesh.vertexCount; if(DebugLog) { Debug.Log(string.Format("Processing mesh: {0} triangles, {1} vertices.", info.uTriangleCount, info.uVertexCount)); } } Vector3[] av3Vertices = theMesh.sharedMesh.vertices; float fMeshRescale = 1.0f; if(NormalizeInputMesh == false && InternalScale > 0.0f && Algorithm != EAlgorithm.VHACD) { av3Vertices = new Vector3[theMesh.sharedMesh.vertexCount]; float fMaxDistSquared = 0.0f; for(int nVertex = 0; nVertex < theMesh.sharedMesh.vertexCount; nVertex++) { float fDistSquared = theMesh.sharedMesh.vertices[nVertex].sqrMagnitude; if(fDistSquared > fMaxDistSquared) { fMaxDistSquared = fDistSquared; } } fMeshRescale = InternalScale / Mathf.Sqrt(fMaxDistSquared); if(DebugLog) { Debug.Log("Max vertex distance = " + Mathf.Sqrt(fMaxDistSquared) + ". Rescaling mesh by a factor of " + fMeshRescale); } for(int nVertex = 0; nVertex < theMesh.sharedMesh.vertexCount; nVertex++) { av3Vertices[nVertex] = theMesh.sharedMesh.vertices[nVertex] * fMeshRescale; } } bool bConvexDecompositionOK = false; int nHullsOut = 0; if(Algorithm == EAlgorithm.VHACD) { bConvexDecompositionOK = ConcaveColliderDll20.DoConvexDecomposition(ref infoVHACD, av3Vertices, theMesh.sharedMesh.triangles); nHullsOut = infoVHACD.nHullsOut; bDllCloseVHACD = true; } else { bConvexDecompositionOK = DoConvexDecomposition(ref info, av3Vertices, theMesh.sharedMesh.triangles); nHullsOut = info.nHullsOut; } if(bConvexDecompositionOK) { if(nHullsOut > 0) { if(DebugLog) { Debug.Log(string.Format("Created {0} hulls", nHullsOut)); } DestroyHulls(); foreach(Collider collider in GetComponents<Collider>()) { collider.enabled = false; } m_aGoHulls = new GameObject[nHullsOut]; } else if(nHullsOut == 0) { hadError = true; if(log != null) log("Error: No hulls were generated"); } else { // -1 User cancelled hadError = true; } Transform hullParent = this.transform.Find("Generated Colliders"); if (hullParent == null) { hullParent = (new GameObject("Generated Colliders")).transform; hullParent.transform.SetParent(this.transform, false); } for(int nHull = 0; nHull < nHullsOut; nHull++) { SConvexDecompositionHullInfo hullInfo = new SConvexDecompositionHullInfo(); if(Algorithm == EAlgorithm.VHACD) { ConcaveColliderDll20.GetHullInfo((uint)nHull, ref hullInfo); } else { GetHullInfo((uint)nHull, ref hullInfo); } if(hullInfo.nTriangleCount > 0) { m_aGoHulls[nHull] = new GameObject("Hull " + nHull); m_aGoHulls[nHull].transform.position = this.transform.position; m_aGoHulls[nHull].transform.rotation = this.transform.rotation; m_aGoHulls[nHull].transform.parent = hullParent; m_aGoHulls[nHull].layer = this.gameObject.layer; Vector3[] hullVertices = new Vector3[hullInfo.nVertexCount]; int[] hullIndices = new int[hullInfo.nTriangleCount * 3]; float fHullVolume = -1.0f; float fInvMeshRescale = 1.0f / fMeshRescale; if(Algorithm == EAlgorithm.VHACD) { ConcaveColliderDll20.FillHullMeshData((uint)nHull, ref fHullVolume, hullIndices, hullVertices); } else { FillHullMeshData((uint)nHull, ref fHullVolume, hullIndices, hullVertices); } if(NormalizeInputMesh == false && InternalScale > 0.0f && Algorithm != EAlgorithm.VHACD) { fInvMeshRescale = 1.0f / fMeshRescale; for(int nVertex = 0; nVertex < hullVertices.Length; nVertex++) { hullVertices[nVertex] *= fInvMeshRescale; hullVertices[nVertex] = Vector3.Scale(hullVertices[nVertex], transform.localScale); } } else { for(int nVertex = 0; nVertex < hullVertices.Length; nVertex++) { hullVertices[nVertex] = Vector3.Scale(hullVertices[nVertex], transform.localScale); } } Mesh hullMesh = new Mesh(); hullMesh.vertices = hullVertices; hullMesh.triangles = hullIndices; Collider hullCollider = null; fHullVolume *= Mathf.Pow(fInvMeshRescale, 3.0f); if(fHullVolume < MinHullVolume && Algorithm != EAlgorithm.VHACD) { if(DebugLog) { Debug.Log(string.Format("Hull {0} will be approximated as a box collider (volume is {1:F2})", nHull, fHullVolume)); } MeshFilter meshf = m_aGoHulls[nHull].AddComponent<MeshFilter>(); meshf.sharedMesh = hullMesh; // Let Unity3D compute the best fitting box (it will use the meshfilter) hullCollider = m_aGoHulls[nHull].AddComponent<BoxCollider>() as BoxCollider; BoxCollider boxCollider = hullCollider as BoxCollider; boxCollider.center = hullMesh.bounds.center; boxCollider.size = hullMesh.bounds.size; if(CreateHullMesh == false) { if(Application.isEditor && Application.isPlaying == false) { DestroyImmediate(meshf); DestroyImmediate(hullMesh); } else { Destroy(meshf); Destroy(hullMesh); } } else { meshf.sharedMesh.RecalculateNormals(); meshf.sharedMesh.uv = new Vector2[hullVertices.Length]; } } else { if(DebugLog) { Debug.Log(string.Format("Hull {0} collider: {1} vertices and {2} triangles. Volume = {3}", nHull, hullMesh.vertexCount, hullMesh.triangles.Length / 3, fHullVolume)); } MeshCollider meshCollider = m_aGoHulls[nHull].AddComponent<MeshCollider>() as MeshCollider; meshCollider.sharedMesh = hullMesh; meshCollider.convex = true; hullCollider = meshCollider; if(CreateHullMesh) { MeshFilter meshf = m_aGoHulls[nHull].AddComponent<MeshFilter>(); meshf.sharedMesh = hullMesh; } if(CreateMeshAssets) { if(nMeshCount == 0) { if(progress != null) { progress("Creating mesh assets", 0.0f); } // Avoid some shader warnings hullMesh.RecalculateNormals(); hullMesh.uv = new Vector2[hullVertices.Length]; UnityEditor.AssetDatabase.CreateAsset(hullMesh, strMeshAssetPath); } else { if(progress != null) { progress("Creating mesh assets", nHullsOut > 1 ? (nHull / (nHullsOut - 1.0f)) * 100.0f : 100.0f); } // Avoid some shader warnings hullMesh.RecalculateNormals(); hullMesh.uv = new Vector2[hullVertices.Length]; UnityEditor.AssetDatabase.AddObjectToAsset(hullMesh, strMeshAssetPath); UnityEditor.AssetDatabase.ImportAsset(UnityEditor.AssetDatabase.GetAssetPath(hullMesh)); } } nMeshCount++; } if(hullCollider) { hullCollider.material = PhysMaterial; hullCollider.isTrigger = IsTrigger; if(hullInfo.nTriangleCount > LargestHullFaces) LargestHullFaces = hullInfo.nTriangleCount; if(hullInfo.nVertexCount > LargestHullVertices) LargestHullVertices = hullInfo.nVertexCount; } } } if(CreateMeshAssets) { UnityEditor.AssetDatabase.Refresh(); } } else { hadError = true; if(log != null) log("Error: convex decomposition could not be completed due to errors"); } } else { hadError = true; if(log != null) log("Error: " + this.name + " has no mesh"); } } else { hadError = true; if(log != null) log("Error: " + this.name + " has no mesh"); } if(Algorithm == EAlgorithm.VHACD) { if(bDllCloseVHACD) { ConcaveColliderDll20.DllClose(); } } else { DllClose(); } return !hadError; }
public static int ComputeHull(GameObject gameObject, int nMaxHullVertices, bool bVerbose) { int nTotalTriangles = 0; MeshFilter theMesh = (MeshFilter)gameObject.GetComponent <MeshFilter>(); DllInit(true); SConvexDecompositionInfoInOut info = new SConvexDecompositionInfoInOut(); if (theMesh) { if (theMesh.sharedMesh) { info.uMaxHullVertices = (uint)(Mathf.Max(3, nMaxHullVertices)); info.uMaxHulls = 1; info.fPrecision = 0.8f; info.fBackFaceDistanceFactor = 0.2f; info.uLegacyDepth = 0; info.uNormalizeInputMesh = 0; info.uUseFastVersion = 1; info.uTriangleCount = (uint)theMesh.sharedMesh.triangles.Length / 3; info.uVertexCount = (uint)theMesh.sharedMesh.vertexCount; Vector3[] av3Vertices = theMesh.sharedMesh.vertices; if (DoConvexDecomposition(ref info, av3Vertices, theMesh.sharedMesh.triangles)) { for (int nHull = 0; nHull < info.nHullsOut; nHull++) { SConvexDecompositionHullInfo hullInfo = new SConvexDecompositionHullInfo(); GetHullInfo((uint)nHull, ref hullInfo); if (hullInfo.nTriangleCount > 0) { Vector3[] hullVertices = new Vector3[hullInfo.nVertexCount]; int[] hullIndices = new int[hullInfo.nTriangleCount * 3]; float fHullVolume = -1.0f; FillHullMeshData((uint)nHull, ref fHullVolume, hullIndices, hullVertices); Mesh hullMesh = new Mesh(); hullMesh.vertices = hullVertices; hullMesh.triangles = hullIndices; hullMesh.uv = new Vector2[hullVertices.Length]; hullMesh.RecalculateNormals(); GameObject goNewHull = new GameObject("Hull " + (nHull + 1)); goNewHull.transform.position = gameObject.transform.position; goNewHull.transform.rotation = gameObject.transform.rotation; goNewHull.transform.localScale = gameObject.transform.localScale; goNewHull.transform.parent = gameObject.transform; MeshCollider meshCollider = goNewHull.AddComponent <MeshCollider>() as MeshCollider; meshCollider.sharedMesh = null; meshCollider.sharedMesh = hullMesh; meshCollider.convex = true; nTotalTriangles += hullInfo.nTriangleCount; } else { if (bVerbose) { // Debug.LogWarning("Error generating collider for " + gameObject.name + ": ComputeHull() returned 0 triangles. Approximating with another collider."); } } } if (info.nHullsOut == 0 && bVerbose) { // Debug.LogWarning("Error generating collider for " + gameObject.name + ": ComputeHull() returned 0 hulls. Approximating with another collider."); } } else { if (bVerbose) { // Debug.LogWarning("Error generating collider for " + gameObject.name + ": ComputeHull() returned false. Approximating with another collider."); } } } } DllClose(); return(nTotalTriangles); }
public void ComputeHulls(LogDelegate log, ProgressDelegate progress) { string strMeshAssetPath = ""; if(CreateMeshAssets) { strMeshAssetPath = UnityEditor.EditorUtility.SaveFilePanelInProject("Save mesh asset", "mesh_" + gameObject.name + this.GetInstanceID().ToString() + ".asset", "asset", "Please enter a file name to save the mesh asset to"); if(strMeshAssetPath.Length == 0) { return; } } MeshFilter theMesh = (MeshFilter)gameObject.GetComponent<MeshFilter>(); bool bForceNoMultithreading = ForceNoMultithreading; if(Algorithm == EAlgorithm.Legacy) { // Force no multithreading for the legacy method since sometimes it hangs when merging hulls bForceNoMultithreading = true; } DllInit(!bForceNoMultithreading); if(log != null) { SetLogFunctionPointer(Marshal.GetFunctionPointerForDelegate(log)); } else { SetLogFunctionPointer(IntPtr.Zero); } if(progress != null) { SetProgressFunctionPointer(Marshal.GetFunctionPointerForDelegate(progress)); } else { SetProgressFunctionPointer(IntPtr.Zero); } SConvexDecompositionInfoInOut info = new SConvexDecompositionInfoInOut(); int nMeshCount = 0; if(theMesh) { if(theMesh.sharedMesh) { info.uMaxHullVertices = (uint)(Mathf.Max(3, MaxHullVertices)); info.uMaxHulls = (uint)(Mathf.Max(1, MaxHulls)); info.fPrecision = 1.0f - Mathf.Clamp01(Precision); info.fBackFaceDistanceFactor = BackFaceDistanceFactor; info.uLegacyDepth = Algorithm == EAlgorithm.Legacy ? (uint)(Mathf.Max(1, LegacyDepth)) : 0; info.uNormalizeInputMesh = NormalizeInputMesh == true ? (uint)1 : (uint)0; info.uUseFastVersion = Algorithm == EAlgorithm.Fast ? (uint)1 : (uint)0; info.uTriangleCount = (uint)theMesh.sharedMesh.triangles.Length / 3; info.uVertexCount = (uint)theMesh.sharedMesh.vertexCount; if(DebugLog) { Debug.Log(string.Format("Processing mesh: {0} triangles, {1} vertices.", info.uTriangleCount, info.uVertexCount)); } Vector3[] av3Vertices = theMesh.sharedMesh.vertices; float fMeshRescale = 1.0f; if(NormalizeInputMesh == false && InternalScale > 0.0f) { av3Vertices = new Vector3[theMesh.sharedMesh.vertexCount]; float fMaxDistSquared = 0.0f; for(int nVertex = 0; nVertex < theMesh.sharedMesh.vertexCount; nVertex++) { float fDistSquared = theMesh.sharedMesh.vertices[nVertex].sqrMagnitude; if(fDistSquared > fMaxDistSquared) { fMaxDistSquared = fDistSquared; } } fMeshRescale = InternalScale / Mathf.Sqrt(fMaxDistSquared); if(DebugLog) { Debug.Log("Max vertex distance = " + Mathf.Sqrt(fMaxDistSquared) + ". Rescaling mesh by a factor of " + fMeshRescale); } for(int nVertex = 0; nVertex < theMesh.sharedMesh.vertexCount; nVertex++) { av3Vertices[nVertex] = theMesh.sharedMesh.vertices[nVertex] * fMeshRescale; } } if(DoConvexDecomposition(ref info, av3Vertices, theMesh.sharedMesh.triangles)) { if(info.nHullsOut > 0) { if(DebugLog) { Debug.Log(string.Format("Created {0} hulls", info.nHullsOut)); } DestroyHulls(); foreach(Collider collider in GetComponents<Collider>()) { collider.enabled = false; } m_aGoHulls = new GameObject[info.nHullsOut]; } else if(info.nHullsOut == 0) { if(log != null) log("Error: No hulls were generated"); } else { // -1 User cancelled } for(int nHull = 0; nHull < info.nHullsOut; nHull++) { SConvexDecompositionHullInfo hullInfo = new SConvexDecompositionHullInfo(); GetHullInfo((uint)nHull, ref hullInfo); if(hullInfo.nTriangleCount > 0) { m_aGoHulls[nHull] = new GameObject("Hull " + nHull); m_aGoHulls[nHull].transform.position = this.transform.position; m_aGoHulls[nHull].transform.rotation = this.transform.rotation; m_aGoHulls[nHull].transform.parent = this.transform; m_aGoHulls[nHull].layer = this.gameObject.layer; Vector3[] hullVertices = new Vector3[hullInfo.nVertexCount]; int[] hullIndices = new int[hullInfo.nTriangleCount * 3]; float fHullVolume = -1.0f; float fInvMeshRescale = 1.0f / fMeshRescale; FillHullMeshData((uint)nHull, ref fHullVolume, hullIndices, hullVertices); if(NormalizeInputMesh == false && InternalScale > 0.0f) { fInvMeshRescale = 1.0f / fMeshRescale; for(int nVertex = 0; nVertex < hullVertices.Length; nVertex++) { hullVertices[nVertex] *= fInvMeshRescale; hullVertices[nVertex] = Vector3.Scale(hullVertices[nVertex], transform.localScale); } } else { for(int nVertex = 0; nVertex < hullVertices.Length; nVertex++) { hullVertices[nVertex] = Vector3.Scale(hullVertices[nVertex], transform.localScale); } } Mesh hullMesh = new Mesh(); hullMesh.vertices = hullVertices; hullMesh.triangles = hullIndices; Collider hullCollider = null; fHullVolume *= Mathf.Pow(fInvMeshRescale, 3.0f); if(fHullVolume < MinHullVolume) { if(DebugLog) { Debug.Log(string.Format("Hull {0} will be approximated as a box collider (volume is {1:F2})", nHull, fHullVolume)); } MeshFilter meshf = m_aGoHulls[nHull].AddComponent<MeshFilter>(); meshf.sharedMesh = hullMesh; // Let Unity3D compute the best fitting box (it will use the meshfilter) hullCollider = m_aGoHulls[nHull].AddComponent<BoxCollider>() as BoxCollider; BoxCollider boxCollider = hullCollider as BoxCollider; boxCollider.center = hullMesh.bounds.center; boxCollider.size = hullMesh.bounds.size; if(CreateHullMesh == false) { if(Application.isEditor && Application.isPlaying == false) { DestroyImmediate(meshf); DestroyImmediate(hullMesh); } else { Destroy(meshf); Destroy(hullMesh); } } else { meshf.sharedMesh.RecalculateNormals(); meshf.sharedMesh.uv = new Vector2[hullVertices.Length]; } } else { if(DebugLog) { Debug.Log(string.Format("Hull {0} collider: {1} vertices and {2} triangles. Volume = {3}", nHull, hullMesh.vertexCount, hullMesh.triangles.Length / 3, fHullVolume)); } MeshCollider meshCollider = m_aGoHulls[nHull].AddComponent<MeshCollider>() as MeshCollider; meshCollider.sharedMesh = hullMesh; meshCollider.convex = true; hullCollider = meshCollider; if(CreateHullMesh) { MeshFilter meshf = m_aGoHulls[nHull].AddComponent<MeshFilter>(); meshf.sharedMesh = hullMesh; } if(CreateMeshAssets) { if(nMeshCount == 0) { if(progress != null) { progress("Creating mesh assets", 0.0f); } // Avoid some shader warnings hullMesh.RecalculateNormals(); hullMesh.uv = new Vector2[hullVertices.Length]; UnityEditor.AssetDatabase.CreateAsset(hullMesh, strMeshAssetPath); } else { if(progress != null) { progress("Creating mesh assets", info.nHullsOut > 1 ? (nHull / (info.nHullsOut - 1.0f)) * 100.0f : 100.0f); } // Avoid some shader warnings hullMesh.RecalculateNormals(); hullMesh.uv = new Vector2[hullVertices.Length]; UnityEditor.AssetDatabase.AddObjectToAsset(hullMesh, strMeshAssetPath); UnityEditor.AssetDatabase.ImportAsset(UnityEditor.AssetDatabase.GetAssetPath(hullMesh)); } } nMeshCount++; } if(hullCollider) { hullCollider.material = PhysMaterial; hullCollider.isTrigger = IsTrigger; if(hullInfo.nTriangleCount > LargestHullFaces) LargestHullFaces = hullInfo.nTriangleCount; if(hullInfo.nVertexCount > LargestHullVertices) LargestHullVertices = hullInfo.nVertexCount; } } } if(CreateMeshAssets) { UnityEditor.AssetDatabase.Refresh(); } } else { if(log != null) log("Error: convex decomposition could not be completed due to errors"); } } else { if(log != null) log("Error: " + this.name + " has no mesh"); } } else { if(log != null) log("Error: " + this.name + " has no mesh"); } DllClose(); }
public static int ComputeHull(GameObject gameObject, int nMaxHullVertices, bool bVerbose) { int nTotalTriangles = 0; MeshFilter theMesh = (MeshFilter)gameObject.GetComponent<MeshFilter>(); DllInit(true); SConvexDecompositionInfoInOut info = new SConvexDecompositionInfoInOut(); if(theMesh) { if(theMesh.sharedMesh) { info.uMaxHullVertices = (uint)(Mathf.Max(3, nMaxHullVertices)); info.uMaxHulls = 1; info.fPrecision = 0.8f; info.fBackFaceDistanceFactor = 0.2f; info.uLegacyDepth = 0; info.uNormalizeInputMesh = 0; info.uUseFastVersion = 1; info.uTriangleCount = (uint)theMesh.sharedMesh.triangles.Length / 3; info.uVertexCount = (uint)theMesh.sharedMesh.vertexCount; Vector3[] av3Vertices = theMesh.sharedMesh.vertices; if(DoConvexDecomposition(ref info, av3Vertices, theMesh.sharedMesh.triangles)) { for(int nHull = 0; nHull < info.nHullsOut; nHull++) { SConvexDecompositionHullInfo hullInfo = new SConvexDecompositionHullInfo(); GetHullInfo((uint)nHull, ref hullInfo); if(hullInfo.nTriangleCount > 0) { Vector3[] hullVertices = new Vector3[hullInfo.nVertexCount]; int[] hullIndices = new int[hullInfo.nTriangleCount * 3]; float fHullVolume = -1.0f; FillHullMeshData((uint)nHull, ref fHullVolume, hullIndices, hullVertices); Mesh hullMesh = new Mesh(); hullMesh.vertices = hullVertices; hullMesh.triangles = hullIndices; hullMesh.uv = new Vector2[hullVertices.Length]; hullMesh.RecalculateNormals(); GameObject goNewHull = new GameObject("Hull " + (nHull + 1)); goNewHull.transform.position = gameObject.transform.position; goNewHull.transform.rotation = gameObject.transform.rotation; goNewHull.transform.localScale = gameObject.transform.localScale; goNewHull.transform.parent = gameObject.transform; MeshCollider meshCollider = goNewHull.AddComponent<MeshCollider>() as MeshCollider; meshCollider.sharedMesh = null; meshCollider.sharedMesh = hullMesh; meshCollider.convex = true; nTotalTriangles += hullInfo.nTriangleCount; } else { if(bVerbose) { // Debug.LogWarning("Error generating collider for " + gameObject.name + ": ComputeHull() returned 0 triangles. Approximating with another collider."); } } } if(info.nHullsOut == 0 && bVerbose) { // Debug.LogWarning("Error generating collider for " + gameObject.name + ": ComputeHull() returned 0 hulls. Approximating with another collider."); } } else { if(bVerbose) { // Debug.LogWarning("Error generating collider for " + gameObject.name + ": ComputeHull() returned false. Approximating with another collider."); } } } } DllClose(); return nTotalTriangles; }