diff --git a/OpenMetaverse.Rendering.GPL/Face.cs b/OpenMetaverse.Rendering.GPL/Face.cs new file mode 100644 index 00000000..973b3075 --- /dev/null +++ b/OpenMetaverse.Rendering.GPL/Face.cs @@ -0,0 +1,685 @@ +/* + * This file is part of OpenMetaverse.Rendering.GPL. + * + * libprimrender is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 as + * published by the Free Software Foundation. + * + * libprimrender is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenMetaverse.Rendering.GPL. If not, see + * . + */ + + +using System; +using System.Collections.Generic; + +namespace OpenMetaverse.Rendering +{ + public partial class GPLRenderer : IRendering + { + private static void BuildFace(ref Face face, LLObject.ObjectData prim, List 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 static void CreateUnCutCubeCap(ref Face face, List 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 = LLVector3.Norm(baseVert.Normal); + + if ((face.Mask & FaceMask.Top) != 0) + { + baseVert.Normal *= -1f; + } + else + { + // Swap the UVs on the U(X) axis for top face + LLVector2 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 CreateCap(ref Face face, List 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 = LLVector3.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 + LLVector2 cuv; + LLVector2 minUV = LLVector2.Zero; + LLVector2 maxUV = LLVector2.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; + + LLVector3 binormal = CalcBinormalFromTriangle( + face.Center, cuv, + face.Vertices[0].Position, face.Vertices[0].TexCoord, + face.Vertices[1].Position, face.Vertices[1].TexCoord); + binormal = LLVector3.Norm(binormal); + + LLVector3 d0 = face.Center - face.Vertices[0].Position; + LLVector3 d1 = face.Center - face.Vertices[1].Position; + LLVector3 normal = ((face.Mask & FaceMask.Top) != 0) ? (d0 % d1) : (d1 % d0); + normal = LLVector3.Norm(normal); + + // 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 void CreateSide(ref Face face, LLObject.ObjectData prim, List 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 = LLVector3.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 LLVector2(ss, tt); + vertex.Normal = LLVector3.Zero; + vertex.Binormal = LLVector3.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 LLVector2(ss, tt); + //vertex.Normal = LLVector3.Zero; + //vertex.Binormal = LLVector3.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 LLVector2(ss, tt); + vertex.Normal = LLVector3.Zero; + vertex.Binormal = LLVector3.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 + LLVector3 norm = (v0.Position - v1.Position) % (v0.Position - v2.Position); + + // Calculate binormal + LLVector3 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 + bool sBottomConverges = ( + LLVector3.MagSquared( + face.Vertices[0].Position - + face.Vertices[face.NumS * (face.NumT - 2)].Position + ) < 0.000001f); + bool sTopConverges = ( + LLVector3.MagSquared( + face.Vertices[face.NumS - 1].Position - + face.Vertices[face.NumS * (face.NumT - 2) + + face.NumS - 1].Position + ) < 0.000001f); + Primitive.SculptType sculptType = Primitive.SculptType.None; // TODO: Sculpt support + + if (sculptType == Primitive.SculptType.None) + { + if (!path.Open) + { + // Wrap normals on T + for (i = 0; i < face.NumS; i++) + { + LLVector3 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++) + { + LLVector3 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 + LLVector3 unitX = new LLVector3(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 + LLVector3 negUnitX = new LLVector3(-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 = LLVector3.Norm(vertex.Normal); + vertex.Binormal = LLVector3.Norm(vertex.Binormal); + face.Vertices[i] = vertex; + } + } + + private static void LerpPlanarVertex(Vertex v0, Vertex v1, Vertex v2, ref Vertex vout, float coef01, float coef02) + { + vout.Position = v0.Position + ((v1.Position - v0.Position) * coef01) + ((v2.Position - v0.Position) * coef02); + vout.TexCoord = v0.TexCoord + ((v1.TexCoord - v0.TexCoord) * coef01) + ((v2.TexCoord - v0.TexCoord) * coef02); + vout.Normal = v0.Normal; + vout.Binormal = v0.Binormal; + } + + private static void UpdateMinMax(ref Face face, LLVector3 position) + { + if (face.MinExtent.X > position.X) + face.MinExtent.X = position.X; + if (face.MinExtent.Y > position.Y) + face.MinExtent.Y = position.Y; + if (face.MinExtent.Z > position.Z) + face.MinExtent.Z = position.Z; + + if (face.MaxExtent.X < position.X) + face.MaxExtent.X = position.X; + if (face.MaxExtent.Y < position.Y) + face.MaxExtent.Y = position.Y; + if (face.MaxExtent.Z < position.Z) + face.MaxExtent.Z = position.Z; + } + + private static void UpdateMinMax(ref LLVector2 min, ref LLVector2 max, LLVector2 current) + { + if (min.X > current.X) + min.X = current.X; + if (min.Y > current.Y) + min.Y = current.Y; + + if (max.X < current.X) + max.X = current.X; + if (max.Y < current.Y) + max.Y = current.Y; + } + + private static LLVector3 CalcBinormalFromTriangle(LLVector3 pos0, LLVector2 tex0, LLVector3 pos1, + LLVector2 tex1, LLVector3 pos2, LLVector2 tex2) + { + LLVector3 rx0 = new LLVector3(pos0.X, tex0.X, tex0.Y); + LLVector3 rx1 = new LLVector3(pos1.X, tex1.X, tex1.Y); + LLVector3 rx2 = new LLVector3(pos2.X, tex2.X, tex2.Y); + + LLVector3 ry0 = new LLVector3(pos0.Y, tex0.X, tex0.Y); + LLVector3 ry1 = new LLVector3(pos1.Y, tex1.X, tex1.Y); + LLVector3 ry2 = new LLVector3(pos2.Y, tex2.X, tex2.Y); + + LLVector3 rz0 = new LLVector3(pos0.Z, tex0.X, tex0.Y); + LLVector3 rz1 = new LLVector3(pos1.Z, tex1.X, tex1.Y); + LLVector3 rz2 = new LLVector3(pos2.Z, tex2.X, tex2.Y); + + LLVector3 r0 = (rx0 - rx1) % (rx0 - rx2); + LLVector3 r1 = (ry0 - ry1) % (ry0 - ry2); + LLVector3 r2 = (rz0 - rz1) % (rz0 - rz2); + + if (r0.X != 0f && r1.X != 0f && r2.X != 0f) + { + return new LLVector3( + -r0.Z / r0.X, + -r1.Z / r1.X, + -r2.Z / r2.X); + } + else + { + return new LLVector3(0f, 1f, 0f); + } + } + } +} diff --git a/OpenMetaverse.Rendering.GPL/GPLRenderer.cs b/OpenMetaverse.Rendering.GPL/GPLRenderer.cs new file mode 100644 index 00000000..50f42967 --- /dev/null +++ b/OpenMetaverse.Rendering.GPL/GPLRenderer.cs @@ -0,0 +1,1209 @@ +/* + * This file is part of OpenMetaverse.Rendering.GPL. + * + * libprimrender is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 as + * published by the Free Software Foundation. + * + * libprimrender is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenMetaverse.Rendering.GPL. If not, see + * . + */ + + +using System; +using System.Collections.Generic; + +namespace OpenMetaverse.Rendering +{ + [RendererName("GPL Renderer")] + public partial class GPLRenderer : IRendering + { + private const float F_PI = (float)Math.PI; + private const int MIN_DETAIL_FACES = 6; + private const int MIN_LOD = 0; + private const int MAX_LOD = 3; + + private const int SCULPT_REZ_1 = 6; + private const int SCULPT_REZ_2 = 8; + private const int SCULPT_REZ_3 = 16; + private const int SCULPT_REZ_4 = 32; + + private const float SCULPT_MIN_AREA = 0.002f; + + private static readonly float[] DETAIL_LEVELS = new float[] { 1f, 1.5f, 2.5f, 4f }; + private static readonly float[] TABLE_SCALE = new float[] { 1f, 1f, 1f, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f }; + + public SimpleMesh GenerateSimpleMesh(Primitive prim, DetailLevel lod) + { + float detail = DETAIL_LEVELS[(int)lod]; + + Path path = GeneratePath(prim.Data, detail); + Profile profile = GenerateProfile(prim.Data, path, detail); + + SimpleMesh mesh = new SimpleMesh(); + mesh.Prim = prim; + mesh.Path = path; + mesh.Profile = profile; + mesh.Vertices = GenerateVertices(prim.Data, detail, path, profile); + mesh.Indices = GenerateIndices(prim.Data, path, profile); + + return mesh; + } + + public FacetedMesh GenerateFacetedMesh(Primitive prim, DetailLevel lod) + { + float detail = DETAIL_LEVELS[(int)lod]; + + Path path = GeneratePath(prim.Data, detail); + Profile profile = GenerateProfile(prim.Data, path, detail); + + List vertices = GenerateVertices(prim.Data, detail, path, profile); + + FacetedMesh mesh = new FacetedMesh(); + mesh.Prim = prim; + mesh.Path = path; + mesh.Profile = profile; + mesh.Faces = CreateVolumeFaces(prim, path, profile, vertices); + + return mesh; + } + + private static List CreateVolumeFaces(Primitive prim, Path path, + Profile profile, List vertices) + { + int numFaces = profile.Faces.Count; + List faces = new List(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(); + face.Indices = new List(); + face.Edge = new List(); + + 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 List GenerateVertices(LLObject.ObjectData prim, float detail, Path path, Profile profile) + { + int sizeS = path.Points.Count; + int sizeT = profile.Positions.Count; + + // Generate vertex positions + List vertices = new List(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) + { + LLVector2 scale = path.Points[s].Scale; + LLQuaternion rot = path.Points[s].Rotation; + LLVector3 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; + } + + private static List 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 indices = new List(); + + 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 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 + LLVector3 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 + LLVector3 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; + LLVector3 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 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++) + { + LLVector3 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); + } + + 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(np); + + step = 1f / (np - 1); + + LLVector2 startScale = prim.PathBeginScale; + LLVector2 endScale = prim.PathEndScale; + + for (int i = 0; i < np; i++) + { + PathPoint point = new PathPoint(); + + float t = Helpers.Lerp(prim.PathBegin, prim.PathEnd, (float)i * step); + point.Position = new LLVector3( + Helpers.Lerp(0, prim.PathShearX, t), + Helpers.Lerp(0, prim.PathShearY, t), + t - 0.5f); + point.Rotation.SetQuaternion(Helpers.Lerp(F_PI * prim.PathTwistBegin, F_PI * prim.PathTwist, t), 0f, 0f, 1f); + point.Scale.X = Helpers.Lerp(startScale.X, endScale.X, t); + point.Scale.Y = Helpers.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; + } + + 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(); + profile.Faces = new List(); + + float scale = 0.5f; + float t, tStep, tFirst, tFraction, ang, angStep; + LLVector3 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 = Helpers.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 LLVector3((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 LLVector3((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) + { + LLVector3 newPt = Helpers.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 LLVector3((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 LLVector3((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) + { + LLVector3 newPt = Helpers.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(LLVector3.Zero); + } + else + { + // The profile isn't open + profile.Open = false; + profile.Concave = false; + } + + return profile; + } + + private static Path GeneratePathPolygon(LLObject.ObjectData prim, int sides) + { + return GeneratePathPolygon(prim, sides, 0f, 1f, 1f); + } + + private static Path GeneratePathPolygon(LLObject.ObjectData prim, int sides, float startOff, float endScale, + float twistScale) + { + Path path = new Path(); + path.Points = new List(); + + 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; + LLQuaternion twist = LLQuaternion.Identity; + LLQuaternion qang = LLQuaternion.Identity; + PathPoint point; + LLVector3 pathAxis = new LLVector3(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) * Helpers.Lerp(radiusStart, radiusEnd, t); + c = (float)Math.Cos(ang) * Helpers.Lerp(radiusStart, radiusEnd, t); + + point = new PathPoint(); + point.Position = new LLVector3( + 0 + Helpers.Lerp(0, prim.PathShearX, s) + + 0 + Helpers.Lerp(-skew, skew, t) * 0.5f, + c + Helpers.Lerp(0, prim.PathShearY, s), + s); + point.Scale.X = holeX * Helpers.Lerp(taperXBegin, taperXEnd, t); + point.Scale.Y = holeY * Helpers.Lerp(taperYBegin, taperYEnd, t); + point.TexT = t; + + // Twist rotates the path along the x,y plane + twist.SetQuaternion(Helpers.Lerp(twistBegin, twistEnd, t) * 2f * F_PI - F_PI, 0f, 0f, 1f); + // Rotate the point around the circle's center + qang.SetQuaternion(ang, pathAxis); + 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) * Helpers.Lerp(radiusStart, radiusEnd, t); + s = (float)Math.Sin(ang) * Helpers.Lerp(radiusStart, radiusEnd, t); + + point.Position = new LLVector3( + 0 + Helpers.Lerp(0, prim.PathShearX, s) + + 0 + Helpers.Lerp(-skew, skew, t) * 0.5f, + c + Helpers.Lerp(0, prim.PathShearY, s), + s); + + point.Scale.X = holeX * Helpers.Lerp(taperXBegin, taperXEnd, t); + point.Scale.Y = holeY * Helpers.Lerp(taperYBegin, taperYEnd, t); + point.TexT = t; + + // Twist rotates the path along the x,y plane + twist.SetQuaternion(Helpers.Lerp(twistBegin, twistEnd, t) * 2f * F_PI - F_PI, 0f, 0f, 1f); + // Rotate the point around the circle's center + qang.SetQuaternion(ang, pathAxis); + 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) * Helpers.Lerp(radiusStart, radiusEnd, t); + s = (float)Math.Sin(ang) * Helpers.Lerp(radiusStart, radiusEnd, t); + + point.Position = new LLVector3( + Helpers.Lerp(0, prim.PathShearX, s) + Helpers.Lerp(-skew, skew, t) * 0.5f, + c + Helpers.Lerp(0, prim.PathShearY, s), + s); + point.Scale.X = holeX * Helpers.Lerp(taperXBegin, taperXEnd, t); + point.Scale.Y = holeY * Helpers.Lerp(taperYBegin, taperYEnd, t); + point.TexT = t; + + // Twist rotates the path along the x,y plane + twist.SetQuaternion(Helpers.Lerp(twistBegin, twistEnd, t) * 2f * F_PI - F_PI, 0f, 0f, 1f); + qang.SetQuaternion(ang, pathAxis); + point.Rotation = twist * qang; + + path.Points.Add(point); + + return path; + } + + private static ProfileFace CreateProfileCap(FaceType type, int count) + { + ProfileFace face = new ProfileFace(); + face.Index = 0; + face.Count = count; + face.ScaleU = 1f; + face.Cap = true; + face.Type = type; + + return face; + } + + private static ProfileFace CreateProfileFace(int index, int count, float scaleU, FaceType type, bool flat) + { + ProfileFace face = new ProfileFace(); + face.Index = index; + face.Count = count; + face.ScaleU = scaleU; + face.Flat = flat; + face.Cap = false; + face.Type = type; + + return face; + } + + private static bool use_tri_1a2(Profile profilePoints, int pt1, int pt2) + { + // Use the profile points instead of the mesh, since you want + // the un-transformed profile distances + LLVector3 p1 = profilePoints.Positions[pt1]; + LLVector3 p2 = profilePoints.Positions[pt2]; + LLVector3 pa = profilePoints.Positions[pt1 + 1]; + LLVector3 pb = profilePoints.Positions[pt2 - 1]; + + p1.Z = 0f; + p2.Z = 0f; + pa.Z = 0f; + pb.Z = 0f; + + // Use area of triangle to determine backfacing + float area_1a2, area_1ba, area_21b, area_2ab; + + area_1a2 = + (p1.X * pa.Y - pa.X * p1.Y) + + (pa.X * p2.Y - p2.X * pa.Y) + + (p2.X * p1.Y - p1.X * p2.Y); + + area_1ba = + (p1.X * pb.Y - pb.X * p1.Y) + + (pb.X * pa.Y - pa.X * pb.Y) + + (pa.X * p1.Y - p1.X * pa.Y); + + area_21b = + (p2.X * p1.Y - p1.X * p2.Y) + + (p1.X * pb.Y - pb.X * p1.Y) + + (pb.X * p2.Y - p2.X * pb.Y); + + area_2ab = + (p2.X * pa.Y - pa.X * p2.Y) + + (pa.X * pb.Y - pb.X * pa.Y) + + (pb.X * p2.Y - p2.X * pb.Y); + + bool use_tri_1a2 = true; + bool tri_1a2 = true; + bool tri_21b = true; + + if (area_1a2 < 0) + tri_1a2 = false; + if (area_2ab < 0) + tri_1a2 = false; // Can't use, because it contains point b + if (area_21b < 0) + tri_21b = false; + if (area_1ba < 0) + tri_21b = false; // Can't use, because it contains point b + + if (!tri_1a2) + { + use_tri_1a2 = false; + } + else if (!tri_21b) + { + use_tri_1a2 = true; + } + else + { + LLVector3 d1 = p1 - pa; + LLVector3 d2 = p2 - pb; + + if (LLVector3.MagSquared(d1) < LLVector3.MagSquared(d2)) + use_tri_1a2 = true; + else + use_tri_1a2 = false; + } + + return use_tri_1a2; + } + } +} diff --git a/OpenMetaverse.Rendering.GPL/Texture.cs b/OpenMetaverse.Rendering.GPL/Texture.cs new file mode 100644 index 00000000..60670f9c --- /dev/null +++ b/OpenMetaverse.Rendering.GPL/Texture.cs @@ -0,0 +1,119 @@ +/* + * This file is part of OpenMetaverse.Rendering.GPL. + * + * libprimrender is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 as + * published by the Free Software Foundation. + * + * libprimrender is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenMetaverse.Rendering.GPL. If not, see + * . + */ + + +using System; +using System.Collections.Generic; + +namespace OpenMetaverse.Rendering +{ + public partial class GPLRenderer : IRendering + { + public void TransformTexCoords(List vertices, LLVector3 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) + { + LLVector3 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; + } + } + + private static void TransformTexCoord(ref LLVector2 texCoord, float cosAng, float sinAng, float offsetS, + float offsetT, float magS, float magT) + { + float s = texCoord.X; + float t = texCoord.Y; + + // Texture transforms are done about the center of the face + s -= 0.5f; + t -= 0.5f; + + // Rotation + float temp = s; + s = s * cosAng + t * sinAng; + t = -temp * sinAng + t * cosAng; + + // Scale + s *= magS; + t *= magT; + + // Offset + s += offsetS + 0.5f; + t += offsetT + 0.5f; + + texCoord.X = s; + texCoord.Y = t; + } + + private static void TransformPlanarTexCoord(ref LLVector2 texCoord, Vertex vertex, LLVector3 center, + LLVector3 vec) + { + LLVector3 binormal; + float d = LLVector3.Dot(vertex.Normal, LLVector3.Fwd); + if (d >= 0.5f || d <= -0.5f) + { + binormal = new LLVector3(0f, 1f, 0f); + + if (vertex.Normal.X < 0f) + { + binormal.X = -binormal.X; + binormal.Y = -binormal.Y; + binormal.Z = -binormal.Z; + } + } + else + { + binormal = new LLVector3(1f, 0f, 0f); + + if (vertex.Normal.Y > 0f) + { + binormal.X = -binormal.X; + binormal.Y = -binormal.Y; + binormal.Z = -binormal.Z; + } + } + + LLVector3 tangent = binormal % vertex.Normal; + + texCoord.Y = -(LLVector3.Dot(tangent, vec) * 2f - 0.5f); + texCoord.X = 1f + (LLVector3.Dot(binormal, vec) * 2f - 0.5f); + } + } +} diff --git a/OpenMetaverse.Rendering.Simple/SimpleRenderer.cs b/OpenMetaverse.Rendering.Simple/SimpleRenderer.cs new file mode 100644 index 00000000..7aba9b22 --- /dev/null +++ b/OpenMetaverse.Rendering.Simple/SimpleRenderer.cs @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2008, openmetaverse.org + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.org nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; + +namespace OpenMetaverse.Rendering +{ + [RendererName("Simple Cube Renderer")] + public partial class SimpleRenderer : IRendering + { + public SimpleMesh GenerateSimpleMesh(Primitive prim, DetailLevel lod) + { + Path path = GeneratePath(); + Profile profile = GenerateProfile(); + + SimpleMesh mesh = new SimpleMesh(); + mesh.Prim = prim; + mesh.Path = path; + mesh.Profile = profile; + mesh.Vertices = GenerateVertices(); + mesh.Indices = GenerateIndices(); + + return mesh; + } + + public FacetedMesh GenerateFacetedMesh(Primitive prim, DetailLevel lod) + { + Path path = GeneratePath(); + Profile profile = GenerateProfile(); + + FacetedMesh mesh = new FacetedMesh(); + mesh.Prim = prim; + mesh.Path = path; + mesh.Profile = profile; + mesh.Faces = GenerateFaces(prim.Textures); + + return mesh; + } + + public void TransformTexCoords(List vertices, LLVector3 center, LLObject.TextureEntryFace teFace) + { + // Lalala... + } + + private Path GeneratePath() + { + Path path = new Path(); + path.Points = new List(); + return path; + } + + private Profile GenerateProfile() + { + Profile profile = new Profile(); + profile.Faces = new List(); + profile.Positions = new List(); + return profile; + } + + private List GenerateVertices() + { + List vertices = new List(8); + + Vertex v = new Vertex(); + + // FIXME: Implement these + v.Binormal = LLVector3.Zero; + v.Normal = LLVector3.Zero; + v.TexCoord = LLVector2.Zero; + + v.Position = new LLVector3(0.5f, 0.5f, -0.5f); + vertices.Add(v); + v.Position = new LLVector3(0.5f, -0.5f, -0.5f); + vertices.Add(v); + v.Position = new LLVector3(-0.5f, -0.5f, -0.5f); + vertices.Add(v); + v.Position = new LLVector3(-0.5f, 0.5f, -0.5f); + vertices.Add(v); + v.Position = new LLVector3(0.5f, 0.5f, 0.5f); + vertices.Add(v); + v.Position = new LLVector3(0.5f, -0.5f, 0.5f); + vertices.Add(v); + v.Position = new LLVector3(-0.5f, -0.5f, 0.5f); + vertices.Add(v); + v.Position = new LLVector3(-0.5f, 0.5f, 0.5f); + vertices.Add(v); + + return vertices; + } + + private List GenerateIndices() + { + ushort[] indices = new ushort[] { + 0, 1, 2, + 0, 2, 3, + 4, 7, 6, + 4, 6, 5, + 0, 4, 5, + 0, 5, 1, + 1, 5, 6, + 1, 6, 2, + 2, 6, 7, + 2, 7, 3, + 4, 0, 3, + 4, 3, 7, + }; + + return new List(indices); + } + + private List GenerateFaces(LLObject.TextureEntry te) + { + Face face = new Face(); + face.Edge = new List(); + face.TextureFace = te.DefaultTexture; + face.Vertices = GenerateVertices(); + face.Indices = GenerateIndices(); + + List faces = new List(1); + faces.Add(face); + + return faces; + } + } +} diff --git a/Programs/PrimWorkshop/Program.cs b/Programs/PrimWorkshop/Program.cs index bcaf7ec7..f06a569a 100644 --- a/Programs/PrimWorkshop/Program.cs +++ b/Programs/PrimWorkshop/Program.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Windows.Forms; -namespace primpreview +namespace PrimWorkshop { static class Program { @@ -14,7 +14,7 @@ namespace primpreview { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new frmPrimPreview()); + Application.Run(new frmPrimWorkshop()); } } } \ No newline at end of file diff --git a/Programs/PrimWorkshop/Properties/Resources.Designer.cs b/Programs/PrimWorkshop/Properties/Resources.Designer.cs index 76c2b5f8..e1a6b5c3 100644 --- a/Programs/PrimWorkshop/Properties/Resources.Designer.cs +++ b/Programs/PrimWorkshop/Properties/Resources.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace primpreview.Properties +namespace PrimWorkshop.Properties { diff --git a/Programs/PrimWorkshop/Properties/Settings.Designer.cs b/Programs/PrimWorkshop/Properties/Settings.Designer.cs index 967209ff..531461cb 100644 --- a/Programs/PrimWorkshop/Properties/Settings.Designer.cs +++ b/Programs/PrimWorkshop/Properties/Settings.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace primpreview.Properties +namespace PrimWorkshop.Properties { diff --git a/Programs/PrimWorkshop/TexturePipeline.cs b/Programs/PrimWorkshop/TexturePipeline.cs index 0409133e..14dd9a80 100644 --- a/Programs/PrimWorkshop/TexturePipeline.cs +++ b/Programs/PrimWorkshop/TexturePipeline.cs @@ -1,8 +1,10 @@ + using System; using System.Collections.Generic; using System.Text; using System.Threading; -using libsecondlife; +using OpenMetaverse; + /* * [14:09] the onnewprim function will add missing texture uuids to the download queue, * and a separate thread will pull entries off that queue. @@ -19,7 +21,7 @@ using libsecondlife; * request image data with GetTextureToRender() using key returned in OnImageRenderReady event * (optionally) use RemoveFromPipeline() with key to cleanup dictionary */ -namespace primpreview +namespace PrimWorkshop { class TaskInfo { @@ -39,7 +41,7 @@ namespace primpreview /// public class TexturePipeline { - private static SecondLife Client; + private static GridClient Client; // queue for requested images private Queue RequestQueue; @@ -95,7 +97,7 @@ namespace primpreview /// Default constructor /// /// Reference to SecondLife client - public TexturePipeline(SecondLife client) + public TexturePipeline(GridClient client) { Running = true; diff --git a/Programs/PrimWorkshop/frmBrowser.Designer.cs b/Programs/PrimWorkshop/frmBrowser.Designer.cs index 882eb74e..0d6a35e3 100644 --- a/Programs/PrimWorkshop/frmBrowser.Designer.cs +++ b/Programs/PrimWorkshop/frmBrowser.Designer.cs @@ -1,4 +1,4 @@ -namespace primpreview +namespace PrimWorkshop { partial class frmBrowser { diff --git a/Programs/PrimWorkshop/frmBrowser.cs b/Programs/PrimWorkshop/frmBrowser.cs index ac672de1..2eee26de 100644 --- a/Programs/PrimWorkshop/frmBrowser.cs +++ b/Programs/PrimWorkshop/frmBrowser.cs @@ -9,12 +9,12 @@ using System.Runtime.InteropServices; using Tao.OpenGl; using Tao.Platform.Windows; using ICSharpCode.SharpZipLib.Zip; -using libsecondlife; -using libsecondlife.StructuredData; -using libsecondlife.Imaging; -using libsecondlife.Rendering; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenMetaverse.Imaging; +using OpenMetaverse.Rendering; -namespace primpreview +namespace PrimWorkshop { public partial class frmBrowser : Form { @@ -24,7 +24,7 @@ namespace primpreview ContextMenu ExportPrimMenu; ContextMenu ExportTerrainMenu; - SecondLife Client; + GridClient Client; Camera Camera; Dictionary RenderFoliageList = new Dictionary(); Dictionary RenderPrimList = new Dictionary(); @@ -40,7 +40,7 @@ namespace primpreview // Terrain float MaxHeight = 0.1f; - libsecondlife.TerrainManager.Patch[,] Heightmap; + TerrainManager.Patch[,] Heightmap; HeightmapLookupValue[] LookupHeightTable; // Picking globals @@ -142,7 +142,7 @@ namespace primpreview DownloadList.Clear(); // Initialize the SL client - Client = new SecondLife(); + Client = new GridClient(); Client.Settings.MULTIPLE_SIMS = false; Client.Settings.ALWAYS_DECODE_OBJECTS = true; Client.Settings.ALWAYS_REQUEST_OBJECTS = true; @@ -930,7 +930,7 @@ namespace primpreview { RenderablePrim render = new RenderablePrim(); render.Prim = prim; - render.Mesh = Render.GenerateMeshWithFaces(prim, 2.5f); + render.Mesh = Render.Plugin.GenerateFacetedMesh(prim, DetailLevel.High); // Create a FaceData struct for each face that stores the 3D data // in a Tao.OpenGL friendly format @@ -953,7 +953,7 @@ namespace primpreview // Texture transform for this face LLObject.TextureEntryFace teFace = prim.Textures.GetFace((uint)j); - Render.TransformTexCoords(face.Vertices, face.Center, teFace); + Render.Plugin.TransformTexCoords(face.Vertices, face.Center, teFace); // Texcoords for this face data.TexCoords = new float[face.Vertices.Count * 2]; @@ -1843,7 +1843,7 @@ StartRender: public struct RenderablePrim { public Primitive Prim; - public PrimMeshMultiFace Mesh; + public FacetedMesh Mesh; public readonly static RenderablePrim Empty = new RenderablePrim(); } diff --git a/Programs/PrimWorkshop/frmPrimPreview.Designer.cs b/Programs/PrimWorkshop/frmPrimWorkshop.Designer.cs similarity index 97% rename from Programs/PrimWorkshop/frmPrimPreview.Designer.cs rename to Programs/PrimWorkshop/frmPrimWorkshop.Designer.cs index 02f886c9..a3c9cb2c 100644 --- a/Programs/PrimWorkshop/frmPrimPreview.Designer.cs +++ b/Programs/PrimWorkshop/frmPrimWorkshop.Designer.cs @@ -1,6 +1,6 @@ -namespace primpreview +namespace PrimWorkshop { - partial class frmPrimPreview + partial class frmPrimWorkshop { /// /// Required designer variable. @@ -31,6 +31,9 @@ namespace primpreview { this.menu = new System.Windows.Forms.MenuStrip(); this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.opToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.openPrimXMLToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.textureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); this.savePrimXMLToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveTextureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -58,9 +61,6 @@ namespace primpreview this.scrollYaw = new System.Windows.Forms.HScrollBar(); this.picTexture = new System.Windows.Forms.PictureBox(); this.scrollZoom = new System.Windows.Forms.HScrollBar(); - this.openPrimXMLToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); - this.textureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.opToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.menu.SuspendLayout(); this.splitContainer.Panel1.SuspendLayout(); this.splitContainer.Panel2.SuspendLayout(); @@ -98,6 +98,29 @@ namespace primpreview this.fileToolStripMenuItem.Size = new System.Drawing.Size(35, 20); this.fileToolStripMenuItem.Text = "File"; // + // opToolStripMenuItem + // + this.opToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.openPrimXMLToolStripMenuItem1, + this.textureToolStripMenuItem}); + this.opToolStripMenuItem.Name = "opToolStripMenuItem"; + this.opToolStripMenuItem.Size = new System.Drawing.Size(154, 22); + this.opToolStripMenuItem.Text = "Open"; + // + // openPrimXMLToolStripMenuItem1 + // + this.openPrimXMLToolStripMenuItem1.Name = "openPrimXMLToolStripMenuItem1"; + this.openPrimXMLToolStripMenuItem1.Size = new System.Drawing.Size(127, 22); + this.openPrimXMLToolStripMenuItem1.Text = "Prim XML"; + this.openPrimXMLToolStripMenuItem1.Click += new System.EventHandler(this.openPrimXMLToolStripMenuItem1_Click); + // + // textureToolStripMenuItem + // + this.textureToolStripMenuItem.Name = "textureToolStripMenuItem"; + this.textureToolStripMenuItem.Size = new System.Drawing.Size(127, 22); + this.textureToolStripMenuItem.Text = "Texture"; + this.textureToolStripMenuItem.Click += new System.EventHandler(this.textureToolStripMenuItem_Click); + // // toolStripMenuItem2 // this.toolStripMenuItem2.Name = "toolStripMenuItem2"; @@ -377,30 +400,7 @@ namespace primpreview this.scrollZoom.Value = -50; this.scrollZoom.ValueChanged += new System.EventHandler(this.scrollZoom_ValueChanged); // - // openPrimXMLToolStripMenuItem1 - // - this.openPrimXMLToolStripMenuItem1.Name = "openPrimXMLToolStripMenuItem1"; - this.openPrimXMLToolStripMenuItem1.Size = new System.Drawing.Size(152, 22); - this.openPrimXMLToolStripMenuItem1.Text = "Prim XML"; - this.openPrimXMLToolStripMenuItem1.Click += new System.EventHandler(this.openPrimXMLToolStripMenuItem1_Click); - // - // textureToolStripMenuItem - // - this.textureToolStripMenuItem.Name = "textureToolStripMenuItem"; - this.textureToolStripMenuItem.Size = new System.Drawing.Size(152, 22); - this.textureToolStripMenuItem.Text = "Texture"; - this.textureToolStripMenuItem.Click += new System.EventHandler(this.textureToolStripMenuItem_Click); - // - // opToolStripMenuItem - // - this.opToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.openPrimXMLToolStripMenuItem1, - this.textureToolStripMenuItem}); - this.opToolStripMenuItem.Name = "opToolStripMenuItem"; - this.opToolStripMenuItem.Size = new System.Drawing.Size(154, 22); - this.opToolStripMenuItem.Text = "Open"; - // - // frmPrimPreview + // frmPrimWorkshop // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; @@ -408,8 +408,9 @@ namespace primpreview this.Controls.Add(this.splitContainer); this.Controls.Add(this.menu); this.MainMenuStrip = this.menu; - this.Name = "frmPrimPreview"; - this.Text = "Prim Preview"; + this.Name = "frmPrimWorkshop"; + this.Text = "Prim Workshop"; + this.Shown += new System.EventHandler(this.frmPrimWorkshop_Shown); this.menu.ResumeLayout(false); this.menu.PerformLayout(); this.splitContainer.Panel1.ResumeLayout(false); diff --git a/Programs/PrimWorkshop/frmPrimPreview.cs b/Programs/PrimWorkshop/frmPrimWorkshop.cs similarity index 90% rename from Programs/PrimWorkshop/frmPrimPreview.cs rename to Programs/PrimWorkshop/frmPrimWorkshop.cs index f21cb947..7c3c4386 100644 --- a/Programs/PrimWorkshop/frmPrimPreview.cs +++ b/Programs/PrimWorkshop/frmPrimWorkshop.cs @@ -7,14 +7,14 @@ using System.IO; using System.Windows.Forms; using Tao.OpenGl; using Tao.Platform.Windows; -using libsecondlife; -using libsecondlife.StructuredData; -using libsecondlife.Imaging; -using libsecondlife.Rendering; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenMetaverse.Imaging; +using OpenMetaverse.Rendering; // NOTE: Batches are divided by texture, fullbright, shiny, transparent, and glow -namespace primpreview +namespace PrimWorkshop { public struct FaceData { @@ -26,12 +26,17 @@ namespace primpreview // TODO: Normals / binormals? } - public partial class frmPrimPreview : Form + public static class Render + { + public static IRendering Plugin; + } + + public partial class frmPrimWorkshop : Form { #region Form Globals - List Prims = null; - PrimMeshMultiFace CurrentPrim = null; + List Prims = null; + FacetedMesh CurrentPrim = null; ProfileFace? CurrentFace = null; bool DraggingTexture = false; @@ -40,7 +45,7 @@ namespace primpreview #endregion Form Globals - public frmPrimPreview() + public frmPrimWorkshop() { InitializeComponent(); glControl.InitializeContexts(); @@ -61,6 +66,30 @@ namespace primpreview glControl_Resize(null, null); } + private void frmPrimWorkshop_Shown(object sender, EventArgs e) + { + // Get a list of rendering plugins + List renderers = RenderingLoader.ListRenderers("."); + + foreach (string r in renderers) + { + DialogResult result = MessageBox.Show( + String.Format("Use renderer {0}?", r), "Select Rendering Plugin", MessageBoxButtons.YesNo); + + if (result == DialogResult.Yes) + { + Render.Plugin = RenderingLoader.LoadRenderer(r); + break; + } + } + + if (Render.Plugin == null) + { + MessageBox.Show("No valid rendering plugin loaded, exiting..."); + Application.Exit(); + } + } + #region GLControl Callbacks private void glControl_Paint(object sender, PaintEventArgs e) @@ -246,7 +275,7 @@ namespace primpreview if (llsd != null && llsd.Type == LLSDType.Map) { List primList = Helpers.LLSDToPrimList(llsd); - Prims = new List(primList.Count); + Prims = new List(primList.Count); for (int i = 0; i < primList.Count; i++) { @@ -255,7 +284,7 @@ namespace primpreview continue; Primitive prim = primList[i]; - PrimMeshMultiFace mesh = Render.GenerateMeshWithFaces(prim, 4f); + FacetedMesh mesh = Render.Plugin.GenerateFacetedMesh(prim, DetailLevel.Highest); // Create a FaceData struct for each face that stores the 3D data // in a Tao.OpenGL friendly format @@ -278,7 +307,7 @@ namespace primpreview // Texture transform for this face LLObject.TextureEntryFace teFace = prim.Textures.GetFace((uint)j); - Render.TransformTexCoords(face.Vertices, face.Center, teFace); + Render.Plugin.TransformTexCoords(face.Vertices, face.Center, teFace); // Texcoords for this face data.TexCoords = new float[face.Vertices.Count * 2]; @@ -484,7 +513,7 @@ namespace primpreview private void cboPrim_SelectedIndexChanged(object sender, EventArgs e) { - CurrentPrim = (PrimMeshMultiFace)cboPrim.Items[cboPrim.SelectedIndex]; + CurrentPrim = (FacetedMesh)cboPrim.Items[cboPrim.SelectedIndex]; PopulateFaceCombobox(); glControl.Invalidate(); diff --git a/Programs/PrimWorkshop/frmPrimPreview.resx b/Programs/PrimWorkshop/frmPrimWorkshop.resx similarity index 100% rename from Programs/PrimWorkshop/frmPrimPreview.resx rename to Programs/PrimWorkshop/frmPrimWorkshop.resx diff --git a/Programs/PrimWorkshop/meshtoobj.cs b/Programs/PrimWorkshop/meshtoobj.cs index c3c2fe05..01617eb9 100644 --- a/Programs/PrimWorkshop/meshtoobj.cs +++ b/Programs/PrimWorkshop/meshtoobj.cs @@ -2,14 +2,14 @@ using System; using System.Collections.Generic; using System.Text; using System.IO; -using libsecondlife; -using libsecondlife.Rendering; +using OpenMetaverse; +using OpenMetaverse.Rendering; -namespace primpreview +namespace PrimWorkshop { public static class MeshToOBJ { - public static bool MeshesToOBJ(List meshes, string filename) + public static bool MeshesToOBJ(List meshes, string filename) { StringBuilder obj = new StringBuilder(); StringBuilder mtl = new StringBuilder(); @@ -28,7 +28,7 @@ namespace primpreview for (int i = 0; i < meshes.Count; i++) { - PrimMeshMultiFace mesh = meshes[i]; + FacetedMesh mesh = meshes[i]; for (int j = 0; j < mesh.Faces.Count; j++) { diff --git a/prebuild.xml b/prebuild.xml index 9464e384..8dd126e1 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -233,12 +233,14 @@ ../../bin/ + +