void DrawSelectionToolUI() { GUILayout.Label(selectedCount + " particle(s) selected"); GUILayout.BeginHorizontal(); if (GUILayout.Button("Invert", GUILayout.Width(88))) { for (int i = 0; i < selectionStatus.Length; i++) { if (actor.active[i]) { selectionStatus[i] = !selectionStatus[i]; } } SelectionChanged(); } GUI.enabled = selectedCount > 0; if (GUILayout.Button("Clear", GUILayout.Width(88))) { for (int i = 0; i < selectionStatus.Length; i++) { selectionStatus[i] = false; } SelectionChanged(); } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (GUILayout.Button("Select fixed", GUILayout.Width(88))) { for (int i = 0; i < actor.invMasses.Length; i++) { if (actor.active[i] && actor.invMasses[i] == 0) { selectionStatus[i] = true; } } SelectionChanged(); } GUI.enabled = selectedCount > 0; if (GUILayout.Button("Unselect fixed", GUILayout.Width(88))) { for (int i = 0; i < actor.invMasses.Length; i++) { if (actor.active[i] && actor.invMasses[i] == 0) { selectionStatus[i] = false; } } SelectionChanged(); } GUI.enabled = true; GUILayout.EndHorizontal(); GUI.enabled = selectedCount > 0; GUILayout.BeginHorizontal(); if (GUILayout.Button(new GUIContent("Fix", EditorGUIUtility.Load("PinIcon.psd") as Texture2D), GUILayout.MaxHeight(18), GUILayout.Width(88))) { Undo.RecordObject(actor, "Fix particles"); for (int i = 0; i < selectionStatus.Length; i++) { if (selectionStatus[i]) { if (actor.invMasses[i] != 0) { SetPropertyValue(ParticleProperty.MASS, i, Mathf.Infinity); newProperty = GetPropertyValue(ParticleProperty.MASS, i); actor.velocities[i] = Vector3.zero; } } } actor.PushDataToSolver(new ObiSolverData(ObiSolverData.ParticleData.INV_MASSES | ObiSolverData.ParticleData.VELOCITIES)); EditorUtility.SetDirty(actor); } if (GUILayout.Button(new GUIContent("Unfix", EditorGUIUtility.Load("UnpinIcon.psd") as Texture2D), GUILayout.MaxHeight(18), GUILayout.Width(88))) { Undo.RecordObject(actor, "Unfix particles"); for (int i = 0; i < selectionStatus.Length; i++) { if (selectionStatus[i]) { if (actor.invMasses[i] == 0) { SetPropertyValue(ParticleProperty.MASS, i, 1); } } } actor.PushDataToSolver(new ObiSolverData(ObiSolverData.ParticleData.INV_MASSES)); EditorUtility.SetDirty(actor); } if (GUILayout.Button("CUT")) { ObiCloth mesh = ((ObiCloth)actor); mesh.DistanceConstraints.RemoveFromSolver(null); mesh.AerodynamicConstraints.RemoveFromSolver(null); MeshBuffer buf = new MeshBuffer(mesh.clothMesh); int[] sel = new int[2]; int k = 0; for (int i = 0; i < selectionStatus.Length; i++) { if (selectionStatus[i]) { sel[k] = i; k++; if (k == 2) { break; } } } int cindex = -1; for (int j = 0; j < mesh.DistanceConstraints.restLengths.Count; j++) { if ((mesh.DistanceConstraints.springIndices[j * 2] == sel[0] && mesh.DistanceConstraints.springIndices[j * 2 + 1] == sel[1]) || (mesh.DistanceConstraints.springIndices[j * 2] == sel[1] && mesh.DistanceConstraints.springIndices[j * 2 + 1] == sel[0])) { cindex = j; break; } } if (cindex >= 0) { mesh.Tear(cindex, buf); } mesh.DistanceConstraints.AddToSolver(mesh); mesh.AerodynamicConstraints.AddToSolver(mesh); buf.Apply(); mesh.GetMeshDataArrays(mesh.clothMesh); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); EditorGUI.showMixedValue = false; for (int i = 0; i < selectionStatus.Length; i++) { if (selectionStatus[i] && !Mathf.Approximately(GetPropertyValue(currentProperty, i), selectionProperty)) { EditorGUI.showMixedValue = true; } } newProperty = EditorGUILayout.FloatField(newProperty, GUILayout.Width(88)); EditorGUI.showMixedValue = false; if (GUILayout.Button("Set " + GetPropertyName(), GUILayout.Width(88))) { Undo.RecordObject(actor, "Set particle property"); selectionProperty = newProperty; for (int i = 0; i < selectionStatus.Length; i++) { if (selectionStatus[i]) { SetPropertyValue(currentProperty, i, selectionProperty); } } ParticlePropertyChanged(); EditorUtility.SetDirty(actor); } GUILayout.EndHorizontal(); GUI.enabled = true; }
void DrawSelectionToolUI() { GUILayout.Label(selectedCount+" particle(s) selected"); GUILayout.BeginHorizontal(); if (GUILayout.Button("Invert",GUILayout.Width(88))){ for(int i = 0; i < selectionStatus.Length; i++){ if (actor.active[i]) selectionStatus[i] = !selectionStatus[i]; } SelectionChanged(); } GUI.enabled = selectedCount > 0; if (GUILayout.Button("Clear",GUILayout.Width(88))){ for(int i = 0; i < selectionStatus.Length; i++) selectionStatus[i] = false; SelectionChanged(); } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (GUILayout.Button("Select fixed",GUILayout.Width(88))){ for(int i = 0; i < actor.invMasses.Length; i++){ if (actor.active[i] && actor.invMasses[i] == 0) selectionStatus[i] = true; } SelectionChanged(); } GUI.enabled = selectedCount > 0; if (GUILayout.Button("Unselect fixed",GUILayout.Width(88))){ for(int i = 0; i < actor.invMasses.Length; i++){ if (actor.active[i] && actor.invMasses[i] == 0) selectionStatus[i] = false; } SelectionChanged(); } GUI.enabled = true; GUILayout.EndHorizontal(); GUI.enabled = selectedCount > 0; GUILayout.BeginHorizontal(); if (GUILayout.Button(new GUIContent("Fix",EditorGUIUtility.Load("PinIcon.psd") as Texture2D),GUILayout.MaxHeight(18),GUILayout.Width(88))){ Undo.RecordObject(actor, "Fix particles"); for(int i = 0; i < selectionStatus.Length; i++){ if (selectionStatus[i]){ if (actor.invMasses[i] != 0){ SetPropertyValue(ParticleProperty.MASS,i,Mathf.Infinity); newProperty = GetPropertyValue(ParticleProperty.MASS,i); actor.velocities[i] = Vector3.zero; } } } actor.PushDataToSolver(new ObiSolverData(ObiSolverData.ParticleData.INV_MASSES | ObiSolverData.ParticleData.VELOCITIES)); EditorUtility.SetDirty(actor); } if (GUILayout.Button(new GUIContent("Unfix",EditorGUIUtility.Load("UnpinIcon.psd") as Texture2D),GUILayout.MaxHeight(18),GUILayout.Width(88))){ Undo.RecordObject(actor, "Unfix particles"); for(int i = 0; i < selectionStatus.Length; i++){ if (selectionStatus[i]){ if (actor.invMasses[i] == 0){ SetPropertyValue(ParticleProperty.MASS,i,1); } } } actor.PushDataToSolver(new ObiSolverData(ObiSolverData.ParticleData.INV_MASSES)); EditorUtility.SetDirty(actor); } if (GUILayout.Button("CUT")){ ObiCloth mesh = ((ObiCloth)actor); mesh.DistanceConstraints.RemoveFromSolver(null); mesh.AerodynamicConstraints.RemoveFromSolver(null); MeshBuffer buf = new MeshBuffer(mesh.clothMesh); int[] sel = new int[2]; int k = 0; for(int i = 0; i < selectionStatus.Length; i++){ if (selectionStatus[i]){ sel[k] = i; k++; if (k == 2) break; } } int cindex = -1; for (int j = 0; j < mesh.DistanceConstraints.restLengths.Count; j++){ if ((mesh.DistanceConstraints.springIndices[j*2] == sel[0] && mesh.DistanceConstraints.springIndices[j*2+1] == sel[1]) || (mesh.DistanceConstraints.springIndices[j*2] == sel[1] && mesh.DistanceConstraints.springIndices[j*2+1] == sel[0])){ cindex = j; break; } } if (cindex >= 0) mesh.Tear(cindex,buf); mesh.DistanceConstraints.AddToSolver(mesh); mesh.AerodynamicConstraints.AddToSolver(mesh); buf.Apply(); mesh.GetMeshDataArrays(mesh.clothMesh); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); EditorGUI.showMixedValue = false; for(int i = 0; i < selectionStatus.Length; i++){ if (selectionStatus[i] && !Mathf.Approximately(GetPropertyValue(currentProperty,i), selectionProperty)){ EditorGUI.showMixedValue = true; } } newProperty = EditorGUILayout.FloatField(newProperty,GUILayout.Width(88)); EditorGUI.showMixedValue = false; if (GUILayout.Button("Set "+GetPropertyName(),GUILayout.Width(88))){ Undo.RecordObject(actor, "Set particle property"); selectionProperty = newProperty; for(int i = 0; i < selectionStatus.Length; i++){ if (selectionStatus[i]){ SetPropertyValue(currentProperty,i,selectionProperty); } } ParticlePropertyChanged(); EditorUtility.SetDirty(actor); } GUILayout.EndHorizontal(); GUI.enabled = true; }
/** * Calculates angle-weighted normals for the input mesh, taking into account shared vertices. */ /*public Vector3[] AngleWeightedNormals(){ * * if (input == null) return null; * * Vector3[] normals = input.normals; * Vector3[] vertices = input.vertices; * * for(int i = 0; i < normals.Length; i++) * normals[i] = Vector3.zero; * * int i1,i2,i3; * Vector3 e1, e2; * foreach(HEFace face in heFaces){ * * HEVertex hv1 = heVertices[heHalfEdges[face.edges[0]].endVertex]; * HEVertex hv2 = heVertices[heHalfEdges[face.edges[1]].endVertex]; * HEVertex hv3 = heVertices[heHalfEdges[face.edges[2]].endVertex]; * * i1 = hv1.physicalIDs[0]; * i2 = hv2.physicalIDs[0]; * i3 = hv3.physicalIDs[0]; * * e1 = vertices[i2]-vertices[i1]; * e2 = vertices[i3]-vertices[i1]; * foreach(int pi in hv1.physicalIDs) * normals[pi] += Vector3.Cross(e1,e2) * Mathf.Acos(Vector3.Dot(e1.normalized,e2.normalized)); * * e1 = vertices[i3]-vertices[i2]; * e2 = vertices[i1]-vertices[i2]; * foreach(int pi in hv2.physicalIDs) * normals[pi] += Vector3.Cross(e1,e2) * Mathf.Acos(Vector3.Dot(e1.normalized,e2.normalized)); * * e1 = vertices[i1]-vertices[i3]; * e2 = vertices[i2]-vertices[i3]; * foreach(int pi in hv3.physicalIDs) * normals[pi] += Vector3.Cross(e1,e2) * Mathf.Acos(Vector3.Dot(e1.normalized,e2.normalized)); * * } * * for(int i = 0; i < normals.Length; i++) * normals[i].Normalize(); * * return normals; * }*/ /** * Splits a vertex in two along a plane. Returns true if the vertex can be split, false otherwise. * \param vertex the vertex to split. * \param splitPlane plane to split the vertex at. * \param newVertex the newly created vertex after the split operation has been performed. * \param vertices new mesh vertices list after the split operation. * \param updatedEdges indices of half-edges that need some kind of constraint update. */ public bool SplitVertex(Oni.Vertex vertex, Plane splitPlane, MeshBuffer meshBuffer, Vector4[] positions, List <int> particleIndices, out Oni.Vertex newVertex, out HashSet <int> updatedEdges, out HashSet <int> addedEdges) { // initialize return values: updatedEdges = new HashSet <int>(); addedEdges = new HashSet <int>(); newVertex = new Oni.Vertex(); // initialize face lists for each side of the split plane. List <Oni.Face> side1Faces = new List <Oni.Face>(); List <Oni.Face> side2Faces = new List <Oni.Face>(); HashSet <int> side2Edges = new HashSet <int>(); // classify adjacent faces depending on which side of the cut plane they reside in: foreach (Oni.Face face in GetNeighbourFacesEnumerator(vertex)) { Oni.HalfEdge e1 = heHalfEdges[face.halfEdge]; Oni.HalfEdge e2 = heHalfEdges[e1.nextHalfEdge]; Oni.HalfEdge e3 = heHalfEdges[e2.nextHalfEdge]; // Skip this face if it doesnt contain the splitted vertex. // This can happen because edge pair links are not updated, and so a vertex in the cut stil "sees" // the faces at the other side like neighbour faces. if (e1.endVertex != vertex.index && e2.endVertex != vertex.index && e3.endVertex != vertex.index) { continue; } // Average positions to get the center of the face: Vector3 faceCenter = (positions[particleIndices[e1.endVertex]] + positions[particleIndices[e2.endVertex]] + positions[particleIndices[e3.endVertex]]) / 3.0f; if (splitPlane.GetSide(faceCenter)) { side1Faces.Add(face); } else { side2Faces.Add(face); side2Edges.Add(e1.index); side2Edges.Add(e2.index); side2Edges.Add(e3.index); } } // If the vertex cant be split, return false. if (side1Faces.Count == 0 || side2Faces.Count == 0) { return(false); } // create a new vertex: newVertex = new Oni.Vertex(vertex.position, heVertices.Count, vertex.halfEdge); // add a new vertex to the mesh too, if needed. if (meshBuffer != null) { visualVertexBuffer.Add(new List <int>() { meshBuffer.vertexCount }); meshBuffer.AddVertex(visualVertexBuffer[vertex.index][0]); } // rearrange edges at side 1: foreach (Oni.Face face in side1Faces) { // find half edges that start or end at the split vertex: int[] faceEdges = GetFaceEdges(face); Oni.HalfEdge edgeIn = heHalfEdges[Array.Find <int>(faceEdges, e => heHalfEdges[e].endVertex == vertex.index)]; Oni.HalfEdge edgeOut = heHalfEdges[Array.Find <int>(faceEdges, e => this.GetHalfEdgeStartVertex(heHalfEdges[e]) == vertex.index)]; // Edges whose pair is on the other side of the cut and share the same vertices, will spawn a new constraint. if (side2Edges.Contains(edgeIn.pair) && GetHalfEdgeStartVertex(edgeIn) == heHalfEdges[edgeIn.pair].endVertex) { addedEdges.Add(Mathf.Max(edgeIn.index, edgeIn.pair)); } if (side2Edges.Contains(edgeOut.pair) && GetHalfEdgeStartVertex(heHalfEdges[edgeOut.pair]) == edgeOut.endVertex) { addedEdges.Add(Mathf.Max(edgeOut.index, edgeOut.pair)); } // Constraints for these edges should be updated. (There's no guarantee the constraint exists!). updatedEdges.Add(edgeIn.index); updatedEdges.Add(edgeIn.pair); updatedEdges.Add(edgeOut.index); updatedEdges.Add(edgeOut.pair); // stitch in half edge to new vertex edgeIn.endVertex = newVertex.index; newVertex.halfEdge = edgeOut.index; heHalfEdges[edgeIn.index] = edgeIn; heHalfEdges[edgeOut.index] = edgeOut; // update mesh triangle buffer to point at new vertex where needed: if (meshBuffer != null) { if (meshBuffer.triangles[face.index * 3] == visualVertexBuffer[vertex.index][0]) { meshBuffer.triangles[face.index * 3] = meshBuffer.vertexCount - 1; } if (meshBuffer.triangles[face.index * 3 + 1] == visualVertexBuffer[vertex.index][0]) { meshBuffer.triangles[face.index * 3 + 1] = meshBuffer.vertexCount - 1; } if (meshBuffer.triangles[face.index * 3 + 2] == visualVertexBuffer[vertex.index][0]) { meshBuffer.triangles[face.index * 3 + 2] = meshBuffer.vertexCount - 1; } } } // Add the nex vertex to the half-edge. heVertices.Add(newVertex); meshInfo.closed = false; return(true); }