public int Hash(Vector3 p) { for (int i = 0; i < count; i++) { var item = buckets[i]; float diffX = p.x - item.x; float diffY = p.y - item.y; float diffZ = p.z - item.z; float sqrMag = diffX * diffX + diffY * diffY + diffZ * diffZ; if (sqrMag < bucketSize2) { return(i); } } if (count >= buckets.Length) { ExploderUtils.Log("Hash out of range: " + count + " " + buckets.Length); return(count - 1); } buckets[count++] = p; return(count - 1); }
void Boom() { var centroid = ExploderUtils.GetCentroid(gameObject); // place the exploder object to centroid position exploder.transform.position = centroid; exploder.ExplodeSelf = false; // adjust force vector to be in direction from shotgun exploder.ForceVector = ForceVector; // Utils.Log("ForceVec: " + exploder.ForceVector); exploder.Force = force; exploder.UseForceVector = true; // fragment pieces exploder.TargetFragments = targetFragments; // set explosion radius to 5 meters exploder.Radius = radius; // run explosion exploder.Explode(); //Destroy (this); }
public ExploderParams(ExploderObject exploder) { this.Position = ExploderUtils.GetCentroid(((Component)exploder).get_gameObject()); this.DontUseTag = exploder.DontUseTag; this.Radius = exploder.Radius; this.UseCubeRadius = exploder.UseCubeRadius; this.CubeRadius = exploder.CubeRadius; this.ForceVector = exploder.ForceVector; this.UseForceVector = exploder.UseForceVector; this.Force = exploder.Force; this.FrameBudget = exploder.FrameBudget; this.TargetFragments = exploder.TargetFragments; this.ExplodeSelf = exploder.ExplodeSelf; this.HideSelf = exploder.HideSelf; this.ThreadOptions = exploder.ThreadOption; this.DestroyOriginalObject = exploder.DestroyOriginalObject; this.SplitMeshIslands = exploder.SplitMeshIslands; this.FragmentOptions = exploder.FragmentOptions.Clone(); this.FragmentDeactivation = exploder.FragmentDeactivation.Clone(); this.FragmentSFX = exploder.FragmentSFX.Clone(); this.Use2DCollision = exploder.Use2DCollision; this.FragmentPoolSize = exploder.FragmentPoolSize; this.DisableRadiusScan = exploder.DisableRadiusScan; this.UniformFragmentDistribution = exploder.UniformFragmentDistribution; this.DisableTriangulation = exploder.DisableTriangulation; this.ExploderGameObject = ((Component)exploder).get_gameObject(); this.CuttingStyle = exploder.CuttingStyle; }
public ExploderParams(ExploderObject exploder) { Position = ExploderUtils.GetCentroid(exploder.gameObject); DontUseTag = exploder.DontUseTag; Radius = exploder.Radius; UseCubeRadius = exploder.UseCubeRadius; CubeRadius = exploder.CubeRadius; ForceVector = exploder.ForceVector; UseForceVector = exploder.UseForceVector; Force = exploder.Force; FrameBudget = exploder.FrameBudget; TargetFragments = exploder.TargetFragments; ExplodeSelf = exploder.ExplodeSelf; HideSelf = exploder.HideSelf; ThreadOptions = exploder.ThreadOption; DestroyOriginalObject = exploder.DestroyOriginalObject; SplitMeshIslands = exploder.SplitMeshIslands; FragmentOptions = exploder.FragmentOptions.Clone(); FragmentDeactivation = exploder.FragmentDeactivation.Clone(); FragmentSFX = exploder.FragmentSFX.Clone(); Use2DCollision = exploder.Use2DCollision; FragmentPoolSize = exploder.FragmentPoolSize; DisableRadiusScan = exploder.DisableRadiusScan; UniformFragmentDistribution = exploder.UniformFragmentDistribution; DisableTriangulation = exploder.DisableTriangulation; ExploderGameObject = exploder.gameObject; CuttingStyle = exploder.CuttingStyle; }
public void StartExplosionFromQueue(ExploderParams p) { ExploderUtils.Assert(currTaskType == TaskType.None, "Wrong task: " + currTaskType); this.parameters = p; processingFrames = 1; explosionWatch.Reset(); explosionWatch.Start(); // // do preprocess right away // currTaskType = TaskType.Preprocess; InitTask(currTaskType); RunTask(currTaskType); if (parameters.ThreadOptions != ExploderObject.ThreadOptions.Disabled) { currTaskType = NextTask(currTaskType); if (currTaskType != TaskType.None) { InitTask(currTaskType); RunTask(currTaskType, parameters.FrameBudget); } else { explosionWatch.Stop(); queue.OnExplosionFinished(parameters.id, explosionWatch.ElapsedMilliseconds); } } }
public void OnExplosionFinished(int id) { var explosion = queue.Dequeue(); ExploderUtils.Assert(explosion.id == id, "Explosion id mismatch!"); ProcessQueue(); }
/// <summary> /// deactivate this fragment piece /// </summary> public void Deactivate() { if (activePlayback == audioSource) { activePlayback = null; } Sleep(); ExploderUtils.SetActiveRecursively(gameObject, false); Visible = false; IsActive = false; if (meshFilter && meshFilter.sharedMesh) { DestroyImmediate(meshFilter.sharedMesh, true); } // turn off particles if (particleSystems != null) { foreach (var psystem in particleSystems) { psystem.Clear(); } } }
public void Explode(ExploderObject.OnExplosion callback) { var settings = new ExploderSettings { Position = ExploderUtils.GetCentroid(exploder.gameObject), DontUseTag = exploder.DontUseTag, Radius = exploder.Radius, ForceVector = exploder.ForceVector, UseForceVector = exploder.UseForceVector, Force = exploder.Force, FrameBudget = exploder.FrameBudget, TargetFragments = exploder.TargetFragments, DeactivateOptions = exploder.DeactivateOptions, DeactivateTimeout = exploder.DeactivateTimeout, MeshColliders = exploder.MeshColliders, ExplodeSelf = exploder.ExplodeSelf, HideSelf = exploder.HideSelf, DestroyOriginalObject = exploder.DestroyOriginalObject, ExplodeFragments = exploder.ExplodeFragments, SplitMeshIslands = exploder.SplitMeshIslands, FragmentOptions = exploder.FragmentOptions.Clone(), Callback = callback, processing = false, }; queue.Enqueue(settings); ProcessQueue(); }
public void StartExplosionFromQueue(ExploderParams p) { this.parameters = p; this.processingFrames = 1; this.explosionWatch.Reset(); this.explosionWatch.Start(); AudioSource component = (AudioSource)p.ExploderGameObject.GetComponent <AudioSource>(); if (Object.op_Implicit((Object)component)) { ExploderUtils.CopyAudioSource(component, this.audioSource); } this.currTaskType = TaskType.Preprocess; this.InitTask(this.currTaskType); this.RunTask(this.currTaskType, 0.0f); if (this.parameters.ThreadOptions == ExploderObject.ThreadOptions.Disabled) { return; } this.currTaskType = this.NextTask(this.currTaskType); if (this.currTaskType != TaskType.None) { this.InitTask(this.currTaskType); this.RunTask(this.currTaskType, this.parameters.FrameBudget); } else { this.explosionWatch.Stop(); this.queue.OnExplosionFinished(this.parameters.id, this.explosionWatch.ElapsedMilliseconds); } }
public ExploderSettings(ExploderObject exploder) { Position = ExploderUtils.GetCentroid(exploder.gameObject); DontUseTag = exploder.DontUseTag; Radius = exploder.Radius; UseCubeRadius = exploder.UseCubeRadius; CubeRadius = exploder.CubeRadius; ForceVector = exploder.ForceVector; UseForceVector = exploder.UseForceVector; Force = exploder.Force; FrameBudget = exploder.FrameBudget; TargetFragments = exploder.TargetFragments; DeactivateOptions = exploder.DeactivateOptions; DeactivateTimeout = exploder.DeactivateTimeout; MeshColliders = exploder.MeshColliders; ExplodeSelf = exploder.ExplodeSelf; HideSelf = exploder.HideSelf; DestroyOriginalObject = exploder.DestroyOriginalObject; ExplodeFragments = exploder.ExplodeFragments; SplitMeshIslands = exploder.SplitMeshIslands; FragmentOptions = exploder.FragmentOptions.Clone(); SfxOptions = exploder.SFXOptions.Clone(); Use2DCollision = exploder.Use2DCollision; FragmentPoolSize = exploder.FragmentPoolSize; FragmentPrefab = exploder.FragmentPrefab; FadeoutOptions = exploder.FadeoutOptions; SFXOptions = exploder.SFXOptions; DisableRadiusScan = exploder.DisableRadiusScan; UniformFragmentDistribution = exploder.UniformFragmentDistribution; AllowOpenMeshCutting = exploder.AllowOpenMeshCutting; }
private TaskType NextTask(TaskType taskType) { switch (taskType) { case TaskType.Preprocess: { if (meshSet.Count == 0) { return(TaskType.None); } if (parameters.PartialExplosion) { return(TaskType.PartialSeparator); } return(TaskType.ProcessCutter); } case TaskType.PartialSeparator: return(TaskType.ProcessCutter); case TaskType.ProcessCutter: { if (splitMeshIslands) { return(TaskType.IsolateMeshIslands); } if (parameters.Crack) { return(TaskType.PostprocessCrack); } return(TaskType.PostprocessExplode); } case TaskType.IsolateMeshIslands: { if (parameters.Crack) { return(TaskType.PostprocessCrack); } return(TaskType.PostprocessExplode); } case TaskType.PostprocessExplode: return(TaskType.None); case TaskType.PostprocessCrack: return(TaskType.None); default: ExploderUtils.Assert(false, "Invalid task type!"); break; } ExploderUtils.Assert(false, "Invalid task type!"); return(TaskType.None); }
/// <summary> /// create pool (array) of fragment game objects with all necessary components /// </summary> /// <param name="poolSize">number of fragments</param> /// <param name="useMeshColliders">use mesh colliders</param> /// <param name="use2dCollision">enable Unity 2D collision system</param> public void Allocate(int poolSize, bool useMeshColliders, bool use2dCollision, bool useGravity) { ExploderUtils.Assert(poolSize > 0, ""); if (pool == null || pool.Length < poolSize || useMeshColliders != this.meshColliders) { DestroyFragments(); pool = new Fragment[poolSize]; this.meshColliders = useMeshColliders; for (int i = 0; i < poolSize; i++) { var fragment = new GameObject("fragment_" + i); fragment.AddComponent <MeshFilter>(); fragment.AddComponent <MeshRenderer>(); if (use2dCollision) { #if PHYSICS_2D fragment.AddComponent <PolygonCollider2D>(); fragment.AddComponent <Rigidbody2D>(); #endif } else { if (useMeshColliders) { var meshCollider = fragment.AddComponent <MeshCollider>(); meshCollider.convex = true; } else { fragment.AddComponent <BoxCollider>(); } Rigidbody rb = fragment.AddComponent <Rigidbody>(); rb.useGravity = useGravity; } fragment.AddComponent <ExploderOption>(); var fragmentComponent = fragment.AddComponent <Fragment>(); fragment.transform.parent = gameObject.transform; pool[i] = fragmentComponent; ExploderUtils.SetActiveRecursively(fragment.gameObject, false); fragmentComponent.RefreshComponentsCache(); fragmentComponent.Sleep(); } } }
/// <summary> /// c-tor /// </summary> /// <param name="pnts">points of the polygon</param> public Polygon(Vector2[] pnts) { ExploderUtils.Assert(pnts.Length >= 3, "Invalid polygon!"); Points = pnts; Area = GetArea(); holes = new List <Polygon>(); }
public long Explode() { int count = this.pool.Count; int index = 0; if (count == 0) { return(0); } this.watch.Start(); if (this.parameters.Callback != null) { this.parameters.Callback(0.0f, ExploderObject.ExplosionState.ExplosionStarted); } Vector3 vector3 = Vector3.get_zero(); Quaternion quaternion = Quaternion.get_identity(); if (Object.op_Implicit((Object)this.originalObject)) { vector3 = Vector3.op_Subtraction(this.originalObject.get_transform().get_position(), this.initPos); quaternion = Quaternion.op_Multiply(this.originalObject.get_transform().get_rotation(), Quaternion.Inverse(this.initRot)); } while (index < count) { Fragment fragment = this.pool[index]; ++index; if (Object.op_Inequality((Object)this.originalObject, (Object)this.parameters.ExploderGameObject)) { ExploderUtils.SetActiveRecursively(this.originalObject, false); } else { ExploderUtils.EnableCollider(this.originalObject, false); ExploderUtils.SetVisible(this.originalObject, false); } Transform transform1 = ((Component)fragment).get_transform(); transform1.set_position(Vector3.op_Addition(transform1.get_position(), vector3)); Transform transform2 = ((Component)fragment).get_transform(); transform2.set_rotation(Quaternion.op_Multiply(transform2.get_rotation(), quaternion)); fragment.Explode(this.parameters); } if (this.parameters.DestroyOriginalObject && Object.op_Implicit((Object)this.originalObject) && !Object.op_Implicit((Object)this.originalObject.GetComponent <Fragment>())) { Object.Destroy((Object)this.originalObject); } if (this.parameters.ExplodeSelf && !this.parameters.DestroyOriginalObject) { ExploderUtils.SetActiveRecursively(this.parameters.ExploderGameObject, false); } if (this.parameters.HideSelf) { ExploderUtils.SetActiveRecursively(this.parameters.ExploderGameObject, false); } this.watch.Stop(); return(this.watch.ElapsedMilliseconds); }
public void OnExplosionFinished(int id, long ellapsedMS) { var explosion = queue.Dequeue(); ExploderUtils.Assert(explosion.id == id, "Explosion id mismatch!"); if (explosion.Callback != null) { explosion.Callback(ellapsedMS, explosion.Crack ? ExploderObject.ExplosionState.ObjectCracked : ExploderObject.ExplosionState.ExplosionFinished); } ProcessQueue(); }
public static void SetActiveRecursively(GameObject obj, bool status) { if (!Object.op_Implicit((Object)obj)) { return; } int childCount = obj.get_transform().get_childCount(); for (int index = 0; index < childCount; ++index) { ExploderUtils.SetActiveRecursively(((Component)obj.get_transform().GetChild(index)).get_gameObject(), status); } obj.SetActive(status); }
/// <summary> /// this is called from exploder class to start the explosion /// </summary> public void Explode() { activeObj = true; ExploderUtils.SetActiveRecursively(gameObject, true); visibilityCheckTimer = 0.1f; visible = true; deactivateTimer = deactivateTimeout; originalScale = transform.localScale; if (explodable) { tag = ExploderObject.Tag; } }
/// <summary> /// deactivate this fragment piece /// </summary> public void Deactivate() { ExploderUtils.SetActive(gameObject, false); visible = false; activeObj = false; // turn off particles if (emmiters != null) { foreach (var emitter in emmiters) { emitter.ClearParticles(); } } }
/// <summary> /// deactivate this fragment piece /// </summary> public void Deactivate() { ExploderUtils.SetActive(gameObject, false); visible = false; activeObj = false; // turn off particles if (particleSystems != null) { foreach (var psystem in particleSystems) { psystem.Clear(); } } }
private bool IsInRadius(GameObject o) { var centroid = ExploderUtils.GetCentroid(o); if (core.parameters.UseCubeRadius) { var localP = core.parameters.ExploderGameObject.transform.InverseTransformPoint(centroid); var localC = core.parameters.ExploderGameObject.transform.InverseTransformPoint(core.parameters.Position); return(Mathf.Abs(localP.x - localC.x) < core.parameters.CubeRadius.x && Mathf.Abs(localP.y - localC.y) < core.parameters.CubeRadius.y && Mathf.Abs(localP.z - localC.z) < core.parameters.CubeRadius.z); } return(core.parameters.Radius * core.parameters.Radius > (centroid - core.parameters.Position).sqrMagnitude); }
public List <Fragment> GetActiveFragments() { if (this.pool == null) { return((List <Fragment>)null); } List <Fragment> fragmentList = new List <Fragment>(this.pool.Length); foreach (Fragment fragment in this.pool) { if (ExploderUtils.IsActive(((Component)fragment).get_gameObject())) { fragmentList.Add(fragment); } } return(fragmentList); }
public void Explode(ExploderParams parameters) { this.settings = parameters; this.IsActive = true; ExploderUtils.SetActiveRecursively(((Component)this).get_gameObject(), true); this.visibilityCheckTimer = 0.1f; this.Visible = true; this.Cracked = false; this.collided = false; this.deactivateTimer = this.settings.FragmentDeactivation.DeactivateTimeout; this.originalScale = ((Component)this).get_transform().get_localScale(); if (this.settings.FragmentOptions.ExplodeFragments) { ((Component)this).set_tag(ExploderObject.Tag); } this.Emit(true); }
private void OnDrawGizmos() { if (!((Behaviour)this).get_enabled() || this.ExplodeSelf && this.DisableRadiusScan) { return; } Gizmos.set_color(Color.get_red()); if (this.UseCubeRadius) { Vector3 centroid = ExploderUtils.GetCentroid(((Component)this).get_gameObject()); Gizmos.set_matrix(((Component)this).get_transform().get_localToWorldMatrix()); Gizmos.DrawWireCube(((Component)this).get_transform().InverseTransformPoint(centroid), this.CubeRadius); } else { Gizmos.DrawWireSphere(ExploderUtils.GetCentroid(((Component)this).get_gameObject()), this.Radius); } }
void OnDrawGizmos() { if (enabled && !(ExplodeSelf && DisableRadiusScan)) { Gizmos.color = Color.red; if (UseCubeRadius) { var pos = ExploderUtils.GetCentroid(gameObject); Gizmos.matrix = transform.localToWorldMatrix; Gizmos.DrawWireCube(transform.InverseTransformPoint(pos), CubeRadius); } else { Gizmos.DrawWireSphere(ExploderUtils.GetCentroid(gameObject), Radius); } } }
/// <summary> /// returns list of currently active (visible) fragments /// </summary> /// <returns></returns> public List <Fragment> GetActiveFragments() { if (pool != null) { var list = new List <Fragment>(pool.Length); foreach (var fragment in pool) { if (ExploderUtils.IsActive(fragment.gameObject)) { list.Add(fragment); } } return(list); } return(null); }
/// <summary> /// this is called from exploder class to start the explosion /// </summary> public void Explode(ExploderParams parameters) { this.settings = parameters; IsActive = true; ExploderUtils.SetActiveRecursively(gameObject, true); visibilityCheckTimer = 0.1f; Visible = true; Cracked = false; collided = false; deactivateTimer = settings.FragmentDeactivation.DeactivateTimeout; originalScale = transform.localScale; if (settings.FragmentOptions.ExplodeFragments) { tag = ExploderObject.Tag; } Emit(true); }
public void Deactivate() { if (Object.op_Equality((Object)Fragment.activePlayback, (Object)this.audioSource)) { Fragment.activePlayback = (AudioSource)null; } this.Sleep(); ExploderUtils.SetActiveRecursively(((Component)this).get_gameObject(), false); this.Visible = false; this.IsActive = false; if (Object.op_Implicit((Object)this.meshFilter) && Object.op_Implicit((Object)this.meshFilter.get_sharedMesh())) { Object.DestroyImmediate((Object)this.meshFilter.get_sharedMesh(), true); } if (this.particleSystems == null) { return; } foreach (ParticleSystem particleSystem in this.particleSystems) { particleSystem.Clear(); } }
/// <summary> /// Compute intersection between a segment line (a, b) and a plane (p) /// from Real-Time Collision Detection Book by Christer Ericson /// </summary> /// <param name="a">first point of a segment</param> /// <param name="b">second point of a segment</param> /// <param name="t">normalized distance of intersection point on vector (ab)</param> /// <param name="q">point in intersection</param> /// <returns>true if there is an intersection</returns> public bool IntersectSegment(Vector3 a, Vector3 b, out float t, ref Vector3 q) { var abx = b.x - a.x; var aby = b.y - a.y; var abz = b.z - a.z; var dot0 = Normal.x * a.x + Normal.y * a.y + Normal.z * a.z; var dot1 = Normal.x * abx + Normal.y * aby + Normal.z * abz; t = (Distance - dot0) / dot1; if (t >= 0.0f - epsylon && t <= 1.0f + epsylon) { q.x = a.x + t * abx; q.y = a.y + t * aby; q.z = a.z + t * abz; return(true); } ExploderUtils.Log("IntersectSegment failed: " + t); q = Vector3.zero; return(false); }
void Triangulate(List <Dictionary <int, int> > contours, Plane plane, List <Vector3>[] vertices, List <Vector3>[] normals, List <Vector2>[] uvs, List <Vector4>[] tangents, List <Color32>[] colors, List <int>[] triangles, bool uvCutMesh, bool useTangents, bool useColors, bool useNormals) { if (contours.Count == 0 || contours[0].Count < 3) { // Utils.Log("Contour empty!: "); return; } // prepare plane matrix var m = plane.GetPlaneMatrix(); var mInv = m.inverse; var zShit = 0.0f; var polygons = new List <Polygon>(contours.Count); // construct polygons from contours Polygon highAreaPoly = null; foreach (var ctr in contours) { var polygonPoints = new Vector2[ctr.Count]; var j = 0; foreach (var i in ctr.Values) { var p = mInv * vertices[0][i]; polygonPoints[j++] = p; // save z-coordinate zShit = p.z; } var polygon = new Polygon(polygonPoints); polygons.Add(polygon); if (highAreaPoly == null || Mathf.Abs(highAreaPoly.Area) < Mathf.Abs(polygon.Area)) { highAreaPoly = polygon; } } ExploderUtils.Assert(polygons.Count > 0, "Zero polygons!"); // test for holes if (polygons.Count > 0) { var polyToRemove = new List <Polygon>(); foreach (var polygon in polygons) { if (polygon != highAreaPoly) { if (highAreaPoly.IsPointInside(polygon.Points[0])) { highAreaPoly.AddHole(polygon); polyToRemove.Add(polygon); } } } foreach (var polygon in polyToRemove) { polygons.Remove(polygon); } } var vertCounter0 = vertices[0].Count; var vertCounter1 = vertices[1].Count; foreach (var polygon in polygons) { if (!polygon.Triangulate(polygonIndicesArray)) { continue; } // ExploderUtils.Assert(indices.Count == polygonIndicesArray.Count, "Count mismatch!"); // // for (int i = 0; i < indices.Count; i++) // { // ExploderUtils.Assert(polygonIndicesArray[i] == indices[i], "Indices not match: " + polygons.Count); // // if (indices[i] != polygonIndicesArray[i]) // { // ExploderUtils.Assert(false, ""); // } // } // get polygon bounding square size var min = Mathf.Min(polygon.Min.x, polygon.Min.y); var max = Mathf.Max(polygon.Max.x, polygon.Max.y); var polygonSize = max - min; // Utils.Log("PolygonSize: " + polygonSize + " " + polygon.Min + " " + polygon.Max); // Utils.Log("Triangulate polygons: " + polygon.Points.Length); foreach (var polyPoint in polygon.Points) { var p = m * new Vector3(polyPoint.x, polyPoint.y, zShit); vertices[0].Add(p); vertices[1].Add(p); if (useNormals) { normals[0].Add(-plane.Normal); normals[1].Add(plane.Normal); } if (uvCutMesh) { var uv0 = new Vector2((polyPoint.x - min) / polygonSize, (polyPoint.y - min) / polygonSize); var uv1 = new Vector2((polyPoint.x - min) / polygonSize, (polyPoint.y - min) / polygonSize); // normalize uv to fit cross-section uv area var areaSizeX = crossSectionUV.z - crossSectionUV.x; var areaSizeY = crossSectionUV.w - crossSectionUV.y; uv0.x = crossSectionUV.x + uv0.x * areaSizeX; uv0.y = crossSectionUV.y + uv0.y * areaSizeY; uv1.x = crossSectionUV.x + uv1.x * areaSizeX; uv1.y = crossSectionUV.y + uv1.y * areaSizeY; uvs[0].Add(uv0); uvs[1].Add(uv1); } else { uvs[0].Add(Vector2.zero); uvs[1].Add(Vector2.zero); } if (useTangents) { // fast and dirty way to create tangents var v0 = plane.Normal; MeshUtils.Swap(ref v0.x, ref v0.y); MeshUtils.Swap(ref v0.y, ref v0.z); Vector4 tangent = Vector3.Cross(plane.Normal, v0); tangent.w = 1.0f; tangents[0].Add(tangent); tangent.w = -1.0f; tangents[1].Add(tangent); } if (useColors) { colors[0].Add(crossSectionVertexColour); colors[1].Add(crossSectionVertexColour); } } var indicesCount = polygonIndicesArray.Count; var j = indicesCount - 1; for (int i = 0; i < indicesCount; i++) { triangles[0].Add(vertCounter0 + polygonIndicesArray[i]); triangles[1].Add(vertCounter1 + polygonIndicesArray[j]); j--; } vertCounter0 += polygon.Points.Length; vertCounter1 += polygon.Points.Length; } }
float Cut(ExploderMesh mesh, ExploderTransform meshTransform, Plane plane, bool triangulateHoles, bool allowOpenMesh, ref List <ExploderMesh> meshes) { var stopWatch = new Stopwatch(); stopWatch.Start(); #if PROFILING MeasureIt.Begin("CutAllocations"); #endif // cache mesh data var trianglesNum = mesh.triangles.Length; var verticesNum = mesh.vertices.Length; var meshTriangles = mesh.triangles; var meshTangents = mesh.tangents; var meshColors = mesh.colors32; var meshVertices = mesh.vertices; var meshNormals = mesh.normals; var meshUV = mesh.uv; var useMeshTangents = meshTangents != null && meshTangents.Length > 0; var useVertexColors = meshColors != null && meshColors.Length > 0; var useNormals = meshNormals != null && meshNormals.Length > 0; // preallocate buffers AllocateBuffers(trianglesNum, verticesNum, useMeshTangents, useVertexColors); ExploderMesh mesh0, mesh1; #if PROFILING MeasureIt.End("CutAllocations"); MeasureIt.Begin("CutCycleFirstPass"); #endif // first pass - find complete triangles on both sides of the plane for (int i = 0; i < trianglesNum; i += 3) { // get triangle points var v0 = meshVertices[meshTriangles[i]]; var v1 = meshVertices[meshTriangles[i + 1]]; var v2 = meshVertices[meshTriangles[i + 2]]; var side0 = plane.GetSideFix(ref v0); var side1 = plane.GetSideFix(ref v1); var side2 = plane.GetSideFix(ref v2); meshVertices[meshTriangles[i]] = v0; meshVertices[meshTriangles[i + 1]] = v1; meshVertices[meshTriangles[i + 2]] = v2; // Utils.Log(plane.Pnt + " " + v0 + " " + v1 + " " + " " + v2); // all points on one side if (side0 == side1 && side1 == side2) { var idx = side0 ? 0 : 1; if (meshTriangles[i] >= triCache.Length) { ExploderUtils.Log("TriCacheError " + meshTriangles[i] + " " + triCache.Length + " " + meshVertices.Length); } if (triCache[meshTriangles[i]] == 0) { triangles[idx].Add(triCounter[idx]); vertices[idx].Add(meshVertices[meshTriangles[i]]); uvs[idx].Add(meshUV[meshTriangles[i]]); if (useNormals) { normals[idx].Add(meshNormals[meshTriangles[i]]); } if (useMeshTangents) { tangents[idx].Add(meshTangents[meshTriangles[i]]); } if (useVertexColors) { vertexColors[idx].Add(meshColors[meshTriangles[i]]); } centroid[idx] += meshVertices[meshTriangles[i]]; triCache[meshTriangles[i]] = triCounter[idx] + 1; triCounter[idx]++; } else { triangles[idx].Add(triCache[meshTriangles[i]] - 1); } if (triCache[meshTriangles[i + 1]] == 0) { triangles[idx].Add(triCounter[idx]); vertices[idx].Add(meshVertices[meshTriangles[i + 1]]); uvs[idx].Add(meshUV[meshTriangles[i + 1]]); if (useNormals) { normals[idx].Add(meshNormals[meshTriangles[i + 1]]); } if (useMeshTangents) { tangents[idx].Add(meshTangents[meshTriangles[i + 1]]); } if (useVertexColors) { vertexColors[idx].Add(meshColors[meshTriangles[i + 1]]); } centroid[idx] += meshVertices[meshTriangles[i + 1]]; triCache[meshTriangles[i + 1]] = triCounter[idx] + 1; triCounter[idx]++; } else { triangles[idx].Add(triCache[meshTriangles[i + 1]] - 1); } if (triCache[meshTriangles[i + 2]] == 0) { triangles[idx].Add(triCounter[idx]); vertices[idx].Add(meshVertices[meshTriangles[i + 2]]); uvs[idx].Add(meshUV[meshTriangles[i + 2]]); if (useNormals) { normals[idx].Add(meshNormals[meshTriangles[i + 2]]); } if (useMeshTangents) { tangents[idx].Add(meshTangents[meshTriangles[i + 2]]); } if (useVertexColors) { vertexColors[idx].Add(meshColors[meshTriangles[i + 2]]); } centroid[idx] += meshVertices[meshTriangles[i + 2]]; triCache[meshTriangles[i + 2]] = triCounter[idx] + 1; triCounter[idx]++; } else { triangles[idx].Add(triCache[meshTriangles[i + 2]] - 1); } } else { // intersection triangles add to list and process it in second pass cutTris.Add(i); } } if (vertices[0].Count == 0) { centroid[0] = meshVertices[0]; } else { centroid[0] /= vertices[0].Count; } if (vertices[1].Count == 0) { centroid[1] = meshVertices[1]; } else { centroid[1] /= vertices[1].Count; } // UnityEngine.Debug.LogFormat("cut: {0} -- {1}, normal: {2}, tris: {3}", vertices[0].Count, vertices[1].Count, plane.Normal, cutTris.Count); #if PROFILING MeasureIt.End("CutCycleFirstPass"); MeasureIt.Begin("CutCycleSecondPass"); #endif if (cutTris.Count < 1) { stopWatch.Stop(); return(stopWatch.ElapsedMilliseconds); } AllocateContours(cutTris.Count); // second pass - cut intersecting triangles in half foreach (var cutTri in cutTris) { var triangle = new Triangle { ids = new[] { meshTriangles[cutTri + 0], meshTriangles[cutTri + 1], meshTriangles[cutTri + 2] }, pos = new[] { meshVertices[meshTriangles[cutTri + 0]], meshVertices[meshTriangles[cutTri + 1]], meshVertices[meshTriangles[cutTri + 2]] }, normal = useNormals ? new[] { meshNormals[meshTriangles[cutTri + 0]], meshNormals[meshTriangles[cutTri + 1]], meshNormals[meshTriangles[cutTri + 2]] } : new[] { Vector3.zero, Vector3.zero, Vector3.zero }, uvs = new[] { meshUV[meshTriangles[cutTri + 0]], meshUV[meshTriangles[cutTri + 1]], meshUV[meshTriangles[cutTri + 2]] }, tangents = useMeshTangents ? new[] { meshTangents[meshTriangles[cutTri + 0]], meshTangents[meshTriangles[cutTri + 1]], meshTangents[meshTriangles[cutTri + 2]] } : new [] { Vector4.zero, Vector4.zero, Vector4.zero }, colors = useVertexColors ? new[] { meshColors[meshTriangles[cutTri + 0]], meshColors[meshTriangles[cutTri + 1]], meshColors[meshTriangles[cutTri + 2]] } : new Color32[] { Color.white, Color.white, Color.white }, }; // check points with a plane var side0 = plane.GetSide(triangle.pos[0]); var side1 = plane.GetSide(triangle.pos[1]); var side2 = plane.GetSide(triangle.pos[2]); float t0, t1; Vector3 s0 = Vector3.zero, s1 = Vector3.zero; var idxLeft = side0 ? 0 : 1; var idxRight = 1 - idxLeft; if (side0 == side1) { var a = plane.IntersectSegment(triangle.pos[2], triangle.pos[0], out t0, ref s0); var b = plane.IntersectSegment(triangle.pos[2], triangle.pos[1], out t1, ref s1); ExploderUtils.Assert(a && b, "!!!!!!!!!!!!!!!"); // left side ... 2 triangles var s0Left = AddIntersectionPoint(s0, triangle, triangle.ids[2], triangle.ids[0], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var s1Left = AddIntersectionPoint(s1, triangle, triangle.ids[2], triangle.ids[1], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v0Left = AddTrianglePoint(triangle.pos[0], triangle.normal[0], triangle.uvs[0], triangle.tangents[0], triangle.colors[0], triangle.ids[0], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v1Left = AddTrianglePoint(triangle.pos[1], triangle.normal[1], triangle.uvs[1], triangle.tangents[1], triangle.colors[1], triangle.ids[1], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); // Triangle (s0, v0, s1) triangles[idxLeft].Add(s0Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(s1Left); // Triangle (s1, v0, v1) triangles[idxLeft].Add(s1Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(v1Left); // right side ... 1 triangle var s0Right = AddIntersectionPoint(s0, triangle, triangle.ids[2], triangle.ids[0], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var s1Right = AddIntersectionPoint(s1, triangle, triangle.ids[2], triangle.ids[1], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var v2Right = AddTrianglePoint(triangle.pos[2], triangle.normal[2], triangle.uvs[2], triangle.tangents[2], triangle.colors[2], triangle.ids[2], triCache, cornerVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); // Triangle (v2, s0, s1) triangles[idxRight].Add(v2Right); triangles[idxRight].Add(s0Right); triangles[idxRight].Add(s1Right); // buffer intersection vertices for triangulation if (triangulateHoles) { if (idxLeft == 0) { contour.AddTriangle(cutTri, s0Left, s1Left, s0, s1); } else { contour.AddTriangle(cutTri, s0Right, s1Right, s0, s1); } } } else if (side0 == side2) { var a = plane.IntersectSegment(triangle.pos[1], triangle.pos[0], out t0, ref s1); var b = plane.IntersectSegment(triangle.pos[1], triangle.pos[2], out t1, ref s0); ExploderUtils.Assert(a && b, "!!!!!!!!!!!!!"); // left side ... 2 triangles var s0Left = AddIntersectionPoint(s0, triangle, triangle.ids[1], triangle.ids[2], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var s1Left = AddIntersectionPoint(s1, triangle, triangle.ids[1], triangle.ids[0], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v0Left = AddTrianglePoint(triangle.pos[0], triangle.normal[0], triangle.uvs[0], triangle.tangents[0], triangle.colors[0], triangle.ids[0], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v2Left = AddTrianglePoint(triangle.pos[2], triangle.normal[2], triangle.uvs[2], triangle.tangents[2], triangle.colors[2], triangle.ids[2], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); // Triangle (v2, s1, s0) triangles[idxLeft].Add(v2Left); triangles[idxLeft].Add(s1Left); triangles[idxLeft].Add(s0Left); // Triangle (v2, v0, s1) triangles[idxLeft].Add(v2Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(s1Left); // right side ... 1 triangle var s0Right = AddIntersectionPoint(s0, triangle, triangle.ids[1], triangle.ids[2], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var s1Right = AddIntersectionPoint(s1, triangle, triangle.ids[1], triangle.ids[0], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var v1Right = AddTrianglePoint(triangle.pos[1], triangle.normal[1], triangle.uvs[1], triangle.tangents[1], triangle.colors[1], triangle.ids[1], triCache, cornerVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); // Triangle (s0, s1, v1) triangles[idxRight].Add(s0Right); triangles[idxRight].Add(s1Right); triangles[idxRight].Add(v1Right); // buffer intersection vertices for triangulation if (triangulateHoles) { if (idxLeft == 0) { contour.AddTriangle(cutTri, s0Left, s1Left, s0, s1); } else { contour.AddTriangle(cutTri, s0Right, s1Right, s0, s1); } } } else { var a = plane.IntersectSegment(triangle.pos[0], triangle.pos[1], out t0, ref s0); var b = plane.IntersectSegment(triangle.pos[0], triangle.pos[2], out t1, ref s1); ExploderUtils.Assert(a && b, "!!!!!!!!!!!!!"); // right side ... 2 triangles var s0Right = AddIntersectionPoint(s0, triangle, triangle.ids[0], triangle.ids[1], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var s1Right = AddIntersectionPoint(s1, triangle, triangle.ids[0], triangle.ids[2], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var v1Right = AddTrianglePoint(triangle.pos[1], triangle.normal[1], triangle.uvs[1], triangle.tangents[1], triangle.colors[1], triangle.ids[1], triCache, cornerVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var v2Right = AddTrianglePoint(triangle.pos[2], triangle.normal[2], triangle.uvs[2], triangle.tangents[2], triangle.colors[2], triangle.ids[2], triCache, cornerVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); // Triangle (v2, s1, v1) triangles[idxRight].Add(v2Right); triangles[idxRight].Add(s1Right); triangles[idxRight].Add(v1Right); // Triangle (s1, s0, v1) triangles[idxRight].Add(s1Right); triangles[idxRight].Add(s0Right); triangles[idxRight].Add(v1Right); // left side ... 1 triangle var s0Left = AddIntersectionPoint(s0, triangle, triangle.ids[0], triangle.ids[1], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var s1Left = AddIntersectionPoint(s1, triangle, triangle.ids[0], triangle.ids[2], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v0Left = AddTrianglePoint(triangle.pos[0], triangle.normal[0], triangle.uvs[0], triangle.tangents[0], triangle.colors[0], triangle.ids[0], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); // Triangle (s1, v0, s0) triangles[idxLeft].Add(s1Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(s0Left); // buffer intersection vertices for triangulation if (triangulateHoles) { if (idxLeft == 0) { contour.AddTriangle(cutTri, s0Left, s1Left, s0, s1); } else { contour.AddTriangle(cutTri, s0Right, s1Right, s0, s1); } } } } #if PROFILING MeasureIt.End("CutCycleSecondPass"); #endif if (triangulateHoles) { #if PROFILING MeasureIt.Begin("FindContours"); #endif contour.FindContours(); if (contour.contour.Count == 0 || contour.contour[0].Count < 3) { // triangulateHoles = false; if (allowOpenMesh) { triangulateHoles = false; } else { stopWatch.Stop(); return(stopWatch.ElapsedMilliseconds); } } #if PROFILING MeasureIt.End("FindContours"); #endif } List <int>[] trianglesCut = null; var centroid0 = Vector3.zero; var centroid1 = Vector3.zero; var min0 = Vector3.zero; var max0 = Vector3.zero; var min1 = Vector3.zero; var max1 = Vector3.zero; ExploderMesh.CalculateCentroid(vertices[0], ref centroid0, ref min0, ref max0); ExploderMesh.CalculateCentroid(vertices[1], ref centroid1, ref min1, ref max1); if (triangulateHoles) { #if PROFILING MeasureIt.Begin("Triangulate"); #endif trianglesCut = new List <int>[2] { new List <int>(contour.MidPointsCount), new List <int>(contour.MidPointsCount) }; Triangulate(contour.contour, plane, vertices, normals, uvs, tangents, vertexColors, trianglesCut, true, useMeshTangents, useVertexColors, useNormals); #if PROFILING MeasureIt.End("Triangulate"); #endif } if (vertices[0].Count > 3 && vertices[1].Count > 3) { #if PROFILING MeasureIt.Begin("CutEndCopyBack"); #endif mesh0 = new ExploderMesh(); mesh1 = new ExploderMesh(); var verticesArray0 = vertices[0].ToArray(); var verticesArray1 = vertices[1].ToArray(); mesh0.vertices = verticesArray0; mesh0.uv = uvs[0].ToArray(); mesh1.vertices = verticesArray1; mesh1.uv = uvs[1].ToArray(); if (useNormals) { mesh0.normals = normals[0].ToArray(); mesh1.normals = normals[1].ToArray(); } if (useMeshTangents) { mesh0.tangents = tangents[0].ToArray(); mesh1.tangents = tangents[1].ToArray(); } if (useVertexColors) { mesh0.colors32 = vertexColors[0].ToArray(); mesh1.colors32 = vertexColors[1].ToArray(); } if (trianglesCut != null && trianglesCut[0].Count > 3) { triangles[0].AddRange(trianglesCut[0]); triangles[1].AddRange(trianglesCut[1]); } mesh0.triangles = triangles[0].ToArray(); mesh1.triangles = triangles[1].ToArray(); mesh0.centroid = centroid0; mesh0.min = min0; mesh0.max = max0; mesh1.centroid = centroid1; mesh1.min = min1; mesh1.max = max1; #if PROFILING MeasureIt.End("CutEndCopyBack"); #endif meshes = new List <ExploderMesh> { mesh0, mesh1 }; stopWatch.Stop(); return(stopWatch.ElapsedMilliseconds); } stopWatch.Stop(); // UnityEngine.Debug.Log("Empty cut! " + vertices[0].Count + " " + vertices[1].Count); return(stopWatch.ElapsedMilliseconds); }