Skip to content

Commit 1bfa613

Browse files
committed
Add initial support for pbr materials.
1 parent c215696 commit 1bfa613

18 files changed

Lines changed: 816 additions & 81 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ path = "examples/custom_attribute.rs"
6868
name = "lights"
6969
path = "examples/lights.rs"
7070

71+
[[example]]
72+
name = "materials"
73+
path = "examples/materials.rs"
74+
7175
[profile.wasm-release]
7276
inherits = "release"
7377
opt-level = "z"

crates/processing_ffi/src/lib.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,14 @@ pub extern "C" fn processing_geometry_box(width: f32, height: f32, depth: f32) -
10921092
.unwrap_or(0)
10931093
}
10941094

1095+
#[unsafe(no_mangle)]
1096+
pub extern "C" fn processing_geometry_sphere(radius: f32, sectors: u32, stacks: u32) -> u64 {
1097+
error::clear_error();
1098+
error::check(|| geometry_sphere(radius, sectors, stacks))
1099+
.map(|e| e.to_bits())
1100+
.unwrap_or(0)
1101+
}
1102+
10951103
#[unsafe(no_mangle)]
10961104
pub extern "C" fn processing_light_create_directional(
10971105
graphics_id: u64,
@@ -1146,3 +1154,62 @@ pub extern "C" fn processing_light_create_spot(
11461154
.map(|e| e.to_bits())
11471155
.unwrap_or(0)
11481156
}
1157+
1158+
#[unsafe(no_mangle)]
1159+
pub extern "C" fn processing_material_create_pbr() -> u64 {
1160+
error::clear_error();
1161+
error::check(|| material_create_pbr())
1162+
.map(|e| e.to_bits())
1163+
.unwrap_or(0)
1164+
}
1165+
1166+
#[unsafe(no_mangle)]
1167+
pub extern "C" fn processing_material_set_float(
1168+
mat_id: u64,
1169+
name: *const std::ffi::c_char,
1170+
value: f32,
1171+
) {
1172+
error::clear_error();
1173+
let name = unsafe { std::ffi::CStr::from_ptr(name) }.to_str().unwrap();
1174+
error::check(|| {
1175+
material_set(
1176+
Entity::from_bits(mat_id),
1177+
name,
1178+
material::MaterialValue::Float(value),
1179+
)
1180+
});
1181+
}
1182+
1183+
#[unsafe(no_mangle)]
1184+
pub extern "C" fn processing_material_set_float4(
1185+
mat_id: u64,
1186+
name: *const std::ffi::c_char,
1187+
r: f32,
1188+
g: f32,
1189+
b: f32,
1190+
a: f32,
1191+
) {
1192+
error::clear_error();
1193+
let name = unsafe { std::ffi::CStr::from_ptr(name) }.to_str().unwrap();
1194+
error::check(|| {
1195+
material_set(
1196+
Entity::from_bits(mat_id),
1197+
name,
1198+
material::MaterialValue::Float4([r, g, b, a]),
1199+
)
1200+
});
1201+
}
1202+
1203+
#[unsafe(no_mangle)]
1204+
pub extern "C" fn processing_material_destroy(mat_id: u64) {
1205+
error::clear_error();
1206+
error::check(|| material_destroy(Entity::from_bits(mat_id)));
1207+
}
1208+
1209+
#[unsafe(no_mangle)]
1210+
pub extern "C" fn processing_material(window_id: u64, mat_id: u64) {
1211+
error::clear_error();
1212+
let window_entity = Entity::from_bits(window_id);
1213+
let mat_entity = Entity::from_bits(mat_id);
1214+
error::check(|| graphics_record_command(window_entity, DrawCommand::Material(mat_entity)));
1215+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from processing import *
2+
3+
mat = None
4+
5+
def setup():
6+
global mat
7+
size(800, 600)
8+
mode_3d()
9+
10+
# Add lighting
11+
dir_light = create_directional_light(1.0, 0.98, 0.95, 1500.0)
12+
point_light = create_point_light(1.0, 1.0, 1.0, 100000.0, 800.0, 0.0)
13+
point_light.position(200.0, 200.0, 400.0)
14+
15+
# Create a PBR material
16+
mat = Material()
17+
mat.set_float("roughness", 0.3)
18+
mat.set_float("metallic", 0.8)
19+
mat.set_float4("base_color", 1.0, 0.85, 0.57, 1.0)
20+
21+
def draw():
22+
camera_position(0.0, 0.0, 200.0)
23+
camera_look_at(0.0, 0.0, 0.0)
24+
background(12, 12, 18)
25+
26+
use_material(mat)
27+
draw_sphere(50.0)
28+
29+
run()

crates/processing_pyo3/src/graphics.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,23 @@ impl Graphics {
293293
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
294294
}
295295

296+
pub fn draw_sphere(&self, radius: f32, sectors: u32, stacks: u32) -> PyResult<()> {
297+
let sphere_geo = geometry_sphere(radius, sectors, stacks)
298+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))?;
299+
graphics_record_command(self.entity, DrawCommand::Geometry(sphere_geo))
300+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
301+
}
302+
296303
pub fn draw_geometry(&self, geometry: &Geometry) -> PyResult<()> {
297304
graphics_record_command(self.entity, DrawCommand::Geometry(geometry.entity))
298305
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
299306
}
300307

308+
pub fn use_material(&self, material: &crate::material::Material) -> PyResult<()> {
309+
graphics_record_command(self.entity, DrawCommand::Material(material.entity))
310+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
311+
}
312+
301313
pub fn scale(&self, x: f32, y: f32) -> PyResult<()> {
302314
graphics_record_command(self.entity, DrawCommand::Scale { x, y })
303315
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
@@ -313,6 +325,11 @@ impl Graphics {
313325
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
314326
}
315327

328+
pub fn set_material(&self, material: &crate::material::Material) -> PyResult<()> {
329+
graphics_record_command(self.entity, DrawCommand::Material(material.entity))
330+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
331+
}
332+
316333
pub fn begin_draw(&self) -> PyResult<()> {
317334
graphics_begin_draw(self.entity).map_err(|e| PyRuntimeError::new_err(format!("{e}")))
318335
}

crates/processing_pyo3/src/lib.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
//! functions that forward to a singleton Graphics object pub(crate) behind the scenes.
1111
mod glfw;
1212
mod graphics;
13+
pub(crate) mod material;
1314

1415
use graphics::{Geometry, Graphics, Image, Light, Topology, get_graphics, get_graphics_mut};
16+
use material::Material;
1517
use pyo3::{
1618
exceptions::PyRuntimeError,
1719
prelude::*,
@@ -27,6 +29,7 @@ fn processing(m: &Bound<'_, PyModule>) -> PyResult<()> {
2729
m.add_class::<Image>()?;
2830
m.add_class::<Light>()?;
2931
m.add_class::<Topology>()?;
32+
m.add_class::<Material>()?;
3033
m.add_function(wrap_pyfunction!(size, m)?)?;
3134
m.add_function(wrap_pyfunction!(run, m)?)?;
3235
m.add_function(wrap_pyfunction!(mode_3d, m)?)?;
@@ -48,6 +51,8 @@ fn processing(m: &Bound<'_, PyModule>) -> PyResult<()> {
4851
m.add_function(wrap_pyfunction!(create_directional_light, m)?)?;
4952
m.add_function(wrap_pyfunction!(create_point_light, m)?)?;
5053
m.add_function(wrap_pyfunction!(create_spot_light, m)?)?;
54+
m.add_function(wrap_pyfunction!(draw_sphere, m)?)?;
55+
m.add_function(wrap_pyfunction!(use_material, m)?)?;
5156

5257
Ok(())
5358
}
@@ -320,3 +325,20 @@ fn create_spot_light(
320325
) -> PyResult<Light> {
321326
get_graphics(module)?.light_spot(r, g, b, intensity, range, radius, inner_angle, outer_angle)
322327
}
328+
329+
#[pyfunction]
330+
#[pyo3(pass_module, signature = (radius, sectors=32, stacks=18))]
331+
fn draw_sphere(
332+
module: &Bound<'_, PyModule>,
333+
radius: f32,
334+
sectors: u32,
335+
stacks: u32,
336+
) -> PyResult<()> {
337+
get_graphics(module)?.draw_sphere(radius, sectors, stacks)
338+
}
339+
340+
#[pyfunction]
341+
#[pyo3(pass_module, signature = (material))]
342+
fn use_material(module: &Bound<'_, PyModule>, material: &Bound<'_, Material>) -> PyResult<()> {
343+
get_graphics(module)?.use_material(&*material.extract::<PyRef<Material>>()?)
344+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use bevy::prelude::Entity;
2+
use processing::prelude::*;
3+
use pyo3::{exceptions::PyRuntimeError, prelude::*};
4+
5+
#[pyclass(unsendable)]
6+
pub struct Material {
7+
pub(crate) entity: Entity,
8+
}
9+
10+
#[pymethods]
11+
impl Material {
12+
#[new]
13+
pub fn new() -> PyResult<Self> {
14+
let entity = material_create_pbr().map_err(|e| PyRuntimeError::new_err(format!("{e}")))?;
15+
Ok(Self { entity })
16+
}
17+
18+
pub fn set_float(&self, name: &str, value: f32) -> PyResult<()> {
19+
material_set(self.entity, name, material::MaterialValue::Float(value))
20+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
21+
}
22+
23+
pub fn set_float4(&self, name: &str, r: f32, g: f32, b: f32, a: f32) -> PyResult<()> {
24+
material_set(
25+
self.entity,
26+
name,
27+
material::MaterialValue::Float4([r, g, b, a]),
28+
)
29+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
30+
}
31+
}
32+
33+
impl Drop for Material {
34+
fn drop(&mut self) {
35+
let _ = material_destroy(self.entity);
36+
}
37+
}

crates/processing_render/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,8 @@ pub enum ProcessingError {
3030
LayoutNotFound,
3131
#[error("Transform not found")]
3232
TransformNotFound,
33+
#[error("Material not found")]
34+
MaterialNotFound,
35+
#[error("Unknown material property: {0}")]
36+
UnknownMaterialProperty(String),
3337
}

crates/processing_render/src/geometry/mod.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,28 @@ pub fn create_box(
174174
commands.spawn(Geometry::new(handle, layout_entity)).id()
175175
}
176176

177+
pub fn create_sphere(
178+
In((radius, sectors, stacks)): In<(f32, u32, u32)>,
179+
mut commands: Commands,
180+
mut meshes: ResMut<Assets<Mesh>>,
181+
builtins: Res<BuiltinAttributes>,
182+
) -> Entity {
183+
let sphere = Sphere::new(radius);
184+
let mesh = sphere.mesh().uv(sectors, stacks);
185+
let handle = meshes.add(mesh);
186+
187+
let layout_entity = commands
188+
.spawn(VertexLayout::with_attributes(vec![
189+
builtins.position,
190+
builtins.normal,
191+
builtins.color,
192+
builtins.uv,
193+
]))
194+
.id();
195+
196+
commands.spawn(Geometry::new(handle, layout_entity)).id()
197+
}
198+
177199
pub fn normal(world: &mut World, entity: Entity, nx: f32, ny: f32, nz: f32) -> Result<()> {
178200
let mut geometry = world
179201
.get_mut::<Geometry>(entity)

crates/processing_render/src/graphics.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::{
2929
Flush,
3030
error::{ProcessingError, Result},
3131
image::{Image, bytes_to_pixels, create_readback_buffer, pixel_size, pixels_to_bytes},
32+
material::DefaultMaterial,
3233
render::{
3334
RenderState,
3435
command::{CommandBuffer, DrawCommand},
@@ -186,6 +187,7 @@ pub fn create(
186187
mut layer_manager: ResMut<RenderLayersManager>,
187188
p_images: Query<&Image, With<Surface>>,
188189
render_device: Res<RenderDevice>,
190+
default_material: Res<DefaultMaterial>,
189191
) -> Result<Entity> {
190192
// find the surface entity, if it is an image, we will render to that image
191193
// otherwise we will render to the window
@@ -243,7 +245,7 @@ pub fn create(
243245
Transform::from_xyz(0.0, 0.0, 999.9),
244246
render_layer,
245247
CommandBuffer::new(),
246-
RenderState::default(),
248+
RenderState::new(default_material.0),
247249
SurfaceSize(width, height),
248250
Graphics {
249251
readback_buffer,
@@ -424,11 +426,15 @@ pub fn destroy(
424426
Ok(())
425427
}
426428

427-
pub fn begin_draw(In(entity): In<Entity>, mut state_query: Query<&mut RenderState>) -> Result<()> {
429+
pub fn begin_draw(
430+
In(entity): In<Entity>,
431+
mut state_query: Query<&mut RenderState>,
432+
default_material: Res<DefaultMaterial>,
433+
) -> Result<()> {
428434
let mut state = state_query
429435
.get_mut(entity)
430436
.map_err(|_| ProcessingError::GraphicsNotFound)?;
431-
state.reset();
437+
state.reset(default_material.0);
432438
Ok(())
433439
}
434440

0 commit comments

Comments
 (0)