1
- use bevy_math:: { bounding:: Aabb3d , Dir3 , Mat4 , Ray3d , Vec3 , Vec3A } ;
2
- use bevy_mesh:: { Indices , Mesh , PrimitiveTopology } ;
1
+ use bevy_math:: { bounding:: Aabb3d , Dir3 , Mat4 , Ray3d , Vec2 , Vec3 , Vec3A } ;
2
+ use bevy_mesh:: { Indices , Mesh , PrimitiveTopology , VertexAttributeValues } ;
3
3
use bevy_reflect:: Reflect ;
4
4
5
5
use super :: Backfaces ;
@@ -18,6 +18,8 @@ pub struct RayMeshHit {
18
18
pub distance : f32 ,
19
19
/// The vertices of the triangle that was hit.
20
20
pub triangle : Option < [ Vec3 ; 3 ] > ,
21
+ /// UV coordinate of the hit, if the mesh has UV attributes.
22
+ pub uv : Option < Vec2 > ,
21
23
/// The index of the triangle that was hit.
22
24
pub triangle_index : Option < usize > ,
23
25
}
@@ -26,6 +28,10 @@ pub struct RayMeshHit {
26
28
#[ derive( Default , Debug ) ]
27
29
pub struct RayTriangleHit {
28
30
pub distance : f32 ,
31
+ /// Note this uses the convention from the Moller-Trumbore algorithm:
32
+ /// P = (1 - u - v)A + uB + vC
33
+ /// This is different from the more common convention of
34
+ /// P = uA + vB + (1 - u - v)C
29
35
pub barycentric_coords : ( f32 , f32 ) ,
30
36
}
31
37
@@ -34,7 +40,7 @@ pub(super) fn ray_intersection_over_mesh(
34
40
mesh : & Mesh ,
35
41
transform : & Mat4 ,
36
42
ray : Ray3d ,
37
- culling : Backfaces ,
43
+ cull : Backfaces ,
38
44
) -> Option < RayMeshHit > {
39
45
if mesh. primitive_topology ( ) != PrimitiveTopology :: TriangleList {
40
46
return None ; // ray_mesh_intersection assumes vertices are laid out in a triangle list
@@ -47,26 +53,37 @@ pub(super) fn ray_intersection_over_mesh(
47
53
. attribute ( Mesh :: ATTRIBUTE_NORMAL )
48
54
. and_then ( |normal_values| normal_values. as_float3 ( ) ) ;
49
55
56
+ let uvs = mesh
57
+ . attribute ( Mesh :: ATTRIBUTE_UV_0 )
58
+ . and_then ( |uvs| match uvs {
59
+ VertexAttributeValues :: Float32x2 ( uvs) => Some ( uvs. as_slice ( ) ) ,
60
+ _ => None ,
61
+ } ) ;
62
+
50
63
match mesh. indices ( ) {
51
64
Some ( Indices :: U16 ( indices) ) => {
52
- ray_mesh_intersection ( ray, transform, positions, normals, Some ( indices) , culling )
65
+ ray_mesh_intersection ( ray, transform, positions, normals, Some ( indices) , uvs , cull )
53
66
}
54
67
Some ( Indices :: U32 ( indices) ) => {
55
- ray_mesh_intersection ( ray, transform, positions, normals, Some ( indices) , culling )
68
+ ray_mesh_intersection ( ray, transform, positions, normals, Some ( indices) , uvs , cull )
56
69
}
57
- None => ray_mesh_intersection :: < usize > ( ray, transform, positions, normals, None , culling ) ,
70
+ None => ray_mesh_intersection :: < usize > ( ray, transform, positions, normals, None , uvs , cull ) ,
58
71
}
59
72
}
60
73
61
74
/// Checks if a ray intersects a mesh, and returns the nearest intersection if one exists.
62
- pub fn ray_mesh_intersection < I : TryInto < usize > + Clone + Copy > (
75
+ pub fn ray_mesh_intersection < I > (
63
76
ray : Ray3d ,
64
77
mesh_transform : & Mat4 ,
65
78
positions : & [ [ f32 ; 3 ] ] ,
66
79
vertex_normals : Option < & [ [ f32 ; 3 ] ] > ,
67
80
indices : Option < & [ I ] > ,
81
+ uvs : Option < & [ [ f32 ; 2 ] ] > ,
68
82
backface_culling : Backfaces ,
69
- ) -> Option < RayMeshHit > {
83
+ ) -> Option < RayMeshHit >
84
+ where
85
+ I : TryInto < usize > + Clone + Copy ,
86
+ {
70
87
let world_to_mesh = mesh_transform. inverse ( ) ;
71
88
72
89
let ray = Ray3d :: new (
@@ -139,17 +156,12 @@ pub fn ray_mesh_intersection<I: TryInto<usize> + Clone + Copy>(
139
156
closest_hit. and_then ( |( tri_idx, hit) | {
140
157
let [ a, b, c] = match indices {
141
158
Some ( indices) => {
142
- let triangle = indices. get ( ( tri_idx * 3 ) ..( tri_idx * 3 + 3 ) ) ?;
143
-
144
- let [ Ok ( a) , Ok ( b) , Ok ( c) ] = [
145
- triangle[ 0 ] . try_into ( ) ,
146
- triangle[ 1 ] . try_into ( ) ,
147
- triangle[ 2 ] . try_into ( ) ,
148
- ] else {
149
- return None ;
150
- } ;
151
-
152
- [ a, b, c]
159
+ let [ i, j, k] = [ tri_idx * 3 , tri_idx * 3 + 1 , tri_idx * 3 + 2 ] ;
160
+ [
161
+ indices. get ( i) . copied ( ) ?. try_into ( ) . ok ( ) ?,
162
+ indices. get ( j) . copied ( ) ?. try_into ( ) . ok ( ) ?,
163
+ indices. get ( k) . copied ( ) ?. try_into ( ) . ok ( ) ?,
164
+ ]
153
165
}
154
166
None => [ tri_idx * 3 , tri_idx * 3 + 1 , tri_idx * 3 + 2 ] ,
155
167
} ;
@@ -168,10 +180,12 @@ pub fn ray_mesh_intersection<I: TryInto<usize> + Clone + Copy>(
168
180
} ) ;
169
181
170
182
let point = ray. get_point ( hit. distance ) ;
183
+ // Note that we need to convert from the Möller-Trumbore convention to the more common
184
+ // P = uA + vB + (1 - u - v)C convention.
171
185
let u = hit. barycentric_coords . 0 ;
172
186
let v = hit. barycentric_coords . 1 ;
173
187
let w = 1.0 - u - v;
174
- let barycentric = Vec3 :: new ( u , v , w ) ;
188
+ let barycentric = Vec3 :: new ( w , u , v ) ;
175
189
176
190
let normal = if let Some ( normals) = tri_normals {
177
191
normals[ 1 ] * u + normals[ 2 ] * v + normals[ 0 ] * w
@@ -181,9 +195,29 @@ pub fn ray_mesh_intersection<I: TryInto<usize> + Clone + Copy>(
181
195
. normalize ( )
182
196
} ;
183
197
198
+ let uv = uvs. and_then ( |uvs| {
199
+ let tri_uvs = if let Some ( indices) = indices {
200
+ let i = tri_idx * 3 ;
201
+ [
202
+ uvs[ indices[ i] . try_into ( ) . ok ( ) ?] ,
203
+ uvs[ indices[ i + 1 ] . try_into ( ) . ok ( ) ?] ,
204
+ uvs[ indices[ i + 2 ] . try_into ( ) . ok ( ) ?] ,
205
+ ]
206
+ } else {
207
+ let i = tri_idx * 3 ;
208
+ [ uvs[ i] , uvs[ i + 1 ] , uvs[ i + 2 ] ]
209
+ } ;
210
+ Some (
211
+ barycentric. x * Vec2 :: from ( tri_uvs[ 0 ] )
212
+ + barycentric. y * Vec2 :: from ( tri_uvs[ 1 ] )
213
+ + barycentric. z * Vec2 :: from ( tri_uvs[ 2 ] ) ,
214
+ )
215
+ } ) ;
216
+
184
217
Some ( RayMeshHit {
185
218
point : mesh_transform. transform_point3 ( point) ,
186
219
normal : mesh_transform. transform_vector3 ( normal) ,
220
+ uv,
187
221
barycentric_coords : barycentric,
188
222
distance : mesh_transform
189
223
. transform_vector3 ( ray. direction * hit. distance )
@@ -329,6 +363,7 @@ mod tests {
329
363
positions,
330
364
vertex_normals,
331
365
indices,
366
+ None ,
332
367
backface_culling,
333
368
) ;
334
369
@@ -350,6 +385,7 @@ mod tests {
350
385
positions,
351
386
vertex_normals,
352
387
indices,
388
+ None ,
353
389
backface_culling,
354
390
) ;
355
391
@@ -372,6 +408,7 @@ mod tests {
372
408
positions,
373
409
vertex_normals,
374
410
indices,
411
+ None ,
375
412
backface_culling,
376
413
) ;
377
414
@@ -394,6 +431,7 @@ mod tests {
394
431
positions,
395
432
vertex_normals,
396
433
indices,
434
+ None ,
397
435
backface_culling,
398
436
) ;
399
437
@@ -415,6 +453,7 @@ mod tests {
415
453
positions,
416
454
vertex_normals,
417
455
indices,
456
+ None ,
418
457
backface_culling,
419
458
) ;
420
459
@@ -436,6 +475,7 @@ mod tests {
436
475
positions,
437
476
vertex_normals,
438
477
indices,
478
+ None ,
439
479
backface_culling,
440
480
) ;
441
481
@@ -457,6 +497,7 @@ mod tests {
457
497
positions,
458
498
vertex_normals,
459
499
indices,
500
+ None ,
460
501
backface_culling,
461
502
) ;
462
503
@@ -478,6 +519,7 @@ mod tests {
478
519
positions,
479
520
vertex_normals,
480
521
indices,
522
+ None ,
481
523
backface_culling,
482
524
) ;
483
525
0 commit comments