@@ -26,15 +26,15 @@ use bevy_render::{
26
26
view:: ViewUniforms ,
27
27
RenderApp , RenderStage , RenderWorld ,
28
28
} ;
29
- use bevy_sprite:: { SpriteAssetEvents , TextureAtlas } ;
29
+ use bevy_sprite:: { Rect , SpriteAssetEvents , TextureAtlas } ;
30
30
use bevy_text:: { DefaultTextPipeline , Text } ;
31
31
use bevy_transform:: components:: GlobalTransform ;
32
32
use bevy_utils:: HashMap ;
33
33
use bevy_window:: Windows ;
34
34
35
35
use bytemuck:: { Pod , Zeroable } ;
36
36
37
- use crate :: { Node , UiColor , UiImage } ;
37
+ use crate :: { CalculatedClip , Node , UiColor , UiImage } ;
38
38
39
39
pub mod node {
40
40
pub const UI_PASS_DRIVER : & str = "ui_pass_driver" ;
@@ -120,9 +120,10 @@ pub fn build_ui_render(app: &mut App) {
120
120
pub struct ExtractedUiNode {
121
121
pub transform : Mat4 ,
122
122
pub color : Color ,
123
- pub rect : bevy_sprite :: Rect ,
123
+ pub rect : Rect ,
124
124
pub image : Handle < Image > ,
125
125
pub atlas_size : Option < Vec2 > ,
126
+ pub clip : Option < Rect > ,
126
127
}
127
128
128
129
#[ derive( Default ) ]
@@ -133,11 +134,17 @@ pub struct ExtractedUiNodes {
133
134
pub fn extract_uinodes (
134
135
mut render_world : ResMut < RenderWorld > ,
135
136
images : Res < Assets < Image > > ,
136
- uinode_query : Query < ( & Node , & GlobalTransform , & UiColor , & UiImage ) > ,
137
+ uinode_query : Query < (
138
+ & Node ,
139
+ & GlobalTransform ,
140
+ & UiColor ,
141
+ & UiImage ,
142
+ Option < & CalculatedClip > ,
143
+ ) > ,
137
144
) {
138
145
let mut extracted_uinodes = render_world. get_resource_mut :: < ExtractedUiNodes > ( ) . unwrap ( ) ;
139
146
extracted_uinodes. uinodes . clear ( ) ;
140
- for ( uinode, transform, color, image) in uinode_query. iter ( ) {
147
+ for ( uinode, transform, color, image, clip ) in uinode_query. iter ( ) {
141
148
let image = image. 0 . clone_weak ( ) ;
142
149
// Skip loading images
143
150
if !images. contains ( image. clone_weak ( ) ) {
@@ -152,6 +159,7 @@ pub fn extract_uinodes(
152
159
} ,
153
160
image,
154
161
atlas_size : None ,
162
+ clip : clip. map ( |clip| clip. clip ) ,
155
163
} ) ;
156
164
}
157
165
}
@@ -161,7 +169,13 @@ pub fn extract_text_uinodes(
161
169
texture_atlases : Res < Assets < TextureAtlas > > ,
162
170
text_pipeline : Res < DefaultTextPipeline > ,
163
171
windows : Res < Windows > ,
164
- uinode_query : Query < ( Entity , & Node , & GlobalTransform , & Text ) > ,
172
+ uinode_query : Query < (
173
+ Entity ,
174
+ & Node ,
175
+ & GlobalTransform ,
176
+ & Text ,
177
+ Option < & CalculatedClip > ,
178
+ ) > ,
165
179
) {
166
180
let mut extracted_uinodes = render_world. get_resource_mut :: < ExtractedUiNodes > ( ) . unwrap ( ) ;
167
181
@@ -171,7 +185,7 @@ pub fn extract_text_uinodes(
171
185
1.
172
186
} ;
173
187
174
- for ( entity, uinode, transform, text) in uinode_query. iter ( ) {
188
+ for ( entity, uinode, transform, text, clip ) in uinode_query. iter ( ) {
175
189
// Skip if size is set to zero (e.g. when a parent is set to `Display::None`)
176
190
if uinode. size == Vec2 :: ZERO {
177
191
continue ;
@@ -203,6 +217,7 @@ pub fn extract_text_uinodes(
203
217
rect,
204
218
image : texture,
205
219
atlas_size,
220
+ clip : clip. map ( |clip| clip. clip ) ,
206
221
} ) ;
207
222
}
208
223
}
@@ -231,15 +246,15 @@ impl Default for UiMeta {
231
246
}
232
247
}
233
248
234
- const QUAD_VERTEX_POSITIONS : & [ Vec3 ] = & [
235
- const_vec3 ! ( [ -0.5 , -0.5 , 0.0 ] ) ,
236
- const_vec3 ! ( [ 0.5 , 0.5 , 0.0 ] ) ,
237
- const_vec3 ! ( [ -0.5 , 0.5 , 0.0 ] ) ,
249
+ const QUAD_VERTEX_POSITIONS : [ Vec3 ; 4 ] = [
238
250
const_vec3 ! ( [ -0.5 , -0.5 , 0.0 ] ) ,
239
251
const_vec3 ! ( [ 0.5 , -0.5 , 0.0 ] ) ,
240
252
const_vec3 ! ( [ 0.5 , 0.5 , 0.0 ] ) ,
253
+ const_vec3 ! ( [ -0.5 , 0.5 , 0.0 ] ) ,
241
254
] ;
242
255
256
+ const QUAD_INDICES : [ usize ; 6 ] = [ 0 , 2 , 3 , 0 , 1 , 2 ] ;
257
+
243
258
#[ derive( Component ) ]
244
259
pub struct UiBatch {
245
260
pub range : Range < u32 > ,
@@ -279,47 +294,90 @@ pub fn prepare_uinodes(
279
294
}
280
295
281
296
let uinode_rect = extracted_uinode. rect ;
297
+ let rect_size = uinode_rect. size ( ) . extend ( 1.0 ) ;
282
298
283
299
// Specify the corners of the node
284
- let mut bottom_left = Vec2 :: new ( uinode_rect. min . x , uinode_rect. max . y ) ;
285
- let mut top_left = uinode_rect. min ;
286
- let mut top_right = Vec2 :: new ( uinode_rect. max . x , uinode_rect. min . y ) ;
287
- let mut bottom_right = uinode_rect. max ;
300
+ let positions = QUAD_VERTEX_POSITIONS
301
+ . map ( |pos| ( extracted_uinode. transform * ( pos * rect_size) . extend ( 1. ) ) . xyz ( ) ) ;
302
+
303
+ // Calculate the effect of clipping
304
+ // Note: this won't work with rotation/scaling, but that's much more complex (may need more that 2 quads)
305
+ let positions_diff = if let Some ( clip) = extracted_uinode. clip {
306
+ [
307
+ Vec2 :: new (
308
+ f32:: max ( clip. min . x - positions[ 0 ] . x , 0. ) ,
309
+ f32:: max ( clip. min . y - positions[ 0 ] . y , 0. ) ,
310
+ ) ,
311
+ Vec2 :: new (
312
+ f32:: min ( clip. max . x - positions[ 1 ] . x , 0. ) ,
313
+ f32:: max ( clip. min . y - positions[ 1 ] . y , 0. ) ,
314
+ ) ,
315
+ Vec2 :: new (
316
+ f32:: min ( clip. max . x - positions[ 2 ] . x , 0. ) ,
317
+ f32:: min ( clip. max . y - positions[ 2 ] . y , 0. ) ,
318
+ ) ,
319
+ Vec2 :: new (
320
+ f32:: max ( clip. min . x - positions[ 3 ] . x , 0. ) ,
321
+ f32:: min ( clip. max . y - positions[ 3 ] . y , 0. ) ,
322
+ ) ,
323
+ ]
324
+ } else {
325
+ [ Vec2 :: ZERO ; 4 ]
326
+ } ;
288
327
289
- let atlas_extent = extracted_uinode. atlas_size . unwrap_or ( uinode_rect. max ) ;
290
- bottom_left /= atlas_extent;
291
- bottom_right /= atlas_extent;
292
- top_left /= atlas_extent;
293
- top_right /= atlas_extent;
294
-
295
- let uvs: [ [ f32 ; 2 ] ; 6 ] = [
296
- bottom_left. into ( ) ,
297
- top_right. into ( ) ,
298
- top_left. into ( ) ,
299
- bottom_left. into ( ) ,
300
- bottom_right. into ( ) ,
301
- top_right. into ( ) ,
328
+ let positions_clipped = [
329
+ positions[ 0 ] + positions_diff[ 0 ] . extend ( 0. ) ,
330
+ positions[ 1 ] + positions_diff[ 1 ] . extend ( 0. ) ,
331
+ positions[ 2 ] + positions_diff[ 2 ] . extend ( 0. ) ,
332
+ positions[ 3 ] + positions_diff[ 3 ] . extend ( 0. ) ,
302
333
] ;
303
334
304
- let rect_size = extracted_uinode. rect . size ( ) . extend ( 1.0 ) ;
335
+ // Cull nodes that are completely clipped
336
+ if positions_diff[ 0 ] . x - positions_diff[ 1 ] . x >= rect_size. x
337
+ || positions_diff[ 1 ] . y - positions_diff[ 2 ] . y >= rect_size. y
338
+ {
339
+ continue ;
340
+ }
341
+
342
+ // Clip UVs (Note: y is reversed in UV space)
343
+ let atlas_extent = extracted_uinode. atlas_size . unwrap_or ( uinode_rect. max ) ;
344
+ let uvs = [
345
+ Vec2 :: new (
346
+ uinode_rect. min . x + positions_diff[ 0 ] . x ,
347
+ uinode_rect. max . y - positions_diff[ 0 ] . y ,
348
+ ) ,
349
+ Vec2 :: new (
350
+ uinode_rect. max . x + positions_diff[ 1 ] . x ,
351
+ uinode_rect. max . y - positions_diff[ 1 ] . y ,
352
+ ) ,
353
+ Vec2 :: new (
354
+ uinode_rect. max . x + positions_diff[ 2 ] . x ,
355
+ uinode_rect. min . y - positions_diff[ 2 ] . y ,
356
+ ) ,
357
+ Vec2 :: new (
358
+ uinode_rect. min . x + positions_diff[ 3 ] . x ,
359
+ uinode_rect. min . y - positions_diff[ 3 ] . y ,
360
+ ) ,
361
+ ]
362
+ . map ( |pos| pos / atlas_extent) ;
363
+
305
364
let color = extracted_uinode. color . as_linear_rgba_f32 ( ) ;
306
365
// encode color as a single u32 to save space
307
366
let color = ( color[ 0 ] * 255.0 ) as u32
308
367
| ( ( color[ 1 ] * 255.0 ) as u32 ) << 8
309
368
| ( ( color[ 2 ] * 255.0 ) as u32 ) << 16
310
369
| ( ( color[ 3 ] * 255.0 ) as u32 ) << 24 ;
311
- for ( index, vertex_position) in QUAD_VERTEX_POSITIONS . iter ( ) . enumerate ( ) {
312
- let mut final_position = * vertex_position * rect_size;
313
- final_position = ( extracted_uinode. transform * final_position. extend ( 1.0 ) ) . xyz ( ) ;
370
+
371
+ for i in QUAD_INDICES {
314
372
ui_meta. vertices . push ( UiVertex {
315
- position : final_position . into ( ) ,
316
- uv : uvs[ index ] ,
373
+ position : positions_clipped [ i ] . into ( ) ,
374
+ uv : uvs[ i ] . into ( ) ,
317
375
color,
318
376
} ) ;
319
377
}
320
378
321
379
last_z = extracted_uinode. transform . w_axis [ 2 ] ;
322
- end += QUAD_VERTEX_POSITIONS . len ( ) as u32 ;
380
+ end += QUAD_INDICES . len ( ) as u32 ;
323
381
}
324
382
325
383
// if start != end, there is one last batch to process
0 commit comments