Skip to content

Commit

Permalink
- add start of a basic pbr example
Browse files Browse the repository at this point in the history
  • Loading branch information
polymonster committed Oct 28, 2023
1 parent 05afabb commit d4d7b2b
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 42 deletions.
2 changes: 1 addition & 1 deletion hotline-data
2 changes: 2 additions & 0 deletions plugins/ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@ impl BevyPlugin {
// warn of missing demo
if self.errors.contains_key("active_demo") {
client.imgui.colour_text(&format!("warning: missing demo function: {}, using default schedule.", self.session_info.active_demo), warning_col);
client.imgui.colour_text(&format!(" tip: make sure 'mod {};' is added to a plugins lib.rs file", self.session_info.active_demo), default_col);
client.imgui.colour_text(&format!(" tip: make sure \"{}\" is added to a plugins 'demos!' array inside the 'get_demos_..()' function", self.session_info.active_demo), default_col);
}

self.status_ui_category(&mut client.imgui, "Setup:", &self.schedule_info.setup);
Expand Down
39 changes: 0 additions & 39 deletions plugins/ecs_examples/src/dynamic_cubemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,42 +157,3 @@ pub fn render_meshes_cubemap_reflect(

Ok(())
}

/// Blit a single fullscreen texture into the render target
#[no_mangle]
#[export_render_fn]
pub fn cubemap_clear(
pmfx: &Res<PmfxRes>,
view: &pmfx::View<gfx_platform::Device>) -> Result<(), hotline_rs::Error> {

let pmfx = &pmfx;
let fmt = view.pass.get_format_hash();
let pipeline = pmfx.get_render_pipeline_for_format("cubemap_clear", fmt)?;
let camera = pmfx.get_camera_constants(&view.camera)?;

if view.use_indices.len() != 1 {
return Err(hotline_rs::Error {
msg: "blit expects a single read resource specified in the `pmfx` uses".to_string()
});
}
let srv = view.use_indices[0].index;

view.cmd_buf.set_render_pipeline(pipeline);

let slot = pipeline.get_pipeline_slot(0, 0, gfx::DescriptorType::PushConstants);
if let Some(slot) = slot {
let inv = camera.view_projection_matrix.inverse();
view.cmd_buf.push_render_constants(slot.index, 16, 0, &inv);
}

let slot = pipeline.get_pipeline_slot(0, 0, gfx::DescriptorType::ShaderResource);
if let Some(slot) = slot {
view.cmd_buf.set_binding(pipeline, &pmfx.shader_heap, slot.index, srv as usize);
}

view.cmd_buf.set_index_buffer(&pmfx.0.unit_quad_mesh.ib);
view.cmd_buf.set_vertex_buffer(&pmfx.0.unit_quad_mesh.vb, 0);
view.cmd_buf.draw_indexed_instanced(6, 1, 0, 0, 0);

Ok(())
}
43 changes: 42 additions & 1 deletion plugins/ecs_examples/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mod generate_mip_maps;
mod shadow_map;
mod omni_shadow_map;
mod dynamic_cubemap;
mod pbr;

use prelude::*;

Expand Down Expand Up @@ -481,6 +482,45 @@ pub fn blit(
Ok(())
}

/// Blit a single fullscreen texture into the render target
#[no_mangle]
#[export_render_fn]
pub fn cubemap_clear(
pmfx: &Res<PmfxRes>,
view: &pmfx::View<gfx_platform::Device>) -> Result<(), hotline_rs::Error> {

let pmfx = &pmfx;
let fmt = view.pass.get_format_hash();
let pipeline = pmfx.get_render_pipeline_for_format("cubemap_clear", fmt)?;
let camera = pmfx.get_camera_constants(&view.camera)?;

if view.use_indices.len() != 1 {
return Err(hotline_rs::Error {
msg: "blit expects a single read resource specified in the `pmfx` uses".to_string()
});
}
let srv = view.use_indices[0].index;

view.cmd_buf.set_render_pipeline(pipeline);

let slot = pipeline.get_pipeline_slot(0, 0, gfx::DescriptorType::PushConstants);
if let Some(slot) = slot {
let inv = camera.view_projection_matrix.inverse();
view.cmd_buf.push_render_constants(slot.index, 16, 0, &inv);
}

let slot = pipeline.get_pipeline_slot(0, 0, gfx::DescriptorType::ShaderResource);
if let Some(slot) = slot {
view.cmd_buf.set_binding(pipeline, &pmfx.shader_heap, slot.index, srv as usize);
}

view.cmd_buf.set_index_buffer(&pmfx.0.unit_quad_mesh.ib);
view.cmd_buf.set_vertex_buffer(&pmfx.0.unit_quad_mesh.vb, 0);
view.cmd_buf.draw_indexed_instanced(6, 1, 0, 0, 0);

Ok(())
}

/// Generic compute dispatch which binds usage information supplied in pmfx files
#[no_mangle]
#[export_compute_fn]
Expand Down Expand Up @@ -543,7 +583,8 @@ pub fn get_demos_ecs_examples() -> Vec<String> {
"generate_mip_maps",
"shadow_map",
"dynamic_cubemap",
"omni_shadow_map"
"omni_shadow_map",
"pbr"
]
}

Expand Down
98 changes: 98 additions & 0 deletions plugins/ecs_examples/src/pbr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// currently windows only because here we need a concrete gfx and os implementation
#![cfg(target_os = "windows")]

///
/// Cubemap
///

use crate::prelude::*;

/// Basic pbr example
#[no_mangle]
pub fn pbr(client: &mut Client<gfx_platform::Device, os_platform::App>) -> ScheduleInfo {
client.pmfx.load(hotline_rs::get_data_path("shaders/util").as_str()).unwrap();
client.pmfx.load(hotline_rs::get_data_path("shaders/ecs_examples").as_str()).unwrap();
ScheduleInfo {
setup: systems![
"setup_pbr"
],
render_graph: "mesh_pbr",
..Default::default()
}
}

#[no_mangle]
#[export_update_fn]
pub fn setup_pbr(
mut device: ResMut<DeviceRes>,
mut pmfx: ResMut<PmfxRes>,
mut commands: Commands) -> Result<(), hotline_rs::Error> {

let sphere_mesh = hotline_rs::primitives::create_sphere_mesh(&mut device.0, 64);

// square number of rows and columns
let rc = 5.0;
let irc = (rc + 0.5) as i32;

let size = 10.0;
let half_size = size * 0.5;
let step = size * half_size;
let half_extent = (rc-1.0) * step * 0.5;
let start_pos = vec3f(-half_extent, -half_extent, 0.0);

let cubemap_filepath = hotline_rs::get_data_path("textures/cubemap.dds");
let cubemap = image::load_texture_from_file(&mut device.0, &cubemap_filepath, Some(&mut pmfx.shader_heap)).unwrap();

for y in 0..irc {
for x in 0..irc {
let iter_pos = start_pos + vec3f(x as f32 * step, y as f32 * step, 0.0);
commands.spawn((
MeshComponent(sphere_mesh.clone()),
Position(iter_pos),
Rotation(Quatf::identity()),
Scale(splat3f(size)),
WorldMatrix(Mat34f::identity()),
TextureInstance(cubemap.get_srv_index().unwrap() as u32)
));
}
}

// spawn entity to keep hold of the texture
commands.spawn(
TextureComponent(cubemap)
);

Ok(())
}

/// Renders all scene meshes with a irradiance and specular cubemaps bound to perform image based pbr lighting
#[no_mangle]
#[export_render_fn]
pub fn render_meshes_pbr(
pmfx: &Res<PmfxRes>,
view: &pmfx::View<gfx_platform::Device>,
mesh_draw_query: Query<(&WorldMatrix, &MeshComponent, &TextureInstance)>) -> Result<(), hotline_rs::Error> {

let fmt = view.pass.get_format_hash();
let pipeline = pmfx.get_render_pipeline_for_format(&view.view_pipeline, fmt)?;
let camera = pmfx.get_camera_constants(&view.camera)?;

view.cmd_buf.set_render_pipeline(pipeline);
view.cmd_buf.push_render_constants(0, 16, 0, gfx::as_u8_slice(&camera.view_projection_matrix));

view.cmd_buf.set_heap(pipeline, &pmfx.shader_heap);

let mut mip = 0;
for (world_matrix, mesh, cubemap) in &mesh_draw_query {
view.cmd_buf.push_render_constants(1, 12, 0, &world_matrix.0);
view.cmd_buf.push_render_constants(1, 2, 16, gfx::as_u8_slice(&[cubemap.0, mip, 0, 0]));

view.cmd_buf.set_index_buffer(&mesh.0.ib);
view.cmd_buf.set_vertex_buffer(&mesh.0.vb, 0);
view.cmd_buf.draw_indexed_instanced(mesh.0.num_indices, 1, 0, 0, 0);

mip += 1;
}

Ok(())
}
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,7 @@ A sample to demonstrate how to configure a single shadow map with a vertex shade

<img src="https://raw.githubusercontent.com/polymonster/polymonster.github.io/master/images/hotline/ecs_examples/dynamic_cubemap.png" width="100%"/>

Demonstrates how multiple cubemap faces can be render to with cubemap cameras and then sampled from to create dynamic environment mapped reflections. A single `pmfx` view is created with the `cubemap` flag set to true, this will automatically create 6 child views for each face of the cubemap.
Demonstrates how multiple cubemap faces can be rendered using cubemap cameras and then sampled from to create dynamic environment mapped reflections. A single `pmfx` view is created with the `cubemap` flag set to true, this will automatically create 6 child views for each face of the cubemap.

## Tests

Expand Down

0 comments on commit d4d7b2b

Please sign in to comment.