/// <summary> /// Sets the line width at the start and at the end. /// Note, varying line widths will have a segmented appearance vs. the smooth look one gets with the traditional linerenderer. /// </summary> public void SetWidth(float start, float end) { // Update internal data m_WidthStart = start; m_WidthEnd = end; // See if the data needs initializing if (Initialize()) { return; } // Otherwise, do fast setting var pointCounter = 0; var elementCounter = 0; // We go through the element list, much like initialization, but only update the width part of the variables m_MeshData.SetElementSize(elementCounter, m_WidthStart); elementCounter++; pointCounter++; float stepSize = 1.0f / Mathf.Max((m_Positions.Length - 1.0f), 1.0f); float stepPercent = stepSize; var lastWidth = m_WidthStart; while (pointCounter < m_Positions.Length) { var currentWidth = Mathf.Lerp(m_WidthStart, m_WidthEnd, stepPercent); m_MeshData.SetElementSize(elementCounter, lastWidth, currentWidth); elementCounter++; m_MeshData.SetElementSize(elementCounter, currentWidth); lastWidth = currentWidth; elementCounter++; pointCounter++; stepPercent += stepSize; } // Dirty all the MeshChain flags so everything gets refreshed m_MeshData.SetMeshDataDirty(VRLineRendererInternal.MeshChain.MeshRefreshFlag.Sizes); m_MeshNeedsRefreshing = true; }
/// <summary> /// Updates the built-in mesh data for each control point of the trail /// </summary> void LateUpdate() { // We do the actual internal mesh updating as late as possible so nothing ends up a frame behind var deltaTime = Time.deltaTime; // We give the editor a little help with handling delta time in edit mode if (Application.isPlaying == false) { deltaTime = Time.realtimeSinceStartup - m_EditorDeltaHelper; m_EditorDeltaHelper = Time.realtimeSinceStartup; } // Get the current position of the renderer var currentPoint = transform.position; var pointDistance = (currentPoint - m_LastRecordedPoint).sqrMagnitude; // Is it more than minVertexDistance from the last position? if (pointDistance > (m_MinVertexDistance * m_MinVertexDistance)) { // In the situation we have no points, we need to record the start point as well if (m_PointIndexStart == m_PointIndexEnd) { m_Points[m_PointIndexStart] = m_LastRecordedPoint; m_PointTimes[m_PointIndexStart] = m_Time; } // Make space for a new point var newEndIndex = (m_PointIndexEnd + 1) % m_MaxTrailPoints; // In the situation that we are rendering all available vertices // We can either keep using the current point, or take the last point, depending on the user's preference if (newEndIndex != m_PointIndexStart) { m_PointIndexEnd = newEndIndex; m_PointTimes[m_PointIndexEnd] = 0; m_UsedPoints++; } else { if (m_StealLastPointWhenEmpty) { m_MeshData.SetElementSize(m_PointIndexStart * 2, 0); m_MeshData.SetElementSize((m_PointIndexStart * 2) + 1, 0); m_PointIndexStart = (m_PointIndexStart + 1) % m_MaxTrailPoints; m_PointIndexEnd = newEndIndex; m_PointTimes[m_PointIndexEnd] = 0; m_LastPointTime = m_PointTimes[m_PointIndexStart]; } } m_Points[m_PointIndexEnd] = currentPoint; // Update the last recorded point m_LastRecordedPoint = currentPoint; } // Do time processing // The end point counts up to a maximum of 'time' m_PointTimes[m_PointIndexEnd] = Mathf.Min(m_PointTimes[m_PointIndexEnd] + deltaTime, m_Time); if (m_PointIndexStart != m_PointIndexEnd) { // Run down the counter on the start point m_PointTimes[m_PointIndexStart] -= deltaTime; // If we've hit 0, this point is done for if (m_PointTimes[m_PointIndexStart] <= 0.0f) { m_MeshData.SetElementSize(m_PointIndexStart * 2, 0); m_MeshData.SetElementSize((m_PointIndexStart * 2) + 1, 0); m_PointIndexStart = (m_PointIndexStart + 1) % m_MaxTrailPoints; m_LastPointTime = m_PointTimes[m_PointIndexStart]; m_UsedPoints--; } } if (m_PointIndexStart != m_PointIndexEnd) { m_MeshNeedsRefreshing = true; m_MeshRenderer.enabled = true; } else { m_MeshNeedsRefreshing = false; m_MeshRenderer.enabled = false; } if (m_MeshNeedsRefreshing == true) { m_MeshRenderer.enabled = true; // Update first and last points position-wise var nextIndex = (m_PointIndexStart + 1) % m_MaxTrailPoints; if (m_SmoothInterpolation) { var toNextPoint = 1.0f - (m_PointTimes[m_PointIndexStart] / m_LastPointTime); var lerpPoint = Vector3.Lerp(m_Points[m_PointIndexStart], m_Points[nextIndex], toNextPoint); m_MeshData.SetElementPosition((m_PointIndexStart * 2), ref lerpPoint); m_MeshData.SetElementPipe((m_PointIndexStart * 2) + 1, ref lerpPoint, ref m_Points[nextIndex]); } else { m_MeshData.SetElementPosition((m_PointIndexStart * 2), ref m_Points[m_PointIndexStart]); m_MeshData.SetElementPipe((m_PointIndexStart * 2) + 1, ref m_Points[m_PointIndexStart], ref m_Points[nextIndex]); } var prevIndex = m_PointIndexEnd - 1; if (prevIndex < 0) { prevIndex = m_MaxTrailPoints - 1; } m_MeshData.SetElementPipe((prevIndex * 2) + 1, ref m_Points[prevIndex], ref m_Points[m_PointIndexEnd]); m_MeshData.SetElementPosition((m_PointIndexEnd * 2), ref m_Points[m_PointIndexEnd]); // Go through all points and update size and color var pointUpdateCounter = m_PointIndexStart; var pointCount = 0; var blendStep = 1.0f / m_UsedPoints; var colorValue = 1.0f; while (pointUpdateCounter != m_PointIndexEnd) { var currentBlend = blendStep * pointCount; var nextBlend = blendStep * (pointCount + 1.0f); var currentWidth = Mathf.Lerp(m_EndWidth, m_StartWidth, currentBlend); var nextWidth = Mathf.Lerp(m_EndWidth, m_StartWidth, nextBlend); m_MeshData.SetElementSize(pointUpdateCounter * 2, currentWidth); m_MeshData.SetElementSize((pointUpdateCounter * 2) + 1, currentWidth, nextWidth); var currentColor = GetLerpedColor(colorValue); var nextColor = GetLerpedColor(colorValue - blendStep); m_MeshData.SetElementColor(pointUpdateCounter * 2, ref currentColor); m_MeshData.SetElementColor((pointUpdateCounter * 2) + 1, ref currentColor, ref nextColor); pointUpdateCounter = (pointUpdateCounter + 1) % m_MaxTrailPoints; pointCount++; colorValue -= blendStep; } m_MeshData.SetElementSize((m_PointIndexEnd * 2), m_StartWidth); m_MeshData.SetElementColor((m_PointIndexEnd * 2), ref m_Colors[0]); m_MeshData.SetMeshDataDirty(VRLineRendererInternal.MeshChain.MeshRefreshFlag.All); m_MeshData.RefreshMesh(); } }