world_remove_shadows/
world_remove_shadows.rs

1use brdb::{AsBrdbValue, Brdb, IntoReader, pending::BrPendingFs, schema::BrdbValue};
2use std::path::PathBuf;
3
4/// Opens a world and turns off shadow casting for all bricks
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6    let src = PathBuf::from("world.brdb");
7    let dst = PathBuf::from("world_patched.brdb");
8
9    assert!(src.exists());
10
11    let db = Brdb::open(src)?.into_reader();
12
13    let mut grid_ids = vec![1];
14
15    // Iterate all entity chunks to find dynamic brick grids...
16    // This could totally be a helper function
17    for index in db.entity_chunk_index()? {
18        for e in db.entity_chunk(index)? {
19            // Ensure the chunk is a dynamic brick grid
20            if !e.is_brick_grid() {
21                continue;
22            }
23            let Some(id) = e.id else {
24                continue;
25            };
26            grid_ids.push(id);
27        }
28    }
29
30    let component_schema = db.components_schema()?;
31    let mut grids_files = vec![];
32
33    // Iterate all grids (there can be bricks on entities)
34    for grid in &grid_ids {
35        let chunks = db.brick_chunk_index(*grid)?;
36        let mut chunk_files = vec![];
37        let mut num_grid_modified = 0;
38
39        // Iterate all chunks in the grid
40        for index in chunks {
41            let mut num_chunk_modified = 0;
42            if index.num_components == 0 {
43                println!("ignoring grid {grid} chunk {} with no components", *index);
44                continue;
45            }
46
47            // Iterate all the components in the chunk
48            let (mut soa, components) = db.component_chunk(*grid, *index)?;
49            for mut s in components {
50                // Disable the shadow casting property if it's present and true
51                if s.prop("bCastShadows")
52                    .is_ok_and(|v| v.as_brdb_bool().unwrap_or_default())
53                {
54                    println!(
55                        "grid {grid} chunk {} mutating component {}",
56                        *index,
57                        s.get_name()
58                    );
59                    s.set_prop("bCastShadows", BrdbValue::Bool(false))?;
60                    num_grid_modified += 1;
61                    num_chunk_modified += 1;
62                }
63
64                soa.unwritten_struct_data.push(Box::new(s));
65            }
66
67            if num_chunk_modified == 0 {
68                continue;
69            }
70
71            chunk_files.push((
72                format!("{}.mps", *index),
73                // ComponentChunkSoA::to_bytes ensures the extra data is written after the SoA data
74                BrPendingFs::File(Some(soa.to_bytes(&component_schema)?)),
75            ));
76        }
77
78        if num_grid_modified == 0 {
79            println!("grid {grid} has no shadow-casting components, skipping");
80            continue;
81        } else {
82            println!(
83                "grid {grid} has {num_grid_modified} shadow-casting components in {} files",
84                chunk_files.len()
85            );
86        }
87
88        grids_files.push((
89            grid.to_string(),
90            BrPendingFs::Folder(Some(vec![(
91                "Components".to_string(),
92                BrPendingFs::Folder(Some(chunk_files)),
93            )])),
94        ))
95    }
96
97    let patch = BrPendingFs::Root(vec![(
98        "World".to_owned(),
99        BrPendingFs::Folder(Some(vec![(
100            "0".to_string(),
101            BrPendingFs::Folder(Some(vec![(
102                "Bricks".to_string(),
103                BrPendingFs::Folder(Some(vec![(
104                    "Grids".to_string(),
105                    BrPendingFs::Folder(Some(grids_files)),
106                )])),
107            )])),
108        )])),
109    )]);
110
111    // Use .to_pending_patch() if you want to update the same world
112    let pending = db.to_pending()?.with_patch(patch)?;
113    if dst.exists() {
114        std::fs::remove_file(&dst)?;
115    }
116    Brdb::new(&dst)?.write_pending("Disable Shadow Casting", pending)?;
117
118    // Ensure all the components can be read
119    let db = Brdb::open(dst)?.into_reader();
120    for grid in grid_ids {
121        let chunks = db.brick_chunk_index(grid)?;
122        for index in chunks {
123            if index.num_components == 0 {
124                continue;
125            }
126            let (_soa, _components) = db.component_chunk(grid, *index)?;
127        }
128    }
129
130    Ok(())
131}