public void Impact(Vector3 impactPoint, Vector3 impactForce, Meshinator.ImpactShapes impactShape, Meshinator.ImpactTypes impactType) { // Look through all triangles to see which ones are within the impactForce from the impactPoint, // and measure the area of every triangle in the list Dictionary<int, float> triangleIndexToTriangleArea = new Dictionary<int, float>(); foreach (int triangleIndex in GetIntersectedTriangleIndices(impactPoint, impactForce.magnitude)) { float areaOfTriangle = GetAreaOfTriangle(triangleIndex); triangleIndexToTriangleArea.Add(triangleIndex, areaOfTriangle); } // Keep breaking down the largest triangle until there are more than c_MinTrianglesPerImpact // triangles in the list while (triangleIndexToTriangleArea.Keys.Count < c_MinTrianglesPerImpact) { // If we have 64988 vertices or more, we can't add any more or we risk going over the // 65000 limit, which causes problems for unity. if (m_Vertices.Count > 64988) break; // Get the index of the biggest triangle in our dictionary int indexOfLargestTriangle = GetIndexOfLargestTriangle(triangleIndexToTriangleArea); // Break that triangle down and remove it from the dictionary List<int> newTriangleIndices = BreakDownTriangle(indexOfLargestTriangle); triangleIndexToTriangleArea.Remove(indexOfLargestTriangle); // Measure the areas of the resulting triangles, and add them to the dictionary foreach (int triangleIndex in newTriangleIndices) { // Make sure each triangle is still intersected by our force before we add it back to the list if (IsTriangleIndexIntersected(triangleIndex, impactPoint, impactForce.magnitude)) { float areaOfTriangle = GetAreaOfTriangle(triangleIndex); triangleIndexToTriangleArea.Add(triangleIndex, areaOfTriangle); } } } // Now that we have the proper vertices and triangles, actually go about deforming the mesh // by moving the vertices around. AdjustVerticesForImpact(impactPoint, impactForce, impactShape, impactType); }
public void Impact(Vector3 point, Vector3 force, ImpactShapes impactShape, ImpactTypes impactType) { // See if we can do this right now if (!CanDoImpact(point, force)) { return; } // We're now set on course to calculate the impact deformation m_Calculating = true; // Set up m_Hull InitializeHull(); // Figure out the true impact force if (force.magnitude > m_MaxForcePerImpact) { force = force.normalized * m_MaxForcePerImpact; } float impactFactor = (force.magnitude - m_ForceResistance) * m_ForceMultiplier; if (impactFactor <= 0) { return; } // Localize the point and the force to account for transform scaling (and maybe rotation or translation) Vector3 impactPoint = transform.InverseTransformPoint(point); Vector3 impactForce = transform.InverseTransformDirection(force.normalized) * impactFactor; // Limit the force by the extents of the initial bounds to keep things reasonable float impactForceX = Mathf.Max(Mathf.Min(impactForce.x, m_InitialBounds.extents.x), -m_InitialBounds.extents.x); float impactForceY = Mathf.Max(Mathf.Min(impactForce.y, m_InitialBounds.extents.y), -m_InitialBounds.extents.y); float impactForceZ = Mathf.Max(Mathf.Min(impactForce.z, m_InitialBounds.extents.z), -m_InitialBounds.extents.z); impactForce = new Vector3(impactForceX, impactForceY, impactForceZ); // Run the mesh deformation on another thread ThreadManager.RunAsync(() => { // Do all the math to deform this mesh m_Hull.Impact(impactPoint, impactForce, impactShape, impactType); // Queue the final mesh setting on the main thread once the deformations are done ThreadManager.QueueOnMainThread(() => { // Clear out the current Mesh and MeshCollider (if we have one) for now MeshFilter meshFilter = gameObject.GetComponent <MeshFilter>(); if (meshFilter != null) { meshFilter.sharedMesh = null; } MeshCollider meshCollider = gameObject.GetComponent <MeshCollider>(); if (meshCollider != null) { meshCollider.sharedMesh = null; } // Get the newly-adjusted Mesh so we can work with it Mesh newMesh = m_Hull.GetMesh(); // If this is a fracture, then create a new GameObject for the chunk that broke off if (impactType == ImpactTypes.Fracture) { Mesh subHullMesh = m_Hull.GetSubHullMesh(); if (subHullMesh != null) { // Create the new GameObject GameObject newGO = (GameObject)GameObject.Instantiate(gameObject); // Set the new Mesh onto the MeshFilter and MeshCollider MeshFilter newMeshFilter = newGO.GetComponent <MeshFilter>(); MeshCollider newMeshCollider = newGO.GetComponent <MeshCollider>(); if (newMeshFilter != null) { newMeshFilter.sharedMesh = subHullMesh; } if (newMeshCollider != null) { newMeshCollider.sharedMesh = subHullMesh; } // If using convex MeshColliders, it's possible for colliders to overlap right after a // fracture, which causes exponentially more fractures... So, right after a fracture, // temporarily disable impacts to both the old GameObject, and the new one we just created DelayCollisions(); Meshinator subHullMeshinator = newGO.GetComponent <Meshinator>(); if (subHullMeshinator != null) { subHullMeshinator.DelayCollisions(); } // Figure out the approximate volume of our Hull and SubHull meshes. This // will be used to calculate the rigidbody masses (if a rigidbodies are present) // for the old and new GameObjects. if (gameObject.GetComponent <Rigidbody>() != null && newGO.GetComponent <Rigidbody>() != null) { Vector3 hullSize = newMesh.bounds.size; float hullVolume = hullSize.x * hullSize.y * hullSize.z; Vector3 subHullSize = subHullMesh.bounds.size; float subHullVolume = subHullSize.x * subHullSize.y * subHullSize.z; float totalVolume = hullVolume + subHullVolume; float totalMass = gameObject.GetComponent <Rigidbody>().mass; gameObject.GetComponent <Rigidbody>().mass = totalMass * (hullVolume / totalVolume); newGO.GetComponent <Rigidbody>().mass = totalMass * (subHullVolume / totalVolume); // Set the old velocity onto the new GameObject's rigidbody newGO.GetComponent <Rigidbody>().velocity = gameObject.GetComponent <Rigidbody>().velocity; // Set the centers of mass to be within the new meshes gameObject.GetComponent <Rigidbody>().centerOfMass = newMesh.bounds.center; newGO.GetComponent <Rigidbody>().centerOfMass = subHullMesh.bounds.center; } } } // Set the hull's new mesh back onto this game object if (meshFilter != null) { meshFilter.sharedMesh = newMesh; } // If this GameObject has a MeshCollider, put the new mesh there too if (meshCollider != null) { meshCollider.sharedMesh = newMesh; } // Drop our cached Hull if we're not supposed to keep it around if (m_CacheOption == CacheOptions.None) { m_Hull = null; } // Our calculations are done m_Calculating = false; }); }); }
private void AdjustVerticesForImpact(Vector3 impactPoint, Vector3 impactForce, Meshinator.ImpactShapes impactShape, Meshinator.ImpactTypes impactType) { // Get the radius from the impactPoint that we'll be looking into float impactRadius = impactForce.magnitude; // Figure out how many vertices we will move Dictionary<int, float> movedVertexToForceMagnitudeMap = new Dictionary<int, float>(); for (int i = 0; i < m_Vertices.Count; i++) { Vector3 vertex = m_Vertices[i]; // Figure out the distance from the impact to this vector in different ways to // determine the shape of the impact Vector3 distanceVector = Vector3.zero; switch (impactShape) { case Meshinator.ImpactShapes.FlatImpact: distanceVector = Vector3.Project(vertex - impactPoint, impactForce.normalized); break; case Meshinator.ImpactShapes.SphericalImpact: distanceVector = vertex - impactPoint; break; } // If we're using a FlatImpact and the angle between the impact vector, and this vertex to the point // if impact is greater than 90 degrees, then make the distance the negative of itself. This fixes some // weird issues that pop up by the physics system determining that the point of collision is inside the // mesh instead of on the surface float distance = distanceVector.magnitude; if (impactShape == Meshinator.ImpactShapes.FlatImpact && Vector3.Angle(vertex - impactPoint, impactForce) > 90f) distance = -distance; // If this vertex is within the impact radius, then we'll be moving this vertex. // Store the magnitude of the force by which this vertex will be moved float vertexForceMagnitude = Mathf.Max(0, (impactRadius - distance)) * c_CompressionResistance; if (distance < impactRadius && vertexForceMagnitude > 0) movedVertexToForceMagnitudeMap.Add(i, vertexForceMagnitude); } // Depending on our ImpactType, deform the mesh appropriately switch (impactType) { case Meshinator.ImpactTypes.Compression: CompressMeshVertices(movedVertexToForceMagnitudeMap, impactForce); break; case Meshinator.ImpactTypes.Fracture: FractureMeshVertices(movedVertexToForceMagnitudeMap, impactForce); break; } }