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/
+
+