//If we have a forward and an up reference vector //So this is not going to work if we have loops //tangent is same as forward public InterpolationTransform(MyVector3 position, MyVector3 tangent, MyVector3 up) { this.position = position; MyVector3 biNormal = MyVector3.Normalize(MyVector3.Cross(up, tangent)); MyVector3 normal = MyVector3.Normalize(MyVector3.Cross(tangent, biNormal)); this.orientation = Quaternion.LookRotation(tangent.ToVector3(), normal.ToVector3()); }
// // Quaternion operations // //Rotate a quaternion some degrees around some axis public static MyQuaternion RotateQuaternion(MyQuaternion oldQuaternion, float angleInDegrees, MyVector3 rotationAxis) { Quaternion rotationQuaternion = Quaternion.AngleAxis(angleInDegrees, rotationAxis.ToVector3()); //To rotate a quaternion you just multiply it with the rotation quaternion //Important that rotationQuaternion is first! Quaternion newQuaternion = rotationQuaternion * oldQuaternion.unityQuaternion; MyQuaternion myNewQuaternion = new MyQuaternion(newQuaternion); return(myNewQuaternion); }
// // Get a Transform (includes position and orientation) at point t // public InterpolationTransform GetTransform(float t) { //Same as when we calculate t MyVector3 interpolation_1_2 = _Interpolation.BezierQuadratic(posA, handleB, handleA, t); MyVector3 interpolation_2_3 = _Interpolation.BezierQuadratic(posA, posB, handleB, t); MyVector3 finalInterpolation = _Interpolation.BezierLinear(interpolation_1_2, interpolation_2_3, t); //This direction is always tangent to the curve MyVector3 forwardDir = MyVector3.Normalize(interpolation_2_3 - interpolation_1_2); //A simple way to get the other directions is to use LookRotation with just forward dir as parameter //Then the up direction will always be the world up direction, and it calculates the right direction Quaternion orientation = Quaternion.LookRotation(forwardDir.ToVector3()); InterpolationTransform trans = new InterpolationTransform(finalInterpolation, orientation); return(trans); }
//Fill the hole (or holes) in the mesh private static HashSet <Hole> FillHoles(HashSet <HalfEdge3> holeEdgesI, HashSet <HalfEdge3> holeEdgesO, OrientedPlane3 orientedCutPlane, Transform meshTrans, MyVector3 planeNormal) { if (holeEdgesI == null || holeEdgesI.Count == 0) { Debug.Log("This mesh has no hole"); return(null); } //Find all separate holes HashSet <List <HalfEdge3> > allHoles = IdentifySeparateHoles(holeEdgesI); if (allHoles.Count == 0) { Debug.LogWarning("Couldn't identify any holes even though we have hole edges"); return(null); } //Debug //foreach (List<HalfEdge3> hole in allHoles) //{ // foreach (HalfEdge3 e in hole) // { // Debug.DrawLine(meshTrans.TransformPoint(e.v.position.ToVector3()), Vector3.zero, Color.white, 5f); // } //} //Fill the hole with a mesh HashSet <Hole> holeMeshes = new HashSet <Hole>(); foreach (List <HalfEdge3> hole in allHoles) { HalfEdgeData3 holeMeshI = new HalfEdgeData3(); HalfEdgeData3 holeMeshO = new HalfEdgeData3(); //Transform vertices to local position of the cut plane to make it easier to triangulate with Ear Clipping //Ear CLipping wants vertices in 2d List <MyVector2> sortedEdges_2D = new List <MyVector2>(); Transform planeTrans = orientedCutPlane.planeTrans; foreach (HalfEdge3 e in hole) { MyVector3 pMeshSpace = e.v.position; //Mesh space to Global space Vector3 pGlobalSpace = meshTrans.TransformPoint(pMeshSpace.ToVector3()); //Global space to Plane space Vector3 pPlaneSpace = planeTrans.InverseTransformPoint(pGlobalSpace); //Y is normal direction so should be 0 MyVector2 p2D = new MyVector2(pPlaneSpace.x, pPlaneSpace.z); sortedEdges_2D.Add(p2D); } //Triangulate with Ear Clipping HashSet <Triangle2> triangles = _EarClipping.Triangulate(sortedEdges_2D, null, optimizeTriangles: false); //Debug.Log($"Number of triangles from Ear Clipping: {triangles.Count}"); //Transform vertices to mesh space and half-edge data structure foreach (Triangle2 t in triangles) { //3d space Vector3 p1 = new Vector3(t.p1.x, 0f, t.p1.y); Vector3 p2 = new Vector3(t.p2.x, 0f, t.p2.y); Vector3 p3 = new Vector3(t.p3.x, 0f, t.p3.y); //Plane space to Global space Vector3 p1Global = planeTrans.TransformPoint(p1); Vector3 p2Global = planeTrans.TransformPoint(p2); Vector3 p3Global = planeTrans.TransformPoint(p3); //Global space to Mesh space Vector3 p1Mesh = meshTrans.InverseTransformPoint(p1Global); Vector3 p2Mesh = meshTrans.InverseTransformPoint(p2Global); Vector3 p3Mesh = meshTrans.InverseTransformPoint(p3Global); //For inside mesh MyMeshVertex v1_I = new MyMeshVertex(p1Mesh.ToMyVector3(), planeNormal); MyMeshVertex v2_I = new MyMeshVertex(p2Mesh.ToMyVector3(), planeNormal); MyMeshVertex v3_I = new MyMeshVertex(p3Mesh.ToMyVector3(), planeNormal); //For inside mesh MyMeshVertex v1_O = new MyMeshVertex(p1Mesh.ToMyVector3(), -planeNormal); MyMeshVertex v2_O = new MyMeshVertex(p2Mesh.ToMyVector3(), -planeNormal); MyMeshVertex v3_O = new MyMeshVertex(p3Mesh.ToMyVector3(), -planeNormal); //Now we can finally add this triangle to the half-edge data structure AddTriangleToMesh(v1_I, v2_I, v3_I, holeMeshI, null); AddTriangleToMesh(v1_O, v3_O, v2_O, holeMeshO, null); } //We also need an edge belonging to the mesh (not hole mesh) to easier merge mesh with hole //The hole edges were generated for the Inside mesh HalfEdge3 holeEdgeI = hole[0]; //But we also need an edge for the Outside mesh bool foundCorrespondingEdge = false; MyVector3 eGoingTo = holeEdgeI.v.position; MyVector3 eGoingFrom = holeEdgeI.prevEdge.v.position; foreach (HalfEdge3 holeEdgeO in holeEdgesO) { MyVector3 eOppsiteGoingTo = holeEdgeO.v.position; MyVector3 eOppsiteGoingFrom = holeEdgeO.prevEdge.v.position; if (eOppsiteGoingTo.Equals(eGoingFrom) && eOppsiteGoingFrom.Equals(eGoingTo)) { Hole newHoleMesh = new Hole(holeMeshI, holeMeshO, holeEdgeI, holeEdgeO); holeMeshes.Add(newHoleMesh); foundCorrespondingEdge = true; break; } } if (!foundCorrespondingEdge) { Debug.Log("Couldnt find opposite edge in hole, so no hole was added"); } } return(holeMeshes); }
//Remove flat tetrahedrons (a vertex in a triangle) private static bool RemoveFlatTetrahedrons(HalfEdgeData3 meshData, Normalizer3 normalizer = null) { HashSet <HalfEdgeVertex3> vertices = meshData.verts; bool foundFlatTetrahedron = false; foreach (HalfEdgeVertex3 vertex in vertices) { HashSet <HalfEdge3> edgesGoingToVertex = vertex.GetEdgesPointingToVertex(meshData); if (edgesGoingToVertex.Count == 3) { //Find the vertices of the triangle covering this vertex clock-wise HalfEdgeVertex3 v1 = vertex.edge.v; HalfEdgeVertex3 v2 = vertex.edge.prevEdge.oppositeEdge.v; HalfEdgeVertex3 v3 = vertex.edge.oppositeEdge.nextEdge.v; //Build a plane MyVector3 normal = MyVector3.Normalize(MyVector3.Cross(v3.position - v2.position, v1.position - v2.position)); Plane3 plane = new Plane3(v1.position, normal); //Find the distance from the vertex to the plane float distance = _Geometry.GetSignedDistanceFromPointToPlane(vertex.position, plane); distance = Mathf.Abs(distance); if (distance < FLAT_TETRAHEDRON_DISTANCE) { //Debug.Log("Found flat tetrahedron"); Vector3 p1 = normalizer.UnNormalize(v1.position).ToVector3(); Vector3 p2 = normalizer.UnNormalize(v2.position).ToVector3(); Vector3 p3 = normalizer.UnNormalize(v3.position).ToVector3(); TestAlgorithmsHelpMethods.DebugDrawTriangle(p1, p2, p3, normal.ToVector3(), Color.blue, Color.red); foundFlatTetrahedron = true; //Save the opposite edges HashSet <HalfEdge3> oppositeEdges = new HashSet <HalfEdge3>(); oppositeEdges.Add(v1.edge.oppositeEdge); oppositeEdges.Add(v2.edge.oppositeEdge); oppositeEdges.Add(v3.edge.oppositeEdge); //Remove the three triangles foreach (HalfEdge3 e in edgesGoingToVertex) { meshData.DeleteFace(e.face); } //Add the new triangle (could maybe connect it ourselves) HalfEdgeFace3 newTriangle = meshData.AddTriangle(v1.position, v2.position, v3.position, findOppositeEdge: false); meshData.TryFindOppositeEdge(newTriangle.edge, oppositeEdges); meshData.TryFindOppositeEdge(newTriangle.edge.nextEdge, oppositeEdges); meshData.TryFindOppositeEdge(newTriangle.edge.nextEdge.nextEdge, oppositeEdges); break; } } } return(foundFlatTetrahedron); }
//Generate a mesh public static Mesh GenerateMesh(List <InterpolationTransform> transforms, MeshProfile profile, float profileScale) { if (profile == null) { Debug.Log("You need to assign a mesh profile"); return(null); } if (transforms == null || transforms.Count <= 1) { Debug.Log("You need more transforms"); return(null); } //Test that the profile is correct //InterpolationTransform testTrans = transforms[1]; //DisplayMeshProfile(profile, testTrans, profileScale); //Vertices List <Vector3> vertices = new List <Vector3>(); //Normals List <Vector3> normals = new List <Vector3>(); for (int step = 0; step < transforms.Count; step++) { InterpolationTransform thisTransform = transforms[step]; for (int i = 0; i < profile.vertices.Length; i++) { MyVector2 localPos2d = profile.vertices[i].point; MyVector3 localPos = new MyVector3(localPos2d.x, localPos2d.y, 0f); MyVector3 pos = thisTransform.LocalToWorld_Pos(localPos * profileScale); vertices.Add(pos.ToVector3()); //Normals MyVector2 localNormal2d = profile.vertices[i].normal; MyVector3 localNormal = new MyVector3(localNormal2d.x, localNormal2d.y, 0f); MyVector3 normal = thisTransform.LocalToWorld_Dir(localNormal); normals.Add(normal.ToVector3()); } } //Triangles List <int> triangles = new List <int>(); //We connect the first transform with the next transform, ignoring the last transform because it doesnt have a next for (int step = 0; step < transforms.Count - 1; step++) { //The index where this profile starts in the list of all vertices in the entire mesh int profileIndexThis = step * profile.vertices.Length; //The index where the next profile starts int profileIndexNext = (step + 1) * profile.vertices.Length; //Each line has 2 points for (int line = 0; line < profile.lineIndices.Length; line++) { int lineIndexA = profile.lineIndices[line].x; int lineIndexB = profile.lineIndices[line].y; //Now we can identify the vertex we need in the list of all vertices in the entire mesh //The current profile int thisA = profileIndexThis + lineIndexA; int thisB = profileIndexThis + lineIndexB; //The next profile int nextA = profileIndexNext + lineIndexA; int nextB = profileIndexNext + lineIndexB; //Build two triangles triangles.Add(thisA); triangles.Add(nextA); triangles.Add(nextB); triangles.Add(thisB); triangles.Add(thisA); triangles.Add(nextB); } } Mesh mesh = new Mesh(); mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.normals = normals.ToArray(); //mesh.RecalculateNormals(); return(mesh); }
public MyVector3 RotateVector(MyVector3 vec) { Vector3 rotatedVec = unityQuaternion * vec.ToVector3(); return(rotatedVec.ToMyVector3()); }
//Rotate a vector by using a quaternion public static MyVector3 RotateVector(MyQuaternion quat, MyVector3 vec) { Vector3 rotatedVec = quat.unityQuaternion * vec.ToVector3(); return(rotatedVec.ToMyVector3()); }
public MyQuaternion(MyVector3 forward, MyVector3 up) { this.unityQuaternion = Quaternion.LookRotation(forward.ToVector3(), up.ToVector3()); }