internal override void UpdateShape(bool force = true) { Volume = CalculateVolume(); OuterDiameter = InnerDiameter / OuterToInnerFactor; GenerateMeshes(); // WriteMeshes in AbstractSoRShape typically does UpdateNodeSize, UpdateProps, RaiseModelAndColliderChanged UpdateNodeSize(TopNodeName); UpdateNodeSize(BottomNodeName); PPart.UpdateProps(); RaiseModelAndColliderChanged(); }
internal override void UpdateShape(bool force = true) { part.CoMOffset = CoMOffset; Volume = CalculateVolume(); GenerateMeshes(outerDiameter / 2, innerDiameter / 2, length, numSides); GenerateColliders(); // WriteMeshes in AbstractSoRShape typically does UpdateNodeSize, UpdateProps, RaiseModelAndColliderChanged UpdateNodeSize(TopNodeName); UpdateNodeSize(BottomNodeName); PPart.UpdateProps(); RaiseModelAndColliderChanged(); }
/// <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); lastProfile = 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.legacyTextureHandler.SidesMaterial, new Vector2(tankULength, tankVLength)); if (HighLogic.LoadedScene == GameScenes.LOADING) { m.WriteTo(PPart.SidesIconMesh); } else { 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); if (HighLogic.LoadedScene == GameScenes.LOADING) { m.WriteTo(PPart.EndsIconMesh); } else { m.WriteTo(EndsMesh); } // build the collider mesh at a lower resolution than the visual mesh. //Debug.LogWarning("Collider mesh vert=" + nColVrt + " tris=" + nColTri); // collider endcaps ProfilePoint firstColPt, lastColPt; firstColPt = pts.First(x => x.inCollider); lastColPt = pts.Last(x => x.inCollider); int nColEndVrt = firstColPt.colliderCirc.totVertexes + lastColPt.colliderCirc.totVertexes; int nColEndTri = firstColPt.colliderCirc.totVertexes - 2 + lastColPt.colliderCirc.totVertexes - 2; m = new UncheckedMesh(nColVrt + nColEndVrt, nColTri + nColEndTri); odd = false; { ProfilePoint prev = null; int off = 0, prevOff = 0; int tOff = 0; foreach (ProfilePoint pt in pts) { if (!pt.inCollider) { continue; } if (prev == null) { pt.colliderCirc.WriteEndcap(pt.dia, pt.y, false, 0, 0, m, odd); off = firstColPt.colliderCirc.totVertexes; tOff = (firstColPt.colliderCirc.totVertexes - 2); } //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; } prev.colliderCirc.WriteEndcap(prev.dia, prev.y, true, off, tOff * 3, m, odd); } if (colliderMesh == null) { colliderMesh = new Mesh(); } m.WriteTo(colliderMesh); //m.WriteTo(SidesMesh); if (colliderMesh.triangles.Length / 3 > 255) { Debug.LogWarning("Collider mesh contains " + colliderMesh.triangles.Length / 3 + " triangles. Maximum allowed triangles: 255"); } PPart.ColliderMesh = colliderMesh; PPart.UpdateProps(); RaiseModelAndColliderChanged(); }