This crate implements a method to split the meshes when the intersecting faces are coplanar. This allows the geometry to match at the interfaces of the meshes, which enables ray tracing for translucent materials reliably.
In the following diagram, we can see that for the face on the mesh, this might have the outside material defined non-uniformly. Hence, coplanar faces intersection is necessary to identify how to split this faces to define their outside material correctly on their sub-mesh.
For each coplanar intersection of 2 meshes
the meshes are split in two parts, such that the overlapping region is identical.
Then each triangle of the intersection has the attribute interface set such that the outside material is the inside of the other mesh and vice-versa.
An object surface is described as a closed mesh, formed of polygons (specifically triangles in our case). Each surface has an attribute described in @def-surface-attribute, which can be an interface between two materials, a reflector, a mirror or a detector.
When two objects are adjacent to each other, their meshes can be co-planar at the boundary of their
intersection. In the case where one of the attributes is Interface, then the meshes need to be split
such that we decide how rays refract/reflect at these interfaces. Otherwise, the rays are not expected
to cross these planes and be influence by the other mesh.
The splitting function
where $\bigcup_{T_u \in \mathnormal{M}u} \bigcup{T_v \in \mathnormal{M}_v} \mathbf{S}(T_u, T_v)$ is the union of the splitting of all triangle pairs from the two meshes and the operation is defined as
We require that calling
Tip
The splitting function can return early, by running a collision test as discussed in "Ericson: Real Time Collision Detection 2004" using bounding boxes or a BSP tree to determine if the meshes intersect or not, and only run the splitting if they do.
We define the splitting primitives
Then we can define the triangle conversion functions of the set of primitives as
Then it is more useful for us to define another splitting function
The primitives are then reused to split the other triangle, preserving compatible geometry at the interface
For triangle splitting is easy to think of as a set of edges resulting from intersecting two triangles, however, the similar concept might not be compatible to construct from the meshes point of view.
Firstly, what would be the splitting primitive $\mathcal{P}{M_u \cap M_v}$ such that a mesh functional $\mathcal{M}(\mathcal{P}{M_u \cap M_v}) = M_u \cap M_v$ can convert it to the intersection mesh?
Secondly, how should the splitting function be defined to achieve consistent
results between
::: {.callout-note} $\mathbf{S}(\mathnormal{M}_u, \mathnormal{M}v)$ and $\tilde{\mathbf{S}}(\mathnormal{M}v, \mathcal{P}{M_u \cap M_v})$ are compatible by the definition of $\mathcal{P}{M_u \cap M_v}$. :::
The splitting primitive can be the union of all the cross mesh intersection primitives as defined in:
Warning
While we run the splitting on the triangle we know the splitting primitive would
only apply to other triangle used to split the target triangle rather than the
whole mesh, therefore having constant
For construction purposes and to avoid doing an expensive k-dimensional
coordinates equality check for matching the vertices position, we hold each
verts position in a
Hence, matching vertices is done based on an identifier rather than comparing floating points. This mitigates problems in operation with floating point errors, avoiding openings in the mesh and computationally lighter to compute matches.
For barycentric coordinates on triangles to determine their surface normal, they
might be described as an interpolation between normals from its vertices.
Therefore, we also hold a list of normals
In order to keep the intersections consistent the intersection primitives are
used to split the other triangle, therefore, the primitives are grouped by the
triangle they are splitting. For example, in
@fig-triangle_polygons_split_primitives, while splitting
Tip
The intersection primitives are a set of polygons, which for the purpose of the
aetherus project are chosen to be triangulated ahead of time, such that
produced meshes only use triangles.
Then, the splitting function is
Expanding this functionality at the meshes level looks like:
Which make the splitting of mesh
Note
We can determine
- The intersections with triangle
$U_1$ are$\bar{\mathbf{S}}(U_1, M_v) = { P_{U_1 \cap V_1}, P_{U_1 \cap V_2}, P_{U_1 \cap V_3} }$ - Then run the intersections with the other triangles. We are particularly
interested in triangles
$U_2$ and$U_3$ which have an intersections with$V_1$ - Finally, collect the intersections for
$V_1$ from$\bar{\mathbf{S}}(U_2, M_v)$ and$\bar{\mathbf{S}}(U_3, M_v)$ , which results in $\mathcal{P}{M_u \cap U_1} = { P{U_1 \cap V_1}, P_{U_2 \cap V_1}, P_{U_3 \cap V_1} }$ shown in orange
Now that we have the intersection mesh, there are 4 cases that are worth considering in terms of the attribute value of the surface described by the meshes.
- (
$\mathcal{M}_u$ : Interface, $\mathcal{M}v$: Interface): The intersection mesh $\mathcal{M}{u \cap v}$ is an interface between the two materials described in the attributes of the meshes. The outside material of one is the inside of the other and vice-versa which is equivalent to:
- (
$\mathcal{M}_u$ : Interface, $\mathcal{M}v$: {Reflector, Mirror, Detector}): The intersection mesh $\mathcal{M}{u \cap v}$ is discarded for$\mathcal{M}_u$ splitting while$\mathcal{M}_v$ is not split
- (
$\mathcal{M}_u$ : {Reflector, Mirror, Detector},$\mathcal{M}_v$ : Interface): Same thing as the previous case, but in reverse - (
$\mathcal{M}_u$ : {Reflector, Mirror, Detector},$\mathcal{M}_v$ : {Reflector, Mirror, Detector}): No splitting is performed
For the triangle-triangle splitting function
Tip
Intersection of two convex polygons (triangles in this case) will be a convex polygon. Therefore, triangulation can be done simply using the fan method, connecting one vertex to all other non-adjacent vertices, but a better choice might be a MaxMin triangulation @keilAlgorithmMaxMinArea2003 @keilAlgorithmsOptimalArea2006
-
Resolved @fig-resolved_tris_split:
$(1,1)$ -
Edge collinear @fig-colinear_edges:
- (a) Two edges collinear
- (i) One edge is the same:
$(2,1)$ - (ii) Edges are collinear, but not the same:
$(3,1)$
- (i) One edge is the same:
- (b) Vertex out:
$(1,1)$ - (c) Vertex in:
- (i)
$v_1 = u_1$ and$v_2 = u_2$ :$(3,1)$ - (ii)
$v_1 = u_1$ ,$v_2 \in (u_1, u_2)$ :$(4,1)$ - (iii)
${v_1, v_2} \in (u_1, u_2)$ :$(5,1)$
- (i)
- (a) Two edges collinear
-
Vertex in = 1 (
$v_1$ ) @fig-vertex_in_1:- (a)
$[v_1,v_2]$ and$[v_1,v_3]$ intersect same$U$ edge: (5,3) - (b)
$[v_1,v_2]$ and$[v_1,v_3]$ intersect distinct$U$ edges- (i)
$[v_2, v_3]$ not intersecting$U$ : (5,5) - (ii)
$[v_2, v_3]$ intersects two distinct edges of$U$ : (7,5)
- (i)
- (c) One edge of
$V$ triangle contains$U$ vertex:$(4,3)$ - (d) Two edges of
$V$ triangle contain$U$ vertices:$(3,3)$
- (a)
-
Vertex in = 2 (
$v_1, v_2$ ) @fig-vertex_in_2:- (a)
$[v_1,v_3]$ and$[v_2,v_3]$ intersect$U$ edges- (i) Same
$U$ edge:$(7,3)$ - (ii) Two distinct
$U$ edges:$(7,5)$
- (i) Same
- (b) One edge of
$V$ contains$U$ vertex:$(6,3)$
- (a)
-
Vertex in = 3:
$(7,1)$ -
Vertex in = 0 @fig-vertex_out:
- (a) Two edges of
$V$ each intersect with any two edges of$U$ - (i) Vertex of
$U$ inside$V$ :$(5,7)$ - (ii) Vertex of
$U$ on edge of$V$ :$(5,6)$
- (i) Vertex of
- (b) One edge of
$V$ intersects with two edges of$U$ - (i) One vertex of
$U$ on edge of$V$ :$(3,6)$ - (ii) Two vertices of
$U$ on edges of$V$ :$(3,5)$ - (iii) Two vertices of
$U$ inside$V$ :$(3,7)$
- (i) One vertex of
- (c) Three edges of
$V$ intersect three edges of$U$ :$(7,7)$
- (a) Two edges of

