Skip to main content

source_map_gen/
vmf.rs

1//! Vmf format traits and impls.
2//! See [`vmf_parser_nom`] crate.
3
4use crate::generation2::disp::Displacement;
5use crate::prelude::Texture;
6use crate::prelude::UVAxis;
7use crate::utils::Vec2d;
8use crate::utils::{NextChunk, TryMap};
9use crate::{
10    generation::region::Room,
11    map::{DispInfo, Entity, Map, Side, Solid},
12    prelude::{Plane, Vector3},
13    StrType,
14};
15use std::fmt::Display;
16use std::str::FromStr;
17use vmf_parser_nom::ast::{Block, Property, Vmf};
18
19// TODO: different trait?
20// tolower and toblock?
21// FIXME: switch almost all to to_lower instead of into
22
23/// Convert to a lower level of abstraction.
24/// Example: A [`Solid`](crate::map::solid::Solid) into a [`Block`](vmf_parser_nom::ast::Block).
25pub trait ToLower<T>: Clone {
26    /// Convert to a lower level of abstraction. See [`ToLower`]
27    fn into_lower(self) -> T {
28        self.to_lower()
29    }
30
31    /// Convert to an owned lower level of abstraction. See [`ToLower`]
32    fn to_lower(&self) -> T {
33        self.clone().into_lower()
34    }
35}
36
37/// Convert to a higher level of abstraction.
38/// Example: A [`Block`](vmf_parser_nom::ast::Block) into a [`Solid`](crate::map::solid::Solid).
39pub(crate) trait ToHigher<T>: Clone {
40    /// Convert this into a low level map element, consuming `self`. See [`ToHigher`].
41    fn into_higher(self) -> T {
42        self.to_higher()
43    }
44    /// Convert this into an owned low level map element. See [`ToHigher`].
45    fn to_higher(&self) -> T {
46        self.clone().into_higher()
47    }
48}
49
50// Implementations //
51
52impl<'a> ToLower<Vmf<StrType<'a>>> for Map<'a> {
53    fn into_lower(self) -> Vmf<StrType<'a>> {
54        let mut vmf = Vmf::default();
55        let mut solid_blocks: Vec<_> = self.solids.iter().map(|s| s.to_lower()).collect();
56        if let Some(cordon) = self.options.cordon.clone() {
57            // TODO: add as actual cordon
58            solid_blocks
59                .extend(Room::new(cordon).construct_sky_inside().iter().map(|s| s.to_lower()));
60        }
61
62        vmf.inner.blocks.push(Block {
63            name: "versioninfo".into(),
64            props: vec![
65                Property::new("editorversion", "400"),
66                Property::new("editorbuild", "9540"),
67                Property::new("mapversion", "1"),
68                Property::new("formatversion", "100"),
69                Property::new("prefab", "0"),
70            ],
71            blocks: vec![],
72        });
73        vmf.inner
74            .blocks
75            .push(Block { name: "visgroups".into(), props: vec![], blocks: vec![] });
76        vmf.inner.blocks.push(Block {
77            name: "viewsettings".into(),
78            props: vec![
79                Property::new("bSnapToGrid", "1"),
80                Property::new("bShowGrid", "1"),
81                Property::new("bShowLogicalGrid", "0"),
82                Property::new("nGridSpacing", "64"),
83                Property::new("bShow3DGrid", "0"),
84            ],
85            blocks: vec![],
86        });
87        vmf.inner.blocks.push(Block {
88            name: "world".into(),
89            props: vec![
90                Property::new("mapversion", "1"),
91                Property::new("classname", "worldspawn"),
92                Property::new("skyname", self.options.sky_name),
93            ],
94            blocks: solid_blocks,
95        });
96        // ENTS HERE
97        vmf.inner.blocks.extend(self.entities.iter().map(|e| e.to_lower()));
98        // cameras unnessesary
99        // cordons unnessesary
100
101        vmf
102    }
103}
104
105impl<'a> ToLower<Block<StrType<'a>>> for Solid<'a> {
106    fn into_lower(self) -> Block<StrType<'a>> {
107        Block {
108            name: "solid".into(),
109            // id unnecessary as [`vmf_parser_nom`] can generate new ids
110            props: vec![],
111            blocks: self.sides.iter().map(|x| x.to_lower()).collect(),
112        }
113    }
114}
115
116impl<'a> ToLower<Block<StrType<'a>>> for Side<'a> {
117    fn into_lower(self) -> Block<StrType<'a>> {
118        let props = vec![
119            // id unnecessary as [`vmf_parser_nom`] can generate new ids
120            Property::new("plane", self.plane.to_string()),
121            Property::new("material", self.texture.material.to_string()),
122            Property::new("uaxis", self.texture.uaxis.to_string()),
123            Property::new("vaxis", self.texture.vaxis.to_string()),
124            // rotation and smoothing group not mandatory
125            // rotation is just for hammer display, smoothing group defaults to 0 (none)
126            // Property::new("rotation", self.rotation.to_string()),
127            Property::new("lightmapscale", self.texture.light_scale.to_string()),
128            Property::new("smoothing_groups", "0".to_string()),
129        ];
130        let blocks = match self.disp {
131            Some(disp) => {
132                let disp_info = disp.into_disp_info();
133                vec![disp_info.into_lower()]
134            }
135            None => vec![],
136        };
137        Block::new("side", props, blocks)
138    }
139}
140
141// impl ToLower<DispInfo> for Displacement {
142//     fn to_lower(&self) -> DispInfo {
143//         self.clone().into_disp_info()
144//     }
145// }
146
147impl<'a> ToLower<Block<StrType<'a>>> for DispInfo {
148    fn into_lower(self) -> Block<StrType<'a>> {
149        use std::fmt::Write;
150        let props = vec![
151            Property::new("power", self.power.to_string()),
152            Property::new("startposition", format!("[{}]", self.start_position)),
153            Property::new("flags", self.flags.to_string()),
154            Property::new("elevation", self.elevation.to_string()),
155            Property::new("subdiv", (self.is_subdiv as i32).to_string()),
156        ];
157        let mut allowed_verts = String::new();
158        for int in self.allowed_verts {
159            write!(&mut allowed_verts, "{} ", int).unwrap();
160        }
161        allowed_verts.truncate(allowed_verts.len() - 1);
162        let allowed_verts = Property::new("10", allowed_verts);
163        let blocks = vec![
164            Block::new("normals", self.normals.to_lower(), vec![]),
165            Block::new("distances", self.distances.to_lower(), vec![]),
166            Block::new("offsets", self.offsets.to_lower(), vec![]),
167            Block::new("offset_normals", self.offset_normals.to_lower(), vec![]),
168            Block::new("alphas", self.alphas.to_lower(), vec![]),
169            Block::new("triangle_tags", self.triangle_tags.to_lower(), vec![]),
170            Block::new("allowed_verts", vec![allowed_verts], vec![]),
171        ];
172        Block::new("dispinfo", props, blocks)
173    }
174}
175
176impl<'a> ToLower<Block<StrType<'a>>> for Entity<StrType<'a>> {
177    fn into_lower(self) -> Block<StrType<'a>> {
178        Block {
179            // name: self.classname.into(),
180            name: "entity".into(),
181            props: self.props,
182            blocks: vec![],
183        }
184    }
185}
186
187impl<'a, T: Clone + Display> ToLower<Vec<Property<StrType<'a>, StrType<'a>>>> for Vec2d<T> {
188    /// https://developer.valvesoftware.com/wiki/.vmf#Normals
189    fn to_lower(&self) -> Vec<Property<StrType<'a>, StrType<'a>>> {
190        use std::fmt::Write;
191        let mut i = 0;
192        let row_to_prop = |row: &[T]| {
193            let key = format!("row{}", i);
194            i += 1;
195
196            // rigmarole for join(" ")
197            let mut value = String::new();
198
199            // let mut row = row.to_vec();
200            // row.reverse();
201            for item in row {
202                write!(&mut value, "{item} ").unwrap();
203            }
204            value.truncate(value.len() - 1);
205
206            Property::new(key, value)
207        };
208
209        // let mut rows = self.rows().collect::<Vec<_>>();
210        // rows.reverse(); // FIXME:HACK:
211        let rows = self.rows();
212
213        rows.into_iter().map(row_to_prop).collect()
214    }
215}
216
217// TODO: better. rn only for tests
218// TODO: FIXME: NOW: git and move and parse stuff instead of floating
219pub(crate) fn block_to_solid<'a>(block: &'a Block<impl AsRef<str>>) -> Solid<'a> {
220    assert_eq!(block.name.as_ref(), "solid");
221
222    // just ignore props lol
223    // Blocks
224    let mut sides = Vec::with_capacity(6); // usually 6
225    for block in block.blocks.iter() {
226        match block.name.as_ref() {
227            "side" => {
228                sides.push(parse_side(block));
229            }
230            "editor" => {}
231            block => panic!("Unexpected block {block}"),
232        }
233    }
234
235    Solid::new(sides)
236}
237
238pub(crate) fn parse_side<'a>(block: &'a Block<impl AsRef<str>>) -> Side<'a> {
239    assert_eq!(block.name.as_ref(), "side");
240
241    // Props
242    let mut plane = None;
243    let mut material = None;
244    let mut uaxis = None;
245    let mut vaxis = None;
246    let mut lightmapscale = None;
247    let mut smoothing_groups = None;
248    for prop in block.props.iter() {
249        let Property { key, value } = prop;
250        let key = key.as_ref();
251        let value = value.as_ref();
252
253        match key {
254            "id" => {}
255            "plane" => {
256                // "plane" "(128 128 128) (128 -128 128) (-128 -128 128)"
257                let s = value.trim_start_matches('(').trim_end_matches(')');
258                let spaces_split = s.split(") (");
259                // NOTE: chunks/next_chunk would be good when stabilized
260                // or like actual good parsing
261                let triplets: Vec<Vector3<f32>> = spaces_split
262                    .map(|three_floats| {
263                        let three_floats: Vec<f32> = three_floats
264                            .split(' ')
265                            .map(|float| float.parse::<f32>().expect("Error parsing  float"))
266                            .collect();
267                        assert_eq!(three_floats.len(), 3, "A point must have 3 numbers");
268                        let array: [_; 3] = three_floats.try_into().unwrap();
269                        Vector3::from(array)
270                    })
271                    .collect();
272
273                // NOTE: most useless required type hint
274                let [bl, tl, tr]: [_; 3] = triplets.try_into().expect("A plane must have 3 points");
275
276                plane = Some(Plane::new(bl, tl, tr));
277            }
278            "material" => {
279                material = Some(value);
280            }
281            "uaxis" => {
282                uaxis = Some(parse_uvaxis(value));
283            }
284            "vaxis" => {
285                vaxis = Some(parse_uvaxis(value));
286            }
287            "rotation" => {}
288            "lightmapscale" => {
289                lightmapscale = value.parse::<u8>().ok();
290            }
291            "smoothing_groups" => {
292                smoothing_groups = value.parse::<i32>().ok();
293            }
294            key => panic!("Unexpected key {key}"),
295        }
296    }
297
298    // Blocks
299    let mut disp_info = None;
300    for block in block.blocks.iter() {
301        let name = block.name.as_ref();
302        match name {
303            "dispinfo" => {
304                disp_info = Some(parse_dispinfo(block).unwrap());
305            }
306            block => panic!("Unexpected block {block}"),
307        }
308    }
309
310    let texture = Texture::new(
311        material.unwrap().into(),
312        uaxis.unwrap(),
313        vaxis.unwrap(),
314        lightmapscale.unwrap(),
315    );
316    let plane = plane.unwrap();
317    let disp = disp_info.map(|disp_info| Displacement::from_disp_info(disp_info, plane.clone()));
318
319    Side { plane, texture, disp }
320}
321
322fn parse_uvaxis(s: &str) -> UVAxis<f32> {
323    // iter over just floats
324    let mut floats = s
325        .split(|char| "[] ".contains(char))
326        .filter(|s| !s.is_empty())
327        .map(|s| s.parse::<f32>().expect("Error parsing f32"));
328    let [x, y, z, trans, scale] = floats.next_chunk2().expect("Bad UVAxis");
329    assert!(floats.next().is_none(), "Too many");
330    UVAxis::new(x, y, z, trans, scale)
331}
332
333fn parse_dispinfo<'a>(block: &Block<impl AsRef<str>>) -> Result<DispInfo, &'static str> {
334    assert_eq!(block.name.as_ref(), "dispinfo");
335
336    // Props
337    let mut power = None;
338    let mut start_position = None;
339    let mut flags = None;
340    let mut elevation = None;
341    let mut is_subdiv = None;
342
343    for prop in block.props.iter() {
344        let Property { key, value } = prop;
345        let key = key.as_ref();
346        let value = value.as_ref();
347
348        match key {
349            "power" => {
350                power = value.parse::<u32>().ok();
351            }
352            "startposition" => {
353                start_position = value.parse::<Vector3<_>>().ok();
354            }
355            "flags" => {
356                flags = value.parse::<i32>().ok();
357            }
358            "elevation" => {
359                elevation = value.parse::<f32>().ok();
360            }
361            "subdiv" => {
362                is_subdiv = value.parse::<i32>().ok();
363            }
364            _ => return Err("Unexpected key"),
365        }
366    }
367
368    // Blocks
369    let mut normals = None;
370    let mut distances = None;
371    let mut offsets = None;
372    let mut offset_normals = None;
373    let mut alphas = None;
374    let mut triangle_tags = None;
375    let mut allowed_verts = None;
376
377    for block in block.blocks.iter() {
378        let name = block.name.as_ref();
379
380        match name {
381            "normals" => {
382                normals = parse_vec2d::<f32>(block).ok();
383            }
384            "distances" => {
385                distances = parse_vec2d::<f32>(block).ok();
386            }
387            "offsets" => {
388                offsets = parse_vec2d::<f32>(block).ok();
389            }
390            "offset_normals" => {
391                offset_normals = parse_vec2d::<f32>(block).ok();
392            }
393            "alphas" => {
394                alphas = parse_vec2d::<f32>(block).ok();
395            }
396            "triangle_tags" => {
397                triangle_tags = parse_vec2d::<i32>(block).ok();
398            }
399            "allowed_verts" => {
400                allowed_verts = parse_seps_chunks_exact(block.props[0].value.as_ref(), " ").ok();
401            }
402            _ => return Err("Unexpected block"),
403        }
404    }
405
406    let normals = vec2d_f32_to_vector3(normals.ok_or("Normal misparse or missing")?);
407    let offsets = vec2d_f32_to_vector3(offsets.ok_or("offsets misparse or missing")?);
408    let offset_normals =
409        vec2d_f32_to_vector3(offset_normals.ok_or("offset_normals misparse or missing")?);
410
411    // let normals = {normals.f32s_to_vector3(normals.unwrap().inner);}
412    // TODO: allow default values for resilience? ex: default flags
413    Ok(DispInfo {
414        power: power.ok_or("power is missing")?,
415        start_position: start_position.ok_or("start_position is missing")?,
416        flags: flags.ok_or("flags is missing")?,
417        elevation: elevation.ok_or("elevation is missing")?,
418        is_subdiv: is_subdiv.ok_or("is_subdiv is missing")? != 0,
419        normals,
420        distances: distances.ok_or("distances is missing")?,
421        offsets,
422        offset_normals,
423        alphas: alphas.ok_or("alphas is missing")?,
424        triangle_tags: triangle_tags.ok_or("triangle_tags is missing")?,
425        allowed_verts: allowed_verts.ok_or("allowed_verts is missing")?,
426    })
427}
428
429/// Helper function.
430/// `split()` on any `seperators` ignore empty strings `parse()` returning array of length `N`
431/// return first error if any.
432fn parse_seps_chunks_exact<const N: usize, T: FromStr>(
433    s: &str, seperators: &str,
434) -> Result<[T; N], &'static str> {
435    // strs between seperators ignoring empty
436    let mut strs = s.split(|char| seperators.contains(char)).filter(|s| !s.is_empty());
437    let str_arr = strs.next_chunk2().ok_or("Too short")?;
438    if strs.next().is_some() {
439        // too many
440        return Err("Too long");
441    }
442    let parsed = str_arr.try_map2(|s| s.parse::<T>()).map_err(|_| "Error parsing")?;
443
444    Ok(parsed)
445}
446
447/// Helper function.
448/// `split()` on any `seperators` ignore empty strings `parse()` returning a vec
449/// return first error if any.
450fn parse_seps_chunks<'a, 'b, T: FromStr + std::fmt::Debug>(
451    s: &'a str, seperators: &'b str,
452) -> Result<Vec<T>, &'static str>
453where
454    <T as FromStr>::Err: std::fmt::Debug,
455{
456    // strs between seperators ignoring empty
457    let strs = s.split(|char| seperators.contains(char)).filter(|s| !s.is_empty());
458    dbg!(strs.clone().map(|s| s.parse::<T>()).collect::<Vec<_>>());
459    strs.map(|s| s.parse::<T>().map_err(|_| "Error parsing")).collect()
460}
461
462impl FromStr for Vector3<f32> {
463    type Err = &'static str;
464
465    fn from_str(s: &str) -> Result<Self, Self::Err> {
466        dbg!(s);
467        match parse_seps_chunks_exact(s, "()[] ") {
468            Ok(array) => Ok(Vector3::from(array)),
469            Err(_) => Err("Error parsing Vector3"),
470        }
471    }
472}
473
474fn vec2d_f32_to_vector3(vec2d: Vec2d<f32>) -> Vec2d<Vector3<f32>> {
475    Vec2d::from_parts(f32s_to_vector3s(&vec2d.inner), [vec2d.strides[0] / 3, 1])
476}
477
478fn f32s_to_vector3s(f32s: &[f32]) -> Vec<Vector3<f32>> {
479    f32s.chunks_exact(3)
480        .map(|slice| {
481            let arr: [_; 3] = slice.try_into().unwrap();
482            Vector3::from(arr)
483        })
484        .collect()
485}
486
487fn parse_vec2d<'a, T: FromStr + std::fmt::Debug>(
488    block: &Block<impl AsRef<str>>,
489) -> Result<Vec2d<T>, &'static str>
490where
491    <T as FromStr>::Err: std::fmt::Debug,
492{
493    let mut output = Vec::new();
494    // TODO: better way to ensure all rows are the same length. hashmap or mut option
495    let mut lens = Vec::with_capacity(block.props.len());
496
497    for (i, prop) in block.props.iter().enumerate() {
498        let Property { key, value } = prop;
499        let key = key.as_ref();
500        let value = value.as_ref();
501        let row_key = format!("row{i}");
502
503        // let mut vec2d = Vec2d::new(Vec2d::strides(1));
504        if row_key == key {
505            let values = parse_seps_chunks(value, " ")?.into_iter();
506            lens.push(values.len());
507            output.extend(values);
508        } else {
509            return Err("Bad row key");
510        }
511    }
512
513    let width = lens.first().copied().unwrap_or(0);
514    if !lens.iter().copied().all(|v| v == width) {
515        return Err("Rows must be the same length");
516    }
517
518    Ok(Vec2d::from_parts(output, Vec2d::strides(width)))
519}
520
521// /// Trait to convert into a low level map element representation.
522// /// Example: A mid level [`Solid`](crate::map::solid) into a low level [`Block`](vmf_parser_nom::ast::Block)
523// pub trait ToLowLevel {
524//     type Target: ?Sized;
525//     /// Convert this into a low level map element, consuming `self`.
526//     fn into_low_level(self) -> Self::Target;
527//     /// Convert this into an owned low level map element.
528//     fn to_low_level(&self) -> Self::Target;
529// }
530
531// /// Trait to convert into a mid level map element representation.
532// /// Example: A high level <TODO: ROOM> into a mid level vec [`Solid`](crate::map::solid)s
533// pub trait ToMidLevel {
534//     type Target: ?Sized;
535//     /// Convert this into a mid level map element, consuming `self`.
536//     fn into_low_level(self) -> Self::Target;
537//     /// Convert this into an owned mid level map element.
538//     fn to_low_level(&self) -> Self::Target;
539// }
540
541// /// Trait to convert into a high level map element representation.
542// /// Example: A vec of solids into a room.
543// pub trait ToHighLevel {
544//     type Target: ?Sized;
545//     /// Convert this into a high level map element, consuming `self`.
546//     fn into_low_level(self) -> Self::Target;
547//     /// Convert this into an owned high level map element.
548//     fn to_low_level(&self) -> Self::Target;
549// }
550
551// use std::{ops::{Deref, DerefMut}, fmt::Display};
552
553// pub use vmf_impl::*;
554// use vmf_parser_nom::ast::{Block, Property};
555
556// #[derive(Clone, Debug, Default)]
557// pub struct Vmf<S>(Vec<Block<S>>);
558
559// pub trait ToVmf<S, T, E> {
560//     /// Convert into vmf ast.
561//     fn to_vmf(&self, state: &mut T) -> Vmf<S>;
562// }
563
564// pub trait ToBlock<S, T, E> {
565//     /// Convert into vmf [`Block`].
566//     fn to_block(&self, state: &mut T) -> Block<S>;
567// }
568
569// pub trait ToProps<T, K, V, E> {
570//     /// Convert into vmf [`Property`]s.
571//     fn to_props(&self, state: &mut T) -> Vec<Property<K, V>>;
572// }
573
574// pub trait FromVmf<T, U, E>
575// where
576//     Self: Sized,
577// {
578//     /// Parse from a part of vmf file.
579//     fn from_vmf(vmf: Vmf<U>, state: &mut T) -> Result<Self, E>;
580// }
581
582// // convenience traits:
583
584// // pub trait PushProp<T, K> {
585// //     fn push_prop(&mut self, key: T, value: String);
586// // }
587
588// // impl<T, K> PushProp<T, K> for Vec<Property<K>>
589// // where
590// //     T: Into<K>,
591// // {
592// //     fn push_prop(&mut self, key: T, value: String) {
593// //         self.push(Property { key: key.into(), value })
594// //     }
595// // }
596
597// pub trait PropertyExt<T, K> {
598//     fn new(key: T, value: String) -> Self;
599// }
600
601// impl<S> Deref for Vmf<S> {
602//     type Target = Vec<Block<S>>;
603
604//     fn deref(&self) -> &Self::Target {
605//         &self.0
606//     }
607// }
608
609// impl<S> DerefMut for Vmf<S> {
610//     fn deref_mut(&mut self) -> &mut Self::Target {
611//         &mut self.0
612//     }
613// }
614
615// impl<S: Display> Display for Vmf<S> {
616//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
617//         writeln!(f, "// auto generated vmf file")?;
618//         for block in self.iter() {
619//             write!(f, "{block:#}")?;
620//         }
621//         Ok(())
622//     }
623// }
624
625// // impl<T, K, V> PropertyExt<T, K> for Property<K, V>
626// // where
627// //     T: Into<K>,
628// // {
629// //     fn new(key: T, value: String) -> Property<K, V> {
630// //         Property { key: key.into(), value }
631// //     }
632// // }