@@ -5,7 +5,7 @@ use crate::{
5
5
6
6
use super :: Meshable ;
7
7
use bevy_math:: {
8
- primitives:: { Circle , Ellipse , Rectangle , RegularPolygon , Triangle2d , WindingOrder } ,
8
+ primitives:: { Capsule2d , Circle , Ellipse , Rectangle , RegularPolygon , Triangle2d , WindingOrder } ,
9
9
Vec2 ,
10
10
} ;
11
11
use wgpu:: PrimitiveTopology ;
@@ -266,3 +266,142 @@ impl From<Rectangle> for Mesh {
266
266
rectangle. mesh ( )
267
267
}
268
268
}
269
+
270
+ /// A builder used for creating a [`Mesh`] with a [`Capsule2d`] shape.
271
+ #[ derive( Clone , Copy , Debug ) ]
272
+ pub struct Capsule2dMeshBuilder {
273
+ /// The [`Capsule2d`] shape.
274
+ pub capsule : Capsule2d ,
275
+ /// The number of vertices used for one hemicircle.
276
+ /// The total number of vertices for the capsule mesh will be two times the resolution.
277
+ ///
278
+ /// The default is `16`.
279
+ pub resolution : usize ,
280
+ }
281
+
282
+ impl Default for Capsule2dMeshBuilder {
283
+ fn default ( ) -> Self {
284
+ Self {
285
+ capsule : Capsule2d :: default ( ) ,
286
+ resolution : 16 ,
287
+ }
288
+ }
289
+ }
290
+
291
+ impl Capsule2dMeshBuilder {
292
+ /// Creates a new [`Capsule2dMeshBuilder`] from a given radius, length, and the number of vertices
293
+ /// used for one hemicircle. The total number of vertices for the capsule mesh will be two times the resolution.
294
+ #[ inline]
295
+ pub fn new ( radius : f32 , length : f32 , resolution : usize ) -> Self {
296
+ Self {
297
+ capsule : Capsule2d :: new ( radius, length) ,
298
+ resolution,
299
+ }
300
+ }
301
+
302
+ /// Sets the number of vertices used for one hemicircle.
303
+ /// The total number of vertices for the capsule mesh will be two times the resolution.
304
+ #[ inline]
305
+ pub const fn resolution ( mut self , resolution : usize ) -> Self {
306
+ self . resolution = resolution;
307
+ self
308
+ }
309
+
310
+ /// Builds a [`Mesh`] based on the configuration in `self`.
311
+ pub fn build ( & self ) -> Mesh {
312
+ // The resolution is the number of vertices for one semicircle
313
+ let resolution = self . resolution as u32 ;
314
+ let vertex_count = 2 * self . resolution ;
315
+
316
+ // Six extra indices for the two triangles between the hemicircles
317
+ let mut indices = Vec :: with_capacity ( ( self . resolution - 2 ) * 2 * 3 + 6 ) ;
318
+ let mut positions = Vec :: with_capacity ( vertex_count) ;
319
+ let normals = vec ! [ [ 0.0 , 0.0 , 1.0 ] ; vertex_count] ;
320
+ let mut uvs = Vec :: with_capacity ( vertex_count) ;
321
+
322
+ let radius = self . capsule . radius ;
323
+ let step = std:: f32:: consts:: TAU / vertex_count as f32 ;
324
+
325
+ // If the vertex count is even, offset starting angle of top semicircle by half a step
326
+ // to position the vertices evenly.
327
+ let start_angle = if vertex_count % 2 == 0 {
328
+ step / 2.0
329
+ } else {
330
+ 0.0
331
+ } ;
332
+
333
+ // How much the hemicircle radius is of the total half-height of the capsule.
334
+ // This is used to prevent the UVs from stretching between the hemicircles.
335
+ let radius_frac = self . capsule . radius / ( self . capsule . half_length + self . capsule . radius ) ;
336
+
337
+ // Create top semicircle
338
+ for i in 0 ..resolution {
339
+ // Compute vertex position at angle theta
340
+ let theta = start_angle + i as f32 * step;
341
+ let ( sin, cos) = theta. sin_cos ( ) ;
342
+ let ( x, y) = ( cos * radius, sin * radius + self . capsule . half_length ) ;
343
+
344
+ positions. push ( [ x, y, 0.0 ] ) ;
345
+ uvs. push ( [ 0.5 * ( cos + 1.0 ) , radius_frac * ( 1.0 - 0.5 * ( sin + 1.0 ) ) ] ) ;
346
+ }
347
+
348
+ // Add top semicircle indices
349
+ for i in 1 ..resolution - 1 {
350
+ indices. extend_from_slice ( & [ 0 , i, i + 1 ] ) ;
351
+ }
352
+
353
+ // Add indices for top left triangle of the part between the hemicircles
354
+ indices. extend_from_slice ( & [ 0 , resolution - 1 , resolution] ) ;
355
+
356
+ // Create bottom semicircle
357
+ for i in resolution..vertex_count as u32 {
358
+ // Compute vertex position at angle theta
359
+ let theta = start_angle + i as f32 * step;
360
+ let ( sin, cos) = theta. sin_cos ( ) ;
361
+ let ( x, y) = ( cos * radius, sin * radius - self . capsule . half_length ) ;
362
+
363
+ positions. push ( [ x, y, 0.0 ] ) ;
364
+ uvs. push ( [ 0.5 * ( cos + 1.0 ) , 1.0 - radius_frac * 0.5 * ( sin + 1.0 ) ] ) ;
365
+ }
366
+
367
+ // Add bottom semicircle indices
368
+ for i in 1 ..resolution - 1 {
369
+ indices. extend_from_slice ( & [ resolution, resolution + i, resolution + i + 1 ] ) ;
370
+ }
371
+
372
+ // Add indices for bottom right triangle of the part between the hemicircles
373
+ indices. extend_from_slice ( & [ resolution, vertex_count as u32 - 1 , 0 ] ) ;
374
+
375
+ Mesh :: new (
376
+ PrimitiveTopology :: TriangleList ,
377
+ RenderAssetUsages :: default ( ) ,
378
+ )
379
+ . with_inserted_attribute ( Mesh :: ATTRIBUTE_POSITION , positions)
380
+ . with_inserted_attribute ( Mesh :: ATTRIBUTE_NORMAL , normals)
381
+ . with_inserted_attribute ( Mesh :: ATTRIBUTE_UV_0 , uvs)
382
+ . with_indices ( Some ( Indices :: U32 ( indices) ) )
383
+ }
384
+ }
385
+
386
+ impl Meshable for Capsule2d {
387
+ type Output = Capsule2dMeshBuilder ;
388
+
389
+ fn mesh ( & self ) -> Self :: Output {
390
+ Capsule2dMeshBuilder {
391
+ capsule : * self ,
392
+ ..Default :: default ( )
393
+ }
394
+ }
395
+ }
396
+
397
+ impl From < Capsule2d > for Mesh {
398
+ fn from ( capsule : Capsule2d ) -> Self {
399
+ capsule. mesh ( ) . build ( )
400
+ }
401
+ }
402
+
403
+ impl From < Capsule2dMeshBuilder > for Mesh {
404
+ fn from ( capsule : Capsule2dMeshBuilder ) -> Self {
405
+ capsule. build ( )
406
+ }
407
+ }
0 commit comments