use lambda::{
component::Component,
events::{
Events,
Key,
VirtualKey,
WindowEvent,
},
render::{
command::RenderCommand,
pipeline::{
self,
PipelineStage,
},
render_pass,
shader::{
Shader,
ShaderBuilder,
ShaderKind,
VirtualShader,
},
viewport,
RenderContext,
},
runtime::start_runtime,
runtimes::{
application::ComponentResult,
ApplicationRuntimeBuilder,
},
};
pub struct TrianglesComponent {
triangle_vertex: Shader,
vertex_shader: Shader,
render_pass: Option<lambda::render::ResourceId>,
render_pipeline: Option<lambda::render::ResourceId>,
width: u32,
height: u32,
animation_scalar: f32,
position: (f32, f32),
}
impl Component<ComponentResult, String> for TrianglesComponent {
fn on_attach(
&mut self,
render_context: &mut RenderContext,
) -> Result<ComponentResult, String> {
let render_pass =
render_pass::RenderPassBuilder::new().build(&render_context);
let push_constants_size = std::mem::size_of::<PushConstant>() as u32;
let pipeline = pipeline::RenderPipelineBuilder::new()
.with_push_constant(PipelineStage::VERTEX, push_constants_size)
.build(
render_context,
&render_pass,
&self.vertex_shader,
Some(&self.triangle_vertex),
);
self.render_pass = Some(render_context.attach_render_pass(render_pass));
self.render_pipeline = Some(render_context.attach_pipeline(pipeline));
logging::info!("Attached the DemoComponent.");
return Ok(ComponentResult::Success);
}
fn on_detach(
&mut self,
_render_context: &mut RenderContext,
) -> Result<ComponentResult, String> {
return Ok(ComponentResult::Success);
}
fn on_render(
&mut self,
_render_context: &mut lambda::render::RenderContext,
) -> Vec<RenderCommand> {
let viewport =
viewport::ViewportBuilder::new().build(self.width, self.height);
let (x, y) = self.position;
let triangle_data = &[
PushConstant {
color: [
1.0,
1.0 * self.animation_scalar,
0.5 * self.animation_scalar,
1.0,
],
pos: [x, y],
scale: [0.3, 0.3],
},
PushConstant {
color: [0.0, 1.0, 0.0, 1.0],
pos: [0.5, 0.0],
scale: [0.4, 0.4],
},
PushConstant {
color: [0.0, 0.0, 1.0, 1.0],
pos: [0.25, 0.5],
scale: [0.5, 0.5],
},
PushConstant {
color: [1.0, 1.0, 1.0, 1.0],
pos: [0.0, 0.0],
scale: [0.5, 0.5],
},
];
let render_pipeline = self
.render_pipeline
.expect("No render pipeline actively set for rendering.");
let mut commands = vec![
RenderCommand::SetViewports {
start_at: 0,
viewports: vec![viewport.clone()],
},
RenderCommand::SetScissors {
start_at: 0,
viewports: vec![viewport.clone()],
},
RenderCommand::SetPipeline {
pipeline: render_pipeline.clone(),
},
RenderCommand::BeginRenderPass {
render_pass: self
.render_pass
.expect("Cannot begin the render pass when it doesn't exist.")
.clone(),
viewport: viewport.clone(),
},
];
for triangle in triangle_data {
commands.push(RenderCommand::PushConstants {
pipeline: render_pipeline.clone(),
stage: PipelineStage::VERTEX,
offset: 0,
bytes: Vec::from(push_constants_to_bytes(triangle)),
});
commands.push(RenderCommand::Draw { vertices: 0..3 });
}
commands.push(RenderCommand::EndRenderPass);
return commands;
}
fn on_event(&mut self, event: Events) -> Result<ComponentResult, String> {
match event {
Events::Runtime { event, issued_at } => match event {
lambda::events::RuntimeEvent::Shutdown => {
logging::info!("Shutting down the runtime");
}
_ => {}
},
Events::Window { event, issued_at } => match event {
WindowEvent::Resize { width, height } => {
logging::info!("Window resized to {}x{}", width, height);
self.width = width;
self.height = height;
}
WindowEvent::Close => {
logging::info!("Window closed");
}
},
Events::Component { event, issued_at } => todo!(),
Events::Keyboard { event, issued_at } => match event {
Key::Pressed {
scan_code,
virtual_key,
} => match virtual_key {
Some(VirtualKey::W) => {
self.position.1 -= 0.01;
}
Some(VirtualKey::S) => {
self.position.1 += 0.01;
}
Some(VirtualKey::A) => {
self.position.0 -= 0.01;
}
Some(VirtualKey::D) => {
self.position.0 += 0.01;
}
_ => {}
},
_ => {}
},
_ => {}
};
return Ok(ComponentResult::Success);
}
fn on_update(
&mut self,
last_frame: &std::time::Duration,
) -> Result<ComponentResult, String> {
match last_frame.as_millis() > 20 {
true => {
logging::warn!("Last frame took {}ms", last_frame.as_millis());
}
false => {}
};
return Ok(ComponentResult::Success);
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct PushConstant {
color: [f32; 4],
pos: [f32; 2],
scale: [f32; 2],
}
pub fn push_constants_to_bytes(push_constants: &PushConstant) -> &[u32] {
let bytes = unsafe {
let size_in_bytes = std::mem::size_of::<PushConstant>();
let size_in_u32 = size_in_bytes / std::mem::size_of::<u32>();
let ptr = push_constants as *const PushConstant as *const u32;
std::slice::from_raw_parts(ptr, size_in_u32)
};
return bytes;
}
impl Default for TrianglesComponent {
fn default() -> Self {
let triangle_vertex = VirtualShader::Source {
source: include_str!("../assets/shaders/triangles.vert").to_string(),
kind: ShaderKind::Vertex,
name: String::from("triangles"),
entry_point: String::from("main"),
};
let triangle_fragment = VirtualShader::Source {
source: include_str!("../assets/shaders/triangles.frag").to_string(),
kind: ShaderKind::Fragment,
name: String::from("triangles"),
entry_point: String::from("main"),
};
let mut builder = ShaderBuilder::new();
let vs = builder.build(triangle_vertex);
let fs = builder.build(triangle_fragment);
return TrianglesComponent {
vertex_shader: vs,
triangle_vertex: fs,
render_pass: None,
render_pipeline: None,
width: 800,
height: 600,
animation_scalar: 0.0,
position: (0.0, 0.0),
};
}
}
fn main() {
let runtime = ApplicationRuntimeBuilder::new("Multiple Triangles Demo")
.with_renderer_configured_as(move |render_context_builder| {
return render_context_builder.with_render_timeout(1_000_000_000);
})
.with_window_configured_as(move |window_builder| {
return window_builder
.with_dimensions(800, 600)
.with_name("Triangles");
})
.with_component(move |runtime, triangles: TrianglesComponent| {
return (runtime, triangles);
})
.build();
start_runtime(runtime);
}