コード例 #1
0
ファイル: Face.cs プロジェクト: RavenB/gridsearch
        private static void BuildFace(ref Face face, LLObject.ObjectData prim, List<Vertex> vertices, Path path,
            Profile profile, LLObject.TextureEntryFace teFace)
        {
            if (teFace != null)
                face.TextureFace = teFace;
            else
                throw new ArgumentException("teFace cannot be null");

            face.Vertices.Clear();

            if ((face.Mask & FaceMask.Cap) != 0)
            {
                if (((face.Mask & FaceMask.Hollow) == 0) &&
                    ((face.Mask & FaceMask.Open) == 0) &&
                    (prim.PathBegin == 0f) &&
                    (prim.ProfileCurve == LLObject.ProfileCurve.Square) &&
                    (prim.PathCurve == LLObject.PathCurve.Line))
                {
                    CreateUnCutCubeCap(ref face, vertices, path, profile);
                }
                else
                {
                    CreateCap(ref face, vertices, path, profile);
                }
            }
            else if ((face.Mask & FaceMask.End) != 0 || (face.Mask & FaceMask.Side) != 0)
            {
                CreateSide(ref face, prim, vertices, path, profile);
            }
            else
            {
                throw new RenderingException("Unknown/uninitialized face type");
            }
        }
コード例 #2
0
ファイル: Texture.cs プロジェクト: RavenB/gridsearch
        public void TransformTexCoords(List<Vertex> vertices, Vector3 center, LLObject.TextureEntryFace teFace)
        {
            float r = teFace.Rotation;
            float os = teFace.OffsetU;
            float ot = teFace.OffsetV;
            float ms = teFace.RepeatU;
            float mt = teFace.RepeatV;
            float cosAng = (float)Math.Cos(r);
            float sinAng = (float)Math.Sin(r);

            for (int i = 0; i < vertices.Count; i++)
            {
                Vertex vertex = vertices[i];

                if (teFace.TexMapType == MappingType.Default)
                {
                    TransformTexCoord(ref vertex.TexCoord, cosAng, sinAng, os, ot, ms, mt);
                }
                else if (teFace.TexMapType == MappingType.Planar)
                {
                    Vector3 vec = vertex.Position;
                    vec.X *= vec.X;
                    vec.Y *= vec.Y;
                    vec.Z *= vec.Z;

                    TransformPlanarTexCoord(ref vertex.TexCoord, vertex, center, vec);
                }

                vertices[i] = vertex;
            }
        }
コード例 #3
0
        public void PathSkew()
        {
            for (sbyte i = sbyte.MinValue; i < sbyte.MaxValue; i++)
            {
                float floatValue = LLObject.PathSkewFloat(i);
                sbyte result     = LLObject.PathSkewByte(floatValue);

                Assert.IsTrue(result == i, "Started with " + i + ", float value was " + floatValue +
                              ", and ended up with " + result);
            }
        }
コード例 #4
0
        public void PathShear()
        {
            for (byte i = 0; i < byte.MaxValue; i++)
            {
                float floatValue = LLObject.PathShearFloat(i);
                byte  result     = LLObject.PathShearByte(floatValue);

                Assert.IsTrue(result == i, "Started with " + i + ", float value was " + floatValue +
                              ", and ended up with " + result);
            }
        }
コード例 #5
0
        public void PathEnd()
        {
            for (byte i = 0; i < byte.MaxValue; i++)
            {
                float  floatValue = LLObject.PathEndFloat(i);
                ushort result     = LLObject.PathEndUInt16(floatValue);

                Assert.IsTrue(result == i, "Started with " + i + ", float value was " + floatValue +
                              ", and ended up with " + result);
            }
        }
コード例 #6
0
        public void PathRevolutions()
        {
            for (byte i = 0; i < byte.MaxValue; i++)
            {
                float floatValue = LLObject.UnpackPathRevolutions(i);
                byte  result     = LLObject.PackPathRevolutions(floatValue);

                Assert.IsTrue(result == i, "Started with " + i + ", float value was " + floatValue +
                              ", and ended up with " + result);
            }
        }
コード例 #7
0
ファイル: SimpleRenderer.cs プロジェクト: RavenB/gridsearch
 public void TransformTexCoords(List<Vertex> vertices, Vector3 center, LLObject.TextureEntryFace teFace)
 {
     // Lalala...
 }
コード例 #8
0
ファイル: SimpleRenderer.cs プロジェクト: RavenB/gridsearch
        private List<Face> GenerateFaces(LLObject.TextureEntry te)
        {
            Face face = new Face();
            face.Edge = new List<int>();
            face.TextureFace = te.DefaultTexture;
            face.Vertices = GenerateVertices();
            face.Indices = GenerateIndices();

            List<Face> faces = new List<Face>(1);
            faces.Add(face);

            return faces;
        }
コード例 #9
0
        void Objects_OnObjectProperties(Simulator simulator, LLObject.ObjectProperties properties)
        {
            if (PermsSent)
            {
                if (Objects.ContainsKey(properties.ObjectID))
                {
                    // FIXME: Confirm the current operation against properties.Permissions.NextOwnerMask

                    ++PermCount;
                    if (PermCount >= Objects.Count)
                        GotPermissionsEvent.Set();
                }
            }
        }
コード例 #10
0
ファイル: GPLRenderer.cs プロジェクト: RavenB/gridsearch
 private static Path GeneratePathPolygon(LLObject.ObjectData prim, int sides)
 {
     return GeneratePathPolygon(prim, sides, 0f, 1f, 1f);
 }
コード例 #11
0
ファイル: GPLRenderer.cs プロジェクト: RavenB/gridsearch
        private static Profile GenerateProfilePolygon(LLObject.ObjectData prim, int sides, float offset, float angScale)
        {
            // Create a polygon by starting at (1, 0) and proceeding counterclockwise generating vectors
            Profile profile = new Profile();
            profile.Positions = new List<Vector3>();
            profile.Faces = new List<ProfileFace>();

            float scale = 0.5f;
            float t, tStep, tFirst, tFraction, ang, angStep;
            Vector3 pt1, pt2;

            float begin = prim.ProfileBegin;
            float end = prim.ProfileEnd;

            tStep = 1f / sides;
            angStep = 2f * F_PI * tStep * angScale;

            // Scale to have size "match" scale. Compensates to get object to generally fill bounding box
            int totalSides = MathHelper.Round(sides / angScale);

            if (totalSides < 8)
                scale = TABLE_SCALE[totalSides];

            tFirst = (float)Math.Floor(begin * sides) / (float)sides;

            // pt1 is the first point on the fractional face.
            // Starting t and ang values for the first face
            t = tFirst;
            ang = 2f * F_PI * (t * angScale + offset);
            pt1 = new Vector3((float)Math.Cos(ang) * scale, (float)Math.Sin(ang) * scale, t);

            // Increment to the next point.
            // pt2 is the end point on the fractional face
            t += tStep;
            ang += angStep;
            pt2 = new Vector3((float)Math.Cos(ang) * scale, (float)Math.Sin(ang) * scale, t);

            tFraction = (begin - tFirst) * sides;

            // Only use if it's not almost exactly on an edge
            if (tFraction < 0.9999f)
            {
                Vector3 newPt = Vector3.Lerp(pt1, pt2, tFraction);
                float ptX = newPt.X;

                if (ptX < profile.MinX)
                    profile.MinX = ptX;
                else if (ptX > profile.MaxX)
                    profile.MaxX = ptX;

                profile.Positions.Add(newPt);
            }

            // There's lots of potential here for floating point error to generate unneeded extra points
            while (t < end)
            {
                // Iterate through all the integer steps of t.
                pt1 = new Vector3((float)Math.Cos(ang) * scale, (float)Math.Sin(ang) * scale, t);

                float ptX = pt1.X;
                if (ptX < profile.MinX)
                    profile.MinX = ptX;
                else if (ptX > profile.MaxX)
                    profile.MaxX = ptX;

                profile.Positions.Add(pt1);

                t += tStep;
                ang += angStep;
            }

            // pt1 is the first point on the fractional face
            // pt2 is the end point on the fractional face
            pt2 = new Vector3((float)Math.Cos(ang) * scale, (float)Math.Sin(ang) * scale, t);

            // Find the fraction that we need to add to the end point
            tFraction = (end - (t - tStep)) * sides;
            if (tFraction > 0.0001f)
            {
                Vector3 newPt = Vector3.Lerp(pt1, pt2, tFraction);

                float ptX = newPt.X;
                if (ptX < profile.MinX)
                    profile.MinX = ptX;
                else if (ptX > profile.MaxX)
                    profile.MaxX = ptX;

                profile.Positions.Add(newPt);
            }

            // If we're sliced, the profile is open
            if ((end - begin) * angScale < 0.99f)
            {
                if ((end - begin) * angScale > 0.5f)
                    profile.Concave = true;
                else
                    profile.Concave = false;

                profile.Open = true;

                // Put center point if not hollow
                if (prim.ProfileHollow == 0f)
                    profile.Positions.Add(Vector3.Zero);
            }
            else
            {
                // The profile isn't open
                profile.Open = false;
                profile.Concave = false;
            }

            return profile;
        }
コード例 #12
0
ファイル: GPLRenderer.cs プロジェクト: RavenB/gridsearch
        private static Path GeneratePath(LLObject.ObjectData prim, float detail)
        {
            Path path;
            path.Open = true;
            int np = 2; // Hardcode for line
            float step;

            switch (prim.PathCurve)
            {
                default:
                case LLObject.PathCurve.Line:
                    {
                        // Take the begin/end twist into account for detail
                        np = (int)Math.Floor(Math.Abs(prim.PathTwistBegin - prim.PathTwist) * 3.5f * (detail - 0.5f)) + 2;
                        path.Points = new List<PathPoint>(np);

                        step = 1f / (np - 1);

                        Vector2 startScale = prim.PathBeginScale;
                        Vector2 endScale = prim.PathEndScale;

                        for (int i = 0; i < np; i++)
                        {
                            PathPoint point = new PathPoint();

                            float t = MathHelper.Lerp(prim.PathBegin, prim.PathEnd, (float)i * step);
                            point.Position = new Vector3(
                                MathHelper.Lerp(0, prim.PathShearX, t),
                                MathHelper.Lerp(0, prim.PathShearY, t),
                                t - 0.5f);
                            point.Rotation = Quaternion.CreateFromAxisAngle(MathHelper.Lerp(F_PI * prim.PathTwistBegin, F_PI * prim.PathTwist, t), 0f, 0f, 1f);
                            point.Scale.X = MathHelper.Lerp(startScale.X, endScale.X, t);
                            point.Scale.Y = MathHelper.Lerp(startScale.Y, endScale.Y, t);
                            point.TexT = t;

                            path.Points.Add(point);
                        }
                    }
                    break;
                case LLObject.PathCurve.Circle:
                    {
                        // Increase the detail as the revolutions and twist increase
                        float twistMag = Math.Abs(prim.PathTwistBegin - prim.PathTwist);

                        int sides = (int)Math.Floor(
                            Math.Floor(MIN_DETAIL_FACES * detail + twistMag * 3.5f * (detail - 0.5f))
                            * prim.PathRevolutions);

                        // FIXME: Sculpty support
                        //if (is_sculpted)
                        //    sides = sculpt_sides(detail);

                        path = GeneratePathPolygon(prim, sides);
                    }
                    break;
                case LLObject.PathCurve.Circle2:
                    {
                        if (prim.PathEnd - prim.PathBegin >= 0.99f && prim.PathScaleX >= 0.99f)
                            path.Open = false;

                        path = GeneratePathPolygon(prim, (int)Math.Floor(MIN_DETAIL_FACES * detail));

                        float t = 0f;
                        float tStep = 1f / path.Points.Count;
                        float toggle = 0.5f;

                        for (int i = 0; i < path.Points.Count; i++)
                        {
                            PathPoint point = path.Points[i];
                            point.Position.X = toggle;
                            path.Points[i] = point;

                            if (toggle == 0.5f)
                                toggle = -0.5f;
                            else
                                toggle = 0.5f;
                            t += tStep;
                        }
                    }
                    break;
                case LLObject.PathCurve.Test:
                    throw new NotImplementedException("PathCurve.Test is not supported");
            }

            if (prim.PathTwist != prim.PathTwistBegin)
                path.Open = true;

            return path;
        }
コード例 #13
0
ファイル: GPLRenderer.cs プロジェクト: RavenB/gridsearch
        private static void GenerateProfileHole(LLObject.ObjectData prim, ref Profile profile, bool flat, float sides,
            float offset, float boxHollow, float angScale)
        {
            // Total add has number of vertices on outside
            profile.TotalOutsidePoints = profile.Positions.Count;

            // Create the hole
            Profile hole = GenerateProfilePolygon(prim, (int)Math.Floor(sides), offset, angScale);
            // FIXME: Should we overwrite profile.Values with hole.Values?

            // Apply the hollow scale modifier
            for (int i = 0; i < hole.Positions.Count; i++)
            {
                Vector3 point = hole.Positions[i];
                point *= boxHollow;
                hole.Positions[i] = point;
            }

            // Reverse the order
            hole.Positions.Reverse();

            // Add the hole to the profile
            profile.Positions.AddRange(hole.Positions);

            // Create the inner side face for the hole
            ProfileFace innerFace = CreateProfileFace(profile.TotalOutsidePoints,
                profile.Positions.Count - profile.TotalOutsidePoints, 0f, FaceType.InnerSide, flat);
            profile.Faces.Add(innerFace);
        }
コード例 #14
0
ファイル: GPLRenderer.cs プロジェクト: RavenB/gridsearch
        private static Profile GenerateProfile(LLObject.ObjectData prim, Path path, float detail)
        {
            Profile profile;

            if (detail < (float)MIN_LOD)
                detail = (float)MIN_LOD;

            // Generate the face data
            int i;
            float begin = prim.ProfileBegin;
            float end = prim.ProfileEnd;
            float hollow = prim.ProfileHollow;

            // Sanity check
            if (begin > end - 0.01f)
                begin = end - 0.01f;

            int faceNum = 0;

            switch (prim.ProfileCurve)
            {
                #region Squares
                case LLObject.ProfileCurve.Square:
                    {
                        profile = GenerateProfilePolygon(prim, 4, -0.375f, 1f);

                        #region Create Faces

                        if (path.Open)
                            profile.Faces.Add(CreateProfileCap(FaceType.PathBegin, profile.Positions.Count));

                        int iBegin = (int)Math.Floor(begin * 4f);
                        int iEnd = (int)Math.Floor(end * 4f + 0.999f);

                        for (i = iBegin; i < iEnd; i++)
                        {
                            FaceType type = (FaceType)((ushort)FaceType.OuterSide0 << i);
                            profile.Faces.Add(CreateProfileFace(faceNum++, 2, 1f, type, true));
                        }

                        #endregion Create Faces

                        for (i = 0; i < profile.Positions.Count; i++)
                        {
                            // Scale by 4 to generate proper tex coords
                            Vector3 point = profile.Positions[i];
                            point.Z *= 4f;
                            profile.Positions[i] = point;
                        }

                        if (hollow > 0f)
                        {
                            switch (prim.ProfileHole)
                            {
                                case LLObject.HoleType.Triangle:
                                    // This is the wrong offset, but it is what the official viewer uses
                                    GenerateProfileHole(prim, ref profile, true, 3f, -0.375f, hollow, 1f);
                                    break;
                                case LLObject.HoleType.Circle:
                                    // TODO: Compute actual detail levels for cubes
                                    GenerateProfileHole(prim, ref profile, false, MIN_DETAIL_FACES * detail, -0.375f, hollow, 1f);
                                    break;
                                case LLObject.HoleType.Same:
                                case LLObject.HoleType.Square:
                                default:
                                    GenerateProfileHole(prim, ref profile, true, 4, -0.375f, hollow, 1f);
                                    break;
                            }
                        }
                    }
                    break;
                #endregion Squares
                #region Triangles
                case LLObject.ProfileCurve.IsoTriangle:
                case LLObject.ProfileCurve.RightTriangle:
                case LLObject.ProfileCurve.EqualTriangle:
                    {
                        profile = GenerateProfilePolygon(prim, 3, 0f, 1f);

                        #region Create Faces

                        if (path.Open)
                            profile.Faces.Add(CreateProfileCap(FaceType.PathBegin, profile.Positions.Count));

                        int iBegin = (int)Math.Floor(begin * 3f);
                        int iEnd = (int)Math.Floor(end * 3f + 0.999f);

                        for (i = iBegin; i < iEnd; i++)
                        {
                            FaceType type = (FaceType)((ushort)FaceType.OuterSide0 << i);
                            profile.Faces.Add(CreateProfileFace(faceNum++, 2, 1f, type, true));
                        }

                        #endregion Create Faces

                        for (i = 0; i < profile.Positions.Count; i++)
                        {
                            // Scale by 3 to generate proper tex coords
                            Vector3 point = profile.Positions[i];
                            point.Z *= 3f;
                            profile.Positions[i] = point;
                        }

                        if (hollow > 0f)
                        {
                            // Swept triangles need smaller hollowness values,
                            // because the triangle doesn't fill the bounding box
                            float triangleHollow = hollow / 2f;

                            switch (prim.ProfileHole)
                            {
                                case LLObject.HoleType.Circle:
                                    GenerateProfileHole(prim, ref profile, false, MIN_DETAIL_FACES * detail, 0f, triangleHollow, 1f);
                                    break;
                                case LLObject.HoleType.Square:
                                    GenerateProfileHole(prim, ref profile, true, 4f, 0f, triangleHollow, 1f);
                                    break;
                                case LLObject.HoleType.Same:
                                case LLObject.HoleType.Triangle:
                                default:
                                    GenerateProfileHole(prim, ref profile, true, 3f, 0f, triangleHollow, 1f);
                                    break;
                            }
                        }
                    }
                    break;
                #endregion Triangles
                #region Circles
                case LLObject.ProfileCurve.Circle:
                    {
                        float circleDetail = MIN_DETAIL_FACES * detail;

                        // If this has a square hollow, we should adjust the
                        // number of faces a bit so that the geometry lines up
                        if (hollow > 0f && prim.ProfileHole == LLObject.HoleType.Square)
                        {
                            // Snap to the next multiple of four sides,
                            // so that corners line up
                            circleDetail = (float)Math.Ceiling(circleDetail / 4f) * 4f;
                        }

                        int sides = (int)circleDetail;

                        // FIXME: Handle sculpted prims at some point
                        //if (is_sculpted)
                        //    sides = sculpt_sides(detail);

                        profile = GenerateProfilePolygon(prim, sides, 0f, 1f);

                        #region Create Faces

                        if (path.Open)
                            profile.Faces.Add(CreateProfileCap(FaceType.PathBegin, profile.Positions.Count));

                        if (profile.Open && prim.ProfileHollow == 0f)
                            profile.Faces.Add(CreateProfileFace(0, profile.Positions.Count - 1, 0f, FaceType.OuterSide0, false));
                        else
                            profile.Faces.Add(CreateProfileFace(0, profile.Positions.Count, 0f, FaceType.OuterSide0, false));

                        #endregion Create Faces

                        if (hollow > 0f)
                        {
                            switch (prim.ProfileHole)
                            {
                                case LLObject.HoleType.Square:
                                    GenerateProfileHole(prim, ref profile, true, 4f, 0f, hollow, 1f);
                                    break;
                                case LLObject.HoleType.Triangle:
                                    GenerateProfileHole(prim, ref profile, true, 3f, 0f, hollow, 1f);
                                    break;
                                case LLObject.HoleType.Circle:
                                case LLObject.HoleType.Same:
                                default:
                                    GenerateProfileHole(prim, ref profile, false, circleDetail, 0f, hollow, 1f);
                                    break;
                            }
                        }
                    }
                    break;
                #endregion Circles
                #region HalfCircles
                case LLObject.ProfileCurve.HalfCircle:
                    {
                        // Number of faces is cut in half because it's only a half-circle
                        float circleDetail = MIN_DETAIL_FACES * detail * 0.5f;

                        // If this has a square hollow, we should adjust the
                        // number of faces a bit so that the geometry lines up
                        if (hollow > 0f && prim.ProfileHole == LLObject.HoleType.Square)
                        {
                            // Snap to the next multiple of four sides (div 2),
                            // so that corners line up
                            circleDetail = (float)Math.Ceiling(circleDetail / 2f) * 2f;
                        }

                        profile = GenerateProfilePolygon(prim, (int)Math.Floor(circleDetail), 0.5f, 0.5f);

                        #region Create Faces

                        if (path.Open)
                            profile.Faces.Add(CreateProfileCap(FaceType.PathBegin, profile.Positions.Count));

                        if (profile.Open && prim.ProfileHollow == 0f)
                            profile.Faces.Add(CreateProfileFace(0, profile.Positions.Count - 1, 0f, FaceType.OuterSide0, false));
                        else
                            profile.Faces.Add(CreateProfileFace(0, profile.Positions.Count, 0f, FaceType.OuterSide0, false));

                        #endregion Create Faces

                        if (hollow > 0f)
                        {
                            switch (prim.ProfileHole)
                            {
                                case LLObject.HoleType.Square:
                                    GenerateProfileHole(prim, ref profile, true, 2f, 0.5f, hollow, 0.5f);
                                    break;
                                case LLObject.HoleType.Triangle:
                                    GenerateProfileHole(prim, ref profile, true, 3f, 0.5f, hollow, 0.5f);
                                    break;
                                case LLObject.HoleType.Circle:
                                case LLObject.HoleType.Same:
                                default:
                                    GenerateProfileHole(prim, ref profile, false, circleDetail, 0.5f, hollow, 0.5f);
                                    break;
                            }
                        }

                        // Special case for openness of sphere
                        if (prim.ProfileEnd - prim.ProfileEnd < 1f)
                        {
                            profile.Open = true;
                        }
                        else if (hollow == 0f)
                        {
                            profile.Open = false;
                            Vector3 first = profile.Positions[0];
                            profile.Positions.Add(first);
                        }
                    }
                    break;
                #endregion HalfCircles
                default:
                    throw new RenderingException("Unknown profile curve type " + prim.ProfileCurve.ToString());
            }

            // Bottom cap
            if (path.Open)
                profile.Faces.Add(CreateProfileCap(FaceType.PathEnd, profile.Positions.Count));

            if (profile.Open)
            {
                // Interior edge caps
                profile.Faces.Add(CreateProfileFace(profile.Positions.Count - 1, 2, 0.5f, FaceType.ProfileBegin, true));

                if (prim.ProfileHollow > 0f)
                    profile.Faces.Add(CreateProfileFace(profile.TotalOutsidePoints - 1, 2, 0.5f, FaceType.ProfileEnd, true));
                else
                    profile.Faces.Add(CreateProfileFace(profile.Positions.Count - 2, 2, 0.5f, FaceType.ProfileEnd, true));
            }

            return profile;
        }
コード例 #15
0
ファイル: GPLRenderer.cs プロジェクト: RavenB/gridsearch
        private static List<ushort> GenerateIndices(LLObject.ObjectData prim, Path path, Profile profile)
        {
            bool open = profile.Open;
            bool hollow = (prim.ProfileHollow > 0f);
            int sizeS = profile.Positions.Count;
            int sizeSOut = profile.TotalOutsidePoints;
            int sizeT = path.Points.Count;
            int s, t, i;
            List<ushort> indices = new List<ushort>();

            if (open)
            {
                if (hollow)
                {
                    // Open hollow -- much like the closed solid, except we 
                    // we need to stitch up the gap between s=0 and s=size_s-1
                    for (t = 0; t < sizeT - 1; t++)
                    {
                        // The outer face, first cut, and inner face
                        for (s = 0; s < sizeS - 1; s++)
                        {
                            i = s + t * sizeS;
                            indices.Add((ushort)i); // x,y
                            indices.Add((ushort)(i + 1)); // x+1,y
                            indices.Add((ushort)(i + sizeS)); // x,y+1

                            indices.Add((ushort)(i + sizeS)); // x,y+1
                            indices.Add((ushort)(i + 1)); // x+1,y
                            indices.Add((ushort)(i + sizeS + 1)); // x+1,y+1
                        }

                        // The other cut face
                        indices.Add((ushort)(s + t * sizeS)); // x,y
                        indices.Add((ushort)(0 + t * sizeS)); // x+1,y
                        indices.Add((ushort)(s + (t + 1) * sizeS)); // x,y+1

                        indices.Add((ushort)(s + (t + 1) * sizeS)); // x,y+1
                        indices.Add((ushort)(0 + t * sizeS)); // x+1,y
                        indices.Add((ushort)(0 + (t + 1) * sizeS)); // x+1,y+1
                    }

                    // Do the top and bottom caps, if necessary
                    if (path.Open)
                    {
                        // Top cap
                        int pt1 = 0;
                        int pt2 = sizeS - 1;
                        i = (sizeT - 1) * sizeS;

                        while (pt2 - pt1 > 1)
                        {
                            if (use_tri_1a2(profile, pt1, pt2))
                            {
                                indices.Add((ushort)(pt1 + i));
                                indices.Add((ushort)(pt1 + 1 + i));
                                indices.Add((ushort)(pt2 + i));
                                pt1++;
                            }
                            else
                            {
                                indices.Add((ushort)(pt1 + i));
                                indices.Add((ushort)(pt2 - 1 + i));
                                indices.Add((ushort)(pt2 + i));
                                pt2--;
                            }
                        }

                        // Bottom cap
                        pt1 = 0;
                        pt2 = sizeS - 1;

                        while (pt2 - pt1 > 1)
                        {
                            if (use_tri_1a2(profile, pt1, pt2))
                            {
                                indices.Add((ushort)pt1);
                                indices.Add((ushort)pt2);
                                indices.Add((ushort)(pt1 + 1));
                                pt1++;
                            }
                            else
                            {
                                indices.Add((ushort)pt1);
                                indices.Add((ushort)pt2);
                                indices.Add((ushort)(pt2 - 1));
                                pt2--;
                            }
                        }
                    }
                }
                else
                {
                    // Open solid
                    for (t = 0; t < sizeT - 1; t++)
                    {
                        // Outer face + 1 cut face
                        for (s = 0; s < sizeS - 1; s++)
                        {
                            i = s + t * sizeS;

                            indices.Add((ushort)i); // x,y
                            indices.Add((ushort)(i + 1)); // x+1,y
                            indices.Add((ushort)(i + sizeS)); // x,y+1

                            indices.Add((ushort)(i + sizeS)); // x,y+1
                            indices.Add((ushort)(i + 1)); // x+1,y
                            indices.Add((ushort)(i + sizeS + 1)); // x+1,y+1
                        }

                        // The other cut face
                        indices.Add((ushort)((sizeS - 1) + (t * sizeS))); // x,y
                        indices.Add((ushort)(0 + t * sizeS)); // x+1,y
                        indices.Add((ushort)((sizeS - 1) + (t + 1) * sizeS)); // x,y+1

                        indices.Add((ushort)((sizeS - 1) + (t + 1) * sizeS)); // x,y+1
                        indices.Add((ushort)(0 + (t * sizeS))); // x+1,y
                        indices.Add((ushort)(0 + (t + 1) * sizeS)); // x+1,y+1
                    }

                    // Do the top and bottom caps, if necessary
                    if (path.Open)
                    {
                        for (s = 0; s < sizeS - 2; s++)
                        {
                            indices.Add((ushort)(s + 1));
                            indices.Add((ushort)s);
                            indices.Add((ushort)(sizeS - 1));
                        }

                        // We've got a top cap
                        int offset = (sizeT - 1) * sizeS;

                        for (s = 0; s < sizeS - 2; s++)
                        {
                            // Inverted ordering from bottom cap
                            indices.Add((ushort)(offset + sizeS - 1));
                            indices.Add((ushort)(offset + s));
                            indices.Add((ushort)(offset + s + 1));
                        }
                    }
                }
            }
            else if (hollow)
            {
                // Closed hollow
                // Outer face
                for (t = 0; t < sizeT - 1; t++)
                {
                    for (s = 0; s < sizeSOut - 1; s++)
                    {
                        i = s + t * sizeS;

                        indices.Add((ushort)i); // x,y
                        indices.Add((ushort)(i + 1)); // x+1,y
                        indices.Add((ushort)(i + sizeS)); // x,y+1

                        indices.Add((ushort)(i + sizeS)); // x,y+1
                        indices.Add((ushort)(i + 1)); // x+1,y
                        indices.Add((ushort)(i + 1 + sizeS)); // x+1,y+1
                    }
                }

                // Inner face
                // Invert facing from outer face
                for (t = 0; t < sizeT - 1; t++)
                {
                    for (s = sizeSOut; s < sizeS - 1; s++)
                    {
                        i = s + t * sizeS;

                        indices.Add((ushort)i); // x,y
                        indices.Add((ushort)(i + 1)); // x+1,y
                        indices.Add((ushort)(i + sizeS)); // x,y+1

                        indices.Add((ushort)(i + sizeS)); // x,y+1
                        indices.Add((ushort)(i + 1)); // x+1,y
                        indices.Add((ushort)(i + 1 + sizeS)); // x+1,y+1
                    }
                }

                // Do the top and bottom caps, if necessary
                if (path.Open)
                {
                    // Top cap
                    int pt1 = 0;
                    int pt2 = sizeS - 1;
                    i = (sizeT - 1) * sizeS;

                    while (pt2 - pt1 > 1)
                    {
                        if (use_tri_1a2(profile, pt1, pt2))
                        {
                            indices.Add((ushort)(pt1 + i));
                            indices.Add((ushort)(pt2 + 1 + i));
                            indices.Add((ushort)(pt2 + i));
                            pt1++;
                        }
                        else
                        {
                            indices.Add((ushort)(pt1 + i));
                            indices.Add((ushort)(pt2 - 1 + i));
                            indices.Add((ushort)(pt2 + i));
                            pt2--;
                        }
                    }

                    // Bottom cap
                    pt1 = 0;
                    pt2 = sizeS - 1;

                    while (pt2 - pt1 > 1)
                    {
                        if (use_tri_1a2(profile, pt1, pt2))
                        {
                            indices.Add((ushort)pt1);
                            indices.Add((ushort)pt2);
                            indices.Add((ushort)(pt1 + 1));
                            pt1++;
                        }
                        else
                        {
                            indices.Add((ushort)pt1);
                            indices.Add((ushort)pt2);
                            indices.Add((ushort)(pt2 - 1));
                            pt2--;
                        }
                    }
                }
            }
            else
            {
                // Closed solid. Easy case
                for (t = 0; t < sizeT - 1; t++)
                {
                    for (s = 0; s < sizeS - 1; s++)
                    {
                        // Should wrap properly, but for now...
                        i = s + t * sizeS;

                        indices.Add((ushort)i); // x,y
                        indices.Add((ushort)(i + 1)); // x+1,y
                        indices.Add((ushort)(i + sizeS)); // x,y+1

                        indices.Add((ushort)(i + sizeS)); // x,y+1
                        indices.Add((ushort)(i + 1)); // x+1,y
                        indices.Add((ushort)(i + sizeS + 1)); // x+1,y+1
                    }
                }

                // Do the top and bottom caps, if necessary
                if (path.Open)
                {
                    // Bottom cap
                    for (s = 1; s < sizeS - 2; s++)
                    {
                        indices.Add((ushort)(s + 1));
                        indices.Add((ushort)s);
                        indices.Add((ushort)0);
                    }

                    // Top cap
                    int offset = (sizeT - 1) * sizeS;
                    for (s = 1; s < sizeS - 2; s++)
                    {
                        // Inverted ordering from bottom cap
                        indices.Add((ushort)offset);
                        indices.Add((ushort)(offset + s));
                        indices.Add((ushort)(offset + s + 1));
                    }
                }
            }

            return indices;
        }
コード例 #16
0
ファイル: GPLRenderer.cs プロジェクト: RavenB/gridsearch
        private static List<Vertex> GenerateVertices(LLObject.ObjectData prim, float detail, Path path, Profile profile)
        {
            int sizeS = path.Points.Count;
            int sizeT = profile.Positions.Count;

            // Generate vertex positions
            List<Vertex> vertices = new List<Vertex>(sizeT * sizeS);
            for (int i = 0; i < sizeT * sizeS; i++)
                vertices.Add(new Vertex());

            // Run along the path
            for (int s = 0; s < sizeS; ++s)
            {
                Vector2 scale = path.Points[s].Scale;
                Quaternion rot = path.Points[s].Rotation;
                Vector3 pos = path.Points[s].Position;

                // Run along the profile
                for (int t = 0; t < sizeT; ++t)
                {
                    int m = s * sizeT + t;
                    Vertex vertex = vertices[m];

                    vertex.Position.X = profile.Positions[t].X * scale.X;
                    vertex.Position.Y = profile.Positions[t].Y * scale.Y;
                    vertex.Position.Z = 0f;

                    vertex.Position *= rot;
                    vertex.Position += pos;

                    vertices[m] = vertex;
                }
            }

            return vertices;
        }
コード例 #17
0
ファイル: Face.cs プロジェクト: RavenB/gridsearch
        private static void CreateSide(ref Face face, LLObject.ObjectData prim, List<Vertex> primVertices, Path path,
            Profile profile)
        {
            bool flat = (face.Mask & FaceMask.Flat) != 0;

            int maxS = profile.Positions.Count;
            int s, t, i;
            float ss, tt;

            int numVertices = face.NumS * face.NumT;
            int numIndices = (face.NumS - 1) * (face.NumT - 1) * 6;

            face.Center = Vector3.Zero;

            int beginSTex = (int)Math.Floor(profile.Positions[face.BeginS].Z);
            int numS =
                (((face.Mask & FaceMask.Inner) != 0) && ((face.Mask & FaceMask.Flat) != 0) && face.NumS > 2) ?
                    face.NumS / 2 :
                    face.NumS;

            int curVertex = 0;

            // Copy the vertices into the array
            for (t = face.BeginT; t < face.BeginT + face.NumT; t++)
            {
                tt = path.Points[t].TexT;

                for (s = 0; s < numS; s++)
                {
                    if ((face.Mask & FaceMask.End) != 0)
                    {
                        if (s != 0)
                            ss = 1f;
                        else
                            ss = 0f;
                    }
                    else
                    {
                        // Get s value for tex-coord
                        if (!flat)
                            ss = profile.Positions[face.BeginS + s].Z;
                        else
                            ss = profile.Positions[face.BeginS + s].Z - beginSTex;
                    }

                    // Check to see if this triangle wraps around the array
                    if (face.BeginS + s >= maxS)
                        i = face.BeginS + s + maxS * (t - 1); // We're wrapping
                    else
                        i = face.BeginS + s + maxS * t;

                    Vertex vertex = new Vertex();
                    vertex.Position = primVertices[i].Position;
                    vertex.TexCoord = new Vector2(ss, tt);
                    vertex.Normal = Vector3.Zero;
                    vertex.Binormal = Vector3.Zero;

                    if (curVertex == 0)
                        face.MinExtent = face.MaxExtent = primVertices[i].Position;
                    else
                        UpdateMinMax(ref face, primVertices[i].Position);

                    face.Vertices.Add(vertex);
                    ++curVertex;

                    if (((face.Mask & FaceMask.Inner) != 0) && ((face.Mask & FaceMask.Flat) != 0) && face.NumS > 2 && s > 0)
                    {
                        vertex.Position = primVertices[i].Position;
                        //vertex.TexCoord = new Vector2(ss, tt);
                        //vertex.Normal = Vector3.Zero;
                        //vertex.Binormal = Vector3.Zero;

                        face.Vertices.Add(vertex);
                        ++curVertex;
                    }
                }

                if (((face.Mask & FaceMask.Inner) != 0) && ((face.Mask & FaceMask.Flat) != 0) && face.NumS > 2)
                {
                    if ((face.Mask & FaceMask.Open) != 0)
                        s = numS - 1;
                    else
                        s = 0;

                    i = face.BeginS + s + maxS * t;
                    ss = profile.Positions[face.BeginS + s].Z - beginSTex;

                    Vertex vertex = new Vertex();
                    vertex.Position = primVertices[i].Position;
                    vertex.TexCoord = new Vector2(ss, tt);
                    vertex.Normal = Vector3.Zero;
                    vertex.Binormal = Vector3.Zero;

                    UpdateMinMax(ref face, vertex.Position);

                    face.Vertices.Add(vertex);
                    ++curVertex;
                }
            }

            face.Center = (face.MinExtent + face.MaxExtent) * 0.5f;

            bool flatFace = ((face.Mask & FaceMask.Flat) != 0);

            // Now we generate the indices
            for (t = 0; t < (face.NumT - 1); t++)
            {
                for (s = 0; s < (face.NumS - 1); s++)
                {
                    face.Indices.Add((ushort)(s + face.NumS * t)); // Bottom left
                    face.Indices.Add((ushort)(s + 1 + face.NumS * (t + 1))); // Top right
                    face.Indices.Add((ushort)(s + face.NumS * (t + 1))); // Top left
                    face.Indices.Add((ushort)(s + face.NumS * t)); // Bottom left
                    face.Indices.Add((ushort)(s + 1 + face.NumS * t)); // Bottom right
                    face.Indices.Add((ushort)(s + 1 + face.NumS * (t + 1))); // Top right

                    face.Edge.Add((face.NumS - 1) * 2 * t + s * 2 + 1); // Bottom left/top right neighbor face

                    if (t < face.NumT - 2) // Top right/top left neighbor face
                        face.Edge.Add((face.NumS - 1) * 2 * (t + 1) + s * 2 + 1);
                    else if (face.NumT <= 3 || path.Open) // No neighbor
                        face.Edge.Add(-1);
                    else // Wrap on T
                        face.Edge.Add(s * 2 + 1);

                    if (s > 0) // Top left/bottom left neighbor face
                        face.Edge.Add((face.NumS - 1) * 2 * t + s * 2 - 1);
                    else if (flatFace || profile.Open) // No neighbor
                        face.Edge.Add(-1);
                    else // Wrap on S
                        face.Edge.Add((face.NumS - 1) * 2 * t + (face.NumS - 2) * 2 + 1);

                    if (t > 0) // Bottom left/bottom right neighbor face
                        face.Edge.Add((face.NumS - 1) * 2 * (t - 1) + s * 2);
                    else if (face.NumT <= 3 || path.Open) // No neighbor
                        face.Edge.Add(-1);
                    else // Wrap on T
                        face.Edge.Add((face.NumS - 1) * 2 * (face.NumT - 2) + s * 2);

                    if (s < face.NumS - 2) // Bottom right/top right neighbor face
                        face.Edge.Add((face.NumS - 1) * 2 * t + (s + 1) * 2);
                    else if (flatFace || profile.Open) // No neighbor
                        face.Edge.Add(-1);
                    else // Wrap on S
                        face.Edge.Add((face.NumS - 1) * 2 * t);

                    face.Edge.Add((face.NumS - 1) * 2 * t + s * 2); // Top right/bottom left neighbor face	
                }
            }

            // Generate normals, loop through each triangle
            for (i = 0; i < face.Indices.Count / 3; i++)
            {
                Vertex v0 = face.Vertices[face.Indices[i * 3 + 0]];
                Vertex v1 = face.Vertices[face.Indices[i * 3 + 1]];
                Vertex v2 = face.Vertices[face.Indices[i * 3 + 2]];

                // Calculate triangle normal
                Vector3 norm = (v0.Position - v1.Position) % (v0.Position - v2.Position);

                // Calculate binormal
                Vector3 binorm = CalcBinormalFromTriangle(v0.Position, v0.TexCoord, v1.Position, v1.TexCoord,
                    v2.Position, v2.TexCoord);

                // Add triangle normal to vertices
                for (int j = 0; j < 3; j++)
                {
                    Vertex vertex = face.Vertices[face.Indices[i * 3 + j]];
                    vertex.Normal += norm;
                    vertex.Binormal += binorm;
                    face.Vertices[face.Indices[i * 3 + j]] = vertex;
                }

                // Even out quad contributions
                if (i % 2 == 0)
                {
                    Vertex vertex = face.Vertices[face.Indices[i * 3 + 2]];
                    vertex.Normal += norm;
                    vertex.Binormal += binorm;
                    face.Vertices[face.Indices[i * 3 + 2]] = vertex;
                }
                else
                {
                    Vertex vertex = face.Vertices[face.Indices[i * 3 + 1]];
                    vertex.Normal += norm;
                    vertex.Binormal += binorm;
                    face.Vertices[face.Indices[i * 3 + 1]] = vertex;
                }
            }

            // Adjust normals based on wrapping and stitching
            Vector3 test1 =
                face.Vertices[0].Position -
                face.Vertices[face.NumS * (face.NumT - 2)].Position;

            Vector3 test2 =
                face.Vertices[face.NumS - 1].Position -
                face.Vertices[face.NumS * (face.NumT - 2) +
                face.NumS - 1].Position;

            bool sBottomConverges = (test1.LengthSquared() < 0.000001f);
            bool sTopConverges = (test2.LengthSquared() < 0.000001f);

            // TODO: Sculpt support
            Primitive.SculptType sculptType = Primitive.SculptType.None;

            if (sculptType == Primitive.SculptType.None)
            {
                if (!path.Open)
                {
                    // Wrap normals on T
                    for (i = 0; i < face.NumS; i++)
                    {
                        Vector3 norm = face.Vertices[i].Normal + face.Vertices[face.NumS * (face.NumT - 1) + i].Normal;

                        Vertex vertex = face.Vertices[i];
                        vertex.Normal = norm;
                        face.Vertices[i] = vertex;

                        vertex = face.Vertices[face.NumS * (face.NumT - 1) + i];
                        vertex.Normal = norm;
                        face.Vertices[face.NumS * (face.NumT - 1) + i] = vertex;
                    }
                }

                if (!profile.Open && !sBottomConverges)
                {
                    // Wrap normals on S
                    for (i = 0; i < face.NumT; i++)
                    {
                        Vector3 norm = face.Vertices[face.NumS * i].Normal + face.Vertices[face.NumS * i + face.NumS - 1].Normal;

                        Vertex vertex = face.Vertices[face.NumS * i];
                        vertex.Normal = norm;
                        face.Vertices[face.NumS * i] = vertex;

                        vertex = face.Vertices[face.NumS * i + face.NumS - 1];
                        vertex.Normal = norm;
                        face.Vertices[face.NumS * i + face.NumS - 1] = vertex;
                    }
                }

                if (prim.PathCurve == LLObject.PathCurve.Circle &&
                    prim.ProfileCurve == LLObject.ProfileCurve.HalfCircle)
                {
                    if (sBottomConverges)
                    {
                        // All lower S have same normal
                        Vector3 unitX = new Vector3(1f, 0f, 0f);

                        for (i = 0; i < face.NumT; i++)
                        {
                            Vertex vertex = face.Vertices[face.NumS * i];
                            vertex.Normal = unitX;
                            face.Vertices[face.NumS * i] = vertex;
                        }
                    }

                    if (sTopConverges)
                    {
                        // All upper S have same normal
                        Vector3 negUnitX = new Vector3(-1f, 0f, 0f);

                        for (i = 0; i < face.NumT; i++)
                        {
                            Vertex vertex = face.Vertices[face.NumS * i + face.NumS - 1];
                            vertex.Normal = negUnitX;
                            face.Vertices[face.NumS * i + face.NumS - 1] = vertex;
                        }
                    }
                }
            }
            else
            {
                // FIXME: Sculpt support
            }

            // Normalize normals and binormals
            for (i = 0; i < face.Vertices.Count; i++)
            {
                Vertex vertex = face.Vertices[i];
                vertex.Normal.Normalize();
                vertex.Binormal.Normalize();
                face.Vertices[i] = vertex;
            }
        }
コード例 #18
0
ファイル: GPLRenderer.cs プロジェクト: RavenB/gridsearch
        private static Path GeneratePathPolygon(LLObject.ObjectData prim, int sides, float startOff, float endScale,
            float twistScale)
        {
            Path path = new Path();
            path.Points = new List<PathPoint>();

            float revolutions = prim.PathRevolutions;
            float skew = prim.PathSkew;
            float skewMag = (float)Math.Abs(skew);
            float holeX = prim.PathScaleX * (1f - skewMag);
            float holeY = prim.PathScaleY;

            // Calculate taper begin/end for x,y (Negative means taper the beginning)
            float taperXBegin = 1f;
            float taperXEnd = 1f - prim.PathTaperX;
            float taperYBegin = 1f;
            float taperYEnd = 1f - prim.PathTaperY;

            if (taperXEnd > 1f)
            {
                // Flip tapering
                taperXBegin = 2f - taperXEnd;
                taperXEnd = 1f;
            }
            if (taperYEnd > 1f)
            {
                // Flip tapering
                taperYBegin = 2f - taperYEnd;
                taperYEnd = 1f;
            }

            // For spheres, the radius is usually zero
            float radiusStart = 0.5f;
            if (sides < 8)
                radiusStart = TABLE_SCALE[sides];

            // Scale the radius to take the hole size into account
            radiusStart *= 1f - holeY;

            // Now check the radius offset to calculate the start,end radius. (Negative means
            // decrease the start radius instead)
            float radiusEnd = radiusStart;
            float radiusOffset = prim.PathRadiusOffset;
            if (radiusOffset < 0f)
                radiusStart *= 1f + radiusOffset;
            else
                radiusEnd *= 1f - radiusOffset;

            // Is the path NOT a closed loop?
            path.Open =
                ((prim.PathEnd * endScale - prim.PathBegin < 1f) ||
                (skewMag > 0.001f) ||
                (Math.Abs(taperXEnd - taperXBegin) > 0.001d) ||
                (Math.Abs(taperYEnd - taperYBegin) > 0.001d) ||
                (Math.Abs(radiusEnd - radiusStart) > 0.001d));

            float ang, c, s;
            Quaternion twist = Quaternion.Identity;
            Quaternion qang = Quaternion.Identity;
            PathPoint point;
            Vector3 pathAxis = new Vector3(1f, 0f, 0f);
            float twistBegin = prim.PathTwistBegin * twistScale;
            float twistEnd = prim.PathTwist * twistScale;

            // We run through this once before the main loop, to make sure
            // the path begins at the correct cut
            float step = 1f / sides;
            float t = prim.PathBegin;
            ang = 2f * F_PI * revolutions * t;
            s = (float)Math.Sin(ang) * MathHelper.Lerp(radiusStart, radiusEnd, t);
            c = (float)Math.Cos(ang) * MathHelper.Lerp(radiusStart, radiusEnd, t);

            point = new PathPoint();
            point.Position = new Vector3(
                0 + MathHelper.Lerp(0, prim.PathShearX, s) +
                0 + MathHelper.Lerp(-skew, skew, t) * 0.5f,
                c + MathHelper.Lerp(0, prim.PathShearY, s),
                s);
            point.Scale.X = holeX * MathHelper.Lerp(taperXBegin, taperXEnd, t);
            point.Scale.Y = holeY * MathHelper.Lerp(taperYBegin, taperYEnd, t);
            point.TexT = t;

            // Twist rotates the path along the x,y plane
            twist = Quaternion.CreateFromAxisAngle(MathHelper.Lerp(twistBegin, twistEnd, t) * 2f * F_PI - F_PI, 0f, 0f, 1f);
            // Rotate the point around the circle's center
            qang = Quaternion.CreateFromAxisAngle(pathAxis, ang);
            point.Rotation = twist * qang;

            path.Points.Add(point);
            t += step;

            // Snap to a quantized parameter, so that cut does not
            // affect most sample points
            t = ((int)(t * sides)) / (float)sides;

            // Run through the non-cut dependent points
            point = new PathPoint();
            while (t < prim.PathEnd)
            {
                ang = 2f * F_PI * revolutions * t;
                c = (float)Math.Cos(ang) * MathHelper.Lerp(radiusStart, radiusEnd, t);
                s = (float)Math.Sin(ang) * MathHelper.Lerp(radiusStart, radiusEnd, t);

                point.Position = new Vector3(
                    0 + MathHelper.Lerp(0, prim.PathShearX, s) +
                    0 + MathHelper.Lerp(-skew, skew, t) * 0.5f,
                    c + MathHelper.Lerp(0, prim.PathShearY, s),
                    s);

                point.Scale.X = holeX * MathHelper.Lerp(taperXBegin, taperXEnd, t);
                point.Scale.Y = holeY * MathHelper.Lerp(taperYBegin, taperYEnd, t);
                point.TexT = t;

                // Twist rotates the path along the x,y plane
                twist = Quaternion.CreateFromAxisAngle(MathHelper.Lerp(twistBegin, twistEnd, t) * 2f * F_PI - F_PI, 0f, 0f, 1f);
                // Rotate the point around the circle's center
                qang = Quaternion.CreateFromAxisAngle(pathAxis, ang);
                point.Rotation = twist * qang;

                path.Points.Add(point);
                t += step;
            }

            // Make one final pass for the end cut
            t = prim.PathEnd;
            point = new PathPoint();
            ang = 2f * F_PI * revolutions * t;
            c = (float)Math.Cos(ang) * MathHelper.Lerp(radiusStart, radiusEnd, t);
            s = (float)Math.Sin(ang) * MathHelper.Lerp(radiusStart, radiusEnd, t);

            point.Position = new Vector3(
                MathHelper.Lerp(0, prim.PathShearX, s) + MathHelper.Lerp(-skew, skew, t) * 0.5f,
                c + MathHelper.Lerp(0, prim.PathShearY, s),
                s);
            point.Scale.X = holeX * MathHelper.Lerp(taperXBegin, taperXEnd, t);
            point.Scale.Y = holeY * MathHelper.Lerp(taperYBegin, taperYEnd, t);
            point.TexT = t;

            // Twist rotates the path along the x,y plane
            twist = Quaternion.CreateFromAxisAngle(MathHelper.Lerp(twistBegin, twistEnd, t) * 2f * F_PI - F_PI, 0f, 0f, 1f);
            qang = Quaternion.CreateFromAxisAngle(pathAxis, ang);
            point.Rotation = twist * qang;

            path.Points.Add(point);

            return path;
        }