private static MeshLOD AdvancedToMesh(this ObjectPart.PrimitiveShape.Decoded shape) { ProfileDetails profile; switch (shape.ShapeType) { case PrimitiveShapeType.Ring: profile = CalcPrismProfile(shape); break; case PrimitiveShapeType.Torus: profile = CalcCylinderProfile(shape); break; case PrimitiveShapeType.Tube: profile = CalcBoxProfile(shape); break; case PrimitiveShapeType.Sphere: profile = CalcSphereProfile(shape); break; default: throw new NotImplementedException(); } double cut = shape.PathBegin; double cutBegin = cut; double cutEnd = shape.PathEnd; double cutStep = (cutEnd - cut) / 36f / shape.Revolutions; double twistBegin = shape.TwistBegin * Math.PI * 2; double twistEnd = shape.TwistEnd * Math.PI * 2; double neededSteps = Math.Max(1, Math.Ceiling((shape.TwistEnd - shape.TwistBegin) / (5 * Math.PI / 180) * (cutEnd - cut))); cutStep /= neededSteps; var mesh = new MeshLOD(); for (; cut < cutEnd; cut += cutStep) { mesh.Vertices.AddRange(profile.ExtrudeAdvanced(shape, twistBegin, twistEnd, cut)); } mesh.Vertices.AddRange(profile.ExtrudeAdvanced(shape, twistBegin, twistEnd, cutEnd)); shape.BuildAdvancedTriangles(mesh, profile, cutBegin, cutEnd); return(mesh); }
private static List <Vector3> ExtrudeBasic(this ProfileDetails path, ObjectPart.PrimitiveShape.Decoded shape, double twistBegin, double twistEnd, double cut) { var extrusionPath = new List <Vector3>(); double twist; Vector3 topSize; Vector3 shear; CalcTopSizeAndShear(shape, twistBegin, twistEnd, cut, out topSize, out shear, out twist); /* generate extrusions */ foreach (var vertex in path.Vertices) { var outvertex = vertex; outvertex.X *= topSize.X; outvertex.Y *= topSize.Y; outvertex = outvertex.Rotate2D_XY(twist); outvertex += shear; outvertex.Z = cut - 0.5; extrusionPath.Add(outvertex); } return(extrusionPath); }
private static Vector3 CalcAdvVertex(this ObjectPart.PrimitiveShape.Decoded shape, Vector3 vertex, double angle, double twist, double cut, Vector3 taper, double radiusOffset) { double pathscale = shape.PathScale.Y; double skew = shape.Skew; double innerpathscale = pathscale - 0.5; var outvertex = vertex; outvertex.X *= taper.X; outvertex.Y *= taper.Y; outvertex = outvertex.Rotate2D_XY(twist); outvertex.Z *= shape.PathScale.X; outvertex.Y *= 1 - Math.Abs(skew); outvertex.Y += skew * (1f - cut); outvertex.Y = innerpathscale.Lerp(0.5, outvertex.Y + 0.5) * radiusOffset; outvertex = outvertex.Rotate2D_YZ(angle); outvertex.X += outvertex.Z * shape.TopShear.X; outvertex.Y += outvertex.Z * shape.TopShear.Y; return(outvertex); }
public bool Run() { PhysicsShapeReference physicsShapeRef; bool success = true; foreach (PrimitiveShapeType shapeType in new PrimitiveShapeType[] { PrimitiveShapeType.Box, PrimitiveShapeType.Cylinder, PrimitiveShapeType.Prism, PrimitiveShapeType.Ring, PrimitiveShapeType.Sphere, PrimitiveShapeType.Torus, PrimitiveShapeType.Tube }) { foreach (PrimitiveProfileShape profileShape in new PrimitiveProfileShape[] { PrimitiveProfileShape.Circle, PrimitiveProfileShape.EquilateralTriangle, PrimitiveProfileShape.HalfCircle, PrimitiveProfileShape.IsometricTriangle, PrimitiveProfileShape.RightTriangle, PrimitiveProfileShape.Square }) { foreach (PrimitiveProfileHollowShape hollowShape in new PrimitiveProfileHollowShape[] { PrimitiveProfileHollowShape.Circle, PrimitiveProfileHollowShape.Same, PrimitiveProfileHollowShape.Square, PrimitiveProfileHollowShape.Triangle }) { foreach (PrimitivePhysicsShapeType physicsShapeType in new PrimitivePhysicsShapeType[] { PrimitivePhysicsShapeType.Convex, PrimitivePhysicsShapeType.Prim }) { foreach (PrimitiveExtrusion pathCurve in new PrimitiveExtrusion[] { PrimitiveExtrusion.Curve1, PrimitiveExtrusion.Curve2, PrimitiveExtrusion.Default, PrimitiveExtrusion.Straight }) { for (double pathbegin = 0; pathbegin < 1; pathbegin += 0.1) { for (double pathend = 0; pathend < 1; pathend += 0.1) { for (double profilebegin = 0; profilebegin < 1; profilebegin += 0.1) { for (double profileend = 0; profileend < 1; profileend += 0.1) { for (double revolutions = 1; revolutions < 4; revolutions += 0.5) { for (double twistbegin = -1; twistbegin < 1; twistbegin += 0.1) { for (double twistend = -1; twistend < 1; twistend += 0.1) { /* * public double RadiusOffset; */ for (double hollow = 0; hollow < 0.90; hollow += 0.1) { for (double skew = -0.9; skew < 0.9; skew += 0.1) { for (double topshearx = -1; topshearx < 1; topshearx += 0.1) { for (double topsheary = -1; topsheary < 1; topsheary += 0.1) { for (double taperx = -1; taperx < 1; taperx += 0.1) { for (double tapery = -1; tapery < 1; tapery += 0.1) { for (double pathscalex = 0; pathscalex < 1; pathscalex += 0.1) { for (double pathscaley = 0; pathscaley < 1; pathscaley += 0.1) { var shape = new ObjectPart.PrimitiveShape.Decoded(); shape.ProfileShape = profileShape; shape.ShapeType = shapeType; shape.HoleShape = hollowShape; shape.ProfileHollow = hollow; shape.PathCurve = pathCurve; shape.Revolutions = revolutions; shape.PathBegin = pathbegin; shape.PathEnd = pathend; shape.TwistBegin = twistbegin; shape.TwistEnd = twistend; shape.ProfileBegin = profilebegin; shape.ProfileEnd = profileend; shape.TopShear.X = topshearx; shape.TopShear.Y = topsheary; shape.Taper.X = taperx; shape.Taper.Y = tapery; shape.PathScale.X = pathscalex; shape.PathScale.Y = pathscaley; shape.Skew = skew; var ps = new ObjectPart.PrimitiveShape { DecodedParams = shape }; m_Log.Info("---- Transformed to physics shape manager accepted format ----"); m_Log.InfoFormat("PhysicsShapeType: {0}/{1}", physicsShapeType, shapeType); m_Log.Info($"Serialized data {physicsShapeType.ToString()}/{ps.Serialization.ToHexString()}"); try { if (!m_PhysicsShapeManager.TryGetConvexShape(physicsShapeType, ps, out physicsShapeRef)) { DumpParams(shape); m_Log.Error("Could not generate physics hull shape"); success = false; continue; } } catch (Exception e) { DumpParams(shape); m_Log.Error("Could not generate physics hull shape", e); continue; } PhysicsConvexShape convexShape = physicsShapeRef; int hullidx = 0; foreach (PhysicsConvexShape.ConvexHull hull in convexShape.Hulls) { m_Log.InfoFormat("Hull {0}: Generated vertices: {1}", hullidx, hull.Vertices.Count); m_Log.InfoFormat("Hull {0}: Generated triangles: {1}", hullidx, hull.Triangles.Count / 3); ++hullidx; } m_Log.InfoFormat("Generated hulls: {0}", hullidx); } } } } } } } } } } } } } } } /* write a blender .raw */ // ((PhysicsConvexShape)physicsShapeRef).DumpToBlenderRaw(m_OutputFileName); } } } } } return(success); }
public void Startup(ConfigurationLoader loader) { IConfig config = loader.Config.Configs[GetType().FullName]; m_AssetService = loader.GetService <AssetServiceInterface>(config.GetString("AssetService", "AssetService")); m_PhysicsShapeManager = loader.GetService <PhysicsShapeManager>(config.GetString("PhysicsShapeManager", "PhysicsShapeManager")); string physicsShapeType = config.GetString("PhysicsShapeType", "prim").ToLowerInvariant(); switch (physicsShapeType) { case "none": m_PhysicsShapeType = PrimitivePhysicsShapeType.None; break; case "prim": m_PhysicsShapeType = PrimitivePhysicsShapeType.Prim; break; case "convex": m_PhysicsShapeType = PrimitivePhysicsShapeType.Convex; break; default: throw new ConfigurationLoader.ConfigurationErrorException(string.Format("Invalid PhysicsShapeType: {0}", physicsShapeType)); } if (config.Contains("HexData")) { ObjectPart.PrimitiveShape p = new ObjectPart.PrimitiveShape { Serialization = config.GetString("HexData").FromHexStringToByteArray() }; m_Shape = p.DecodedParams; } else { string shapeType = config.GetString("ShapeType", "Box").ToLowerInvariant(); switch (shapeType) { case "box": m_Shape.ShapeType = PrimitiveShapeType.Box; break; case "cylinder": m_Shape.ShapeType = PrimitiveShapeType.Cylinder; break; case "prism": m_Shape.ShapeType = PrimitiveShapeType.Prism; break; case "sphere": m_Shape.ShapeType = PrimitiveShapeType.Sphere; break; case "torus": m_Shape.ShapeType = PrimitiveShapeType.Torus; break; case "tube": m_Shape.ShapeType = PrimitiveShapeType.Tube; break; case "ring": m_Shape.ShapeType = PrimitiveShapeType.Ring; break; case "sculpt": m_Shape.ShapeType = PrimitiveShapeType.Sculpt; break; default: throw new ConfigurationLoader.ConfigurationErrorException(string.Format("Invalid ShapeType: {0}", shapeType)); } string sculptType = config.GetString("SculptType", "sphere").ToLowerInvariant(); switch (sculptType) { case "sphere": m_Shape.SculptType = PrimitiveSculptType.Sphere; break; case "torus": m_Shape.SculptType = PrimitiveSculptType.Torus; break; case "plane": m_Shape.SculptType = PrimitiveSculptType.Plane; break; case "cylinder": m_Shape.SculptType = PrimitiveSculptType.Cylinder; break; case "mesh": m_Shape.SculptType = PrimitiveSculptType.Mesh; break; default: throw new ConfigurationLoader.ConfigurationErrorException(string.Format("Invalid SculptType: {0}", sculptType)); } if (config.Contains("SculptMapID")) { m_Shape.SculptMap = new UUID(config.GetString("SculptMapID")); } if (config.Contains("SculptMapFile")) { if (!config.Contains("SculptMapID")) { throw new ConfigurationLoader.ConfigurationErrorException("SculptMap parameter not present"); } byte[] data; using (var fs = new FileStream(config.GetString("SculptMapFile"), FileMode.Open)) { var fileLength = (int)fs.Length; data = new byte[fileLength]; if (fileLength != fs.Read(data, 0, fileLength)) { throw new ConfigurationLoader.ConfigurationErrorException("Failed to load file"); } } var assetdata = new AssetData { Data = data, Type = m_Shape.SculptType == PrimitiveSculptType.Mesh ? AssetType.Mesh : AssetType.Texture, ID = m_Shape.SculptMap, Name = "PrimToMesh imported" }; m_AssetService.Store(assetdata); } if (config.GetBoolean("IsSculptInverted", false)) { m_Shape.IsSculptInverted = true; } if (config.GetBoolean("IsSculptMirrored", false)) { m_Shape.IsSculptMirrored = true; } string profileShape = config.GetString("ProfileShape", "Circle").ToLowerInvariant(); switch (profileShape) { case "circle": m_Shape.ProfileShape = PrimitiveProfileShape.Circle; break; case "square": m_Shape.ProfileShape = PrimitiveProfileShape.Square; break; case "isometrictriangle": m_Shape.ProfileShape = PrimitiveProfileShape.IsometricTriangle; break; case "equilateraltriangle": m_Shape.ProfileShape = PrimitiveProfileShape.EquilateralTriangle; break; case "righttriangle": m_Shape.ProfileShape = PrimitiveProfileShape.RightTriangle; break; case "halfcircle": m_Shape.ProfileShape = PrimitiveProfileShape.HalfCircle; break; default: throw new ConfigurationLoader.ConfigurationErrorException(string.Format("Invalid ProfileShape: {0}", profileShape)); } string holeShape = config.GetString("HollowShape", "Same").ToLowerInvariant(); switch (holeShape) { case "same": m_Shape.HoleShape = PrimitiveProfileHollowShape.Same; break; case "circle": m_Shape.HoleShape = PrimitiveProfileHollowShape.Circle; break; case "square": m_Shape.HoleShape = PrimitiveProfileHollowShape.Square; break; case "triangle": m_Shape.HoleShape = PrimitiveProfileHollowShape.Triangle; break; default: throw new ConfigurationLoader.ConfigurationErrorException(string.Format("Invalid HollowShape: {0}", holeShape)); } m_Shape.ProfileBegin = double.Parse(config.GetString("ProfileBegin", "0"), CultureInfo.InvariantCulture); m_Shape.ProfileEnd = double.Parse(config.GetString("ProfileEnd", "1"), CultureInfo.InvariantCulture); m_Shape.ProfileHollow = double.Parse(config.GetString("ProfileHollow", "0"), CultureInfo.InvariantCulture); m_Shape.IsHollow = m_Shape.ProfileHollow > 0; m_Shape.PathBegin = double.Parse(config.GetString("PathBegin", "0"), CultureInfo.InvariantCulture); m_Shape.PathEnd = double.Parse(config.GetString("PathEnd", "1"), CultureInfo.InvariantCulture); m_Shape.IsOpen = m_Shape.ProfileBegin > 0 || m_Shape.ProfileEnd < 1f; m_Shape.PathScale.X = double.Parse(config.GetString("PathScaleX", "0"), CultureInfo.InvariantCulture); m_Shape.PathScale.Y = double.Parse(config.GetString("PathScaleY", "0"), CultureInfo.InvariantCulture); m_Shape.TopShear.X = double.Parse(config.GetString("TopShearX", "0"), CultureInfo.InvariantCulture); m_Shape.TopShear.Y = double.Parse(config.GetString("TopShearY", "0"), CultureInfo.InvariantCulture); m_Shape.TwistBegin = double.Parse(config.GetString("TwistBegin", "0"), CultureInfo.InvariantCulture); m_Shape.TwistEnd = double.Parse(config.GetString("TwistEnd", "0"), CultureInfo.InvariantCulture); m_Shape.RadiusOffset = double.Parse(config.GetString("RadiusOffset", "0"), CultureInfo.InvariantCulture); m_Shape.Taper.X = double.Parse(config.GetString("TaperX", "0"), CultureInfo.InvariantCulture); m_Shape.Taper.Y = double.Parse(config.GetString("TaperY", "0"), CultureInfo.InvariantCulture); m_Shape.Revolutions = double.Parse(config.GetString("Revolutions", "1"), CultureInfo.InvariantCulture); m_Shape.Skew = double.Parse(config.GetString("Skew", "0"), CultureInfo.InvariantCulture); } m_OutputFileName = config.GetString("OutputFilename"); }
private static ProfileDetails CalcPrismProfile(this ObjectPart.PrimitiveShape.Decoded shape) { /* Prism has cut start at 0,0.5 */ var profile = new ProfileDetails(); double startangle = 2 * Math.PI * shape.ProfileBegin; double endangle = 2 * Math.PI * shape.ProfileEnd; double stepangle = (endangle - startangle) / 60; var angles = shape.CalcBaseAngles(startangle, endangle, stepangle); if (shape.IsHollow) { /* we calculate two points */ double profileHollow = shape.ProfileHollow * 0.5; Vector3 startPoint = Vector3.UnitX * 0.5; foreach (var angle in angles) { Vector3 outerDirectionalVec = CalcTrianglePoint(angle); Vector3 innerDirectionalVec; switch (shape.HoleShape) { case PrimitiveProfileHollowShape.Triangle: case PrimitiveProfileHollowShape.Same: innerDirectionalVec = outerDirectionalVec * profileHollow; break; case PrimitiveProfileHollowShape.Circle: /* circle is simple as we are calculating with such objects */ innerDirectionalVec = startPoint.Rotate2D_XY(angle) * profileHollow; break; case PrimitiveProfileHollowShape.Square: innerDirectionalVec = CalcTopEdgedSquare(angle) * profileHollow; break; default: throw new NotImplementedException(); } /* inner path is reversed */ profile.Vertices.Add(outerDirectionalVec); profile.Vertices.Insert(0, innerDirectionalVec); } /* no center point here, even though we can have path cut */ profile.IsOpenHollow = shape.IsOpen; } else { /* no hollow, so it becomes simple */ foreach (var angle in angles) { Vector3 directionalVec = CalcTrianglePoint(angle); profile.Vertices.Add(directionalVec); } if (shape.IsOpen) { profile.Vertices.Add(new Vector3(0, 0, 0)); } else { profile.Vertices.RemoveAt(profile.Vertices.Count - 1); } } return(profile); }
private static ProfileDetails CalcBoxProfile(this ObjectPart.PrimitiveShape.Decoded shape) { /* Box has cut start at 0.5,-0.5 */ var profile = new ProfileDetails(); double startangle = 2 * Math.PI * shape.ProfileBegin; double endangle = 2 * Math.PI * shape.ProfileEnd; double stepangle = (endangle - startangle) / 60; List <double> angles = shape.CalcBaseAngles(startangle, endangle, stepangle); if (shape.IsHollow) { /* we calculate two points */ Vector3 startPoint = START_VECTOR_BOX * 0.5; foreach (var angle in angles) { Vector3 outerDirectionalVec = startPoint.Rotate2D_XY(angle); Vector3 innerDirectionalVec = outerDirectionalVec; /* outer normalize on single component to 0.5, simplifies algorithm */ outerDirectionalVec = outerDirectionalVec.CalcPointToSquareBoundary(1); switch (shape.HoleShape) { case PrimitiveProfileHollowShape.Triangle: innerDirectionalVec = CalcTrianglePoint(angle).Rotate2D_XY(-2.3561944901923448) * shape.ProfileHollow * 0.5; break; case PrimitiveProfileHollowShape.Circle: /* circle is simple as we are calculating with such objects */ innerDirectionalVec *= shape.ProfileHollow; break; case PrimitiveProfileHollowShape.Same: case PrimitiveProfileHollowShape.Square: /* inner normalize on single component to 0.5 * hollow */ innerDirectionalVec = innerDirectionalVec.CalcPointToSquareBoundary(shape.ProfileHollow); break; default: throw new NotImplementedException(); } /* inner path is reversed */ profile.Vertices.Add(outerDirectionalVec); profile.Vertices.Insert(0, innerDirectionalVec); } /* no center point here, even though we can have path cut */ profile.IsOpenHollow = shape.IsOpen; } else { /* no hollow, so it becomes simple */ Vector3 startPoint = START_VECTOR_BOX * 0.5; foreach (var angle in angles) { Vector3 directionalVec = startPoint.Rotate2D_XY(angle); /* normalize on single component to 0.5, simplifies algorithm */ directionalVec = directionalVec.CalcPointToSquareBoundary(1); profile.Vertices.Add(directionalVec); } if (shape.IsOpen) { profile.Vertices.Add(new Vector3(0, 0, 0)); } else { profile.Vertices.RemoveAt(profile.Vertices.Count - 1); } } return(profile); }
private static void BuildBasicTriangles(this ObjectPart.PrimitiveShape.Decoded shape, MeshLOD mesh, ProfileDetails path, double cutBegin, double cutEnd) { double twistBegin = shape.TwistBegin * Math.PI; double twistEnd = shape.TwistEnd * Math.PI; int verticeRowCount = path.Vertices.Count; int verticeTotalCount = mesh.Vertices.Count; int verticeRowEndCount = verticeRowCount; if (!shape.IsOpen && shape.IsHollow) { --verticeRowEndCount; } /* generate z-triangles */ for (int l = 0; l < verticeRowEndCount; ++l) { if (!shape.IsOpen && shape.IsHollow && l == verticeRowCount / 2 - 1) { continue; } for (int z = 0; z < verticeTotalCount - verticeRowCount; z += verticeRowCount) { /* p0 ___ p1 */ /* | | */ /* |___| */ /* p3 p2 */ /* tris: p0, p1, p2 and p0, p3, p2 */ /* p2 and p3 are on next row */ int z2 = z + verticeRowCount; int l2 = (l + 1) % verticeRowCount; /* loop closure */ var tri = new Triangle { Vertex1 = z + l, Vertex2 = z2 + l2, Vertex3 = z + l2 }; mesh.Triangles.Add(tri); tri = new Triangle { Vertex1 = z + l, Vertex2 = z2 + l, Vertex3 = z2 + l2 }; mesh.Triangles.Add(tri); } } /* generate top and bottom triangles */ if (shape.IsHollow) { /* simpler just close neighboring dots */ /* no need for uneven check here. * The path generator always generates two pathes here which are connected and therefore always a multiple of two */ int bottomIndex = verticeTotalCount - verticeRowCount; for (int l = 0; l < verticeRowCount / 2; ++l) { int l2 = verticeRowCount - l - 1; var tri = new Triangle { Vertex1 = l, Vertex2 = l2, Vertex3 = l + 1 }; mesh.Triangles.Add(tri); tri = new Triangle { Vertex1 = l + 1, Vertex2 = l2, Vertex3 = l2 - 1 }; mesh.Triangles.Add(tri); tri = new Triangle { Vertex1 = l + bottomIndex, Vertex2 = l2 + bottomIndex, Vertex3 = l + 1 + bottomIndex }; mesh.Triangles.Add(tri); tri = new Triangle { Vertex1 = l + 1 + bottomIndex, Vertex2 = l2 + bottomIndex, Vertex3 = l2 - 1 + bottomIndex }; mesh.Triangles.Add(tri); } } else { /* build a center point and connect all vertexes with triangles */ int centerpointTop = mesh.Vertices.Count; int bottomIndex = verticeTotalCount - verticeRowCount; mesh.Vertices.Add(ApplyTortureParams(shape, new Vector3(0, 0, 0), twistBegin, twistEnd, cutBegin)); int centerpointBottom = mesh.Vertices.Count; mesh.Vertices.Add(ApplyTortureParams(shape, new Vector3(0, 0, 0), twistBegin, twistEnd, cutEnd)); for (int l = 0; l < verticeRowCount; ++l) { int l2 = (l + 1) % verticeRowCount; var tri = new Triangle { Vertex1 = l, Vertex2 = l2, Vertex3 = centerpointTop }; mesh.Triangles.Add(tri); tri = new Triangle { Vertex1 = l + bottomIndex, Vertex2 = l2 + bottomIndex, Vertex3 = centerpointBottom }; mesh.Triangles.Add(tri); } } }
private static ProfileDetails CalcSphereProfile(this ObjectPart.PrimitiveShape.Decoded shape) { /* calculate a half-sphere here * starting from UnitX to -UnitX */ var profile = new ProfileDetails(); double startangle = Math.PI * shape.ProfileBegin; double endangle = Math.PI * shape.ProfileEnd; double stepangle = (endangle - startangle) / 60; if (shape.IsHollow) { /* we calculate two points */ Vector3 startPoint = Vector3.UnitX * 0.5; for (; startangle < endangle; startangle += stepangle) { Vector3 outerDirectionalVec = startPoint.Rotate2D_XY(startangle); Vector3 innerDirectionalVec; switch (shape.HoleShape) { case PrimitiveProfileHollowShape.Triangle: /* Even though, the option is called Triangle. It is actually a Trapezoid. */ innerDirectionalVec = CalcTrapezoidInSpherePoint(startangle) * (shape.ProfileHollow * 0.5); break; case PrimitiveProfileHollowShape.Same: case PrimitiveProfileHollowShape.Circle: /* circle is simple as we are calculating with such objects */ innerDirectionalVec = CalcTrianglePoint(startangle) * shape.ProfileHollow; break; case PrimitiveProfileHollowShape.Square: innerDirectionalVec = CalcSquareInSpherePoint(startangle) * (shape.ProfileHollow * 0.5); break; default: throw new NotImplementedException(); } /* inner path is reversed */ profile.Vertices.Add(outerDirectionalVec); profile.Vertices.Insert(0, innerDirectionalVec); } /* no center point here, even though we can have path cut */ profile.IsOpenHollow = shape.IsOpen; } else { /* no hollow, so it becomes simple */ Vector3 startPoint = Vector3.UnitX * 0.5; for (; startangle < endangle; startangle += stepangle) { Vector3 directionalVec = startPoint.Rotate2D_XY(startangle); profile.Vertices.Add(directionalVec); } if (shape.IsOpen) { profile.Vertices.Add(new Vector3(0, 0, 0)); } } return(profile); }
internal static MeshLOD SculptMeshToMesh(this Bitmap bitmap, ObjectPart.PrimitiveShape.Decoded shape, bool generate_uv = false) { bool mirror = shape.IsSculptMirrored; var mesh = new MeshLOD(); bool reverse_horizontal = shape.IsSculptInverted ? !mirror : mirror; PrimitiveSculptType sculptType = shape.SculptType; int sculptSizeS; int sculptSizeT; int sculptVerts = bitmap.Width * bitmap.Height / 4; if (sculptVerts > 32 * 32) { sculptVerts = 32 * 32; } double ratio = (double)bitmap.Width / bitmap.Height; sculptSizeS = (int)Math.Sqrt(sculptVerts / ratio); sculptSizeS = Math.Max(sculptSizeS, 4); sculptSizeT = sculptVerts / sculptSizeS; sculptSizeT = Math.Max(sculptSizeT, 4); sculptSizeS = sculptVerts / sculptSizeT; /* generate vertex map */ for (int s = 0; s < sculptSizeS; ++s) { for (int t = 0; t < sculptSizeT; ++t) { int reversed_t = t; if (reverse_horizontal) { reversed_t = sculptSizeT - t - 1; } var x = (int)((double)reversed_t / (sculptSizeT - 1) * bitmap.Width); var y = (int)((double)s / (sculptSizeS - 1) * bitmap.Height); if (y == 0) { if (sculptType == PrimitiveSculptType.Sphere) { x = bitmap.Width / 2; } } else if (y == bitmap.Height) { y = (sculptType == PrimitiveSculptType.Torus) ? 0 : bitmap.Height - 1; if (sculptType == PrimitiveSculptType.Sphere) { x = bitmap.Width / 2; } } if (x == bitmap.Width) { switch (sculptType) { case PrimitiveSculptType.Sphere: case PrimitiveSculptType.Torus: case PrimitiveSculptType.Cylinder: x = 0; break; default: x = bitmap.Width - 1; break; } } Vector3 v = bitmap.GetVertex(x, y, mirror); mesh.Vertices.Add(v); if (generate_uv) { var uv = new UVCoord { U = (float)reversed_t / (sculptSizeS - t), V = (float)s / (sculptSizeS - 1) }; mesh.UVCoords.Add(uv); } } } /* generate triangles */ for (int row = 0; row < sculptSizeS - 1; ++row) { int rowIndex = row * sculptSizeT; int row2Index = rowIndex + sculptSizeT; for (int col = 0; col < sculptSizeT - 1; ++col) { var tri = new Triangle { Vertex1 = rowIndex + col, Vertex2 = row2Index + col + 1, Vertex3 = rowIndex + col + 1 }; mesh.Triangles.Add(tri); tri = new Triangle { Vertex1 = rowIndex + col, Vertex2 = row2Index + col, Vertex3 = row2Index + col + 1 }; mesh.Triangles.Add(tri); } } return(mesh); }