private void MainForm_MouseClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { if (points == null) { points = new List<DelaunauTriangulationSample.Classes.Point>(); g.Clear(Color.White); } DelaunauTriangulationSample.Classes.Point mbToAdd = new DelaunauTriangulationSample.Classes.Point(e.X, Size.Height - e.Y); if (!points.Contains(mbToAdd)) points.Add(mbToAdd); g.DrawEllipse(new Pen(Color.Red), e.X - 1, e.Y - 1, 3, 3); } else { if (points == null) { g.Clear(Color.White); Random rnd = new Random(); points = new List<DelaunauTriangulationSample.Classes.Point>(); for (int i = 0; i < Count.Value; i++) { DelaunauTriangulationSample.Classes.Point tmp = new DelaunauTriangulationSample.Classes.Point(rnd.Next(Size.Width - 200) + 30, rnd.Next(Size.Height - 100) + 50); if (!points.Contains(tmp)) points.Add(tmp); g.DrawEllipse(defaultPointPen, (int)tmp.X - 1, Size.Height - (int)tmp.Y + 1, 3, 3); } } Triangulation tb = new Triangulation(points, g, defaultLinePen, defaultPointPen, defaultNewLintPen, Size.Height, Delay.Value); points = null; } }
public VoronoiDiagram(MeshFilter mesh, Transform transform) { _initial = new Triangle(new Pnt(-10000, -10000), new Pnt(10000, -10000), new Pnt(0, 10000)); _delaunay = new Triangulation(_initial); _mesh = mesh; _transform = transform; }
public void UpdateTriangulation(Triangulation tri) { TriangulationView.UpdateTriangulation(tri); SetNeedsDisplay(); }
public static Solid CreateSolidRevolve(List <List <Entity> > entitiyLoops, float angle, float helixStep, Vector3 axis, Vector3 origin, float angleStep, UnityEngine.Matrix4x4 tf, IdPath feature) { var ids = new List <List <Id> >(); var polygons = Sketch.GetPolygons(entitiyLoops, ref ids); bool isHelix = (Math.Abs(helixStep) > 1e-6); if (!isHelix && Mathf.Abs(angle) > 360f) { angle = Mathf.Sign(angle) * 360f; } bool inversed = angle < 0f; int subdiv = (int)Mathf.Ceil(Math.Abs(angle) / angleStep); var drot = UnityEngine.Matrix4x4.Translate(origin) * UnityEngine.Matrix4x4.Rotate(Quaternion.AngleAxis(angle / subdiv, axis)) * UnityEngine.Matrix4x4.Translate(-origin); Func <float, Vector3, Vector3> PointOn = (float a, Vector3 point) => { var ax = axis; var axn = axis.normalized; var t = a / 360.0f; var o = origin; var prj = ExpVector.ProjectPointToLine(point, o, o + ax); var ra = Mathf.Atan2(helixStep / 4.0f, (point - prj).magnitude); var res = ExpVector.RotateAround(point, point - prj, o, ra); res = ExpVector.RotateAround(res, ax, o, a * Mathf.PI / 180.0f); return(res + axn * t * helixStep); }; var polys = new List <Polygon>(); Func <Vector3, Vector3> TF = v => tf.MultiplyPoint(v); for (int pi = 0; pi < polygons.Count; pi++) { var p = polygons[pi]; var pid = ids[pi]; var pv = new List <Vector3>(p); var triangles = Triangulation.Triangulate(pv); IdPath polyId = feature.With(new Id(-1)); bool invComp = true; if (Math.Abs(Math.Abs(angle) - 360f) > 1e-6 || isHelix) { float a = 0f; for (int side = 0; side < 2; side++) { for (int i = 0; i < triangles.Count / 3; i++) { var polygonVertices = new List <Vertex>(); for (int j = 0; j < 3; j++) { polygonVertices.Add(PointOn(a, TF(triangles[i * 3 + j])).ToVertex()); } if (inversed == invComp) { polygonVertices.Reverse(); } polys.Add(new Polygon(polygonVertices, polyId)); } polyId = feature.With(new Id(-2)); invComp = false; a = angle; } } Dictionary <Id, IdPath> paths = new Dictionary <Id, IdPath>(); for (int i = 0; i < p.Count; i++) { float a = 0f; float da = angle / subdiv; for (int j = 0; j < subdiv; j++) { var polygonVertices = new List <Vertex>(); polygonVertices.Add(PointOn(a, TF(p[(i + 1) % p.Count])).ToVertex()); polygonVertices.Add(PointOn(a + da, TF(p[(i + 1) % p.Count])).ToVertex()); polygonVertices.Add(PointOn(a + da, TF(p[i])).ToVertex()); polygonVertices.Add(PointOn(a, TF(p[i])).ToVertex()); a += da; if (!inversed) { polygonVertices.Reverse(); } IdPath curPath = null; if (!paths.ContainsKey(pid[i])) { curPath = feature.With(pid[i]); paths.Add(pid[i], curPath); } else { curPath = paths[pid[i]]; } if (isHelix) { var verts = new List <Vertex>(); verts.Add(polygonVertices[0]); verts.Add(polygonVertices[1]); verts.Add(polygonVertices[2]); polys.Add(new Polygon(verts, curPath)); verts = new List <Vertex>(); verts.Add(polygonVertices[0]); verts.Add(polygonVertices[2]); verts.Add(polygonVertices[3]); polys.Add(new Polygon(verts, curPath)); } else { polys.Add(new Polygon(polygonVertices, curPath)); } } } } return(Solid.FromPolygons(polys)); }
public void setTriangulateData(Triangulation.Delaunay delaunayTriangulation) { this.triangulation = delaunayTriangulation; if (this.triangulation != null && this.triangulation.pointList != null && this.triangulation.triangleList != null && this.triangulation.pointList.Count != 0 && this.triangulation.triangleList.Count != 0) { setVertices(triangulation.pointList); setMap(); setIndicesMesh(); CreateMesh(); } }
public MeshPrimitive GenerateTriangleMesh(Triangulation triangulation, IEnumerable <Vector4> colors = null, PBRTexture texture = null) { return(GenerateTriangleMesh(triangulation.Positions, triangulation.Indices.ToList(), colors, texture)); }
/// <summary> /// Generate a triangle mesh from supplied height map, triangulating and optionaly mapping UVs /// </summary> /// <param name="heightMap"></param> /// <param name="colors"></param> /// <param name="texture">Texture path relative from the model</param> /// <returns></returns> public MeshPrimitive GenerateTriangleMesh(HeightMap heightMap, IEnumerable <Vector4> colors = null, PBRTexture texture = null) { Triangulation triangulation = _meshService.TriangulateHeightMap(heightMap); return(GenerateTriangleMesh(triangulation, colors, texture)); }
private List <SlicedMesh> GenerateMesh(SlicedMesh original, Plane plane, bool positiveSide, Transform objectTransform) { SlicedMesh slicePositive = new SlicedMesh(); SlicedMesh sliceNegative = new SlicedMesh(); //polygon we use to fill in the sliced surface PolygonCreator polygonPositive = new PolygonCreator(objectTransform, plane.normal * -1); PolygonCreator polygonNegative = new PolygonCreator(objectTransform, plane.normal); bool matPositiveAdded = false, matNegativeAdded = false; //we loop over all submeshes for (int submesh = 0; submesh < original.triangles.Length; submesh++) { int[] originalTriangles = original.triangles[submesh]; setEdge = false; //increase t by 3 because a triangle consist out of 3 vertices; for (int t = 0; t < originalTriangles.Length; t += 3) { //which triangle we need int t1 = t, t2 = t + 1, t3 = t + 2; //Check if vertice is on positive side of the plane bool sideA = plane.GetSide(original.vertices[originalTriangles[t1]]) == positiveSide; bool sideB = plane.GetSide(original.vertices[originalTriangles[t2]]) == positiveSide; bool sideC = plane.GetSide(original.vertices[originalTriangles[t3]]) == positiveSide; //how many vertices are on the positive side of the plane int sideCount = (sideA ? 1 : 0) + (sideB ? 1 : 0) + (sideC ? 1 : 0); //if none of the vertices is located on the positive side if (sideCount == 0) { //add entire triangle to negative side sliceNegative.AddTriangle(submesh, original.vertices[originalTriangles[t1]], original.vertices[originalTriangles[t2]], original.vertices[originalTriangles[t3]], original.normals[originalTriangles[t1]], original.normals[originalTriangles[t2]], original.normals[originalTriangles[t3]], original.uv[originalTriangles[t1]], original.uv[originalTriangles[t2]], original.uv[originalTriangles[t3]]); if (!matNegativeAdded) { matNegativeAdded = true; sliceNegative.materialIndex.Add(submesh); } continue; } //if all the vertices are located on the positive side else if (sideCount == 3) { //add entire triangle to positive side slicePositive.AddTriangle(submesh, original.vertices[originalTriangles[t1]], original.vertices[originalTriangles[t2]], original.vertices[originalTriangles[t3]], original.normals[originalTriangles[t1]], original.normals[originalTriangles[t2]], original.normals[originalTriangles[t3]], original.uv[originalTriangles[t1]], original.uv[originalTriangles[t2]], original.uv[originalTriangles[t3]]); if (!matPositiveAdded) { matPositiveAdded = true; slicePositive.materialIndex.Add(submesh); } continue; } //else a triangle is cut and submesh material must be added to both sides if (!matNegativeAdded) { matNegativeAdded = true; sliceNegative.materialIndex.Add(submesh); } if (!matPositiveAdded) { matPositiveAdded = true; slicePositive.materialIndex.Add(submesh); } //determines which vertex in the triangle is solely located on one side of the plane int singleIndex = sideB == sideC ? 0 : sideA == sideC ? 1 : 2; int indexB = t + ((singleIndex + 1) % 3), indexC = t + ((singleIndex + 2) % 3); singleIndex += t; //calculate which vertices/normals/uv should be used to calculate intersection points Vector3 singleVertex = original.vertices[originalTriangles[singleIndex]], vertexB = original.vertices[originalTriangles[indexB]], //right vertex vertexC = original.vertices[originalTriangles[indexC]]; //left vertex Vector3 singleNormal = original.normals[originalTriangles[singleIndex]], normalB = original.normals[originalTriangles[indexB]], normalC = original.normals[originalTriangles[indexC]]; Vector2 singleUv = original.uv[originalTriangles[singleIndex]], uvB = original.uv[originalTriangles[indexB]], uvC = original.uv[originalTriangles[indexC]]; //calculate new vertices/normals/uv where edge intersects plane float lerpB, lerpC; Vector3 newVertexB = PointOnPlane(plane, singleVertex, vertexB, out lerpB), //new right vertex newVertexC = PointOnPlane(plane, singleVertex, vertexC, out lerpC); //new left vertex Vector3 newNormalB = Vector3.Lerp(singleNormal, normalB, lerpB), //lerp to get the point between the old vertices where the new vertex is located newNormalC = Vector3.Lerp(singleNormal, normalC, lerpC); Vector2 newUvB = Vector2.Lerp(singleUv, uvB, lerpB), newUvC = Vector2.Lerp(singleUv, uvC, lerpC); if (!concave) { //add and edge to "fill" the mesh AddSliceTriangle(submesh, slicePositive, newVertexB, newVertexC, plane.normal * -1, newUvB, newUvC); AddSliceTriangle(submesh, sliceNegative, newVertexB, newVertexC, plane.normal, newUvB, newUvC); } if (sideCount == 1) { //positive data slicePositive.AddTriangle(submesh, singleVertex, newVertexB, newVertexC, singleNormal, newNormalB, newNormalC, singleUv, newUvB, newUvC); //negative data sliceNegative.AddTriangle(submesh, newVertexB, vertexB, vertexC, newNormalB, normalB, normalC, newUvB, uvB, uvC); sliceNegative.AddTriangle(submesh, newVertexB, vertexC, newVertexC, newNormalB, normalC, newNormalC, newUvB, uvC, newUvC); if (concave) { //positive Edge edgePositive = new Edge(newVertexB, newVertexC, plane.normal * -1, newUvB, newUvC); polygonPositive.AddEdge(edgePositive); //negative Edge edgeNegative = new Edge(newVertexC, newVertexB, plane.normal, newUvC, newUvB); polygonNegative.AddEdge(edgeNegative); } continue; } else if (sideCount == 2) { //positive data slicePositive.AddTriangle(submesh, newVertexB, vertexB, vertexC, newNormalB, normalB, normalC, newUvB, uvB, uvC); slicePositive.AddTriangle(submesh, newVertexB, vertexC, newVertexC, newNormalB, normalC, newNormalC, newUvB, uvC, newUvC); //negative data sliceNegative.AddTriangle(submesh, singleVertex, newVertexB, newVertexC, singleNormal, newNormalB, newNormalC, singleUv, newUvB, newUvC); if (concave) { //positive Edge edgePositive = new Edge(newVertexC, newVertexB, plane.normal * -1, newUvC, newUvB); polygonPositive.AddEdge(edgePositive); //negative Edge edgeNegative = new Edge(newVertexB, newVertexC, plane.normal, newUvB, newUvC); polygonNegative.AddEdge(edgeNegative); } continue; } } } if (concave) { //build polygons polygonPositive.ConnectEdges(); polygonNegative.ConnectEdges(); //build meshdata for polygons FinishedPolygon polygonPositiveFinished = Triangulation.Triangulate(polygonPositive, slicePositive.VertexCount); FinishedPolygon polygonNegativeFinished = Triangulation.Triangulate(polygonNegative, sliceNegative.VertexCount); //add meshdata to slices slicePositive.AddPolygon(polygonPositiveFinished); sliceNegative.AddPolygon(polygonNegativeFinished); } slicePositive.FillArray(correctData); sliceNegative.FillArray(correctData); return(new List <SlicedMesh>() { slicePositive, sliceNegative }); }
/// <summary> /// Function to display only a specified range (i.e., subset) of indices of the FrequencyPolygons. /// </summary> /// <param name="startIndex">Start index (including).</param> /// <param name="endIndex">End index (including).</param> public void displayRange(int startIndex, int endIndex) { // get positions for selected range Vector3[] rangePositions = frequencyPointPositionsToArrayForRange(fpcList, startIndex, endIndex); // update LineRenderer's positions (if required) if (tdrcInterface.cnfg2d_isFrequencyPolygonLineEnabled) { // line renderer incl. connecting to the 2D Frequency Polygon origin axis lineRenderer.positionCount = rangePositions.Length + 2; Vector3[] lrPoints = new Vector3[lineRenderer.positionCount]; lrPoints[0] = new Vector3(rangePositions[0].x, origin.y, origin.z); // add one point at the start for (int i = 1; i <= rangePositions.Length; i++) { lrPoints[i] = rangePositions[i - 1]; // add points for the Frequency Polygon } lrPoints[lineRenderer.positionCount - 1] = new Vector3(rangePositions[rangePositions.Length - 1].x, origin.y, origin.z); // add one point at the end lineRenderer.SetPositions(lrPoints); // set line renderer positions } // mesh for renderering the FrequencyPolygon in the UI layer if (tdrcInterface.cnfg2d_isFrequencyPolygonEnabled) { // init mesh and required variables Mesh polygonMesh = new Mesh(); List <Vector2> meshPoints = new List <Vector2>(); List <int> indices = null; List <Vector3> vertices = null; List <List <Vector2> > holesList = new List <List <Vector2> >(); // setup mesh coordinates meshPoints.Add(new Vector2(rangePositions[0].x, origin.y)); // add one point at origin at the start of the polygon foreach (Vector3 v in rangePositions) { meshPoints.Add(new Vector2(v.x, v.y)); // add points for the Frequency Polygon } meshPoints.Add(new Vector2(rangePositions[rangePositions.Length - 1].x, origin.y)); // add one end point at the end of the polygon // perform Triangulation // Developer Note: This is based on the implemented PolyExtruder package, Source: https://github.com/nicoversity/unity_polyextruder Triangulation.triangulate(meshPoints, holesList, 0.0f, out indices, out vertices); // update mesh with new data polygonMesh.Clear(); polygonMesh.vertices = vertices.ToArray(); polygonMesh.triangles = indices.ToArray(); polygonMesh.RecalculateNormals(); polygonMesh.RecalculateBounds(); // setup mesh in the UI GameObject canvasRendererRef.SetMesh(polygonMesh); // IN_ENGINE_SCREENSHOT_CAPTURE // Developer Note: Enable this to capture polygons in 360 screenshot capture. //MeshFilter mf = canvasRendererRef.gameObject.GetComponent<MeshFilter>(); //if (mf == null) mf = canvasRendererRef.gameObject.AddComponent<MeshFilter>(); //mf.mesh = polygonMesh; //MeshRenderer mr = canvasRendererRef.gameObject.GetComponent<MeshRenderer>(); //if (mr == null) mr = canvasRendererRef.gameObject.AddComponent<MeshRenderer>(); } }
/// <summary> /// Initialize the display / drawing of the FrequencyPolygon based on its input data. /// </summary> /// <param name="fpl">List of FrequencyPoints representing the Frequency Polygon.</param> private void initFrequencyPolygonFromListData(List <TDRCFrequencyPoint> fpl) { // init FrequencyPointComponents for (int i = 0; i < fpl.Count; i++) { // calculate and set position float yPos = getDataValueForVisualizationBasedOnTDRCInterfaceConfig(fpl[i].value, tdrcInterface); // transform original data value based on configured options fpl[i].pos = new Vector3(i * tdrcInterface.cnfg2d_frequencyPointDistance, yPos, 0.0f); // distance between data points as configures in the 3D Radar Chart's Interface //fpl[i].pos = new Vector3(i, yPos, 0.0f); // distance between data points is default (i.e., 1.0f) fpl[i].color = pointColor; // keep track of color configuration // setup new GameObject Transform p = Instantiate((Resources.Load(frequencyPointSpherePrefabRef) as GameObject).transform); p.parent = pointsRef; p.localPosition = fpl[i].pos; p.localScale = Vector3.one * tdrcInterface.cnfg2d_frequencyPointScale; p.name = fpl[i].dimension + "_" + fpl[i].time; p.gameObject.GetComponent <Renderer>().material.color = pointColor; // attach and set FrequencyPoint data structure to GameObject TDRCFrequencyPointComponent fpc = p.gameObject.AddComponent <TDRCFrequencyPointComponent>(); fpc.fp = fpl[i]; // keep track of all instantiated FrequencyPointComponents fpcList.Add(fpc); } // update length property for drawing length = fpcList[fpcList.Count - 1].transform.localPosition.x; // init LineRenderer component if (tdrcInterface.cnfg2d_isFrequencyPolygonLineEnabled) { // initialize all LineRenderer component related properties lineRenderer = pointsRef.gameObject.AddComponent <LineRenderer>(); lineRenderer.startWidth = lineWidth; lineRenderer.endWidth = lineWidth; lineRenderer.useWorldSpace = false; lineRenderer.material = new Material(Shader.Find("Standard")); lineRenderer.material.color = pointColor; // update LineRenderer's positions // // line renderer only using original points //lr.positionCount = fpl.Count; //lr.SetPositions(frequencyPointPositionsToArray(fpl)); // line renderer incl. connecting to the 2D Frequency Polygon origin axis lineRenderer.positionCount = fpl.Count + 2; Vector3[] lrPoints = new Vector3[lineRenderer.positionCount]; lrPoints[0] = new Vector3(origin.x, origin.y, origin.z); // add one point at the start for (int i = 1; i <= fpl.Count; i++) { lrPoints[i] = fpl[i - 1].pos; // add points for the Frequency Polygon } lrPoints[lineRenderer.positionCount - 1] = new Vector3(fpl[fpl.Count - 1].pos.x, origin.y, origin.z); // add one point at the end lineRenderer.SetPositions(lrPoints); // set line renderer positions } // init mesh for renderering the FrequencyPolygon (in the UI layer) if (tdrcInterface.cnfg2d_isFrequencyPolygonEnabled) { // init mesh and required variables Mesh polygonMesh = new Mesh(); List <Vector2> meshPoints = new List <Vector2>(); List <int> indices = null; List <Vector3> vertices = null; List <List <Vector2> > holesList = new List <List <Vector2> >(); // setup mesh coordinates Vector3[] originalPositions = frequencyPointPositionsToArray(fpl); meshPoints.Add(new Vector2(origin.x, origin.y)); // add one point at origin at the start of the polygon foreach (Vector3 v in originalPositions) { meshPoints.Add(new Vector2(v.x, v.y)); // add points for the Frequency Polygon } meshPoints.Add(new Vector2(originalPositions[originalPositions.Length - 1].x, origin.y)); // add one end point at the end of the polygon // perform Triangulation // Developer Note: This is based on the implemented PolyExtruder package, Source: https://github.com/nicoversity/unity_polyextruder Triangulation.triangulate(meshPoints, holesList, 0.0f, out indices, out vertices); // update mesh with new data polygonMesh.Clear(); polygonMesh.vertices = vertices.ToArray(); polygonMesh.triangles = indices.ToArray(); polygonMesh.RecalculateNormals(); polygonMesh.RecalculateBounds(); // setup mesh in the UI GameObject canvasRendererRef.SetMesh(polygonMesh); // setup the mesh's material Material polygonMeshMat = new Material(Shader.Find("UI/Default")); polygonMeshMat.color = polygonColor; canvasRendererRef.SetMaterial(polygonMeshMat, null); // rotate due to Triangulation (i.e., PolyExtruder) implementation canvasRendererRef.transform.Rotate(Vector3.right * -90.0f); // IN_ENGINE_SCREENSHOT_CAPTURE // Developer Note: Enable this to capture polygons in 360 screenshot capture. //MeshFilter mf = canvasRendererRef.gameObject.GetComponent<MeshFilter>(); //if (mf == null) mf = canvasRendererRef.gameObject.AddComponent<MeshFilter>(); //mf.mesh = polygonMesh; //MeshRenderer mr = canvasRendererRef.gameObject.GetComponent<MeshRenderer>(); //if (mr == null) mr = canvasRendererRef.gameObject.AddComponent<MeshRenderer>(); //mr.material = polygonMeshMat; } }
// Update is called once per frame void Update() { float rt = Time.realtimeSinceStartup; if (doTriangulation) { doTriangulation = false; if (!firstTriangulationDone) { Debug.Log("Get random points"); // create random points randomPoints = new RandomPoints(max_x, max_z, Yplane); points = randomPoints.CreateRandomPoints(pointsNum, VERBOSE); Debug.Log("Compute triangulation"); triangulation = null; triangulation = new Triangulation(max_x, max_z, Yplane, points, VERBOSE, validateAfterEveryPoint); triangulation.ComputeTriangulation(); firstTriangulationDone = true; if (showTimer) { Debug.Log("(Triangulation) Timer: " + (Time.realtimeSinceStartup - rt) + " s"); } } else { Debug.LogError("Triangulation has already been computed."); } } if (doVoronoi) { doVoronoi = false; if (!firstVoronoiDone && firstTriangulationDone) { Debug.Log("Convert triangulation to Voronoi"); ConvertTriangulationToVoronoi(); firstVoronoiDone = true; int numOfValidCells = 0; foreach (VoronoiCell cell in voronoi.voronoiCells) { if (cell.isValid) { numOfValidCells++; } } Debug.Log("Number of cells = " + numOfValidCells); } else if (!firstTriangulationDone) { Debug.LogError("Triangulation must be computed before the Voronoi."); } else { Debug.LogError("First Voronoi has already been computed."); } } if (doRelaxation) { doRelaxation = false; if (firstVoronoiDone) { if (relaxTimes < 0) { relaxTimes = 0; } Debug.Log("The Voronoi will be relaxed " + relaxTimes + " time" + (relaxTimes != 1 ? "s." : ".")); while (relaxTimesCounter < relaxTimes) { totalRelaxesDone++; Debug.Log("Apply Relaxation #" + totalRelaxesDone); RelaxVoronoi(); ConvertTriangulationToVoronoi(); relaxTimesCounter++; if (showTimer) { Debug.Log("(Relaxation) Timer: " + (Time.realtimeSinceStartup - rt) + " s"); } } relaxTimesCounter = 0; } else { Debug.LogError("The first Voronoi must be computed before the diagram is relaxed."); } } if (doMesh) { doMesh = false; if (!meshesCreated && firstVoronoiDone) { Debug.Log("Generate meshes"); CreateMeshColumns(); FindCellNeighbors(); meshesCreated = true; } else if (meshesCreated) { Debug.LogError("Meshes have already been generated."); } else { Debug.LogError("The first Voronoi must be computed before the meshes are created."); } } if (doPerlin) { doPerlin = false; if (meshesCreated) { Debug.Log("Apply Perlin Noise to column heights"); ApplyPerlinToVoronoi(); } else { Debug.LogError("Meshes must be generated before noise is applied."); } } if (doRandomRipple) { doRandomRipple = false; doRandomPathing = false; if (meshesCreated) { Debug.Log("Do random ripple (may need to reset first)"); // rippleResetNeeded = true; ChooseRandomObjectToStartRipple(); } else { Debug.LogError("Meshes must be generated before ripple is applied."); } } if (doRandomPathing) { doRandomPathing = false; if (meshesCreated) { Debug.Log("Do random pathing (may need to reset first)"); // rippleResetNeeded = true; ChooseRandomObjectToStartPathing(); } else { Debug.LogError("Meshes must be generated before pathing is applied."); } } if (resetRipples) { resetRipples = false; if (meshesCreated) { Debug.Log("Reset ripple"); // reset all RippleColumn foreach (GameObject obj in meshObjects) { obj.GetComponent <RippleColumn>().RESET = true; } // rippleResetNeeded = false; } } //showTimer = false; // so timer only gets shown the first time Update() runs }
private void Update() { // Mouse left button released if (Input.GetMouseButtonUp(0)) { Ray ray = _raycastCamera.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit)) { _lineRenderer.startColor = Color.white; _lineRenderer.endColor = _lineRenderer.startColor; if (_polygonOutline.Count > 1) { _lineRenderer.startColor = Color.green; _lineRenderer.endColor = _lineRenderer.startColor; } _gateEdgeOverlap = false; if (_lineRenderer.positionCount > 2) { if (EdgeOverlapsOutline(_lineRenderer.GetPosition(_lineRenderer.positionCount - 1), hit.point)) { return; } if (EdgeOverlapsOutline(hit.point, _lineRenderer.GetPosition(0))) { _lineRenderer.startColor = Color.red; _lineRenderer.endColor = _lineRenderer.startColor; _gateEdgeOverlap = true; } } GameObject go = Instantiate(_pointPrefab); go.transform.position = hit.point + _positionOffset; _polygonOutline.Add(go); _lineRenderer.positionCount = _polygonOutline.Count; _lineRenderer.SetPositions(_polygonOutline.Select(gam => gam.transform.position).ToArray()); } else { Debug.LogError("Mouse position raycast failed, this should never happen!"); } } else if (Input.GetKeyUp(KeyCode.Return) && !_gateEdgeOverlap) { if (_polygonOutline.Count < 3) { Debug.LogWarning("Cannot triangulate only one edge."); return; } Mesh m = new Mesh(); Vector3[] edge = _polygonOutline.Select(gam => gam.transform.position).ToArray(); m.vertices = edge; Triangulation.CapMesh(m, Enumerable.Range(0, edge.Length).ToList(), 0, _convexSupport); MeshObject outputGO = Instantiate(_outputGameObjectPrefab.gameObject).GetComponent <MeshObject>(); outputGO.SetMesh(m); outputGO.SetPolygonCollider(edge.Select(v3 => new Vector2(v3.x, v3.y)).ToArray()); _polygonOutline.ForEach(go => Destroy(go)); _polygonOutline.Clear(); _lineRenderer.positionCount = _polygonOutline.Count; _lineRenderer.startColor = Color.white; _lineRenderer.endColor = _lineRenderer.startColor; } }
// Mouse released public override void OnMouseUp(MouseEventArgs e) { ICollection <Sector> selected; PixelColor c; base.OnMouseUp(e); // Item highlighted? if ((highlighted != null) && !highlighted.IsDisposed) { Sector s = highlighted; // Remove highlight to avoid confusion Highlight(null); // Get a triangulator and bind events Triangulation t = new Triangulation(); t.OnShowPolygon = ShowPolygon; t.OnShowEarClip = ShowEarClip; //t.OnShowRemaining = ShowRemaining; t.Triangulate(s); // Start with a clear display if (renderer.StartPlotter(true)) { // Render lines and vertices renderer.PlotLinedefSet(General.Map.Map.Linedefs); renderer.PlotVerticesSet(General.Map.Map.Vertices); // Go for all triangle vertices to render the inside lines only for (int i = 0; i < t.Vertices.Count; i += 3) { if (t.Sidedefs[i + 0] == null) { renderer.PlotLine(t.Vertices[i + 0], t.Vertices[i + 1], PixelColor.FromColor(Color.DeepSkyBlue)); } if (t.Sidedefs[i + 1] == null) { renderer.PlotLine(t.Vertices[i + 1], t.Vertices[i + 2], PixelColor.FromColor(Color.DeepSkyBlue)); } if (t.Sidedefs[i + 2] == null) { renderer.PlotLine(t.Vertices[i + 2], t.Vertices[i + 0], PixelColor.FromColor(Color.DeepSkyBlue)); } } // Go for all triangle vertices to renderthe outside lines only for (int i = 0; i < t.Vertices.Count; i += 3) { if (t.Sidedefs[i + 0] != null) { renderer.PlotLine(t.Vertices[i + 0], t.Vertices[i + 1], PixelColor.FromColor(Color.Red)); } if (t.Sidedefs[i + 1] != null) { renderer.PlotLine(t.Vertices[i + 1], t.Vertices[i + 2], PixelColor.FromColor(Color.Red)); } if (t.Sidedefs[i + 2] != null) { renderer.PlotLine(t.Vertices[i + 2], t.Vertices[i + 0], PixelColor.FromColor(Color.Red)); } } // Done renderer.Finish(); renderer.Present(); Thread.Sleep(200); } } }
/// <summary> /// Function to initialize and setup the (meshes of the) prism based on the the PolyExtruder's set properties. /// </summary> private void initPrism() { // The prism consists of 3 meshes: // 1. bottom mesh with y = 0 // 2. top mesh with y = dynamically assigned // 3. surrounding polygon connecting 1 and 2 on their outline // Note: Meshes will be created as individual Child GameObjects. // 1. BOTTOM MESH // // create bottom GameObject with required components GameObject goB = new GameObject(); goB.transform.parent = this.transform; goB.name = "bottom_" + this.prismName; MeshFilter mfB = goB.AddComponent <MeshFilter>(); goB.AddComponent <MeshCollider>(); bottomMeshRenderer = goB.AddComponent <MeshRenderer>(); bottomMeshRenderer.material = new Material(Shader.Find("Diffuse")); // keep reference to bottom mesh this.bottomMesh = mfB.mesh; // init helper values to create bottom mesh List <Vector2> pointsB = new List <Vector2>(); List <List <Vector2> > holesB = new List <List <Vector2> >(); List <int> indicesB = null; List <Vector3> verticesB = null; // convert original polygon data for bottom mesh foreach (Vector2 v in originalPolygonVertices) { pointsB.Add(v); } // handle hole data (if existing) List <Vector2> holeB = new List <Vector2>(); // perform TRIANGULATION Triangulation.triangulate(pointsB, holesB, DEFAULT_BOTTOM_Y, out indicesB, out verticesB); // assign indices and vertices and create mesh redrawMesh(this.bottomMesh, verticesB, indicesB); // reset mesh collider after (re-)creation goB.GetComponent <MeshCollider>().sharedMesh = this.bottomMesh; /* * // generate a simple UVmap * Vector2[] uvsB = new Vector2[this.bottomMesh.vertices.Length]; * for (int i = 0; i < uvsB.Length; i++) * { * uvsB[i] = new Vector2(this.bottomMesh.vertices[i].x, this.bottomMesh.vertices[i].y); * } * this.bottomMesh.uv = uvsB; */ // if polygon is being extruded, "flip" bottom polygon // (otherwise it would be facing its rendered side "inside" the extrusion, thus not being visible) if (this.is3D) { goB.transform.localScale = new Vector3(-1.0f, -1.0f, -1.0f); goB.transform.localRotation = Quaternion.Euler(0.0f, 180.0f, 0.0f); } // create and render top and surround mesh only if GameObject is created as 3D // if (this.is3D) { // 2. TOP MESH // // create top GameObject with required components GameObject goT = new GameObject(); goT.transform.parent = this.transform; goT.name = "top_" + this.prismName; MeshFilter mfT = goT.AddComponent <MeshFilter>(); goT.AddComponent <MeshCollider>(); topMeshRenderer = goT.AddComponent <MeshRenderer>(); topMeshRenderer.material = new Material(Shader.Find("Diffuse")); // keep reference to top mesh this.topMesh = mfT.mesh; // init helper values to create top mesh List <Vector2> pointsT = new List <Vector2>(); List <List <Vector2> > holesT = new List <List <Vector2> >(); List <int> indicesT = null; List <Vector3> verticesT = null; // convert original polygon data for top mesh foreach (Vector2 v in originalPolygonVertices) { pointsT.Add(v); } // handle hole data (if existing) List <Vector2> holeT = new List <Vector2>(); // perform TRIANGULATION Triangulation.triangulate(pointsT, holesT, DEFAULT_TOP_Y, out indicesT, out verticesT); // assign indices and vertices and create mesh redrawMesh(this.topMesh, verticesT, indicesT); // reset mesh collider after (re-)creation goT.GetComponent <MeshCollider>().sharedMesh = this.topMesh; /* * // generate a simple UV map * Vector2[] uvsT = new Vector2[this.topMesh.vertices.Length]; * for (int i = 0; i < uvsT.Length; i++) * { * uvsT[i] = new Vector2(this.topMesh.vertices[i].x, this.topMesh.vertices[i].y); * } * this.topMesh.uv = uvsT; */ // 3. SURROUNDING MESH // // create surrounding GameObject with required components GameObject goS = new GameObject(); goS.transform.parent = this.transform; goS.name = "surround_" + this.prismName; MeshFilter mfS = goS.AddComponent <MeshFilter>(); goS.AddComponent <MeshCollider>(); surroundMeshRenderer = goS.AddComponent <MeshRenderer>(); surroundMeshRenderer.material = new Material(Shader.Find("Diffuse")); // keep reference to surrounding mesh this.surroundMesh = mfS.mesh; // init helper values to create surrounding mesh List <int> indicesS = new List <int>(); List <Vector3> verticesS = new List <Vector3>(); // prepare bottom and top mesh data to build surrounding mesh foreach (Vector3 bv in pointsB) { verticesS.Add(new Vector3(bv.x, DEFAULT_BOTTOM_Y, bv.y)); } foreach (Vector2 tv in pointsT) { verticesS.Add(new Vector3(tv.x, DEFAULT_TOP_Y, tv.y)); } // CONSTRUCT TRIANGLES: // create an array of integers // 0 -> verticesB[0] // pointsB.Count -> pointsT[0] // create 2 triangles (= quad) in each loop int indexB = 0; int indexT = pointsB.Count; int sumQuads = verticesS.Count / 2; for (int i = 0; i < sumQuads; i++) { // handle closing triangles if (i == (sumQuads - 1)) { // second to last triangle (1st in quad) indicesS.Add(indexB); indicesS.Add(0); // flipped with 3. index indicesS.Add(indexT); // flipped with 2. index // last triangle (2nd in quad) indicesS.Add(0); indicesS.Add(pointsB.Count); // flipped with 6. index indicesS.Add(indexT); // flipped with 5. index } // handle normal case else { // triangle 1 indicesS.Add(indexB); indicesS.Add(indexB + 1); indicesS.Add(indexT); // triangle 2 indicesS.Add(indexB + 1); indicesS.Add(indexT + 1); indicesS.Add(indexT); // increment bottom and top index indexB++; indexT++; } } // assign indices and vertices and create mesh redrawMesh(this.surroundMesh, verticesS, indicesS); /* * // reset mesh collider after (re-)creation (not needed right now since no mesh collider is attached) * goS.GetComponent<MeshCollider>().sharedMesh = this.surroundMesh; * * // generate a simple UV map * Vector2[] uvsS = new Vector2[this.surroundMesh.vertices.Length]; * for (int i = 0; i < uvsS.Length; i++) * { * uvsS[i] = new Vector2(this.surroundMesh.vertices[i].x, this.surroundMesh.vertices[i].y); * } * this.surroundMesh.uv = uvsS; */ // note: for 3D prism, only keep top mesh collider activated (adapt to own preferences this if needed) goB.GetComponent <MeshCollider>().enabled = false; goT.GetComponent <MeshCollider>().enabled = true; goS.GetComponent <MeshCollider>().enabled = false; } // set height and color updateHeight(this.extrusionHeightY); updateColor(this.prismColor); }
public ModelRoot CreateTerrainMesh(Triangulation triangulation, PBRTexture textures, bool doubleSided = true) { return(AddTerrainMesh(CreateNewModel(), triangulation, textures, doubleSided)); }
public ModelRoot GenerateTriangleMesh(List <GeoPoint> points, List <int> indices, PBRTexture textures) { Triangulation triangulation = new Triangulation(points, indices); return(CreateTerrainMesh(triangulation, textures)); }
public void Flip() { #region Arrange // Nodes. var A = new Point(0, 5); var B = new Point(5, 0); var C = new Point(0, 0); var D = new Point(5, 5); var L = new Point(-3, 2); var T = new Point(3, 7); var R = new Point(7, 2); var Q = new Point(3, -2); // Triangles. var LT = new Triangle(); var TT = new Triangle(); var RT = new Triangle(); var QT = new Triangle(); var T1 = new Triangle(); var T2 = new Triangle(); // Ribs. // Left triangle. var LA = new Rib(L, A, LT, null); var LC = new Rib(L, C, LT, null); var AC = new Rib(A, C, LT, T1); // Top triangle. var TA = new Rib(T, A, TT, null); var TD = new Rib(T, D, TT, null); var AD = new Rib(A, D, TT, T2); // Right triangle. var RD = new Rib(R, D, RT, null); var RB = new Rib(R, B, RT, null); var BD = new Rib(B, D, RT, T2); // Bottom triangle. var QC = new Rib(Q, C, QT, null); var QB = new Rib(Q, B, QT, null); var BC = new Rib(B, C, QT, T1); // Diagonal. var AB = new Rib(A, B, T1, T2); // Set ribs for triangles. LT.SetRibs(LA, LC, AC); TT.SetRibs(TA, TD, AD); RT.SetRibs(RD, RB, BD); QT.SetRibs(QC, QB, BC); T1.SetRibs(AC, BC, AB); T2.SetRibs(AD, BD, AB); // Update triangles. Triangle.Update(LT, TT, RT, QT, T1, T2); #endregion #region Act Triangulation.Flip(T1, T2); #endregion #region Assert // Left. Assert.IsTrue(LT.IsAdjacent(T1)); Assert.IsFalse(LT.IsAdjacent(T2)); Assert.IsTrue(LT.IsAdjacent(null)); // Top. Assert.IsTrue(TT.IsAdjacent(T1)); Assert.IsFalse(TT.IsAdjacent(T2)); Assert.IsTrue(TT.IsAdjacent(null)); // Right. Assert.IsTrue(RT.IsAdjacent(T2)); Assert.IsFalse(RT.IsAdjacent(T1)); Assert.IsTrue(RT.IsAdjacent(null)); // Bottom. Assert.IsTrue(QT.IsAdjacent(T2)); Assert.IsFalse(QT.IsAdjacent(T1)); Assert.IsTrue(QT.IsAdjacent(null)); // T1 & T2. Assert.IsTrue(T1.IsAdjacent(T2)); Assert.IsTrue(T2.IsAdjacent(T1)); Assert.IsFalse(T1.IsAdjacent(null)); Assert.IsFalse(T2.IsAdjacent(null)); #endregion }
static void splitTrianglesLH(Vector4 plane, Vector3[] snapshot, PlaneTriResult[] sidePlanes, int[] sourceIndices, MeshCache meshCache, TurboList <int> frontIndices, TurboList <int> backIndices, Infill?infillMode, TurboList <int> frontInfill, TurboList <int> backInfill) { bool doInfill = infillMode.HasValue && frontInfill != null && backInfill != null; Vector3[] sourceGeometry = meshCache.vertices.array; Vector3[] sourceNormals = meshCache.normals.array; Vector2[] sourceUVs = meshCache.UVs.array; BoneWeight[] sourceWeights = meshCache.weights.array; float[] pointClassifications = new float[sourceIndices.Length]; for (int i = 0; i < pointClassifications.Length; i++) { pointClassifications[i] = MuffinSliceCommon.classifyPoint(ref plane, ref snapshot[sourceIndices[i]]); } //Now we're going to do the decision making pass. This is where we assess the side figures and produce actions... int inputTriangleCount = sourceIndices.Length / 3; //A good action count estimate can avoid reallocations. //We expect exactly five actions per triangle. int actionEstimate = inputTriangleCount * 5; List <SplitAction> splitActions = new List <SplitAction>(actionEstimate); //We want to count how many vertices are yielded from each triangle split. This will be used later to add the indices. short[] frontVertexCount = new short[inputTriangleCount]; short[] backVertexCount = new short[inputTriangleCount]; short totalFront = 0, totalBack = 0; for (int i = 0; i < sourceIndices.Length; i += 3) { int[] indices = { sourceIndices[i], sourceIndices[i + 1], sourceIndices[i + 2] }; float[] sides = { pointClassifications[i], pointClassifications[i + 1], pointClassifications[i + 2] }; short indexA = 2; short front = 0, back = 0; for (short indexB = 0; indexB < 3; indexB++) { float sideA = sides[indexA]; float sideB = sides[indexB]; if (sideB > 0f) { if (sideA < 0f) { //Find intersection between A, B. Add to BOTH splitActions.Add(new SplitAction(indices[indexA], indices[indexB], i)); front++; back++; } //Add B to FRONT. splitActions.Add(new SplitAction(true, false, indices[indexB])); front++; } else if (sideB < 0f) { if (sideA > 0f) { //Find intersection between A, B. Add to BOTH splitActions.Add(new SplitAction(indices[indexA], indices[indexB], i)); front++; back++; } //Add B to BACK. splitActions.Add(new SplitAction(false, true, indices[indexB])); back++; } else { //Add B to BOTH. splitActions.Add(new SplitAction(false, true, indices[indexB])); front++; back++; } indexA = indexB; } int j = i / 3; //This is the triangle counter. frontVertexCount[j] = front; backVertexCount[j] = back; totalFront += front; totalBack += back; } // We're going to iterate through the splits only several times, so let's //find the subset once now. // Since these are STRUCTs, this is going to COPY the array content. The //intersectionInverseRelation table made below helps us put it back into the //main array before we use it. SplitAction[] intersectionActions; int[] intersectionInverseRelation; { int intersectionCount = 0; foreach (SplitAction sa in splitActions) { if ((sa.flags & SplitAction.INTERSECT) == SplitAction.INTERSECT) { intersectionCount++; } } intersectionActions = new SplitAction[intersectionCount]; intersectionInverseRelation = new int[intersectionCount]; int j = 0; for (int i = 0; i < splitActions.Count; i++) { SplitAction sa = splitActions[i]; if ((sa.flags & SplitAction.INTERSECT) == SplitAction.INTERSECT) { intersectionActions[j] = sa; intersectionInverseRelation[j] = i; j++; } } } // Next, we're going to find out which splitActions replicate the work of other split actions. //A given SA replicates another if and only if it _both_ calls for an intersection _and_ has //the same two parent indices (index0 and index1). This is because all intersections are called //with the same other parameters, so any case with an index0 and index1 matching will yield the //same results. // Only caveat is that two given splitActions might as the source indices in reverse order, so //we'll arbitrarily decide that "greater first" or something is the correct order. Flipping this //order has no consequence until after the intersection is found (at which point flipping the order //necessitates converting intersection i to 1-i to flip it as well.) // We can assume that every SA has at most 1 correlation. For a given SA, we'll search the list //UP TO its own index and, if we find one, we'll take the other's index and put it into the CLONE OF //slot. // So if we had a set like AFDBAK, than when the _latter_ A comes up for assessment, it'll find //the _first_ A (with an index of 0) and set the latter A's cloneOf figure to 0. This way we know //any latter As are a clone of the first A. for (int i = 0; i < intersectionActions.Length; i++) { SplitAction a = intersectionActions[i]; //Ensure that the index0, index1 figures are all in the same order. //(We'll do this as we walk the list.) if (a.index0 > a.index1) { int j = a.index0; a.index0 = a.index1; a.index1 = j; } Vector3 aVector = sourceGeometry[a.index0] + sourceGeometry[a.index1]; //Only latters clone formers, so we don't need to search up to and past the self. for (int j = 0; j < i; j++) { SplitAction b = intersectionActions[j]; bool match = a.index0 == b.index0 && a.index1 == b.index1; if (!match) { Vector3 bVector = sourceGeometry[b.index0] + sourceGeometry[b.index1]; // match = Mathf.Approximately(aVector.x, bVector.x); // match &= Mathf.Approximately(aVector.y, bVector.y); // match &= Mathf.Approximately(aVector.z, bVector.z); // What are the chances, really? match = Mathf.Approximately(aVector.x + aVector.y + aVector.z, bVector.x + bVector.y + bVector.z); } if (match) { a.cloneOf = j; } } intersectionActions[i] = a; } //Next, we want to perform all INTERSECTIONS. Any action which has an intersection needs to have that, like, done. for (int i = 0; i < intersectionActions.Length; i++) { SplitAction sa = intersectionActions[i]; if (sa.cloneOf == SplitAction.nullIndex) { /*float ir = vertexSums[ sa.index0 ] + vertexSums[ sa.index1 ]; * * ir += 1f; * ir *= 0.5f; * ir = 1f - ir; * * sa.intersectionResult = ir;*/ Vector3 pointA = snapshot[sa.index0]; Vector3 pointB = snapshot[sa.index1]; sa.intersectionResult = MuffinSliceCommon.intersectCommon(ref pointB, ref pointA, ref plane); intersectionActions[i] = sa; } } // Let's create a table that relates an INTERSECTION index to a GEOMETRY index with an offset of 0 (for example //to refer to our newVertices or to the transformedVertices or whatever; internal use.) // We can also set up our realIndex figures in the same go. int newIndexStartsAt = meshCache.vertices.Count; int uniqueVertexCount = 0; int[] localIndexByIntersection = new int[intersectionActions.Length]; { int currentLocalIndex = 0; for (int i = 0; i < intersectionActions.Length; i++) { SplitAction sa = intersectionActions[i]; int j; if (sa.cloneOf == SplitAction.nullIndex) { j = currentLocalIndex++; } else { //This assumes that the widget that we are a clone of already has its localIndexByIntersection assigned. //We assume this because above – where we seek for clones – we only look behind for cloned elements. j = localIndexByIntersection[sa.cloneOf]; } sa.realIndex = newIndexStartsAt + j; localIndexByIntersection[i] = j; intersectionActions[i] = sa; } uniqueVertexCount = currentLocalIndex; } //Let's figure out how much geometry we might have. //The infill geometry is a pair of clones of this geometry, but with different NORMALS and UVs. (Each set has different normals.) int newGeometryEstimate = uniqueVertexCount * (doInfill ? 3 : 1); //In this ACTION pass we'll act upon intersections by fetching both referred vertices and LERPing as appropriate. //The resultant indices will be written out over the index0 figures. Vector3[] newVertices = new Vector3[newGeometryEstimate]; Vector3[] newNormals = new Vector3[newGeometryEstimate]; Vector2[] newUVs = new Vector2[newGeometryEstimate]; BoneWeight[] newWeights = new BoneWeight[newGeometryEstimate]; //LERP to create vertices { int currentNewIndex = 0; foreach (SplitAction sa in intersectionActions) { if (sa.cloneOf == SplitAction.nullIndex) { Vector3 v = sourceGeometry[sa.index0]; Vector3 v2 = sourceGeometry[sa.index1]; newVertices[currentNewIndex] = Vector3.Lerp(v2, v, sa.intersectionResult); currentNewIndex++; } } } //Normals: { int currentNewIndex = 0; foreach (SplitAction sa in intersectionActions) { if (sa.cloneOf == SplitAction.nullIndex) { Vector3 n = sourceNormals[sa.index0]; Vector3 n2 = sourceNormals[sa.index1]; newNormals[currentNewIndex] = Vector3.Lerp(n2, n, sa.intersectionResult); currentNewIndex++; } } } //UVs: { int currentNewIndex = 0; foreach (SplitAction sa in intersectionActions) { if (sa.cloneOf == SplitAction.nullIndex) { Vector2 uv = sourceUVs[sa.index0]; Vector2 uv2 = sourceUVs[sa.index1]; newUVs[currentNewIndex] = Vector2.Lerp(uv2, uv, sa.intersectionResult); currentNewIndex++; } } } //Bone Weights: { int currentNewIndex = 0; foreach (SplitAction sa in intersectionActions) { if (sa.cloneOf == SplitAction.nullIndex) { BoneWeight bw; if (sidePlanes[sa.index0] == PlaneTriResult.PTR_FRONT) { bw = sourceWeights[sa.index0]; } else { bw = sourceWeights[sa.index1]; } newWeights[currentNewIndex] = bw; currentNewIndex++; } } } //All the polygon triangulation algorithms depend on having a 2D polygon. We also need the slice plane's //geometry in two-space to map the UVs. //NOTE that as we only need this data to analyze polygon geometry for triangulation, we can TRANSFORM (scale, translate, rotate) //these figures any way we like, as long as they retain the same relative geometry. So we're going to perform ops on this //data to create the UVs by scaling it around, and we'll feed the same data to the triangulator. //Our's exists in three-space, but is essentially flat... So we can transform it onto a flat coordinate system. //The first three figures of our plane four-vector describe the normal to the plane, so if we can create //a transformation matrix from that normal to the up normal, we can transform the vertices for observation. //We don't need to transform them back; we simply refer to the original vertex coordinates by their index, //which (as this is an ordered set) will match the indices of coorisponding transformed vertices. //This vector-vector transformation comes from Benjamin Zhu at SGI, pulled from a 1992 //forum posting here: http://steve.hollasch.net/cgindex/math/rotvecs.html /* "A somewhat "nasty" way to solve this problem: * * Let V1 = [ x1, y1, z1 ], V2 = [ x2, y2, z2 ]. Assume V1 and V2 are already normalized. * * V3 = normalize(cross(V1, V2)). (the normalization here is mandatory.) * V4 = cross(V3, V1). * * [ V1 ] * M1 = [ V4 ] * [ V3 ] * * cos = dot(V2, V1), sin = dot(V2, V4) * * [ cos sin 0 ] * M2 = [ -sin cos 0 ] * [ 0 0 1 ] * * The sought transformation matrix is just M1^-1 * M2 * M1. This might well be a standard-text solution." * * -Ben Zhu, SGI, 1992 */ Vector2[] transformedVertices = new Vector2[0]; int infillFrontOffset = 0, infillBackOffset = 0; if (doInfill) { transformedVertices = new Vector2[newGeometryEstimate / 3]; Matrix4x4 flattenTransform; //Based on the algorithm described above, this will create a matrix permitting us //to multiply a given vertex yielding a vertex transformed to an XY plane (where Z is //undefined.) { Vector3 v1 = Vector3.forward; Vector3 v2 = new Vector3(plane.x, plane.y, plane.z).normalized; float difference = (v1 - v2).magnitude; if (difference > 0.01f) { Vector3 v3 = Vector3.Cross(v1, v2).normalized; Vector3 v4 = Vector3.Cross(v3, v1); float cos = Vector3.Dot(v2, v1); float sin = Vector3.Dot(v2, v4); Matrix4x4 m1 = Matrix4x4.identity; m1.SetRow(0, (Vector4)v1); m1.SetRow(1, (Vector4)v4); m1.SetRow(2, (Vector4)v3); Matrix4x4 m1i = m1.inverse; Matrix4x4 m2 = Matrix4x4.identity; m2.SetRow(0, new Vector4(cos, sin, 0, 0)); m2.SetRow(1, new Vector4(-sin, cos, 0, 0)); flattenTransform = m1i * m2 * m1; } else { flattenTransform = Matrix4x4.identity; } } for (int i = 0; i < transformedVertices.Length; i++) { transformedVertices[i] = (Vector2)flattenTransform.MultiplyPoint3x4(newVertices[i]); // Debug.Log(newVertices[i] + " > " + transformedVertices[i]); } // We want to normalize the entire transformed vertices. To do this, we find the largest //floats in either (by abs). Then we scale. Of course, this normalizes us to figures //in the range of [-1f,1f] (not necessarily extending all the way on both sides), and //what we need are figures between 0f and 1f (not necessarily filling, but necessarily //not spilling.) So we'll shift it here. { float x = 0f, y = 0f; for (int i = 0; i < transformedVertices.Length; i++) { Vector2 v = transformedVertices[i]; v.x = Mathf.Abs(v.x); v.y = Mathf.Abs(v.y); if (v.x > x) { x = v.x; } if (v.y > y) { y = v.y; } } //We would use 1f/x, 1f/y but we also want to scale everything to half (and perform an offset) as //described above. x = 0.5f / x; y = 0.5f / y; Rect r = new Rect(0, 0, 1f, 1f); for (int i = 0; i < transformedVertices.Length; i++) { Vector2 v = transformedVertices[i]; v.x *= x; v.y *= y; v.x += 0.5f; v.y += 0.5f; v.x *= r.width; v.y *= r.height; v.x += r.x; v.y += r.y; transformedVertices[i] = v; } } //Now let's build the geometry for the two slice in-fills. //One is for the front side, and the other for the back side. Each has differing normals. infillFrontOffset = uniqueVertexCount; infillBackOffset = uniqueVertexCount * 2; //The geometry is identical... System.Array.Copy(newVertices, 0, newVertices, infillFrontOffset, uniqueVertexCount); System.Array.Copy(newVertices, 0, newVertices, infillBackOffset, uniqueVertexCount); System.Array.Copy(newWeights, 0, newWeights, infillFrontOffset, uniqueVertexCount); System.Array.Copy(newWeights, 0, newWeights, infillBackOffset, uniqueVertexCount); System.Array.Copy(transformedVertices, 0, newUVs, infillFrontOffset, uniqueVertexCount); System.Array.Copy(transformedVertices, 0, newUVs, infillBackOffset, uniqueVertexCount); Vector3 infillFrontNormal = ((Vector3)plane) * -1f; infillFrontNormal.Normalize(); for (int i = infillFrontOffset; i < infillBackOffset; i++) { newNormals[i] = infillFrontNormal; } Vector3 infillBackNormal = (Vector3)plane; infillBackNormal.Normalize(); for (int i = infillBackOffset; i < newNormals.Length; i++) { newNormals[i] = infillBackNormal; } } //Get the exact indices into two tables. Note that these are indices for TRIANGLES and QUADS, which we'll triangulate in the next section. int[] newFrontIndex = new int[totalFront]; int[] newBackIndex = new int[totalBack]; //Note that here we refer to split actions again, so let's copy back the updated splitActions. for (int i = 0; i < intersectionActions.Length; i++) { int j = intersectionInverseRelation[i]; splitActions[j] = intersectionActions[i]; } int newFrontIndexCount = 0, newBackIndexCount = 0; foreach (SplitAction sa in splitActions) { if ((sa.flags & SplitAction.TO_FRONT) == SplitAction.TO_FRONT) { newFrontIndex[newFrontIndexCount] = sa.realIndex; newFrontIndexCount++; } if ((sa.flags & SplitAction.TO_BACK) == SplitAction.TO_BACK) { newBackIndex[newBackIndexCount] = sa.realIndex; newBackIndexCount++; } } //Now we need to triangulate sets of quads. //We recorded earlier whether we're looking at triangles or quads – in order. So we have a pattern like TTQTTQQTTTQ, and //we can expect these vertices to match up perfectly to what the above section of code dumped out. int startIndex = 0; int[] _indices3 = new int[3]; int[] _indices4 = new int[6]; foreach (short s in frontVertexCount) { if (s == 3) { _indices3[0] = newFrontIndex[startIndex]; _indices3[1] = newFrontIndex[startIndex + 1]; _indices3[2] = newFrontIndex[startIndex + 2]; frontIndices.AddArray(_indices3); } else if (s == 4) { _indices4[0] = newFrontIndex[startIndex]; _indices4[1] = newFrontIndex[startIndex + 1]; _indices4[2] = newFrontIndex[startIndex + 3]; _indices4[3] = newFrontIndex[startIndex + 1]; _indices4[4] = newFrontIndex[startIndex + 2]; _indices4[5] = newFrontIndex[startIndex + 3]; frontIndices.AddArray(_indices4); } startIndex += s; } startIndex = 0; foreach (short s in backVertexCount) { if (s == 3) { _indices3[0] = newBackIndex[startIndex]; _indices3[1] = newBackIndex[startIndex + 1]; _indices3[2] = newBackIndex[startIndex + 2]; backIndices.AddArray(_indices3); } else if (s == 4) { _indices4[0] = newBackIndex[startIndex]; _indices4[1] = newBackIndex[startIndex + 1]; _indices4[2] = newBackIndex[startIndex + 3]; _indices4[3] = newBackIndex[startIndex + 1]; _indices4[4] = newBackIndex[startIndex + 2]; _indices4[5] = newBackIndex[startIndex + 3]; backIndices.AddArray(_indices4); } startIndex += s; } //Let's add this shiznit in! meshCache.vertices.AddArray(newVertices); meshCache.normals.AddArray(newNormals); meshCache.UVs.AddArray(newUVs); meshCache.weights.AddArray(newWeights); //Now we need to fill in the slice hole. There are TWO infillers; the Sloppy and Meticulous. //The sloppy infiller will find a point in the middle of all slice vertices and produce a triangle fan. //It can work fast, but will have issues with non-roundish cross sections or cross sections with multiple holes. //The meticulous infill can distinguish between polygons and accurately fill multiple holes, but is more sensitive to //geometrical oddities. It may fail when slicing certain joints because of the way that not all geometry is sliced. //It is transferred from Turbo Slicer, where it is a key part of the product, but it is not most appropriate here. //Nevertheless, it is here in case it is needed. if (doInfill && infillMode == Infill.Sloppy) { VectorAccumulator centerVertex = new VectorAccumulator(); VectorAccumulator centerUV = new VectorAccumulator(); VectorAccumulator centerNormal = new VectorAccumulator(); Dictionary <int, float> weightsByBone = new Dictionary <int, float>(); int sliceVertexCount = newGeometryEstimate / 3; for (int i = 0; i < sliceVertexCount; i++) { centerVertex.addFigure(newVertices[i]); centerUV.addFigure(newUVs[i]); centerNormal.addFigure(newNormals[i]); BoneWeight bw = newWeights[i]; if (weightsByBone.ContainsKey(bw.boneIndex0)) { weightsByBone[bw.boneIndex0] += bw.weight0; } else { weightsByBone[bw.boneIndex0] = bw.weight0; } /*if(weightsByBone.ContainsKey(bw.boneIndex1)) * weightsByBone[bw.boneIndex1] += bw.weight1; * else * weightsByBone[bw.boneIndex1] = bw.weight1; * * if(weightsByBone.ContainsKey(bw.boneIndex2)) * weightsByBone[bw.boneIndex2] += bw.weight2; * else * weightsByBone[bw.boneIndex2] = bw.weight2; * * if(weightsByBone.ContainsKey(bw.boneIndex3)) * weightsByBone[bw.boneIndex3] += bw.weight3; * else * weightsByBone[bw.boneIndex3] = bw.weight3;*/ } List <KeyValuePair <int, float> > orderedWeights = new List <KeyValuePair <int, float> >(weightsByBone); orderedWeights.Sort((firstPair, nextPair) => { return(-firstPair.Value.CompareTo(nextPair.Value)); } ); BoneWeight centerWeight = new BoneWeight(); Vector4 weightNormalizer = Vector4.zero; if (orderedWeights.Count > 0) { centerWeight.boneIndex0 = orderedWeights[0].Key; weightNormalizer.x = 1f; } weightNormalizer.Normalize(); centerWeight.weight0 = weightNormalizer.x; centerWeight.weight1 = weightNormalizer.y; centerWeight.weight2 = weightNormalizer.z; centerWeight.weight3 = weightNormalizer.w; int centerIndex = meshCache.vertices.Count; meshCache.vertices.Count++; meshCache.normals.Count++; meshCache.UVs.Count++; meshCache.weights.Count++; meshCache.vertices.array[centerIndex] = centerVertex.mean; meshCache.UVs.array[centerIndex] = centerUV.mean; meshCache.normals.array[centerIndex] = centerNormal.mean; meshCache.weights.array[centerIndex] = centerWeight; Vector2 transformedCenter = Vector2.zero; foreach (Vector2 v in transformedVertices) { transformedCenter += v; } transformedCenter /= transformedVertices.Length; Dictionary <int, float> angleByIndex = new Dictionary <int, float>(); for (int i = 0; i < transformedVertices.Length; i++) { Vector2 delta = transformedVertices[i] - transformedCenter; angleByIndex[i] = Mathf.Atan2(delta.y, delta.x); } List <KeyValuePair <int, float> > orderedVertices = new List <KeyValuePair <int, float> >(angleByIndex); orderedVertices.Sort((firstPair, nextPair) => { return(firstPair.Value.CompareTo(nextPair.Value)); } ); for (int i = 0; i < orderedVertices.Count; i++) { bool atEnd = i == orderedVertices.Count - 1; int iNext = atEnd ? 0 : i + 1; int index0 = orderedVertices[i].Key; int index1 = orderedVertices[iNext].Key; int[] frontInfillIndices = { centerIndex, index1 + infillFrontOffset + newIndexStartsAt, index0 + infillFrontOffset + newIndexStartsAt }; frontInfill.AddArray(frontInfillIndices); int[] backInfillIndices = { centerIndex, index0 + infillBackOffset + newIndexStartsAt, index1 + infillBackOffset + newIndexStartsAt }; backInfill.AddArray(backInfillIndices); } } else if (doInfill && infillMode == Infill.Meticulous) { //If that fails, one can use the more accurate but more delicate "meticulous" infiller. //We need to find the POLYGON[s] representing the slice hole[s]. There may be more than one. //Then we need to TRIANGULATE these polygons and write them out. //Above we've built the data necessary to pull this off. We have: // - Geometry for the polygon around the edges in Vertex3 / Normal / UV format, already added //to the geometry setup. // - Geometry for the polygon in Vertex2 format in matching order, aligned to the slice plane. // - A collection of all data points and 1:1 hashes representing their physical location. //In this mess of data here may be 0 or non-zero CLOSED POLYGONS. We need to walk the list and //identify each CLOSED POLYGON (there may be none, or multiples). Then, each of these must be //triangulated separately. //Vertices connected to each other in a closed polygon can be found to associate with each other //in two ways. Envision a triangle strip that forms a circular ribbon – and that we slice through //the middle of this ribbon. Slice vertices come in two kinds of pairs; there are pairs that COME FROM //the SAME triangle, and pairs that come from ADJACENT TRIANGLES. The whole chain is formed from //alternating pair-types. //So for example vertex A comes from the same triangle as vertex B, which in turn matches the position //of the NEXT triangle's vertex A. //The data is prepared for us to be able to identify both kinds of associations. First, //association by parent triangle is encoded in the ORDERING. Every PAIR from index 0 shares a parent //triangle; so indices 0-1, 2-3, 4-5 and so on are each a pair from a common parent triangle. //Meanwhile, vertices generated from the common edge of two different triangles will have the SAME //POSITION in three-space. //We don't have to compare Vector3s, however; this has already been done. Uniques were eliminated above. //What we have is a table; localIndexByIntersection. This list describes ALL SLICE VERTICES in terms //of which VERTEX (in the array – identified by index) represents that slice vertex. So if we see that //localIndexByIntersection[0] == localIndexByIntersection[4], than we know that slice vertices 0 and 4 //share the same position in three space. //With that in mind, we're going to go through the list in circles building chains out of these //connections. List <int> currentWorkingPoly = new List <int>(); List <int> currentTargetPoly = new List <int>(); List <List <int> > allPolys = new List <List <int> >(); List <int> claimed = new List <int>(); int lastAdded = -1; //ASSUMPTION: Every element will be claimed into some kind of chain by the end whether correlated or not. do { for (int i = 0; i < localIndexByIntersection.Length; i++) { bool go = false, fail = false, startNewChain = false; //If we didn't just add one, we're looking to start a chain. That means we have to find one that //isn't already claimed. if (lastAdded < 0) { go = claimed.Contains(i) == false; } else if (lastAdded == i) { //We've gone through twice without finding a match. This means there isn't one, or something. fail = true; } else { //Otherwise, we're trying to find the next-in-chain. //A valid next-in-chain is connected by geometry which, as discussed, means it's connected //by having matching parent indices (index0, index1). bool match = localIndexByIntersection[i] == localIndexByIntersection[lastAdded]; //But there's a special case about the match; it's possible that we've closed the loop! //How do we know we've closed the loop? There are multiple ways but the simplest is that //the chain already contains the element in question. bool loopComplete = match && currentWorkingPoly.Contains(i); if (loopComplete) { allPolys.Add(currentTargetPoly); startNewChain = true; } else { go = match; } } if (go) { int partnerByParent = i % 2 == 1 ? i - 1 : i + 1; int[] pair = { i, partnerByParent }; currentWorkingPoly.AddRange(pair); claimed.AddRange(pair); currentTargetPoly.Add(partnerByParent); lastAdded = partnerByParent; //Skip ahead and resume the search _from_ here, so that we don't step into it //again from within this loop walk. i = partnerByParent; } else if (fail) { //We want to start a fresh poly without adding this to the valid polys. startNewChain = true; //Debug.Log("[fail]"); } if (startNewChain) { currentWorkingPoly.Clear(); currentTargetPoly = new List <int>(); lastAdded = -1; } } }while(currentWorkingPoly.Count > 0); //Now we go through each poly and triangulate it. foreach (List <int> _poly in allPolys) { Vector2[] poly = new Vector2[_poly.Count]; for (int i = 0; i < poly.Length; i++) { int j = localIndexByIntersection[_poly[i]]; poly[i] = transformedVertices[j]; } int[] result; if (Triangulation.triangulate(poly, out result)) { int[] front = new int[result.Length]; int[] back = new int[result.Length]; for (int i = 0; i < result.Length; i++) { int p = _poly[result[i]]; int local = localIndexByIntersection[p]; front[i] = local + infillFrontOffset + newIndexStartsAt; back[i] = local + infillBackOffset + newIndexStartsAt; } for (int i = 0; i < result.Length; i += 3) { int j = front[i]; front[i] = front[i + 2]; front[i + 2] = j; } frontInfill.AddArray(front); backInfill.AddArray(back); } } } }
public static ModelRoot AddTINMesh(ModelRoot model, HeightMap hMap, double precision, SharpGltfService gltf, PBRTexture textures, int srid) { Triangulation triangulation = GenerateTIN(hMap, precision, srid); return(gltf.AddTerrainMesh(model, triangulation, textures, doubleSided: true)); }
/// <summary> /// Generate a triangle mesh from supplied height map, triangulating and optionaly mapping UVs /// and generate sides and bottom (like a box where the top is the triangulated height map) /// </summary> /// <param name="heightMap">Height map.</param> /// <param name="thickness">Determines how box height will be calculated</param> /// <param name="zValue">Z value to apply for box calculation</param> /// <returns></returns> public MeshPrimitive GenerateTriangleMesh_Boxed(HeightMap heightMap, BoxBaseThickness thickness = BoxBaseThickness.FixedElevation, float zValue = 0f) { Triangulation triangulation = _meshService.GenerateTriangleMesh_Boxed(heightMap, thickness, zValue); return(GenerateTriangleMesh(triangulation)); }
static TimeSpan TestNDDelau(int dim, int numVert, double size) { var vertices = CreateRandomVertices(dim, numVert, size); return(RunComputation(() => Triangulation.CreateDelaunay(vertices))); }
public static List <List <Vector3> > GetPolygons(List <List <Entity> > loops, ref List <List <Id> > ids) { if (ids != null) { ids.Clear(); } var result = new List <List <Vector3> >(); if (loops == null) { return(result); } foreach (var loop in loops) { var polygon = new List <Vector3>(); List <Id> idPolygon = null; if (ids != null) { idPolygon = new List <Id>(); } Action <IEnumerable <Vector3>, Entity> AddToPolygon = (points, entity) => { polygon.AddRange(points); if (idPolygon != null) { idPolygon.AddRange(Enumerable.Repeat(entity.guid, points.Count())); } }; for (int i = 0; i < loop.Count; i++) { if (loop[i] is ISegmentaryEntity) { var cur = loop[i] as ISegmentaryEntity; var next = loop[(i + 1) % loop.Count] as ISegmentaryEntity; if (!next.begin.IsCoincidentWith(cur.begin) && !next.end.IsCoincidentWith(cur.begin)) { AddToPolygon(cur.segmentPoints, loop[i]); } else if (!next.begin.IsCoincidentWith(cur.end) && !next.end.IsCoincidentWith(cur.end)) { AddToPolygon(cur.segmentPoints.Reverse(), loop[i]); } else if (next.begin.IsCoincidentWith(cur.end)) { AddToPolygon(cur.segmentPoints, loop[i]); } else if (i % 2 == 0) { AddToPolygon(cur.segmentPoints, loop[i]); } else { AddToPolygon(cur.segmentPoints.Reverse(), loop[i]); } } else if (loop[i] is ILoopEntity) { var cur = loop[i] as ILoopEntity; AddToPolygon(cur.loopPoints, loop[i]); } else { continue; } if (polygon.Count > 0) { polygon.RemoveAt(polygon.Count - 1); if (idPolygon != null) { idPolygon.RemoveAt(idPolygon.Count - 1); } } } if (polygon.Count < 3) { continue; } if (!Triangulation.IsClockwise(polygon)) { polygon.Reverse(); if (idPolygon != null) { idPolygon.Reverse(); } } result.Add(polygon); if (ids != null) { ids.Add(idPolygon); } } return(result); }
public static Mesh Triangulate3D(Polygon2D polygon, float z, Vector2 UVScale, Vector2 UVOffset, Triangulation triangulation) { Mesh result = null; switch (triangulation) { case Triangulation.Advanced: Slicer2DProfiler.IncAdvancedTriangulation(); Polygon2D newPolygon = polygon; List <Vector3> sideVertices = new List <Vector3>(); List <int> sideTriangles = new List <int>(); int vCount = 0; foreach (Pair2D pair in Pair2D.GetList(polygon.pointsList)) { Vector3 pointA = new Vector3((float)pair.A.x, (float)pair.A.y, 0); Vector3 pointB = new Vector3((float)pair.B.x, (float)pair.B.y, 0); Vector3 pointC = new Vector3((float)pair.B.x, (float)pair.B.y, 1); Vector3 pointD = new Vector3((float)pair.A.x, (float)pair.A.y, 1); sideVertices.Add(pointA); sideVertices.Add(pointB); sideVertices.Add(pointC); sideVertices.Add(pointD); sideTriangles.Add(vCount + 2); sideTriangles.Add(vCount + 1); sideTriangles.Add(vCount + 0); sideTriangles.Add(vCount + 0); sideTriangles.Add(vCount + 3); sideTriangles.Add(vCount + 2); vCount += 4; } Mesh meshA = PerformTriangulation(newPolygon, UVScale, UVOffset); Mesh meshB = new Mesh(); List <Vector3> verticesB = new List <Vector3>(); foreach (Vector3 v in meshA.vertices) { verticesB.Add(new Vector3(v.x, v.y, v.z + z)); } meshB.vertices = verticesB.ToArray(); meshB.triangles = meshA.triangles.Reverse().ToArray(); Mesh mesh = new Mesh(); mesh.SetVertices(sideVertices); mesh.SetTriangles(sideTriangles, 0); List <Vector3> vertices = new List <Vector3>(); foreach (Vector3 v in meshA.vertices) { vertices.Add(v); } foreach (Vector3 v in meshB.vertices) { vertices.Add(v); } foreach (Vector3 v in sideVertices) { vertices.Add(v); } mesh.vertices = vertices.ToArray(); List <int> triangles = new List <int>(); foreach (int p in meshA.triangles) { triangles.Add(p); } int count = meshA.vertices.Count(); foreach (int p in meshB.triangles) { triangles.Add(p + count); } count = meshA.vertices.Count() + meshB.vertices.Count(); foreach (int p in sideTriangles) { triangles.Add(p + count); } mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); result = mesh; break; } return(result); }
public static Solid CreateSolidExtrusion(List <List <Entity> > entitiyLoops, float extrude, UnityEngine.Matrix4x4 tf, IdPath feature) { var ids = new List <List <Id> >(); var polygons = Sketch.GetPolygons(entitiyLoops, ref ids); bool inversed = extrude < 0f; var polys = new List <Polygon>(); Func <Vector3, Vector3> TF = v => tf.MultiplyPoint(v); for (int pi = 0; pi < polygons.Count; pi++) { var p = polygons[pi]; var pid = ids[pi]; var pv = new List <Vector3>(p); var triangles = Triangulation.Triangulate(pv); IdPath polyId = feature.With(new Id(-1)); bool invComp = true; var shift = Vector3.zero; var extrudeVector = Vector3.forward * extrude; for (int side = 0; side < 2; side++) { for (int i = 0; i < triangles.Count / 3; i++) { var polygonVertices = new List <Vertex>(); for (int j = 0; j < 3; j++) { polygonVertices.Add(TF(triangles[i * 3 + j] + shift).ToVertex()); } if (inversed == invComp) { polygonVertices.Reverse(); } polys.Add(new Polygon(polygonVertices, polyId)); } polyId = feature.With(new Id(-2)); invComp = false; shift = extrudeVector; } Dictionary <Id, IdPath> paths = new Dictionary <Id, IdPath>(); for (int i = 0; i < p.Count; i++) { var polygonVertices = new List <Vertex>(); polygonVertices.Add(TF(p[i]).ToVertex()); polygonVertices.Add(TF(p[(i + 1) % p.Count]).ToVertex()); polygonVertices.Add(TF((p[(i + 1) % p.Count] + extrudeVector)).ToVertex()); polygonVertices.Add(TF((p[i] + extrudeVector)).ToVertex()); if (!inversed) { polygonVertices.Reverse(); } IdPath curPath = null; if (!paths.ContainsKey(pid[i])) { curPath = feature.With(pid[i]); paths.Add(pid[i], curPath); } else { curPath = paths[pid[i]]; } polys.Add(new Polygon(polygonVertices, curPath)); } } return(Solid.FromPolygons(polys)); }
public static Mesh Triangulate2D(Polygon2D polygon, Vector2 UVScale, Vector2 UVOffset, Triangulation triangulation) { polygon.Normalize(); Mesh result = null; switch (triangulation) { case Triangulation.Advanced: Slicer2D.Profiler.IncAdvancedTriangulation(); float GC = Slicer2D.Settings.GetGarbageCollector(); if (GC > 0 & polygon.GetArea() < GC) { Debug.LogWarning("Smart Utility 2D: Slice was removed because it was too small"); return(null); } Polygon2D newPolygon = new Polygon2D(PreparePolygon.Get(polygon)); if (newPolygon.pointsList.Count < 3) { Debug.LogWarning("Smart Utility 2D: Mesh is too small for advanced triangulation, using simplified triangulations instead (size: " + polygon.GetArea() + ")"); result = PerformTriangulation(polygon, UVScale, UVOffset); return(result); } foreach (Polygon2D hole in polygon.holesList) { newPolygon.AddHole(new Polygon2D(PreparePolygon.Get(hole, -1))); } result = PerformTriangulation(newPolygon, UVScale, UVOffset); break; case Triangulation.Legacy: Slicer2D.Profiler.IncLegacyTriangulation(); List <Vector2> list = new List <Vector2>(); foreach (Vector2D p in polygon.pointsList) { list.Add(p.ToVector2()); } result = Triangulator.Create(list.ToArray(), UVScale, UVOffset); return(result); } return(result); }
public static void CreateMeshExtrusion(List <List <Vector3> > polygons, float extrude, ref Mesh mesh) { var capacity = polygons.Sum(p => (p.Count - 2) * 3 + p.Count) * 2; var vertices = new List <Vector3>(capacity); var indices = new List <int>(capacity); bool inversed = extrude < 0f; foreach (var p in polygons) { var pv = new List <Vector3>(p); var triangles = Triangulation.Triangulate(pv); var start = vertices.Count; vertices.AddRange(triangles); if (!inversed) { for (int i = 0; i < triangles.Count; i++) { indices.Add(i + start); } } else { for (int i = 0; i < triangles.Count / 3; i++) { indices.Add(start + i * 3 + 0); indices.Add(start + i * 3 + 2); indices.Add(start + i * 3 + 1); } } var extrudeVector = Vector3.forward * extrude; var striangles = triangles.Select(pt => pt + extrudeVector).ToList(); start = vertices.Count; vertices.AddRange(striangles); if (inversed) { for (int i = 0; i < striangles.Count; i++) { indices.Add(i + start); } } else { for (int i = 0; i < striangles.Count / 3; i++) { indices.Add(start + i * 3 + 0); indices.Add(start + i * 3 + 2); indices.Add(start + i * 3 + 1); } } start = vertices.Count(); if (inversed) { for (int i = 0; i < p.Count; i++) { vertices.Add(p[i]); vertices.Add(p[(i + 1) % p.Count]); vertices.Add(p[i] + extrudeVector); vertices.Add(p[(i + 1) % p.Count]); vertices.Add(p[(i + 1) % p.Count] + extrudeVector); vertices.Add(p[i] + extrudeVector); } } else { for (int i = 0; i < p.Count; i++) { vertices.Add(p[i]); vertices.Add(p[i] + extrudeVector); vertices.Add(p[(i + 1) % p.Count]); vertices.Add(p[(i + 1) % p.Count]); vertices.Add(p[i] + extrudeVector); vertices.Add(p[(i + 1) % p.Count] + extrudeVector); } } for (int i = 0; i < p.Count * 6; i++) { indices.Add(start + i); } } mesh.Clear(); mesh.name = "extrusion"; mesh.SetVertices(vertices); mesh.SetIndices(indices.ToArray(), MeshTopology.Triangles, 0); mesh.RecalculateBounds(); mesh.RecalculateNormals(); mesh.RecalculateTangents(); }
public static Mesh Triangulate3D(Polygon2D polygon, float z, Vector2 UVScale, Vector2 UVOffset, float UVRotation, Triangulation triangulation) { polygon.Normalize(); Mesh result = null; switch (triangulation) { case Triangulation.Advanced: List <Vector2> uvs = new List <Vector2>(); List <Vector3> sideVertices = new List <Vector3>(); List <int> sideTriangles = new List <int>(); Vector3 pointA = new Vector3(0, 0, 0); Vector3 pointB = new Vector3(0, 0, 0); Vector3 pointC = new Vector3(0, 0, z); Vector3 pointD = new Vector3(0, 0, z); Vector2 uv0 = new Vector2(); Vector2 uv1 = new Vector2(); float a, b; int vCount = 0; Pair2D pair = new Pair2D(new Vector2D(polygon.pointsList.Last()), null); foreach (Vector2D p in polygon.pointsList) { pair.B = p; pointA.x = (float)pair.A.x; pointA.y = (float)pair.A.y; pointB.x = (float)pair.B.x; pointB.y = (float)pair.B.y; pointC.x = (float)pair.B.x; pointC.y = (float)pair.B.y; pointD.x = (float)pair.A.x; pointD.y = (float)pair.A.y; sideVertices.Add(pointA); sideVertices.Add(pointB); sideVertices.Add(pointC); sideVertices.Add(pointD); a = ((float)pair.A.x + 25f) / 50 + UVOffset.x / 2; b = ((float)pair.B.x + 25f) / 50 + UVOffset.x / 2; uv0.x = a; uv0.y = a; uv1.x = b; uv1.y = b; uvs.Add(new Vector2(0, 0)); uvs.Add(new Vector2(0, 1)); uvs.Add(new Vector2(1, 1)); uvs.Add(new Vector2(1, 0)); sideTriangles.Add(vCount + 2); sideTriangles.Add(vCount + 1); sideTriangles.Add(vCount + 0); sideTriangles.Add(vCount + 0); sideTriangles.Add(vCount + 3); sideTriangles.Add(vCount + 2); vCount += 4; pair.A = pair.B; } Mesh mainMesh = new Mesh(); mainMesh.subMeshCount = 2; Mesh surfaceMesh = PerformTriangulationAdvanced(polygon, UVScale, UVOffset, UVRotation); ///// UVS ///// foreach (Vector2 p in surfaceMesh.uv) { uvs.Add(p); } foreach (Vector2 p in surfaceMesh.uv) { uvs.Add(p); } surfaceMesh.triangles = surfaceMesh.triangles.Reverse().ToArray(); ///// VERTICES //// List <Vector3> vertices = new List <Vector3>(); foreach (Vector3 v in sideVertices) { vertices.Add(v); } foreach (Vector3 v in surfaceMesh.vertices) { Vector3 nV = v; nV.z = z; vertices.Add(nV); } foreach (Vector3 v in surfaceMesh.vertices) { vertices.Add(v); } mainMesh.SetVertices(vertices); ///// TRIANGLES ///// List <int> triangles = new List <int>(); foreach (int p in sideTriangles) { triangles.Add(p); } mainMesh.SetTriangles(triangles, 0); triangles.Clear(); int count = sideVertices.Count(); foreach (int p in surfaceMesh.triangles) { triangles.Add(p + count); } int trisCount = surfaceMesh.triangles.Count() / 3; int[] tris = surfaceMesh.triangles; for (var i = 0; i < trisCount; i++) { var tmp = tris[i * 3]; tris[i * 3] = tris[i * 3 + 1]; tris[i * 3 + 1] = tmp; } count += surfaceMesh.vertices.Count(); foreach (int p in tris) { triangles.Add(p + count); } mainMesh.SetTriangles(triangles, 1); ///// LEFTOVERS ///// mainMesh.uv = uvs.ToArray(); mainMesh.RecalculateNormals(); mainMesh.RecalculateBounds(); result = mainMesh; break; } return(result); }
void Start() { // create a new game object (as a child) and add required components GameObject go = new GameObject(); go.transform.parent = this.transform; go.name = "Cross"; MeshFilter mf = go.AddComponent <MeshFilter>(); MeshCollider mc = go.AddComponent <MeshCollider>(); MeshRenderer mr = go.AddComponent <MeshRenderer>(); mr.material = new Material(Shader.Find("Standard")); // create collections of input vectors and indices representing the (hard-coded) cross and its hole List <Vector2> points = new List <Vector2>(); List <List <Vector2> > holes = new List <List <Vector2> >(); List <int> indices = null; List <Vector3> vertices = null; // manually hard-coded 2D vectors representing the cross points.Add(new Vector2(10, 0)); points.Add(new Vector2(20, 0)); points.Add(new Vector2(20, 10)); points.Add(new Vector2(30, 10)); points.Add(new Vector2(30, 20)); points.Add(new Vector2(20, 20)); points.Add(new Vector2(20, 30)); points.Add(new Vector2(10, 30)); points.Add(new Vector2(10, 20)); points.Add(new Vector2(0, 20)); points.Add(new Vector2(0, 10)); points.Add(new Vector2(10, 10)); // manually hard-coded 2D vectors representing the square-shaped hole inside the cross List <Vector2> hole = new List <Vector2>(); hole.Add(new Vector2(12, 12)); hole.Add(new Vector2(18, 12)); hole.Add(new Vector2(18, 18)); hole.Add(new Vector2(12, 18)); holes.Add(hole); // perform TRIANGULATION Triangulation.triangulate(points, holes, 0.0f, out indices, out vertices); // create mesh instance and assign indices and vertices representing the (newly triangulated) mesh Mesh mesh = mf.mesh; mesh.Clear(); mesh.vertices = vertices.ToArray(); mesh.triangles = indices.ToArray(); mesh.RecalculateNormals(); // reset mesh collider after (re-)creation go.GetComponent <MeshCollider>().sharedMesh = mesh; // generate simple UV map Vector2[] uvs = new Vector2[mesh.vertices.Length]; for (int i = 0; i < uvs.Length; i++) { uvs[i] = new Vector2(mesh.vertices[i].x, mesh.vertices[i].y); } mesh.uv = uvs; }
public ModelRoot AddTerrainMesh(ModelRoot model, HeightMap heightMap, PBRTexture textures) { Triangulation triangulation = _meshService.TriangulateHeightMap(heightMap); return(AddTerrainMesh(model, triangulation, textures)); }