Пример #1
0
        /// <summary>
        ///     ** Experimental ** May disappear from future versions ** not recommeneded for use in applications
        ///     Construct a sculpt mesh from a 2D array of floats
        /// </summary>
        /// <param name="zMap"></param>
        /// <param name="xBegin"></param>
        /// <param name="xEnd"></param>
        /// <param name="yBegin"></param>
        /// <param name="yEnd"></param>
        /// <param name="viewerMode"></param>
        public SculptMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd, bool viewerMode)
        {
            float xStep, yStep;
            float uStep, vStep;

            int numYElements = zMap.GetLength(0);
            int numXElements = zMap.GetLength(1);

            try
            {
                xStep = (xEnd - xBegin)/(numXElements - 1);
                yStep = (yEnd - yBegin)/(numYElements - 1);

                uStep = 1.0f/(numXElements - 1);
                vStep = 1.0f/(numYElements - 1);
            }
            catch (DivideByZeroException)
            {
                return;
            }

            coords = new List<Coord>();
            faces = new List<Face>();
            normals = new List<Coord>();
            uvs = new List<UVCoord>();

            viewerFaces = new List<ViewerFace>();

            int p1, p2, p3, p4;

            int x, y;
            int xStart = 0, yStart = 0;

            for (y = yStart; y < numYElements; y++)
            {
                int rowOffset = y*numXElements;

                for (x = xStart; x < numXElements; x++)
                {
                    /*
                    *   p1-----p2
                    *   | \ f2 |
                    *   |   \  |
                    *   | f1  \|
                    *   p3-----p4
                    */

                    p4 = rowOffset + x;
                    p3 = p4 - 1;

                    p2 = p4 - numXElements;
                    p1 = p3 - numXElements;
                    Coord c = new Coord(xBegin + x*xStep, yBegin + y*yStep, zMap[y, x]);
                    this.coords.Add(c);
                    if (viewerMode)
                    {
                        this.normals.Add(new Coord());
                        this.uvs.Add(new UVCoord(uStep*x, 1.0f - vStep*y));
                    }

                    if (y > 0 && x > 0)
                    {
                        Face f1, f2;

                        if (viewerMode)
                        {
                            f1 = new Face(p1, p4, p3, p1, p4, p3) {uv1 = p1, uv2 = p4, uv3 = p3};

                            f2 = new Face(p1, p2, p4, p1, p2, p4) {uv1 = p1, uv2 = p2, uv3 = p4};
                        }
                        else
                        {
                            f1 = new Face(p1, p4, p3);
                            f2 = new Face(p1, p2, p4);
                        }

                        this.faces.Add(f1);
                        this.faces.Add(f2);
                    }
                }
            }

            if (viewerMode)
                calcVertexNormals(SculptType.plane, numXElements, numYElements);
        }
Пример #2
0
        private void _SculptMesh(List<List<Coord>> rows, SculptType sculptType, bool viewerMode, bool mirror,
                                 bool invert)
        {
            coords = new List<Coord>();
            faces = new List<Face>();
            normals = new List<Coord>();
            uvs = new List<UVCoord>();

            sculptType = (SculptType) (((int) sculptType) & 0x07);

            if (mirror)
                invert = !invert;

            viewerFaces = new List<ViewerFace>();

            int width = rows[0].Count;

            int p1, p2, p3, p4;

            int imageX, imageY;

            if (sculptType != SculptType.plane)
            {
                if (rows.Count%2 == 0)
                {
                    foreach (List<Coord> t in rows)
                        t.Add(t[0]);
                }
                else
                {
                    int lastIndex = rows[0].Count - 1;

                    foreach (List<Coord> t in rows)
                        t[0] = t[lastIndex];
                }
            }

            Coord topPole = rows[0][width/2];
            Coord bottomPole = rows[rows.Count - 1][width/2];

            if (sculptType == SculptType.sphere)
            {
                if (rows.Count%2 == 0)
                {
                    int count = rows[0].Count;
                    List<Coord> topPoleRow = new List<Coord>(count);
                    List<Coord> bottomPoleRow = new List<Coord>(count);

                    for (int i = 0; i < count; i++)
                    {
                        topPoleRow.Add(topPole);
                        bottomPoleRow.Add(bottomPole);
                    }
                    rows.Insert(0, topPoleRow);
                    rows.Add(bottomPoleRow);
                }
                else
                {
                    int count = rows[0].Count;

                    List<Coord> topPoleRow = rows[0];
                    List<Coord> bottomPoleRow = rows[rows.Count - 1];

                    for (int i = 0; i < count; i++)
                    {
                        topPoleRow[i] = topPole;
                        bottomPoleRow[i] = bottomPole;
                    }
                }
            }

            if (sculptType == SculptType.torus)
                rows.Add(rows[0]);

            int coordsDown = rows.Count;
            int coordsAcross = rows[0].Count;

            float widthUnit = 1.0f/(coordsAcross - 1);
            float heightUnit = 1.0f/(coordsDown - 1);

            for (imageY = 0; imageY < coordsDown; imageY++)
            {
                int rowOffset = imageY*coordsAcross;

                for (imageX = 0; imageX < coordsAcross; imageX++)
                {
                    /*
                    *   p1-----p2
                    *   | \ f2 |
                    *   |   \  |
                    *   | f1  \|
                    *   p3-----p4
                    */

                    p4 = rowOffset + imageX;
                    p3 = p4 - 1;

                    p2 = p4 - coordsAcross;
                    p1 = p3 - coordsAcross;

                    this.coords.Add(rows[imageY][imageX]);
                    if (viewerMode)
                    {
                        this.normals.Add(new Coord());
                        this.uvs.Add(new UVCoord(widthUnit*imageX, heightUnit*imageY));
                    }

                    if (imageY > 0 && imageX > 0)
                    {
                        Face f1, f2;

                        if (viewerMode)
                        {
                            if (invert)
                            {
                                f1 = new Face(p1, p4, p3, p1, p4, p3) {uv1 = p1, uv2 = p4, uv3 = p3};

                                f2 = new Face(p1, p2, p4, p1, p2, p4) {uv1 = p1, uv2 = p2, uv3 = p4};
                            }
                            else
                            {
                                f1 = new Face(p1, p3, p4, p1, p3, p4) {uv1 = p1, uv2 = p3, uv3 = p4};

                                f2 = new Face(p1, p4, p2, p1, p4, p2) {uv1 = p1, uv2 = p4, uv3 = p2};
                            }
                        }
                        else
                        {
                            if (invert)
                            {
                                f1 = new Face(p1, p4, p3);
                                f2 = new Face(p1, p2, p4);
                            }
                            else
                            {
                                f1 = new Face(p1, p3, p4);
                                f2 = new Face(p1, p4, p2);
                            }
                        }

                        this.faces.Add(f1);
                        this.faces.Add(f2);
                    }
                }
            }

            if (viewerMode)
                calcVertexNormals(sculptType, coordsAcross, coordsDown);
        }
Пример #3
0
 private Coord SurfaceNormal(Face face)
 {
     return SurfaceNormal(coords[face.v1], coords[face.v2], coords[face.v3]);
 }
Пример #4
0
        internal Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides,
            bool createFaces, bool calcVertexNormals)
        {
            this.calcVertexNormals = calcVertexNormals;
            coords = new List<Coord>();
            faces = new List<Face>();
            vertexNormals = new List<Coord>();
            us = new List<float>();
            faceUVs = new List<UVCoord>();
            faceNumbers = new List<int>();

            Coord center = new Coord(0.0f, 0.0f, 0.0f);

            List<Coord> hollowCoords = new List<Coord>();
            List<Coord> hollowNormals = new List<Coord>();
            List<float> hollowUs = new List<float>();

            if (calcVertexNormals)
            {
                outerCoordIndices = new List<int>();
                hollowCoordIndices = new List<int>();
                cut1CoordIndices = new List<int>();
                cut2CoordIndices = new List<int>();
            }

            bool hasHollow = (hollow > 0.0f);

            bool hasProfileCut = (profileStart > 0.0f || profileEnd < 1.0f);

            AngleList angles = new AngleList();
            AngleList hollowAngles = new AngleList();

            float xScale = 0.5f;
            float yScale = 0.5f;
            if (sides == 4) // corners of a square are sqrt(2) from center
            {
                xScale = 0.707f;
                yScale = 0.707f;
            }

            float startAngle = profileStart*twoPi;
            float stopAngle = profileEnd*twoPi;

            try
            {
                angles.makeAngles(sides, startAngle, stopAngle);
            }
            catch (Exception ex)
            {
                errorMessage = "makeAngles failed: Exception: " + ex
                               + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() +
                               " stopAngle: " + stopAngle.ToString();

                return;
            }

            numOuterVerts = angles.angles.Count;

            // flag to create as few triangles as possible for 3 or 4 side profile
            bool simpleFace = (sides < 5 && !hasHollow && !hasProfileCut);

            if (hasHollow)
            {
                if (sides == hollowSides)
                    hollowAngles = angles;
                else
                {
                    try
                    {
                        hollowAngles.makeAngles(hollowSides, startAngle, stopAngle);
                    }
                    catch (Exception ex)
                    {
                        errorMessage = "makeAngles failed: Exception: " + ex
                                       + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() +
                                       " stopAngle: " + stopAngle.ToString();

                        return;
                    }
                }
                numHollowVerts = hollowAngles.angles.Count;
            }
            else if (!simpleFace)
            {
                coords.Add(center);
                //hasCenter = true;
                if (this.calcVertexNormals)
                    vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f));
                us.Add(0.0f);
            }

            const float z = 0.0f;

            Angle angle;
            Coord newVert = new Coord();
            if (hasHollow && hollowSides != sides)
            {
                int numHollowAngles = hollowAngles.angles.Count;
                for (int i = 0; i < numHollowAngles; i++)
                {
                    angle = hollowAngles.angles[i];
                    newVert.X = hollow*xScale*angle.X;
                    newVert.Y = hollow*yScale*angle.Y;
                    newVert.Z = z;

                    hollowCoords.Add(newVert);
                    if (this.calcVertexNormals)
                    {
                        hollowNormals.Add(hollowSides < 5
                                              ? hollowAngles.normals[i].Invert()
                                              : new Coord(-angle.X, -angle.Y, 0.0f));

                        hollowUs.Add(angle.angle*hollow);
                    }
                }
            }

            int index = 0;
            int numAngles = angles.angles.Count;

            for (int i = 0; i < numAngles; i++)
            {
                angle = angles.angles[i];
                newVert.X = angle.X*xScale;
                newVert.Y = angle.Y*yScale;
                newVert.Z = z;
                coords.Add(newVert);
                if (this.calcVertexNormals)
                {
                    outerCoordIndices.Add(coords.Count - 1);

                    if (sides < 5)
                    {
                        vertexNormals.Add(angles.normals[i]);
                        float u = angle.angle;
                        us.Add(u);
                    }
                    else
                    {
                        vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f));
                        us.Add(angle.angle);
                    }
                }

                if (hasHollow)
                {
                    if (hollowSides == sides)
                    {
                        newVert.X *= hollow;
                        newVert.Y *= hollow;
                        newVert.Z = z;
                        hollowCoords.Add(newVert);
                        if (this.calcVertexNormals)
                        {
                            hollowNormals.Add(sides < 5
                                                  ? angles.normals[i].Invert()
                                                  : new Coord(-angle.X, -angle.Y, 0.0f));

                            hollowUs.Add(angle.angle*hollow);
                        }
                    }
                }
                else if (!simpleFace && createFaces && angle.angle > 0.0001f)
                {
                    Face newFace = new Face {v1 = 0, v2 = index, v3 = index + 1};

                    faces.Add(newFace);
                }
                index += 1;
            }

            if (hasHollow)
            {
                hollowCoords.Reverse();
                if (this.calcVertexNormals)
                {
                    hollowNormals.Reverse();
                    hollowUs.Reverse();
                }

                if (createFaces)
                {
                    int numTotalVerts = numOuterVerts + numHollowVerts;

                    if (numOuterVerts == numHollowVerts)
                    {
                        Face newFace = new Face();

                        for (int coordIndex = 0; coordIndex < numOuterVerts - 1; coordIndex++)
                        {
                            newFace.v1 = coordIndex;
                            newFace.v2 = coordIndex + 1;
                            newFace.v3 = numTotalVerts - coordIndex - 1;
                            faces.Add(newFace);

                            newFace.v1 = coordIndex + 1;
                            newFace.v2 = numTotalVerts - coordIndex - 2;
                            newFace.v3 = numTotalVerts - coordIndex - 1;
                            faces.Add(newFace);
                        }
                    }
                    else
                    {
                        if (numOuterVerts < numHollowVerts)
                        {
                            Face newFace = new Face();
                            int j = 0; // j is the index for outer vertices
                            int maxJ = numOuterVerts - 1;
                            for (int i = 0; i < numHollowVerts; i++) // i is the index for inner vertices
                            {
                                if (j < maxJ)
                                    if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle <
                                        hollowAngles.angles[i].angle - angles.angles[j].angle + 0.000001f)
                                    {
                                        newFace.v1 = numTotalVerts - i - 1;
                                        newFace.v2 = j;
                                        newFace.v3 = j + 1;

                                        faces.Add(newFace);
                                        j += 1;
                                    }

                                newFace.v1 = j;
                                newFace.v2 = numTotalVerts - i - 2;
                                newFace.v3 = numTotalVerts - i - 1;

                                faces.Add(newFace);
                            }
                        }
                        else // numHollowVerts < numOuterVerts
                        {
                            Face newFace = new Face();
                            int j = 0; // j is the index for inner vertices
                            int maxJ = numHollowVerts - 1;
                            for (int i = 0; i < numOuterVerts; i++)
                            {
                                if (j < maxJ)
                                    if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle <
                                        angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f)
                                    {
                                        newFace.v1 = i;
                                        newFace.v2 = numTotalVerts - j - 2;
                                        newFace.v3 = numTotalVerts - j - 1;

                                        faces.Add(newFace);
                                        j += 1;
                                    }

                                newFace.v1 = numTotalVerts - j - 1;
                                newFace.v2 = i;
                                newFace.v3 = i + 1;

                                faces.Add(newFace);
                            }
                        }
                    }
                }

                if (calcVertexNormals)
                {
                    foreach (Coord hc in hollowCoords)
                    {
                        coords.Add(hc);
                        hollowCoordIndices.Add(coords.Count - 1);
                    }
                }
                else
                    coords.AddRange(hollowCoords);

                if (this.calcVertexNormals)
                {
                    vertexNormals.AddRange(hollowNormals);
                    us.AddRange(hollowUs);
                }
            }

            if (simpleFace && createFaces)
            {
                if (sides == 3)
                    faces.Add(new Face(0, 1, 2));
                else if (sides == 4)
                {
                    faces.Add(new Face(0, 1, 2));
                    faces.Add(new Face(0, 2, 3));
                }
            }

            if (calcVertexNormals && hasProfileCut)
            {
                int lastOuterVertIndex = numOuterVerts - 1;

                if (hasHollow)
                {
                    cut1CoordIndices.Add(0);
                    cut1CoordIndices.Add(coords.Count - 1);

                    cut2CoordIndices.Add(lastOuterVertIndex + 1);
                    cut2CoordIndices.Add(lastOuterVertIndex);

                    cutNormal1.X = coords[0].Y - coords[coords.Count - 1].Y;
                    cutNormal1.Y = -(coords[0].X - coords[coords.Count - 1].X);

                    cutNormal2.X = coords[lastOuterVertIndex + 1].Y - coords[lastOuterVertIndex].Y;
                    cutNormal2.Y = -(coords[lastOuterVertIndex + 1].X - coords[lastOuterVertIndex].X);
                }

                else
                {
                    cut1CoordIndices.Add(0);
                    cut1CoordIndices.Add(1);

                    cut2CoordIndices.Add(lastOuterVertIndex);
                    cut2CoordIndices.Add(0);

                    cutNormal1.X = vertexNormals[1].Y;
                    cutNormal1.Y = -vertexNormals[1].X;

                    cutNormal2.X = -vertexNormals[vertexNormals.Count - 2].Y;
                    cutNormal2.Y = vertexNormals[vertexNormals.Count - 2].X;
                }
                cutNormal1.Normalize();
                cutNormal2.Normalize();
            }

            MakeFaceUVs();

            hollowCoords = null;
            hollowNormals = null;
            hollowUs = null;

            if (calcVertexNormals)
            {
                // calculate prim face numbers

                // face number order is top, outer, hollow, bottom, start cut, end cut
                // I know it's ugly but so is the whole concept of prim face numbers

                int faceNum = 1; // start with outer faces
                outerFaceNumber = faceNum;

                int startVert = hasProfileCut && !hasHollow ? 1 : 0;
                if (startVert > 0)
                    faceNumbers.Add(-1);
                for (int i = 0; i < numOuterVerts - 1; i++)
                    //this.faceNumbers.Add(sides < 5 ? faceNum++ : faceNum);
                    faceNumbers.Add(sides < 5 && i < sides ? faceNum++ : faceNum);

                //if (!hasHollow && !hasProfileCut)
                //    this.bottomFaceNumber = faceNum++;

                faceNumbers.Add(hasProfileCut ? -1 : faceNum++);

                if (sides > 4 && (hasHollow || hasProfileCut))
                    faceNum++;

                if (sides < 5 && (hasHollow || hasProfileCut) && numOuterVerts < sides)
                    faceNum++;

                if (hasHollow)
                {
                    for (int i = 0; i < numHollowVerts; i++)
                        faceNumbers.Add(faceNum);

                    hollowFaceNumber = faceNum++;
                }
                //if (hasProfileCut || hasHollow)
                //    this.bottomFaceNumber = faceNum++;
                bottomFaceNumber = faceNum++;

                if (hasHollow && hasProfileCut)
                    faceNumbers.Add(faceNum++);

                for (int i = 0; i < faceNumbers.Count; i++)
                    if (faceNumbers[i] == -1)
                        faceNumbers[i] = faceNum++;

                numPrimFaces = faceNum;
            }
        }
Пример #5
0
        /// <summary>
        ///     Extrudes a profile along a path.
        /// </summary>
        public void Extrude(PathType pathType)
        {
            bool needEndFaces = false;

            coords = new List<Coord>();
            faces = new List<Face>();

            if (viewerMode)
            {
                viewerFaces = new List<ViewerFace>();
                calcVertexNormals = true;
            }

            if (calcVertexNormals)
                normals = new List<Coord>();

            int steps = 1;

            float length = pathCutEnd - pathCutBegin;
            normalsProcessed = false;

            if (viewerMode && sides == 3)
            {
                // prisms don't taper well so add some vertical resolution
                // other prims may benefit from this but just do prisms for now
                if (Math.Abs(taperX) > 0.01 || Math.Abs(taperY) > 0.01)
                    steps = (int) (steps*4.5*length);
            }

            if (sphereMode)
                hasProfileCut = profileEnd - profileStart < 0.4999f;
            else
                this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f;
            this.hasHollow = (this.hollow > 0.001f);

            float twistBegin2 = twistBegin/360.0f*twoPi;
            float twistEnd2 = twistEnd/360.0f*twoPi;
            float twistTotal = twistEnd2 - twistBegin2;
            float twistTotalAbs = Math.Abs(twistTotal);
            if (twistTotalAbs > 0.01f)
                steps += (int) (twistTotalAbs*3.66); //  dahlia's magic number

            float hollow2 = hollow;

            // sanity checks
            float initialProfileRot = 0.0f;
            if (pathType == PathType.Circular)
            {
                if (sides == 3)
                {
                    initialProfileRot = (float) Math.PI;
                    if (hollowSides == 4)
                    {
                        if (hollow2 > 0.7f)
                            hollow2 = 0.7f;
                        hollow2 *= 0.707f;
                    }
                    else hollow2 *= 0.5f;
                }
                else if (sides == 4)
                {
                    initialProfileRot = 0.25f*(float) Math.PI;
                    if (hollowSides != 4)
                        hollow2 *= 0.707f;
                }
                else if (sides > 4)
                {
                    initialProfileRot = (float) Math.PI;
                    if (hollowSides == 4)
                    {
                        if (hollow2 > 0.7f)
                            hollow2 = 0.7f;
                        hollow2 /= 0.7f;
                    }
                }
            }
            else
            {
                if (sides == 3)
                {
                    if (hollowSides == 4)
                    {
                        if (hollow2 > 0.7f)
                            hollow2 = 0.7f;
                        hollow2 *= 0.707f;
                    }
                    else hollow2 *= 0.5f;
                }
                else if (sides == 4)
                {
                    initialProfileRot = 1.25f*(float) Math.PI;
                    if (hollowSides != 4)
                        hollow2 *= 0.707f;
                }
                else if (sides == 24 && hollowSides == 4)
                    hollow2 *= 1.414f;
            }

            Profile profile = new Profile(sides, profileStart, profileEnd, hollow2, hollowSides, true,
                                          calcVertexNormals);
            errorMessage = profile.errorMessage;

            numPrimFaces = profile.numPrimFaces;

            profileOuterFaceNumber = profile.outerFaceNumber;
            //this is always true
            if (!needEndFaces)
                profileOuterFaceNumber--;

            if (hasHollow)
            {
                profileHollowFaceNumber = profile.hollowFaceNumber;
                if (!needEndFaces)
                    profileHollowFaceNumber--;
            }

            int cut1Vert = -1;
            int cut2Vert = -1;
            if (hasProfileCut)
            {
                cut1Vert = hasHollow ? profile.coords.Count - 1 : 0;
                cut2Vert = hasHollow ? profile.numOuterVerts - 1 : profile.numOuterVerts;
            }

            if (initialProfileRot != 0.0f)
            {
                profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot));
                if (viewerMode)
                    profile.MakeFaceUVs();
            }

            Coord lastCutNormal1 = new Coord();
            Coord lastCutNormal2 = new Coord();
            float lastV = 1.0f;

            Path path = new Path
                            {
                                twistBegin = twistBegin2,
                                twistEnd = twistEnd2,
                                topShearX = topShearX,
                                topShearY = topShearY,
                                pathCutBegin = pathCutBegin,
                                pathCutEnd = pathCutEnd,
                                dimpleBegin = dimpleBegin,
                                dimpleEnd = dimpleEnd,
                                skew = skew,
                                holeSizeX = holeSizeX,
                                holeSizeY = holeSizeY,
                                taperX = taperX,
                                taperY = taperY,
                                radius = radius,
                                revolutions = revolutions,
                                stepsPerRevolution = stepsPerRevolution
                            };

            path.Create(pathType, steps);

            if (pathType == PathType.Circular)
            {
                needEndFaces = false;
                if (pathCutBegin != 0.0f || pathCutEnd != 1.0f)
                    needEndFaces = true;
                else if (taperX != 0.0f || taperY != 0.0f)
                    needEndFaces = true;
                else if (skew != 0.0f)
                    needEndFaces = true;
                else if (twistTotal != 0.0f)
                    needEndFaces = true;
                else if (radius != 0.0f)
                    needEndFaces = true;
            }
            else needEndFaces = true;

            for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++)
            {
                PathNode node = path.pathNodes[nodeIndex];
                Profile newLayer = profile.Copy();
                newLayer.Scale(node.xScale, node.yScale);

                newLayer.AddRot(node.rotation);
                newLayer.AddPos(node.position);

                if (needEndFaces && nodeIndex == 0)
                {
                    newLayer.FlipNormals();

                    // add the top faces to the viewerFaces list here
                    if (viewerMode)
                    {
                        Coord faceNormal = newLayer.faceNormal;
                        ViewerFace newViewerFace = new ViewerFace(profile.bottomFaceNumber);
                        int numFaces = newLayer.faces.Count;
                        List<Face> faces2 = newLayer.faces;

                        for (int i = 0; i < numFaces; i++)
                        {
                            Face face = faces2[i];
                            newViewerFace.v1 = newLayer.coords[face.v1];
                            newViewerFace.v2 = newLayer.coords[face.v2];
                            newViewerFace.v3 = newLayer.coords[face.v3];

                            newViewerFace.coordIndex1 = face.v1;
                            newViewerFace.coordIndex2 = face.v2;
                            newViewerFace.coordIndex3 = face.v3;

                            newViewerFace.n1 = faceNormal;
                            newViewerFace.n2 = faceNormal;
                            newViewerFace.n3 = faceNormal;

                            newViewerFace.uv1 = newLayer.faceUVs[face.v1];
                            newViewerFace.uv2 = newLayer.faceUVs[face.v2];
                            newViewerFace.uv3 = newLayer.faceUVs[face.v3];

                            viewerFaces.Add(newViewerFace);
                        }
                    }
                } // if (nodeIndex == 0)

                // append this layer

                int coordsLen = coords.Count;
                newLayer.AddValue2FaceVertexIndices(coordsLen);

                coords.AddRange(newLayer.coords);

                if (calcVertexNormals)
                {
                    newLayer.AddValue2FaceNormalIndices(normals.Count);
                    normals.AddRange(newLayer.vertexNormals);
                }

                if (node.percentOfPath < pathCutBegin + 0.01f || node.percentOfPath > pathCutEnd - 0.01f)
                    faces.AddRange(newLayer.faces);

                // fill faces between layers

                int numVerts = newLayer.coords.Count;
                if (nodeIndex > 0)
                {
                    Face newFace = new Face();

                    int startVert = coordsLen + 1;
                    int endVert = coords.Count;

                    if (sides < 5 || hasProfileCut || hasHollow)
                        startVert--;

                    for (int i = startVert; i < endVert; i++)
                    {
                        int iNext = i + 1;
                        if (i == endVert - 1)
                            iNext = startVert;

                        int whichVert = i - startVert;

                        newFace.v1 = i;
                        newFace.v2 = i - numVerts;
                        newFace.v3 = iNext - numVerts;
                        faces.Add(newFace);

                        newFace.v2 = iNext - numVerts;
                        newFace.v3 = iNext;
                        faces.Add(newFace);

                        if (viewerMode)
                        {
                            // add the side faces to the list of viewerFaces here

                            int primFaceNum = profile.faceNumbers[whichVert];
                            if (!needEndFaces)
                                primFaceNum -= 1;

                            ViewerFace newViewerFace1 = new ViewerFace(primFaceNum);
                            ViewerFace newViewerFace2 = new ViewerFace(primFaceNum);

                            float u1 = newLayer.us[whichVert];
                            float u2 = 1.0f;
                            if (whichVert < newLayer.us.Count - 1)
                                u2 = newLayer.us[whichVert + 1];

                            if (whichVert == cut1Vert || whichVert == cut2Vert)
                            {
                                u1 = 0.0f;
                                u2 = 1.0f;
                            }
                            else if (sides < 5)
                            {
                                if (whichVert < profile.numOuterVerts)
                                {
                                    // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled
                                    // to reflect the entire texture width
                                    u1 *= sides;
                                    u2 *= sides;
                                    u2 -= (int) u1;
                                    u1 -= (int) u1;
                                    if (u2 < 0.1f)
                                        u2 = 1.0f;
                                }
                                else if (whichVert > profile.coords.Count - profile.numHollowVerts - 1)
                                {
                                    u1 *= 2.0f;
                                    u2 *= 2.0f;
                                    //this.profileHollowFaceNumber = primFaceNum;
                                }
                            }

                            if (this.sphereMode)
                            {
                                if (whichVert != cut1Vert && whichVert != cut2Vert)
                                {
                                    u1 = u1 * 2.0f - 1.0f;
                                    u2 = u2 * 2.0f - 1.0f;

                                    if (whichVert >= newLayer.numOuterVerts)
                                    {
                                        u1 -= hollow;
                                        u2 -= hollow;
                                    }

                                }
                            }

                            newViewerFace1.uv1.U = u1;
                            newViewerFace1.uv2.U = u1;
                            newViewerFace1.uv3.U = u2;

                            newViewerFace1.uv1.V = 1.0f - node.percentOfPath;
                            newViewerFace1.uv2.V = lastV;
                            newViewerFace1.uv3.V = lastV;

                            newViewerFace2.uv1.U = u1;
                            newViewerFace2.uv2.U = u2;
                            newViewerFace2.uv3.U = u2;

                            newViewerFace2.uv1.V = 1.0f - node.percentOfPath;
                            newViewerFace2.uv2.V = lastV;
                            newViewerFace2.uv3.V = 1.0f - node.percentOfPath;

                            newViewerFace1.v1 = coords[i];
                            newViewerFace1.v2 = coords[i - numVerts];
                            newViewerFace1.v3 = coords[iNext - numVerts];

                            newViewerFace2.v1 = coords[i];
                            newViewerFace2.v2 = coords[iNext - numVerts];
                            newViewerFace2.v3 = coords[iNext];

                            newViewerFace1.coordIndex1 = i;
                            newViewerFace1.coordIndex2 = i - numVerts;
                            newViewerFace1.coordIndex3 = iNext - numVerts;

                            newViewerFace2.coordIndex1 = i;
                            newViewerFace2.coordIndex2 = iNext - numVerts;
                            newViewerFace2.coordIndex3 = iNext;

                            // profile cut faces
                            if (whichVert == cut1Vert)
                            {
                                newViewerFace1.n1 = newLayer.cutNormal1;
                                newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1;

                                newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1;
                                newViewerFace2.n2 = lastCutNormal1;
                            }
                            else if (whichVert == cut2Vert)
                            {
                                newViewerFace1.n1 = newLayer.cutNormal2;
                                newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal2;

                                newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal2;
                                newViewerFace2.n2 = lastCutNormal2;
                            }

                            else // outer and hollow faces
                            {
                                if ((sides < 5 && whichVert < newLayer.numOuterVerts) ||
                                    (hollowSides < 5 && whichVert >= newLayer.numOuterVerts))
                                {
                                    // looks terrible when path is twisted... need vertex normals here
                                    newViewerFace1.CalcSurfaceNormal();
                                    newViewerFace2.CalcSurfaceNormal();
                                }
                                else
                                {
                                    newViewerFace1.n1 = normals[i];
                                    newViewerFace1.n2 = normals[i - numVerts];
                                    newViewerFace1.n3 = normals[iNext - numVerts];

                                    newViewerFace2.n1 = normals[i];
                                    newViewerFace2.n2 = normals[iNext - numVerts];
                                    newViewerFace2.n3 = normals[iNext];
                                }
                            }

                            viewerFaces.Add(newViewerFace1);
                            viewerFaces.Add(newViewerFace2);
                        }
                    }
                }

                lastCutNormal1 = newLayer.cutNormal1;
                lastCutNormal2 = newLayer.cutNormal2;
                lastV = 1.0f - node.percentOfPath;

                if (needEndFaces && nodeIndex == path.pathNodes.Count - 1 && viewerMode)
                {
                    // add the top faces to the viewerFaces list here
                    Coord faceNormal = newLayer.faceNormal;
                    ViewerFace newViewerFace = new ViewerFace {primFaceNumber = 0};
                    int numFaces = newLayer.faces.Count;
                    List<Face> faces2 = newLayer.faces;

                    for (int i = 0; i < numFaces; i++)
                    {
                        Face face = faces2[i];
                        newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen];
                        newViewerFace.v2 = newLayer.coords[face.v2 - coordsLen];
                        newViewerFace.v3 = newLayer.coords[face.v3 - coordsLen];

                        newViewerFace.coordIndex1 = face.v1 - coordsLen;
                        newViewerFace.coordIndex2 = face.v2 - coordsLen;
                        newViewerFace.coordIndex3 = face.v3 - coordsLen;

                        newViewerFace.n1 = faceNormal;
                        newViewerFace.n2 = faceNormal;
                        newViewerFace.n3 = faceNormal;

                        newViewerFace.uv1 = newLayer.faceUVs[face.v1 - coordsLen];
                        newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen];
                        newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen];

                        viewerFaces.Add(newViewerFace);
                    }
                }
            } // for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++)
        }