//Alternate mirroring function where the getter is a reference to an actual corner instead of a corner location identifier void MirrorCorner(ColliderCorner getter, ColliderCorner.CornerId setter, int xFactor, int yFactor, int zFactor) { ColliderCorner setCorner = GetCornerAtLocation(setter); setCorner.localPos.Set(xFactor * getter.localPos.x, yFactor * getter.localPos.y, zFactor * getter.localPos.z); setCorner.axleRadii = getter.axleRadii; setCorner.radiusOffsets = getter.radiusOffsets; }
} //Empty constructor //Creates a new collider corner by copying an existing one public ColliderCorner(ColliderCorner cc) { cornerLocation = cc.cornerLocation; normalizedCornerLocation = cc.normalizedCornerLocation; localPos = cc.localPos; axleRadii = cc.axleRadii; radiusOffsets = cc.radiusOffsets; }
//Sets corner positions based on box properties, called by the editor window after editing box properties public void SetCornerPositionsFromBox() { //Corner positioning based on boxSize and boxCenter if (cornerPositionMode == CornerPositionEditMode.BoxCenter) { for (int i = 0; i < corners.Length; i++) { corners[i].localPos = Vector3.Scale(corners[i].normalizedCornerLocation, boxSize) * 0.5f + boxOffset; } } //Corner positioning based on boxSidesPos and boxSidesNeg else if (cornerPositionMode == CornerPositionEditMode.BoxSides) { for (int i = 0; i < corners.Length; i++) { ColliderCorner curCorner = corners[i]; switch (curCorner.cornerLocation) { case ColliderCorner.CornerId.FrontTopRight: curCorner.localPos = boxSidesPos; break; case ColliderCorner.CornerId.FrontTopLeft: curCorner.localPos = new Vector3(boxSidesNeg.x, boxSidesPos.y, boxSidesPos.z); break; case ColliderCorner.CornerId.FrontBottomRight: curCorner.localPos = new Vector3(boxSidesPos.x, boxSidesNeg.y, boxSidesPos.z); break; case ColliderCorner.CornerId.FrontBottomLeft: curCorner.localPos = new Vector3(boxSidesNeg.x, boxSidesNeg.y, boxSidesPos.z); break; case ColliderCorner.CornerId.BackTopRight: curCorner.localPos = new Vector3(boxSidesPos.x, boxSidesPos.y, boxSidesNeg.z); break; case ColliderCorner.CornerId.BackTopLeft: curCorner.localPos = new Vector3(boxSidesNeg.x, boxSidesPos.y, boxSidesNeg.z); break; case ColliderCorner.CornerId.BackBottomRight: curCorner.localPos = new Vector3(boxSidesPos.x, boxSidesNeg.y, boxSidesNeg.z); break; case ColliderCorner.CornerId.BackBottomLeft: curCorner.localPos = boxSidesNeg; break; } } } }
//Resets the corners to form a basic box shape public void ResetCorners() { boxSize = Vector3.one; boxOffset = Vector3.zero; boxSidesPos = Vector3.one * 0.5f; boxSidesNeg = Vector3.one * -0.5f; for (int i = 0; i < corners.Length; i++) { ColliderCorner curCorner = corners[i]; curCorner.localPos = Vector3.Scale(curCorner.normalizedCornerLocation, boxSize) * 0.5f + boxOffset; curCorner.ResetRadii(); } }
//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]); } }
} //Empty constructor //Constructor with properties to use public VertexStrip(VertexStripProps setProps) { props = setProps; Vector3 center = Vector3.zero; //Center of each corner Vector3 nextCenter = Vector3.zero; //Center of subsequent corner Vector3 newVertex = Vector3.zero; //Current vertex being added Vector3 nextVertex = Vector3.zero; //Next vertex along strip float increment = 0.0f; //Angle between vertices in each corner float angle = 0.0f; //Current angle of the vertex in a corner int iteration = 0; //Current being generated float radiusScale = 1.0f; //Scale of each corner float sqrtTwo = Mathf.Sqrt(2.0f); //Constant used to avoid repeat calculations //Looping through each corner while (iteration < 4) { //Vertex angle is incremented based on corner detail increment = props.cornerDetails[iteration] > 0 ? (Mathf.PI * 0.5f) / props.cornerDetails[iteration] : Mathf.PI * 0.25f; angle = Mathf.PI * 0.5f * iteration;//Starting angle in a corner int curCornerIndex = (props.isTop ? iteration + 4 : iteration); ColliderCorner curCorner = props.corners[curCornerIndex]; ColliderCorner nextCorner = props.corners[(iteration == 3 ? (props.isTop ? 4 : 0) : curCornerIndex + 1)]; center = curCorner.localPos + curCorner.axleRadii.y * (props.isTop ? Vector3.up : Vector3.down) * (props.flat ? 1.0f : props.cornerProgress); nextCenter = nextCorner.localPos + nextCorner.axleRadii.y * (props.isTop ? Vector3.up : Vector3.down) * (props.flat ? 1.0f : props.cornerProgress); radiusScale = Mathf.Sqrt(1.0f - Mathf.Pow(Mathf.Clamp01(props.cornerProgress), 2.0f));//Implicit formula of unit circle solved for y above x-axis, x^2 + y^2 = 1 => y = sqrt(1 - x^2) Vector3 curRadiusOffset = curCorner.GetOffset(); Vector3 nextRadiusOffset = nextCorner.GetOffset(); if (props.cornerDetails[iteration] == 0) { //If corners are right angles angle += increment; radiusScale *= sqrtTwo;//This is to keep the collider the same size when the corner detail is zero. Pythagorean Theorem: 1 + 1 = c^2 => sqrt(2) = c newVertex = center - curRadiusOffset + new Vector3(curCorner.axleRadii.x * radiusScale * Mathf.Cos(angle), 0.0f, curCorner.axleRadii.z * radiusScale * Mathf.Sin(angle)); verts.Add(newVertex); nextVertex = nextCenter - nextRadiusOffset + new Vector3(nextCorner.axleRadii.x * radiusScale * Mathf.Cos(angle + increment * 2.0f), 0.0f, nextCorner.axleRadii.z * radiusScale * Mathf.Sin(angle + increment * 2.0f)); } else { //If corners are round for (int i = 0; i < props.cornerDetails[iteration] + 1; i++) { newVertex = center - curRadiusOffset + new Vector3(curCorner.axleRadii.x * radiusScale * Mathf.Cos(angle), 0.0f, curCorner.axleRadii.z * radiusScale * Mathf.Sin(angle)); verts.Add(newVertex); angle += increment; } nextVertex = nextCenter - nextRadiusOffset + new Vector3(nextCorner.axleRadii.x * radiusScale * Mathf.Cos(Mathf.PI * (iteration + 1) * 0.5f), 0.0f, nextCorner.axleRadii.z * radiusScale * Mathf.Sin(Mathf.PI * (iteration + 1) * 0.5f)); } if (props.XYDetail > 0 && (iteration == 1 || iteration == 3)) { //Adding detail along sides of strip orthogonal to the X-Y plane int curDetail = 1; while (curDetail < props.XYDetail + 1) { float progress = ((curDetail * 1.0f) / ((props.XYDetail + 1) * 1.0f)); float smoothPower = Mathf.Pow(progress, 2.0f - progress * 2.0f); verts.Add(Vector3.Lerp(Vector3.Lerp(newVertex, nextVertex, progress), new Vector3(Mathf.Lerp(newVertex.x, nextVertex.x, smoothPower), Mathf.Lerp(newVertex.y, nextVertex.y, smoothPower), Mathf.Lerp(newVertex.z, nextVertex.z, progress)), props.detailSmoothness)); curDetail++; } } if (props.YZDetail > 0 && (iteration == 0 || iteration == 2)) { //Adding detail along sides of strip orthogonal to the Y-Z plane int curDetail = 1; while (curDetail < props.YZDetail + 1) { float progress = ((curDetail * 1.0f) / ((props.YZDetail + 1) * 1.0f)); float smoothPower = Mathf.Pow(progress, 2.0f - progress * 2.0f); verts.Add(Vector3.Lerp(Vector3.Lerp(newVertex, nextVertex, progress), new Vector3(Mathf.Lerp(newVertex.x, nextVertex.x, progress), Mathf.Lerp(newVertex.y, nextVertex.y, smoothPower), Mathf.Lerp(newVertex.z, nextVertex.z, smoothPower)), props.detailSmoothness)); curDetail++; } } iteration++; } verts.Add(verts[0]);//Extra copy of first vertex to close the strip }
//Function for flipping corners //cids must be a list of corner ID pairs where each pair represents corners being swapped void FlipOperation(ColliderCorner.CornerId[] cids, ref DeformHook[] mHooks, int xFactor, int yFactor, int zFactor) { if (cids.Length != 8) { Debug.LogError("Mirror operation failed, corner input array must contain 8 corners (4 pairs of corners being mirrored)."); return; } //Temporary corners for swapping ColliderCorner[] tempCorners = new ColliderCorner[4] { new ColliderCorner(GetCornerAtLocation(cids[1])), new ColliderCorner(GetCornerAtLocation(cids[3])), new ColliderCorner(GetCornerAtLocation(cids[5])), new ColliderCorner(GetCornerAtLocation(cids[7])) }; //First part of flipping by mirroring MirrorCorner(cids[0], cids[1], xFactor, yFactor, zFactor); MirrorCorner(cids[2], cids[3], xFactor, yFactor, zFactor); MirrorCorner(cids[4], cids[5], xFactor, yFactor, zFactor); MirrorCorner(cids[6], cids[7], xFactor, yFactor, zFactor); //Second part of flipping by mirroring temporary corners MirrorCorner(tempCorners[0], cids[0], xFactor, yFactor, zFactor); MirrorCorner(tempCorners[1], cids[2], xFactor, yFactor, zFactor); MirrorCorner(tempCorners[2], cids[4], xFactor, yFactor, zFactor); MirrorCorner(tempCorners[3], cids[6], xFactor, yFactor, zFactor); //Flipping hooks for (int i = 0; i < mHooks.Length; i++) { DeformHook curHook = mHooks[i]; curHook.localPos.Set(xFactor * curHook.localPos.x, yFactor * curHook.localPos.y, zFactor * curHook.localPos.z); Vector3 forwardDir = curHook.localRot * Vector3.forward; Vector3 upDir = curHook.localRot * Vector3.up; if (curHook.hookType != DeformHook.HookType.Twist) { //Flipping rules for pull and expand hook types Vector3 flippedForwardDir = new Vector3(xFactor * forwardDir.x, yFactor * forwardDir.y, zFactor * forwardDir.z); Vector3 flippedUpDir = new Vector3(xFactor * upDir.x, yFactor * upDir.y, zFactor * upDir.z); curHook.localRot = Quaternion.LookRotation(flippedForwardDir, flippedUpDir); } else { //Special rules for flipping twist hook types if (xFactor == -1) { Vector3 flippedForwardDir = new Vector3(xFactor * forwardDir.x, yFactor * forwardDir.y, zFactor * forwardDir.z); Vector3 flippedUpDir = new Vector3(xFactor * upDir.x, yFactor * upDir.y, zFactor * upDir.z); curHook.localRot = Quaternion.LookRotation(flippedForwardDir, flippedUpDir); } else if (yFactor == -1) { Vector3 flippedForwardDir = new Vector3(xFactor * forwardDir.x, yFactor * forwardDir.y, zFactor * forwardDir.z); Vector3 flippedUpDir = new Vector3(yFactor * upDir.x, upDir.y, yFactor * upDir.z); curHook.localRot = Quaternion.LookRotation(flippedForwardDir, flippedUpDir); } else if (zFactor == -1) { Vector3 flippedForwardDir = new Vector3(zFactor * forwardDir.x, zFactor * forwardDir.y, forwardDir.z); Vector3 flippedUpDir = new Vector3(zFactor * upDir.x, yFactor * upDir.y, zFactor * upDir.z); curHook.localRot = Quaternion.LookRotation(flippedForwardDir, flippedUpDir); } } curHook.localEulerRot = curHook.localRot.eulerAngles; } }