released vdf-reader

This commit is contained in:
Robin Appelman 2023-12-21 20:42:40 +01:00
commit f39661ca95
21 changed files with 395 additions and 31 deletions

7
Cargo.lock generated
View file

@ -319,9 +319,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.70"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
dependencies = [
"unicode-ident",
]
@ -615,7 +615,8 @@ checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "vdf-reader"
version = "0.1.0"
source = "git+https://github.com/icewind1991/vdf-reader#63b3bdbefdb97d7ea20858697702a8530b541e70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bff9669f8e9cf7a82acac623509d7dbd119b4bc90da2469f1e7501097e04c5a3"
dependencies = [
"logos",
"miette",

View file

@ -2,13 +2,15 @@
name = "vmt-parser"
version = "0.1.0"
edition = "2021"
description = "Rust parser for valve vmt files."
license = "MIT"
repository = "https://github.com/icewind1991/vdf-parser"
rust-version = "1.65.0"
[dependencies]
thiserror = "1.0.50"
miette = "5.10.0"
#vdf-reader = { version = "0.1", path = "../vdf-reader" }
vdf-reader = { version = "0.1", git = "https://github.com/icewind1991/vdf-reader" }
vdf-reader = "0.1"
serde = { version = "1.0.193", features = ["derive"] }
serde_repr = "0.1.17"

View file

@ -16,7 +16,7 @@ fn main() -> Result<()> {
.filter(|e| e.file_name().to_str().unwrap_or_default().ends_with(".vmt"))
{
if let Err(e) = try_parse(entry.path()) {
err.push(e);
err.push((entry.path().to_path_buf(), e));
let e = try_parse(entry.path()).unwrap_err();
println!("{:?}", e);
} else {
@ -27,8 +27,8 @@ fn main() -> Result<()> {
println!("successfully parsed {success} files");
println!("found errors in {} files", err.len());
for e in err {
println!("{:?}", e);
for (path, e) in err {
println!("{}: {e:?}", path.display());
}
Ok(())

View file

@ -1,13 +1,29 @@
pub mod texture_transform;
use serde::{Deserialize, Serialize};
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::borrow::Cow;
pub use texture_transform::TextureTransform;
#[derive(Debug, Serialize, Deserialize, Copy, Clone, Default)]
#[serde(from = "Vec2OrSingle<f32>")]
pub struct Vec2(pub [f32; 2]);
pub(crate) fn deserialize_bare_vec2<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Vec2, D::Error> {
let str = Cow::<str>::deserialize(deserializer)?;
let (x, y) = str
.trim()
.split_once(' ')
.ok_or_else(|| D::Error::custom("doesn't look like a vec2"))?;
let x = x.trim().parse().map_err(D::Error::custom)?;
let y = y.trim().parse().map_err(D::Error::custom)?;
Ok(Vec2([x, y]))
}
impl From<Vec2OrSingle<f32>> for Vec2 {
fn from(value: Vec2OrSingle<f32>) -> Self {
match value {

34
src/material/cable.rs Normal file
View file

@ -0,0 +1,34 @@
use super::deserialize_path;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CableMaterial {
/// Defines an albedo texture.
#[serde(rename = "$basetexture", deserialize_with = "deserialize_path")]
pub base_texture: String,
/// Specifies a texture that will provide three-dimensional lighting information for a material.
#[serde(rename = "$bumpmap", default, deserialize_with = "deserialize_path")]
pub bump_map: Option<String>,
/// Use computed vertex colors.
#[serde(rename = "$vertexcolor", default)]
pub vertex_color: bool,
/// Minimum amount of light received
#[serde(rename = "$minlight", default = "default_min_light")]
pub min_light: f32,
/// Maximum amount of light received
#[serde(rename = "$maxlight", default = "default_max_light")]
pub max_light: f32,
/// Disables backface culling.
#[serde(rename = "$nocull", default)]
pub no_cull: bool,
}
fn default_min_light() -> f32 {
0.1
}
fn default_max_light() -> f32 {
0.3
}

View file

@ -1,16 +1,30 @@
mod cable;
mod eyerefract;
mod lightmappedgeneric;
mod modulate;
mod refract;
mod replacements;
mod sky;
mod sprite;
mod spritecard;
mod subrect;
mod unlitgeneric;
mod unlittwotexture;
mod vertexlitgeneric;
mod water;
mod worldvertextransition;
pub use cable::CableMaterial;
pub use eyerefract::EyeRefractMaterial;
pub use lightmappedgeneric::LightMappedGenericMaterial;
pub use modulate::ModulateMaterial;
pub use refract::RefractMaterial;
pub use replacements::{ReplacementPattern, ReplacementTemplate, ReplacementsMaterial};
use serde::{Deserialize, Deserializer, Serialize};
pub use sky::SkyMaterial;
pub use sprite::{SpriteMaterial, SpriteOrientation};
pub use spritecard::SpriteCardMaterial;
pub use subrect::SubRectMaterial;
pub use unlitgeneric::UnlitGenericMaterial;
pub use unlittwotexture::UnlitTwoTextureMaterial;
use vdf_reader::entry::{Entry, Table};
@ -22,24 +36,26 @@ pub use worldvertextransition::WorldVertexTransitionMaterial;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(rename_all = "lowercase")]
pub enum Material {
#[serde(rename = "lightmappedgeneric")]
LightMappedGeneric(LightMappedGenericMaterial),
#[serde(rename = "vertexlitgeneric")]
VertexLitGeneric(VertexLitGenericMaterial),
#[serde(rename = "unlitgeneric")]
#[serde(rename = "vertexlitgeneric_dx6")]
VertexLitGenericDx6(VertexLitGenericMaterial),
UnlitGeneric(UnlitGenericMaterial),
#[serde(rename = "unlittwotexture")]
UnlitTwoTexture(UnlitTwoTextureMaterial),
#[serde(rename = "water")]
Water(WaterMaterial),
#[serde(rename = "worldvertextransition")]
WorldVertexTransition(WorldVertexTransitionMaterial),
#[serde(rename = "eyerefract")]
EyeRefract(EyeRefractMaterial),
#[serde(rename = "sprite")]
SubRect(SubRectMaterial),
Sprite(SpriteMaterial),
#[serde(rename = "patch")]
SpriteCard(SpriteCardMaterial),
Cable(CableMaterial),
Refract(RefractMaterial),
Modulate(ModulateMaterial),
DecalModulate(ModulateMaterial),
Sky(SkyMaterial),
Replacements(ReplacementsMaterial),
Patch(PatchMaterial),
}
@ -60,6 +76,7 @@ impl Material {
match self {
Material::LightMappedGeneric(mat) => mat.translucent,
Material::VertexLitGeneric(mat) => mat.translucent,
Material::VertexLitGenericDx6(mat) => mat.translucent,
Material::UnlitGeneric(mat) => mat.translucent,
Material::UnlitTwoTexture(mat) => mat.translucent,
Material::WorldVertexTransition(mat) => mat.translucent,
@ -73,6 +90,7 @@ impl Material {
match self {
Material::LightMappedGeneric(mat) => mat.no_cull,
Material::VertexLitGeneric(mat) => mat.no_cull,
Material::VertexLitGenericDx6(mat) => mat.no_cull,
Material::UnlitGeneric(mat) => mat.no_cull,
Material::UnlitTwoTexture(mat) => mat.no_cull,
Material::WorldVertexTransition(mat) => mat.no_cull,
@ -85,6 +103,9 @@ impl Material {
match self {
Material::LightMappedGeneric(mat) => mat.alpha_test.then_some(mat.alpha_test_reference),
Material::VertexLitGeneric(mat) => mat.alpha_test.then_some(mat.alpha_test_reference),
Material::VertexLitGenericDx6(mat) => {
mat.alpha_test.then_some(mat.alpha_test_reference)
}
Material::UnlitGeneric(mat) => mat.alpha_test.then_some(mat.alpha_test_reference),
Material::UnlitTwoTexture(mat) => mat.alpha_test.then_some(mat.alpha_test_reference),
Material::WorldVertexTransition(mat) => {
@ -99,9 +120,10 @@ impl Material {
pub fn base_texture(&self) -> Option<&str> {
match self {
Material::LightMappedGeneric(mat) => Some(&mat.base_texture),
Material::VertexLitGeneric(mat) => Some(&mat.base_texture),
Material::UnlitGeneric(mat) => Some(&mat.base_texture),
Material::UnlitTwoTexture(mat) => Some(&mat.base_texture),
Material::VertexLitGeneric(mat) => mat.base_texture.as_deref(),
Material::VertexLitGenericDx6(mat) => mat.base_texture.as_deref(),
Material::UnlitGeneric(mat) => mat.base_texture.as_deref(),
Material::UnlitTwoTexture(mat) => mat.base_texture.as_deref(),
Material::WorldVertexTransition(mat) => Some(&mat.base_texture),
Material::Sprite(mat) => Some(&mat.base_texture),
Material::Water(mat) => mat.base_texture.as_deref(),
@ -114,6 +136,7 @@ impl Material {
match self {
Material::LightMappedGeneric(mat) => mat.bump_map.as_deref(),
Material::VertexLitGeneric(mat) => mat.bump_map.as_deref(),
Material::VertexLitGenericDx6(mat) => mat.bump_map.as_deref(),
Material::UnlitGeneric(mat) => mat.bump_map.as_deref(),
Material::UnlitTwoTexture(mat) => mat.bump_map.as_deref(),
Material::WorldVertexTransition(mat) => mat.bump_map.as_deref(),
@ -136,6 +159,7 @@ impl Material {
match self {
Material::LightMappedGeneric(mat) => mat.alpha,
Material::VertexLitGeneric(mat) => mat.alpha,
Material::VertexLitGenericDx6(mat) => mat.alpha,
Material::UnlitGeneric(mat) => mat.alpha,
Material::UnlitTwoTexture(mat) => mat.alpha,
Material::Sprite(mat) => mat.alpha,
@ -148,6 +172,7 @@ impl Material {
match self {
Material::LightMappedGeneric(mat) => mat.ignore_z,
Material::VertexLitGeneric(mat) => mat.ignore_z,
Material::VertexLitGenericDx6(mat) => mat.ignore_z,
Material::UnlitGeneric(mat) => mat.ignore_z,
Material::UnlitTwoTexture(mat) => mat.ignore_z,
Material::WorldVertexTransition(mat) => mat.ignore_z,

25
src/material/modulate.rs Normal file
View file

@ -0,0 +1,25 @@
use super::deserialize_path;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModulateMaterial {
/// Multiplies the color behind it with this surface's texture.
#[serde(rename = "$basetexture", deserialize_with = "deserialize_path")]
pub base_texture: String,
/// Doubles the modulation, making it appear brighter. 1 enables this, 0 disables. Disabled by default.
#[serde(rename = "$mod2x", default)]
pub mod_2x: bool,
/// Disables backface culling.
#[serde(rename = "$nocull", default)]
pub no_cull: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum SpriteOrientation {
ParallelUpright,
#[default]
VpParallel,
Oriented,
VpParallelOriented,
}

112
src/material/refract.rs Normal file
View file

@ -0,0 +1,112 @@
use super::deserialize_path;
use crate::{
default_detail_scale, default_scale, default_scale3, BlendMode, TextureTransform, Vec2, Vec3,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RefractMaterial {
/// The pattern of refraction is defined by a normal map (DX9+) or DUDV map (DX8-). May be animated.
#[serde(rename = "$normalmap", deserialize_with = "deserialize_path")]
pub normal_map: String,
/// The pattern of refraction is defined by a normal map (DX9+) or DUDV map (DX8-). May be animated.
#[serde(rename = "$dudvmap", default, deserialize_with = "deserialize_path")]
pub du_dv_map: Option<String>,
/// If a second normal map is specified, it will be blended with the first one.
#[serde(rename = "$normalmap2", default, deserialize_with = "deserialize_path")]
pub normal_map2: Option<String>,
/// Use a texture instead of rendering the view for the source of the distorted pixels.
#[serde(
rename = "$basetexture",
default,
deserialize_with = "deserialize_path"
)]
pub base_texture: Option<String>,
/// Transforms the bump map texture.
#[serde(rename = "$bumptransform", default)]
pub bump_transform: TextureTransform,
/// Transforms the bump map texture.
#[serde(rename = "$bumptransform2", default)]
pub bump_transform2: TextureTransform,
#[serde(rename = "$refracttint", default = "default_scale3")]
pub refract_tint: Vec3,
/// Tints the colour of the refraction either uniformly or per-texel. Can be used in conjunction with $refracttint
#[serde(
rename = "$refracttinttexture",
default,
deserialize_with = "deserialize_path"
)]
pub refract_tint_texture: Option<String>,
/// Controls the strength of the refraction by multiplying the normal map intensity.
#[serde(rename = "$refractamount", default = "default_scale")]
pub refract_amount: f32,
/// Adds a blur effect. Valid values are 0, 1 and 2 (0 and 1 for DX8-).
#[serde(rename = "$bluramount", default)]
pub blur_amount: f32,
#[serde(rename = "$decalscale", default = "default_detail_scale")]
/// Fits the detail texture onto the material the given number of times
pub detail_scale: Vec2,
/// Controls the amount that the detail texture affects the base texture. The precise use of this depends on the blend factor; in most cases it acts similarly to $alpha. A value of 0 usually makes the detail texture have no effect, whilst a value of 1 applies the full effect.
#[serde(rename = "$detailblendfactor", default = "default_scale")]
pub detail_blend_factor: f32,
/// How to combine the detail material with the albedo.
#[serde(rename = "$detailblendmode", default)]
pub detail_blend_mode: BlendMode,
/// A separate VertexLitGeneric material to that will replace this one if the decal hits a model.
#[serde(
rename = "$modelmaterial",
default,
deserialize_with = "deserialize_path"
)]
pub model_material: Option<String>,
/// Disables texture filtering.
#[serde(rename = "$pointsamplemagfilter", default)]
pub point_sample_mag_filter: bool,
/// Mitigation for displacement texture stretching.
#[serde(rename = "$seamless_scale", default = "default_scale")]
pub seamless_scale: f32,
/// Scales the opacity of an entire material.
#[serde(rename = "$alpha", default = "default_scale")]
pub alpha: f32,
/// Specifies a mask to use to determine binary opacity.
#[serde(rename = "$alphatest", default)]
pub alpha_test: bool,
/// Specifies a mask to use to determine binary opacity.
#[serde(rename = "$alphatestreference", default = "default_scale")]
pub alpha_test_reference: f32,
/// Vector-like edge filtering.
#[serde(rename = "$distancealpha", default)]
pub distance_alpha: bool,
/// Disables backface culling.
#[serde(rename = "$nocull", default)]
pub no_cull: bool,
/// Specifies that the material should be partially see-through.
#[serde(rename = "$translucent", default)]
pub translucent: bool,
/// Specifies a texture that will provide three-dimensional lighting information for a material.
#[serde(rename = "$bumpmap", default, deserialize_with = "deserialize_path")]
pub bump_map: Option<String>,
/// Per-texel color modification via a warp texture.
#[serde(
rename = "$lightwarptexture",
default,
deserialize_with = "deserialize_path"
)]
pub light_wrap_texture: Option<String>,
/// Determines whether the surface is self-illuminated independent of environment lighting.
#[serde(rename = "$selfillum", default)]
pub self_illum: bool,
/// Flags the $bumpmap as being a self-shadowing bumpmap.
#[serde(rename = "$ssbump", default)]
pub ss_bump: bool,
/// Specular reflections.
#[serde(rename = "$envmap", default, deserialize_with = "deserialize_path")]
pub env_map: Option<String>,
#[serde(rename = "$envmaptint", default = "default_scale3")]
pub env_map_tint: Vec3,
}

View file

@ -0,0 +1,17 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use vdf_reader::entry::Table;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReplacementsMaterial {
pub templates: HashMap<String, ReplacementTemplate>,
pub patterns: HashMap<String, ReplacementPattern>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReplacementTemplate(pub HashMap<String, Table>);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReplacementPattern {
pub template: String,
}

34
src/material/sky.rs Normal file
View file

@ -0,0 +1,34 @@
use super::deserialize_path;
use crate::TextureTransform;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SkyMaterial {
/// Defines an albedo texture.
#[serde(rename = "$basetexture", deserialize_with = "deserialize_path")]
pub base_texture: String,
/// Defines an albedo texture.
#[serde(
rename = "$hdrbasetexture",
default,
deserialize_with = "deserialize_path"
)]
pub hdr_base_texture: Option<String>,
/// Links the surface to a set of physical properties.
#[serde(rename = "$surfaceprop", default)]
pub surface_prop: Option<String>,
/// Transforms the texture before use in the material. This does not affect lightmaps on the surface.
#[serde(rename = "$basetexturetransform", default)]
pub base_texture_transform: TextureTransform,
/// Ignore z filtering
#[serde(rename = "$ignorez", default)]
pub ignore_z: bool,
/// Prevents fog from overdrawing a material.
#[serde(rename = "$nofog", default)]
pub no_fog: bool,
#[serde(rename = "$nomip", default)]
pub no_mip: bool,
}

View file

@ -1,5 +1,5 @@
use super::deserialize_path;
use crate::{default_scale, default_scale3, Vec3};
use crate::{default_scale, default_scale3, Vec2, Vec3};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -14,7 +14,7 @@ pub struct SpriteMaterial {
#[serde(rename = "$spriteorientation", default)]
pub sprite_orientation: SpriteOrientation,
#[serde(rename = "$spriteorigin", default)]
pub sprite_origin: Vec3,
pub sprite_origin: Vec2,
/// Independently scales the red, green and blue channels of an albedo.
#[serde(rename = "$color", default = "default_scale3")]

View file

@ -0,0 +1,54 @@
use super::deserialize_path;
use crate::{default_scale, Vec2};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpriteCardMaterial {
/// Defines an albedo texture.
#[serde(
rename = "$basetexture",
default,
deserialize_with = "deserialize_path"
)]
pub base_texture: Option<String>,
/// Links the surface to a set of physical properties.
#[serde(rename = "$surfaceprop", default)]
pub surface_prop: Option<String>,
#[serde(rename = "$spriteorigin", default)]
pub sprite_origin: Vec2,
#[serde(rename = "$additive", default)]
pub additive: bool,
#[serde(rename = "$overbrightfactor", default)]
pub over_bright_factor: f32,
/// Use computed vertex colors.
#[serde(rename = "$vertexcolor", default)]
pub vertex_color: bool,
/// Use computed vertex alpha.
#[serde(rename = "$vertexalpha", default)]
pub vertex_alpha: bool,
/// Scales the opacity of an entire material.
#[serde(rename = "$alpha", default = "default_scale")]
pub alpha: f32,
/// Specifies that the material should be partially see-through.
#[serde(rename = "$translucent", default)]
pub translucent: bool,
/// Disables backface culling.
#[serde(rename = "$nocull", default)]
pub no_cull: bool,
/// Multiply the output by 2x.
#[serde(rename = "$mod2x", default)]
pub mod_2x: bool,
/// Are we opaque? Default 0.
#[serde(rename = "$opaque", default)]
pub opaque: bool,
/// Multiply output RGB by intensity factor.
#[serde(rename = "$intensity", default = "default_scale")]
pub intensity: f32,
}

14
src/material/subrect.rs Normal file
View file

@ -0,0 +1,14 @@
use super::deserialize_path;
use crate::{deserialize_bare_vec2, Vec2};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubRectMaterial {
/// Base material
#[serde(rename = "$material", deserialize_with = "deserialize_path")]
pub material: String,
#[serde(rename = "$pos", deserialize_with = "deserialize_bare_vec2")]
pub pos: Vec2,
#[serde(rename = "$size", deserialize_with = "deserialize_bare_vec2")]
pub size: Vec2,
}

View file

@ -5,8 +5,12 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnlitGenericMaterial {
/// Defines an albedo texture.
#[serde(rename = "$basetexture", deserialize_with = "deserialize_path")]
pub base_texture: String,
#[serde(
rename = "$basetexture",
default,
deserialize_with = "deserialize_path"
)]
pub base_texture: Option<String>,
/// Links the surface to a set of physical properties.
#[serde(rename = "$surfaceprop", default)]
pub surface_prop: Option<String>,

View file

@ -5,8 +5,12 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnlitTwoTextureMaterial {
/// The first texture in the blend.
#[serde(rename = "$basetexture", deserialize_with = "deserialize_path")]
pub base_texture: String,
#[serde(
rename = "$basetexture",
default,
deserialize_with = "deserialize_path"
)]
pub base_texture: Option<String>,
/// The second texture to blend to.
#[serde(rename = "$texture2", deserialize_with = "deserialize_path")]
pub texture2: String,

View file

@ -7,8 +7,12 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VertexLitGenericMaterial {
/// Defines an albedo texture.
#[serde(rename = "$basetexture", deserialize_with = "deserialize_path")]
pub base_texture: String,
#[serde(
rename = "$basetexture",
default,
deserialize_with = "deserialize_path"
)]
pub base_texture: Option<String>,
/// Detail texturing.
#[serde(rename = "$detail", default, deserialize_with = "deserialize_path")]
pub detail: Option<String>,

View file

@ -0,0 +1,8 @@
"Subrect"
{
"$Material" "decals/decals_mod2x"
"$Pos" "64 0"
"$Size" "64 64"
"$decalscale" 0.16
"$modelmaterial" "decals/concrete/shot2"
}

View file

@ -19,6 +19,7 @@ enum LoaderError {
#[test_case("tests/data/blendrocktograss002.vmt")]
#[test_case("tests/data/patch.vmt")]
#[test_case("tests/data/handrail128_skin2.vmt")]
#[test_case("tests/data/shot2_subrect.vmt")]
fn test_serde(path: &str) {
let raw = read_to_string(path).unwrap();
match from_str(&raw) {

View file

@ -3,7 +3,7 @@ source: tests/parse.rs
expression: result
---
vertexlitgeneric(VertexLitGenericMaterial(
r#$basetexture: "models/props_trainyard/handrail128_skin2",
r#$basetexture: Some("models/props_trainyard/handrail128_skin2"),
r#$detail: None,
r#$decaltexture: None,
r#$color2: Vec3((1.0, 1.0, 1.0)),

View file

@ -3,7 +3,7 @@ source: tests/parse.rs
expression: result
---
unlitgeneric(UnlitGenericMaterial(
r#$basetexture: "vgui/pve/mvm_backpack",
r#$basetexture: Some("vgui/pve/mvm_backpack"),
r#$surfaceprop: None,
r#$model: false,
r#$color: Vec3((1.0, 1.0, 1.0)),

View file

@ -0,0 +1,9 @@
---
source: tests/parse.rs
expression: result
---
subrect(SubRectMaterial(
r#$material: "decals/decals_mod2x",
r#$pos: Vec2((64.0, 0.0)),
r#$size: Vec2((64.0, 64.0)),
))