how can I find the volume of the intersection of two cubes aligned to the axis? The cubes can have different sizes and positions. (I add a picture to show a simple example case of two cubes)
After deep research, I am quite sure that does not exist a specific function in Unity to use for this purpose, the only way to work out this problem is mathematical logic. For example, my first idea was:
To find the 8 vertexes of the cube"intersection" (B in image).
Try to build a new cube with this vertex.
Find the size and volume of cube "intersection".
Unity permits to find:
documentation avaible here:https://docs.unity3d.com/ScriptReference/Bounds.html
However, I can't find a way to have the vertex of each cube. And, secondly, a logical function that can find what 8 of 16 vertexes found are the right vertex to use to build the cube"Intersection".
Have u got any help or suggest?
Image:
If as you say the cubes are going to be always axis aligned with the Unity world (=> bounds = collider/renderer volume) you could probably simply do something like
Bounds
of both boxesmin
and max
of theseWhat you get by this are a new min and max point of the overlapping box.
This is enough information
to calculate the volume by multiplying the individual components of the vector between these min and max.
to get all the vertices by getting all permutations between min and max for each axis.
something like
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 = default;
// 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
}
Now in order to actually get the overlap mesh ( if that is where you are going) you have two options
Either use the given edge points and actually create a mesh yourself (note the vertices are in world space so the object should have no scaling itself or on any parent)
...
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
}
}
as a little demo
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);
}
}
}
or you could instead use an already existing primitive cube (the default Unity cube) and just set it to the correct coordinates and scale it like
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;
again a little demo of that
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);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With