mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2024-09-29 21:49:33 +00:00
165 lines
5.6 KiB
Rust
165 lines
5.6 KiB
Rust
|
//! I guess these docs will never appear since we re-export the canvas
|
||
|
//! module from graphics...
|
||
|
use gfx::format::Swizzle;
|
||
|
use gfx::handle::RawRenderTargetView;
|
||
|
use gfx::memory::{Bind, Usage};
|
||
|
use gfx::texture::{AaMode, Kind};
|
||
|
use gfx::Factory;
|
||
|
|
||
|
use crate::ggez::conf;
|
||
|
use crate::ggez::context::DebugId;
|
||
|
use crate::ggez::error::*;
|
||
|
use crate::ggez::graphics::*;
|
||
|
use crate::ggez::Context;
|
||
|
|
||
|
/// A generic canvas independent of graphics backend. This type should
|
||
|
/// never need to be used directly; use [`graphics::Canvas`](type.Canvas.html)
|
||
|
/// instead.
|
||
|
#[derive(Debug)]
|
||
|
pub struct CanvasGeneric<Spec>
|
||
|
where
|
||
|
Spec: BackendSpec,
|
||
|
{
|
||
|
target: RawRenderTargetView<Spec::Resources>,
|
||
|
image: Image,
|
||
|
debug_id: DebugId,
|
||
|
}
|
||
|
|
||
|
/// A canvas that can be rendered to instead of the screen (sometimes referred
|
||
|
/// to as "render target" or "render to texture"). Set the canvas with the
|
||
|
/// [`graphics::set_canvas()`](fn.set_canvas.html) function, and then anything you
|
||
|
/// draw will be drawn to the canvas instead of the screen.
|
||
|
///
|
||
|
/// Resume drawing to the screen by calling `graphics::set_canvas(None)`.
|
||
|
///
|
||
|
/// A `Canvas` allows graphics to be rendered to images off-screen
|
||
|
/// in order to do things like saving to an image file or creating cool effects
|
||
|
/// by using shaders that render to an image.
|
||
|
/// If you just want to draw multiple things efficiently, look at
|
||
|
/// [`SpriteBatch`](spritebatch/struct.Spritebatch.html).
|
||
|
pub type Canvas = CanvasGeneric<GlBackendSpec>;
|
||
|
|
||
|
impl Canvas {
|
||
|
/// Create a new `Canvas` with the given size and number of samples.
|
||
|
pub fn new(
|
||
|
ctx: &mut Context,
|
||
|
width: u16,
|
||
|
height: u16,
|
||
|
samples: conf::NumSamples,
|
||
|
) -> GameResult<Canvas> {
|
||
|
let debug_id = DebugId::get(ctx);
|
||
|
let aa = match samples {
|
||
|
conf::NumSamples::One => AaMode::Single,
|
||
|
s => AaMode::Multi(s as u8),
|
||
|
};
|
||
|
let kind = Kind::D2(width, height, aa);
|
||
|
let levels = 1;
|
||
|
let color_format = ctx.gfx_context.color_format();
|
||
|
let factory = &mut ctx.gfx_context.factory;
|
||
|
let texture_create_info = gfx::texture::Info {
|
||
|
kind,
|
||
|
levels,
|
||
|
format: color_format.0,
|
||
|
bind: Bind::SHADER_RESOURCE | Bind::RENDER_TARGET | Bind::TRANSFER_SRC,
|
||
|
usage: Usage::Data,
|
||
|
};
|
||
|
let tex = factory.create_texture_raw(texture_create_info, Some(color_format.1), None)?;
|
||
|
let resource_desc = gfx::texture::ResourceDesc {
|
||
|
channel: color_format.1,
|
||
|
layer: None,
|
||
|
min: 0,
|
||
|
max: levels - 1,
|
||
|
swizzle: Swizzle::new(),
|
||
|
};
|
||
|
let resource = factory.view_texture_as_shader_resource_raw(&tex, resource_desc)?;
|
||
|
let render_desc = gfx::texture::RenderDesc {
|
||
|
channel: color_format.1,
|
||
|
level: 0,
|
||
|
layer: None,
|
||
|
};
|
||
|
let target = factory.view_texture_as_render_target_raw(&tex, render_desc)?;
|
||
|
Ok(Canvas {
|
||
|
target,
|
||
|
image: Image {
|
||
|
texture: resource,
|
||
|
texture_handle: tex,
|
||
|
sampler_info: ctx.gfx_context.default_sampler_info,
|
||
|
blend_mode: None,
|
||
|
width,
|
||
|
height,
|
||
|
debug_id,
|
||
|
},
|
||
|
debug_id,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
/// Create a new `Canvas` with the current window dimensions.
|
||
|
pub fn with_window_size(ctx: &mut Context) -> GameResult<Canvas> {
|
||
|
use crate::graphics;
|
||
|
let (w, h) = graphics::drawable_size(ctx);
|
||
|
// Default to no multisampling
|
||
|
Canvas::new(ctx, w as u16, h as u16, conf::NumSamples::One)
|
||
|
}
|
||
|
|
||
|
/// Gets the backend `Image` that is being rendered to.
|
||
|
pub fn image(&self) -> &Image {
|
||
|
&self.image
|
||
|
}
|
||
|
|
||
|
/// Get the filter mode for the image.
|
||
|
pub fn filter(&self) -> FilterMode {
|
||
|
self.image.filter()
|
||
|
}
|
||
|
|
||
|
/// Set the filter mode for the canvas.
|
||
|
pub fn set_filter(&mut self, mode: FilterMode) {
|
||
|
self.image.set_filter(mode)
|
||
|
}
|
||
|
|
||
|
/// Destroys the `Canvas` and returns the `Image` it contains.
|
||
|
pub fn into_inner(self) -> Image {
|
||
|
// TODO: This texture is created with different settings
|
||
|
// than the default; does that matter?
|
||
|
// Test; we really just need to add Bind::TRANSFER_SRC
|
||
|
// and change the Usage's to match to make them identical.
|
||
|
// Ask termhn maybe?
|
||
|
self.image
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Drawable for Canvas {
|
||
|
fn draw(&self, ctx: &mut Context, param: DrawParam) -> GameResult {
|
||
|
self.debug_id.assert(ctx);
|
||
|
// Gotta flip the image on the Y axis here
|
||
|
// to account for OpenGL's origin being at the bottom-left.
|
||
|
let mut flipped_param = param;
|
||
|
flipped_param.scale.y *= -1.0;
|
||
|
flipped_param.dest.y += f32::from(self.image.height()) * param.scale.y;
|
||
|
self.image.draw(ctx, flipped_param)?;
|
||
|
Ok(())
|
||
|
}
|
||
|
fn dimensions(&self, _: &mut Context) -> Option<Rect> {
|
||
|
Some(self.image.dimensions())
|
||
|
}
|
||
|
fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
|
||
|
self.image.blend_mode = mode;
|
||
|
}
|
||
|
fn blend_mode(&self) -> Option<BlendMode> {
|
||
|
self.image.blend_mode
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Set the `Canvas` to render to. Specifying `Option::None` will cause all
|
||
|
/// rendering to be done directly to the screen.
|
||
|
pub fn set_canvas(ctx: &mut Context, target: Option<&Canvas>) {
|
||
|
match target {
|
||
|
Some(surface) => {
|
||
|
surface.debug_id.assert(ctx);
|
||
|
ctx.gfx_context.data.out = surface.target.clone();
|
||
|
}
|
||
|
None => {
|
||
|
ctx.gfx_context.data.out = ctx.gfx_context.screen_render_target.clone();
|
||
|
}
|
||
|
};
|
||
|
}
|