Загрузка данных


using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class ConveyorGenerator : MonoBehaviour
{
    // ============================  НАСТРОЙКИ  ============================
    [Header("Плита")]
    public float bedLength = 4.5f;
    public float bedWidth  = 1.5f;
    public float bedThick  = 0.10f;

    [Header("Лента")]
    public float beltThick = 0.012f;
    public float clearance = 0.001f;
    public float minRadius = 0.01f;

    [Header("Зубцы")]
    public float ribHeight     = 0.007f;
    public float ribThickRel   = 0.16f;
    public float ribPitchRel   = 0.24f;
    public float ribMarginRel  = 0.015f;
    public float ribAngleDeg   = 45f;
    public float ribRound      = 0.0025f;
    public int   direction     = 1;

    [Header("Сетка")]
    public int plateSegments = 200;
    public int ribSegmentsY  = 12;
    public int ribSegmentsX  = 3;

    [Header("Движение")]
    public float speed = 1.0f;    // шагов в секунду (1 = шаг за секунду)

    [Header("Материалы")]
    public Material beltMaterial;
    public Material ribMaterial;

    [Header("Плита под лентой")]
    public bool createBed = true;
    public Material bedMaterial;

    // ============================  ВНУТРЕННЕЕ  ============================
    float R, TH, L, pathLen, pitch;
    int ribCount;
    Transform ribsGo;
    Mesh ribsMesh;
    Mesh plateMesh;

    void Start()
    {
        BuildAll();
    }

    void Update()
    {
        float offset = Mathf.Repeat(Time.time * speed * pitch, pitch);
        RebuildRibs(offset);
    }

    // ============================  СБОРКА  ============================
    void BuildAll()
    {
        R = Mathf.Max(bedThick / 2f + clearance, minRadius);
        TH = Mathf.Max(beltThick, 0.45f * R);
        L = bedLength - 2f * R;
        if (L < 0.1f) L = 0.1f;
        pathLen = 2f * L + 2f * Mathf.PI * R;

        float wantPitch = ribPitchRel * bedWidth;
        ribCount = Mathf.Max(1, Mathf.RoundToInt(pathLen / wantPitch));
        pitch = pathLen / ribCount;

        // --- плита под лентой ---
        if (createBed)
        {
            var bed = GameObject.CreatePrimitive(PrimitiveType.Cube);
            bed.name = "Bed";
            bed.transform.SetParent(transform, false);
            bed.transform.localScale = new Vector3(bedLength, bedThick, bedWidth);
            bed.transform.localPosition = new Vector3(0f, 0f, 0f);
            if (bedMaterial != null)
                bed.GetComponent<MeshRenderer>().material = bedMaterial;
        }

        // --- лента ---
        plateMesh = BuildPlateMesh();
        var mf = GetComponent<MeshFilter>();
        mf.mesh = plateMesh;
        var mr = GetComponent<MeshRenderer>();
        if (beltMaterial != null) mr.material = beltMaterial;

        // --- объект для зубцов ---
        var ribsObj = new GameObject("Ribs");
        ribsObj.transform.SetParent(transform, false);
        ribsObj.AddComponent<MeshFilter>();
        var rr = ribsObj.AddComponent<MeshRenderer>();
        if (ribMaterial != null) rr.material = ribMaterial;
        ribsGo = ribsObj.transform;

        ribsMesh = new Mesh();
        ribsMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
        ribsMesh.MarkDynamic();
        ribsObj.GetComponent<MeshFilter>().mesh = ribsMesh;
    }

    // ============================  ЛЕНТА  ============================
    Mesh BuildPlateMesh()
    {
        int n = Mathf.Max(64, plateSegments);
        int vertsPerRing = 4;
        var verts = new Vector3[(n + 1) * vertsPerRing];
        var tris = new int[n * vertsPerRing * 6];
        float hw = bedWidth / 2f;

        // профиль поперёк ленты: (offset_по_нормали, высота_над_плитой)
        Vector2[] profile = new Vector2[]
        {
            new Vector2(-hw, 0f),
            new Vector2( hw, 0f),
            new Vector2( hw, TH),
            new Vector2(-hw, TH),
        };

        for (int i = 0; i <= n; i++)
        {
            float t = (float)i / n * pathLen;
            Vector3 p = PointOnPath(WrapT(t));
            Vector3 dir = PathTangent(WrapT(t));
            Vector3 normal = Vector3.Cross(Vector3.up, dir).normalized;

            for (int k = 0; k < vertsPerRing; k++)
            {
                verts[i * vertsPerRing + k] = p + normal * profile[k].x + Vector3.up * profile[k].y;
            }
        }

        int ti = 0;
        for (int i = 0; i < n; i++)
        {
            int a = i * vertsPerRing;
            int b = (i + 1) * vertsPerRing;
            for (int k = 0; k < vertsPerRing; k++)
            {
                int k2 = (k + 1) % vertsPerRing;
                tris[ti++] = a + k;
                tris[ti++] = a + k2;
                tris[ti++] = b + k2;

                tris[ti++] = a + k;
                tris[ti++] = b + k2;
                tris[ti++] = b + k;
            }
        }

        var m = new Mesh();
        m.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
        m.vertices = verts;
        m.triangles = tris;
        m.RecalculateNormals();
        m.RecalculateBounds();
        return m;
    }

    // ============================  ЗУБЦЫ  ============================
    void RebuildRibs(float offset)
    {
        float hw = bedWidth / 2f - ribMarginRel * bedWidth;
        float a  = hw * Mathf.Tan(ribAngleDeg * Mathf.Deg2Rad);
        float t  = ribThickRel * bedWidth;
        float span = a + t;
        float xOff = (span >= pitch) ? 0f : (pitch - span) / 2f;

        float embed = 0.3f * TH;
        float z0 = TH - embed;
        float z1 = TH + ribHeight;

        int ny = Mathf.Max(4, ribSegmentsY);
        int nx = Mathf.Max(2, ribSegmentsX);
        int rowVerts = (nx + 1) * 2;

        var verts = new List<Vector3>(ribCount * (ny + 1) * rowVerts);
        var tris  = new List<int>(ribCount * ny * rowVerts * 6);

        for (int r = 0; r < ribCount; r++)
        {
            float baseT = WrapT(r * pitch + offset);
            BuildOneRib(baseT, hw, a, t, span, xOff, z0, z1, ny, nx, rowVerts, verts, tris);
        }

        ribsMesh.Clear();
        ribsMesh.SetVertices(verts);
        ribsMesh.SetTriangles(tris, 0);
        ribsMesh.RecalculateNormals();
        ribsMesh.RecalculateBounds();
    }

    void BuildOneRib(float baseT,
                     float hw, float a, float t, float span, float xOff,
                     float z0, float z1,
                     int ny, int nx, int rowVerts,
                     List<Vector3> verts, List<int> tris)
    {
        int startIdx = verts.Count;

        for (int j = 0; j <= ny; j++)
        {
            float yLocal = -hw + 2f * hw * j / ny;
            float d = a * Mathf.Abs(yLocal) / hw;
            float xl = span - d;
            float xt = xl - t;
            if (direction < 0) { xl = span - xl; xt = span - xt; }
            xl += xOff;
            xt += xOff;

            // нижний контур (z0), от xt к xl
            for (int i = 0; i <= nx; i++)
            {
                float xLocal = Mathf.Lerp(xt, xl, (float)i / nx);
                float tAlong = WrapT(baseT + xLocal);
                Vector3 p = PointOnPath(tAlong);
                Vector3 dir = PathTangent(tAlong);
                Vector3 normal = Vector3.Cross(Vector3.up, dir).normalized;
                verts.Add(p + normal * yLocal + Vector3.up * z0);
            }
            // верхний контур (z1), обратно от xl к xt
            for (int i = nx; i >= 0; i--)
            {
                float xLocal = Mathf.Lerp(xt, xl, (float)i / nx);
                float tAlong = WrapT(baseT + xLocal);
                Vector3 p = PointOnPath(tAlong);
                Vector3 dir = PathTangent(tAlong);
                Vector3 normal = Vector3.Cross(Vector3.up, dir).normalized;
                verts.Add(p + normal * yLocal + Vector3.up * z1);
            }
        }

        // боковые грани между строками
        int loop = rowVerts;
        for (int j = 0; j < ny; j++)
        {
            int r0 = startIdx + j * loop;
            int r1 = startIdx + (j + 1) * loop;
            for (int k = 0; k < loop; k++)
            {
                int k2 = (k + 1) % loop;
                tris.Add(r0 + k);
                tris.Add(r0 + k2);
                tris.Add(r1 + k2);

                tris.Add(r0 + k);
                tris.Add(r1 + k2);
                tris.Add(r1 + k);
            }
        }

        // закрываем торцы (первая и последняя строка)
        int first = startIdx;
        int last  = startIdx + ny * loop;
        for (int k = 1; k < loop - 1; k++)
        {
            tris.Add(first);
            tris.Add(first + k);
            tris.Add(first + k + 1);
        }
        for (int k = 1; k < loop - 1; k++)
        {
            tris.Add(last);
            tris.Add(last + k + 1);
            tris.Add(last + k);
        }
    }

    // ============================  ПУТЬ  ============================
    float WrapT(float t)
    {
        t %= pathLen;
        if (t < 0f) t += pathLen;
        return t;
    }

    // Путь как в Blender: две длинных прямых + два полукруга по бокам
    Vector3 PointOnPath(float t)
    {
        float straight = L;
        float halfCirc = Mathf.PI * R;

        if (t < straight)
            return new Vector3(-L / 2f + t, R, 0f);
        t -= straight;

        if (t < halfCirc)
        {
            float ang = t / R;
            float x = L / 2f + R * Mathf.Sin(ang);
            float y = R * Mathf.Cos(ang);
            return new Vector3(x, y, 0f);
        }
        t -= halfCirc;

        if (t < straight)
            return new Vector3(L / 2f - t, -R, 0f);
        t -= straight;

        {
            float ang = t / R;
            float x = -L / 2f - R * Mathf.Sin(ang);
            float y = -R * Mathf.Cos(ang);
            return new Vector3(x, y, 0f);
        }
    }

    Vector3 PathTangent(float t)
    {
        float dt = 0.001f;
        Vector3 p0 = PointOnPath(WrapT(t - dt));
        Vector3 p1 = PointOnPath(WrapT(t + dt));
        return (p1 - p0).normalized;
    }
}