Last active
August 16, 2024 22:57
-
-
Save mathiassoeholm/15f3eeda606e9be543165360615c8bef to your computer and use it in GitHub Desktop.
Similar to Unity's LineRenderer, but renders a cylindrical mesh.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Author: Mathias Soeholm | |
// Date: 05/10/2016 | |
// No license, do whatever you want with this script | |
using UnityEngine; | |
using UnityEngine.Serialization; | |
[ExecuteInEditMode] | |
public class TubeRenderer : MonoBehaviour | |
{ | |
[SerializeField] Vector3[] _positions; | |
[SerializeField] int _sides; | |
[SerializeField] float _radiusOne; | |
[SerializeField] float _radiusTwo; | |
[SerializeField] bool _useWorldSpace = true; | |
[SerializeField] bool _useTwoRadii = false; | |
private Vector3[] _vertices; | |
private Mesh _mesh; | |
private MeshFilter _meshFilter; | |
private MeshRenderer _meshRenderer; | |
public Material material | |
{ | |
get { return _meshRenderer.material; } | |
set { _meshRenderer.material = value; } | |
} | |
void Awake() | |
{ | |
_meshFilter = GetComponent<MeshFilter>(); | |
if (_meshFilter == null) | |
{ | |
_meshFilter = gameObject.AddComponent<MeshFilter>(); | |
} | |
_meshRenderer = GetComponent<MeshRenderer>(); | |
if (_meshRenderer == null) | |
{ | |
_meshRenderer = gameObject.AddComponent<MeshRenderer>(); | |
} | |
_mesh = new Mesh(); | |
_meshFilter.mesh = _mesh; | |
} | |
private void OnEnable() | |
{ | |
_meshRenderer.enabled = true; | |
} | |
private void OnDisable() | |
{ | |
_meshRenderer.enabled = false; | |
} | |
void Update () | |
{ | |
GenerateMesh(); | |
} | |
private void OnValidate() | |
{ | |
_sides = Mathf.Max(3, _sides); | |
} | |
public void SetPositions(Vector3[] positions) | |
{ | |
_positions = positions; | |
GenerateMesh(); | |
} | |
private void GenerateMesh() | |
{ | |
if (_mesh == null || _positions == null || _positions.Length <= 1) | |
{ | |
_mesh = new Mesh(); | |
return; | |
} | |
var verticesLength = _sides*_positions.Length; | |
if (_vertices == null || _vertices.Length != verticesLength) | |
{ | |
_vertices = new Vector3[verticesLength]; | |
var indices = GenerateIndices(); | |
var uvs = GenerateUVs(); | |
if (verticesLength > _mesh.vertexCount) | |
{ | |
_mesh.vertices = _vertices; | |
_mesh.triangles = indices; | |
_mesh.uv = uvs; | |
} | |
else | |
{ | |
_mesh.triangles = indices; | |
_mesh.vertices = _vertices; | |
_mesh.uv = uvs; | |
} | |
} | |
var currentVertIndex = 0; | |
for (int i = 0; i < _positions.Length; i++) | |
{ | |
var circle = CalculateCircle(i); | |
foreach (var vertex in circle) | |
{ | |
_vertices[currentVertIndex++] = _useWorldSpace ? transform.InverseTransformPoint(vertex) : vertex; | |
} | |
} | |
_mesh.vertices = _vertices; | |
_mesh.RecalculateNormals(); | |
_mesh.RecalculateBounds(); | |
_meshFilter.mesh = _mesh; | |
} | |
private Vector2[] GenerateUVs() | |
{ | |
var uvs = new Vector2[_positions.Length*_sides]; | |
for (int segment = 0; segment < _positions.Length; segment++) | |
{ | |
for (int side = 0; side < _sides; side++) | |
{ | |
var vertIndex = (segment * _sides + side); | |
var u = side/(_sides-1f); | |
var v = segment/(_positions.Length-1f); | |
uvs[vertIndex] = new Vector2(u, v); | |
} | |
} | |
return uvs; | |
} | |
private int[] GenerateIndices() | |
{ | |
// Two triangles and 3 vertices | |
var indices = new int[_positions.Length*_sides*2*3]; | |
var currentIndicesIndex = 0; | |
for (int segment = 1; segment < _positions.Length; segment++) | |
{ | |
for (int side = 0; side < _sides; side++) | |
{ | |
var vertIndex = (segment*_sides + side); | |
var prevVertIndex = vertIndex - _sides; | |
// Triangle one | |
indices[currentIndicesIndex++] = prevVertIndex; | |
indices[currentIndicesIndex++] = (side == _sides - 1) ? (vertIndex - (_sides - 1)) : (vertIndex + 1); | |
indices[currentIndicesIndex++] = vertIndex; | |
// Triangle two | |
indices[currentIndicesIndex++] = (side == _sides - 1) ? (prevVertIndex - (_sides - 1)) : (prevVertIndex + 1); | |
indices[currentIndicesIndex++] = (side == _sides - 1) ? (vertIndex - (_sides - 1)) : (vertIndex + 1); | |
indices[currentIndicesIndex++] = prevVertIndex; | |
} | |
} | |
return indices; | |
} | |
private Vector3[] CalculateCircle(int index) | |
{ | |
var dirCount = 0; | |
var forward = Vector3.zero; | |
// If not first index | |
if (index > 0) | |
{ | |
forward += (_positions[index] - _positions[index - 1]).normalized; | |
dirCount++; | |
} | |
// If not last index | |
if (index < _positions.Length-1) | |
{ | |
forward += (_positions[index + 1] - _positions[index]).normalized; | |
dirCount++; | |
} | |
// Forward is the average of the connecting edges directions | |
forward = (forward/dirCount).normalized; | |
var side = Vector3.Cross(forward, forward+new Vector3(.123564f, .34675f, .756892f)).normalized; | |
var up = Vector3.Cross(forward, side).normalized; | |
var circle = new Vector3[_sides]; | |
var angle = 0f; | |
var angleStep = (2*Mathf.PI)/_sides; | |
var t = index / (_positions.Length-1f); | |
var radius = _useTwoRadii ? Mathf.Lerp(_radiusOne, _radiusTwo, t) : _radiusOne; | |
for (int i = 0; i < _sides; i++) | |
{ | |
var x = Mathf.Cos(angle); | |
var y = Mathf.Sin(angle); | |
circle[i] = _positions[index] + side*x* radius + up*y* radius; | |
angle += angleStep; | |
} | |
return circle; | |
} | |
} |
nicogarciasdev
commented
May 25, 2023
via email
Hey thank you very much! I'll check it out and upload it to patreon as an
update! thanks a lot!
El mié, 24 may 2023 a las 21:24, Didier Surka ***@***.***>)
escribió:
… ***@***.**** commented on this gist.
------------------------------
Hi, thanks you for this script. I made a small modification that may be
useful to other users, feel free to integrate it. It's a width along curve
property, controllable with an animation curve.
//this may be an enum with Single, StartEnd and Curve options.
[SerializeField] bool _useCurve = false;
[SerializeField] AnimationCurve _radiusOverLength;
and then when setting up the radius:
float radius = _radiusOne;
if(_useTwoRadii) {
radius = Mathf.Lerp(_radiusOne, _radiusTwo, t);
}
else if(_useCurve){
radius = _radiusOverLength.Evaluate(t);
}
—
Reply to this email directly, view it on GitHub
<https://gist.github.com/mathiassoeholm/15f3eeda606e9be543165360615c8bef#gistcomment-4578869>
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AYG76OBR5U6LVCPZXN22EVDXH2YGTBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFQKSXMYLMOVS2I5DSOVS2I3TBNVS3W5DIOJSWCZC7OBQXE5DJMNUXAYLOORPWCY3UNF3GS5DZVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVA2DANJUG42TKMNHORZGSZ3HMVZKMY3SMVQXIZI>
.
You are receiving this email because you commented on the thread.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>
.
--
*Nicolás Soto*
*Game Developer*
***@***.*** | +56 9 5873 8467
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment