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


using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class ConveyorGenerator : MonoBehaviour
{
    // ============================  НАСТРОЙКИ (1-в-1 как в Blender) ============================
    [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 int   direction     = 1;

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

    [Header("Движение")]
    [Tooltip("Шагов зубца в секунду. 1 = один шаг за секунду")]
    public float stepsPerSecond = 1.0f;

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

    void Start()
    {
        BuildAll();
    }

    void Update()
    {
        float offset = Mathf.Repeat(Time.time * stepsPerSecond * 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;

        // ---- материалы создаются в коде ----
        Material beltMat = CreateMaterial("Belt_Mat", new Color(0.045f, 0.045f, 0.05f));
        Material ribMat  = CreateMaterial("Rib_Mat",  new Color(0.16f, 0.16f, 0.18f));
        Material bedMat  = CreateMaterial("Bed_Mat",  new Color(0.10f, 0.10f, 0.10f));

        // ---- плита под лентой ----
        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);
        bed.GetComponent<MeshRenderer>().material = bedMat;

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

        // ---- зубцы ----
        var ribsObj = new GameObject("Ribs");
        ribsObj.transform.SetParent(transform, false);
        ribsObj.AddComponent<MeshFilter>();
        var rr = ribsObj.AddComponent<MeshRenderer>();
        rr.material = ribMat;

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

        // первый прогон зубцов
        RebuildRibs(0f);
    }

    Material CreateMaterial(string name, Color color)
    {
        var shader = Shader.Find("Standard");
        if (shader == null) shader = Shader.Find("Diffuse");
        var mat = new Material(shader);
        mat.name = name;
        mat.color = color;
        if (mat.HasProperty("_Glossiness")) mat.SetFloat("_Glossiness", 0.15f);
        if (mat.HasProperty("_Metallic"))   mat.SetFloat("_Metallic", 0f);
        return mat;
    }

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

        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 < ringVerts; k++)
                verts[i * ringVerts + k] = p + normal * profile[k].x + Vector3.up * profile[k].y;
        }

        int ti = 0;
        for (int i = 0; i < n; i++)
        {
            int a = i * ringVerts;
            int b = (i + 1) * ringVerts;
            for (int k = 0; k < ringVerts; k++)
            {
                int k2 = (k + 1) % ringVerts;
                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;

            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);
            }
            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;
    }

    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;
    }
}