A hardware-accelerated 2D sprite renderer. https://crates.io/crates/fae
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 

175 行
6.2 KiB

use crate::gl_version::OpenGlVersion;
use crate::renderer::Renderer;
#[cfg(feature = "text")]
use crate::text::TextRenderer;
/// The overarching state of the crate. Intended to live outside of
/// the main game loop.
///
/// This is the struct you can get a GraphicsContext from, and which
/// should live as long as you're drawing anything. Illustrated:
/// ```no_run
/// use fae::Context;
/// let mut fae_context: Context = Context::new();
/// # let (width, height, dpi_factor) = (0.0, 0.0, 0.0);
/// # let spritesheet = fae::SpritesheetBuilder::default().build(&mut fae_context);
///
/// loop {
/// // Here's your gameloop, and now you want to draw something.
///
/// // First, create the GraphicsContext with start_frame.
/// let mut ctx: fae::GraphicsContext = fae_context.start_frame(width, height, dpi_factor);
///
/// // Then do your rendering stuff.
/// spritesheet.draw(&mut ctx)
/// /* ... */
/// .finish();
///
/// // Finish frame and consume the GraphicsContext.
/// ctx.finish_frame();
///
/// // swap buffers, fae_context.synchronize(), etc.
/// }
/// ```
///
/// This construct makes the state of fae more clear, as you can only
/// have access to either the Context or the GraphicsContext, as
/// well as providing a good synchronization point (start_frame) where
/// the window's state is passed to fae, ensuring that all rendering
/// operations are done based on up-to-date information.
pub struct Context {
pub(crate) renderer: Renderer,
#[cfg(feature = "text")]
pub(crate) text_renderers: Vec<TextRenderer>,
}
impl Context {
/// Creates a new GraphicsContext. See the Safety section.
///
/// # Safety
///
/// Basically everything in fae assumes that it can call OpenGL,
/// so please ensure you have called something along the lines of:
///
/// ```ignore
/// unsafe { fae::gl::load_with(|symbol| context.get_proc_address(symbol) as *const _); }
/// ```
///
/// Before creating a Context.
///
/// The width, height and dpi_factor are only initial values; they
/// are updated in the call to
/// [`Context::start_frame()`](struct.Context.html#method.start_frame).
pub fn new() -> Context {
Context {
renderer: Renderer::new(),
#[cfg(feature = "text")]
text_renderers: Vec::new(),
}
}
/// Returns true when running in legacy mode (OpenGL 3.3+
/// optimizations off).
pub fn is_legacy(&self) -> bool {
self.renderer.legacy
}
/// Returns the OpenGL version if it could be parsed.
pub fn get_opengl_version(&self) -> &OpenGlVersion {
&self.renderer.version
}
/// Tries to ensure that all the commands queued in the GPU have been processed.
///
/// Call this after swap_buffers to ensure that everything after
/// this happens only after the frame has been sent to the screen,
/// but don't trust this to actually work. Doing vsync properly
/// with OpenGL is a mess, as far as I know.
pub fn synchronize(&mut self) {
self.renderer.synchronize();
}
/// Creates a GraphicsContext for this frame.
///
/// The parameters `width` and `height` are the dimensions of the
/// window, and dpi_factor is a multiplier, such that: `width *
/// dpi_factor` is the window's width in physical pixels, and
/// `height * dpi_factor` is the height in physical pixels.
pub fn start_frame(&mut self, width: f32, height: f32, dpi_factor: f32) -> GraphicsContext {
self.renderer.prepare_new_frame(dpi_factor);
#[cfg(feature = "text")]
for font in &mut self.text_renderers {
font.prepare_new_frame(&mut self.renderer, dpi_factor, width, height);
}
GraphicsContext {
renderer: &mut self.renderer,
#[cfg(feature = "text")]
text_renderers: &mut self.text_renderers,
width,
height,
dpi_factor,
}
}
/// Renders the frame with the given `width`, `height` and
/// `clear_color`.
///
/// The `clear_color` is defined between 0.0 and 1.0, and the
/// components are (red, green, blue, alpha). None if you don't
/// want to clear the screen.
///
/// See
/// [`Context::start_frame`](struct.Context.html#method.start_frame)
/// for more information on what `width` and `height` are,
/// specifically.
///
/// This should generally be called after
/// [`GraphicsContext::finish_frame`](struct.GraphicsContext.html#method.finish_frame),
/// but can also be used to redraw the previous frame.
pub fn render(&mut self, width: f32, height: f32, clear_color: Option<(f32, f32, f32, f32)>) {
self.renderer.render(width, height, clear_color);
}
}
/// Draw stuff on the screen with this.
///
/// Create this struct with
/// [`Context::start_frame()`](struct.Context.html#method.start_frame).
///
/// Then, pass it to:
/// - [`Spritesheet::draw`](struct.Spritesheet.html#method.draw) to draw sprites,
/// - [`Font::draw`](struct.Font.html#method.draw) to draw text.
///
/// And after doing all the drawing, call
/// [`GraphicsContext::finish_frame()`](struct.GraphicsContext.html#method.finish_frame)
/// to flush all the rendering operations.
pub struct GraphicsContext<'a> {
pub(crate) renderer: &'a mut Renderer,
#[cfg(feature = "text")]
pub(crate) text_renderers: &'a mut Vec<TextRenderer>,
/// The width of the window in logical coordinates. Multiply with
/// `dpi_factor` to get the width in physical pixels.
pub width: f32,
/// The height of the window in logical coordinates. Multiply with
/// `dpi_factor` to get the height in physical pixels.
pub height: f32,
/// The dpi multiplier of the window.
pub dpi_factor: f32,
}
impl GraphicsContext<'_> {
/// Consume this GraphicsContext to render everything that has
/// been queued with `draw` calls so far. Call
/// [`Context::render()`](struct.Context.html#method.render)
/// and swap buffers after this.
pub fn finish_frame(self) {
#[cfg(feature = "text")]
for font in self.text_renderers {
font.compose_draw_call(self.renderer);
}
self.renderer.finish_frame();
}
}