Как я могу найти объем и вершины пересечения двух выровненных по оси кубов?

#c# #unity3d

Вопрос:

как я могу найти объем пересечения двух кубов, выровненных по оси? Кубики могут иметь разные размеры и положения. (Я добавляю картинку, чтобы показать простой пример двух кубов)

После глубоких исследований я совершенно уверен, что в Unity не существует конкретной функции, которую можно было бы использовать для этой цели, единственный способ решить эту проблему-математическая логика. Например, моей первой идеей было:

  1. Чтобы найти 8 вершин куба»пересечение» (B на изображении).
  2. Попробуйте построить новый куб с этой вершиной.
  3. Найдите размер и объем куба «пересечение».

Единство позволяет найти:

  • центр каждого первичного куба (A на изображении) с «Границами.центр»;
  • размеры каждого первичного куба (A на изображении) от центра куба (половина размера границ) с помощью «Bounds.extents».

доступная документация here:https://docs.unity3d.com/ScriptReference/Bounds.html

Однако я не могу найти способ получить вершину каждого куба. И, во-вторых, логическая функция, которая может найти, какие 8 из 16 найденных вершин являются правильной вершиной для построения куба»Пересечение».

У вас есть какая-нибудь помощь или предложение?

Изображение: введите описание изображения здесь

Комментарии:

1. Не лучше ли было бы задать этот вопрос на сайте обмена математическими стеками? Кроме того, вы, вероятно, захотите удалить любую болтовню из заголовка и текста вашего вопроса, поскольку это не помогает вопросу и на самом деле ему не принадлежит.

2. @HovercraftFullOfEels Готово! У вас есть какие-нибудь предложения по обмену математическими стеками? Я ничего об этом не знаю

3. math.stackexchange.com например?

4. @derpirscher хорошо, спасибо, я попробую и там

5. Вам нужно также для повернутых кубов? или только вращение всегда будет кватернионом.идентичность?

Ответ №1:

Если, как вы говорите, кубы всегда будут выровнены по оси с миром единства (=> границы = объем коллайдера/визуализатора), вы, вероятно, могли бы просто сделать что-то вроде

  • возьмите Bounds обе коробки
  • возьмите min и max из них
  • => проверьте, насколько они перекрываются на каждой оси по отдельности, используя максимум минут и минимум максимумов

То, что вы получаете при этом, — это новая минимальная и максимальная точка перекрывающегося поля.

Этой информации достаточно

  • для вычисления объема путем умножения отдельных компонентов вектора между этими минимальными и максимальными значениями.
  • чтобы получить все вершины, получив все перестановки между min и max для каждой оси.

что-то вроде

 public class OverlapArea
{
    public readonly Vector3 min;
    public readonly Vector3 max;

    public readonly float volume;
    public Vector3 frontBottomLeft => min;
    public readonly Vector3 frontBottomRight;
    public readonly Vector3 frontTopLeft;
    public readonly Vector3 frontTopRight;
    public readonly Vector3 backBottomLeft;
    public readonly Vector3 backBottomRight;
    public readonly Vector3 backTopLeft;
    public Vector3 backTopRight => max;

    public readonly Bounds bounds;

    public OverlapArea(Bounds a, Bounds b)
    {
        // The min and max points
        var minA = a.min;
        var maxA = a.max;
        var minB = b.min;
        var maxB = b.max;

        min.x = Mathf.Max(minA.x, minB.x);
        min.y = Mathf.Max(minA.y, minB.y);
        min.z = Mathf.Max(minA.z, minB.z);

        max.x = Mathf.Min(maxA.x, maxB.x);
        max.y = Mathf.Min(maxA.y, maxB.y);
        max.z = Mathf.Min(maxA.z, maxB.z);

        frontBottomRight = new Vector3(max.x, min.y, min.z);
        frontTopLeft = new Vector3(min.x, max.y, min.z);
        frontTopRight = new Vector3(max.x, max.y, min.z);
        backBottomLeft = new Vector3(min.x, min.y, max.z);
        backBottomRight = new Vector3(max.x, min.y, max.z);
        backTopLeft = new Vector3(min.x, max.y, max.z);

        // The diagonal of this overlap box itself
        var diagonal = max - min;
        volume = diagonal.x * diagonal.y * diagonal.z;

        bounds.SetMinMax(min, max);
    }

    public static bool GetOverlapArea(Bounds a, Bounds b, out OverlapArea overlapArea)
    {
        overlapArea = defau<

        // If they are not intersecting we can stop right away ;)
        if (!a.Intersects(b)) return false;

        overlapArea = new OverlapArea(a, b);

        return true;
    }
}
    
    
 

And so in order to get the overlap information you would do e.g.

 // I intentionally used the Bounds as parameters for the method because you can
// either use the Renderer bounds
var boundsA = cubeA.GetComponent<Renderer>().bounds;
// or use Collider bounds in your case they should be equal
var boundsB = cubeB.GetComponent<Collider>().bounds;

if(GetOverlapArea(boundsA, boundsB, out var overlap))
{
    // Now in overlap you have all the information you wanted
}
 

Теперь, чтобы на самом деле получить сетку перекрытия ( если вы собираетесь именно туда), у вас есть два варианта

Либо используйте заданные точки ребер и фактически создайте сетку самостоятельно (обратите внимание, что вершины находятся в мировом пространстве, поэтому объект не должен масштабироваться сам по себе или на каком-либо родителе).

 ...

var mesh = new Mesh
{
    vertices = new[]
    {
        overlapArea.frontBottomLeft,
        overlapArea.frontBottomRight,
        overlapArea.frontTopLeft,
        overlapArea.frontTopRight,

        overlapArea.backBottomLeft,
        overlapArea.backBottomRight,
        overlapArea.backTopLeft,
        overlapArea.backTopRight
    },
    triangles = new[]
    {
        // Front
        0, 2, 1,
        1, 2, 3,
        // Back
        5, 7, 4,
        4, 7, 6,
        // Left
        4, 6, 2,
        4, 2, 0,
        // Right
        1, 7, 5,
        1, 3, 7,
        // Top
        2, 7, 3,
        2, 6, 7,
        // Bottom
        0, 4, 1,
        1, 4, 5
    }
}
 

в качестве небольшой демонстрации

 public class Example : MonoBehaviour
{
    public Renderer CubeA;
    public Renderer CubeB;

    public Material overlapMaterial;

    private MeshFilter overlap;
    private readonly Vector3[] overlapVertices = new Vector3[8];

    public void Awake()
    {
        overlap = new GameObject("Overlap", typeof(MeshRenderer)).AddComponent<MeshFilter>();

        var overlapMesh = new Mesh
        {
            vertices = overlapVertices,
            triangles = new[]
            {
                // Front
                0, 2, 1,
                1, 2, 3,

                // Back
                5, 7, 4,
                4, 7, 6,

                // Left
                4, 6, 2,
                4, 2, 0,

                // Right
                1, 7, 5,
                1, 3, 7,

                // Top
                2, 7, 3,
                2, 6, 7,

                // Bottom
                0, 4, 1,
                1, 4, 5
            }
        };

        overlap.mesh = overlapMesh;
        overlap.GetComponent<Renderer>().material = overlapMaterial;
    }
    
    public void Update()
    {
        if (OverlapArea.GetOverlapArea(CubeA.bounds, CubeB.bounds, out var overlapArea))
        {
            overlap.gameObject.SetActive(true);

            overlap.mesh.vertices = new[]
            {
                overlapArea.frontBottomLeft,
                overlapArea.frontBottomRight,
                overlapArea.frontTopLeft,
                overlapArea.frontTopRight,

                overlapArea.backBottomLeft,
                overlapArea.backBottomRight,
                overlapArea.backTopLeft,
                overlapArea.backTopRight
            };

            overlap.mesh.RecalculateBounds();
        }
        else
        {
            overlap.gameObject.SetActive(false);
        }
    }
}
 

введите описание изображения здесь

или вы можете вместо этого использовать уже существующий примитивный куб (куб единства по умолчанию), просто установить его в правильные координаты и масштабировать следующим образом

 overlapVisualizer.transform.position = overlap.bounds.center;
// note we set the local scale so there should be no parent scaling
overlapVisualizer.transform.localScale = overlap.bounds.size;
 

снова небольшая демонстрация этого

 public class Example : MonoBehaviour
{
    public Renderer CubeA;
    public Renderer CubeB;

    public Transform overlap;

    public void Update()
    {
        if (OverlapArea.GetOverlapArea(CubeA.bounds, CubeB.bounds, out var overlapArea))
        {
            overlap.gameObject.SetActive(true);

            overlap.position = overlapArea.bounds.center;
            overlap.localScale = overlapArea.bounds.size;
        }
        else
        {
            overlap.gameObject.SetActive(false);
        }
    }
}
 

введите описание изображения здесь