A hardware-accelerated 2D sprite renderer. https://crates.io/crates/fae
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

176 lines
6.2 KiB

  1. use crate::gl_version::OpenGlVersion;
  2. use crate::renderer::Renderer;
  3. #[cfg(feature = "text")]
  4. use crate::text::TextRenderer;
  5. /// The overarching state of the crate. Intended to live outside of
  6. /// the main game loop.
  7. ///
  8. /// This is the struct you can get a GraphicsContext from, and which
  9. /// should live as long as you're drawing anything. Illustrated:
  10. /// ```no_run
  11. /// use fae::Context;
  12. /// let mut fae_context: Context = Context::new();
  13. /// # let (width, height, dpi_factor) = (0.0, 0.0, 0.0);
  14. /// # let spritesheet = fae::SpritesheetBuilder::default().build(&mut fae_context);
  15. ///
  16. /// loop {
  17. /// // Here's your gameloop, and now you want to draw something.
  18. ///
  19. /// // First, create the GraphicsContext with start_frame.
  20. /// let mut ctx: fae::GraphicsContext = fae_context.start_frame(width, height, dpi_factor);
  21. ///
  22. /// // Then do your rendering stuff.
  23. /// spritesheet.draw(&mut ctx)
  24. /// /* ... */
  25. /// .finish();
  26. ///
  27. /// // Finish frame and consume the GraphicsContext.
  28. /// ctx.finish_frame();
  29. ///
  30. /// // swap buffers, fae_context.synchronize(), etc.
  31. /// }
  32. /// ```
  33. ///
  34. /// This construct makes the state of fae more clear, as you can only
  35. /// have access to either the Context or the GraphicsContext, as
  36. /// well as providing a good synchronization point (start_frame) where
  37. /// the window's state is passed to fae, ensuring that all rendering
  38. /// operations are done based on up-to-date information.
  39. pub struct Context {
  40. pub(crate) renderer: Renderer,
  41. #[cfg(feature = "text")]
  42. pub(crate) text_renderers: Vec<TextRenderer>,
  43. }
  44. impl Context {
  45. /// Creates a new GraphicsContext. See the Safety section.
  46. ///
  47. /// # Safety
  48. ///
  49. /// Basically everything in fae assumes that it can call OpenGL,
  50. /// so please ensure you have called something along the lines of:
  51. ///
  52. /// ```ignore
  53. /// unsafe { fae::gl::load_with(|symbol| context.get_proc_address(symbol) as *const _); }
  54. /// ```
  55. ///
  56. /// Before creating a Context.
  57. ///
  58. /// The width, height and dpi_factor are only initial values; they
  59. /// are updated in the call to
  60. /// [`Context::start_frame()`](struct.Context.html#method.start_frame).
  61. pub fn new() -> Context {
  62. Context {
  63. renderer: Renderer::new(),
  64. #[cfg(feature = "text")]
  65. text_renderers: Vec::new(),
  66. }
  67. }
  68. /// Returns true when running in legacy mode (OpenGL 3.3+
  69. /// optimizations off).
  70. pub fn is_legacy(&self) -> bool {
  71. self.renderer.legacy
  72. }
  73. /// Returns the OpenGL version if it could be parsed.
  74. pub fn get_opengl_version(&self) -> &OpenGlVersion {
  75. &self.renderer.version
  76. }
  77. /// Tries to ensure that all the commands queued in the GPU have been processed.
  78. ///
  79. /// Call this after swap_buffers to ensure that everything after
  80. /// this happens only after the frame has been sent to the screen,
  81. /// but don't trust this to actually work. Doing vsync properly
  82. /// with OpenGL is a mess, as far as I know.
  83. pub fn synchronize(&mut self) {
  84. self.renderer.synchronize();
  85. }
  86. /// Creates a GraphicsContext for this frame.
  87. ///
  88. /// The parameters `width` and `height` are the dimensions of the
  89. /// window, and dpi_factor is a multiplier, such that: `width *
  90. /// dpi_factor` is the window's width in physical pixels, and
  91. /// `height * dpi_factor` is the height in physical pixels.
  92. pub fn start_frame(&mut self, width: f32, height: f32, dpi_factor: f32) -> GraphicsContext {
  93. self.renderer.prepare_new_frame(dpi_factor);
  94. #[cfg(feature = "text")]
  95. for font in &mut self.text_renderers {
  96. font.prepare_new_frame(&mut self.renderer, dpi_factor, width, height);
  97. }
  98. GraphicsContext {
  99. renderer: &mut self.renderer,
  100. #[cfg(feature = "text")]
  101. text_renderers: &mut self.text_renderers,
  102. width,
  103. height,
  104. dpi_factor,
  105. }
  106. }
  107. /// Renders the frame with the given `width`, `height` and
  108. /// `clear_color`.
  109. ///
  110. /// The `clear_color` is defined between 0.0 and 1.0, and the
  111. /// components are (red, green, blue, alpha). None if you don't
  112. /// want to clear the screen.
  113. ///
  114. /// See
  115. /// [`Context::start_frame`](struct.Context.html#method.start_frame)
  116. /// for more information on what `width` and `height` are,
  117. /// specifically.
  118. ///
  119. /// This should generally be called after
  120. /// [`GraphicsContext::finish_frame`](struct.GraphicsContext.html#method.finish_frame),
  121. /// but can also be used to redraw the previous frame.
  122. pub fn render(&mut self, width: f32, height: f32, clear_color: Option<(f32, f32, f32, f32)>) {
  123. self.renderer.render(width, height, clear_color);
  124. }
  125. }
  126. /// Draw stuff on the screen with this.
  127. ///
  128. /// Create this struct with
  129. /// [`Context::start_frame()`](struct.Context.html#method.start_frame).
  130. ///
  131. /// Then, pass it to:
  132. /// - [`Spritesheet::draw`](struct.Spritesheet.html#method.draw) to draw sprites,
  133. /// - [`Font::draw`](struct.Font.html#method.draw) to draw text.
  134. ///
  135. /// And after doing all the drawing, call
  136. /// [`GraphicsContext::finish_frame()`](struct.GraphicsContext.html#method.finish_frame)
  137. /// to flush all the rendering operations.
  138. pub struct GraphicsContext<'a> {
  139. pub(crate) renderer: &'a mut Renderer,
  140. #[cfg(feature = "text")]
  141. pub(crate) text_renderers: &'a mut Vec<TextRenderer>,
  142. /// The width of the window in logical coordinates. Multiply with
  143. /// `dpi_factor` to get the width in physical pixels.
  144. pub width: f32,
  145. /// The height of the window in logical coordinates. Multiply with
  146. /// `dpi_factor` to get the height in physical pixels.
  147. pub height: f32,
  148. /// The dpi multiplier of the window.
  149. pub dpi_factor: f32,
  150. }
  151. impl GraphicsContext<'_> {
  152. /// Consume this GraphicsContext to render everything that has
  153. /// been queued with `draw` calls so far. Call
  154. /// [`Context::render()`](struct.Context.html#method.render)
  155. /// and swap buffers after this.
  156. pub fn finish_frame(self) {
  157. #[cfg(feature = "text")]
  158. for font in self.text_renderers {
  159. font.compose_draw_call(self.renderer);
  160. }
  161. self.renderer.finish_frame();
  162. }
  163. }