mirror of
https://codeberg.org/icewind/vmt-parser.git
synced 2026-06-03 03:54:07 +02:00
LightMappedGeneric
This commit is contained in:
parent
971b42452d
commit
ab10e2d05c
14 changed files with 378 additions and 243 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -3,3 +3,4 @@ target
|
|||
*.obj
|
||||
result
|
||||
.direnv
|
||||
*.snap.new
|
||||
15
Cargo.lock
generated
15
Cargo.lock
generated
|
|
@ -426,6 +426,17 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.3.0"
|
||||
|
|
@ -608,9 +619,9 @@ name = "vmt-parser"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"insta",
|
||||
"logos",
|
||||
"miette",
|
||||
"parse-display",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"test-case",
|
||||
"thiserror",
|
||||
"vdf-reader",
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
logos = "0.13.0"
|
||||
thiserror = "1.0.50"
|
||||
miette = "5.10.0"
|
||||
parse-display = "0.8.2"
|
||||
vdf-reader = { version = "0.1", path = "../vdf-reader" }
|
||||
serde = { version = "1.0.193", features = ["derive"] }
|
||||
serde_repr = "0.1.17"
|
||||
|
||||
[dev-dependencies]
|
||||
test-case = "3.3.1"
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
use miette::{Context, IntoDiagnostic, Result};
|
||||
use std::env::args;
|
||||
use std::fs::read_to_string;
|
||||
use vdf_reader::Reader;
|
||||
use vmt_parser::material::Material;
|
||||
use vmt_parser::from_str;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let path = args().nth(1).expect("no path provided");
|
||||
let raw = read_to_string(path)
|
||||
.into_diagnostic()
|
||||
.wrap_err("failed to read input")?;
|
||||
let mut reader = Reader::from(raw.as_str());
|
||||
let material = Material::parse(&mut reader).wrap_err("failed to parse material")?;
|
||||
let material = from_str(&raw).wrap_err("failed to parse material")?;
|
||||
dbg!(material);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@
|
|||
cargo-audit
|
||||
cargo-msrv
|
||||
cargo-semver-checks
|
||||
cargo-insta
|
||||
(writeShellApplication {
|
||||
name = "cargo-fuzz";
|
||||
runtimeInputs = [cargo-fuzz toolchain];
|
||||
|
|
|
|||
80
src/data/mod.rs
Normal file
80
src/data/mod.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
pub mod texture_transform;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
pub use texture_transform::TextureTransform;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
|
||||
#[serde(from = "Vec2OrSingle<f32>")]
|
||||
pub struct Vec2([f32; 2]);
|
||||
|
||||
impl From<Vec2OrSingle<f32>> for Vec2 {
|
||||
fn from(value: Vec2OrSingle<f32>) -> Self {
|
||||
match value {
|
||||
Vec2OrSingle::Vec2(vec) => Vec2(vec),
|
||||
Vec2OrSingle::Single(val) => Vec2([val; 2]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
|
||||
#[serde(from = "Vec3OrSingle<f32>")]
|
||||
pub struct Vec3([f32; 3]);
|
||||
|
||||
impl From<Vec3OrSingle<f32>> for Vec3 {
|
||||
fn from(value: Vec3OrSingle<f32>) -> Self {
|
||||
match value {
|
||||
Vec3OrSingle::Vec3(vec) => Vec3(vec),
|
||||
Vec3OrSingle::Single(val) => Vec3([val; 3]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum Vec3OrSingle<T> {
|
||||
Vec3([T; 3]),
|
||||
Single(T),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum Vec2OrSingle<T> {
|
||||
Vec2([T; 2]),
|
||||
Single(T),
|
||||
}
|
||||
|
||||
pub(crate) fn default_scale3() -> Vec3 {
|
||||
Vec3([1.0; 3])
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn default_scale2() -> Vec2 {
|
||||
Vec2([1.0; 2])
|
||||
}
|
||||
|
||||
pub(crate) fn default_scale() -> f32 {
|
||||
1.0
|
||||
}
|
||||
|
||||
pub(crate) fn default_detail_scale() -> Vec2 {
|
||||
Vec2([4.0; 2])
|
||||
}
|
||||
|
||||
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug, Copy, Clone, Default)]
|
||||
#[repr(u8)]
|
||||
pub enum BlendMode {
|
||||
DecalModulate = 0,
|
||||
#[default]
|
||||
Additive = 1,
|
||||
TranslucentOverlay = 2,
|
||||
BlendFactorOverlay = 3,
|
||||
TranslucentBase = 4,
|
||||
UnlitAdditive = 5,
|
||||
UnlitAdditiveThreshold = 6,
|
||||
TwoPatternModulate = 7,
|
||||
Multiply = 8,
|
||||
BaseMaskAlpha = 9,
|
||||
SelfShadowedBumpMap = 10,
|
||||
SelfShadowedBumpAlbedo = 11,
|
||||
}
|
||||
123
src/data/texture_transform.rs
Normal file
123
src/data/texture_transform.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct TextureTransform {
|
||||
pub center: [f32; 2],
|
||||
pub scale: [f32; 2],
|
||||
pub rotate: f32,
|
||||
pub translate: [f32; 2],
|
||||
}
|
||||
|
||||
impl Default for TextureTransform {
|
||||
fn default() -> Self {
|
||||
TextureTransform {
|
||||
center: [0.5; 2],
|
||||
scale: [1.0; 2],
|
||||
rotate: 0.0,
|
||||
translate: [0.0; 2],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TextureTransform {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"center {} {} scale {} {} rotate {} translate {} {}",
|
||||
self.center[0],
|
||||
self.center[1],
|
||||
self.scale[0],
|
||||
self.scale[1],
|
||||
self.rotate,
|
||||
self.translate[0],
|
||||
self.translate[1],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_base_texture_transform() {
|
||||
assert_eq!(
|
||||
TextureTransform {
|
||||
center: [0.5; 2],
|
||||
scale: [1.0; 2],
|
||||
rotate: 0.0,
|
||||
translate: [0.0; 2],
|
||||
},
|
||||
TextureTransform::from_str("center .5 .5 scale 1 1 rotate 0 translate 0 0").unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
TextureTransform {
|
||||
center: [0.2, 0.3],
|
||||
scale: [1.1, 1.2],
|
||||
rotate: 1.0,
|
||||
translate: [0.4, 0.5],
|
||||
},
|
||||
TextureTransform::from_str("center .2 .3 scale 1.1 1.2 rotate 1 translate 0.4 0.5")
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
impl FromStr for TextureTransform {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(str: &str) -> Result<Self, Self::Err> {
|
||||
let mut parts = str.split(' ').filter(|p| !p.is_empty());
|
||||
match (
|
||||
parts.next(),
|
||||
parts.next().and_then(|val| f32::from_str(val).ok()),
|
||||
parts.next().and_then(|val| f32::from_str(val).ok()),
|
||||
parts.next(),
|
||||
parts.next().and_then(|val| f32::from_str(val).ok()),
|
||||
parts.next().and_then(|val| f32::from_str(val).ok()),
|
||||
parts.next(),
|
||||
parts.next().and_then(|val| f32::from_str(val).ok()),
|
||||
parts.next(),
|
||||
parts.next().and_then(|val| f32::from_str(val).ok()),
|
||||
parts.next().and_then(|val| f32::from_str(val).ok()),
|
||||
) {
|
||||
(
|
||||
Some("center"),
|
||||
Some(center_x),
|
||||
Some(center_y),
|
||||
Some("scale"),
|
||||
Some(scale_x),
|
||||
Some(scale_y),
|
||||
Some("rotate"),
|
||||
Some(rotate),
|
||||
Some("translate"),
|
||||
Some(translate_x),
|
||||
Some(translate_y),
|
||||
) => Ok(TextureTransform {
|
||||
center: [center_x, center_y],
|
||||
scale: [scale_x, scale_y],
|
||||
rotate,
|
||||
translate: [translate_x, translate_y],
|
||||
}),
|
||||
_ => Err("invalid $basetexturetransform format"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for TextureTransform {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let str = Cow::<str>::deserialize(deserializer)?;
|
||||
Self::from_str(str.as_ref()).map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for TextureTransform {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
60
src/error.rs
60
src/error.rs
|
|
@ -1,7 +1,5 @@
|
|||
use miette::{Diagnostic, SourceSpan};
|
||||
use miette::Diagnostic;
|
||||
use thiserror::Error;
|
||||
use vdf_reader::entry::Entry;
|
||||
use vdf_reader::error::ParseEntryError;
|
||||
use vdf_reader::VdfError;
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
|
|
@ -9,60 +7,4 @@ pub enum VmtError {
|
|||
#[error(transparent)]
|
||||
#[diagnostic(transparent)]
|
||||
Vdf(#[from] VdfError),
|
||||
#[error(transparent)]
|
||||
#[diagnostic(transparent)]
|
||||
Eof(#[from] EofError),
|
||||
#[error(transparent)]
|
||||
#[diagnostic(transparent)]
|
||||
ParseValue(#[from] ParseValueError),
|
||||
}
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
#[error("Unexpected end of input while looking for {expected}")]
|
||||
#[diagnostic(code(vmt_parser::eof))]
|
||||
pub struct EofError {
|
||||
expected: &'static str,
|
||||
#[label("Expected {}", self.expected)]
|
||||
err_span: SourceSpan,
|
||||
#[source_code]
|
||||
src: String,
|
||||
}
|
||||
|
||||
impl EofError {
|
||||
pub fn new(src: String, expected: &'static str) -> Self {
|
||||
let span = src.len()..src.len();
|
||||
EofError {
|
||||
src,
|
||||
err_span: span.into(),
|
||||
expected,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
#[error("Can't parse value {value:?} as {ty} to read {name}")]
|
||||
#[diagnostic(code(vmt_parser::eof))]
|
||||
pub struct ParseValueError {
|
||||
name: &'static str,
|
||||
ty: &'static str,
|
||||
pub value: Entry,
|
||||
#[label("Expected a {}", self.ty)]
|
||||
err_span: SourceSpan,
|
||||
#[source_code]
|
||||
src: String,
|
||||
}
|
||||
|
||||
impl ParseValueError {
|
||||
pub fn new(
|
||||
src: String,
|
||||
name: &'static str,
|
||||
err_span: SourceSpan,
|
||||
parse_err: ParseEntryError,
|
||||
) -> Self {
|
||||
ParseValueError {
|
||||
src,
|
||||
err_span,
|
||||
name,
|
||||
ty: parse_err.ty,
|
||||
value: parse_err.value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
mod data;
|
||||
mod error;
|
||||
pub mod material;
|
||||
|
||||
use crate::material::Material;
|
||||
pub use data::*;
|
||||
pub use error::VmtError;
|
||||
|
||||
pub type Result<T, E = VmtError> = std::result::Result<T, E>;
|
||||
|
||||
pub fn from_str(input: &str) -> Result<Material> {
|
||||
let input = input.to_ascii_lowercase();
|
||||
vdf_reader::from_str(&input).map_err(VmtError::from)
|
||||
}
|
||||
|
|
|
|||
176
src/material.rs
176
src/material.rs
|
|
@ -1,176 +0,0 @@
|
|||
use crate::error::{EofError, ParseValueError};
|
||||
use crate::Result;
|
||||
use std::collections::HashMap;
|
||||
use vdf_reader::entry::{Entry, FromEntry, Table};
|
||||
use vdf_reader::{Event, GroupStartEvent, Reader, VdfError};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Material {
|
||||
Other(OtherMaterial),
|
||||
LightMappedGeneric(LightMappedGenericMaterial),
|
||||
}
|
||||
|
||||
impl Material {
|
||||
pub fn parse(reader: &mut Reader) -> Result<Self> {
|
||||
let start: GroupStartEvent = expect_event(reader, "material name")?;
|
||||
let name = start.name.to_ascii_lowercase();
|
||||
|
||||
Ok(match name.as_str() {
|
||||
"lightmappedgeneric" => {
|
||||
Material::LightMappedGeneric(LightMappedGenericMaterial::parse(reader)?)
|
||||
}
|
||||
_ => Material::Other(OtherMaterial::parse(name, reader)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LightMappedGenericMaterial {
|
||||
pub keywords: String,
|
||||
pub detail: Option<String>,
|
||||
pub detail_blend_factory: f32,
|
||||
pub detail_scale: f32,
|
||||
pub detail_blend_mode: u32,
|
||||
pub base_texture: String,
|
||||
pub ss_bump: bool,
|
||||
pub bump_map: Option<String>,
|
||||
pub rest: HashMap<String, Entry>,
|
||||
}
|
||||
|
||||
impl LightMappedGenericMaterial {
|
||||
fn parse(reader: &mut Reader) -> Result<Self> {
|
||||
let src = reader.source;
|
||||
let mut keywords = Default::default();
|
||||
let mut detail = Default::default();
|
||||
let mut detail_blend_factory = Default::default();
|
||||
let mut detail_scale = Default::default();
|
||||
let mut detail_blend_mode = Default::default();
|
||||
let mut base_texture = Default::default();
|
||||
let mut ss_bump = Default::default();
|
||||
let mut bump_map = Default::default();
|
||||
|
||||
let mut rest: HashMap<String, Entry> = Default::default();
|
||||
|
||||
loop {
|
||||
let (span, key, value) = match event(reader, "item or group end")? {
|
||||
Event::GroupEnd(_) => break,
|
||||
Event::GroupStart(start) => (
|
||||
start.span,
|
||||
start.name.to_ascii_lowercase(),
|
||||
Entry::Table(Table::load(reader)?),
|
||||
),
|
||||
Event::Entry(entry) => (
|
||||
entry.span,
|
||||
entry.key.into_content().to_ascii_lowercase(),
|
||||
entry.value.into(),
|
||||
),
|
||||
};
|
||||
match key.as_str() {
|
||||
"%keywords" => {
|
||||
keywords = FromEntry::from_entry(value).map_err(|err| {
|
||||
ParseValueError::new(src.into(), "keywords", span.into(), err)
|
||||
})?;
|
||||
}
|
||||
"$detail" => {
|
||||
detail = FromEntry::from_entry(value).map_err(|err| {
|
||||
ParseValueError::new(src.into(), "detail", span.into(), err)
|
||||
})?;
|
||||
}
|
||||
"$detailblendfactor" => {
|
||||
detail_blend_factory = FromEntry::from_entry(value).map_err(|err| {
|
||||
ParseValueError::new(src.into(), "detail_blend_factory", span.into(), err)
|
||||
})?;
|
||||
}
|
||||
"$detailscale" => {
|
||||
detail_scale = FromEntry::from_entry(value).map_err(|err| {
|
||||
ParseValueError::new(src.into(), "detail_scale", span.into(), err)
|
||||
})?;
|
||||
}
|
||||
"$detailblendmode" => {
|
||||
detail_blend_mode = FromEntry::from_entry(value).map_err(|err| {
|
||||
ParseValueError::new(src.into(), "detail_blend_mode", span.into(), err)
|
||||
})?;
|
||||
}
|
||||
"$basetexture" => {
|
||||
base_texture = FromEntry::from_entry(value).map_err(|err| {
|
||||
ParseValueError::new(src.into(), "base_texture", span.into(), err)
|
||||
})?;
|
||||
}
|
||||
"$ssbump" => {
|
||||
ss_bump = FromEntry::from_entry(value).map_err(|err| {
|
||||
ParseValueError::new(src.into(), "ss_bump", span.into(), err)
|
||||
})?;
|
||||
}
|
||||
"$bumpmap" => {
|
||||
bump_map = FromEntry::from_entry(value).map_err(|err| {
|
||||
ParseValueError::new(src.into(), "bump_map", span.into(), err)
|
||||
})?;
|
||||
}
|
||||
_ => {
|
||||
rest.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(LightMappedGenericMaterial {
|
||||
keywords,
|
||||
detail,
|
||||
detail_blend_factory,
|
||||
detail_scale,
|
||||
detail_blend_mode,
|
||||
base_texture,
|
||||
ss_bump,
|
||||
bump_map,
|
||||
rest,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OtherMaterial {
|
||||
pub name: String,
|
||||
pub values: HashMap<String, Entry>,
|
||||
}
|
||||
|
||||
impl OtherMaterial {
|
||||
fn parse(name: String, reader: &mut Reader) -> Result<Self> {
|
||||
let mut values: HashMap<String, _> = HashMap::new();
|
||||
|
||||
loop {
|
||||
let (key, value) = match event(reader, "item or group end")? {
|
||||
Event::GroupEnd(_) => break,
|
||||
Event::GroupStart(start) => (
|
||||
start.name.to_ascii_lowercase(),
|
||||
Entry::Table(Table::load(reader)?),
|
||||
),
|
||||
Event::Entry(entry) => (
|
||||
entry.key.into_content().to_ascii_lowercase(),
|
||||
entry.value.into(),
|
||||
),
|
||||
};
|
||||
values.insert(key, value);
|
||||
}
|
||||
|
||||
Ok(OtherMaterial { name, values })
|
||||
}
|
||||
}
|
||||
|
||||
fn event<'a>(reader: &mut Reader<'a>, expected: &'static str) -> Result<Event<'a>> {
|
||||
Ok(reader
|
||||
.next()
|
||||
.ok_or_else(|| EofError::new(reader.source.into(), expected))??)
|
||||
}
|
||||
|
||||
fn expect_event<'a, E: 'a>(reader: &mut Reader<'a>, expected: &'static str) -> Result<E>
|
||||
where
|
||||
E: TryFrom<Event<'a>, Error = VdfError>,
|
||||
{
|
||||
let event = event(reader, expected)?;
|
||||
E::try_from(event).map_err(|e| {
|
||||
match e {
|
||||
VdfError::WrongEntryType(e) => e.with_source(reader.source.into()).into(),
|
||||
e => e,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
}
|
||||
88
src/material/lightmappedgeneric.rs
Normal file
88
src/material/lightmappedgeneric.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
use crate::{
|
||||
default_detail_scale, default_scale, default_scale3, BlendMode, TextureTransform, Vec2, Vec3,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LightMappedGenericMaterial {
|
||||
/// Defines an diffuse texture.
|
||||
#[serde(rename = "$basetexture")]
|
||||
pub base_texture: String,
|
||||
/// Use this material as a decal.
|
||||
#[serde(rename = "$decal", default)]
|
||||
pub decal: bool,
|
||||
/// Detail texturing.
|
||||
#[serde(rename = "$detail")]
|
||||
pub detail: Option<String>,
|
||||
/// Links the surface to a set of physical properties.
|
||||
#[serde(rename = "$surfaceprop")]
|
||||
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,
|
||||
/// Independently scales the red, green and blue channels of an albedo.
|
||||
#[serde(rename = "$color", default = "default_scale3")]
|
||||
pub color: Vec3,
|
||||
/// the number of units that each texel covers
|
||||
#[serde(rename = "$decalscale", default = "default_scale")]
|
||||
pub decal_scale: 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)]
|
||||
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,
|
||||
/// 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")]
|
||||
pub bump_map: Option<String>,
|
||||
/// Per-texel color modification via a warp texture.
|
||||
#[serde(rename = "$lightwarptexture")]
|
||||
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")]
|
||||
pub env_map: Option<String>,
|
||||
/// Diffuse reflections.
|
||||
#[serde(rename = "$phong", default)]
|
||||
pub phong: bool,
|
||||
|
||||
/// Prevents fog from overdrawing a material.
|
||||
#[serde(rename = "$nofog", default)]
|
||||
pub no_fog: bool,
|
||||
}
|
||||
10
src/material/mod.rs
Normal file
10
src/material/mod.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
mod lightmappedgeneric;
|
||||
|
||||
use lightmappedgeneric::LightMappedGenericMaterial;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum Material {
|
||||
#[serde(rename = "lightmappedgeneric")]
|
||||
LightMappedGeneric(LightMappedGenericMaterial),
|
||||
}
|
||||
18
tests/parse.rs
Normal file
18
tests/parse.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
use miette::{GraphicalReportHandler, GraphicalTheme};
|
||||
use std::fs::read_to_string;
|
||||
use test_case::test_case;
|
||||
use vmt_parser::from_str;
|
||||
|
||||
#[test_case("tests/data/concretefloor003.vmt")]
|
||||
fn test_serde(path: &str) {
|
||||
let raw = read_to_string(path).unwrap();
|
||||
match from_str(&raw) {
|
||||
Ok(result) => insta::assert_ron_snapshot!(path, result),
|
||||
Err(e) => {
|
||||
let handler = GraphicalReportHandler::new_themed(GraphicalTheme::unicode_nocolor());
|
||||
let mut out = String::new();
|
||||
handler.render_report(&mut out, &e).unwrap();
|
||||
insta::assert_snapshot!(path, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
source: tests/parse.rs
|
||||
expression: result
|
||||
---
|
||||
lightmappedgeneric(LightMappedGenericMaterial(
|
||||
r#$basetexture: "cp_mountainlab/concrete/concretefloor003",
|
||||
r#$decal: false,
|
||||
r#$detail: Some("overlays/detail001"),
|
||||
r#$surfaceprop: None,
|
||||
r#$basetexturetransform: "center 0.5 0.5 scale 1 1 rotate 0 translate 0 0",
|
||||
r#$color: Vec3((1.0, 1.0, 1.0)),
|
||||
r#$decalscale: 1.0,
|
||||
r#$decalscale: Vec2((4.0, 4.0)),
|
||||
r#$detailblendfactor: 1.0,
|
||||
r#$detailblendmode: 0,
|
||||
r#$modelmaterial: None,
|
||||
r#$pointsamplemagfilter: false,
|
||||
r#$seamless_scale: 1.0,
|
||||
r#$alpha: 1.0,
|
||||
r#$alphatest: false,
|
||||
r#$distancealpha: false,
|
||||
r#$nocull: false,
|
||||
r#$translucent: false,
|
||||
r#$bumpmap: Some("concrete/concretefloor007b_height-ssbump"),
|
||||
r#$lightwarptexture: None,
|
||||
r#$selfillum: false,
|
||||
r#$ssbump: true,
|
||||
r#$envmap: None,
|
||||
r#$phong: false,
|
||||
r#$nofog: false,
|
||||
))
|
||||
Loading…
Add table
Add a link
Reference in a new issue