void Prepare(Mesh oldMesh, bool cachePreparation = true) { #if UNITY_4_OR_ABOVE if ( !oldMesh.isReadable ) { Debug.LogError("The mesh is not readable. Switch on the \"Read/Write Enabled\"" + " option on the mesh's import settings."); return; } #endif var usePhysics = type == ExplosionType.Physics; MeshExplosionPreparation prep; if ( cache.TryGetValue(oldMesh, out prep) ) { // The caching is different for physics explosions and non-physics explosions, so make // sure that we have the correct stuff: if ( (usePhysics && prep.physicsMeshes != null) || (!usePhysics && prep.startMesh != null) ) { preparation = prep; return; } } // Make a copy of the mesh but with every triangle made discrete so that it doesn't share // any vertices with any other triangle. var oldVertices = oldMesh.vertices; var oldNormals = oldMesh.normals; var oldTangents = oldMesh.tangents; var oldUV = oldMesh.uv; var oldUV2 = oldMesh.uv2; var oldColors = oldMesh.colors; var subMeshCount = oldMesh.subMeshCount; var oldMeshTriangles = new int[subMeshCount][]; var frontMeshTrianglesPerSubMesh = usePhysics ? prep.frontMeshTrianglesPerSubMesh = new int[subMeshCount] : null; int frontTriangles = 0; for ( var subMesh = 0; subMesh < subMeshCount; ++subMesh ) { int[] triangles; oldMeshTriangles[subMesh] = triangles = oldMesh.GetTriangles(subMesh); var frontMeshTrianglesInThisSubMesh = triangles.Length / 3; if ( usePhysics ) frontMeshTrianglesPerSubMesh[subMesh] = frontMeshTrianglesInThisSubMesh; frontTriangles += frontMeshTrianglesInThisSubMesh; } prep.totalFrontTriangles = frontTriangles; var totalTriangles = frontTriangles * 2; // back faces var newTotalVertices = usePhysics ? 3 * 2 : // one triangle, both sides totalTriangles * 3; const int vertexLimit = 65534; if ( newTotalVertices > vertexLimit ) { Debug.LogError("The mesh has too many triangles to explode. It must have" + " " + ((vertexLimit / 3) / 2) + " or fewer triangles."); return; } var newVertices = new Vector3[newTotalVertices]; var newNormals = oldNormals == null || oldNormals.Length == 0 ? null : new Vector3[newTotalVertices]; var newTangents = oldTangents == null || oldTangents.Length == 0 ? null : new Vector4[newTotalVertices]; var newUV = oldUV == null || oldUV.Length == 0 ? null : new Vector2[newTotalVertices]; var newUV2 = oldUV2 == null || oldUV2.Length == 0 ? null : new Vector2[newTotalVertices]; var newColors = oldColors == null || oldColors.Length == 0 ? null : new Color[newTotalVertices]; var triangleCentroids = prep.triangleCentroids = new Vector3[frontTriangles]; var physicsMeshes = prep.physicsMeshes = usePhysics ? new Mesh[frontTriangles] : null; var rotations = prep.rotations = usePhysics ? new Quaternion[frontTriangles] : null; var physicsMeshTriangles = usePhysics ? new int[] { 0, 1, 2, 3, 4, 5 } : null; var newVertexNumber = 0; var wholeMeshTriangleIndex = 0; var invRotation = Quaternion.identity; for ( var subMesh = 0; subMesh < subMeshCount; ++subMesh ) { var triangles = oldMeshTriangles[subMesh]; var n = triangles.Length; int i = 0; while ( i < n ) { var triangleStartI = i; var centroid = Vector3.zero; for ( int repeat = 0; repeat < 2; ++repeat ) { i = triangleStartI; var back = repeat == 1; while ( i < n ) { var oldVertexNumber = triangles[back ? (triangleStartI + (3 - 1 - (i - triangleStartI))) : i]; if ( usePhysics && (newVertexNumber % 6) == 0 ) { // Start of a triangle var a = oldVertices[oldVertexNumber]; var b = oldVertices[triangles[i + 1]]; var c = oldVertices[triangles[i + 2]]; var triangleRealNormal = Vector3.Cross(b - a, c - a); // We want to rotate the triangle so that it is flat on the x-z plane. // The reason for that is so that we can use an axis-aligned box // collider to be its physics proxy. rotations[wholeMeshTriangleIndex] = Quaternion.FromToRotation(Vector3.up, triangleRealNormal); invRotation = Quaternion.FromToRotation(triangleRealNormal, Vector3.up); triangleCentroids[wholeMeshTriangleIndex] = centroid = (a + b + c) / 3; } if ( !back ) { newVertices[newVertexNumber] = invRotation * (oldVertices[oldVertexNumber] - centroid); if ( newNormals != null ) { newNormals[newVertexNumber] = invRotation * oldNormals[oldVertexNumber]; } if ( newTangents != null ) { newTangents[newVertexNumber] = invRotation * oldTangents[oldVertexNumber]; } } else { // This stuff is handled by MeshExplosion.SetBackTriangleVertices(). } if ( newUV != null ) { newUV[newVertexNumber] = oldUV[oldVertexNumber]; } if ( newUV2 != null ) { newUV2[newVertexNumber] = oldUV2[oldVertexNumber]; } if ( newColors != null ) { newColors[newVertexNumber] = oldColors[oldVertexNumber]; } // It's important that these are here rather than in a for statement so // that they get executed even if we break. ++i; ++newVertexNumber; if ( (newVertexNumber % 6) == 0 ) { // End of a triangle if ( usePhysics ) { MeshExplosion.SetBackTriangleVertices( newVertices, newNormals, newTangents, 1); var mesh = new Mesh(); mesh.vertices = newVertices; if ( newNormals != null ) mesh.normals = newNormals; if ( newTangents != null ) { mesh.tangents = newTangents; } if ( newUV != null ) mesh.uv = newUV; if ( newUV2 != null ) mesh.uv2 = newUV2; if ( newColors != null ) mesh.colors = newColors; mesh.triangles = physicsMeshTriangles; physicsMeshes[wholeMeshTriangleIndex] = mesh; newVertexNumber = 0; } break; } else if ( (newVertexNumber % 3) == 0 && !back ) { break; } } } ++wholeMeshTriangleIndex; } } var newMeshCenter = Vector3.zero; if ( !usePhysics ) { var newMesh = prep.startMesh = new Mesh(); #if UNITY_4_OR_ABOVE newMesh.MarkDynamic(); #endif newMesh.vertices = newVertices; if ( newNormals != null ) newMesh.normals = newNormals; if ( newTangents != null ) newMesh.tangents = newTangents; if ( newUV != null ) newMesh.uv = newUV; if ( newUV2 != null ) newMesh.uv2 = newUV2; if ( newColors != null ) newMesh.colors = newColors; newMesh.subMeshCount = subMeshCount; newVertexNumber = 0; for ( var subMesh = 0; subMesh < subMeshCount; ++subMesh ) { var n = oldMeshTriangles[subMesh].Length * 2; var newTriangles = new int[n]; for ( var i = 0; i < n; ++i, ++newVertexNumber ) { newTriangles[i] = newVertexNumber; } newMesh.SetTriangles(newTriangles, subMesh); } if ( useMeshBoundsCenter ) newMeshCenter = newMesh.bounds.center; } var triangleNormals = prep.triangleNormals = new Vector3[frontTriangles]; var firstVertexIndex = 0; for ( var triangleNumber = 0; triangleNumber < frontTriangles; ++triangleNumber, firstVertexIndex += 6 ) { Vector3 centroid; if ( usePhysics ) { centroid = triangleCentroids[triangleNumber]; } else { centroid = (newVertices[firstVertexIndex] + newVertices[firstVertexIndex + 1] + newVertices[firstVertexIndex + 2]) / 3; triangleCentroids[triangleNumber] = centroid; } Vector3 normal; if ( useNormals && newNormals != null ) { if ( usePhysics ) { newNormals = physicsMeshes[triangleNumber].normals; firstVertexIndex = 0; } normal = ((newNormals[firstVertexIndex] + newNormals[firstVertexIndex + 1] + newNormals[firstVertexIndex + 2]) / 3).normalized; } else { normal = centroid; if ( useMeshBoundsCenter ) { normal -= newMeshCenter; } normal.Normalize(); } triangleNormals[triangleNumber] = normal; } preparation = prep; if ( cachePreparation ) cache[oldMesh] = prep; if ( fadeTime != 0 && !shadersAlreadyHandleTransparency ) { // Preload any replacement shaders that will be needed: foreach ( var i in GetComponent<Renderer>().sharedMaterials ) { var shader = i.shader; var replacement = Fade.GetReplacementFor(shader); if ( replacement == null || !replacement.name.StartsWith("Transparent/") ) { Debug.LogWarning("Couldn't find an explicitly transparent version of shader" + " '" + shader.name + "' so fading may not work. If the shader does" + " support transparency then this warning can be avoided by enabling" + " the 'Shaders Already Handle Transparency' option."); } } } }