Угол отключен в динамической генерации сетки тора

У меня есть код для динамического создания сегмента тора в зависимости от угла (который я в основном взял из Процедурные и внес некоторые изменения, чтобы использовать входной угол вместо создания полного тора):

public class DynamicTorusSegment : MonoBehaviour
{
    [Range(0, 360)] public float CurrentAngle;

    [SerializeField] private int _maxSegments = 20;
    [SerializeField] private float _radius1 = 1f;
    [SerializeField] private float _radius2 = 0.3f;
    [SerializeField] private int _nbSides = 5;

    // For checking if the Angle was changed since the last frame
    private float _lastAngle;
    private const float TwoPi = Mathf.PI * 2f;

    // Update is called once per frame
    private void Update()
    {
        if (Mathf.Approximately(CurrentAngle, _lastAngle)) return;

        _lastAngle = CurrentAngle;
        GenerateTorus(CurrentAngle);
    }

    private void GenerateTorus(float angle)
    {
        var meshFilter = GetComponent<MeshFilter>();
        var mesh = meshFilter.mesh;
        mesh.Clear();

        var segmentsCount = (int)(_maxSegments * angle / 360);
        if (!Mathf.Approximately(angle, 0))
        {
            segmentsCount = Mathf.Clamp(segmentsCount, 2, _maxSegments);
        }


        #region Vertices        
        var vertices = new Vector3[(segmentsCount + 1) * (_nbSides + 1)];

        for (var segment = 0; segment < segmentsCount; segment++)
        {
            var currentSegment = segment == segmentsCount ? 0 : segment;

            var t1 = (float)currentSegment / segmentsCount * TwoPi / 360 * angle;
            var r1 = new Vector3(Mathf.Cos(t1) * _radius1, 0f, Mathf.Sin(t1) * _radius1);

            for (var side = 0; side <= _nbSides; side++)
            {
                var currentSide = side == _nbSides ? 0 : side;

                var t2 = (float)currentSide / _nbSides * TwoPi;
                var r2 = Quaternion.AngleAxis(-t1 * Mathf.Rad2Deg, Vector3.up) * new Vector3(Mathf.Sin(t2) * _radius2, Mathf.Cos(t2) * _radius2);

                vertices[side + segment * (_nbSides + 1)] = r1 + r2;
            }
        }
        #endregion

        #region Normales        
        var normals = new Vector3[vertices.Length];
        for (var segment = 0; segment < segmentsCount; segment++)
        {
            var currentSegment = segment == segmentsCount ? 0 : segment;

            var t1 = (float)currentSegment / segmentsCount * TwoPi / 360 * angle;
            var r1 = new Vector3(Mathf.Cos(t1) * _radius1, 0f, Mathf.Sin(t1) * _radius1);

            for (var side = 0; side <= _nbSides; side++)
            {
                normals[side + segment * (_nbSides + 1)] = (vertices[side + segment * (_nbSides + 1)] - r1).normalized;
            }
        }
        #endregion

        #region UVs
        var uvs = new Vector2[vertices.Length];
        for (var segment = 0; segment < segmentsCount; segment++)
        {
            for (var side = 0; side <= _nbSides; side++)
            {
                uvs[side + segment * (_nbSides + 1)] = new Vector2((float)segment / segmentsCount, (float)side / _nbSides);
            }
        }
        #endregion

        #region Triangles
        var faceCount = vertices.Length;
        var triangleCount = faceCount * 2;
        var indexesCount = triangleCount * 3;
        var triangles = new int[indexesCount];

        var i = 0;
        for (var segment = 0; segment < segmentsCount - 1; segment++)
        {
            for (var side = 0; side <= _nbSides - 1; side++)
            {
                var current = side + segment * (_nbSides + 1);
                var next = side + (segment < (segmentsCount) ? (segment + 1) * (_nbSides + 1) : 0);

                if (i >= triangles.Length - 6) continue;

                triangles[i++] = current;
                triangles[i++] = next;
                triangles[i++] = next + 1;

                triangles[i++] = current;
                triangles[i++] = next + 1;
                triangles[i++] = current + 1;
            }
        }
        #endregion

        mesh.vertices = vertices;
        mesh.normals = normals;
        mesh.uv = uvs;
        mesh.triangles = triangles;

        mesh.RecalculateBounds();
    }
}

Это в основном работает нормально и создает сегмент Torus в зависимости от CurrentAngle.

enter image description here

Но, как вы уже могли заметить, что-то не так с моим кодом / математикой.

Основная проблема: тор не полон на 360 °.

enter image description here

Но когда я проверил его дальше, я заметил, что на самом деле уже раньше, например. 90 ° тоже неверно:

enter image description here

Итак, в настоящее время, чтобы получить замкнутый тор, я должен использовать угол где-то между 378 ° и 379 °. Кажется, что всегда есть пропущенное смещение 19 ° ... но откуда оно взялось? (Кажется, он также работает для небольших углов, поэтому у меня есть сегмент также для angle < 19°)

Кто-нибудь видит, что я здесь делаю не так?


Обновлять

Это количество 19° на самом деле, кажется, зависит от (360° / _maxSegments ) = 18,5° (в моем примере _maxSegments было 20) и фактически становится меньше или больше в зависимости от этого значения _maxSegments.

0
0
123
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Извлечение двух строк из исходного кода:

Vector3[] vertices = new Vector3[(nbRadSeg+1) * (nbSides+1)];

for( int seg = 0; seg <= nbRadSeg; seg++ )
                      ^^

... и соответствующие строки из вашего собственного кода:

var vertices = new Vector3[(segmentsCount + 1) * (_nbSides + 1)];

for (var segment = 0; segment < segmentsCount; segment++)
                              ^

Обратите внимание, что второе условие цикла - это < вместо <=, как должно быть. Это означает, что вы пропускаете последний сегмент.

И поскольку угловой размах сегмента в вашем примере составляет ~ 18,5 °, систематическое несоответствие ~ 19 °, которое вы наблюдали, не случайно.

Обратите внимание, что та же проблема возникает и для нормальных, УФ и треугольных циклов генерации.

Нет, это не решает. Последний только соединял тринагла с начальной точкой, так что тор получился «замкнутым», но через очень уродливый треугольник между последним и первым сегментом. Когда я оставил это, у меня также было много исключений, таких как Assertion failed: Converting invalid MinMaxAABB, Assertion failed: Invalid worldAABB. Object is too large or too far away from the origin., в основном, по-видимому, из-за деления на 0, я думаю

derHugo 31.10.2018 14:08

@derHugo в этом случае вам понадобится дополнительное предложение для обработки случая, когда тор закрыт. Я обновлю пример кода.

meowgoesthedog 31.10.2018 14:13

Вы можете сэкономить свое время! Вы были правы .. Я удалил = только из-за полученных мной исключений. Но исходная ошибка уже есть в строке для треугольников: for (var segment = 0; segment < segmentsCount - 1; segment++) .. -1 (я полагаю: D) на самом деле просто пережиток прошлого теста. Так что это должен быть <= для всех лопов, кроме последнего, там только <, и теперь он работает, спасибо. И я просто пропущу, если угол слишком мал, чтобы он не доставил проблем для 0.

derHugo 31.10.2018 14:20

@derHugo Понятно, нет проблем, ха-ха; Я тоже видел бит -1 (еще одно несоответствие с исходным кодом) и думал упомянуть об этом, но в конце концов отказался от этого

meowgoesthedog 31.10.2018 14:23

Другие вопросы по теме