private CurveSample getCurvePointAtDistance(float d) { if (d < 0 || d > Length) { throw new ArgumentException("Distance must be positive and less than curve length. Length = " + Length + ", given distance was " + d); } CurveSample previous = samples[0]; CurveSample next = null; foreach (CurveSample cp in samples) { if (cp.distance >= d) { next = cp; break; } previous = cp; } if (next == null) { throw new Exception("Can't find curve samples."); } float t = next == previous ? 0 : (d - previous.distance) / (next.distance - previous.distance); CurveSample res = new CurveSample(); res.distance = d; res.location = Vector3.Lerp(previous.location, next.location, t); res.tangent = Vector3.Lerp(previous.tangent, next.tangent, t).normalized; return(res); }
public CurveSample GetProjectionSample(Vector3 pointToProject) { CurveSample closest = default(CurveSample); float minSqrDistance = float.MaxValue; foreach (var curve in curves) { var projection = curve.GetProjectionSample(pointToProject); if (curve == curves[0]) { closest = projection; minSqrDistance = (projection.location - pointToProject).sqrMagnitude; continue; } var sqrDist = (projection.location - pointToProject).sqrMagnitude; if (sqrDist < minSqrDistance) { minSqrDistance = sqrDist; closest = projection; } } return(closest); }
private void returnToTrack() { Debug.Log("Returning car to track: " + CarInfo?.name); var track = FindObjectOfType <SplineMesh.Spline>(); SplineMesh.CurveSample closest = null; float closestDistance = float.MaxValue; for (float i = 0; i < track.Length; i += 0.05f) { var curveSample = track.GetSampleAtDistance(i); var d = (gameObject.transform.position - 0.048f * Vector3.up - curveSample.location); d.y *= 10; var distance = d.magnitude; if (distance < closestDistance) { closestDistance = distance; closest = curveSample; } } rigidBody.position = closest.location + 0.1f * Vector3.up; rigidBody.rotation = closest.Rotation; rigidBody.velocity = Vector3.zero; EventBus.Publish(new CarReturnedToTrack(CarInfo)); }
private void ComputePoints() { samples.Clear(); Length = 0; Vector3 previousPosition = GetLocation(0); for (float t = 0; t < 1; t += T_STEP) { CurveSample sample = new CurveSample(); sample.location = GetLocation(t); sample.tangent = GetTangent(t); Length += Vector3.Distance(previousPosition, sample.location); sample.distance = Length; previousPosition = sample.location; samples.Add(sample); } CurveSample lastSample = new CurveSample(); lastSample.location = GetLocation(1); lastSample.tangent = GetTangent(1); Length += Vector3.Distance(previousPosition, lastSample.location); lastSample.distance = Length; samples.Add(lastSample); if (Changed != null) { Changed.Invoke(); } }
public CurveSample GetProjectionSample(Vector3 pointToProject) { CurveSample closest = default(CurveSample); float minSqrDistance = float.MaxValue; for (int i = 0; i < curves.Count; i++) { var curve = curves[i]; var projection = curve.GetProjectionSample(pointToProject); if (curve == curves[0]) { closest = projection; closest.index = i; minSqrDistance = (projection.location - pointToProject).sqrMagnitude; continue; } var sqrDist = (projection.location - pointToProject).sqrMagnitude; if (sqrDist < minSqrDistance) { minSqrDistance = sqrDist; closest = projection; closest.index = i; } } return(closest); }
/// <summary> /// Returns an interpolated sample of the curve, containing all curve data at this distance. /// </summary> /// <param name="d"></param> /// <returns></returns> public CurveSample GetSampleAtDistance(float d) { if (d < 0 || d > Length) { throw new ArgumentException("Distance must be positive and less than curve length. Length = " + Length + ", given distance was " + d); } CurveSample previous = samples[0]; if (previous == null) { Debug.LogError(samples.Count.ToString()); } CurveSample next = null; foreach (CurveSample cp in samples) { if (cp.distanceInCurve >= d) { next = cp; break; } previous = cp; } if (next == null) { throw new Exception("Can't find curve samples."); } float t = next == previous ? 0 : (d - previous.distanceInCurve) / (next.distanceInCurve - previous.distanceInCurve); return(CurveSample.Lerp(previous, next, t)); }
// Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.F)) { CurveSample sample = spline.GetSample(0.5f); spline.InsertNode(1, (new SplineNode(sample.location + Vector3.left * 3, (spline.nodes[0].Direction + spline.nodes[1].Direction) / 2))); GetComponent <SplineMeshTiling>().CreateMeshes(); Debug.Log(sample.distanceInCurve + "|" + sample.timeInCurve); } if (Input.GetKeyDown(KeyCode.X)) { spline.RemoveNode(spline.nodes[spline.nodes.Count - 1]); GetComponent <SplineMeshTiling>().CreateMeshes(); } if (Input.GetKeyDown(KeyCode.Q)) { CurveSample sample = spline.GetSample(spline.nodes.Count - 2 + 0.5f); spline.nodes[spline.nodes.Count - 1].Position = sample.location; GetComponent <SplineMeshTiling>().CreateMeshes(); } }
/// <summary> /// Returns an interpolated sample of the curve, containing all curve data at this distance. /// </summary> /// <param name="d"></param> /// <returns></returns> public CurveSample GetSampleAtDistance(float d) { if (d < 0 || d > Length) { throw new ArgumentException("Distance must be positive and less than curve length. Length = " + Length + ", given distance was " + d); } CurveSample previous = samples[0]; CurveSample next = default(CurveSample); bool found = false; foreach (CurveSample cp in samples) { if (cp.distanceInCurve >= d) { next = cp; found = true; break; } previous = cp; } if (!found) { throw new Exception("Can't find curve samples."); } float t = next == previous ? 0 : (d - previous.distanceInCurve) / (next.distanceInCurve - previous.distanceInCurve); return(CurveSample.Lerp(previous, next, t)); }
/// <summary> /// Returns an interpolated sample of the curve, containing all curve data at this time. /// </summary> /// <param name="time"></param> /// <returns></returns> public CurveSample GetSample(float time) { AssertTimeInBounds(time); CurveSample previous = samples[0]; CurveSample next = default(CurveSample); bool found = false; foreach (CurveSample cp in samples) { if (cp.timeInCurve >= time) { next = cp; found = true; break; } previous = cp; } if (!found) { throw new Exception("Can't find curve samples."); } float t = next == previous ? 0 : (time - previous.timeInCurve) / (next.timeInCurve - previous.timeInCurve); return(CurveSample.Lerp(previous, next, t)); }
private void PlaceFollower() { if (generated != null) { CurveSample sample = spline.GetSample(Rate); generated.transform.localPosition = sample.location; } }
private void PlaceFollower() { if (go != null) { CurveSample sample = spline.GetSample(rate); go.transform.localPosition = sample.location; go.transform.localRotation = sample.Rotation; } }
public void Sow() { UOUtility.DestroyChildren(generated); UnityEngine.Random.InitState(randomSeed); if (spacing + spacingRange <= 0 || prefab == null) { return; } float distance = 0; while (distance <= spline.Length) { CurveSample sample = spline.GetSampleAtDistance(distance); GameObject go; if (Application.isPlaying) { go = Instantiate(prefab, generated.transform); } else { go = (GameObject)UnityEditor.PrefabUtility.InstantiatePrefab((UnityEngine.Object)prefab); go.transform.parent = generated.transform; } go.transform.localRotation = Quaternion.identity; go.transform.localPosition = Vector3.zero; go.transform.localScale = Vector3.one; // move along spline, according to spacing + random go.transform.localPosition = sample.location; // apply scale + random float rangedScale = scale + UnityEngine.Random.Range(0, scaleRange); go.transform.localScale = new Vector3(rangedScale, rangedScale, rangedScale); // rotate with random yaw if (isRandomYaw) { go.transform.Rotate(0, 0, UnityEngine.Random.Range(-180, 180)); } else { go.transform.rotation = sample.Rotation; } // move orthogonaly to the spline, according to offset + random Vector3 binormal = sample.tangent; binormal = Quaternion.LookRotation(Vector3.right, Vector3.up) * binormal; var localOffset = offset + UnityEngine.Random.Range(0, offsetRange * Math.Sign(offset)); localOffset *= sample.scale.x; binormal *= localOffset; go.transform.position += binormal; distance += spacing + UnityEngine.Random.Range(0, spacingRange); } }
/// <summary> /// Linearly interpolates between two curve samples. /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="t"></param> /// <returns></returns> public static CurveSample Lerp(CurveSample a, CurveSample b, float t) { return(new CurveSample( Vector3.Lerp(a.location, b.location, t), Vector3.Lerp(a.tangent, b.tangent, t).normalized, Vector3.Lerp(a.up, b.up, t), Vector2.Lerp(a.scale, b.scale, t), Mathf.Lerp(a.roll, b.roll, t), Mathf.Lerp(a.distanceInCurve, b.distanceInCurve, t), Mathf.Lerp(a.timeInCurve, b.timeInCurve, t))); }
public CurveSample GetProjectionSample(Vector3 pointToProject) { float minSqrDistance = float.PositiveInfinity; int closestIndex = -1; int i = 0; foreach (var sample in samples) { float sqrDistance = (sample.location - pointToProject).sqrMagnitude; if (sqrDistance < minSqrDistance) { minSqrDistance = sqrDistance; closestIndex = i; } i++; } CurveSample previous, next; if (closestIndex == 0) { previous = samples[closestIndex]; next = samples[closestIndex + 1]; } else if (closestIndex == samples.Count - 1) { previous = samples[closestIndex - 1]; next = samples[closestIndex]; } else { var toPreviousSample = (pointToProject - samples[closestIndex - 1].location).sqrMagnitude; var toNextSample = (pointToProject - samples[closestIndex + 1].location).sqrMagnitude; if (toPreviousSample < toNextSample) { previous = samples[closestIndex - 1]; next = samples[closestIndex]; } else { previous = samples[closestIndex]; next = samples[closestIndex + 1]; } } var onCurve = Vector3.Project(pointToProject - previous.location, next.location - previous.location) + previous.location; var rate = (onCurve - previous.location).sqrMagnitude / (next.location - previous.location).sqrMagnitude; rate = Mathf.Clamp(rate, 0, 1); var result = CurveSample.Lerp(previous, next, rate); return(result); }
private void PlaceFollower() { if (generated != null) { CurveSample sample = spline.GetSample(rate); generated.transform.localPosition = sample.location; generated.transform.localRotation = sample.Rotation; if (rate + ViewOffset < spline.nodes.Count - 1) { CurveSample viewDirSample = spline.GetSample(rate + ViewOffset); generated.transform.localRotation = viewDirSample.Rotation; } } }
public override bool Equals(object obj) { if (obj == null || GetType() != obj.GetType()) { return(false); } CurveSample other = (CurveSample)obj; return(location == other.location && tangent == other.tangent && up == other.up && scale == other.scale && roll == other.roll && distanceInCurve == other.distanceInCurve && timeInCurve == other.timeInCurve); }
public static float GetDistanceFromSample(SplineMesh.Spline spline, SplineMesh.CurveSample sample) { float cumulatedDistance = 0; foreach (SplineMesh.CubicBezierCurve curve in spline.curves) { if (sample.curve != curve) { cumulatedDistance += curve.Length; } else { cumulatedDistance += sample.distanceInCurve; return(cumulatedDistance); } } return(cumulatedDistance); }
/// <summary> /// Get the closest Point on Spline /// </summary> /// <param name="worldPosition"></param> /// <param name="stepsPerUnityUnit"></param> /// <returns></returns> public Vector3 GetNearestPoint(Vector3 worldPosition, int stepsPerUnityUnit = 10) { //How Many Steps used Per UnityUnit(Meters) float stepSize = 1f / stepsPerUnityUnit; int stepLenght = Mathf.FloorToInt(Length * stepsPerUnityUnit); CurveSample nearestsample = GetSampleAtDistance(0); Vector3 rootpos = transform.position; float smallestDelta = float.MaxValue; for (int i = 0; i < stepLenght; i++) { float delta = (worldPosition - (GetSampleAtDistance(i * stepSize).location + rootpos)).sqrMagnitude; if (delta < smallestDelta) { smallestDelta = delta; nearestsample = GetSampleAtDistance(i * stepSize); } } return(nearestsample.location + rootpos); }
void OnSceneGUI() { Event e = Event.current; if (e.type == EventType.MouseDown) { Undo.RegisterCompleteObjectUndo(se, "change extruded shape"); // if control key pressed, we will have to create a new vertex if position is changed if (e.alt) { mustCreateNewNode = true; } } if (e.type == EventType.MouseUp) { mustCreateNewNode = false; } var spline = se.GetComponent <Spline>(); CurveSample startSample = spline.GetSample(0); Quaternion q = startSample.Rotation; foreach (ExtrusionSegment.Vertex v in se.shapeVertices) { // we create point and normal relative to the spline start where the shape is drawn Vector3 point = se.transform.TransformPoint(q * v.point + startSample.location); Vector3 normal = se.transform.TransformPoint(q * (v.point + v.normal) + startSample.location); if (v == selection) { // draw the handles for selected vertex position and normal float size = HandleUtility.GetHandleSize(point) * 0.3f; float snap = 0.1f; // create a handle for the vertex position Vector3 movedPoint = Handles.Slider2D(0, point, startSample.tangent, Vector3.right, Vector3.up, size, Handles.CircleHandleCap, new Vector2(snap, snap)); if (movedPoint != point) { // position has been moved Vector2 newVertexPoint = Quaternion.Inverse(q) * (se.transform.InverseTransformPoint(movedPoint) - startSample.location); if (mustCreateNewNode) { // We must create a new node mustCreateNewNode = false; ExtrusionSegment.Vertex newVertex = new ExtrusionSegment.Vertex(newVertexPoint, v.normal, v.uCoord); int i = se.shapeVertices.IndexOf(v); if (i == se.shapeVertices.Count - 1) { se.shapeVertices.Add(newVertex); } else { se.shapeVertices.Insert(i + 1, newVertex); } selection = newVertex; } else { v.point = newVertexPoint; // normal must be updated if point has been moved normal = se.transform.TransformPoint(q * (v.point + v.normal) + startSample.location); } se.SetToUpdate(); } else { // vertex position handle hasn't been moved // create a handle for normal Vector3 movedNormal = Handles.Slider2D(normal, startSample.tangent, Vector3.right, Vector3.up, size, Handles.CircleHandleCap, snap); if (movedNormal != normal) { // normal has been moved v.normal = (Vector2)(Quaternion.Inverse(q) * (se.transform.InverseTransformPoint(movedNormal) - startSample.location)) - v.point; se.SetToUpdate(); } } Handles.BeginGUI(); DrawQuad(HandleUtility.WorldToGUIPoint(point), CURVE_COLOR); DrawQuad(HandleUtility.WorldToGUIPoint(normal), Color.red); Handles.EndGUI(); } else { // we draw a button to allow selection of the vertex Handles.BeginGUI(); Vector2 p = HandleUtility.WorldToGUIPoint(point); if (GUI.Button(new Rect(p - new Vector2(QUAD_SIZE / 2, QUAD_SIZE / 2), new Vector2(QUAD_SIZE, QUAD_SIZE)), GUIContent.none)) { selection = v; } Handles.EndGUI(); } // draw an arrow from the vertex location to the normal Handles.color = Color.red; Handles.DrawLine(point, normal); // draw a line between that vertex and the next one int index = se.shapeVertices.IndexOf(v); int nextIndex = index == se.shapeVertices.Count - 1 ? 0 : index + 1; ExtrusionSegment.Vertex next = se.shapeVertices[nextIndex]; Handles.color = CURVE_COLOR; Vector3 vAtSplineEnd = se.transform.TransformPoint(q * next.point + startSample.location); Handles.DrawLine(point, vAtSplineEnd); } }
/// <summary> /// Split the spline by adding a node at the specific time /// the tangents of previous and following node are modified /// in order to not alter the curve shape. /// /// previous and next node tangent type become SplineNode.TangentType.Free /// </summary> /// <param name="time"></param> public void SplitAtTime(float time) { if (time <= 0f || time >= (float)nodes.Count - 1) { throw new Exception(string.Format("Can't split at time {0}. Use a value between 0 and {1}", time, (float)nodes.Count - 1)); } CurveSample time_sample = GetSample(time); Vector3 new_point_position = time_sample.location; // percent position between two nodes float percentage = time % 1; int index = Mathf.FloorToInt(time); /** * P0 = starting point * P3 = ending point * * P1 = P0 control point * P2 = mirror of P3 control point (we need the inverse, not the real cp) */ SplineNode starting_node = nodes[index]; Vector3 p0 = starting_node.Position; Vector3 p1 = starting_node.DirectionOut; SplineNode ending_node = nodes[index + 1]; Vector3 p3 = ending_node.Position; Vector3 p2 = ending_node.DirectionIn; // we need to find a positoin at percentage% between P0 and P1 Vector3 m0 = p0 + (p1 - p0) * percentage; Vector3 m1 = p1 + (p2 - p1) * percentage; Vector3 m2 = p2 + (p3 - p2) * percentage; Vector3 q0 = m0 + (m1 - m0) * percentage; Vector3 q1 = m1 + (m2 - m1) * percentage; Vector3 new_point_direction_in = q0; Vector3 new_point_direction_out = q1; // create the new node with previous calculated params SplineNode n = new SplineNode(new_point_position, new_point_direction_out, new_point_direction_in); //time_sample.tangent); n.DirectionType = SplineNode.TangentType.Free; // we also need to alter the directionOut of the previous point // its new direction is m0 // in order to alter it, the tangent type must be free if (nodes[index].DirectionType != SplineNode.TangentType.Free) { nodes[index].DirectionType = SplineNode.TangentType.Free; } nodes[index].DirectionOut = m0; // we also need to alter the directionIn of the final point // its new direction is m2 // in order to alter it, the tangent type must be free if (nodes[index + 1].DirectionType != SplineNode.TangentType.Free) { nodes[index + 1].DirectionType = SplineNode.TangentType.Free; } nodes[index + 1].DirectionIn = m2; InsertNode(index + 1, n); }
public void CreateMeshes() { if (null == spline || null == meshInfos || 0 == meshInfos.Length) { return; } UpdateSourceMeshes(); UpdateDecisionParts(); string generatedName = "generated by " + GetType().Name; var generatedTranform = transform.Find(generatedName); generated = generatedTranform != null ? generatedTranform.gameObject : UOUtility.Create(generatedName, gameObject); generated.isStatic = false == updateInPlayMode; generated.layer = gameObject.layer; var generatedChildren = generated.transform.Cast <Transform>().ToList(); foreach (var child in generatedChildren) { if (null == child) { continue; } var meshCollider = child.GetComponent <MeshCollider>(); if (meshCollider) { meshCollider.enabled = false; } } var meshChunkDict = new Dictionary <Material, List <MeshChunk> >(); var sampleCache = new Dictionary <float, CurveSample>(); float offset = 0; for (int i = 0; i < decisionParts.Count; ++i) { int index = decisionParts[i]; if (false == meshChunkDict.ContainsKey(meshInfos[index].material)) { meshChunkDict.Add(meshInfos[index].material, new List <MeshChunk>()); } var meshChunkList = meshChunkDict[meshInfos[index].material]; int vertexCount = meshInfos[index].mesh.vertices.Length; bool isReachedMaxVertices = 0 < meshChunkList.Count && PerChunkMaxVertices < (meshChunkList.Last().bentVertices.Count + vertexCount); bool isReachedMaxLength = 0 < meshChunkList.Count && PerChunkMaxLength < meshChunkList.Last().length; if (0 == meshChunkList.Count || isReachedMaxVertices || isReachedMaxLength) { meshChunkList.Add(new MeshChunk() { bentVertices = new List <MeshVertex>(vertexCount), triangles = new List <int>(vertexCount / 3), uv = new List <Vector2> [8], length = 0 }); } var meshChunk = meshChunkList.Last(); ref SourceMesh sourceMesh = ref sourceMeshes[index]; meshChunk.triangles.AddRange(sourceMesh.Triangles.Select(idx => idx + meshChunk.bentVertices.Count)); List <Vector2> UV = new List <Vector2>(); for (int channel = 0; channel < 8; ++channel) { UV.Clear(); sourceMesh.Mesh.GetUVs(channel, UV); if (0 < UV.Count) { if (null == meshChunk.uv[channel]) { meshChunk.uv[channel] = new List <Vector2>(); } int fillCount = Mathf.Max(0, (meshChunk.bentVertices.Count - UV.Count) - meshChunk.uv[channel].Count); if (0 < fillCount) { meshChunk.uv[channel].AddRange(Enumerable.Repeat(Vector2.zero, fillCount)); } meshChunk.uv[channel].AddRange(UV); } } foreach (var vertex in sourceMesh.Vertices) { var vert = new MeshVertex(vertex.position, vertex.normal, vertex.uv); vert.position.x *= partsScale; float distance = vert.position.x - sourceMesh.MinX * partsScale + offset; distance = Mathf.Clamp(distance, 0, spline.Length); CurveSample sample; if (false == sampleCache.TryGetValue(distance, out sample)) { sample = spline.GetSampleAtDistance(distance); if (heightSync) { var sampleLocationWS = spline.transform.TransformPoint(sample.location); RaycastHit hitInfo; if (Physics.Raycast(sampleLocationWS + Vector3.up * heightSyncTraceRange, -Vector3.up, out hitInfo, heightSyncTraceRange * 2, heightSyncLayerMask)) { var newSampleLocation = spline.transform.InverseTransformPoint(hitInfo.point); var newSampleUp = heightNormalSync ? spline.transform.InverseTransformDirection(hitInfo.normal) : sample.up; sample = new CurveSample(newSampleLocation, sample.tangent, newSampleUp, sample.scale, sample.roll, sample.distanceInCurve, sample.timeInCurve, sample.curve); } } sampleCache.Add(distance, sample); } MeshVertex bentVertex = sample.GetBent(vert); meshChunk.bentVertices.Add(bentVertex); } offset += sourceMeshes[index].Length * partsScale; meshChunk.length += sourceMeshes[index].Length * partsScale; }
public void Sow() { UOUtility.DestroyChildren(generated); UnityEngine.Random.InitState(randomSeed); if (spacing + spacingRange <= 0 || prefab == null) { return; } float distance = 0; while (distance <= spline.Length) { CurveSample sample = spline.GetSampleAtDistance(distance); if (heightSync) { var sampleLocationWS = spline.transform.TransformPoint(sample.location); RaycastHit hitInfo; if (Physics.Raycast(sampleLocationWS + Vector3.up * heightSyncTraceRange, -Vector3.up, out hitInfo, heightSyncTraceRange * 2, heightSyncLayerMask)) { var newSampleLocation = spline.transform.InverseTransformPoint(hitInfo.point); var newSampleUp = heightNormalSync ? spline.transform.InverseTransformDirection(hitInfo.normal) : sample.up; sample = new CurveSample(newSampleLocation, sample.tangent, newSampleUp, sample.scale, sample.roll, sample.distanceInCurve, sample.timeInCurve, sample.curve); } } GameObject go; go = Instantiate(prefab, generated.transform); go.transform.localRotation = Quaternion.identity; go.transform.localPosition = Vector3.zero; go.transform.localScale = Vector3.one; go.isStatic = false == updateInPlayMode; go.layer = gameObject.layer; foreach (var child in go.transform.Cast <Transform>()) { child.gameObject.layer = go.layer; } // move along spline, according to spacing + random go.transform.localPosition = sample.location; // apply scale + random float rangedScale = scale + UnityEngine.Random.Range(0, scaleRange); go.transform.localScale = new Vector3(rangedScale, rangedScale, rangedScale); // rotate with random yaw if (isRandomYaw) { go.transform.Rotate(0, 0, UnityEngine.Random.Range(-180, 180)); } else { go.transform.rotation = sample.Rotation; } // move orthogonaly to the spline, according to offset + random var binormal = (Quaternion.LookRotation(sample.tangent, sample.up) * Vector3.right).normalized; var localWidthOffset = widthOffset + UnityEngine.Random.Range(0, widthOffsetRange * Math.Sign(widthOffset)); var localHeightOffset = heightOffset + UnityEngine.Random.Range(0, heightOffsetRange * Math.Sign(heightOffset)); localWidthOffset *= sample.scale.x; binormal *= localWidthOffset; go.transform.position += binormal + sample.up * localHeightOffset; distance += spacing + UnityEngine.Random.Range(0, spacingRange); } }
private void CreateMeshes() { if (null == spline) { return; } string generatedName = "generated by " + GetType().Name; var generatedTranform = transform.Find(generatedName); generated = generatedTranform != null ? generatedTranform.gameObject : UOUtility.Create(generatedName, gameObject); generated.isStatic = false == updateInPlayMode; generated.layer = gameObject.layer; var generatedChildren = generated.transform.Cast <Transform>().ToList(); foreach (var child in generatedChildren) { if (null == child) { continue; } var meshCollider = child.GetComponent <MeshCollider>(); if (meshCollider) { meshCollider.enabled = false; } } List <MeshVertex> sourceVertices = new List <MeshVertex>(2 + Mathf.Max(0, slice - 1)); float x_start = offset - width / 2; float x_end = offset + width / 2; sourceVertices.Add(new MeshVertex(new Vector3(0, 0, x_start), Vector3.up, new Vector2(+textureOffsetScale.x, 0))); for (int step = 1; step < slice; ++step) { float t = (float)step / (float)slice; float x = Mathf.Lerp(x_start, x_end, t); sourceVertices.Add(new MeshVertex(new Vector3(0, 0, x), Vector3.up, new Vector2(t / textureOffsetScale.z + textureOffsetScale.x, 0))); } sourceVertices.Add(new MeshVertex(new Vector3(0, 0, x_end), Vector3.up, new Vector2(1 / textureOffsetScale.z + textureOffsetScale.x, 0))); if (Mathf.Approximately(sampleSpacing, 0) || sampleSpacing <= 0) { Debug.LogError("Not enough sampleSpcaing. (must be greater than zero)"); return; } int lineCount = 0; List <MeshVertex> bentVertices = new List <MeshVertex>(); for (float d = 0.0f; d <= spline.Length; d += sampleSpacing) { var sample = spline.GetSampleAtDistance(d); if (heightSync) { var sampleLocationWS = spline.transform.TransformPoint(sample.location); RaycastHit hitInfo; if (Physics.Raycast(sampleLocationWS + Vector3.up * heightSyncTraceRange, -Vector3.up, out hitInfo, heightSyncTraceRange * 2, heightSyncLayerMask)) { var newSampleLocation = spline.transform.InverseTransformPoint(hitInfo.point); sample = new CurveSample(newSampleLocation, sample.tangent, sample.up, sample.scale, sample.roll, sample.distanceInCurve, sample.timeInCurve, sample.curve); } } for (int i = 0; i < sourceVertices.Count; ++i) { var bentVertex = sample.GetBent(sourceVertices[i]); bentVertex.normal = sample.up; if (heightSync) { var bentWS = spline.transform.TransformPoint(bentVertex.position); RaycastHit hitInfo; if (Physics.Raycast(bentWS + Vector3.up * heightSyncTraceRange, -Vector3.up, out hitInfo, heightSyncTraceRange * 2, heightSyncLayerMask)) { bentVertex.position = spline.transform.InverseTransformPoint(hitInfo.point); if (heightNormalSync) { bentVertex.normal = spline.transform.InverseTransformDirection(hitInfo.normal); } } } bentVertex.uv.y = (d / width) / textureOffsetScale.w + textureOffsetScale.y; bentVertices.Add(bentVertex); } ++lineCount; } if (1 >= lineCount) { Debug.LogError("Not enough line length"); return; } List <Transform> newGeneratedTransform = new List <Transform>(); int baseIndex = 0; int lineVerticesCount = 2 + Mathf.Max(0, slice - 1); List <int> triangles = new List <int>((lineVerticesCount - 1) * (lineCount - 1) * 2); for (int i = 0; i < lineCount - 1; ++i) { int nextBaseIndex = baseIndex + lineVerticesCount; for (int v = 0; v < lineVerticesCount - 1; ++v) { int[] vertices = new int[] { baseIndex + v, baseIndex + v + 1, nextBaseIndex + v, nextBaseIndex + v + 1 }; triangles.Add(vertices[0]); triangles.Add(vertices[1]); triangles.Add(vertices[2]); triangles.Add(vertices[2]); triangles.Add(vertices[1]); triangles.Add(vertices[3]); } bool isOverVertices = nextBaseIndex + lineVerticesCount > PerSegmentMaxVertices; bool isEndLine = (i == lineCount - 2); bool needGenerateSegment = isOverVertices || isEndLine; if (needGenerateSegment) { string segmentName = $"{name}-{material.name}-{newGeneratedTransform.Count + 1}"; Transform segmentTransform = generated.transform.Find(segmentName); if (null == segmentTransform) { var go = UOUtility.Create(segmentName, generated, typeof(MeshFilter), typeof(MeshRenderer), typeof(MeshCollider)); segmentTransform = go.transform; } segmentTransform.gameObject.isStatic = generated.isStatic; segmentTransform.gameObject.layer = generated.layer; newGeneratedTransform.Add(segmentTransform); generatedChildren.Remove(segmentTransform); var meshFilter = segmentTransform.GetComponent <MeshFilter>(); var meshRenderer = segmentTransform.GetComponent <MeshRenderer>(); var meshCollider = segmentTransform.GetComponent <MeshCollider>(); Mesh mesh = meshFilter.sharedMesh; if (null == mesh || mesh.name != segmentName) { mesh = new Mesh(); } else if (mesh.vertexCount != bentVertices.Count) { mesh.Clear(); } mesh.name = segmentName; mesh.hideFlags = HideFlags.HideInHierarchy; mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt16; var segmentBentVertices = bentVertices.GetRange(0, nextBaseIndex + lineVerticesCount); mesh.SetVertices(segmentBentVertices.Select(b => b.position).ToList()); mesh.SetNormals(segmentBentVertices.Select(b => b.normal).ToList()); mesh.SetUVs(0, segmentBentVertices.Select(b => b.uv).ToList()); mesh.SetTriangles(triangles, 0, false); mesh.RecalculateBounds(); mesh.RecalculateTangents(); meshFilter.sharedMesh = mesh; meshRenderer.material = material; meshCollider.sharedMesh = mesh; meshCollider.enabled = generateCollider; triangles.Clear(); bentVertices.RemoveRange(0, nextBaseIndex); nextBaseIndex = 0; } baseIndex = nextBaseIndex; } foreach (var deprecatedTransform in generatedChildren) { if (deprecatedTransform != null) { UOUtility.Destroy(deprecatedTransform.gameObject); } } }