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"); } }
private Path GeneratePath() { Path path = new Path(); path.Points = new List<PathPoint>(); return path; }
private static void CreateUnCutCubeCap(ref Face face, List<Vertex> primVertices, Path path, Profile profile) { int maxS = profile.Positions.Count; int maxT = path.Points.Count; int gridSize = (profile.Positions.Count - 1) / 4; int quadCount = gridSize * gridSize; //int numVertices = (gridSize + 1) * (gridSize + 1); //int numIndices = quadCount * 4; int offset = 0; if ((face.Mask & FaceMask.Top) != 0) offset = (maxT - 1) * maxS; else offset = face.BeginS; Vertex[] corners = new Vertex[4]; Vertex baseVert; for (int t = 0; t < 4; t++) { corners[t].Position = primVertices[offset + (gridSize * t)].Position; corners[t].TexCoord.X = profile.Positions[gridSize * t].X + 0.5f; corners[t].TexCoord.Y = 0.5f - profile.Positions[gridSize * t].Y; } baseVert.Normal = ((corners[1].Position - corners[0].Position) % (corners[2].Position - corners[1].Position)); baseVert.Normal = Vector3.Normalize(baseVert.Normal); if ((face.Mask & FaceMask.Top) != 0) { baseVert.Normal *= -1f; } else { // Swap the UVs on the U(X) axis for top face Vector2 swap; swap = corners[0].TexCoord; corners[0].TexCoord = corners[3].TexCoord; corners[3].TexCoord = swap; swap = corners[1].TexCoord; corners[1].TexCoord = corners[2].TexCoord; corners[2].TexCoord = swap; } baseVert.Binormal = CalcBinormalFromTriangle( corners[0].Position, corners[0].TexCoord, corners[1].Position, corners[1].TexCoord, corners[2].Position, corners[2].TexCoord); for (int t = 0; t < 4; t++) { corners[t].Binormal = baseVert.Binormal; corners[t].Normal = baseVert.Normal; } int vtop = face.Vertices.Count; for (int gx = 0; gx < gridSize + 1; gx++) { for (int gy = 0; gy < gridSize + 1; gy++) { Vertex newVert = new Vertex(); LerpPlanarVertex( corners[0], corners[1], corners[3], ref newVert, (float)gx / (float)gridSize, (float)gy / (float)gridSize); face.Vertices.Add(newVert); if (gx == 0 && gy == 0) face.MinExtent = face.MaxExtent = newVert.Position; else UpdateMinMax(ref face, newVert.Position); } } face.Center = (face.MinExtent + face.MaxExtent) * 0.5f; int[] idxs = new int[] { 0, 1, gridSize + 2, gridSize + 2, gridSize + 1, 0 }; for (int gx = 0; gx < gridSize; gx++) { for (int gy = 0; gy < gridSize; gy++) { if ((face.Mask & FaceMask.Top) != 0) { for (int i = 5; i >= 0; i--) face.Indices.Add((ushort)(vtop + (gy * (gridSize + 1)) + gx + idxs[i])); } else { for (int i = 0; i < 6; i++) face.Indices.Add((ushort)(vtop + (gy * (gridSize + 1)) + gx + idxs[i])); } } } }
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; } }
private static void CreateCap(ref Face face, List<Vertex> primVertices, Path path, Profile profile) { int i; int numVertices = profile.Positions.Count; //int numIndices = (numVertices - 2) * 3; int maxS = profile.Positions.Count; int maxT = path.Points.Count; face.Center = Vector3.Zero; int offset = 0; if ((face.Mask & FaceMask.Top) != 0) offset = (maxT - 1) * maxS; else offset = face.BeginS; // Figure out the normal, assume all caps are flat faces. // Cross product to get normals Vector2 cuv; Vector2 minUV = Vector2.Zero; Vector2 maxUV = Vector2.Zero; // Copy the vertices into the array for (i = 0; i < numVertices; i++) { Vertex vertex = new Vertex(); if ((face.Mask & FaceMask.Top) != 0) { vertex.Position = primVertices[i + offset].Position; vertex.TexCoord.X = profile.Positions[i].X + 0.5f; vertex.TexCoord.Y = profile.Positions[i].Y + 0.5f; } else { // Mirror for underside vertex.Position = primVertices[(numVertices - 1) - i].Position; vertex.TexCoord.X = profile.Positions[i].X + 0.5f; vertex.TexCoord.Y = 0.5f - profile.Positions[i].Y; } if (i == 0) { face.MinExtent = face.MaxExtent = primVertices[offset].Position; minUV = maxUV = primVertices[offset].TexCoord; } else { UpdateMinMax(ref face, vertex.Position); UpdateMinMax(ref minUV, ref maxUV, vertex.TexCoord); } face.Vertices.Add(vertex); } face.Center = (face.MinExtent + face.MaxExtent) * 0.5f; cuv = (minUV + maxUV) * 0.5f; Vector3 binormal = CalcBinormalFromTriangle( face.Center, cuv, face.Vertices[0].Position, face.Vertices[0].TexCoord, face.Vertices[1].Position, face.Vertices[1].TexCoord); binormal.Normalize(); Vector3 d0 = face.Center - face.Vertices[0].Position; Vector3 d1 = face.Center - face.Vertices[1].Position; Vector3 normal = ((face.Mask & FaceMask.Top) != 0) ? (d0 % d1) : (d1 % d0); normal.Normalize(); // If not hollow and not open create a center point in the cap if ((face.Mask & FaceMask.Hollow) == 0 && (face.Mask & FaceMask.Open) == 0) { Vertex vertex = new Vertex(); vertex.Position = face.Center; vertex.Normal = normal; vertex.Binormal = binormal; vertex.TexCoord = cuv; face.Vertices.Add(vertex); numVertices++; } for (i = 0; i < numVertices; i++) { Vertex vertex = face.Vertices[i]; vertex.Binormal = binormal; vertex.Normal = normal; face.Vertices[i] = vertex; } if ((face.Mask & FaceMask.Hollow) != 0) { if ((face.Mask & FaceMask.Top) != 0) { // HOLLOW TOP int pt1 = 0; int pt2 = numVertices - 1; i = 0; while (pt2 - pt1 > 1) { if (use_tri_1a2(profile, pt1, pt2)) { face.Indices.Add((ushort)pt1); face.Indices.Add((ushort)(pt1 + 1)); face.Indices.Add((ushort)pt2); pt1++; } else { face.Indices.Add((ushort)pt1); face.Indices.Add((ushort)(pt2 - 1)); face.Indices.Add((ushort)pt2); pt2--; } } } else { // HOLLOW BOTTOM int pt1 = 0; int pt2 = numVertices - 1; i = 0; while (pt2 - pt1 > 1) { // Flipped backfacing from top if (use_tri_1a2(profile, pt1, pt2)) { face.Indices.Add((ushort)pt1); face.Indices.Add((ushort)pt2); face.Indices.Add((ushort)(pt1 + 1)); pt1++; } else { face.Indices.Add((ushort)pt1); face.Indices.Add((ushort)pt2); face.Indices.Add((ushort)(pt2 - 1)); pt2--; } } } } else { // SOLID OPEN TOP // SOLID CLOSED TOP // SOLID OPEN BOTTOM // SOLID CLOSED BOTTOM // Not hollow, generate the triangle fan. // This is a tri-fan, so we reuse the same first point for all triangles for (i = 0; i < numVertices - 2; i++) { face.Indices.Add((ushort)(numVertices - 1)); face.Indices.Add((ushort)i); face.Indices.Add((ushort)(i + 1)); } } }
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; }
private static List<Face> CreateVolumeFaces(Primitive prim, Path path, Profile profile, List<Vertex> vertices) { int numFaces = profile.Faces.Count; List<Face> faces = new List<Face>(numFaces); // Initialize faces with parameter data for (int i = 0; i < numFaces; i++) { ProfileFace pf = profile.Faces[i]; Face face = new Face(); face.Vertices = new List<Vertex>(); face.Indices = new List<ushort>(); face.Edge = new List<int>(); face.BeginS = pf.Index; face.NumS = pf.Count; face.BeginT = 0; face.NumT = path.Points.Count; face.ID = i; // Set the type mask bits correctly if (prim.Data.ProfileHollow > 0f) face.Mask |= FaceMask.Hollow; if (profile.Open) face.Mask |= FaceMask.Open; if (pf.Cap) { face.Mask |= FaceMask.Cap; if (pf.Type == FaceType.PathBegin) face.Mask |= FaceMask.Top; else face.Mask |= FaceMask.Bottom; } else if (pf.Type == FaceType.ProfileBegin || pf.Type == FaceType.ProfileEnd) { face.Mask |= FaceMask.Flat; face.Mask |= FaceMask.End; } else { face.Mask |= FaceMask.Side; if (pf.Flat) face.Mask |= FaceMask.Flat; if (pf.Type == FaceType.InnerSide) { face.Mask |= FaceMask.Inner; if (pf.Flat && face.NumS > 2) face.NumS *= 2; // Flat inner faces have to copy vert normals } else { face.Mask |= FaceMask.Outer; } } faces.Add(face); } for (int i = 0; i < faces.Count; i++) { Face face = faces[i]; BuildFace(ref face, prim.Data, vertices, path, profile, prim.Textures.GetFace((uint)i)); faces[i] = face; } return faces; }
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; }
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; }
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; }