Skip to main content

khora_core/asset/materials/
mod.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Defines the core traits and material types for the rendering system.
16
17mod alpha_mode;
18mod emissive;
19mod standard;
20mod unlit;
21mod wireframe;
22
23pub use alpha_mode::*;
24pub use emissive::*;
25pub use standard::*;
26pub use unlit::*;
27pub use wireframe::*;
28
29use std::any::Any;
30
31use super::Asset;
32
33/// Helper trait to allow downcasting `dyn Material` trait objects to their concrete types.
34pub trait AsAny {
35    /// Returns a reference to the inner value as `&dyn Any`.
36    fn as_any(&self) -> &dyn Any;
37}
38
39impl<T: Any> AsAny for T {
40    fn as_any(&self) -> &dyn Any {
41        self
42    }
43}
44
45/// Supertrait that gives every material a `clone_box` without making `Material`
46/// itself depend on the non-object-safe `Clone`. The blanket impl below covers
47/// every `T: Material + Clone`, so concrete materials implement nothing extra.
48pub trait MaterialClone {
49    /// Clones `self` into a new boxed `dyn Material`.
50    fn clone_box(&self) -> Box<dyn Material>;
51}
52
53impl<T: Material + Clone + 'static> MaterialClone for T {
54    fn clone_box(&self) -> Box<dyn Material> {
55        Box::new(self.clone())
56    }
57}
58
59/// A trait for types that can be used as a material.
60///
61/// A material defines the surface properties of an object being rendered,
62/// influencing how it interacts with light and determining which shader
63/// (`RenderPipeline`) is used to draw it.
64pub trait Material: Asset + AsAny + MaterialClone {
65    /// Returns the base color (albedo or diffuse) of the material.
66    /// Default implementation is White.
67    fn base_color(&self) -> crate::math::LinearRgba {
68        crate::math::LinearRgba::WHITE
69    }
70
71    /// Returns the emissive color of the material.
72    /// Default implementation is Black.
73    fn emissive_color(&self) -> crate::math::LinearRgba {
74        crate::math::LinearRgba::BLACK
75    }
76
77    /// Returns the specular power or roughness conversion for the material.
78    /// Default implementation is 32.0.
79    fn specular_power(&self) -> f32 {
80        32.0
81    }
82
83    /// Returns the ambient color modifier for the material.
84    /// Default implementation is (0.1, 0.1, 0.1, 0.0).
85    fn ambient_color(&self) -> crate::math::LinearRgba {
86        crate::math::LinearRgba::new(0.1, 0.1, 0.1, 0.0)
87    }
88
89    /// Returns the metallic factor (0.0 = dielectric, 1.0 = metal).
90    /// Default implementation is 0.0.
91    fn metallic(&self) -> f32 {
92        0.0
93    }
94
95    /// Returns the roughness factor (0.0 = smooth, 1.0 = rough).
96    /// Default implementation is 1.0.
97    fn roughness(&self) -> f32 {
98        1.0
99    }
100
101    /// Returns the UUID of the base-color (albedo) texture, if any.
102    /// Default implementation is `None` (untextured).
103    fn base_color_texture(&self) -> Option<crate::asset::AssetUUID> {
104        None
105    }
106
107    /// Returns the UUID of the metallic-roughness texture, if any
108    /// (glTF convention: B=metallic, G=roughness).
109    /// Default implementation is `None`.
110    fn metallic_roughness_texture(&self) -> Option<crate::asset::AssetUUID> {
111        None
112    }
113
114    /// Returns the UUID of the tangent-space normal map, if any.
115    /// Default implementation is `None`.
116    fn normal_map(&self) -> Option<crate::asset::AssetUUID> {
117        None
118    }
119
120    /// Returns the UUID of the emissive texture, if any.
121    /// Default implementation is `None`.
122    fn emissive_texture(&self) -> Option<crate::asset::AssetUUID> {
123        None
124    }
125
126    /// Returns the UUID of the ambient-occlusion map, if any (red channel).
127    /// Attenuates the indirect (ambient/IBL) term only, never direct light.
128    /// Default implementation is `None`.
129    fn occlusion_map(&self) -> Option<crate::asset::AssetUUID> {
130        None
131    }
132
133    /// Returns the material's alpha (transparency) mode. For [`AlphaMode::Mask`]
134    /// the contained value is the alpha-cutoff threshold: fragments below it are
135    /// discarded. Default implementation is [`AlphaMode::Opaque`].
136    fn alpha_mode(&self) -> AlphaMode {
137        AlphaMode::Opaque
138    }
139
140    /// Whether the material is rendered double-sided (back faces not culled).
141    /// `false` (the default) culls back faces for correctly-wound meshes.
142    fn double_sided(&self) -> bool {
143        false
144    }
145}
146
147/// This is the key to our type-erased material handle system.
148/// We explicitly tell the compiler that a boxed, dynamic Material trait
149/// object can itself be treated as a valid Asset. This allows it to be
150/// stored inside an AssetHandle.
151impl Asset for Box<dyn Material> {}
152
153impl Clone for Box<dyn Material> {
154    fn clone(&self) -> Self {
155        self.clone_box()
156    }
157}