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}