@@ -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+
91127fn 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
171272impl Drop for Material {
0 commit comments