//Adds a new collider to the group with the given properties public ColliderInstance AddCollider(GeneratorProps gp) { ColliderInstance newCol = new ColliderInstance(gp); colliders.Add(newCol); SetColliderComponents(false); return(newCol); }
//Creates a new collider by copying an existing one public ColliderInstance(ColliderInstance ci) { name = ci.name; if (ci.colMesh != null) { colMesh = Object.Instantiate(ci.colMesh); } props = new GeneratorProps(ci.props); }
//Creates a new collider with the given properties public ColliderInstance(GeneratorProps gp) { if (gp != null) { props = gp; } else { props = new GeneratorProps(Vector3.one, 0.1f); } }
//Creates a new collider with the given name and properties public ColliderInstance(string n, GeneratorProps gp) { name = n; if (gp != null) { props = gp; } else { props = new GeneratorProps(Vector3.one, 0.1f); } }
//Sets the collider at the index in the list to use the given mesh and properties public void SetCollider(int index, Mesh m, GeneratorProps gp) { if (m != null) { colliders[index].colMesh = Instantiate(m); } if (gp != null) { colliders[index].props = gp; } SetColliderComponents(true); }
//Creates a new collider with the given name, mesh, and properties public ColliderInstance(string n, Mesh m, GeneratorProps gp) { name = n; if (m != null) { colMesh = Object.Instantiate(m); } if (gp != null) { props = gp; } else { props = new GeneratorProps(Vector3.one, 0.1f); } }
//Copies properties from another GeneratorProps instance //If copyPreset is true, it will also copy the linked preset reference from the other instance public void CopyProperties(GeneratorProps gp, bool copyPreset) { if (copyPreset) { linkedPreset = gp.linkedPreset; } meshAssetPath = gp.meshAssetPath; cornerPositionMode = gp.cornerPositionMode; cornerRadiusMode = gp.cornerRadiusMode; cornerOffsetMode = gp.cornerOffsetMode; cornerDetailMode = gp.cornerDetailMode; boxSize = gp.boxSize; boxOffset = gp.boxOffset; boxSidesPos = gp.boxSidesPos; boxSidesNeg = gp.boxSidesNeg; for (int i = 0; i < corners.Length; i++) { corners[i] = new ColliderCorner(gp.corners[i]); } for (int i = 0; i < cornerDetails.Length; i++) { cornerDetails[i] = gp.cornerDetails[i]; } topSegments = gp.topSegments; bottomSegments = gp.bottomSegments; XYDetail = gp.XYDetail; YZDetail = gp.YZDetail; XZDetail = gp.XZDetail; detailSmoothness = gp.detailSmoothness; stripDistribution1 = gp.stripDistribution1; stripDistribution2 = gp.stripDistribution2; bypassPolyTest = gp.bypassPolyTest; polyTestMode = gp.polyTestMode; detailReduction = gp.detailReduction; detailReductionAttempts = gp.detailReductionAttempts; boxedCorners = gp.boxedCorners; hooks = new DeformHook[gp.hooks.Length]; for (int i = 0; i < hooks.Length; i++) { hooks[i] = new DeformHook(gp.hooks[i]); } }
private void Update() { if (target == null || colGroup == null) { return; } if (colGroup.colliders.Count == 0) { //Add a collider to the group if it doesn't exist and add a deformation hook to it colProps = colGroup.AddCollider().props; colProps.AddHook(DeformHook.HookType.Pull, Vector3.zero, Quaternion.LookRotation(Vector3.up, Vector3.forward), 1.0f, 0.0f, 1.0f); GenerateCollider(); } if (Input.GetKeyDown(KeyCode.Escape)) { Application.Quit(); } }
//The actual polygon test for the given properties and generation container using the indicated polygon test mode "pt" static bool MeshPolyTest(GeneratorInstance gi, GeneratorProps gp, PolygonTest pt) { if (gi.tris.Count >= 3 && gi.finalVerts.Length >= 3) { switch (pt) { case PolygonTest.TotalTriangles: return(gi.tris.Count / 3 <= 255); case PolygonTest.TotalQuads: return(gi.tris.Count / 6 <= 255); case PolygonTest.EdgeQuads: return(((gp.topSegments + gp.bottomSegments) * (gp.cornerDetails[0] + gp.cornerDetails[1] + gp.cornerDetails[2] + gp.cornerDetails[3] + 6)) <= 255); case PolygonTest.FaceEdgeQuads: return(((gp.topSegments + gp.bottomSegments) * ((gp.cornerDetails[0] + gp.cornerDetails[1] + gp.cornerDetails[2] + gp.cornerDetails[3]) * (gp.XZDetail + 1) + gp.XYDetail + gp.YZDetail + 2)) <= 255); } } return(false); }
//Mesh generation function wrapped by other methods; do not call this one directly static Mesh GenerationOperation(ref GeneratorProps genProps, out ColliderFinishStatus cFin, bool preview) { genProps.VerifyProperties(); //Make sure properties are valid cFin = ColliderFinishStatus.Fail; //Default status PolygonTest polyTest = PolygonTest.TotalTriangles; //Default polygon test //Choose proper polygon test mode if (genProps.polyTestMode == PolygonTestMode.BestGuess) { polyTest = genProps.boxedCorners ? PolygonTest.EdgeQuads : PolygonTest.FaceEdgeQuads; } else if (genProps.polyTestMode == PolygonTestMode.SafeGuess) { polyTest = genProps.boxedCorners ? PolygonTest.TotalQuads : PolygonTest.TotalTriangles; } else { polyTest = (PolygonTest)(genProps.polyTestMode - 2); } GeneratorInstance gi = new GeneratorInstance();//Temporary container for generation data in the static method bool passedPolyTest = false; bool generating = true; int genAttempts = 0; //Generation attempts int reductionType = 0; //0 = plane strips, 1 = top/bottom segments, 2 = corners //Loop is for repeat detail reduction attempts while (generating) { if (gi != null) { gi.ClearData(); //Clear generation data before next attempt } GenerateMesh(ref gi, genProps); //The true mesh generation function if (genProps.bypassPolyTest || preview) { //Skipping polygon testing passedPolyTest = true; generating = false; } else if (genProps.detailReduction == GeneratorProps.DetailReductionMode.None) { //Skipping detail reduction and checking if polygon test was passed passedPolyTest = MeshPolyTest(gi, genProps, polyTest); generating = false; } else if (genProps.detailReductionAttempts > 0) { //Detail reduction process passedPolyTest = MeshPolyTest(gi, genProps, polyTest);//Polygon testing if (genProps.detailReduction == GeneratorProps.DetailReductionMode.All) { //Reduce all detail properties together, one at a time if (genAttempts < genProps.detailReductionAttempts && !passedPolyTest) { switch (reductionType) { case 0: //Place strip reduction genProps.XYDetail = Mathf.Max(0, genProps.XYDetail - 1); genProps.YZDetail = Mathf.Max(0, genProps.YZDetail - 1); genProps.XZDetail = Mathf.Max(0, genProps.XZDetail - 1); break; case 1: //Top and bottom segment reduction genProps.topSegments = Mathf.Max(0, genProps.topSegments - 1); genProps.bottomSegments = Mathf.Max(0, genProps.bottomSegments - 1); break; case 2: //Corner detail reduction genProps.cornerDetails[0] = Mathf.Max(0, genProps.cornerDetails[0] - 1); genProps.cornerDetails[1] = Mathf.Max(0, genProps.cornerDetails[1] - 1); genProps.cornerDetails[2] = Mathf.Max(0, genProps.cornerDetails[2] - 1); genProps.cornerDetails[3] = Mathf.Max(0, genProps.cornerDetails[3] - 1); break; } reductionType = (reductionType + 1) % 3;//Increment to next reduction type for subsequent attempt } else { generating = false; } } else if (genProps.detailReduction == GeneratorProps.DetailReductionMode.LargestFirst) { //Reduce greatest detail property values first if (genAttempts < genProps.detailReductionAttempts && !passedPolyTest) { //Get maximum detail value int maxDetail = Mathf.Max( genProps.XYDetail, genProps.YZDetail, genProps.XZDetail, genProps.topSegments, genProps.bottomSegments, genProps.cornerDetails[0], genProps.cornerDetails[1], genProps.cornerDetails[2], genProps.cornerDetails[3]); //Reduce all detail properties that match the maximum value if (maxDetail == genProps.XYDetail) { genProps.XYDetail = Mathf.Max(0, genProps.XYDetail - 1); } if (maxDetail == genProps.YZDetail) { genProps.YZDetail = Mathf.Max(0, genProps.YZDetail - 1); } if (maxDetail == genProps.XZDetail) { genProps.XZDetail = Mathf.Max(0, genProps.XZDetail - 1); } if (maxDetail == genProps.topSegments) { genProps.topSegments = Mathf.Max(0, genProps.topSegments - 1); } if (maxDetail == genProps.bottomSegments) { genProps.bottomSegments = Mathf.Max(0, genProps.bottomSegments - 1); } if (maxDetail == genProps.cornerDetails[0]) { genProps.cornerDetails[0] = Mathf.Max(0, genProps.cornerDetails[0] - 1); } if (maxDetail == genProps.cornerDetails[1]) { genProps.cornerDetails[1] = Mathf.Max(0, genProps.cornerDetails[1] - 1); } if (maxDetail == genProps.cornerDetails[2]) { genProps.cornerDetails[2] = Mathf.Max(0, genProps.cornerDetails[2] - 1); } if (maxDetail == genProps.cornerDetails[3]) { genProps.cornerDetails[3] = Mathf.Max(0, genProps.cornerDetails[3] - 1); } } else { generating = false; } } else { generating = false; } } else { generating = false; } genAttempts++; if (genAttempts > genProps.detailReductionAttempts) { //Quit detail reduction upon reaching maximum attempts and throw exception generating = false; if (!passedPolyTest) { cFin = ColliderFinishStatus.DetailTimeout; } } } if (passedPolyTest) { cFin = ColliderFinishStatus.Success;//Successful generation after passing polygon test } else if (cFin != ColliderFinishStatus.DetailTimeout) { cFin = ColliderFinishStatus.FailTriCount;//Failed generation due to having to many polygons } return(gi.colMesh); }
//Actual mesh generation with the indicated properties and generation container; do not call this directly static void GenerateMesh(ref GeneratorInstance gi, GeneratorProps genProps) { //Important vertex strips at ends of sections VertexStrip firstBottomStrip = new VertexStrip(); VertexStrip lastBottomStrip = new VertexStrip(); VertexStrip firstTopStrip = new VertexStrip(); VertexStrip lastTopStrip = new VertexStrip(); int bottomSegments = Mathf.Max(1, genProps.bottomSegments); int topSegments = Mathf.Max(1, genProps.topSegments); //Bottom section verts for (int i = 0; i < bottomSegments + 1; i++) { float stripProgress = (i * 1.0f) / (bottomSegments * 1.0f); //Properties for current vertex strip VertexStripProps stripProps = new VertexStripProps { isTop = false, flat = genProps.bottomSegments == 0, cornerDetails = genProps.cornerDetails, XYDetail = genProps.XYDetail, YZDetail = genProps.YZDetail, cornerProgress = Mathf.Pow(1.0f - Mathf.Pow(stripProgress, genProps.stripDistribution1), genProps.stripDistribution2), corners = genProps.corners, detailSmoothness = genProps.detailSmoothness }; lastBottomStrip = new VertexStrip(stripProps); gi.strips.Add(lastBottomStrip); if (i == 0) { firstBottomStrip = new VertexStrip(stripProps); } } //Top section verts for (int i = 0; i < topSegments + 1; i++) { float stripProgress = 1.0f - (i * 1.0f) / (topSegments * 1.0f); //Properties for current vertex strip VertexStripProps stripProps = new VertexStripProps { isTop = true, flat = genProps.topSegments == 0, cornerDetails = genProps.cornerDetails, XYDetail = genProps.XYDetail, YZDetail = genProps.YZDetail, cornerProgress = Mathf.Pow(1.0f - Mathf.Pow(stripProgress, genProps.stripDistribution1), genProps.stripDistribution2), corners = genProps.corners, detailSmoothness = genProps.detailSmoothness }; firstTopStrip = new VertexStrip(stripProps); //Add lateral strips between top and bottom sections if (genProps.XZDetail > 0 && i == 0) { int curStrip = 1; while (curStrip < genProps.XZDetail + 1) { gi.strips.Add(new VertexStrip(lastBottomStrip, firstTopStrip, ((curStrip * 1.0f) / ((genProps.XZDetail + 1) * 1.0f)), genProps.detailSmoothness)); curStrip++; } } else if (i == topSegments) { lastTopStrip = new VertexStrip(stripProps); } gi.strips.Add(firstTopStrip); } //Top cap lastTopStrip.ArrangeSides(); lastTopStrip.GenerateCap(); //Bottom cap firstBottomStrip.ArrangeSides(); firstBottomStrip.GenerateCap(); //Final vert array concatenation List <Vector3> tempVerts = new List <Vector3>(); for (int i = 0; i < gi.strips.Count; i++) { tempVerts.AddRange(gi.strips[i].verts); } //Adding vertices of the caps int capStartIndex = tempVerts.Count; tempVerts.AddRange(lastTopStrip.capVerts); tempVerts.AddRange(firstBottomStrip.capVerts); gi.finalVerts = tempVerts.ToArray(); //Triangle assignment int stripLength = gi.strips[0].verts.Count; int baseVert = 0; int stripLoop = 0; while (stripLoop < gi.strips.Count - 1) { for (int i = 0; i < stripLength - 1; i++) { gi.tris.Add(baseVert + i); gi.tris.Add(baseVert + stripLength + 1 + i); gi.tris.Add(baseVert + i + 1); gi.tris.Add(baseVert + stripLength + i); gi.tris.Add(baseVert + stripLength + 1 + i); gi.tris.Add(baseVert + i); } baseVert += stripLength; stripLoop++; } //Top cap triangles stripLength = lastTopStrip.side1.Count; for (int i = 0; i < lastTopStrip.capVerts.Count - stripLength - 1; i++) { if ((i + 1) % stripLength != 0) { gi.tris.Add(capStartIndex + i); gi.tris.Add(capStartIndex + i + 1); gi.tris.Add(capStartIndex + stripLength + i); gi.tris.Add(capStartIndex + stripLength + i); gi.tris.Add(capStartIndex + i + 1); gi.tris.Add(capStartIndex + stripLength + 1 + i); } } //Bottom cap triangles capStartIndex += lastTopStrip.capVerts.Count; stripLength = firstBottomStrip.side1.Count; for (int i = 0; i < firstBottomStrip.capVerts.Count - stripLength - 1; i++) { if ((i + 1) % stripLength != 0) { gi.tris.Add(capStartIndex + i + 1); gi.tris.Add(capStartIndex + i); gi.tris.Add(capStartIndex + stripLength + i); gi.tris.Add(capStartIndex + i + 1); gi.tris.Add(capStartIndex + stripLength + i); gi.tris.Add(capStartIndex + stripLength + 1 + i); } } //Hook Deformation if (genProps.hooks.Length > 0) { for (int i = 0; i < genProps.hooks.Length; i++) { DeformHook curHook = genProps.hooks[i]; if (curHook.enabled) { for (int j = 0; j < gi.finalVerts.Length; j++) { Vector3 curVert = gi.finalVerts[j]; float deformAmount = Mathf.Pow(Mathf.Max(0.0f, curHook.radius - Vector3.Distance(curVert, curHook.localPos)), curHook.falloff); switch (curHook.hookType) { case DeformHook.HookType.Pull: gi.finalVerts[j] += curHook.localRot * Vector3.forward * deformAmount * curHook.strength; break; case DeformHook.HookType.Twist: Vector3 newVert = curHook.localPos + curHook.localRot * (curVert - curHook.localPos); gi.finalVerts[j] += (newVert - curVert) * deformAmount * curHook.strength; break; case DeformHook.HookType.Expand: gi.finalVerts[j] += curHook.localPos + (curVert - curHook.localPos) * (1.0f + (curHook.strength - 1.0f) * deformAmount) - curVert; break; } } } } } //Set final mesh data gi.colMesh = new Mesh(); gi.colMesh.vertices = gi.finalVerts; gi.colMesh.triangles = gi.tris.ToArray(); }
//Generates a collision mesh with the indicated properties and status information public static Mesh GenerateCollider(ref GeneratorProps genProps, out ColliderFinishStatus cFin) { return(GenerationOperation(ref genProps, out cFin, false)); }
//Generates a collision mesh with the indicated properties and status holder //Preview indicates the mesh is used for previewing and not the actual collider component; will bypass polygon testing public static Mesh GenerateCollider(ref GeneratorProps genProps, out ColliderFinishStatus cFin, bool preview) { return(GenerationOperation(ref genProps, out cFin, preview)); }
//Generates a collision mesh with the indicated properties public static Mesh GenerateCollider(ref GeneratorProps genProps) { ColliderFinishStatus cFin = ColliderFinishStatus.Fail; return(GenerationOperation(ref genProps, out cFin, true)); }
//Creates a new collider with the given name and default properties public ColliderInstance(string n) { name = n; props = new GeneratorProps(Vector3.one, 0.1f); }
public GeneratorProps props; //The properties of the collider for mesh generation //Creates a new collider with default properties public ColliderInstance() { props = new GeneratorProps(Vector3.one, 0.1f); }
//Creates a new instance of collider properties by copying from an existing one public GeneratorProps(GeneratorProps gp) { CopyProperties(gp, true); }