private static void WriteToAppropriateMesh(UncheckedMesh mesh, Mesh iconMesh, Mesh normalMesh)
 {
     if (HighLogic.LoadedScene == GameScenes.LOADING)
     {
         mesh.WriteTo(iconMesh);
     }
     else
     {
         mesh.WriteTo(normalMesh);
     }
 }
        private void GenerateColliderMesh()
        {
            var mesh = new UncheckedMesh(CornerCount * 2, SideTriangles + 2 * TrianglesPerCap);

            GenerateCapVertices(mesh, -HalfHeight, 0);
            GenerateCapVertices(mesh, HalfHeight, CornerCount);
            GenerateSideTriangles(mesh, CornerCount, 1);
            GenerateCapTriangles(mesh, false, SideTriangles);
            GenerateCapTriangles(mesh, true, SideTriangles + TrianglesPerCap);

            var colliderMesh = new Mesh();

            mesh.WriteTo(colliderMesh);
            PPart.ColliderMesh = colliderMesh;
        }
Beispiel #3
0
        private static void WriteToAppropriateMesh(UncheckedMesh mesh, Mesh iconMesh, Mesh normalMesh)
        {
            var target = (HighLogic.LoadedScene == GameScenes.LOADING) ? iconMesh : normalMesh;

            mesh.WriteTo(target);
        }
Beispiel #4
0
        void UpdateFairing()
        {
            ProceduralPart ppart = PPart;

            if (useFairing && ppart != null)
            {
                ProceduralAbstractSoRShape shape = ppart.CurrentShape as ProceduralAbstractSoRShape;

                if (shape != null)
                {
                    Vector3[] topEndcapVerticies = shape.GetEndcapVerticies(true);

                    Vector3[] topInner = new Vector3[topEndcapVerticies.Length + 1];

                    topEndcapVerticies.CopyTo(topInner, 0);
                    topInner[topEndcapVerticies.Length] = topEndcapVerticies[0];

                    int vertCount = topInner.Length;

                    //foreach (Vector3 v in topInner)
                    //    Debug.Log(v);

                    Vector3[] topOuter = (Vector3[])topInner.Clone();

                    for (int i = 0; i < vertCount; ++i)
                    {
                        float r           = topInner[i].magnitude;
                        float r_          = r + fairingThickness;
                        float scaleFactor = r_ / r;
                        topOuter[i].x *= scaleFactor;
                        topOuter[i].z *= scaleFactor;
                    }

                    TextureScale.x = topOuter[0].magnitude * 2 * Mathf.PI;

                    Vector3[] sideTop    = (Vector3[])topOuter.Clone();
                    Vector3[] sideBottom = (Vector3[])sideTop.Clone();

                    Vector3[] bottomInner = (Vector3[])topInner.Clone();
                    Vector3[] bottomOuter = (Vector3[])topOuter.Clone();



                    for (int i = 0; i < vertCount; ++i)
                    {
                        if (bottomNode != null)
                        {
                            sideBottom[i].y  = bottomNode.position.y;
                            bottomInner[i].y = bottomNode.position.y;
                            bottomOuter[i].y = bottomNode.position.y;
                        }
                    }

                    TextureScale.y = Mathf.Abs(topOuter[0].y - bottomOuter[0].y);

                    Vector3[] innerSideTop    = (Vector3[])topInner.Clone();
                    Vector3[] innerSideBottom = (Vector3[])bottomInner.Clone();

                    int topInnerStart        = 0;
                    int topOuterStart        = topInnerStart + vertCount;
                    int sideTopStart         = topOuterStart + vertCount;
                    int sideBottomStart      = sideTopStart + vertCount;
                    int bottomInnerStart     = sideBottomStart + vertCount;
                    int bottomOuterStart     = bottomInnerStart + vertCount;
                    int innerSideTopStart    = bottomOuterStart + vertCount;
                    int innerSideBottomStart = innerSideTopStart + vertCount;

                    UncheckedMesh m = new UncheckedMesh(vertCount * 8, vertCount * 8 * 6);
                    //int tri = 0;
                    for (int i = 0; i < vertCount; ++i)
                    {
                        m.verticies[topInnerStart + i]        = topInner[i];
                        m.verticies[topOuterStart + i]        = topOuter[i];
                        m.verticies[sideTopStart + i]         = sideTop[i];
                        m.verticies[sideBottomStart + i]      = sideBottom[i];
                        m.verticies[bottomInnerStart + i]     = bottomInner[i];
                        m.verticies[bottomOuterStart + i]     = bottomOuter[i];
                        m.verticies[innerSideTopStart + i]    = innerSideTop[i];
                        m.verticies[innerSideBottomStart + i] = innerSideBottom[i];

                        m.normals[topInnerStart + i] = new Vector3(0.0f, 1.0f, 0.0f);
                        m.normals[topOuterStart + i] = new Vector3(0.0f, 1.0f, 0.0f);

                        m.normals[sideTopStart + i]    = m.verticies[sideTopStart + i].xz().normalized;
                        m.normals[sideBottomStart + i] = m.verticies[sideBottomStart + i].xz().normalized;

                        m.normals[bottomInnerStart + i] = new Vector3(0.0f, -1.0f, 0.0f);
                        m.normals[bottomOuterStart + i] = new Vector3(0.0f, -1.0f, 0.0f);

                        m.normals[innerSideTopStart + i]    = -m.verticies[innerSideTopStart + i].xz().normalized;
                        m.normals[innerSideBottomStart + i] = -m.verticies[innerSideBottomStart + i].xz().normalized;

                        m.uv[topInnerStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 0.0f);
                        m.uv[topOuterStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 1.0f);

                        m.uv[sideTopStart + i]    = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 1.0f);
                        m.uv[sideBottomStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 0.0f);

                        m.uv[bottomInnerStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 0.0f);
                        m.uv[bottomOuterStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 1.0f);

                        m.uv[innerSideTopStart + i]    = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 0.0f);
                        m.uv[innerSideBottomStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 1.0f);

                        m.tangents[topInnerStart + i] = Vector3.Cross(m.normals[topInnerStart + i], m.verticies[topInnerStart + i]).xz().normalized.toVec4(-1);
                        m.tangents[topOuterStart + i] = Vector3.Cross(m.normals[topOuterStart + i], m.verticies[topOuterStart + i]).xz().normalized.toVec4(-1);

                        m.tangents[sideTopStart + i]    = Vector3.Cross(m.normals[sideTopStart + i], new Vector3(0, 1, 0)).normalized.toVec4(-1);
                        m.tangents[sideBottomStart + i] = Vector3.Cross(m.normals[sideTopStart + i], new Vector3(0, 1, 0)).normalized.toVec4(-1);

                        m.tangents[bottomInnerStart + i] = Vector3.Cross(m.normals[bottomInnerStart + i], m.verticies[topInnerStart + i]).xz().normalized.toVec4(-1);
                        m.tangents[bottomOuterStart + i] = Vector3.Cross(m.normals[bottomOuterStart + i], m.verticies[topOuterStart + i]).xz().normalized.toVec4(-1);

                        m.tangents[innerSideTopStart + i]    = Vector3.Cross(m.normals[innerSideTopStart + i], new Vector3(0, 1, 0)).normalized.toVec4(-1);
                        m.tangents[innerSideBottomStart + i] = Vector3.Cross(m.normals[innerSideTopStart + i], new Vector3(0, 1, 0)).normalized.toVec4(-1);

                        //Debug.Log(i +" uv: " + Mathf.InverseLerp(0, vertCount - 1, i));
                    }

                    int triangleOffset = 0;
                    triangleOffset = ConnectRings(m, topInnerStart, topOuterStart, vertCount, 0);
                    triangleOffset = ConnectRings(m, sideTopStart, sideBottomStart, vertCount, triangleOffset);
                    triangleOffset = ConnectRings(m, bottomOuterStart, bottomInnerStart, vertCount, triangleOffset);
                    triangleOffset = ConnectRings(m, innerSideBottomStart, innerSideTopStart, vertCount, triangleOffset);

                    if (fairingMesh != null)
                    {
                        m.WriteTo(fairingMesh);
                        fairingMesh.RecalculateNormals();
                    }
                    else
                    {
                        Debug.Log("no fairing mesh");
                    }
                }
                oldTextureSet = null;
                UpdateTexture();
            }
        }
Beispiel #5
0
        /// <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();
        }
        void UpdateFairing()
        {
            Transform fairing     = part.FindModelTransform("fairing");
            Mesh      fairingMesh = fairing.GetComponent <MeshFilter>().mesh;

            if (useFairing && fairingMesh is Mesh && PPart?.CurrentShape is ProceduralAbstractSoRShape shape)
            {
                Vector3[] topEndcapVerticies = shape.GetEndcapVerticies(true);

                Vector3[] topInner = new Vector3[topEndcapVerticies.Length + 1];

                topEndcapVerticies.CopyTo(topInner, 0);
                topInner[topEndcapVerticies.Length] = topEndcapVerticies[0];

                int       vertCount = topInner.Length;
                Vector3[] topOuter  = (Vector3[])topInner.Clone();

                for (int i = 0; i < vertCount; ++i)
                {
                    float r           = topInner[i].magnitude;
                    float r_          = r + fairingThickness;
                    float scaleFactor = r_ / r;
                    topOuter[i].x *= scaleFactor;
                    topOuter[i].z *= scaleFactor;
                }

                Vector3[] sideTop    = (Vector3[])topOuter.Clone();
                Vector3[] sideBottom = (Vector3[])sideTop.Clone();

                Vector3[] bottomInner = (Vector3[])topInner.Clone();
                Vector3[] bottomOuter = (Vector3[])topOuter.Clone();

                for (int i = 0; i < vertCount; ++i)
                {
                    if (bottomNode != null)
                    {
                        sideBottom[i].y  = bottomNode.position.y;
                        bottomInner[i].y = bottomNode.position.y;
                        bottomOuter[i].y = bottomNode.position.y;
                    }
                }

                Vector3[] innerSideTop    = (Vector3[])topInner.Clone();
                Vector3[] innerSideBottom = (Vector3[])bottomInner.Clone();

                int topInnerStart        = 0;
                int topOuterStart        = topInnerStart + vertCount;
                int sideTopStart         = topOuterStart + vertCount;
                int sideBottomStart      = sideTopStart + vertCount;
                int bottomInnerStart     = sideBottomStart + vertCount;
                int bottomOuterStart     = bottomInnerStart + vertCount;
                int innerSideTopStart    = bottomOuterStart + vertCount;
                int innerSideBottomStart = innerSideTopStart + vertCount;

                UncheckedMesh m = new UncheckedMesh(vertCount * 8, vertCount * 8 * 6);
                //int tri = 0;
                for (int i = 0; i < vertCount; ++i)
                {
                    m.vertices[topInnerStart + i]        = topInner[i];
                    m.vertices[topOuterStart + i]        = topOuter[i];
                    m.vertices[sideTopStart + i]         = sideTop[i];
                    m.vertices[sideBottomStart + i]      = sideBottom[i];
                    m.vertices[bottomInnerStart + i]     = bottomInner[i];
                    m.vertices[bottomOuterStart + i]     = bottomOuter[i];
                    m.vertices[innerSideTopStart + i]    = innerSideTop[i];
                    m.vertices[innerSideBottomStart + i] = innerSideBottom[i];

                    m.normals[topInnerStart + i] = new Vector3(0.0f, 1.0f, 0.0f);
                    m.normals[topOuterStart + i] = new Vector3(0.0f, 1.0f, 0.0f);

                    m.normals[sideTopStart + i]    = m.vertices[sideTopStart + i].xz().normalized;
                    m.normals[sideBottomStart + i] = m.vertices[sideBottomStart + i].xz().normalized;

                    m.normals[bottomInnerStart + i] = new Vector3(0.0f, -1.0f, 0.0f);
                    m.normals[bottomOuterStart + i] = new Vector3(0.0f, -1.0f, 0.0f);

                    m.normals[innerSideTopStart + i]    = -m.vertices[innerSideTopStart + i].xz().normalized;
                    m.normals[innerSideBottomStart + i] = -m.vertices[innerSideBottomStart + i].xz().normalized;

                    m.uv[topInnerStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 0.0f);
                    m.uv[topOuterStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 1.0f);

                    m.uv[sideTopStart + i]    = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 1.0f);
                    m.uv[sideBottomStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 0.0f);

                    m.uv[bottomInnerStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 0.0f);
                    m.uv[bottomOuterStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 1.0f);

                    m.uv[innerSideTopStart + i]    = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 0.0f);
                    m.uv[innerSideBottomStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 1.0f);

                    m.tangents[topInnerStart + i] = Vector3.Cross(m.normals[topInnerStart + i], m.vertices[topInnerStart + i]).xz().normalized.toVec4(-1);
                    m.tangents[topOuterStart + i] = Vector3.Cross(m.normals[topOuterStart + i], m.vertices[topOuterStart + i]).xz().normalized.toVec4(-1);

                    m.tangents[sideTopStart + i]    = Vector3.Cross(m.normals[sideTopStart + i], new Vector3(0, 1, 0)).normalized.toVec4(-1);
                    m.tangents[sideBottomStart + i] = Vector3.Cross(m.normals[sideTopStart + i], new Vector3(0, 1, 0)).normalized.toVec4(-1);

                    m.tangents[bottomInnerStart + i] = Vector3.Cross(m.normals[bottomInnerStart + i], m.vertices[topInnerStart + i]).xz().normalized.toVec4(-1);
                    m.tangents[bottomOuterStart + i] = Vector3.Cross(m.normals[bottomOuterStart + i], m.vertices[topOuterStart + i]).xz().normalized.toVec4(-1);

                    m.tangents[innerSideTopStart + i]    = Vector3.Cross(m.normals[innerSideTopStart + i], new Vector3(0, 1, 0)).normalized.toVec4(-1);
                    m.tangents[innerSideBottomStart + i] = Vector3.Cross(m.normals[innerSideTopStart + i], new Vector3(0, 1, 0)).normalized.toVec4(-1);
                }

                int triangleOffset = 0;
                triangleOffset = ConnectRings(m, topInnerStart, topOuterStart, vertCount, triangleOffset);
                triangleOffset = ConnectRings(m, sideTopStart, sideBottomStart, vertCount, triangleOffset);
                triangleOffset = ConnectRings(m, bottomOuterStart, bottomInnerStart, vertCount, triangleOffset);
                _ = ConnectRings(m, innerSideBottomStart, innerSideTopStart, vertCount, triangleOffset);

                m.WriteTo(fairingMesh);
                fairingMesh.RecalculateNormals();
            }
            UpdateMass();
        }
        void UpdateFairing()
        {
            ProceduralPart ppart = PPart;

            if (useFairing && ppart != null)
            {
                ProceduralAbstractSoRShape shape = ppart.CurrentShape as ProceduralAbstractSoRShape;

                if (shape != null)
                {
                    Vector3[] topEndcapVerticies = shape.GetEndcapVerticies(true);

                    Vector3[] topInner = new Vector3[topEndcapVerticies.Length + 1];

                    topEndcapVerticies.CopyTo(topInner, 0);
                    topInner[topEndcapVerticies.Length] = topEndcapVerticies[0];

                    int vertCount = topInner.Length;

                    //foreach (Vector3 v in topInner)
                    //    Debug.Log(v);

                    Vector3[] topOuter = (Vector3[])topInner.Clone();

                    for (int i = 0; i < vertCount; ++i)
                    {
                        float r = topInner[i].magnitude;
                        float r_ = r + fairingThickness;
                        float scaleFactor = r_ / r;
                        topOuter[i].x *= scaleFactor;
                        topOuter[i].z *= scaleFactor;
                    }

                    TextureScale.x = topOuter[0].magnitude * 2 * Mathf.PI;

                    Vector3[] sideTop = (Vector3[])topOuter.Clone();
                    Vector3[] sideBottom = (Vector3[])sideTop.Clone();

                    Vector3[] bottomInner = (Vector3[])topInner.Clone();
                    Vector3[] bottomOuter = (Vector3[])topOuter.Clone();

                    for (int i = 0; i < vertCount; ++i)
                    {
                        if (bottomNode != null)
                        {
                            sideBottom[i].y = bottomNode.position.y;
                            bottomInner[i].y = bottomNode.position.y;
                            bottomOuter[i].y = bottomNode.position.y;
                        }
                    }

                    TextureScale.y = Mathf.Abs(topOuter[0].y - bottomOuter[0].y);

                    Vector3[] innerSideTop = (Vector3[])topInner.Clone();
                    Vector3[] innerSideBottom = (Vector3[])bottomInner.Clone();

                    int topInnerStart = 0;
                    int topOuterStart = topInnerStart + vertCount;
                    int sideTopStart = topOuterStart + vertCount;
                    int sideBottomStart = sideTopStart + vertCount;
                    int bottomInnerStart = sideBottomStart + vertCount;
                    int bottomOuterStart = bottomInnerStart + vertCount;
                    int innerSideTopStart = bottomOuterStart + vertCount;
                    int innerSideBottomStart = innerSideTopStart + vertCount;

                    UncheckedMesh m = new UncheckedMesh(vertCount * 8, vertCount * 8 * 6);
                    //int tri = 0;
                    for (int i = 0; i < vertCount; ++i)
                    {
                        m.verticies[topInnerStart + i] = topInner[i];
                        m.verticies[topOuterStart + i] = topOuter[i];
                        m.verticies[sideTopStart + i] = sideTop[i];
                        m.verticies[sideBottomStart + i] = sideBottom[i];
                        m.verticies[bottomInnerStart + i] = bottomInner[i];
                        m.verticies[bottomOuterStart + i] = bottomOuter[i];
                        m.verticies[innerSideTopStart + i] = innerSideTop[i];
                        m.verticies[innerSideBottomStart + i] = innerSideBottom[i];

                        m.normals[topInnerStart + i] = new Vector3(0.0f, 1.0f, 0.0f);
                        m.normals[topOuterStart + i] = new Vector3(0.0f, 1.0f, 0.0f);

                        m.normals[sideTopStart + i] = m.verticies[sideTopStart + i].xz().normalized;
                        m.normals[sideBottomStart + i] = m.verticies[sideBottomStart + i].xz().normalized;

                        m.normals[bottomInnerStart + i] = new Vector3(0.0f, -1.0f, 0.0f);
                        m.normals[bottomOuterStart + i] = new Vector3(0.0f, -1.0f, 0.0f);

                        m.normals[innerSideTopStart + i] = -m.verticies[innerSideTopStart + i].xz().normalized;
                        m.normals[innerSideBottomStart + i] = -m.verticies[innerSideBottomStart + i].xz().normalized;

                        m.uv[topInnerStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 0.0f);
                        m.uv[topOuterStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 1.0f);

                        m.uv[sideTopStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 1.0f);
                        m.uv[sideBottomStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 0.0f);

                        m.uv[bottomInnerStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 0.0f);
                        m.uv[bottomOuterStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 1.0f);

                        m.uv[innerSideTopStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 0.0f);
                        m.uv[innerSideBottomStart + i] = new Vector2(Mathf.InverseLerp(0, vertCount - 1, i), 1.0f);

                        m.tangents[topInnerStart + i] = Vector3.Cross(m.normals[topInnerStart + i], m.verticies[topInnerStart + i]).xz().normalized.toVec4(-1);
                        m.tangents[topOuterStart + i] = Vector3.Cross(m.normals[topOuterStart + i], m.verticies[topOuterStart + i]).xz().normalized.toVec4(-1);

                        m.tangents[sideTopStart + i] = Vector3.Cross(m.normals[sideTopStart + i], new Vector3(0, 1, 0)).normalized.toVec4(-1);
                        m.tangents[sideBottomStart + i] = Vector3.Cross(m.normals[sideTopStart + i], new Vector3(0, 1, 0)).normalized.toVec4(-1);

                        m.tangents[bottomInnerStart + i] = Vector3.Cross(m.normals[bottomInnerStart + i], m.verticies[topInnerStart + i]).xz().normalized.toVec4(-1);
                        m.tangents[bottomOuterStart + i] = Vector3.Cross(m.normals[bottomOuterStart + i], m.verticies[topOuterStart + i]).xz().normalized.toVec4(-1);

                        m.tangents[innerSideTopStart + i] = Vector3.Cross(m.normals[innerSideTopStart + i], new Vector3(0, 1, 0)).normalized.toVec4(-1);
                        m.tangents[innerSideBottomStart + i] = Vector3.Cross(m.normals[innerSideTopStart + i], new Vector3(0, 1, 0)).normalized.toVec4(-1);

                        //Debug.Log(i +" uv: " + Mathf.InverseLerp(0, vertCount - 1, i));

                    }

                    int triangleOffset = 0;
                    triangleOffset = ConnectRings(m, topInnerStart, topOuterStart, vertCount, 0);
                    triangleOffset = ConnectRings(m, sideTopStart, sideBottomStart, vertCount, triangleOffset);
                    triangleOffset = ConnectRings(m, bottomOuterStart, bottomInnerStart, vertCount, triangleOffset);
                    triangleOffset = ConnectRings(m, innerSideBottomStart, innerSideTopStart, vertCount, triangleOffset);

                    if (fairingMesh != null)
                    {
                        m.WriteTo(fairingMesh);
                        fairingMesh.RecalculateNormals();

                    }
                    else
                        Debug.Log("no fairing mesh");

                }
                oldTextureSet = null;
                UpdateTexture();
            }
        }
        /// <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 tankULength = 0;
            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
            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();
        }