@@ -2,11 +2,13 @@ pub mod command;
22pub mod material;
33pub mod mesh_builder;
44pub mod primitive;
5+ pub mod transform;
56
6- use bevy:: { camera:: visibility:: RenderLayers , ecs:: system:: SystemParam , prelude:: * } ;
7+ use bevy:: { camera:: visibility:: RenderLayers , ecs:: system:: SystemParam , math :: Affine3A , prelude:: * } ;
78use command:: { CommandBuffer , DrawCommand } ;
89use material:: MaterialKey ;
910use primitive:: { TessellationMode , empty_mesh} ;
11+ use transform:: TransformStack ;
1012
1113use crate :: { Flush , graphics:: SurfaceSize , image:: Image , render:: primitive:: rect} ;
1214
@@ -27,21 +29,36 @@ pub struct RenderContext<'w, 's> {
2729 state : Local < ' s , RenderState > ,
2830}
2931
30- #[ derive( Default ) ]
3132struct BatchState {
3233 current_mesh : Option < Mesh > ,
3334 material_key : Option < MaterialKey > ,
35+ transform : Affine3A ,
3436 draw_index : u32 ,
3537 render_layers : RenderLayers ,
3638 graphics_entity : Option < Entity > ,
3739}
3840
41+ impl Default for BatchState {
42+ fn default ( ) -> Self {
43+ Self {
44+ current_mesh : None ,
45+ material_key : None ,
46+ transform : Affine3A :: IDENTITY ,
47+ draw_index : 0 ,
48+ render_layers : RenderLayers :: default ( ) ,
49+ graphics_entity : None ,
50+ }
51+ }
52+ }
53+
3954#[ derive( Debug ) ]
4055pub struct RenderState {
4156 // drawing state
4257 pub fill_color : Option < Color > ,
4358 pub stroke_color : Option < Color > ,
4459 pub stroke_weight : f32 ,
60+ // transform state
61+ pub transform : TransformStack ,
4562}
4663
4764impl Default for RenderState {
@@ -50,6 +67,7 @@ impl Default for RenderState {
5067 fill_color : Some ( Color :: WHITE ) ,
5168 stroke_color : Some ( Color :: BLACK ) ,
5269 stroke_weight : 1.0 ,
70+ transform : TransformStack :: new ( ) ,
5371 }
5472 }
5573}
@@ -172,6 +190,26 @@ pub fn flush_draw_commands(
172190
173191 flush_batch ( & mut ctx) ;
174192 }
193+ DrawCommand :: PushMatrix => ctx. state . transform . push ( ) ,
194+ DrawCommand :: PopMatrix => ctx. state . transform . pop ( ) ,
195+ DrawCommand :: ResetMatrix => {
196+ ctx. state . transform . reset ( ) ;
197+ }
198+ DrawCommand :: Translate { x, y } => {
199+ ctx. state . transform . translate ( x, y) ;
200+ }
201+ DrawCommand :: Rotate { angle } => {
202+ ctx. state . transform . rotate ( angle) ;
203+ }
204+ DrawCommand :: Scale { x, y } => {
205+ ctx. state . transform . scale ( x, y) ;
206+ }
207+ DrawCommand :: ShearX { angle } => {
208+ ctx. state . transform . shear_x ( angle) ;
209+ }
210+ DrawCommand :: ShearY { angle } => {
211+ ctx. state . transform . shear_y ( angle) ;
212+ }
175213 }
176214 }
177215
@@ -207,15 +245,38 @@ fn spawn_mesh(ctx: &mut RenderContext, mesh: Mesh, z_offset: f32) {
207245 let mesh_handle = ctx. meshes . add ( mesh) ;
208246 let material_handle = ctx. materials . add ( material_key. to_material ( ) ) ;
209247
248+ let ( scale, rotation, translation) = ctx. batch . transform . to_scale_rotation_translation ( ) ;
249+ let transform = Transform {
250+ translation : translation + Vec3 :: new ( 0.0 , 0.0 , z_offset) ,
251+ rotation,
252+ scale,
253+ } ;
254+
210255 ctx. commands . spawn ( (
211256 Mesh3d ( mesh_handle) ,
212257 MeshMaterial3d ( material_handle) ,
213258 BelongsToGraphics ( surface_entity) ,
214- Transform :: from_xyz ( 0.0 , 0.0 , z_offset ) ,
259+ transform ,
215260 ctx. batch . render_layers . clone ( ) ,
216261 ) ) ;
217262}
218263
264+ fn maybe_start_batch ( ctx : & mut RenderContext , material_key : MaterialKey ) -> bool {
265+ let current_transform = ctx. state . transform . current ( ) ;
266+ let material_changed = ctx. batch . material_key . as_ref ( ) != Some ( & material_key) ;
267+ let transform_changed = ctx. batch . transform != current_transform;
268+
269+ if material_changed || transform_changed {
270+ flush_batch ( ctx) ;
271+ ctx. batch . material_key = Some ( material_key) ;
272+ ctx. batch . transform = current_transform;
273+ ctx. batch . current_mesh = Some ( empty_mesh ( ) ) ;
274+ true
275+ } else {
276+ false
277+ }
278+ }
279+
219280fn add_fill ( ctx : & mut RenderContext , tessellate : impl FnOnce ( & mut Mesh , Color ) ) {
220281 let Some ( color) = ctx. state . fill_color else {
221282 return ;
@@ -225,12 +286,7 @@ fn add_fill(ctx: &mut RenderContext, tessellate: impl FnOnce(&mut Mesh, Color))
225286 background_image : None ,
226287 } ;
227288
228- // when the material changes, flush the current batch
229- if ctx. batch . material_key . as_ref ( ) != Some ( & material_key) {
230- flush_batch ( ctx) ;
231- ctx. batch . material_key = Some ( material_key) ;
232- ctx. batch . current_mesh = Some ( empty_mesh ( ) ) ;
233- }
289+ maybe_start_batch ( ctx, material_key) ;
234290
235291 // accumulate geometry into the current mega mesh
236292 if let Some ( ref mut mesh) = ctx. batch . current_mesh {
@@ -248,12 +304,7 @@ fn add_stroke(ctx: &mut RenderContext, tessellate: impl FnOnce(&mut Mesh, Color,
248304 background_image : None ,
249305 } ;
250306
251- // when the material changes, flush the current batch
252- if ctx. batch . material_key . as_ref ( ) != Some ( & material_key) {
253- flush_batch ( ctx) ;
254- ctx. batch . material_key = Some ( material_key) ;
255- ctx. batch . current_mesh = Some ( empty_mesh ( ) ) ;
256- }
307+ maybe_start_batch ( ctx, material_key) ;
257308
258309 // accumulate geometry into the current mega mesh
259310 if let Some ( ref mut mesh) = ctx. batch . current_mesh {
0 commit comments