1mod cable;
2mod eyerefract;
3mod lightmappedgeneric;
4mod modulate;
5mod refract;
6mod replacements;
7mod sky;
8mod sprite;
9mod spritecard;
10mod subrect;
11mod unlitgeneric;
12mod unlittwotexture;
13mod vertexlitgeneric;
14mod water;
15mod worldvertextransition;
16
17use crate::TextureTransform;
18pub use cable::CableMaterial;
19pub use eyerefract::EyeRefractMaterial;
20pub use lightmappedgeneric::LightMappedGenericMaterial;
21pub use modulate::ModulateMaterial;
22pub use refract::RefractMaterial;
23pub use replacements::{ReplacementPattern, ReplacementTemplate, ReplacementsMaterial};
24use serde::{Deserialize, Deserializer, Serialize};
25pub use sky::SkyMaterial;
26pub use sprite::{SpriteMaterial, SpriteOrientation};
27pub use spritecard::SpriteCardMaterial;
28pub use subrect::SubRectMaterial;
29pub use unlitgeneric::UnlitGenericMaterial;
30pub use unlittwotexture::UnlitTwoTextureMaterial;
31use vdf_reader::entry::{Entry, Table};
32use vdf_reader::error::UnknownError;
33use vdf_reader::{from_entry, VdfError};
34pub use vertexlitgeneric::VertexLitGenericMaterial;
35pub use water::WaterMaterial;
36pub use worldvertextransition::WorldVertexTransitionMaterial;
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39#[non_exhaustive]
40#[serde(rename_all = "lowercase")]
41pub enum Material {
42 LightMappedGeneric(LightMappedGenericMaterial),
43 VertexLitGeneric(VertexLitGenericMaterial),
44 #[serde(rename = "vertexlitgeneric_dx6")]
45 VertexLitGenericDx6(VertexLitGenericMaterial),
46 UnlitGeneric(UnlitGenericMaterial),
47 UnlitTwoTexture(UnlitTwoTextureMaterial),
48 Water(WaterMaterial),
49 WorldVertexTransition(WorldVertexTransitionMaterial),
50 EyeRefract(EyeRefractMaterial),
51 SubRect(SubRectMaterial),
52 Sprite(SpriteMaterial),
53 SpriteCard(SpriteCardMaterial),
54 Cable(CableMaterial),
55 Refract(RefractMaterial),
56 Modulate(ModulateMaterial),
57 DecalModulate(ModulateMaterial),
58 Sky(SkyMaterial),
59 Replacements(ReplacementsMaterial),
60 Patch(PatchMaterial),
61}
62
63impl Material {
64 pub fn resolve<E, Loader>(self, loader: Loader) -> Result<Material, E>
66 where
67 Loader: FnOnce(&str) -> Result<String, E>,
68 E: From<VdfError>,
69 {
70 match self {
71 Material::Patch(patch) => patch.resolve(loader),
72 mat => Ok(mat),
73 }
74 }
75
76 pub fn translucent(&self) -> bool {
77 match self {
78 Material::LightMappedGeneric(mat) => mat.translucent,
79 Material::VertexLitGeneric(mat) => mat.translucent,
80 Material::VertexLitGenericDx6(mat) => mat.translucent,
81 Material::UnlitGeneric(mat) => mat.translucent,
82 Material::UnlitTwoTexture(mat) => mat.translucent,
83 Material::WorldVertexTransition(mat) => mat.translucent,
84 Material::Sprite(mat) => mat.translucent,
85 Material::Water(_) => true,
86 _ => false,
87 }
88 }
89
90 pub fn no_cull(&self) -> bool {
91 match self {
92 Material::LightMappedGeneric(mat) => mat.no_cull,
93 Material::VertexLitGeneric(mat) => mat.no_cull,
94 Material::VertexLitGenericDx6(mat) => mat.no_cull,
95 Material::UnlitGeneric(mat) => mat.no_cull,
96 Material::UnlitTwoTexture(mat) => mat.no_cull,
97 Material::WorldVertexTransition(mat) => mat.no_cull,
98 Material::Water(_) => true,
99 _ => false,
100 }
101 }
102
103 pub fn alpha_test(&self) -> Option<f32> {
104 match self {
105 Material::LightMappedGeneric(mat) => mat.alpha_test.then_some(mat.alpha_test_reference),
106 Material::VertexLitGeneric(mat) => mat.alpha_test.then_some(mat.alpha_test_reference),
107 Material::VertexLitGenericDx6(mat) => {
108 mat.alpha_test.then_some(mat.alpha_test_reference)
109 }
110 Material::UnlitGeneric(mat) => mat.alpha_test.then_some(mat.alpha_test_reference),
111 Material::UnlitTwoTexture(mat) => mat.alpha_test.then_some(mat.alpha_test_reference),
112 Material::WorldVertexTransition(mat) => {
113 mat.alpha_test.then_some(mat.alpha_test_reference)
114 }
115 Material::Sprite(mat) => mat.alpha_test.then_some(mat.alpha_test_reference),
116 Material::Water(_) => None,
117 _ => None,
118 }
119 }
120
121 pub fn base_texture(&self) -> Option<&str> {
122 match self {
123 Material::LightMappedGeneric(mat) => Some(&mat.base_texture),
124 Material::VertexLitGeneric(mat) => mat.base_texture.as_deref(),
125 Material::VertexLitGenericDx6(mat) => mat.base_texture.as_deref(),
126 Material::UnlitGeneric(mat) => mat.base_texture.as_deref(),
127 Material::UnlitTwoTexture(mat) => mat.base_texture.as_deref(),
128 Material::WorldVertexTransition(mat) => Some(&mat.base_texture),
129 Material::Sprite(mat) => Some(&mat.base_texture),
130 Material::Water(mat) => mat.base_texture.as_deref(),
131 Material::EyeRefract(mat) => Some(&mat.iris),
132 _ => None,
133 }
134 }
135
136 pub fn base_texture_transform(&self) -> Option<&TextureTransform> {
137 match self {
138 Material::LightMappedGeneric(mat) => Some(&mat.base_texture_transform),
139 Material::VertexLitGeneric(mat) => Some(&mat.base_texture_transform),
140 Material::VertexLitGenericDx6(mat) => Some(&mat.base_texture_transform),
141 Material::UnlitTwoTexture(mat) => Some(&mat.base_texture_transform),
142 Material::WorldVertexTransition(mat) => Some(&mat.base_texture_transform),
143 _ => None,
144 }
145 }
146
147 pub fn bump_map(&self) -> Option<&str> {
148 match self {
149 Material::LightMappedGeneric(mat) => mat.bump_map.as_deref(),
150 Material::VertexLitGeneric(mat) => mat.bump_map.as_deref(),
151 Material::VertexLitGenericDx6(mat) => mat.bump_map.as_deref(),
152 Material::UnlitGeneric(mat) => mat.bump_map.as_deref(),
153 Material::UnlitTwoTexture(mat) => mat.bump_map.as_deref(),
154 Material::WorldVertexTransition(mat) => mat.bump_map.as_deref(),
155 Material::Water(mat) => mat.bump_map.as_deref(),
156 _ => None,
157 }
158 }
159
160 pub fn surface_prop(&self) -> Option<&str> {
161 match self {
162 Material::LightMappedGeneric(mat) => mat.surface_prop.as_deref(),
163 Material::UnlitGeneric(mat) => mat.surface_prop.as_deref(),
164 Material::UnlitTwoTexture(mat) => mat.surface_prop.as_deref(),
165 Material::WorldVertexTransition(mat) => mat.surface_prop.as_deref(),
166 _ => None,
167 }
168 }
169
170 pub fn alpha(&self) -> f32 {
171 match self {
172 Material::LightMappedGeneric(mat) => mat.alpha,
173 Material::VertexLitGeneric(mat) => mat.alpha,
174 Material::VertexLitGenericDx6(mat) => mat.alpha,
175 Material::UnlitGeneric(mat) => mat.alpha,
176 Material::UnlitTwoTexture(mat) => mat.alpha,
177 Material::Sprite(mat) => mat.alpha,
178 Material::WorldVertexTransition(mat) => mat.alpha,
179 _ => 1.0,
180 }
181 }
182
183 pub fn ignore_z_test(&self) -> bool {
184 match self {
185 Material::LightMappedGeneric(mat) => mat.ignore_z,
186 Material::VertexLitGeneric(mat) => mat.ignore_z,
187 Material::VertexLitGenericDx6(mat) => mat.ignore_z,
188 Material::UnlitGeneric(mat) => mat.ignore_z,
189 Material::UnlitTwoTexture(mat) => mat.ignore_z,
190 Material::WorldVertexTransition(mat) => mat.ignore_z,
191 _ => false,
192 }
193 }
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct PatchMaterial {
198 #[serde(deserialize_with = "deserialize_path")]
199 include: String,
200 #[serde(default)]
201 replace: Table,
202}
203
204impl PatchMaterial {
205 pub fn resolve<E, Loader>(&self, loader: Loader) -> Result<Material, E>
207 where
208 Loader: FnOnce(&str) -> Result<String, E>,
209 E: From<VdfError>,
210 {
211 let base = loader(&self.include)?.to_ascii_lowercase();
212 let mut material = Table::load_from_str(&base)?;
213
214 let material_values = match material.iter_mut().next() {
215 Some((_, Entry::Table(table))) => table,
216 _ => {
217 return Err(VdfError::from(UnknownError::from(
218 "included vdf doesn't look like a material",
219 ))
220 .into())
221 }
222 };
223 for (key, value) in self.replace.iter() {
224 material_values.insert(key.clone(), value.clone());
225 }
226
227 Ok(from_entry(Entry::Table(material))?)
228 }
229}
230
231trait PathLike {
232 fn normalize(self) -> Self;
233}
234
235impl PathLike for String {
236 fn normalize(self) -> Self {
237 self.replace('\\', "/")
238 }
239}
240
241impl<T: PathLike> PathLike for Option<T> {
242 fn normalize(self) -> Self {
243 self.map(T::normalize)
244 }
245}
246
247fn deserialize_path<'de, T: PathLike + Deserialize<'de>, D: Deserializer<'de>>(
249 deserializer: D,
250) -> Result<T, D::Error> {
251 Ok(T::deserialize(deserializer)?.normalize())
252}