Skip to content

Commit 53e802b

Browse files
committed
Material setters, light transforms and mat4 uniforms for Python.
1 parent bd5940a commit 53e802b

2 files changed

Lines changed: 131 additions & 0 deletions

File tree

‎crates/processing_pyo3/src/graphics.rs‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,36 @@ impl Light {
418418
let v = extract_vec3(args)?;
419419
transform_look_at(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}")))
420420
}
421+
422+
#[pyo3(signature = (*args))]
423+
pub fn translate(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> {
424+
let v = extract_vec3(args)?;
425+
transform_translate(self.entity, v).map_err(rt_err)
426+
}
427+
428+
pub fn rotate_x(&self, angle: f32) -> PyResult<()> {
429+
transform_rotate_x(self.entity, angle).map_err(rt_err)
430+
}
431+
432+
pub fn rotate_y(&self, angle: f32) -> PyResult<()> {
433+
transform_rotate_y(self.entity, angle).map_err(rt_err)
434+
}
435+
436+
pub fn rotate_z(&self, angle: f32) -> PyResult<()> {
437+
transform_rotate_z(self.entity, angle).map_err(rt_err)
438+
}
439+
440+
/// Rotate by `angle` radians around `axis`.
441+
#[pyo3(signature = (angle, *axis))]
442+
pub fn rotate_axis(&self, angle: f32, axis: &Bound<'_, PyTuple>) -> PyResult<()> {
443+
let axis = extract_vec3(axis)?;
444+
transform_rotate_axis(self.entity, angle, axis).map_err(rt_err)
445+
}
446+
447+
/// Back to the origin with no rotation.
448+
pub fn reset(&self) -> PyResult<()> {
449+
transform_reset(self.entity).map_err(rt_err)
450+
}
421451
}
422452

423453
// TODO: implement `light_destroy`

‎crates/processing_pyo3/src/material.rs‎

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ pub(crate) fn py_to_shader_value(value: &Bound<'_, PyAny>) -> PyResult<shader_va
4747
return Ok(shader_value::ShaderValue::Grid(grid.entity));
4848
}
4949

50+
// row-major like `get_matrix()`, WGSL wants columns
51+
if let Ok(rows) = value.extract::<[f32; 16]>() {
52+
let m = bevy::math::Mat4::from_cols_array(&rows).transpose();
53+
return Ok(shader_value::ShaderValue::Mat4(m.to_cols_array()));
54+
}
55+
if let Ok(rows) = value.extract::<[[f32; 4]; 4]>() {
56+
let m = bevy::math::Mat4::from_cols_array_2d(&rows).transpose();
57+
return Ok(shader_value::ShaderValue::Mat4(m.to_cols_array()));
58+
}
59+
5060
if let Ok(v) = value.extract::<[f32; 4]>() {
5161
return Ok(shader_value::ShaderValue::Float4(v));
5262
}
@@ -88,6 +98,32 @@ fn apply_albedo(entity: Entity, value: &Bound<'_, PyAny>) -> PyResult<()> {
8898
)))
8999
}
90100

101+
fn apply_emissive(entity: Entity, value: &Bound<'_, PyAny>) -> PyResult<()> {
102+
let rt = |e| PyRuntimeError::new_err(format!("{e}"));
103+
if let Ok(buf) = value.extract::<PyRef<Buffer>>() {
104+
return material_set_emissive_buffer(entity, buf.entity).map_err(rt);
105+
}
106+
// emissive is a linear radiance, not an sRGB color
107+
let linear: bevy::color::LinearRgba = if let Ok(c) = value.extract::<PyRef<PyColor>>() {
108+
c.0.into()
109+
} else if let Ok([r, g, b, a]) = value.extract::<[f32; 4]>() {
110+
bevy::color::LinearRgba::new(r, g, b, a)
111+
} else if let Ok([r, g, b]) = value.extract::<[f32; 3]>() {
112+
bevy::color::LinearRgba::new(r, g, b, 1.0)
113+
} else {
114+
return Err(PyRuntimeError::new_err(format!(
115+
"unsupported emissive type: {} (expected Color, Buffer, or [r,g,b,(a)])",
116+
value.get_type().name()?
117+
)));
118+
};
119+
material_set(
120+
entity,
121+
"emissive",
122+
shader_value::ShaderValue::Float4([linear.red, linear.green, linear.blue, linear.alpha]),
123+
)
124+
.map_err(rt)
125+
}
126+
91127
fn py_truthy(value: &Bound<'_, PyAny>) -> PyResult<bool> {
92128
value
93129
.extract::<bool>()
@@ -100,6 +136,7 @@ fn apply_kwargs(entity: Entity, kwargs: &Bound<'_, PyDict>) -> PyResult<()> {
100136
let rt = |e| PyRuntimeError::new_err(format!("{e}"));
101137
match name.as_str() {
102138
"albedo" => apply_albedo(entity, &value)?,
139+
"emissive" => apply_emissive(entity, &value)?,
103140
"unlit" => material_set_unlit(entity, py_truthy(&value)?).map_err(rt)?,
104141
"double_sided" => material_set_double_sided(entity, py_truthy(&value)?).map_err(rt)?,
105142
"depth_write" => material_set_depth_write(entity, py_truthy(&value)?).map_err(rt)?,
@@ -166,6 +203,70 @@ impl Material {
166203
};
167204
apply_kwargs(self.entity, kwargs)
168205
}
206+
207+
/// Base color: a `Color`, `[r, g, b, (a)]`, or a per-instance `Buffer`.
208+
pub fn albedo(&self, value: &Bound<'_, PyAny>) -> PyResult<()> {
209+
apply_albedo(self.entity, value)
210+
}
211+
212+
pub fn metalness(&self, value: f32) -> PyResult<()> {
213+
self.set_pbr("metallic", value)
214+
}
215+
216+
pub fn roughness(&self, value: f32) -> PyResult<()> {
217+
self.set_pbr("roughness", value)
218+
}
219+
220+
pub fn reflectance(&self, value: f32) -> PyResult<()> {
221+
self.set_pbr("reflectance", value)
222+
}
223+
224+
/// Emitted light: a `Color`, linear `[r, g, b, (a)]`, or a per-instance `Buffer`.
225+
pub fn emissive(&self, value: &Bound<'_, PyAny>) -> PyResult<()> {
226+
apply_emissive(self.entity, value)
227+
}
228+
229+
pub fn opaque(&self) -> PyResult<()> {
230+
material_set_alpha_mode(self.entity, 0, 0.0).map_err(rt)
231+
}
232+
233+
/// Discards fragments with alpha below `cutoff`.
234+
#[pyo3(signature = (cutoff=0.5))]
235+
pub fn mask(&self, cutoff: f32) -> PyResult<()> {
236+
material_set_alpha_mode(self.entity, 1, cutoff).map_err(rt)
237+
}
238+
239+
pub fn transparent(&self) -> PyResult<()> {
240+
material_set_alpha_mode(self.entity, 2, 0.0).map_err(rt)
241+
}
242+
243+
#[pyo3(signature = (on=true))]
244+
pub fn double_sided(&self, on: bool) -> PyResult<()> {
245+
material_set_double_sided(self.entity, on).map_err(rt)
246+
}
247+
248+
#[pyo3(signature = (on=true))]
249+
pub fn depth_write(&self, on: bool) -> PyResult<()> {
250+
material_set_depth_write(self.entity, on).map_err(rt)
251+
}
252+
253+
/// Blends with what's behind it using a `BlendMode` (`ADD`, `MULTIPLY`, a custom one, ...).
254+
pub fn blend_mode(&self, mode: &crate::graphics::PyBlendMode) -> PyResult<()> {
255+
let state = mode
256+
.blend_state
257+
.unwrap_or(bevy::render::render_resource::BlendState::ALPHA_BLENDING);
258+
material_set_custom_blend(self.entity, state).map_err(rt)
259+
}
260+
}
261+
262+
fn rt(e: impl std::fmt::Display) -> PyErr {
263+
PyRuntimeError::new_err(format!("{e}"))
264+
}
265+
266+
impl Material {
267+
fn set_pbr(&self, name: &str, value: f32) -> PyResult<()> {
268+
material_set(self.entity, name, shader_value::ShaderValue::Float(value)).map_err(rt)
269+
}
169270
}
170271

171272
impl Drop for Material {

0 commit comments

Comments
 (0)