/// <summary> /// ミッキー型の花火用Meshを作成 /// </summary> /// <returns>The micky mesh.</returns> private Mesh CreateMickyMesh() { Mesh mesh = new Mesh(); // 構造体の初期化 CirclePoints cpoints = new CirclePoints(); cpoints.vertextList = new List <Vector3>(); cpoints.indexList = new List <int>(); // 頭部 cpoints = CreateCirclePoints(new Vector3(0, 0, 0), 100, cpoints); // 左耳 cpoints = CreateCirclePoints(new Vector3(100, 100, 0), 70, cpoints); // 右耳 cpoints = CreateCirclePoints(new Vector3(-100, 100, 0), 70, cpoints); // Meshの作成 mesh.RecalculateNormals(); // 法線方向を(0,0,1) 固定にしない mesh.SetVertices(cpoints.vertextList); //meshに頂点群をセット mesh.SetIndices(cpoints.indexList.ToArray(), MeshTopology.Triangles, 0); //メッシュにどの頂点の順番で面を作るかセット return(mesh); }
private ProfilePoint CreatePoint(float t, ref int colliderTri) { // ReSharper disable once InconsistentNaming // B(t) = (1-t)^3 p0 + t(1-t)^2 p1 + t^2(1-t) p2 + t^3 p3 Vector2 Bt = B(t); // ReSharper disable once InconsistentNaming // B'(t) = (1-t)^2 (p1-p0) + t(1-t) (p2-p1) + t^2 (p3-p2) Vector2 Btdt = Bdt(t); // normalized perpendicular to tangent (derivative) Vector2 norm = new Vector2(Btdt.y, -Btdt.x / 2f).normalized; // Count the number of triangles CirclePoints colliderCirc = CirclePoints.ForDiameter(Bt.x, MaxCircleError * 4f, 4, 16); colliderTri += (colliderCirc.totVertexes + 1) * 2; //Debug.LogWarning(string.Format("Creating profile point t={0:F3} coord=({1:F3}, {2:F3}) normal=({3:F3}, {4:F3})", t, Bt.x, Bt.y, norm.x, norm.y)); // We can have a maxium of 255 triangles in the collider. Will leave a bit of breathing room at the top. return(colliderTri <= 220 ? new ProfilePoint(Bt.x, Bt.y, t, norm, colliderCirc: colliderCirc) : new ProfilePoint(Bt.x, Bt.y, t, norm, inCollider: false)); }
internal override void UpdateShape(bool forceUpdate = true) { part.CoMOffset = CoMOffset; Volume = CalculateVolume(); LinkedList <ProfilePoint> points = new LinkedList <ProfilePoint>(); if (fillet == 0) { // Reduces down to a cylinder part. points.AddLast(new ProfilePoint(diameter, -0.5f * length, 0f, new Vector2(1, 0))); points.AddLast(new ProfilePoint(diameter, 0.5f * length, 1f, new Vector2(1, 0))); } else { float bodyLength = length - fillet; float endDiameter = diameter - fillet; float bodyDiameter = diameter; float filletLength = Mathf.PI * fillet * 0.5f; float totLength = filletLength + bodyLength; float s1 = filletLength * 0.5f / totLength; CirclePoints cp = CirclePoints.ForDiameter(fillet, MaxCircleError, MinCircleVertexes); // We need to be careful with the number of points so we don't blow the 255 point budget for colliders CirclePoints collCp = CirclePoints.ForDiameter(fillet, MaxCircleError, 0, 12); CirclePoints collEnds = CirclePoints.ForDiameter(endDiameter, MaxCircleError * 4f, 4, 12); CirclePoints collBody = CirclePoints.ForDiameter(bodyDiameter, MaxCircleError * 4f, 4, 16); points.AddLast(new ProfilePoint(endDiameter, -0.5f * length, 0f, new Vector2(0, -1), colliderCirc: collEnds)); foreach (Vector3 xzu in cp.PointsXZU(0.5f, 0.75f)) { points.AddLast(new ProfilePoint(endDiameter + fillet * xzu.x, -0.5f * (bodyLength - fillet * xzu.y), s1 * Mathf.InverseLerp(0.5f, 0.75f, xzu[2]), xzu, inCollider: false)); } foreach (Vector3 xzu in collCp.PointsXZU(0.5f, 0.75f)) { points.AddLast(new ProfilePoint(endDiameter + fillet * xzu.x, -0.5f * (bodyLength - fillet * xzu.y), s1 * Mathf.InverseLerp(0.5f, 0.75f, xzu[2]), xzu, inRender: false, colliderCirc: collEnds)); } points.AddLast(new ProfilePoint(bodyDiameter, -0.5f * bodyLength, s1, new Vector2(1, 0), colliderCirc: collBody)); if (fillet < length) { points.AddLast(new ProfilePoint(bodyDiameter, 0.5f * bodyLength, 1f - s1, new Vector2(1, 0), colliderCirc: collBody)); } foreach (Vector3 xzu in cp.PointsXZU(0.75f, 1)) { points.AddLast(new ProfilePoint(endDiameter + fillet * xzu.x, 0.5f * (bodyLength + fillet * xzu.y), 1f - s1 * Mathf.InverseLerp(1f, 0.75f, xzu[2]), xzu, inCollider: false)); } foreach (Vector3 xzu in collCp.PointsXZU(0.75f, 1)) { points.AddLast(new ProfilePoint(endDiameter + fillet * xzu.x, 0.5f * (bodyLength + fillet * xzu.y), 1f - s1 * Mathf.InverseLerp(1f, 0.75f, xzu[2]), xzu, inRender: false, colliderCirc: collEnds)); } points.AddLast(new ProfilePoint(endDiameter, 0.5f * length, 1f, new Vector2(0, 1), colliderCirc: collEnds)); } WriteMeshes(points); }
public ProfilePoint(float dia, float y, float v, Vector2 norm, bool inRender = true, bool inCollider = true, CirclePoints circ = null, CirclePoints colliderCirc = null) { this.dia = dia; this.y = y; this.v = v; this.norm = norm; this.inRender = inRender; this.inCollider = inCollider; this.circ = inRender ? (circ ?? CirclePoints.ForDiameter(dia, MaxCircleError, MinCircleVertexes)) : null; this.colliderCirc = inCollider ? (colliderCirc ?? this.circ ?? CirclePoints.ForDiameter(dia, MaxCircleError, MinCircleVertexes)) : null; }
public static CirclePoints ForPoints(int vertexes) { int idx = vertexes / 4 - 1; if (idx >= circlePoints.Count) { CirclePoints prev = circlePoints.Last(); do { circlePoints.Add(prev = new CirclePoints(prev.subdivCount + 1)); }while (prev.totVertexes <= vertexes); } return(circlePoints[idx]); }
/// <summary> /// ハート形のMeshを返す /// </summary> /// <returns>The circumference mesh.</returns> private Mesh CreateHeartMesh(Vector3 theOrigin, float CircleSize, CirclePoints cpoints) { Mesh mesh = new Mesh(); // 構造体の初期化 cpoints.vertextList = new List <Vector3>(); cpoints.indexList = new List <int>(); cpoints = CreateHeartPoints(theOrigin, CircleSize, cpoints); // Meshの作成 mesh.RecalculateNormals(); // 法線方向を(0,0,1) 固定にしない mesh.SetVertices(cpoints.vertextList); //meshに頂点群をセット mesh.SetIndices(cpoints.indexList.ToArray(), MeshTopology.Triangles, 0); //メッシュにどの頂点の順番で面を作るかセット return(mesh); }
/// <summary> /// 円を近似する三角形の頂点の座標と、組み合わせを返す /// </summary> /// 計算方法は、ピザ(三角形)を一切れずつ集めて一枚の丸いピザにするイメージ /// <returns>The circle.</returns> /// <param name="theOrigin">The origin.</param> /// <param name="CircleSize">Circle size.</param> /// <param name="cpoints">Cpoints.</param> private CirclePoints CreateCirclePoints(Vector3 theOrigin, int CircleSize, CirclePoints cpoints) { var unit_angle = 5; var triangle_num = 360 / unit_angle; // 原点 cpoints.vertextList.Add(theOrigin); // 近似のため、円上の座標を取得 for (int i = 0; i < triangle_num; i++) { var sin = Math.Sin(unit_angle * i * (Math.PI / 180)); var cos = Math.Cos(unit_angle * i * (Math.PI / 180)); cpoints.vertextList.Add( new Vector3((float)cos * CircleSize + theOrigin.x, (float)sin * CircleSize + theOrigin.y, theOrigin.z)); } // 原点の番号を算出 int initTriangleNumSize = cpoints.indexList.Count / 3 + cpoints.indexList.Count / 3 / triangle_num; // 座標と三角形の頂点を対応させる for (int i = 0; i < triangle_num; i++) { // 第0頂点(原点) cpoints.indexList.Add(initTriangleNumSize); // 第1頂点 cpoints.indexList.Add(i + 1 + initTriangleNumSize); // 第2頂点 if (i * 3 + 2 < triangle_num * 3 - 1) { cpoints.indexList.Add(i + 2 + initTriangleNumSize); } else { // 最後の三角形は最初の頂点に戻る cpoints.indexList.Add(1 + initTriangleNumSize); } } return(cpoints); }
public static CirclePoints ForDiameter(float diameter, float maxError, int minVertexes, int maxVertexes = int.MaxValue) { int idx = circlePoints.FindIndex(v => (v.totVertexes >= minVertexes) && (v.maxError * diameter * 2f) <= maxError); switch (idx) { case 0: return(circlePoints[0]); case -1: CirclePoints prev; if (circlePoints.Count == 0) { circlePoints.Add(prev = new CirclePoints(0)); } else { prev = circlePoints.Last(); } while (prev.totVertexes <= minVertexes) { circlePoints.Add(prev = new CirclePoints(prev.subdivCount + 1)); } while (true) { CirclePoints nxt = new CirclePoints(prev.subdivCount + 1); circlePoints.Add(nxt); if (nxt.totVertexes >= maxVertexes || nxt.maxError * diameter * 2 < maxError) { return(prev); } prev = nxt; } throw new InvalidProgramException(); default: return(circlePoints[Math.Min(idx - 1, maxVertexes / 4 - 1)]); } }
public void Fill(JsonCheckStatusResponse data, ESymbol mySymbol, bool isMyTurn) { foreach (var p in data.coordinates) { PointData pd = new PointData() { X = p.x, Y = p.y }; if (p.playerId == data.playerCircleId) { CirclePoints.Add(pd); } if (p.playerId == data.playerCrossId) { CrossPoints.Add(pd); } } MySymbol = mySymbol; IsMyTurn = isMyTurn; }
/// <summary> /// ハート型輪郭線の座標を返す /// </summary> /// <returns>The circumference points.</returns> /// <param name="theOrigin">The origin.</param> /// <param name="CircleSize">Circle size.</param> /// <param name="cpoints">Cpoints.</param> private CirclePoints CreateHeartPoints(Vector3 theOrigin, float CircleSize, CirclePoints cpoints) { double increment = 0.0005; var triangle_num = Math.PI * 2 / increment; for (double t = -Math.PI / 2; t < 3 * Math.PI / 2; t += increment) { var x = 16 * Math.Sin(t) * Math.Sin(t) * Math.Sin(t); var y = 13 * Math.Cos(t) - 5 * Math.Cos(2 * t) - 2 * Math.Cos(3 * t) - Math.Cos(4 * t); cpoints.vertextList.Add( new Vector3((float)x * CircleSize, (float)y * CircleSize, 0) + theOrigin ); } // 座標と三角形の頂点を対応させる for (int i = 0; i < triangle_num; i++) { cpoints.indexList.Add(i); } return(cpoints); }
public static CirclePoints ForDiameter(float diameter, float maxError, int minVertexes, int maxVertexes = int.MaxValue) { int idx = circlePoints.FindIndex(v => (v.totVertexes >= minVertexes) && (v.maxError * diameter * 2f) <= maxError); switch (idx) { case 0: return circlePoints[0]; case -1: CirclePoints prev; if (circlePoints.Count == 0) circlePoints.Add(prev = new CirclePoints(0)); else prev = circlePoints.Last(); while (prev.totVertexes <= minVertexes) circlePoints.Add(prev = new CirclePoints(prev.subdivCount + 1)); while (true) { CirclePoints nxt = new CirclePoints(prev.subdivCount + 1); circlePoints.Add(nxt); if (nxt.totVertexes >= maxVertexes || nxt.maxError * diameter * 2 < maxError) return prev; prev = nxt; } throw new InvalidProgramException(); default: return circlePoints[Math.Min(idx - 1, maxVertexes / 4 - 1)]; } }
/// <summary> /// Generate the compShape from profile points from pt to bottom. /// Note that this list will have extra interpolated points added if the change in radius is high to avoid /// texture stretching. /// </summary> /// <param name="pts"></param> protected void WriteMeshes(LinkedList <ProfilePoint> pts) { if (pts == null || pts.Count < 2) { return; } // update nodes UpdateNodeSize(pts.First(), bottomNodeName); UpdateNodeSize(pts.Last(), topNodeName); // Move attachments first, before subdividing MoveAttachments(pts); // Horizontal profile point subdivision SubdivHorizontal(pts); // Tank stats float tankVLength = 0; int nVrt = 0; int nTri = 0; int nColVrt = 0; int nColTri = 0; bool customCollider = false; ProfilePoint first = pts.First.Value; ProfilePoint last = pts.Last.Value; if (!first.inCollider || !last.inCollider) { throw new InvalidOperationException("First and last profile points must be used in the collider"); } foreach (ProfilePoint pt in pts) { customCollider = customCollider || pt.CustomCollider; if (pt.inRender) { nVrt += pt.circ.totVertexes + 1; // one for above, one for below nTri += 2 * pt.circ.totVertexes; } if (pt.inCollider) { nColVrt += pt.colliderCirc.totVertexes + 1; nColTri += 2 * pt.colliderCirc.totVertexes; } } // Have double counted for the first and last circles. nTri -= first.circ.totVertexes + last.circ.totVertexes; nColTri -= first.colliderCirc.totVertexes + last.colliderCirc.totVertexes; UncheckedMesh m = new UncheckedMesh(nVrt, nTri); float sumDiameters = 0; //Debug.LogWarning("Display mesh vert=" + nVrt + " tris=" + nTri); bool odd = false; { ProfilePoint prev = null; int off = 0, prevOff = 0; int tOff = 0; foreach (ProfilePoint pt in pts) { if (!pt.inRender) { continue; } pt.circ.WriteVertexes(diameter: pt.dia, y: pt.y, v: pt.v, norm: pt.norm, off: off, m: m, odd: odd); if (prev != null) { CirclePoints.WriteTriangles(prev.circ, prevOff, pt.circ, off, m.triangles, tOff * 3, !odd); tOff += prev.circ.totVertexes + pt.circ.totVertexes; // Deprecated: Volume has been moved up to callers. This way we can use the idealized rather than aproximate volume // Work out the area of the truncated cone // integral_y1^y2 pi R(y)^2 dy where R(y) = ((r2-r1)(y-y1))/(r2-r1) + r1 Integrate circles along a line // integral_y1^y2 pi ( ((r2-r1)(y-y1))/(r2-r1) + r1) ^2 dy Substituted in formula. // == -1/3 pi (y1-y2) (r1^2+r1*r2+r2^2) Do the calculus // == -1/3 pi (y1-y2) (d1^2/4+d1*d2/4+d2^2/4) r = d/2 // == -1/12 pi (y1-y2) (d1^2+d1*d2+d2^2) Take out the factor //volume += (Mathf.PI * (pt.y - prev.y) * (prev.dia * prev.dia + prev.dia * pt.dia + pt.dia * pt.dia)) / 12f; float dy = (pt.y - prev.y); float dr = (prev.dia - pt.dia) * 0.5f; //print("dy=" + dy + " dr=" + dr + " len=" + Mathf.Sqrt(dy * dy + dr * dr).ToString("F3")); tankVLength += Mathf.Sqrt(dy * dy + dr * dr); // average diameter weighted by dy sumDiameters += (pt.dia + prev.dia) * dy; } prev = pt; prevOff = off; off += pt.circ.totVertexes + 1; odd = !odd; } } // Use the weighted average diameter across segments to set the ULength float tankULength = Mathf.PI * sumDiameters / (last.y - first.y); //print("ULength=" + tankULength + " VLength=" + tankVLength); // set the texture scale. RaiseChangeTextureScale("sides", PPart.SidesMaterial, new Vector2(tankULength, tankVLength)); m.WriteTo(SidesMesh); // The endcaps. nVrt = first.circ.totVertexes + last.circ.totVertexes; nTri = first.circ.totVertexes - 2 + last.circ.totVertexes - 2; m = new UncheckedMesh(nVrt, nTri); first.circ.WriteEndcap(first.dia, first.y, false, 0, 0, m, false); last.circ.WriteEndcap(last.dia, last.y, true, first.circ.totVertexes, (first.circ.totVertexes - 2) * 3, m, !odd); m.WriteTo(EndsMesh); // build the collider mesh at a lower resolution than the visual mesh. if (customCollider) { //Debug.LogWarning("Collider mesh vert=" + nColVrt + " tris=" + nColTri); m = new UncheckedMesh(nColVrt, nColTri); odd = false; { ProfilePoint prev = null; int off = 0, prevOff = 0; int tOff = 0; foreach (ProfilePoint pt in pts) { if (!pt.inCollider) { continue; } //Debug.LogWarning("Collider circ (" + pt.dia + ", " + pt.y + ") verts=" + pt.colliderCirc.totVertexes); pt.colliderCirc.WriteVertexes(diameter: pt.dia, y: pt.y, v: pt.v, norm: pt.norm, off: off, m: m, odd: odd); if (prev != null) { CirclePoints.WriteTriangles(prev.colliderCirc, prevOff, pt.colliderCirc, off, m.triangles, tOff * 3, !odd); tOff += prev.colliderCirc.totVertexes + pt.colliderCirc.totVertexes; } prev = pt; prevOff = off; off += pt.colliderCirc.totVertexes + 1; odd = !odd; } } if (colliderMesh == null) { colliderMesh = new Mesh(); } m.WriteTo(colliderMesh); PPart.ColliderMesh = colliderMesh; } else { PPart.ColliderMesh = SidesMesh; } RaiseModelAndColliderChanged(); }
/// <summary> /// 円周Mesh作成用の座標を返す /// </summary> /// <returns>The circumference points.</returns> /// <param name="theOrigin">The origin.</param> /// <param name="CircleSize">Circle size.</param> /// <param name="cpoints">Cpoints.</param> private CirclePoints CreateCircumferencePoints(Vector3 theOrigin, float CircleSize, CirclePoints cpoints) { var unit_angle = 1; var triangle_num = 360 / unit_angle; // 原点 //cpoints.vertextList.Add(theOrigin); // 近似のため、円上の座標を取得 for (int i = 0; i < triangle_num; i++) { var sin = Math.Sin(unit_angle * i * (Math.PI / 180)); var cos = Math.Cos(unit_angle * i * (Math.PI / 180)); cpoints.vertextList.Add( new Vector3((float)cos * CircleSize + theOrigin.x, (float)sin * CircleSize + theOrigin.y, theOrigin.z)); } // 座標と三角形の頂点を対応させる for (int i = 0; i < triangle_num; i++) { cpoints.indexList.Add(i); } return(cpoints); }
/// <summary> /// Creates a.vertexes + b.vertexes triangles to cover the surface between circle a and b. /// </summary> /// <param name="a">the first circle points</param> /// <param name="ao">offset into vertex array for a points</param> /// <param name="b">the second circle points</param> /// <param name="bo">offset into vertex array for b points</param> /// <param name="triangles">triangles array for output</param> /// <param name="to">offset into triangles array. This must be a multiple of 3</param> /// <param name="odd">Is this an odd row</param> public static void WriteTriangles(CirclePoints a, int ao, CirclePoints b, int bo, int[] triangles, int to, bool odd) { int aq = a.subdivCount + 1, bq = b.subdivCount + 1; int ai = 0, bi = 0; int ad = (odd ? 1 : 0), bd = (odd ? 0 : 1); while (ai < aq || bi < bq) { float au = (ai < aq) ? a.uCoords[ad][ai] : (a.uCoords[ad][0] + 0.25f); float bu = (bi < bq) ? b.uCoords[bd][bi] : (b.uCoords[bd][0] + 0.25f); if (au < bu) { //MonoBehaviour.print("A-tri #" + ai + " tOff=" + to); triangles[to++] = ao + ai; triangles[to++] = bo + bi; triangles[to++] = ao + ai + 1; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); triangles[to++] = ao + ai + aq; triangles[to++] = bo + bi + bq; triangles[to++] = ao + ai + 1 + aq; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); triangles[to++] = ao + ai + 2 * aq; triangles[to++] = bo + bi + 2 * bq; triangles[to++] = ao + ai + 1 + 2 * aq; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); triangles[to++] = ao + ai + 3 * aq; triangles[to++] = bo + bi + 3 * bq; triangles[to++] = ao + ai + 1 + 3 * aq; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); ++ai; } else { //MonoBehaviour.print("B-tri #" + bi + " tOff=" + to); triangles[to++] = bo + bi; triangles[to++] = bo + bi + 1; triangles[to++] = ao + ai; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); triangles[to++] = bo + bi + bq; triangles[to++] = bo + bi + 1 + bq; triangles[to++] = ao + ai + aq; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); triangles[to++] = bo + bi + 2 * bq; triangles[to++] = bo + bi + 1 + 2 * bq; triangles[to++] = ao + ai + 2 * aq; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); triangles[to++] = bo + bi + 3 * bq; triangles[to++] = bo + bi + 1 + 3 * bq; triangles[to++] = ao + ai + 3 * aq; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); ++bi; } } }
protected override void UpdateShape(bool force) { // ReSharper disable CompareOfFloatsByEqualityOperator if (!force && oldDiameter == diameter && oldLength == length && oldFillet == fillet) { return; } if (HighLogic.LoadedSceneIsFlight) { Volume = CalcVolume(); } else if (HighLogic.LoadedSceneIsEditor) { if (filletEdit == null) { filletEdit = (UI_FloatEdit)Fields["fillet"].uiControlEditor; } if (length != oldLength) { if (length < oldLength && fillet > length) { fillet = length; } float volExcess = MaxMinVolume(); if (volExcess != 0) { // Again using alpha, solve the volume equation below equation for l // v = 1/24 pi (6 d^2 l+3 (pi-4) d f^2+(10-3 pi) f^3) for l // l = (-3 (pi-4) pi d f^2+pi (3 pi-10) f^3+24 v)/(6 pi d^2) length = (-3f * (Pi - 4f) * Pi * diameter * pow(fillet, 2) + Pi * (3f * Pi - 10f) * pow(fillet, 3) + 24f * Volume) / (6f * Pi * pow(diameter, 2)); length = (float)Math.Round(length, 3); // We could iterate here with the fillet and push it back up if it's been pushed down // but it's altogether too much bother. User will just have to suck it up and not be // so darn agressive with short lengths. I mean, seriously... :) } filletEdit.maxValue = Mathf.Min(length, useEndDiameter ? PPart.diameterMax : diameter); } else if (diameter != oldDiameter) { if (useEndDiameter) { if (diameter + fillet < PPart.diameterMin) { fillet = PPart.diameterMin - diameter; } else if (diameter + fillet > PPart.diameterMax) { fillet = PPart.diameterMax - diameter; } } else { if (diameter < oldDiameter && fillet > diameter) { fillet = diameter; } } float volExcess = MaxMinVolume(); if (volExcess != 0) { // Unfortunatly diameter is not as easily isolated, but its still possible. // v = 1/24 pi (6 d^2 l+3 (pi-4) d f^2+(10-3 pi) f^3) for d // simplify d = ((-3 pi^2 f^2+12 pi f^2) ± sqrt(3 pi) sqrt(3 pi^3 f^4-24 pi^2 f^4+48 pi f^4+24 pi^2 f^3 l-80 pi f^3 l+192 l v))/(12 pi l) // d = (-3 (pi-4) pi f^2 ± sqrt(3 pi) sqrt(3 (pi-4)^2 pi f^4+8 pi (3 pi-10) f^3 l+192 l v)) / (12 pi l) float t1 = -3 * (Pi - 4f) * Pi * fillet * fillet; float t2 = sqrt(3f * Pi) * sqrt(3f * pow(Pi - 4f, 2) * Pi * pow(fillet, 4) + 8f * Pi * (3f * Pi - 10f) * pow(fillet, 3) * length + 192f * length * Volume); float de = (12f * Pi * length); // I'm pretty sure only the +ve value is required, but make the -ve possible too. diameter = (t1 + t2) / de; if (diameter < 0) { diameter = (t1 - t2) / de; } diameter = (float)Math.Round(diameter, 3); } filletEdit.maxValue = Mathf.Min(length, useEndDiameter ? PPart.diameterMax : diameter); } else if (fillet != oldFillet) { if (useEndDiameter) { // Keep diameter + fillet within range. if (diameter + fillet < PPart.diameterMin) { diameter = PPart.diameterMin - fillet; } else if (diameter + fillet > PPart.diameterMax) { diameter = PPart.diameterMax - fillet; } } // Will do an iterative process for finding the value. // The equation is far too complicated plug this into alpha and you'll see what I mean: // v = 1/24 pi (6 d^2 l+3 (pi-4) d f^2+(10-3 pi) f^3) for f float vol = CalcVolume(); float inc; if (vol < PPart.volumeMin) { Volume = PPart.volumeMin; inc = -0.001f; } else if (vol > PPart.volumeMax) { Volume = PPart.volumeMax; inc = 0.001f; } else { Volume = vol; goto goldilocks; } float lVol; float lFillet; do { lVol = vol; lFillet = fillet; fillet += inc; vol = CalcVolume(); }while (Mathf.Abs(vol - Volume) < Mathf.Abs(lVol - Volume)); fillet = (float)Math.Round(lFillet, 3); goldilocks :; } } LinkedList <ProfilePoint> points = new LinkedList <ProfilePoint>(); if (fillet == 0) { // Reduces down to a cylinder part. points.AddLast(new ProfilePoint(diameter, -0.5f * length, 0f, new Vector2(1, 0))); points.AddLast(new ProfilePoint(diameter, 0.5f * length, 1f, new Vector2(1, 0))); } else { float bodyLength = length - fillet; float endDiameter = useEndDiameter ? diameter : (diameter - fillet); float bodyDiameter = useEndDiameter ? (fillet + diameter) : diameter; float filletLength = Mathf.PI * fillet * 0.5f; float totLength = filletLength + bodyLength; float s1 = filletLength * 0.5f / totLength; CirclePoints cp = CirclePoints.ForDiameter(fillet, MaxCircleError, MinCircleVertexes); // We need to be careful with the number of points so we don't blow the 255 point budget for colliders CirclePoints collCp = CirclePoints.ForDiameter(fillet, MaxCircleError, 0, 12); CirclePoints collEnds = CirclePoints.ForDiameter(endDiameter, MaxCircleError * 4f, 4, 12); CirclePoints collBody = CirclePoints.ForDiameter(bodyDiameter, MaxCircleError * 4f, 4, 16); points.AddLast(new ProfilePoint(endDiameter, -0.5f * length, 0f, new Vector2(0, -1), colliderCirc: collEnds)); foreach (Vector3 xzu in cp.PointsXZU(0.5f, 0.75f)) { points.AddLast(new ProfilePoint(endDiameter + fillet * xzu.x, -0.5f * (bodyLength - fillet * xzu.y), s1 * Mathf.InverseLerp(0.5f, 0.75f, xzu[2]), xzu, inCollider: false)); } foreach (Vector3 xzu in collCp.PointsXZU(0.5f, 0.75f)) { points.AddLast(new ProfilePoint(endDiameter + fillet * xzu.x, -0.5f * (bodyLength - fillet * xzu.y), s1 * Mathf.InverseLerp(0.5f, 0.75f, xzu[2]), xzu, inRender: false, colliderCirc: collEnds)); } points.AddLast(new ProfilePoint(bodyDiameter, -0.5f * bodyLength, s1, new Vector2(1, 0), colliderCirc: collBody)); if (fillet < length) { points.AddLast(new ProfilePoint(bodyDiameter, 0.5f * bodyLength, 1f - s1, new Vector2(1, 0), colliderCirc: collBody)); } foreach (Vector3 xzu in cp.PointsXZU(0.75f, 1)) { points.AddLast(new ProfilePoint(endDiameter + fillet * xzu.x, 0.5f * (bodyLength + fillet * xzu.y), 1f - s1 * Mathf.InverseLerp(1f, 0.75f, xzu[2]), xzu, inCollider: false)); } foreach (Vector3 xzu in collCp.PointsXZU(0.75f, 1)) { points.AddLast(new ProfilePoint(endDiameter + fillet * xzu.x, 0.5f * (bodyLength + fillet * xzu.y), 1f - s1 * Mathf.InverseLerp(1f, 0.75f, xzu[2]), xzu, inRender: false, colliderCirc: collEnds)); } points.AddLast(new ProfilePoint(endDiameter, 0.5f * length, 1f, new Vector2(0, 1), colliderCirc: collEnds)); } WriteMeshes(points); oldDiameter = diameter; oldLength = length; oldFillet = fillet; // ReSharper restore CompareOfFloatsByEqualityOperator //RefreshPartEditorWindow(); UpdateInterops(); }
public ProfilePoint(float dia, float y, float v, Vector2 norm, bool inRender = true, bool inCollider = true, CirclePoints circ = null, CirclePoints colliderCirc = null) { this.dia = dia; this.y = y; this.v = v; this.norm = norm; this.inRender = inRender; this.inCollider = inCollider; this.circ = inRender ? (circ ?? CirclePoints.ForDiameter(dia, maxCircleError, minCircleVertexes)) : null; this.colliderCirc = inCollider ? (colliderCirc ?? this.circ ?? CirclePoints.ForDiameter(dia, maxCircleError, minCircleVertexes)) : null; }
/// <summary> /// Creates a.vertexes + b.vertexes triangles to cover the surface between circle a and b. /// </summary> /// <param name="a">the first circle points</param> /// <param name="ao">offset into vertex array for a points</param> /// <param name="b">the second circle points</param> /// <param name="bo">offset into vertex array for b points</param> /// <param name="triangles">triangles array for output</param> /// <param name="to">offset into triangles array. This must be a multiple of 3</param> public static void WriteTriangles(CirclePoints a, int ao, CirclePoints b, int bo, int[] triangles, int too, bool odd) { int to = too; int aq = a.subdivCount + 1, bq = b.subdivCount + 1; int ai = 0, bi = 0; int ad = (odd ? 1 : 0), bd = (odd ? 0 : 1); while (ai < aq || bi < bq) { float au = (ai < aq) ? a.uCoords[ad][ai] : (a.uCoords[ad][0] + 0.25f); float bu = (bi < bq) ? b.uCoords[bd][bi] : (b.uCoords[bd][0] + 0.25f); if (au < bu) { //MonoBehaviour.print("A-tri #" + ai + " tOff=" + to); triangles[to++] = ao + ai; triangles[to++] = bo + bi; triangles[to++] = ao + ai + 1; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); triangles[to++] = ao + ai + aq; triangles[to++] = bo + bi + bq; triangles[to++] = ao + ai + 1 + aq; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); triangles[to++] = ao + ai + 2 * aq; triangles[to++] = bo + bi + 2 * bq; triangles[to++] = ao + ai + 1 + 2 * aq; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); triangles[to++] = ao + ai + 3 * aq; triangles[to++] = bo + bi + 3 * bq; triangles[to++] = ao + ai + 1 + 3 * aq; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); ++ai; } else { //MonoBehaviour.print("B-tri #" + bi + " tOff=" + to); triangles[to++] = bo + bi; triangles[to++] = bo + bi + 1; triangles[to++] = ao + ai; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); triangles[to++] = bo + bi + bq; triangles[to++] = bo + bi + 1 + bq; triangles[to++] = ao + ai + aq; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); triangles[to++] = bo + bi + 2 * bq; triangles[to++] = bo + bi + 1 + 2 * bq; triangles[to++] = ao + ai + 2 * aq; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); triangles[to++] = bo + bi + 3 * bq; triangles[to++] = bo + bi + 1 + 3 * bq; triangles[to++] = ao + ai + 3 * aq; //MonoBehaviour.print(" (" + triangles[to - 3] + ", " + triangles[to - 2] + ", " + triangles[to - 1] + ") "); ++bi; } } }
public static CirclePoints ForPoints(int vertexes) { int idx = vertexes / 4 - 1; if (idx >= circlePoints.Count) { CirclePoints prev = circlePoints.Last(); do { circlePoints.Add(prev = new CirclePoints(prev.subdivCount + 1)); } while (prev.totVertexes <= vertexes); } return circlePoints[idx]; }
internal void Clear() { CirclePoints.Clear(); CrossPoints.Clear(); }
public Segment(Snake snake, Vector3 globalPosition, Vector3 globalRotation) { position = globalPosition; int offset = snake.segments.Count; // Create the new set of vertices. Vector3[] calculatedPoints = CirclePoints.getPointsOnCircle(BODY_DIAMETER, VERTEX_COUNT); Vector3[] rotatedPoints = CirclePoints.rotatePointsAroundAxis(calculatedPoints, globalRotation); Vector3[] vertices = new Vector3[VERTEX_COUNT]; Vector2[] uvs = new Vector2[VERTEX_COUNT]; //makes a ring of 8 points at the same point as the snake head for (int i = 0; i < VERTEX_COUNT; i++) { vertices[i] = globalPosition + rotatedPoints[i]; uvs[i] = new Vector2(i * (1 / VERTEX_COUNT), 0 + SEGMENT_DISTANCE * snake.segmentTicker); } snake.segmentTicker++; snake.vertices.AddRange(vertices); snake.uvs.AddRange(uvs); if (snake.segments.Count > 0) { int[] triangles = new int[VERTEX_COUNT * 6]; int vertexOffset; for (int i = 0, j = 0; i < VERTEX_COUNT; i++, j += 6) { vertexOffset = i + offset * VERTEX_COUNT; int lastVertex; if (vertexOffset + 1 == snake.vertices.Count) { lastVertex = snake.vertices.Count - VERTEX_COUNT; } else { lastVertex = vertexOffset + 1; } int finalFinalVertex; if (j + 5 == triangles.Length - 1) { finalFinalVertex = vertexOffset - (VERTEX_COUNT * 2) + 1; } else { finalFinalVertex = vertexOffset - VERTEX_COUNT + 1; } // Face 1 triangles[j] = vertexOffset - VERTEX_COUNT; triangles[j + 1] = lastVertex; triangles[j + 2] = vertexOffset; // Face 2 triangles[j + 3] = finalFinalVertex; triangles[j + 4] = lastVertex; triangles[j + 5] = vertexOffset - VERTEX_COUNT; } snake.triangles.AddRange(triangles); } /* * Debug.Log("SEGS: "+ snake.segments.Count); * Debug.Log("VERTS: "); * for (int i = 0; i < snake.vertices.Count; i++) Debug.Log("["+i+"] "+snake.vertices[i]); * Debug.Log("TRIS: "); * for (int i = 0; i < snake.triangles.Count; i++) Debug.Log("["+i+"] "+snake.triangles[i]); * Debug.Log(""); */ }