Skip to main content

rpfm_lib/files/rigidmodel/materials/
mod.rs

1//---------------------------------------------------------------------------//
2// Copyright (c) 2017-2026 Ismael Gutiérrez González. All rights reserved.
3//
4// This file is part of the Rusted PackFile Manager (RPFM) project,
5// which can be found here: https://github.com/Frodo45127/rpfm.
6//
7// This file is licensed under the MIT license, which can be found here:
8// https://github.com/Frodo45127/rpfm/blob/master/LICENSE.
9//---------------------------------------------------------------------------//
10
11//! Material system for RigidModel files.
12//!
13//! # Overview
14//!
15//! Materials define how meshes are rendered in Total War games. Each material type
16//! has specific properties that control textures, shaders, vertex formats, and
17//! rendering behavior. The material system supports 40+ different material types
18//! for various rendering scenarios.
19//!
20//! # Material Types
21//!
22//! Materials are categorized by their rendering purpose:
23//!
24//! ## Standard Rendering
25//! - **DefaultMaterial (68)**: Full-featured material for most meshes
26//! - **Decal (71)**: Applied decals on surfaces
27//! - **Tree (74)**, **TreeLeaf (75)**: Vegetation rendering
28//! - **Grass (69)**: Grass
29//! - **Water (83)**: Water surfaces
30//! - **Unlit (84)**: No lighting calculations
31//!
32//! ## Skeletal Animation (Weighted)
33//! - **WeightedSkin (70)**: Character skin with bone weights
34//! - **WeightedCloth (58)**, **Cloth (60)**: Cloth simulation
35//! - **Weighted (65)**: Generic weighted material
36//!
37//! ## Terrain
38//! - **RsTerrain (66)**: Minimal terrain material
39//! - **CustomTerrain (49)**, **GlobalTerrain (98)**: Terrain variants
40//! - **WeightedTextureBlend (96)**, **TerrainBlend (86)**: Texture blending
41//! - **TiledDirtmap (63)**: Tiled dirt textures
42//!
43//! ## Special Effects
44//! - **Collision (61)**, **CollisionShape (62)**: Collision meshes (invisible)
45//! - **DebugGeometry (46)**: Debug visualization
46//! - **PointLight (38)**, **StaticPointLight (45)**: Light sources
47//! - **Rope (93)**: Rope rendering
48//!
49//! ## Projected Decals
50//! - **ProjectedDecal (67)**, **ProjectedDecalV2 (87)**, **ProjectedDecalV3 (95)**, **ProjectedDecalV4 (97)**
51//!
52//! # Material Data Structure
53//!
54//! Materials contain:
55//! - **Vertex format**: Determines vertex data layout
56//! - **Textures**: Diffuse, normal, specular, masks, etc.
57//! - **Transformation matrices**: 3x4 matrices for positioning/attachment
58//! - **Attachment points**: Named locations for effects/weapons
59//! - **Shader parameters**: Strings, floats, integers, vectors
60//! - **Cloth-specific data**: Physics simulation parameters (cloth materials only)
61//!
62//! # Texture Types
63//!
64//! Materials reference textures by type:
65//! - **Diffuse (0)**: Base color texture
66//! - **Normal (1)**: Normal mapping for surface detail
67//! - **Specular (11)**, **GlossMap (12)**: Reflectivity
68//! - **Mask (3)**: Alpha/transparency mask
69//! - **AmbientOcclusion (5)**: Baked ambient lighting
70//! - **Decal variants**: Special decal textures
71//!
72//! # Implementation Details
73//!
74//! Different material types have different data structures:
75//! - **Default materials**: Full data (textures, params, attachment points)
76//! - **RsTerrain**: Minimal (name + 5 unknown u32 fields)
77//! - **WeightedTextureBlend**: Name + 6 unknown u32 fields
78//! - **AlphaBlend**: Name only
79//! - **Cloth**: Default data + cloth simulation sections (uk_7, uk_8, uk_9)
80//!
81//! See individual material variant modules for format-specific details.
82
83use getset::{Getters, MutGetters, Setters};
84use nalgebra::{Matrix3x4, Vector3, Vector4};
85use serde::{Deserialize, Serialize};
86
87use crate::binary::{ReadBytes, WriteBytes};
88use crate::error::{Result, RLibError};
89
90use super::vertices::VertexFormat;
91use super::{PADDED_SIZE_32, PADDED_SIZE_256};
92
93mod alpha_blend;
94mod cloth;
95mod default;
96mod rs_terrain;
97mod rs_river;
98mod projected_decal_v4;
99mod weighted_texture_blend;
100
101//---------------------------------------------------------------------------//
102//                              Enum & Structs
103//---------------------------------------------------------------------------//
104
105/// Material type identifier determining rendering behavior and data structure.
106///
107/// Each material type corresponds to a specific shader and vertex format combination.
108/// The numeric value is stored in the RigidModel file format as a u16.
109///
110/// # Categories
111///
112/// - **22-46**: Effects and special materials (BowWave, PointLight, DebugGeometry)
113/// - **49-64**: Terrain and campaign materials
114/// - **65-82**: Weighted (skeletal) materials and variants
115/// - **83-100**: Water, unlit, terrain blends, projected decals
116#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
117#[repr(u16)]
118pub enum MaterialType {
119    /// Boat bow wave effect (water displacement).
120    BowWave = 22,
121    /// Non-rendered material (collision, occlusion, etc.).
122    NonRenderable = 26,
123    /// Texture combo with vertex-based wind animation.
124    TextureComboVertexWind = 29,
125    /// Combined texture material.
126    TextureCombo = 30,
127    /// Waterfall decal effect.
128    DecalWaterfall = 31,
129    /// Simplified standard material.
130    StandardSimple = 32,
131    /// Campaign map tree rendering.
132    CampaignTrees = 34,
133    /// Dynamic point light source.
134    PointLight = 38,
135    /// Static baked point light.
136    StaticPointLight = 45,
137    /// Debug visualization geometry (wireframe, normals, etc.).
138    DebugGeometry = 46,
139    /// Custom terrain material variant.
140    CustomTerrain = 49,
141    /// Cloth material with skeletal weights and physics.
142    WeightedCloth = 58,
143    /// Cloth material with physics simulation.
144    Cloth = 60,
145    /// Collision mesh (invisible, physics only).
146    Collision = 61,
147    /// Collision shape variant.
148    CollisionShape = 62,
149    /// Tiled dirt texture mapping.
150    TiledDirtmap = 63,
151    /// Ship ambient mapping (possibly incorrect identification).
152    ShipAmbientmap = 64,
153    /// Generic weighted material (skeletal animation).
154    Weighted = 65,
155    /// Minimal terrain material (Rome 2 style).
156    RsTerrain = 66,
157    /// Projected decal (first version).
158    ProjectedDecal = 67,
159    /// Default full-featured material (most common).
160    #[default]
161    DefaultMaterial = 68,
162    /// Grass and vegetation material.
163    Grass = 69,
164    /// Weighted skin material (characters with bone weights).
165    WeightedSkin = 70,
166    /// Surface decal material.
167    Decal = 71,
168    /// Decal with dirt mapping.
169    DecalDirtmap = 72,
170    /// Dirt map material.
171    Dirtmap = 73,
172    /// Tree trunk/branch material.
173    Tree = 74,
174    /// Tree leaf material (alpha transparency).
175    TreeLeaf = 75,
176    /// Weighted decal (animated decals on characters).
177    WeightedDecal = 77,
178    /// Weighted decal with dirt mapping.
179    WeightedDecalDirtmap = 78,
180    /// Weighted dirt mapping.
181    WeightedDirtmap = 79,
182    /// Weighted skin with decal overlay.
183    WeightedSkinDecal = 80,
184    /// Weighted skin with decal and dirt mapping.
185    WeightedSkinDecalDirtmap = 81,
186    /// Weighted skin with dirt mapping.
187    WeightedSkinDirtmap = 82,
188    /// Water surface material.
189    Water = 83,
190    /// Unlit material (no lighting calculations, full bright).
191    Unlit = 84,
192    /// Weighted unlit material.
193    WeightedUnlit = 85,
194    /// Terrain texture blending material.
195    TerrainBlend = 86,
196    /// Projected decal version 2.
197    ProjectedDecalV2 = 87,
198    /// Ignored/placeholder material.
199    Ignore = 88,
200    /// Billboard-style tree material (always faces camera).
201    TreeBillboardMaterial = 89,
202    /// River rendering material (Rome 2 style).
203    RsRiver = 90,
204    /// Water displacement volume (physics interaction).
205    WaterDisplaceVolume = 91,
206    /// Rope rendering material.
207    Rope = 93,
208    /// Campaign map vegetation.
209    CampaignVegetation = 94,
210    /// Projected decal version 3.
211    ProjectedDecalV3 = 95,
212    /// Weighted texture blending.
213    WeightedTextureBlend = 96,
214    /// Projected decal version 4 (latest).
215    ProjectedDecalV4 = 97,
216    /// Global terrain material.
217    GlobalTerrain = 98,
218    /// Overlay decal material.
219    DecalOverlay = 99,
220    /// Alpha-blended material.
221    AlphaBlend = 100,
222}
223
224/// Complete material definition with textures, transforms, and shader parameters.
225///
226/// Materials contain all rendering data except geometry. The exact fields present
227/// depend on the material type - some types use only a subset of these fields.
228///
229/// # Field Organization
230///
231/// - **Identification**: `vertex_format`, `name`
232/// - **Unknown fields**: `uk_1` through `uk_6` (only in certain material types)
233/// - **Textures**: `texture_directory`, `textures` list
234/// - **Transforms**: `v_pivot`, `matrix1/2/3`, matrix indices
235/// - **Attachments**: `attachment_points` for effects/weapons
236/// - **Shader params**: `params_string`, `params_f32`, `params_i32`, `params_vector4df32`
237/// - **Cloth physics**: `uk_7`, `uk_8`, `uk_9` (cloth materials only)
238///
239/// # Matrix Storage
240///
241/// 3x4 matrices are stored in the same format as skeleton bind pose matrices.
242/// The implicit fourth row `[0, 0, 0, 1]` must be added when converting to 4x4.
243#[derive(Clone, Debug, Default, PartialEq, Getters, MutGetters, Setters, Serialize, Deserialize)]
244#[getset(get = "pub", get_mut = "pub", set = "pub")]
245pub struct Material {
246    /// Vertex format determining vertex data layout.
247    vertex_format: VertexFormat,
248    
249    /// Material name (human-readable identifier).
250    name: String,
251
252    /// Unknown field 1 (only in RsTerrain and projected decal materials).
253    uk_1: u32,
254    /// Unknown field 2 (only in RsTerrain and projected decal materials).
255    uk_2: u32,
256    /// Unknown field 3 (only in RsTerrain and projected decal materials).
257    uk_3: u32,
258    /// Unknown field 4 (only in RsTerrain and projected decal materials).
259    uk_4: u32,
260    /// Unknown field 5 (only in RsTerrain and projected decal materials).
261    uk_5: u32,
262    /// Unknown field 6 (only in RsTerrain and projected decal materials).
263    uk_6: u32,
264
265    /// Directory path for texture files (relative to game data directory).
266    texture_directory: String,
267
268    /// Filter settings (NOT part of file format, runtime-only).
269    filters: String,
270
271    /// Padding byte 0 (alignment/reserved).
272    padding_byte0: u8,
273    /// Padding byte 1 (alignment/reserved).
274    padding_byte1: u8,
275
276    /// Pivot point for transformations.
277    v_pivot: Vector3<f32>,
278    
279    /// Transform matrix 1 (3x4, implicit 4th row: [0, 0, 0, 1]).
280    matrix1: Matrix3x4<f32>,
281    /// Transform matrix 2 (3x4, implicit 4th row: [0, 0, 0, 1]).
282    matrix2: Matrix3x4<f32>,
283    /// Transform matrix 3 (3x4, implicit 4th row: [0, 0, 0, 1]).
284    matrix3: Matrix3x4<f32>,
285
286    /// Matrix index in hierarchy.
287    i_matrix_index: i32,
288    /// Parent matrix index for hierarchical transforms.
289    i_parent_matrix_index: i32,
290
291    /// Additional padding bytes (variable length).
292    sz_padding: Vec<u8>,
293
294    /// Named attachment points for weapons, effects, banners, etc.
295    attachment_points: Vec<AttachmentPointEntry>,
296    
297    /// Texture list with type and path for each texture.
298    textures: Vec<Texture>,
299    
300    /// String shader parameters (index, value pairs).
301    params_string: Vec<(i32, String)>,
302    /// Float shader parameters (index, value pairs).
303    params_f32: Vec<(i32, f32)>,
304    /// Integer shader parameters (index, value pairs).
305    params_i32: Vec<(i32, i32)>,
306    /// Vector4 shader parameters (index, value pairs).
307    params_vector4df32: Vec<(i32, Vector4<f32>)>,
308
309    /// Cloth physics data section 1 (cloth materials only, format undocumented).
310    uk_7: Vec<Uk7>,
311    /// Cloth physics data section 2 (cloth materials only, format undocumented).
312    uk_8: Vec<Uk8>,
313    /// Cloth physics data section 3 (cloth materials only, format undocumented).
314    uk_9: Vec<Uk9>,
315}
316
317/// Named attachment point for visual effects, weapons, or equipment.
318///
319/// Attachment points define locations on a model where other objects can be attached,
320/// such as weapon hardpoints, banner poles, or particle effect emitters.
321#[derive(Clone, Debug, Default, PartialEq, Getters, MutGetters, Setters, Serialize, Deserialize)]
322#[getset(get = "pub", get_mut = "pub", set = "pub")]
323pub struct AttachmentPointEntry {
324    /// Attachment point name (e.g., "weapon_1", "engine_exhaust").
325    name: String,
326    
327    /// 3x4 transform matrix positioning the attachment point (implicit 4th row: [0, 0, 0, 1]).
328    matrix: Matrix3x4<f32>,
329    
330    /// Bone ID for skeletal attachment (0 for non-skeletal).
331    bone_id: u32,
332}
333
334/// Texture reference with type and file path.
335#[derive(Clone, Debug, Default, PartialEq, Getters, MutGetters, Setters, Serialize, Deserialize)]
336#[getset(get = "pub", get_mut = "pub", set = "pub")]
337pub struct Texture {
338    /// Texture type/slot (diffuse, normal, specular, etc.).
339    tex_type: TextureType,
340    
341    /// Relative path to texture file (from `texture_directory`).
342    path: String,
343}
344
345/// Texture type identifier for shader texture slots.
346///
347/// Determines which shader texture slot this texture is bound to during rendering.
348/// The numeric value is stored as an i32 in the file format.
349#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
350#[repr(i32)]
351pub enum TextureType {
352    /// Base color/albedo texture.
353    #[default]
354    Diffuse = 0,
355    /// Normal map for surface detail (tangent space).
356    Normal = 1,
357    /// Alpha/transparency mask.
358    Mask = 3,
359    /// Baked ambient occlusion.
360    AmbientOcclusion = 5,
361    /// Tiling dirt texture (UV set 2).
362    TilingDirtUV2 = 7,
363    /// Dirt alpha mask.
364    DirtAlphaMask = 8,
365    /// Skin mask texture.
366    SkinMask = 10,
367    /// Specular reflectivity map.
368    Specular = 11,
369    /// Gloss/smoothness map (specular roughness).
370    GlossMap = 12,
371    /// Decal dirt map.
372    DecalDirtmap = 13,
373    /// Decal dirt mask.
374    DecalDirtmask = 14,
375    /// Decal alpha mask.
376    DecalMask = 15,
377    /// Damaged diffuse texture variant.
378    DiffuseDamage = 17,
379    /// PBR base color.
380    BaseColor = 27,
381    /// PBR material properties (metallic/roughness/AO packed).
382    MaterialMap = 29,
383}
384
385/// Cloth physics data structure 1 (format undocumented).
386///
387/// This structure appears only in cloth materials and likely contains cloth simulation
388/// parameters. The exact meaning of the fields is not yet reverse-engineered.
389#[derive(Clone, Debug, Default, PartialEq, Getters, MutGetters, Setters, Serialize, Deserialize)]
390#[getset(get = "pub", get_mut = "pub", set = "pub")]
391pub struct Uk7 {
392    /// Unknown integer field 1 (possibly constraint/spring index).
393    uk1: i32,
394    /// Unknown integer field 2 (possibly vertex/node index).
395    uk2: i32,
396    /// Unknown float field (possibly stiffness/damping coefficient).
397    uk3: f32,
398}
399
400/// Cloth physics data structure 2 (format undocumented).
401///
402/// This structure appears only in cloth materials and likely contains cloth simulation
403/// parameters. The exact meaning of the field is not yet reverse-engineered.
404#[derive(Clone, Debug, Default, PartialEq, Getters, MutGetters, Setters, Serialize, Deserialize)]
405#[getset(get = "pub", get_mut = "pub", set = "pub")]
406pub struct Uk8 {
407    /// Unknown integer field (possibly fixed vertex/anchor point index).
408    uk1: i32,
409}
410
411/// Cloth physics data structure 3 (format undocumented).
412///
413/// This structure appears only in cloth materials and likely contains cloth simulation
414/// parameters. The exact meaning of the fields is not yet reverse-engineered.
415#[derive(Clone, Debug, Default, PartialEq, Getters, MutGetters, Setters, Serialize, Deserialize)]
416#[getset(get = "pub", get_mut = "pub", set = "pub")]
417pub struct Uk9 {
418    /// Unknown integer field 1 (possibly triangle/face index).
419    uk1: i32,
420    /// Unknown integer field 2 (possibly collision group).
421    uk2: i32,
422    /// Unknown integer field 3 (possibly property flags).
423    uk3: i32,
424}
425
426//---------------------------------------------------------------------------//
427//                            Implementation
428//---------------------------------------------------------------------------//
429
430
431impl Material {
432
433    /// Reads a material from binary data based on the material type.
434    ///
435    /// Different material types have different binary layouts. This function
436    /// dispatches to the appropriate type-specific reader.
437    ///
438    /// # Errors
439    ///
440    /// Returns [`RLibError::DecodingRigidModelUnsupportedMaterialType`] if the
441    /// material type is not supported.
442    pub fn read<R: ReadBytes>(data: &mut R, mtype: MaterialType) -> Result<Self> {
443        Ok(match mtype {
444            MaterialType::RsTerrain => Self::read_rs_terrain(data)?,
445            MaterialType::RsRiver => Self::read_rs_river(data)?,
446            MaterialType::WeightedTextureBlend => Self::read_weighted_texture_blend(data)?,
447            //MaterialType::ProjectedDecalV4 => Self::read_projected_decal_v4(data)?,
448            MaterialType::AlphaBlend => Self::read_alpha_blend(data)?,
449            MaterialType::Cloth => Self::read_cloth(data)?,
450            MaterialType::ProjectedDecalV4 |
451            MaterialType::Water |
452            MaterialType::TiledDirtmap |
453            MaterialType::ShipAmbientmap |
454            MaterialType::TerrainBlend |
455            MaterialType::Weighted |
456            MaterialType::DefaultMaterial => Self::read_default(data)?,
457            _ => return Err(RLibError::DecodingRigidModelUnsupportedMaterialType(mtype.into()))
458        })
459    }
460
461    /// Writes a material to binary data based on the material type.
462    ///
463    /// Different material types have different binary layouts. This function
464    /// dispatches to the appropriate type-specific writer.
465    ///
466    /// # Errors
467    ///
468    /// Returns [`RLibError::DecodingRigidModelUnsupportedMaterialType`] if the
469    /// material type is not supported.
470    pub fn write<W: WriteBytes>(&self, buffer: &mut W, mtype: MaterialType) -> Result<()> {
471        match mtype {
472            MaterialType::RsTerrain => self.write_rs_terrain(buffer)?,
473            MaterialType::RsRiver => self.write_rs_river(buffer)?,
474            MaterialType::WeightedTextureBlend => self.write_weighted_texture_blend(buffer)?,
475            //MaterialType::ProjectedDecalV4 => self.write_projected_decal_v4(buffer)?,
476            MaterialType::AlphaBlend => self.write_alpha_blend(buffer)?,
477            MaterialType::Cloth => self.write_cloth(buffer)?,
478            MaterialType::ProjectedDecalV4 |
479            MaterialType::Water |
480            MaterialType::TiledDirtmap |
481            MaterialType::ShipAmbientmap |
482            MaterialType::TerrainBlend |
483            MaterialType::Weighted |
484            MaterialType::DefaultMaterial => self.write_default(buffer)?,
485            _ => return Err(RLibError::DecodingRigidModelUnsupportedMaterialType(mtype.into()))
486        }
487
488        Ok(())
489    }
490}
491
492impl TryFrom<u16> for MaterialType {
493    type Error = RLibError;
494    fn try_from(value: u16) -> Result<Self> {
495        match value {
496            _ if value == Self::BowWave as u16 => Ok(Self::BowWave),
497            _ if value == Self::NonRenderable as u16 => Ok(Self::NonRenderable),
498            _ if value == Self::TextureComboVertexWind as u16 => Ok(Self::TextureComboVertexWind),
499            _ if value == Self::TextureCombo as u16 => Ok(Self::TextureCombo),
500            _ if value == Self::DecalWaterfall as u16 => Ok(Self::DecalWaterfall),
501            _ if value == Self::StandardSimple as u16 => Ok(Self::StandardSimple),
502            _ if value == Self::CampaignTrees as u16 => Ok(Self::CampaignTrees),
503            _ if value == Self::PointLight as u16 => Ok(Self::PointLight),
504            _ if value == Self::StaticPointLight as u16 => Ok(Self::StaticPointLight),
505            _ if value == Self::DebugGeometry as u16 => Ok(Self::DebugGeometry),
506            _ if value == Self::CustomTerrain as u16 => Ok(Self::CustomTerrain),
507            _ if value == Self::WeightedCloth as u16 => Ok(Self::WeightedCloth),
508            _ if value == Self::Cloth as u16 => Ok(Self::Cloth),
509            _ if value == Self::Collision as u16 => Ok(Self::Collision),
510            _ if value == Self::CollisionShape as u16 => Ok(Self::CollisionShape),
511            _ if value == Self::TiledDirtmap as u16 => Ok(Self::TiledDirtmap),
512            _ if value == Self::ShipAmbientmap as u16 => Ok(Self::ShipAmbientmap),
513            _ if value == Self::Weighted as u16 => Ok(Self::Weighted),
514            _ if value == Self::RsTerrain as u16 => Ok(Self::RsTerrain),
515            _ if value == Self::ProjectedDecal as u16 => Ok(Self::ProjectedDecal),
516            _ if value == Self::DefaultMaterial as u16 => Ok(Self::DefaultMaterial),
517            _ if value == Self::Grass as u16 => Ok(Self::Grass),
518            _ if value == Self::WeightedSkin as u16 => Ok(Self::WeightedSkin),
519            _ if value == Self::Decal as u16 => Ok(Self::Decal),
520            _ if value == Self::DecalDirtmap as u16 => Ok(Self::DecalDirtmap),
521            _ if value == Self::Dirtmap as u16 => Ok(Self::Dirtmap),
522            _ if value == Self::Tree as u16 => Ok(Self::Tree),
523            _ if value == Self::TreeLeaf as u16 => Ok(Self::TreeLeaf),
524            _ if value == Self::WeightedDecal as u16 => Ok(Self::WeightedDecal),
525            _ if value == Self::WeightedDecalDirtmap as u16 => Ok(Self::WeightedDecalDirtmap),
526            _ if value == Self::WeightedDirtmap as u16 => Ok(Self::WeightedDirtmap),
527            _ if value == Self::WeightedSkinDecal as u16 => Ok(Self::WeightedSkinDecal),
528            _ if value == Self::WeightedSkinDecalDirtmap as u16 => Ok(Self::WeightedSkinDecalDirtmap),
529            _ if value == Self::WeightedSkinDirtmap as u16 => Ok(Self::WeightedSkinDirtmap),
530            _ if value == Self::Water as u16 => Ok(Self::Water),
531            _ if value == Self::Unlit as u16 => Ok(Self::Unlit),
532            _ if value == Self::WeightedUnlit as u16 => Ok(Self::WeightedUnlit),
533            _ if value == Self::TerrainBlend as u16 => Ok(Self::TerrainBlend),
534            _ if value == Self::ProjectedDecalV2 as u16 => Ok(Self::ProjectedDecalV2),
535            _ if value == Self::Ignore as u16 => Ok(Self::Ignore),
536            _ if value == Self::TreeBillboardMaterial as u16 => Ok(Self::TreeBillboardMaterial),
537            _ if value == Self::RsRiver as u16 => Ok(Self::RsRiver),
538            _ if value == Self::WaterDisplaceVolume as u16 => Ok(Self::WaterDisplaceVolume),
539            _ if value == Self::Rope as u16 => Ok(Self::Rope),
540            _ if value == Self::CampaignVegetation as u16 => Ok(Self::CampaignVegetation),
541            _ if value == Self::ProjectedDecalV3 as u16 => Ok(Self::ProjectedDecalV3),
542            _ if value == Self::WeightedTextureBlend as u16 => Ok(Self::WeightedTextureBlend),
543            _ if value == Self::ProjectedDecalV4 as u16 => Ok(Self::ProjectedDecalV4),
544            _ if value == Self::GlobalTerrain as u16 => Ok(Self::GlobalTerrain),
545            _ if value == Self::DecalOverlay as u16 => Ok(Self::DecalOverlay),
546            _ if value == Self::AlphaBlend as u16 => Ok(Self::AlphaBlend),
547            _ => Err(RLibError::DecodingRigidModelUnsupportedMaterialType(value))
548        }
549    }
550}
551
552impl From<MaterialType> for u16 {
553    fn from(value: MaterialType) -> u16 {
554        value as u16
555    }
556}
557
558impl TryFrom<i32> for TextureType {
559    type Error = RLibError;
560    fn try_from(value: i32) -> Result<Self> {
561        match value {
562            _ if value == Self::Diffuse as i32 => Ok(Self::Diffuse),
563            _ if value == Self::Normal as i32 => Ok(Self::Normal),
564            _ if value == Self::Mask as i32 => Ok(Self::Mask),
565            _ if value == Self::AmbientOcclusion as i32 => Ok(Self::AmbientOcclusion),
566            _ if value == Self::TilingDirtUV2 as i32 => Ok(Self::TilingDirtUV2),
567            _ if value == Self::DirtAlphaMask as i32 => Ok(Self::DirtAlphaMask),
568            _ if value == Self::SkinMask as i32 => Ok(Self::SkinMask),
569            _ if value == Self::Specular as i32 => Ok(Self::Specular),
570            _ if value == Self::GlossMap as i32 => Ok(Self::GlossMap),
571            _ if value == Self::DecalDirtmap as i32 => Ok(Self::DecalDirtmap),
572            _ if value == Self::DecalDirtmask as i32 => Ok(Self::DecalDirtmask),
573            _ if value == Self::DecalMask as i32 => Ok(Self::DecalMask),
574            _ if value == Self::DiffuseDamage as i32 => Ok(Self::DiffuseDamage),
575            _ if value == Self::BaseColor as i32 => Ok(Self::BaseColor),
576            _ if value == Self::MaterialMap as i32 => Ok(Self::MaterialMap),
577            _ => Err(RLibError::DecodingRigidModelUnknownTextureType(value))
578        }
579    }
580}
581
582impl TryFrom<TextureType> for i32 {
583    type Error = RLibError;
584    fn try_from(value: TextureType) -> Result<i32> {
585        Ok(value as i32)
586    }
587}