void UpdateMeshFromSplineGraph() { Mesh mesh = meshFilter.sharedMesh; if (mesh == null) { mesh = new Mesh(); } if (radialEdgeCount <= 0) { // TODO: Cleanup case when radialEdgeCount == 0 due to user still editing into inspector field. // This should be handled with a delayed int field. return; } var splineGraph = (splineGraphComponent != null) ? splineGraphComponent.GetSplineGraph() : splineGraphManager.GetSplineGraph(); // First, go through and compute the number of vertex ring subdivisions required to represent the spline graph based on curvature. Int16 vertexIsValidCount = splineGraph.ComputeVertexIsValidCount(); Int16 edgeIsValidCount = splineGraph.ComputeEdgeIsValidCount(); int[] edgeSubdivisionIndex = new int[splineGraph.edgePoolChildren.count]; int[] edgeSubdivisionCount = new int[splineGraph.edgePoolChildren.count]; int meshSubdivisionCountTotal = 0; int meshRingCountTotal = 0; for (Int16 edgeIndex = 0; edgeIndex < splineGraph.edgePoolChildren.count; ++edgeIndex) { DirectedEdge edge = splineGraph.edgePoolChildren.data[edgeIndex]; if (edge.IsValid() == 0) { continue; } float splineLength = splineGraph.payload.edgeLengths.data[edgeIndex]; int subdivisionCount = math.max(1, (int)math.floor(subdivisionsPerMeter * splineLength + 0.5f)); int ringCount = subdivisionCount + 1; edgeSubdivisionIndex[edgeIndex] = meshSubdivisionCountTotal; edgeSubdivisionCount[edgeIndex] = subdivisionCount; meshSubdivisionCountTotal += subdivisionCount; meshRingCountTotal += ringCount; } // TODO: Calculate counts. int meshVertexCount = meshRingCountTotal * radialEdgeCount; Vector3[] vertices = new Vector3[meshVertexCount]; Vector2[] uvs = new Vector2[meshVertexCount]; Vector3[] normals = new Vector3[meshVertexCount]; Color[] colors = new Color[meshVertexCount]; int[] triangles = new int[meshVertexCount * 6]; int meshVertexIndex = 0; int meshTriangleIndex = 0; for (Int16 edgeIndex = 0; edgeIndex < splineGraph.edgePoolChildren.count; ++edgeIndex) { DirectedEdge edge = splineGraph.edgePoolChildren.data[edgeIndex]; if (edge.IsValid() == 0) { continue; } Int16 vertexIndexChild = splineGraph.edgePoolChildren.data[edgeIndex].vertexIndex; Int16 vertexIndexParent = splineGraph.edgePoolParents.data[edgeIndex].vertexIndex; SplineMath.Spline spline = splineGraph.payload.edgeParentToChildSplines.data[edgeIndex]; float splineLength = splineGraph.payload.edgeLengths.data[edgeIndex]; quaternion rotationParent = splineGraph.payload.rotations.data[vertexIndexParent]; quaternion rotationChild = splineGraph.payload.rotations.data[vertexIndexChild]; SplineMath.Spline splineLeash = splineGraph.payload.edgeParentToChildSplinesLeashes.data[edgeIndex]; // Find neighboring edge (if it exists) for use in CSG style operation against current one to handle intersections in branching region. DirectedVertex vertexParent = splineGraph.vertices.data[vertexIndexParent]; Debug.Assert(vertexParent.IsValid() == 1); DirectedVertex vertexChild = splineGraph.vertices.data[vertexIndexChild]; Debug.Assert(vertexChild.IsValid() == 1); int edgeSubdivisionCurrentCount = edgeSubdivisionCount[edgeIndex]; for (int s = 0; s <= edgeSubdivisionCurrentCount; ++s) { float t = (float)s / (float)edgeSubdivisionCurrentCount; // Debug.Log("s = " + s + ", sCount = " + edgeSubdivisionCurrentCount + ", t = " + t); float vNormalized = t; if (s > 0) { // Compute the relative distance we have traveled between our current edge ring and previous. SplineMath.ComputeSplitAtT(out SplineMath.Spline s00, out SplineMath.Spline s01, spline, t); vNormalized = SplineMath.ComputeLengthEstimate(s00, 1e-5f) / splineLength; } float3 positionOnSpline = SplineMath.EvaluatePositionFromT(spline, t); quaternion rotationOnSpline = SplineMath.EvaluateRotationWithRollFromT(spline, rotationParent, rotationChild, t); // float2 leashMaxOS = math.lerp(leashParent, leashChild, t); float2 leashMaxOS = SplineMath.EvaluatePositionFromT(splineLeash, t).xy; // Generate radial ring of triangles if (s < edgeSubdivisionCurrentCount) { for (int v = 0, vLen = radialEdgeCount; v < vLen; ++v) { triangles[meshTriangleIndex + v * 6 + 0] = meshVertexIndex + ((v + 0) % radialEdgeCount) + (0 * radialEdgeCount); triangles[meshTriangleIndex + v * 6 + 1] = meshVertexIndex + ((v + 0) % radialEdgeCount) + (1 * radialEdgeCount); triangles[meshTriangleIndex + v * 6 + 2] = meshVertexIndex + ((v + 1) % radialEdgeCount) + (1 * radialEdgeCount); triangles[meshTriangleIndex + v * 6 + 3] = meshVertexIndex + ((v + 1) % radialEdgeCount) + (1 * radialEdgeCount); triangles[meshTriangleIndex + v * 6 + 4] = meshVertexIndex + ((v + 1) % radialEdgeCount) + (0 * radialEdgeCount); triangles[meshTriangleIndex + v * 6 + 5] = meshVertexIndex + ((v + 0) % radialEdgeCount) + (0 * radialEdgeCount); } meshTriangleIndex += radialEdgeCount * 6; } // Generate radial ring of vertices. // bool ringIntersectsSibling = false; for (int v = 0; v < radialEdgeCount; ++v) { float thetaNormalized = (float)v / (float)radialEdgeCount; float theta = thetaNormalized * 2.0f * math.PI; float2 vertexOffsetOS = new float2( math.cos(theta), math.sin(theta) ) * leashMaxOS;// * radius; float3 vertexOffsetWS = math.mul(rotationOnSpline, new float3(vertexOffsetOS, 0.0f)); float3 vertexPositionWS = positionOnSpline + vertexOffsetWS; float vertexIntersectionSignedDistanceMin = float.MaxValue; { for (Int16 edgeIndexSibling = vertexParent.childHead; edgeIndexSibling != -1; edgeIndexSibling = splineGraph.edgePoolChildren.data[edgeIndexSibling].next) { DirectedEdge edgeSibling = splineGraph.edgePoolChildren.data[edgeIndexSibling]; Debug.Assert(edgeSibling.IsValid() == 1); if (edgeIndexSibling == edgeIndex) { // Ignore ourselves. continue; } // Found our sibling edge. Only use the first one, as we only currently support CSG against a single branch. Int16 siblingVertexIndexChild = edgeSibling.vertexIndex; Debug.Assert(siblingVertexIndexChild != -1); Int16 siblingVertexIndexParent = splineGraph.edgePoolParents.data[edgeIndexSibling].vertexIndex; Debug.Assert(siblingVertexIndexParent != -1); SplineMath.Spline splineSibling = splineGraph.payload.edgeParentToChildSplines.data[edgeIndexSibling]; SplineMath.Spline splineLeashSibling = splineGraph.payload.edgeParentToChildSplinesLeashes.data[edgeIndexSibling]; quaternion siblingRotationParent = splineGraph.payload.rotations.data[siblingVertexIndexParent]; quaternion siblingRotationChild = splineGraph.payload.rotations.data[siblingVertexIndexChild]; SplineMath.FindTFromClosestPointOnSpline(out float siblingClosestT, out float siblingClosestDistance, vertexPositionWS, splineSibling); float3 siblingPositionOnSpline = SplineMath.EvaluatePositionFromT(splineSibling, siblingClosestT); quaternion siblingRotationOnSpline = SplineMath.EvaluateRotationWithRollFromT(splineSibling, siblingRotationParent, siblingRotationChild, siblingClosestT); float2 siblingLeashMaxOS = SplineMath.EvaluatePositionFromT(splineLeashSibling, siblingClosestT).xy; float vertexIntersectionSignedDistance = math.length(math.mul(math.inverse(siblingRotationOnSpline), vertexPositionWS - siblingPositionOnSpline).xy / siblingLeashMaxOS) - 1.0f; vertexIntersectionSignedDistanceMin = math.min(vertexIntersectionSignedDistanceMin, vertexIntersectionSignedDistance); } for (Int16 edgeIndexSibling = vertexChild.parentHead; edgeIndexSibling != -1; edgeIndexSibling = splineGraph.edgePoolParents.data[edgeIndexSibling].next) { DirectedEdge edgeSibling = splineGraph.edgePoolChildren.data[edgeIndexSibling]; Debug.Assert(edgeSibling.IsValid() == 1); if (edgeIndexSibling == edgeIndex) { // Ignore ourselves. continue; } // Found our sibling edge. Only use the first one, as we only currently support CSG against a single branch. Int16 siblingVertexIndexParent = edgeSibling.vertexIndex; Debug.Assert(siblingVertexIndexParent != -1); Int16 siblingVertexIndexChild = splineGraph.edgePoolChildren.data[edgeIndexSibling].vertexIndex; Debug.Assert(siblingVertexIndexChild != -1); SplineMath.Spline splineSibling = splineGraph.payload.edgeParentToChildSplines.data[edgeIndexSibling]; SplineMath.Spline splineLeashSibling = splineGraph.payload.edgeParentToChildSplinesLeashes.data[edgeIndexSibling]; quaternion siblingRotationParent = splineGraph.payload.rotations.data[siblingVertexIndexParent]; quaternion siblingRotationChild = splineGraph.payload.rotations.data[siblingVertexIndexChild]; SplineMath.FindTFromClosestPointOnSpline(out float siblingClosestT, out float siblingClosestDistance, vertexPositionWS, splineSibling); float3 siblingPositionOnSpline = SplineMath.EvaluatePositionFromT(splineSibling, siblingClosestT); quaternion siblingRotationOnSpline = SplineMath.EvaluateRotationWithRollFromT(splineSibling, siblingRotationParent, siblingRotationChild, siblingClosestT); float2 siblingLeashMaxOS = SplineMath.EvaluatePositionFromT(splineLeashSibling, siblingClosestT).xy; float vertexIntersectionSignedDistance = math.length(math.mul(math.inverse(siblingRotationOnSpline), vertexPositionWS - siblingPositionOnSpline).xy / siblingLeashMaxOS) - 1.0f; vertexIntersectionSignedDistanceMin = math.min(vertexIntersectionSignedDistanceMin, vertexIntersectionSignedDistance); } } vertices[meshVertexIndex] = vertexPositionWS; uvs[meshVertexIndex] = new float2( thetaNormalized, vNormalized * splineLength * uvScale // TODO: Gotta figure out propogation of UVs. ); normals[meshVertexIndex] = math.normalize(vertexOffsetWS); float vertexIntersectionDistanceFade = math.smoothstep(vertexIntersectionSignedDistanceFadeMin, vertexIntersectionSignedDistanceFadeMax, vertexIntersectionSignedDistanceMin); colors[meshVertexIndex] = new Color(vertexIntersectionDistanceFade, vertexIntersectionDistanceFade, vertexIntersectionDistanceFade, vertexIntersectionDistanceFade); ++meshVertexIndex; } } } // Trim arrays to final size. Vector3[] verticesTrimmed = new Vector3[meshVertexIndex]; Vector2[] uvsTrimmed = new Vector2[meshVertexIndex]; Vector3[] normalsTrimmed = new Vector3[meshVertexIndex]; Color[] colorsTrimmed = new Color[meshVertexIndex]; int[] trianglesTrimmed = new int[meshTriangleIndex]; Array.Copy(vertices, verticesTrimmed, meshVertexIndex); Array.Copy(uvs, uvsTrimmed, meshVertexIndex); Array.Copy(normals, normalsTrimmed, meshVertexIndex); Array.Copy(colors, colorsTrimmed, meshVertexIndex); Array.Copy(triangles, trianglesTrimmed, meshTriangleIndex); // Finally assign back data (causes GC allocs). mesh.Clear(); mesh.vertices = verticesTrimmed; mesh.uv = uvsTrimmed; mesh.normals = normalsTrimmed; mesh.colors = colorsTrimmed; mesh.triangles = trianglesTrimmed; meshFilter.sharedMesh = mesh; MeshCollider meshCollider = meshFilter.gameObject.GetComponent <MeshCollider>(); if (meshCollider != null) { meshCollider.sharedMesh = mesh; } }
public WeighedDirectedEdge(DirectedVertex <T> source, DirectedVertex <T> target, int weight = 1) : base(source, target) { Weight = weight; }
public void VertexWeldAllRedundant() { Verify(); for (Int16 v0 = 0, v0Count = (Int16)splineGraph.vertices.count; v0 < v0Count; ++v0) { DirectedVertex vertex0 = splineGraph.vertices.data[v0]; if (vertex0.IsValid() == 0) { continue; } // For now, just going to handle the easy, and probably majority case of a spline that looks like: // vertex0 -------------------> vertex0Child -------------------> vertex0ChildChild // where vertex0, and vertex0Child only have 1 child and vertex0 -> vertex0Child -> vertex0ChildChild describes a straight line. Int16 vertex0ChildCount = splineGraph.VertexComputeChildCount(v0); if (vertex0ChildCount != 1) { continue; } Int16 vertex0ChildIndex = splineGraph.edgePoolChildren.data[vertex0.childHead].vertexIndex; Debug.Assert(vertex0ChildIndex >= 0 && vertex0ChildIndex < v0Count); DirectedVertex vertex0Child = splineGraph.vertices.data[vertex0ChildIndex]; Debug.Assert(vertex0Child.IsValid() == 1); Int16 vertex0ChildChildCount = splineGraph.VertexComputeChildCount(vertex0ChildIndex); if (vertex0ChildChildCount != 1) { continue; } Int16 vertex0ChildParentCount = splineGraph.VertexComputeParentCount(vertex0ChildIndex); if (vertex0ChildParentCount != 1) { continue; } // We have now verified that vertex0 -> vertex0Child -> vertex0ChildChild describe a single path. // Now need to verify that this path is a straight line. Int16 vertex0ChildChildIndex = splineGraph.edgePoolChildren.data[vertex0Child.childHead].vertexIndex; Debug.Assert(vertex0ChildChildIndex >= 0 && vertex0ChildChildIndex < v0Count); DirectedVertex vertex0ChildChild = splineGraph.vertices.data[vertex0ChildChildIndex]; Debug.Assert(vertex0ChildChild.IsValid() == 1); float3 vertex0Position = splineGraph.payload.positions.data[v0]; float3 vertex0ChildPosition = splineGraph.payload.positions.data[vertex0ChildIndex]; float3 vertex0ChildChildPosition = splineGraph.payload.positions.data[vertex0ChildChildIndex]; float3 vertex0ChildFromVertex0Direction = math.normalize(vertex0ChildPosition - vertex0Position); float3 vertex0ChildChildFromVertex0Direction = math.normalize(vertex0ChildChildPosition - vertex0Position); float3 vertex0ChildChildFromVertex0ChildDirection = math.normalize(vertex0ChildChildPosition - vertex0ChildPosition); // Note: Mathematically, we only need to actually check the first of these two dot products. // For precision reasons with large distances between vertices, we check both to be extra conservative. if (math.dot(vertex0ChildFromVertex0Direction, vertex0ChildChildFromVertex0Direction) < 0.9999f || math.dot(vertex0ChildFromVertex0Direction, vertex0ChildChildFromVertex0ChildDirection) < 0.9999f) { // Positions alone do not line up. Cannot be a linear path: // // vertex0 ------------> vertexChild0 - // \ // \ // \ // vertex0ChildChild continue; } quaternion vertex0Rotation = splineGraph.payload.rotations.data[v0]; quaternion vertex0ChildRotation = splineGraph.payload.rotations.data[vertex0ChildIndex]; quaternion vertex0ChildChildRotation = splineGraph.payload.rotations.data[vertex0ChildChildIndex]; float3 vertex0Forward = math.mul(vertex0Rotation, new float3(0.0f, 0.0f, 1.0f)); float3 vertex0ChildForward = math.mul(vertex0ChildRotation, new float3(0.0f, 0.0f, 1.0f)); float3 vertex0ChildChildForward = math.mul(vertex0ChildChildRotation, new float3(0.0f, 0.0f, 1.0f)); if (math.dot(vertex0Forward, vertex0ChildForward) < 0.999f) { // Not pointing in the same direction. Cannot be a linear path. continue; } if (math.dot(vertex0Forward, vertex0ChildChildForward) < 0.999f) { // Not pointing in the same direction. Cannot be a linear path. continue; } if (math.abs(math.dot(vertex0Forward, vertex0ChildFromVertex0Direction)) < 0.999f) { // Vertices not pointing directly toward eachother. Cannot be a linear path. continue; } float2 vertex0LeashOS = splineGraph.payload.leashes.data[v0]; float2 vertex0ChildLeashOS = splineGraph.payload.leashes.data[vertex0ChildIndex]; float3 vertex0LeashWS = math.mul(vertex0Rotation, new float3(vertex0LeashOS, 0.0f)); float3 vertex0ChildLeashWS = math.mul(vertex0Rotation, new float3(vertex0ChildLeashOS, 0.0f)); if (math.any(math.abs(vertex0LeashWS - vertex0ChildLeashWS) > 1e-3f)) { // Leash is not identical, cannot be a linear path. continue; } float2 vertex0ChildChildLeashOS = splineGraph.payload.leashes.data[vertex0ChildChildIndex]; float3 vertex0ChildChildLeashWS = math.mul(vertex0ChildChildRotation, new float3(vertex0ChildChildLeashOS, 0.0f)); if (math.any(math.abs(vertex0ChildLeashWS - vertex0ChildChildLeashWS) > 1e-3f)) { // Leash is not identical, cannot be a linear path. continue; } // Found a linear path! // Note, we do not actually care about a scale differences. // All we care about is making sure scaleIn at vertex0 is maintained, and scaleOut at vertex0ChildChildRotation is maintained. // This will automatically happen just by merging vertex0Child into vertex0. splineGraph.VertexMerge(v0, vertex0ChildIndex, Allocator.Persistent); // Need to set the dirty flag here because the call to Verify() above cleared any dirty flags that were possibly set by UndoRecord() // and we have just changed our runtime data representation via welding. isDirty = true; // Since we merged, we can stay at v0 and test again to see if there is a new merge case. --v0; } }
public override void OnInspectorGUI() { serializedObject.Update(); var sgm = target as SplineGraphManager; sgm.Verify(); EditorGUILayout.BeginVertical(); bool isEditingEnabledNew = EditorGUILayout.Toggle("Is Editing Enabled", sgm.isEditingEnabled); if (isEditingEnabledNew != sgm.isEditingEnabled) { sgm.UndoRecord("Toggled Spline Graph Manager isEditingEnabled"); sgm.isEditingEnabled = isEditingEnabledNew; } if (!sgm.isEditingEnabled) { EditorGUILayout.EndVertical(); return; } bool isRenderingEnabledNew = EditorGUILayout.Toggle("Is Rendering Enabled", sgm.isRenderingEnabled); if (isRenderingEnabledNew != sgm.isRenderingEnabled) { sgm.UndoRecord("Toggled Spline Graph Manager isRenderingEnabled"); sgm.isRenderingEnabled = isRenderingEnabledNew; } bool isAutoUpdateEnabledNew = EditorGUILayout.Toggle("Is Auto Update Enabled", sgm.isAutoUpdateEnabled); if (isAutoUpdateEnabledNew != sgm.isAutoUpdateEnabled) { sgm.UndoRecord("Toggled Spline Graph Manager isAutoUpdateEnabled"); sgm.isAutoUpdateEnabled = isAutoUpdateEnabledNew; } // return; // TODO: Remove? sgm.debugIsSpawnEnabled = EditorGUILayout.Toggle("Debug Is Spawn Enabled", sgm.debugIsSpawnEnabled); sgm.debugPosition = EditorGUILayout.Vector3Field("Debug Position", sgm.debugPosition); sgm.debugVelocity = EditorGUILayout.FloatField("Debug Velocity", sgm.debugVelocity); sgm.debugIsReverse = EditorGUILayout.Toggle("Debug Is Reverse", sgm.debugIsReverse); int typeNew = EditorGUILayout.DelayedIntField("Type", sgm.type); typeNew = math.clamp(typeNew, 0, int.MaxValue); if (typeNew != sgm.type) { sgm.UndoRecord("Edited Spline Graph Manager Type"); sgm.type = typeNew; } if (GUILayout.Button("Build Graph From Instances")) { sgm.UndoRecord("Spline Graph Manager Build Graph From Instances"); sgm.BuildGraphFromInstances(); } if (!sgm.isRenderingEnabled) { EditorGUILayout.EndVertical(); return; } for (Int16 v = 0, vCount = (Int16)sgm.splineGraph.vertices.count; v < vCount; ++v) { DirectedVertex vertex = sgm.splineGraph.vertices.data[v]; if (vertex.IsValid() == 0) { continue; } EditorGUILayout.BeginVertical(); // TODO: Convert these input fields to read-only fields. float3 position = sgm.splineGraph.payload.positions.data[v]; EditorGUILayout.Vector3Field("Position", position); quaternion rotation = sgm.splineGraph.payload.rotations.data[v]; float3 rotationEulerDegrees = ((Quaternion)rotation).eulerAngles; EditorGUILayout.Vector3Field("Rotation", rotationEulerDegrees); float2 scale = sgm.splineGraph.payload.scales.data[v]; EditorGUILayout.Vector2Field("Scale", scale); float2 leash = sgm.splineGraph.payload.leashes.data[v]; EditorGUILayout.Vector2Field("Leash", leash); EditorGUILayout.EndVertical(); } EditorGUILayout.EndVertical(); serializedObject.ApplyModifiedProperties(); UnityEditorInternal.InternalEditorUtility.RepaintAllViews(); }
public void VertexWeldAllWithinThreshold() { Verify(); for (Int16 v0 = 0, v0Count = (Int16)splineGraph.vertices.count; v0 < v0Count; ++v0) { DirectedVertex vertex0 = splineGraph.vertices.data[v0]; if (vertex0.IsValid() == 0) { continue; } float3 positionParent = splineGraph.payload.positions.data[v0]; quaternion rotationParent = splineGraph.payload.rotations.data[v0]; float2 scaleParent = splineGraph.payload.scales.data[v0]; float2 leashParent = splineGraph.payload.leashes.data[v0]; for (Int16 v1 = (Int16)(v0 + 1); v1 < v0Count; ++v1) { DirectedVertex vertex1 = splineGraph.vertices.data[v1]; if (vertex1.IsValid() == 0) { continue; } float3 positionChild = splineGraph.payload.positions.data[v1]; quaternion rotationChild = splineGraph.payload.rotations.data[v1]; float2 scaleChild = splineGraph.payload.scales.data[v1]; float2 leashChild = splineGraph.payload.leashes.data[v1]; float positionDelta2 = math.lengthsq(positionParent - positionChild); float positionMagnitude = math.max(math.cmax(math.abs(positionParent)), math.cmax(math.abs(positionChild))); float leashDelta2 = math.lengthsq(leashParent - leashChild); float epsilon = 1e-2f;//(positionMagnitude < 2.0f) ? 1e-5f : (math.log2(positionMagnitude) * 1e-5f); if (positionDelta2 > (epsilon * epsilon)) { continue; } if (leashDelta2 > (epsilon * epsilon)) { continue; } if (!(math.any(math.abs(rotationParent.value - rotationChild.value) < 1e-2f) || math.any(math.abs(rotationParent.value + rotationChild.value) < 1e-2f))) { // Quaternions have double coverage so need to compare component wise equivalence for positive and negative operand b. Debug.Log("Failed to weld because of rotation: {" + rotationParent.value.x + ", " + rotationParent.value.y + ", " + rotationParent.value.z + ", " + rotationParent.value.w + "} and {" + rotationChild.value.x + ", " + rotationChild.value.y + ", " + rotationChild.value.z + ", " + rotationChild.value.w + "}"); float3 forwardParent = math.mul(rotationParent, new float3(0.0f, 0.0f, 1.0f)); float3 forwardChild = math.mul(rotationChild, new float3(0.0f, 0.0f, 1.0f)); Debug.Log("Failed to weld forward: {" + forwardParent.x + ", " + forwardParent.y + ", " + forwardParent.z + "} and {" + forwardChild.x + ", " + forwardChild.y + ", " + forwardChild.z + "}"); float3 tangentParent = math.mul(rotationParent, new float3(1.0f, 0.0f, 0.0f)); float3 tangentChild = math.mul(rotationChild, new float3(1.0f, 0.0f, 0.0f)); Debug.Log("Failed to weld tangent: {" + tangentParent.x + ", " + tangentParent.y + ", " + tangentParent.z + "} and {" + tangentChild.x + ", " + tangentChild.y + ", " + tangentChild.z + "}"); float3 bitangentParent = math.mul(rotationParent, new float3(0.0f, 1.0f, 0.0f)); float3 bitangentChild = math.mul(rotationChild, new float3(0.0f, 1.0f, 0.0f)); Debug.Log("Failed to weld bitangent: {" + bitangentParent.x + ", " + bitangentParent.y + ", " + bitangentParent.z + "} and {" + bitangentChild.x + ", " + bitangentChild.y + ", " + bitangentChild.z + "}"); continue; } // Average vertex payload data: splineGraph.payload.positions.data[v0] = (positionParent * 0.5f + positionChild * 0.5f); splineGraph.payload.rotations.data[v0] = math.slerp(rotationParent, rotationChild, 0.5f); // Weighted average vertex payload scale and leash data. { float v0ParentCount = (float)splineGraph.VertexComputeParentCount(v0); float v0ChildCount = (float)splineGraph.VertexComputeChildCount(v0); float v1ParentCount = (float)splineGraph.VertexComputeParentCount(v1); float v1ChildCount = (float)splineGraph.VertexComputeChildCount(v1); float scaleX = ((v0ParentCount + v1ParentCount) > 0.0f) ? ((splineGraph.payload.scales.data[v0].x * v0ParentCount + splineGraph.payload.scales.data[v1].x * v1ParentCount) / (v0ParentCount + v1ParentCount)) : (splineGraph.payload.scales.data[v0].x); float scaleY = ((v0ChildCount + v1ChildCount) > 0.0f) ? ((splineGraph.payload.scales.data[v0].y * v0ChildCount + splineGraph.payload.scales.data[v1].y * v1ChildCount) / (v0ChildCount + v1ChildCount)) : (splineGraph.payload.scales.data[v0].y); splineGraph.payload.scales.data[v0] = new float2(scaleX, scaleY); float leashX = ((v0ParentCount + v1ParentCount) > 0.0f) ? ((splineGraph.payload.leashes.data[v0].x * v0ParentCount + splineGraph.payload.leashes.data[v1].x * v1ParentCount) / (v0ParentCount + v1ParentCount)) : (splineGraph.payload.leashes.data[v0].x); float leashY = ((v0ChildCount + v1ChildCount) > 0.0f) ? ((splineGraph.payload.leashes.data[v0].y * v0ChildCount + splineGraph.payload.leashes.data[v1].y * v1ChildCount) / (v0ChildCount + v1ChildCount)) : (splineGraph.payload.leashes.data[v0].y); splineGraph.payload.leashes.data[v0] = new float2(leashX, leashY); } splineGraph.VertexMerge(v0, v1, Allocator.Persistent); // Need to set the dirty flag here because the call to Verify() above cleared any dirty flags that were possibly set by UndoRecord() // and we have just changed our runtime data representation via welding. isDirty = true; } } }