public SimpleMesh GenerateSimpleMesh(Primitive prim, DetailLevel lod) { Path path = GeneratePath(); Profile profile = GenerateProfile(); MeshmerizerMesh meshmerizer = new MeshmerizerMesh(); meshmerizer = GenerateMeshmerizerMesh(prim); // Create the vertex array List <Vertex> vertices = new List <Vertex>(meshmerizer.primMesh.coords.Count); for (int i = 0; i < meshmerizer.primMesh.coords.Count; i++) { Coord c = meshmerizer.primMesh.coords[i]; Vertex vertex = new Vertex(); vertex.Position = new Vector3(c.X, c.Y, c.Z); vertices.Add(vertex); } // Create the index array List <ushort> indices = new List <ushort>(meshmerizer.primMesh.faces.Count * 3); for (int i = 0; i < meshmerizer.primMesh.faces.Count; i++) { MeshmerizerFace f = meshmerizer.primMesh.faces[i]; indices.Add((ushort)f.v1); indices.Add((ushort)f.v2); indices.Add((ushort)f.v3); } SimpleMesh mesh = new SimpleMesh(); mesh.Prim = prim; mesh.Path = path; mesh.Profile = profile; mesh.Vertices = vertices; mesh.Indices = indices; return(mesh); }
private List <Face> GenerateFaces(Primitive prim) { MeshmerizerMesh meshmerizer = new MeshmerizerMesh(); meshmerizer = GenerateMeshmerizerMesh(prim); // Create the vertex array List <Vertex> vertices = new List <Vertex>(meshmerizer.primMesh.coords.Count); for (int i = 0; i < meshmerizer.primMesh.coords.Count; i++) { Coord c = meshmerizer.primMesh.coords[i]; Vertex vertex = new Vertex(); vertex.Position = new Vector3(c.X, c.Y, c.Z); vertices.Add(vertex); } // Create the index array List <ushort> indices = new List <ushort>(meshmerizer.primMesh.faces.Count * 3); for (int i = 0; i < meshmerizer.primMesh.faces.Count; i++) { MeshmerizerFace f = meshmerizer.primMesh.faces[i]; indices.Add((ushort)f.v1); indices.Add((ushort)f.v2); indices.Add((ushort)f.v3); } Face face = new Face(); face.Edge = new List <int>(); face.TextureFace = prim.Textures.DefaultTexture; face.Vertices = vertices; face.Indices = indices; List <Face> faces = new List <Face>(1); faces.Add(face); return(faces); }
public SimpleMesh GenerateSimpleMesh(Primitive prim, DetailLevel lod) { Path path = GeneratePath(); Profile profile = GenerateProfile(); MeshmerizerMesh meshmerizer = new MeshmerizerMesh(); meshmerizer = GenerateMeshmerizerMesh(prim); // Create the vertex array List<Vertex> vertices = new List<Vertex>(meshmerizer.primMesh.coords.Count); for (int i = 0; i < meshmerizer.primMesh.coords.Count; i++) { Coord c = meshmerizer.primMesh.coords[i]; Vertex vertex = new Vertex(); vertex.Position = new Vector3(c.X, c.Y, c.Z); vertices.Add(vertex); } // Create the index array List<ushort> indices = new List<ushort>(meshmerizer.primMesh.faces.Count * 3); for (int i = 0; i < meshmerizer.primMesh.faces.Count; i++) { MeshmerizerFace f = meshmerizer.primMesh.faces[i]; indices.Add((ushort)f.v1); indices.Add((ushort)f.v2); indices.Add((ushort)f.v3); } SimpleMesh mesh = new SimpleMesh(); mesh.Prim = prim; mesh.Path = path; mesh.Profile = profile; mesh.Vertices = vertices; mesh.Indices = indices; return mesh; }
/// <summary> /// Creates an extrusion of a profile along a linear path. Used to create prim types box, cylinder, and prism. /// </summary> /// <param name="m"></param> /// <returns>A mesh of the extruded shape</returns> public MeshmerizerMesh ExtrudeLinearPath(MeshmerizerMesh m) { MeshmerizerMesh result = new MeshmerizerMesh(); MeshmerizerMesh newLayer; MeshmerizerMesh lastLayer = null; int step = 0; int steps = 1; float twistTotal = twistTop - twistBot; // if the profile has a lot of twist, add more layers otherwise the layers may overlap // and the resulting mesh may be quite inaccurate. This method is arbitrary and may not // accurately match the viewer float twistTotalAbs = System.Math.Abs(twistTotal); if (twistTotalAbs > 0.01) steps += (int)(twistTotalAbs * 3.66f); // dahlia's magic number ;) double percentOfPathMultiplier = 1.0 / steps; float start = -0.5f; float stepSize = 1.0f / (float)steps; float xProfileScale = 1.0f; float yProfileScale = 1.0f; float xOffset = 0.0f; float yOffset = 0.0f; float zOffset = start; float xOffsetStepIncrement = pushX / steps; float yOffsetStepIncrement = pushY / steps; //float percentOfPath = 0.0f; float percentOfPath = (float)pathBegin * 2.0e-5f; zOffset += percentOfPath; bool done = false; do // loop through the length of the path and add the layers { newLayer = m.Clone(); if (taperBotFactorX < 1.0f) xProfileScale = 1.0f - (1.0f - percentOfPath) * (1.0f - taperBotFactorX); else if (taperTopFactorX < 1.0f) xProfileScale = 1.0f - percentOfPath * (1.0f - taperTopFactorX); else xProfileScale = 1.0f; if (taperBotFactorY < 1.0f) yProfileScale = 1.0f - (1.0f - percentOfPath) * (1.0f - taperBotFactorY); else if (taperTopFactorY < 1.0f) yProfileScale = 1.0f - percentOfPath * (1.0f - taperTopFactorY); else yProfileScale = 1.0f; MeshmerizerVertex vTemp = new MeshmerizerVertex(0.0f, 0.0f, 0.0f); // apply the taper to the profile before any rotations if (xProfileScale != 1.0f || yProfileScale != 1.0f) { foreach (MeshmerizerVertex v in newLayer.vertices) { if (v != null) { v.X *= xProfileScale; v.Y *= yProfileScale; } } } float twist = twistBot + (twistTotal * (float)percentOfPath); // apply twist rotation to the profile layer and position the layer in the prim Quaternion profileRot = Quaternion.CreateFromAxisAngle(new Vector3(0.0f, 0.0f, 1.0f), twist); foreach (MeshmerizerVertex v in newLayer.vertices) { if (v != null) { vTemp = v * profileRot; v.X = vTemp.X + xOffset; v.Y = vTemp.Y + yOffset; v.Z = vTemp.Z + zOffset; } } if (step == 0) // the first layer, invert normals { foreach (Triangle t in newLayer.triangles) { t.invertNormal(); } } result.Append(newLayer); int iLastNull = 0; if (lastLayer != null) { int i, count = newLayer.vertices.Count; for (i = 0; i < count; i++) { int iNext = (i + 1); if (lastLayer.vertices[i] == null) // cant make a simplex here { iLastNull = i + 1; } else { if (i == count - 1) // End of list iNext = iLastNull; if (lastLayer.vertices[iNext] == null) // Null means wrap to begin of last segment iNext = iLastNull; result.Add(new Triangle(newLayer.vertices[i], lastLayer.vertices[i], newLayer.vertices[iNext])); result.Add(new Triangle(newLayer.vertices[iNext], lastLayer.vertices[i], lastLayer.vertices[iNext])); } } } lastLayer = newLayer; // calc the step for the next interation of the loop if (step < steps) { step++; percentOfPath += (float)percentOfPathMultiplier; xOffset += xOffsetStepIncrement; yOffset += yOffsetStepIncrement; zOffset += stepSize; if (percentOfPath > 1.0f - (float)pathEnd * 2.0e-5f) done = true; } else done = true; } while (!done); // loop until all the layers in the path are completed return result; }
/// <summary> /// Extrudes a shape around a circular path. Used to create prim types torus, ring, and tube. /// </summary> /// <param name="m"></param> /// <returns>a mesh of the extruded shape</returns> public MeshmerizerMesh ExtrudeCircularPath(MeshmerizerMesh m) { MeshmerizerMesh result = new MeshmerizerMesh(); MeshmerizerMesh newLayer; MeshmerizerMesh lastLayer = null; int step; int steps = 24; float twistTotal = twistTop - twistBot; // if the profile has a lot of twist, add more layers otherwise the layers may overlap // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't // accurately match the viewer if (System.Math.Abs(twistTotal) > (float)System.Math.PI * 1.5f) steps *= 2; if (System.Math.Abs(twistTotal) > (float)System.Math.PI * 3.0f) steps *= 2; // double percentOfPathMultiplier = 1.0 / steps; // double angleStepMultiplier = System.Math.PI * 2.0 / steps; float yPathScale = pathScaleY * 0.5f; float pathLength = pathCutEnd - pathCutBegin; float totalSkew = skew * 2.0f * pathLength; float skewStart = (-skew) + pathCutBegin * 2.0f * skew; // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used // to calculate the sine for generating the path radius appears to approximate it's effects there // too, but there are some subtle differences in the radius which are noticeable as the prim size // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on // the meshes generated with this technique appear nearly identical in shape to the same prims when // displayed by the viewer. float startAngle = (float)(System.Math.PI * 2.0 * pathCutBegin * revolutions) - pushY * 0.9f; float endAngle = (float)(System.Math.PI * 2.0 * pathCutEnd * revolutions) - pushY * 0.9f; float stepSize = (float)0.2617993878; // 2*PI / 24 segments per revolution step = (int)(startAngle / stepSize); float angle = startAngle; float xProfileScale = 1.0f; float yProfileScale = 1.0f; bool done = false; do // loop through the length of the path and add the layers { newLayer = m.Clone(); float percentOfPath = (angle - startAngle) / (endAngle - startAngle); // endAngle should always be larger than startAngle if (pathTaperX > 0.001f) // can't really compare to 0.0f as the value passed is never exactly zero xProfileScale = 1.0f - percentOfPath * pathTaperX; else if (pathTaperX < -0.001f) xProfileScale = 1.0f + (1.0f - percentOfPath) * pathTaperX; else xProfileScale = 1.0f; if (pathTaperY > 0.001f) yProfileScale = 1.0f - percentOfPath * pathTaperY; else if (pathTaperY < -0.001f) yProfileScale = 1.0f + (1.0f - percentOfPath) * pathTaperY; else yProfileScale = 1.0f; MeshmerizerVertex vTemp = new MeshmerizerVertex(0.0f, 0.0f, 0.0f); // apply the taper to the profile before any rotations if (xProfileScale != 1.0f || yProfileScale != 1.0f) { foreach (MeshmerizerVertex v in newLayer.vertices) { if (v != null) { v.X *= xProfileScale; v.Y *= yProfileScale; } } } float radiusScale; if (radius > 0.001f) radiusScale = 1.0f - radius * percentOfPath; else if (radius < 0.001f) radiusScale = 1.0f + radius * (1.0f - percentOfPath); else radiusScale = 1.0f; float twist = twistBot + (twistTotal * (float)percentOfPath); float xOffset; float yOffset; float zOffset; xOffset = 0.5f * (skewStart + totalSkew * (float)percentOfPath); xOffset += (float)System.Math.Sin(angle) * pushX * 0.45f; yOffset = (float)(System.Math.Cos(angle) * (0.5f - yPathScale)) * radiusScale; zOffset = (float)(System.Math.Sin(angle + pushY * 0.9f) * (0.5f - yPathScale)) * radiusScale; // next apply twist rotation to the profile layer if (twistTotal != 0.0f || twistBot != 0.0f) { Quaternion profileRot = new Quaternion(new Vector3(0.0f, 0.0f, 1.0f), twist); foreach (MeshmerizerVertex v in newLayer.vertices) { if (v != null) { vTemp = v * profileRot; v.X = vTemp.X; v.Y = vTemp.Y; v.Z = vTemp.Z; } } } // now orient the rotation of the profile layer relative to it's position on the path // adding pushY to the angle used to generate the quat appears to approximate the viewer Quaternion layerRot = Quaternion.CreateFromAxisAngle(new Vector3(1.0f, 0.0f, 0.0f), (float)angle + pushY * 0.9f); foreach (MeshmerizerVertex v in newLayer.vertices) { if (v != null) { vTemp = v * layerRot; v.X = vTemp.X + xOffset; v.Y = vTemp.Y + yOffset; v.Z = vTemp.Z + zOffset; } } if (angle == startAngle) // the first layer, invert normals { foreach (Triangle t in newLayer.triangles) { t.invertNormal(); } } result.Append(newLayer); int iLastNull = 0; if (lastLayer != null) { int i, count = newLayer.vertices.Count; for (i = 0; i < count; i++) { int iNext = (i + 1); if (lastLayer.vertices[i] == null) // cant make a simplex here { iLastNull = i + 1; } else { if (i == count - 1) // End of list iNext = iLastNull; if (lastLayer.vertices[iNext] == null) // Null means wrap to begin of last segment iNext = iLastNull; result.Add(new Triangle(newLayer.vertices[i], lastLayer.vertices[i], newLayer.vertices[iNext])); result.Add(new Triangle(newLayer.vertices[iNext], lastLayer.vertices[i], lastLayer.vertices[iNext])); } } } lastLayer = newLayer; // calc the angle for the next interation of the loop if (angle >= endAngle) { done = true; } else { angle = stepSize * ++step; if (angle > endAngle) angle = endAngle; } } while (!done); // loop until all the layers in the path are completed return result; }
private List<Face> GenerateFaces(Primitive prim) { MeshmerizerMesh meshmerizer = new MeshmerizerMesh(); meshmerizer = GenerateMeshmerizerMesh(prim); // Create the vertex array List<Vertex> vertices = new List<Vertex>(meshmerizer.primMesh.coords.Count); for (int i = 0; i < meshmerizer.primMesh.coords.Count; i++) { Coord c = meshmerizer.primMesh.coords[i]; Vertex vertex = new Vertex(); vertex.Position = new Vector3(c.X, c.Y, c.Z); vertices.Add(vertex); } // Create the index array List<ushort> indices = new List<ushort>(meshmerizer.primMesh.faces.Count * 3); for (int i = 0; i < meshmerizer.primMesh.faces.Count; i++) { MeshmerizerFace f = meshmerizer.primMesh.faces[i]; indices.Add((ushort)f.v1); indices.Add((ushort)f.v2); indices.Add((ushort)f.v3); } Face face = new Face(); face.Edge = new List<int>(); face.TextureFace = prim.Textures.DefaultTexture; face.Vertices = vertices; face.Indices = indices; List<Face> faces = new List<Face>(1); faces.Add(face); return faces; }
private MeshmerizerMesh GenerateMeshmerizerMesh(Primitive prim) { PrimitiveBaseShape primShape = new PrimitiveBaseShape(prim); MeshmerizerMesh mesh = new MeshmerizerMesh(); float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f; float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f; float pathBegin = (float)primShape.PathBegin * 2.0e-5f; float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f; float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f; float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f; float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f; float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f; float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f; int sides = 4; if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) sides = 3; else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) sides = 24; else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) { // half circle, prim is a sphere sides = 24; profileBegin = 0.5f * profileBegin + 0.5f; profileEnd = 0.5f * profileEnd + 0.5f; } int hollowSides = sides; if (primShape.HollowShape == HollowShape.Circle) hollowSides = 24; else if (primShape.HollowShape == HollowShape.Square) hollowSides = 4; else if (primShape.HollowShape == HollowShape.Triangle) hollowSides = 3; PrimMesh primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides); primMesh.topShearX = pathShearX; primMesh.topShearY = pathShearY; primMesh.pathCutBegin = pathBegin; primMesh.pathCutEnd = pathEnd; if (primShape.PathCurve == (byte)Extrusion.Straight) { primMesh.twistBegin = primShape.PathTwistBegin * 18 / 10; primMesh.twistEnd = primShape.PathTwist * 18 / 10; primMesh.taperX = pathScaleX; primMesh.taperY = pathScaleY; try { primMesh.ExtrudeLinear(); } catch (Exception) { return null; } } else { primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f; primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f; primMesh.radius = 0.01f * primShape.PathRadiusOffset; primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions; primMesh.skew = 0.01f * primShape.PathSkew; primMesh.twistBegin = primShape.PathTwistBegin * 36 / 10; primMesh.twistEnd = primShape.PathTwist * 36 / 10; primMesh.taperX = primShape.PathTaperX * 0.01f; primMesh.taperY = primShape.PathTaperY * 0.01f; try { primMesh.ExtrudeCircular(); } catch (Exception) { return null; } } int numCoords = primMesh.coords.Count; //int numFaces = primMesh.faces.Count; List<Coord> coords = primMesh.coords; for (int i = 0; i < numCoords; i++) { Coord c = coords[i]; mesh.vertices.Add(new MeshmerizerVertex(c.X, c.Y, c.Z)); } mesh.primMesh = primMesh; // trim the vertex and triangle lists to free up memory mesh.vertices.TrimExcess(); mesh.triangles.TrimExcess(); return mesh; }
private MeshmerizerMesh GenerateMeshmerizerMesh(Primitive prim) { PrimitiveBaseShape primShape = new PrimitiveBaseShape(prim); MeshmerizerMesh mesh = new MeshmerizerMesh(); float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f; float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f; float pathBegin = (float)primShape.PathBegin * 2.0e-5f; float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f; float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f; float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f; float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f; float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f; float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f; int sides = 4; if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) { sides = 3; } else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) { sides = 24; } else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) { // half circle, prim is a sphere sides = 24; profileBegin = 0.5f * profileBegin + 0.5f; profileEnd = 0.5f * profileEnd + 0.5f; } int hollowSides = sides; if (primShape.HollowShape == HollowShape.Circle) { hollowSides = 24; } else if (primShape.HollowShape == HollowShape.Square) { hollowSides = 4; } else if (primShape.HollowShape == HollowShape.Triangle) { hollowSides = 3; } PrimMesh primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides); primMesh.topShearX = pathShearX; primMesh.topShearY = pathShearY; primMesh.pathCutBegin = pathBegin; primMesh.pathCutEnd = pathEnd; if (primShape.PathCurve == (byte)Extrusion.Straight) { primMesh.twistBegin = primShape.PathTwistBegin * 18 / 10; primMesh.twistEnd = primShape.PathTwist * 18 / 10; primMesh.taperX = pathScaleX; primMesh.taperY = pathScaleY; try { primMesh.ExtrudeLinear(); } catch (Exception) { return(null); } } else { primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f; primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f; primMesh.radius = 0.01f * primShape.PathRadiusOffset; primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions; primMesh.skew = 0.01f * primShape.PathSkew; primMesh.twistBegin = primShape.PathTwistBegin * 36 / 10; primMesh.twistEnd = primShape.PathTwist * 36 / 10; primMesh.taperX = primShape.PathTaperX * 0.01f; primMesh.taperY = primShape.PathTaperY * 0.01f; try { primMesh.ExtrudeCircular(); } catch (Exception) { return(null); } } int numCoords = primMesh.coords.Count; //int numFaces = primMesh.faces.Count; List <Coord> coords = primMesh.coords; for (int i = 0; i < numCoords; i++) { Coord c = coords[i]; mesh.vertices.Add(new MeshmerizerVertex(c.X, c.Y, c.Z)); } mesh.primMesh = primMesh; // trim the vertex and triangle lists to free up memory mesh.vertices.TrimExcess(); mesh.triangles.TrimExcess(); return(mesh); }
/// <summary> /// Creates an extrusion of a profile along a linear path. Used to create prim types box, cylinder, and prism. /// </summary> /// <param name="m"></param> /// <returns>A mesh of the extruded shape</returns> public MeshmerizerMesh ExtrudeLinearPath(MeshmerizerMesh m) { MeshmerizerMesh result = new MeshmerizerMesh(); MeshmerizerMesh newLayer; MeshmerizerMesh lastLayer = null; int step = 0; int steps = 1; float twistTotal = twistTop - twistBot; // if the profile has a lot of twist, add more layers otherwise the layers may overlap // and the resulting mesh may be quite inaccurate. This method is arbitrary and may not // accurately match the viewer float twistTotalAbs = System.Math.Abs(twistTotal); if (twistTotalAbs > 0.01) { steps += (int)(twistTotalAbs * 3.66f); // dahlia's magic number ;) } double percentOfPathMultiplier = 1.0 / steps; float start = -0.5f; float stepSize = 1.0f / (float)steps; float xProfileScale = 1.0f; float yProfileScale = 1.0f; float xOffset = 0.0f; float yOffset = 0.0f; float zOffset = start; float xOffsetStepIncrement = pushX / steps; float yOffsetStepIncrement = pushY / steps; //float percentOfPath = 0.0f; float percentOfPath = (float)pathBegin * 2.0e-5f; zOffset += percentOfPath; bool done = false; do // loop through the length of the path and add the layers { newLayer = m.Clone(); if (taperBotFactorX < 1.0f) { xProfileScale = 1.0f - (1.0f - percentOfPath) * (1.0f - taperBotFactorX); } else if (taperTopFactorX < 1.0f) { xProfileScale = 1.0f - percentOfPath * (1.0f - taperTopFactorX); } else { xProfileScale = 1.0f; } if (taperBotFactorY < 1.0f) { yProfileScale = 1.0f - (1.0f - percentOfPath) * (1.0f - taperBotFactorY); } else if (taperTopFactorY < 1.0f) { yProfileScale = 1.0f - percentOfPath * (1.0f - taperTopFactorY); } else { yProfileScale = 1.0f; } MeshmerizerVertex vTemp = new MeshmerizerVertex(0.0f, 0.0f, 0.0f); // apply the taper to the profile before any rotations if (xProfileScale != 1.0f || yProfileScale != 1.0f) { foreach (MeshmerizerVertex v in newLayer.vertices) { if (v != null) { v.X *= xProfileScale; v.Y *= yProfileScale; } } } float twist = twistBot + (twistTotal * (float)percentOfPath); // apply twist rotation to the profile layer and position the layer in the prim Quaternion profileRot = Quaternion.CreateFromAxisAngle(new Vector3(0.0f, 0.0f, 1.0f), twist); foreach (MeshmerizerVertex v in newLayer.vertices) { if (v != null) { vTemp = v * profileRot; v.X = vTemp.X + xOffset; v.Y = vTemp.Y + yOffset; v.Z = vTemp.Z + zOffset; } } if (step == 0) // the first layer, invert normals { foreach (Triangle t in newLayer.triangles) { t.invertNormal(); } } result.Append(newLayer); int iLastNull = 0; if (lastLayer != null) { int i, count = newLayer.vertices.Count; for (i = 0; i < count; i++) { int iNext = (i + 1); if (lastLayer.vertices[i] == null) // cant make a simplex here { iLastNull = i + 1; } else { if (i == count - 1) // End of list { iNext = iLastNull; } if (lastLayer.vertices[iNext] == null) // Null means wrap to begin of last segment { iNext = iLastNull; } result.Add(new Triangle(newLayer.vertices[i], lastLayer.vertices[i], newLayer.vertices[iNext])); result.Add(new Triangle(newLayer.vertices[iNext], lastLayer.vertices[i], lastLayer.vertices[iNext])); } } } lastLayer = newLayer; // calc the step for the next interation of the loop if (step < steps) { step++; percentOfPath += (float)percentOfPathMultiplier; xOffset += xOffsetStepIncrement; yOffset += yOffsetStepIncrement; zOffset += stepSize; if (percentOfPath > 1.0f - (float)pathEnd * 2.0e-5f) { done = true; } } else { done = true; } } while (!done); // loop until all the layers in the path are completed return(result); }
/// <summary> /// Extrudes a shape around a circular path. Used to create prim types torus, ring, and tube. /// </summary> /// <param name="m"></param> /// <returns>a mesh of the extruded shape</returns> public MeshmerizerMesh ExtrudeCircularPath(MeshmerizerMesh m) { MeshmerizerMesh result = new MeshmerizerMesh(); MeshmerizerMesh newLayer; MeshmerizerMesh lastLayer = null; int step; int steps = 24; float twistTotal = twistTop - twistBot; // if the profile has a lot of twist, add more layers otherwise the layers may overlap // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't // accurately match the viewer if (System.Math.Abs(twistTotal) > (float)System.Math.PI * 1.5f) { steps *= 2; } if (System.Math.Abs(twistTotal) > (float)System.Math.PI * 3.0f) { steps *= 2; } // double percentOfPathMultiplier = 1.0 / steps; // double angleStepMultiplier = System.Math.PI * 2.0 / steps; float yPathScale = pathScaleY * 0.5f; float pathLength = pathCutEnd - pathCutBegin; float totalSkew = skew * 2.0f * pathLength; float skewStart = (-skew) + pathCutBegin * 2.0f * skew; // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used // to calculate the sine for generating the path radius appears to approximate it's effects there // too, but there are some subtle differences in the radius which are noticeable as the prim size // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on // the meshes generated with this technique appear nearly identical in shape to the same prims when // displayed by the viewer. float startAngle = (float)(System.Math.PI * 2.0 * pathCutBegin * revolutions) - pushY * 0.9f; float endAngle = (float)(System.Math.PI * 2.0 * pathCutEnd * revolutions) - pushY * 0.9f; float stepSize = (float)0.2617993878; // 2*PI / 24 segments per revolution step = (int)(startAngle / stepSize); float angle = startAngle; float xProfileScale = 1.0f; float yProfileScale = 1.0f; bool done = false; do // loop through the length of the path and add the layers { newLayer = m.Clone(); float percentOfPath = (angle - startAngle) / (endAngle - startAngle); // endAngle should always be larger than startAngle if (pathTaperX > 0.001f) // can't really compare to 0.0f as the value passed is never exactly zero { xProfileScale = 1.0f - percentOfPath * pathTaperX; } else if (pathTaperX < -0.001f) { xProfileScale = 1.0f + (1.0f - percentOfPath) * pathTaperX; } else { xProfileScale = 1.0f; } if (pathTaperY > 0.001f) { yProfileScale = 1.0f - percentOfPath * pathTaperY; } else if (pathTaperY < -0.001f) { yProfileScale = 1.0f + (1.0f - percentOfPath) * pathTaperY; } else { yProfileScale = 1.0f; } MeshmerizerVertex vTemp = new MeshmerizerVertex(0.0f, 0.0f, 0.0f); // apply the taper to the profile before any rotations if (xProfileScale != 1.0f || yProfileScale != 1.0f) { foreach (MeshmerizerVertex v in newLayer.vertices) { if (v != null) { v.X *= xProfileScale; v.Y *= yProfileScale; } } } float radiusScale; if (radius > 0.001f) { radiusScale = 1.0f - radius * percentOfPath; } else if (radius < 0.001f) { radiusScale = 1.0f + radius * (1.0f - percentOfPath); } else { radiusScale = 1.0f; } float twist = twistBot + (twistTotal * (float)percentOfPath); float xOffset; float yOffset; float zOffset; xOffset = 0.5f * (skewStart + totalSkew * (float)percentOfPath); xOffset += (float)System.Math.Sin(angle) * pushX * 0.45f; yOffset = (float)(System.Math.Cos(angle) * (0.5f - yPathScale)) * radiusScale; zOffset = (float)(System.Math.Sin(angle + pushY * 0.9f) * (0.5f - yPathScale)) * radiusScale; // next apply twist rotation to the profile layer if (twistTotal != 0.0f || twistBot != 0.0f) { Quaternion profileRot = new Quaternion(new Vector3(0.0f, 0.0f, 1.0f), twist); foreach (MeshmerizerVertex v in newLayer.vertices) { if (v != null) { vTemp = v * profileRot; v.X = vTemp.X; v.Y = vTemp.Y; v.Z = vTemp.Z; } } } // now orient the rotation of the profile layer relative to it's position on the path // adding pushY to the angle used to generate the quat appears to approximate the viewer Quaternion layerRot = Quaternion.CreateFromAxisAngle(new Vector3(1.0f, 0.0f, 0.0f), (float)angle + pushY * 0.9f); foreach (MeshmerizerVertex v in newLayer.vertices) { if (v != null) { vTemp = v * layerRot; v.X = vTemp.X + xOffset; v.Y = vTemp.Y + yOffset; v.Z = vTemp.Z + zOffset; } } if (angle == startAngle) // the first layer, invert normals { foreach (Triangle t in newLayer.triangles) { t.invertNormal(); } } result.Append(newLayer); int iLastNull = 0; if (lastLayer != null) { int i, count = newLayer.vertices.Count; for (i = 0; i < count; i++) { int iNext = (i + 1); if (lastLayer.vertices[i] == null) // cant make a simplex here { iLastNull = i + 1; } else { if (i == count - 1) // End of list { iNext = iLastNull; } if (lastLayer.vertices[iNext] == null) // Null means wrap to begin of last segment { iNext = iLastNull; } result.Add(new Triangle(newLayer.vertices[i], lastLayer.vertices[i], newLayer.vertices[iNext])); result.Add(new Triangle(newLayer.vertices[iNext], lastLayer.vertices[i], lastLayer.vertices[iNext])); } } } lastLayer = newLayer; // calc the angle for the next interation of the loop if (angle >= endAngle) { done = true; } else { angle = stepSize * ++step; if (angle > endAngle) { angle = endAngle; } } } while (!done); // loop until all the layers in the path are completed return(result); }