Skip to content

Instantly share code, notes, and snippets.

@rngtm
Last active November 28, 2018 15:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rngtm/959baa08cf963324128fda47f3069952 to your computer and use it in GitHub Desktop.
Save rngtm/959baa08cf963324128fda47f3069952 to your computer and use it in GitHub Desktop.
ベジエ曲線に沿ったチューブ型メッシュを生成するスクリプト
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(GenerateBezierTube))]
public class GenerateBezierTubeInspector : Editor
{
public override void OnInspectorGUI()
{
var bezierTube = target as GenerateBezierTube;
EditorGUILayout.LabelField("VertexCount", bezierTube.VertexCount.ToString());
base.OnInspectorGUI();
}
}
#endif
/// <summary>
/// ベジエ曲線に沿ったチューブ型メッシュを生成するスクリプト
/// </summary>
[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
public class GenerateBezierTube : MonoBehaviour
{
[SerializeField] private bool generateMeshOnUpdate = true; // Update()で毎フレームメッシュ更新
[SerializeField] private int bezierDivCount = 48; // ベジエ曲線の分割数
[SerializeField] private int circleCount = 12; // 円の分割数
[SerializeField] private Transform p1; // ベジエ曲線の制御点
[SerializeField] private Transform p2; // ベジエ曲線の制御点
[SerializeField] private Transform p3; // ベジエ曲線の制御点
[SerializeField] private Transform p4; // ベジエ曲線の制御点
private Mesh mesh;
public int VertexCount => bezierDivCount * circleCount * 6;
void Start()
{
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
Create();
}
void Update()
{
if (generateMeshOnUpdate)
{
Create();
}
}
void Create()
{
if (circleCount < 3) { circleCount = 3; }
if (bezierDivCount < 3) { bezierDivCount = 3; }
// 円周上の頂点数(円周の最後の要素を参照したときに最初を参照させるようにするため、1つ余分に確保)
int circleVertexCount = circleCount + 1;
// 三角関数テーブル作成(計算量の軽量化)
float[] cosValues = new float[circleVertexCount];
float[] sinValues = new float[circleVertexCount];
for (int ci = 0; ci < circleVertexCount; ci++)
{
float radian = ci * 2f * Mathf.PI / circleCount;
cosValues[ci] = Mathf.Cos(radian);
sinValues[ci] = Mathf.Sin(radian);
}
// ベジエ曲線に沿うベクトル列の作成
Vector3[] bezierPositions = GetBezierPositions(new Vector3[] {
p1.position, p2.position, p3.position, p4.position
});
Vector3[] bezierForwards = new Vector3[bezierDivCount];
for (int bi = 0; bi < bezierDivCount - 1; bi++)
{
bezierForwards[bi] = (bezierPositions[bi + 1] - bezierPositions[bi]).normalized;
}
bezierForwards[bezierDivCount - 1] = bezierForwards[bezierDivCount - 2];
// 頂点座標・法線・UVの作成
Vector3[] vertices = new Vector3[bezierDivCount * circleVertexCount];
Vector3[] normals = new Vector3[vertices.Length];
Vector2[] uv = new Vector2[vertices.Length];
int vi = 0;
for (int bi = 0; bi < bezierDivCount; bi++)
{
Vector3 forward = bezierForwards[bi];
Vector3 binormal = new Vector3(forward.y, forward.z, forward.x);
Vector3 tangent = Vector3.Cross(forward, binormal);
for (int ci = 0; ci < circleVertexCount; ci++)
{
uv[vi] = new Vector2((float)bi / (bezierDivCount - 1), (float)ci / (circleVertexCount - 1));
normals[vi] = tangent * cosValues[ci] + binormal * sinValues[ci];
vertices[vi] = tangent * cosValues[ci] + binormal * sinValues[ci] + bezierPositions[bi];
vi++;
}
}
// 頂点インデックスの作成
int[] triangles = new int[(bezierDivCount - 1) * circleCount * 6];
int ti = 0;
int triangleOffset = 0;
for (int bi = 0; bi < bezierDivCount - 1; bi++)
{
for (int ci = 0; ci < circleCount; ci++)
{
triangles[ti++] = triangleOffset + circleVertexCount; // 2
triangles[ti++] = triangleOffset + 1; // 1
triangles[ti++] = triangleOffset + 0; // 0
triangles[ti++] = triangleOffset + circleVertexCount + 1; // 3
triangles[ti++] = triangleOffset + 1; // 1
triangles[ti++] = triangleOffset + circleVertexCount; // 2
triangleOffset++;
}
triangleOffset++;
}
// メッシュ更新
mesh.triangles = null;
mesh.vertices = vertices;
mesh.normals = normals;
mesh.uv = uv;
mesh.triangles = triangles;
}
/// <summary>
/// 3次のベジエ曲線を求める
/// </summary>
Vector3[] GetBezierPositions(Vector3[] controlPositions)
{
Vector3[] bezierPositions = new Vector3[bezierDivCount];
float delta = 1f / (bezierDivCount - 1);
float t = 0f;
for (int i = 0; i < bezierDivCount; i++)
{
bezierPositions[i] = Bezier3(controlPositions, t);
t += delta;
}
return bezierPositions;
}
/// <summary>
/// 3次のベジエ曲線上の点を求める
/// </summary>
Vector3 Bezier3(Vector3[] p, float t)
{
Vector3[] p2 = new Vector3[3];
for (int i = 0; i < 3; i++)
{
// p[i]とp[i+1] を t : 1 - t に分割
p2[i] = t * p[i] + (1f - t) * p[i + 1];
}
Vector3[] p3 = new Vector3[2];
for (int i = 0; i < 2; i++)
{
// p2[i]とp2[i+1] を t : 1 - t に分割
p3[i] = t * p2[i] + (1f - t) * p2[i + 1];
}
// p3[0]とp3[1] を t : 1 - t に分割
return t * p3[0] + (1f - t) * p3[1];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment