use bevy::prelude::*;
use bevy_retrograde::{
core::{
graphics::*,
luminance::{
self, context::GraphicsContext, pipeline::PipelineState, render_state::RenderState,
shader::Uniform, tess::Mode, Semantics, UniformInterface, Vertex,
},
},
prelude::*,
};
struct Triangle {
scale: f32,
}
fn main() {
App::build()
.insert_resource(WindowDescriptor {
title: "Bevy Retrograde Custom Rendering".into(),
..Default::default()
})
.add_plugins(RetroPlugins)
.add_render_hook::<TriangleRenderHook>()
.add_startup_system(setup.system())
.add_system(move_triangle.system())
.run();
}
fn setup(mut commands: Commands) {
commands.spawn().insert_bundle(CameraBundle::default());
commands.spawn().insert_bundle((
Triangle { scale: 0.5 },
Transform::default(),
GlobalTransform::default(),
));
}
fn move_triangle(time: Res<Time>, mut query: Query<(&mut Triangle, &mut Transform)>) {
for (mut tri, mut transform) in query.iter_mut() {
tri.scale = time.seconds_since_startup().sin() as f32;
transform.translation.x = (time.seconds_since_startup().sin() * 400.).round() as f32;
}
}
struct TriangleRenderHook {
tri_program: Program<VertexSemantics, (), Uniforms>,
tri_tess: Tess<Vertex>,
current_triangle_batch: Option<Vec<Entity>>,
}
impl RenderHook for TriangleRenderHook {
fn init(_window_id: bevy::window::WindowId, surface: &mut Surface) -> Box<dyn RenderHook>
where
Self: Sized,
{
let tri_program = surface
.new_shader_program::<VertexSemantics, (), Uniforms>()
.from_strings(VERT_SHADER, None, None, FRAG_SHADER)
.expect("program creation")
.ignore_warnings();
let tri_tess = surface
.new_tess()
.set_vertices(TRI_VERTICES)
.set_mode(Mode::Triangle)
.build()
.unwrap();
Box::new(Self {
tri_program,
tri_tess,
current_triangle_batch: None,
})
}
fn prepare(
&mut self,
world: &mut World,
_surface: &mut Surface,
_texture_cache: &mut TextureCache,
_frame_context: &FrameContext,
) -> Vec<RenderHookRenderableHandle> {
let mut triangles = world.query::<(Entity, &Triangle, &GlobalTransform)>();
let mut triangle_batch = Vec::new();
let mut triangle_depths = Vec::new();
for (ent, _, transform) in triangles.iter(world) {
triangle_batch.push(ent);
triangle_depths.push(transform.translation.z);
}
self.current_triangle_batch = Some(triangle_batch);
self.current_triangle_batch
.as_ref()
.unwrap()
.iter()
.enumerate()
.map(|(i, e)| RenderHookRenderableHandle {
identifier: i,
is_transparent: false,
depth: triangle_depths[i],
entity: Some(*e),
})
.collect()
}
fn render(
&mut self,
world: &mut World,
surface: &mut Surface,
_texture_cache: &mut TextureCache,
_frame_context: &FrameContext,
target_framebuffer: &SceneFramebuffer,
renderables: &[RenderHookRenderableHandle],
) {
let Self {
current_triangle_batch,
tri_program,
tri_tess,
..
} = self;
let mut triangles = world.query::<(Entity, &Triangle, &GlobalTransform)>();
surface
.new_pipeline_gate()
.pipeline(
&target_framebuffer,
&PipelineState::default().enable_clear_color(false),
|_, mut shading_gate| {
shading_gate.shade(tri_program, |mut interface, uniforms, mut render_gate| {
for renderable in renderables {
let (_, tri, transform) = triangles
.get(
world,
*current_triangle_batch
.as_ref()
.unwrap()
.get(renderable.identifier)
.unwrap(),
)
.unwrap();
let pos = transform.translation;
interface.set(
&uniforms.tri_pos,
[pos.x as f32, pos.y as f32, pos.z as f32],
);
interface.set(&uniforms.tri_scale, tri.scale);
render_gate.render(&RenderState::default(), |mut tess_gate| {
tess_gate.render(&*tri_tess)
})?
}
Ok(())
})
},
)
.assume()
.into_result()
.expect("Could not render");
*current_triangle_batch = None;
}
}
const FRAG_SHADER: &str = r#"
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
"#;
const VERT_SHADER: &str = r#"
attribute vec2 pos;
attribute vec4 color;
uniform vec3 tri_pos;
uniform float tri_scale;
varying vec4 v_color;
void main() {
v_color = color;
gl_Position = vec4(pos * tri_scale + tri_pos.xy / 1024., tri_pos.z / 1024., 1.);
}
"#;
#[derive(Debug, UniformInterface)]
struct Uniforms {
#[uniform(unbound)]
tri_pos: Uniform<[f32; 3]>,
#[uniform(unbound)]
tri_scale: Uniform<f32>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Semantics)]
pub enum VertexSemantics {
#[sem(name = "pos", repr = "[f32; 2]", wrapper = "VertexPosition")]
Position,
#[sem(name = "color", repr = "[f32; 4]", wrapper = "VertexColor")]
Color,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Vertex)]
#[vertex(sem = "VertexSemantics")]
struct Vertex {
pos: VertexPosition,
#[vertex(normalized = "true")]
rgb: VertexColor,
}
const TRI_VERTICES: [Vertex; 3] = [
Vertex::new(
VertexPosition::new([0.5, -0.5]),
VertexColor::new([0., 1., 0., 1.]),
),
Vertex::new(
VertexPosition::new([0.0, 0.5]),
VertexColor::new([0., 0., 1., 1.]),
),
Vertex::new(
VertexPosition::new([-0.5, -0.5]),
VertexColor::new([1., 0., 0., 1.]),
),
];