solstice_2d/
lib.rs

1mod d2;
2mod d3;
3mod shared;
4
5pub use d2::*;
6pub use d3::*;
7pub use shared::*;
8pub use solstice;
9
10use solstice::{
11    image::Image,
12    mesh::{MappedIndexedMesh, MappedVertexMesh},
13    texture::Texture,
14    viewport::Viewport,
15    Context,
16};
17
18pub struct GraphicsLock<'a, 'b> {
19    ctx: &'a mut Context,
20    gfx: &'a mut Graphics,
21    dl: DrawList<'b>,
22}
23
24impl GraphicsLock<'_, '_> {
25    pub fn ctx_mut(&mut self) -> &mut Context {
26        self.ctx
27    }
28
29    pub fn gfx(&self) -> &Graphics {
30        self.gfx
31    }
32}
33
34impl<'b> std::ops::Deref for GraphicsLock<'_, 'b> {
35    type Target = DrawList<'b>;
36
37    fn deref(&self) -> &Self::Target {
38        &self.dl
39    }
40}
41
42impl std::ops::DerefMut for GraphicsLock<'_, '_> {
43    fn deref_mut(&mut self) -> &mut Self::Target {
44        &mut self.dl
45    }
46}
47
48impl std::ops::Drop for GraphicsLock<'_, '_> {
49    fn drop(&mut self) {
50        self.gfx.process(self.ctx, &mut self.dl)
51    }
52}
53
54struct GeometryBuffers {
55    mesh3d: MappedIndexedMesh<Vertex3D, u32>,
56    mesh3d_unindexed: MappedVertexMesh<Vertex3D>,
57    mesh2d: MappedIndexedMesh<Vertex2D, u32>,
58    mesh2d_unindexed: MappedVertexMesh<Vertex2D>,
59}
60
61#[derive(Debug, Default)]
62pub struct Config {
63    pub width: f32,
64    pub height: f32,
65    pub line_capacity: usize,
66    pub mesh_capacity: usize,
67}
68
69pub struct Graphics {
70    meshes: GeometryBuffers,
71    line_workspace: LineWorkspace,
72    default_shader: Shader,
73    default_texture: Image,
74    text_workspace: text::Text,
75    text_shader: Shader,
76    viewport: Viewport<i32>,
77    scissor: Option<Viewport<i32>>,
78    default_projection_bounds: Option<Rectangle>,
79}
80
81impl Graphics {
82    pub fn new(ctx: &mut Context, width: f32, height: f32) -> Result<Self, GraphicsError> {
83        Self::with_config(
84            ctx,
85            &Config {
86                width,
87                height,
88                line_capacity: 10_000,
89                mesh_capacity: 10_000,
90            },
91        )
92    }
93
94    pub fn with_config(ctx: &mut Context, config: &Config) -> Result<Self, GraphicsError> {
95        let mesh2d = MappedIndexedMesh::new(ctx, config.mesh_capacity, config.mesh_capacity)?;
96        let mesh2d_unindexed = MappedVertexMesh::new(ctx, config.mesh_capacity)?;
97        let mesh3d = MappedIndexedMesh::new(ctx, config.mesh_capacity, config.mesh_capacity)?;
98        let mesh3d_unindexed = MappedVertexMesh::new(ctx, config.mesh_capacity)?;
99        let line_workspace = LineWorkspace::with_capacity(ctx, config.line_capacity)?;
100        let default_shader = Shader::new(ctx)?;
101        let default_texture = create_default_texture(ctx)?;
102
103        let text_workspace = text::Text::new(ctx)?;
104        let text_shader = Shader::with((text::DEFAULT_VERT, text::DEFAULT_FRAG), ctx)?;
105
106        let viewport = Viewport::new(0, 0, config.width as _, config.height as _);
107
108        Ok(Self {
109            meshes: GeometryBuffers {
110                mesh3d,
111                mesh3d_unindexed,
112                mesh2d,
113                mesh2d_unindexed,
114            },
115            line_workspace,
116            default_shader,
117            default_texture,
118            text_workspace,
119            text_shader,
120            viewport,
121            scissor: None,
122            default_projection_bounds: None,
123        })
124    }
125
126    pub fn lock<'a>(&'a mut self, ctx: &'a mut Context) -> GraphicsLock<'a, '_> {
127        GraphicsLock {
128            ctx,
129            gfx: self,
130            dl: Default::default(),
131        }
132    }
133
134    pub fn add_font(&mut self, font_data: text::FontVec) -> glyph_brush::FontId {
135        self.text_workspace.add_font(font_data)
136    }
137
138    pub fn set_default_projection_bounds(&mut self, bounds: Option<Rectangle>) {
139        self.default_projection_bounds = bounds;
140    }
141
142    pub fn set_width_height(&mut self, width: f32, height: f32) {
143        self.set_viewport(Viewport::new(0, 0, width as _, height as _))
144    }
145
146    pub fn set_viewport(&mut self, viewport: Viewport<i32>) {
147        self.viewport = viewport;
148    }
149
150    pub fn viewport(&self) -> &Viewport<i32> {
151        &self.viewport
152    }
153
154    pub fn set_scissor(&mut self, scissor: Option<Viewport<i32>>) {
155        self.scissor = scissor;
156    }
157
158    pub fn process(&mut self, ctx: &mut Context, draw_list: &DrawList) {
159        fn canvas_bounds(t: &Canvas) -> Viewport<i32> {
160            let (w, h) = t.dimensions();
161            Viewport::new(0, 0, w as _, h as _)
162        }
163
164        for command in draw_list.commands.iter() {
165            match command {
166                Command::Draw(draw_state) => {
167                    let DrawState {
168                        data: geometry,
169                        transform,
170                        camera,
171                        projection_mode,
172                        color,
173                        texture,
174                        target,
175                        shader,
176                    } = draw_state;
177
178                    let (default_projection_bounds, scissor_state) = if target.is_some() {
179                        (None, None)
180                    } else {
181                        (self.default_projection_bounds, self.scissor)
182                    };
183
184                    match geometry {
185                        GeometryVariants::D2(geometry) => {
186                            let mut shader = shader.clone();
187                            let shader = shader.as_mut().unwrap_or(&mut self.default_shader);
188                            let viewport = target.as_ref().map_or(self.viewport, canvas_bounds);
189                            shader.set_viewport(
190                                *projection_mode,
191                                default_projection_bounds,
192                                viewport,
193                                target.is_some(),
194                            );
195                            shader.set_view(camera);
196                            shader.set_model(*transform);
197                            shader.set_color(*color);
198                            match texture.as_ref() {
199                                None => shader.bind_texture(&self.default_texture),
200                                Some(texture) => shader.bind_texture(texture),
201                            }
202                            shader.activate(ctx);
203                            ctx.set_viewport(
204                                viewport.x() as _,
205                                viewport.y() as _,
206                                viewport.width() as _,
207                                viewport.height() as _,
208                            );
209
210                            let settings = solstice::PipelineSettings {
211                                depth_state: None,
212                                scissor_state,
213                                framebuffer: target.as_ref().map(|c| &c.inner),
214                                ..solstice::PipelineSettings::default()
215                            };
216                            geometry.draw(&mut self.meshes, ctx, shader, settings);
217                        }
218                        GeometryVariants::D3(geometry) => {
219                            let mut shader = shader.clone();
220                            let shader = shader.as_mut().unwrap_or(&mut self.default_shader);
221                            let viewport = target.as_ref().map_or(self.viewport, canvas_bounds);
222                            shader.set_viewport(
223                                *projection_mode,
224                                default_projection_bounds,
225                                viewport,
226                                target.is_some(),
227                            );
228                            shader.set_view(camera);
229                            shader.set_model(*transform);
230                            shader.set_color(draw_state.color);
231                            match texture.as_ref() {
232                                None => shader.bind_texture(&self.default_texture),
233                                Some(texture) => shader.bind_texture(texture),
234                            }
235                            shader.activate(ctx);
236
237                            ctx.set_viewport(
238                                viewport.x(),
239                                viewport.y(),
240                                viewport.width(),
241                                viewport.height(),
242                            );
243
244                            let settings = solstice::PipelineSettings {
245                                scissor_state,
246                                framebuffer: target.as_ref().map(|c| &c.inner),
247                                ..solstice::PipelineSettings::default()
248                            };
249                            geometry.draw(&mut self.meshes, ctx, shader, settings);
250                        }
251                    };
252                }
253                Command::Line(draw_state) => {
254                    let DrawState {
255                        data:
256                            LineState {
257                                geometry,
258                                is_loop,
259                                depth_buffer,
260                            },
261                        transform,
262                        camera,
263                        projection_mode,
264                        color,
265                        texture,
266                        target,
267                        shader,
268                    } = draw_state;
269                    self.line_workspace.add_points(geometry);
270                    if let Some(first) = geometry.first() {
271                        if *is_loop {
272                            self.line_workspace.add_points(&[*first]);
273                        }
274                    }
275
276                    let (default_projection_bounds, scissor_state) = if target.is_some() {
277                        (None, None)
278                    } else {
279                        (self.default_projection_bounds, self.scissor)
280                    };
281
282                    let shader = shader.clone();
283                    let mut shader = shader.unwrap_or_else(|| self.line_workspace.shader().clone());
284                    let viewport = target.as_ref().map_or(self.viewport, canvas_bounds);
285                    shader.set_viewport(
286                        *projection_mode,
287                        default_projection_bounds,
288                        viewport,
289                        target.is_some(),
290                    );
291                    shader.set_view(camera);
292                    shader.set_model(*transform);
293                    match texture.as_ref() {
294                        None => shader.bind_texture(&self.default_texture),
295                        Some(texture) => shader.bind_texture(texture),
296                    }
297                    shader.set_color(*color);
298                    shader.activate(ctx);
299
300                    let geometry = self.line_workspace.geometry(ctx);
301
302                    let depth_state = if *depth_buffer {
303                        Some(solstice::DepthState::default())
304                    } else {
305                        None
306                    };
307
308                    ctx.set_viewport(
309                        viewport.x(),
310                        viewport.y(),
311                        viewport.width(),
312                        viewport.height(),
313                    );
314                    solstice::Renderer::draw(
315                        ctx,
316                        &shader,
317                        &geometry,
318                        solstice::PipelineSettings {
319                            depth_state,
320                            framebuffer: target.as_ref().map(|c| &c.inner),
321                            scissor_state,
322                            ..solstice::PipelineSettings::default()
323                        },
324                    );
325                }
326                Command::Print(state) => {
327                    let DrawState {
328                        data:
329                            PrintState {
330                                text,
331                                font_id,
332                                scale,
333                                bounds,
334                                layout,
335                            },
336                        transform,
337                        camera,
338                        projection_mode,
339                        color,
340                        texture: _,
341                        target,
342                        shader,
343                    } = state;
344                    self.text_workspace.set_text(
345                        glyph_brush::Text {
346                            text,
347                            scale: glyph_brush::ab_glyph::PxScale::from(*scale),
348                            font_id: *font_id,
349                            extra: glyph_brush::Extra {
350                                color: (*color).into(),
351                                z: 0.0,
352                            },
353                        },
354                        *bounds,
355                        layout.into(),
356                        ctx,
357                    );
358
359                    let (default_projection_bounds, scissor_state) = if target.is_some() {
360                        (None, None)
361                    } else {
362                        (self.default_projection_bounds, self.scissor)
363                    };
364
365                    let mut shader = shader.clone();
366                    let shader = shader.as_mut().unwrap_or(&mut self.text_shader);
367                    shader.bind_texture(self.text_workspace.texture());
368                    let viewport = target.as_ref().map_or(self.viewport, canvas_bounds);
369                    shader.set_viewport(
370                        *projection_mode,
371                        default_projection_bounds,
372                        viewport,
373                        target.is_some(),
374                    );
375                    shader.set_view(camera);
376                    shader.set_model(*transform);
377                    shader.set_color(Color::new(1., 1., 1., 1.));
378                    shader.activate(ctx);
379
380                    let geometry = self.text_workspace.geometry(ctx);
381
382                    ctx.set_viewport(
383                        viewport.x(),
384                        viewport.y(),
385                        viewport.width(),
386                        viewport.height(),
387                    );
388                    solstice::Renderer::draw(
389                        ctx,
390                        shader,
391                        &geometry,
392                        solstice::PipelineSettings {
393                            depth_state: None,
394                            scissor_state,
395                            framebuffer: target.as_ref().map(|c| &c.inner),
396                            ..solstice::PipelineSettings::default()
397                        },
398                    );
399                }
400                Command::Clear(color, target) => {
401                    solstice::Renderer::clear(
402                        ctx,
403                        solstice::ClearSettings {
404                            color: Some((*color).into()),
405                            target: target.as_ref().map(|c| &c.inner),
406                            ..solstice::ClearSettings::default()
407                        },
408                    );
409                }
410            }
411        }
412    }
413}
414
415#[derive(Debug, Clone, PartialEq)]
416enum MaybeOwned<'a, T> {
417    Borrowed(&'a [T]),
418    Owned(Vec<T>),
419}
420
421impl<'a, T> std::ops::Deref for MaybeOwned<'a, T> {
422    type Target = [T];
423
424    fn deref(&self) -> &Self::Target {
425        match self {
426            MaybeOwned::Borrowed(v) => v,
427            MaybeOwned::Owned(v) => v,
428        }
429    }
430}
431
432impl<'a, T> From<std::borrow::Cow<'a, [T]>> for MaybeOwned<'a, T>
433where
434    [T]: std::borrow::ToOwned<Owned = Vec<T>>,
435{
436    fn from(v: std::borrow::Cow<'a, [T]>) -> Self {
437        match v {
438            std::borrow::Cow::Borrowed(v) => MaybeOwned::Borrowed(v),
439            std::borrow::Cow::Owned(v) => MaybeOwned::Owned(v),
440        }
441    }
442}
443
444#[derive(Debug, Clone, PartialEq)]
445pub struct Geometry<'a, V, I = u32> {
446    vertices: MaybeOwned<'a, V>,
447    indices: Option<MaybeOwned<'a, I>>,
448}
449
450impl<'a, V, I> Geometry<'a, V, I> {
451    pub fn new<IV: Into<std::borrow::Cow<'a, [V]>>, II: Into<std::borrow::Cow<'a, [I]>>>(
452        vertices: IV,
453        indices: Option<II>,
454    ) -> Self
455    where
456        [V]: std::borrow::ToOwned<Owned = Vec<V>>,
457        [I]: std::borrow::ToOwned<Owned = Vec<I>>,
458    {
459        Self {
460            vertices: vertices.into().into(),
461            indices: indices.map(Into::into).map(Into::into),
462        }
463    }
464}
465
466pub trait Draw<V: solstice::vertex::Vertex, G> {
467    fn draw(&mut self, geometry: G);
468    fn draw_with_transform<TX>(&mut self, geometry: G, transform: TX)
469    where
470        TX: Into<mint::ColumnMatrix4<f32>>;
471    fn draw_with_color<C: Into<Color>>(&mut self, geometry: G, color: C);
472    fn draw_with_color_and_transform<C, TX>(&mut self, geometry: G, color: C, transform: TX)
473    where
474        C: Into<Color>,
475        TX: Into<mint::ColumnMatrix4<f32>>;
476    fn image<T: Texture>(&mut self, geometry: G, texture: T);
477    fn image_with_color<T, C>(&mut self, geometry: G, texture: T, color: C)
478    where
479        T: Texture,
480        C: Into<Color>;
481    fn image_with_transform<T, TX>(&mut self, geometry: G, texture: T, transform: TX)
482    where
483        T: Texture,
484        TX: Into<mint::ColumnMatrix4<f32>>;
485    fn image_with_color_and_transform<T, C, TX>(
486        &mut self,
487        geometry: G,
488        texture: T,
489        color: C,
490        transform: TX,
491    ) where
492        T: Texture,
493        C: Into<Color>,
494        TX: Into<mint::ColumnMatrix4<f32>>;
495}
496pub trait Stroke<V: solstice::vertex::Vertex, G> {
497    fn stroke(&mut self, geometry: G);
498    fn stroke_with_transform<TX>(&mut self, geometry: G, transform: TX)
499    where
500        TX: Into<mint::ColumnMatrix4<f32>>;
501    fn stroke_with_color<C: Into<Color>>(&mut self, geometry: G, color: C);
502    fn stroke_with_color_and_transform<C, TX>(&mut self, geometry: G, color: C, transform: TX)
503    where
504        C: Into<Color>,
505        TX: Into<mint::ColumnMatrix4<f32>>;
506}
507
508#[derive(PartialEq, Clone, Debug)]
509struct TextureCache {
510    ty: solstice::texture::TextureType,
511    key: solstice::TextureKey,
512    info: solstice::texture::TextureInfo,
513}
514
515impl<T> From<T> for TextureCache
516where
517    T: solstice::texture::Texture,
518{
519    fn from(texture: T) -> Self {
520        TextureCache {
521            ty: texture.get_texture_type(),
522            key: texture.get_texture_key(),
523            info: texture.get_texture_info(),
524        }
525    }
526}
527
528impl solstice::texture::Texture for &TextureCache {
529    fn get_texture_key(&self) -> solstice::TextureKey {
530        self.key
531    }
532
533    fn get_texture_type(&self) -> solstice::texture::TextureType {
534        self.ty
535    }
536
537    fn get_texture_info(&self) -> solstice::texture::TextureInfo {
538        self.info
539    }
540}
541
542trait WriteAndDrawBuffer {
543    fn draw<S>(
544        self,
545        meshes: &mut GeometryBuffers,
546        ctx: &mut Context,
547        shader: &S,
548        settings: solstice::PipelineSettings,
549    ) where
550        S: solstice::shader::Shader;
551}
552
553impl WriteAndDrawBuffer for &Geometry<'_, Vertex2D> {
554    fn draw<S>(
555        self,
556        meshes: &mut GeometryBuffers,
557        ctx: &mut Context,
558        shader: &S,
559        settings: solstice::PipelineSettings,
560    ) where
561        S: solstice::shader::Shader,
562    {
563        match &self.indices {
564            None => {
565                meshes.mesh2d_unindexed.set_vertices(&self.vertices, 0);
566                let mesh = meshes.mesh2d_unindexed.unmap(ctx);
567                let geometry = solstice::Geometry {
568                    mesh,
569                    draw_range: 0..self.vertices.len(),
570                    draw_mode: solstice::DrawMode::Triangles,
571                    instance_count: 1,
572                };
573                solstice::Renderer::draw(ctx, shader, &geometry, settings);
574            }
575            Some(indices) => {
576                meshes.mesh2d.set_vertices(&self.vertices, 0);
577                meshes.mesh2d.set_indices(&indices, 0);
578                let mesh = meshes.mesh2d.unmap(ctx);
579                let geometry = solstice::Geometry {
580                    mesh,
581                    draw_range: 0..indices.len(),
582                    draw_mode: solstice::DrawMode::Triangles,
583                    instance_count: 1,
584                };
585                solstice::Renderer::draw(ctx, shader, &geometry, settings);
586            }
587        }
588    }
589}
590
591impl WriteAndDrawBuffer for &Geometry<'_, Vertex3D> {
592    fn draw<S>(
593        self,
594        meshes: &mut GeometryBuffers,
595        ctx: &mut Context,
596        shader: &S,
597        settings: solstice::PipelineSettings,
598    ) where
599        S: solstice::shader::Shader,
600    {
601        match &self.indices {
602            None => {
603                meshes.mesh3d_unindexed.set_vertices(&self.vertices, 0);
604                let mesh = meshes.mesh3d_unindexed.unmap(ctx);
605                let geometry = solstice::Geometry {
606                    mesh,
607                    draw_range: 0..self.vertices.len(),
608                    draw_mode: solstice::DrawMode::Triangles,
609                    instance_count: 1,
610                };
611                solstice::Renderer::draw(ctx, shader, &geometry, settings);
612            }
613            Some(indices) => {
614                meshes.mesh3d.set_vertices(&self.vertices, 0);
615                meshes.mesh3d.set_indices(&indices, 0);
616                let mesh = meshes.mesh3d.unmap(ctx);
617                let geometry = solstice::Geometry {
618                    mesh,
619                    draw_range: 0..indices.len(),
620                    draw_mode: solstice::DrawMode::Triangles,
621                    instance_count: 1,
622                };
623                solstice::Renderer::draw(ctx, shader, &geometry, settings);
624            }
625        }
626    }
627}
628
629#[derive(Debug, Clone)]
630pub enum MeshVariant<'a, V>
631where
632    V: solstice::vertex::Vertex,
633{
634    Data(Geometry<'a, V>),
635    VertexMesh(solstice::Geometry<&'a solstice::mesh::VertexMesh<V>>),
636    IndexedMesh(solstice::Geometry<&'a solstice::mesh::IndexedMesh<V, u32>>),
637    IndexedMeshU16(solstice::Geometry<&'a solstice::mesh::IndexedMesh<V, u16>>),
638    MultiMesh(solstice::Geometry<&'a solstice::mesh::MultiMesh<'a>>),
639}
640
641impl<'a, V> MeshVariant<'a, V>
642where
643    V: solstice::vertex::Vertex,
644{
645    fn draw<S>(
646        &'a self,
647        meshes: &mut GeometryBuffers,
648        ctx: &mut Context,
649        shader: &S,
650        settings: solstice::PipelineSettings,
651    ) where
652        S: solstice::shader::Shader,
653        &'a Geometry<'a, V>: WriteAndDrawBuffer,
654    {
655        use solstice::Renderer;
656        match self {
657            MeshVariant::Data(data) => data.draw(meshes, ctx, shader, settings),
658            MeshVariant::VertexMesh(geometry) => ctx.draw(shader, geometry, settings),
659            MeshVariant::IndexedMesh(geometry) => ctx.draw(shader, geometry, settings),
660            MeshVariant::IndexedMeshU16(geometry) => ctx.draw(shader, geometry, settings),
661            MeshVariant::MultiMesh(geometry) => ctx.draw(shader, &geometry, settings),
662        }
663    }
664}
665
666pub trait GeometryKind<'a, V>: Sized + std::cmp::PartialEq + Into<MeshVariant<'a, V>>
667where
668    V: solstice::vertex::Vertex,
669{
670}
671impl<'a, V, T> GeometryKind<'a, V> for T
672where
673    T: Sized + std::cmp::PartialEq + Into<MeshVariant<'a, V>>,
674    V: solstice::vertex::Vertex,
675{
676}
677
678#[derive(Clone, Debug)]
679pub enum GeometryVariants<'a> {
680    D2(MeshVariant<'a, Vertex2D>),
681    D3(MeshVariant<'a, Vertex3D>),
682}
683
684impl<'a, T> From<T> for MeshVariant<'a, Vertex3D>
685where
686    T: Into<Geometry<'a, Vertex3D>>,
687{
688    fn from(data: T) -> Self {
689        Self::Data(data.into())
690    }
691}
692
693impl<'a, T> From<T> for MeshVariant<'a, Vertex2D>
694where
695    T: Into<Geometry<'a, Vertex2D>>,
696{
697    fn from(data: T) -> Self {
698        Self::Data(data.into())
699    }
700}
701
702impl<'a, V> From<solstice::Geometry<&'a solstice::mesh::VertexMesh<V>>> for MeshVariant<'a, V>
703where
704    V: solstice::vertex::Vertex,
705{
706    fn from(v: solstice::Geometry<&'a solstice::mesh::VertexMesh<V>>) -> Self {
707        Self::VertexMesh(v)
708    }
709}
710
711impl<'a, V> From<solstice::Geometry<&'a solstice::mesh::IndexedMesh<V, u32>>> for MeshVariant<'a, V>
712where
713    V: solstice::vertex::Vertex,
714{
715    fn from(v: solstice::Geometry<&'a solstice::mesh::IndexedMesh<V, u32>>) -> Self {
716        Self::IndexedMesh(v)
717    }
718}
719
720impl<'a, V> From<solstice::Geometry<&'a solstice::mesh::IndexedMesh<V, u16>>> for MeshVariant<'a, V>
721where
722    V: solstice::vertex::Vertex,
723{
724    fn from(v: solstice::Geometry<&'a solstice::mesh::IndexedMesh<V, u16>>) -> Self {
725        Self::IndexedMeshU16(v)
726    }
727}
728
729impl<'a, V> From<solstice::Geometry<&'a solstice::mesh::MultiMesh<'a>>> for MeshVariant<'a, V>
730where
731    V: solstice::vertex::Vertex,
732{
733    fn from(v: solstice::Geometry<&'a solstice::mesh::MultiMesh<'a>>) -> Self {
734        Self::MultiMesh(v)
735    }
736}
737
738#[derive(Clone, Debug)]
739pub struct DrawState<T> {
740    data: T,
741    transform: mint::ColumnMatrix4<f32>,
742    camera: Transform3D,
743    projection_mode: Projection,
744    color: Color,
745    texture: Option<TextureCache>,
746    target: Option<Canvas>,
747    shader: Option<Shader>,
748}
749
750#[derive(Clone, Debug)]
751pub struct LineState<'a> {
752    geometry: std::borrow::Cow<'a, [LineVertex]>,
753    is_loop: bool,
754    depth_buffer: bool,
755}
756
757#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
758pub enum HorizontalAlign {
759    Left,
760    Center,
761    Right,
762}
763
764impl From<&HorizontalAlign> for glyph_brush::HorizontalAlign {
765    fn from(align: &HorizontalAlign) -> Self {
766        match align {
767            HorizontalAlign::Left => glyph_brush::HorizontalAlign::Left,
768            HorizontalAlign::Center => glyph_brush::HorizontalAlign::Center,
769            HorizontalAlign::Right => glyph_brush::HorizontalAlign::Right,
770        }
771    }
772}
773
774#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
775pub enum VerticalAlign {
776    Top,
777    Center,
778    Bottom,
779}
780
781impl From<&VerticalAlign> for glyph_brush::VerticalAlign {
782    fn from(align: &VerticalAlign) -> Self {
783        match align {
784            VerticalAlign::Top => glyph_brush::VerticalAlign::Top,
785            VerticalAlign::Center => glyph_brush::VerticalAlign::Center,
786            VerticalAlign::Bottom => glyph_brush::VerticalAlign::Bottom,
787        }
788    }
789}
790
791#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
792pub enum PrintLayout {
793    SingleLine {
794        h_align: HorizontalAlign,
795        v_align: VerticalAlign,
796    },
797    Wrap {
798        h_align: HorizontalAlign,
799        v_align: VerticalAlign,
800    },
801}
802
803impl std::default::Default for PrintLayout {
804    fn default() -> Self {
805        PrintLayout::Wrap {
806            h_align: HorizontalAlign::Left,
807            v_align: VerticalAlign::Top,
808        }
809    }
810}
811
812impl From<&PrintLayout> for glyph_brush::Layout<glyph_brush::BuiltInLineBreaker> {
813    fn from(layout: &PrintLayout) -> Self {
814        match layout {
815            PrintLayout::SingleLine { h_align, v_align } => glyph_brush::Layout::SingleLine {
816                line_breaker: glyph_brush::BuiltInLineBreaker::default(),
817                h_align: h_align.into(),
818                v_align: v_align.into(),
819            },
820            PrintLayout::Wrap { h_align, v_align } => glyph_brush::Layout::Wrap {
821                line_breaker: glyph_brush::BuiltInLineBreaker::default(),
822                h_align: h_align.into(),
823                v_align: v_align.into(),
824            },
825        }
826    }
827}
828
829#[derive(Clone, Debug)]
830pub struct PrintState<'a> {
831    text: std::borrow::Cow<'a, str>,
832    font_id: glyph_brush::FontId,
833    scale: f32,
834    bounds: d2::Rectangle,
835    layout: PrintLayout,
836}
837
838#[derive(Clone, Debug)]
839pub enum Command<'a> {
840    Draw(DrawState<GeometryVariants<'a>>),
841    Print(DrawState<PrintState<'a>>),
842    Line(DrawState<LineState<'a>>),
843    Clear(Color, Option<Canvas>),
844}
845
846#[derive(Clone, Debug)]
847pub struct DrawList<'a> {
848    commands: Vec<Command<'a>>,
849    color: Color,
850    transform: mint::ColumnMatrix4<f32>,
851    line_width: f32,
852    camera: Transform3D,
853    projection_mode: Option<Projection>,
854    target: Option<Canvas>,
855    shader: Option<Shader>,
856}
857
858impl Default for DrawList<'_> {
859    fn default() -> Self {
860        Self {
861            commands: vec![],
862            color: Default::default(),
863            transform: Transform3D::default().into(),
864            line_width: 1.0,
865            camera: Default::default(),
866            projection_mode: None,
867            target: None,
868            shader: None,
869        }
870    }
871}
872
873impl<'a> DrawList<'a> {
874    pub fn new() -> Self {
875        Self::default()
876    }
877
878    pub fn new_from_state<'b>(other: &DrawList) -> DrawList<'b> {
879        DrawList {
880            commands: vec![],
881            color: other.color,
882            transform: other.transform,
883            line_width: 1.0,
884            camera: other.camera,
885            projection_mode: other.projection_mode,
886            target: other.target.clone(),
887            shader: other.shader.clone(),
888        }
889    }
890
891    pub fn append(&mut self, other: &mut Self) {
892        self.commands.append(&mut other.commands);
893    }
894
895    pub fn clear<C: Into<Color>>(&mut self, color: C) {
896        let command = Command::Clear(color.into(), self.target.clone());
897        self.commands.push(command)
898    }
899
900    pub fn print<T>(&mut self, text: T, font_id: glyph_brush::FontId, scale: f32, bounds: Rectangle)
901    where
902        T: Into<std::borrow::Cow<'a, str>>,
903    {
904        let command = Command::Print(DrawState {
905            data: PrintState {
906                text: text.into(),
907                font_id,
908                scale,
909                bounds,
910                layout: Default::default(),
911            },
912            transform: self.transform.into(),
913            camera: self.camera,
914            projection_mode: self
915                .projection_mode
916                .unwrap_or(Projection::Orthographic(None)),
917            color: self.color,
918            texture: None,
919            target: self.target.clone(),
920            shader: self.shader.clone(),
921        });
922        self.commands.push(command);
923    }
924
925    pub fn print_with_layout<T>(
926        &mut self,
927        text: T,
928        font_id: glyph_brush::FontId,
929        scale: f32,
930        bounds: Rectangle,
931        layout: PrintLayout,
932    ) where
933        T: Into<std::borrow::Cow<'a, str>>,
934    {
935        let command = Command::Print(DrawState {
936            data: PrintState {
937                text: text.into(),
938                font_id,
939                scale,
940                bounds,
941                layout,
942            },
943            transform: self.transform.into(),
944            camera: self.camera,
945            projection_mode: self
946                .projection_mode
947                .unwrap_or(Projection::Orthographic(None)),
948            color: self.color,
949            texture: None,
950            target: self.target.clone(),
951            shader: self.shader.clone(),
952        });
953        self.commands.push(command);
954    }
955
956    pub fn line_2d<G>(&mut self, points: G)
957    where
958        G: Into<std::borrow::Cow<'a, [LineVertex]>>,
959    {
960        let command = Command::Line(DrawState {
961            data: LineState {
962                geometry: points.into(),
963                is_loop: false,
964                depth_buffer: false,
965            },
966            transform: self.transform.into(),
967            camera: self.camera,
968            projection_mode: self
969                .projection_mode
970                .unwrap_or(Projection::Orthographic(None)),
971            color: self.color,
972            texture: None,
973            target: self.target.clone(),
974            shader: self.shader.clone(),
975        });
976        self.commands.push(command)
977    }
978
979    pub fn line_3d<G>(&mut self, points: G)
980    where
981        G: Into<std::borrow::Cow<'a, [LineVertex]>>,
982    {
983        let command = Command::Line(DrawState {
984            data: LineState {
985                geometry: points.into(),
986                is_loop: false,
987                depth_buffer: true,
988            },
989            transform: self.transform.into(),
990            camera: self.camera,
991            projection_mode: self
992                .projection_mode
993                .unwrap_or(Projection::Perspective(None)),
994            color: self.color,
995            texture: None,
996            target: self.target.clone(),
997            shader: self.shader.clone(),
998        });
999        self.commands.push(command)
1000    }
1001
1002    pub fn set_color<C: Into<Color>>(&mut self, color: C) {
1003        self.color = color.into();
1004    }
1005
1006    pub fn set_transform<T: Into<mint::ColumnMatrix4<f32>>>(&mut self, transform: T) {
1007        self.transform = transform.into();
1008    }
1009
1010    pub fn set_line_width(&mut self, line_width: f32) {
1011        self.line_width = line_width;
1012    }
1013
1014    pub fn set_camera<T: Into<Transform3D>>(&mut self, camera: T) {
1015        self.camera = camera.into();
1016    }
1017
1018    pub fn set_projection_mode(&mut self, projection_mode: Option<Projection>) {
1019        self.projection_mode = projection_mode;
1020    }
1021
1022    pub fn set_canvas(&mut self, target: Option<Canvas>) {
1023        self.target = target;
1024    }
1025
1026    pub fn set_shader(&mut self, shader: Option<Shader>) {
1027        self.shader = shader;
1028    }
1029}
1030
1031impl<'a> DrawList<'a> {
1032    fn push_draw(
1033        &mut self,
1034        data: GeometryVariants<'a>,
1035        color: Color,
1036        transform: mint::ColumnMatrix4<f32>,
1037        texture: Option<TextureCache>,
1038    ) {
1039        let projection_mode = self.projection_mode.unwrap_or_else(|| match &data {
1040            GeometryVariants::D2(_) => Projection::Orthographic(None),
1041            GeometryVariants::D3(_) => Projection::Perspective(None),
1042        });
1043        self.commands.push(Command::Draw(DrawState {
1044            data,
1045            transform,
1046            camera: self.camera,
1047            projection_mode,
1048            color,
1049            texture,
1050            target: self.target.clone(),
1051            shader: self.shader.clone(),
1052        }))
1053    }
1054}
1055
1056type ImageResult = Result<solstice::image::Image, solstice::GraphicsError>;
1057
1058pub fn create_default_texture(gl: &mut solstice::Context) -> ImageResult {
1059    use solstice::image::*;
1060    use solstice::texture::*;
1061    Image::with_data(
1062        gl,
1063        TextureType::Tex2D,
1064        solstice::PixelFormat::RGBA8,
1065        1,
1066        1,
1067        &[255, 255, 255, 255],
1068        Settings {
1069            mipmaps: false,
1070            filter: FilterMode::Nearest,
1071            wrap: WrapMode::Clamp,
1072            ..Settings::default()
1073        },
1074    )
1075}