145 lines
3.8 KiB
Rust
145 lines
3.8 KiB
Rust
// TODO: Move this file somewhere else?
|
|
use std::collections::BTreeMap;
|
|
|
|
use crate::map_block::{MapBlock, NodeMetadataList};
|
|
use crate::spatial::{Vec3, Area};
|
|
|
|
|
|
fn block_parts_valid(a: &Area, b: &Area) -> bool {
|
|
fn part_valid(a: &Area) -> bool {
|
|
a.min.x >= 0 && a.min.y >= 0 && a.min.z >= 0
|
|
&& a.max.x < 16 && a.max.y < 16 && a.max.z < 16
|
|
}
|
|
part_valid(a) && part_valid(b) && a.max - a.min == b.max - b.min
|
|
}
|
|
|
|
|
|
/// Copy an area of nodes from one mapblock to another.
|
|
///
|
|
/// Will not remove duplicate/unused name IDs.
|
|
pub fn merge_blocks(
|
|
src_block: &MapBlock,
|
|
dst_block: &mut MapBlock,
|
|
src_area: Area,
|
|
dst_area: Area
|
|
) {
|
|
assert!(block_parts_valid(&src_area, &dst_area));
|
|
|
|
let src_nd = src_block.node_data.get_ref();
|
|
let dst_nd = dst_block.node_data.get_mut();
|
|
let offset = dst_area.min - src_area.min;
|
|
// Warning: diff can be negative!
|
|
let diff = offset.x + offset.y * 16 + offset.z * 256;
|
|
|
|
// Copy name-ID mappings
|
|
let nimap_diff = dst_block.nimap.get_max_id().unwrap() + 1;
|
|
for (id, name) in &src_block.nimap.0 {
|
|
dst_block.nimap.0.insert(id + nimap_diff, name.to_vec());
|
|
}
|
|
|
|
// Copy node IDs
|
|
for z in src_area.min.z ..= src_area.max.z {
|
|
for y in src_area.min.y ..= src_area.max.y {
|
|
for x in src_area.min.x ..= src_area.max.x {
|
|
let idx = x + y * 16 + z * 256;
|
|
dst_nd.nodes[(idx + diff) as usize] =
|
|
src_nd.nodes[idx as usize] + nimap_diff;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy param1 and param2
|
|
for z in src_area.min.z ..= src_area.max.z {
|
|
for y in src_area.min.y ..= src_area.max.y {
|
|
let row_start = y * 16 + z * 256;
|
|
let start = row_start + src_area.min.x;
|
|
let end = row_start + src_area.max.x;
|
|
|
|
dst_nd.param1[(start + diff) as usize ..= (end + diff) as usize]
|
|
.clone_from_slice(
|
|
&src_nd.param1[start as usize ..= end as usize]
|
|
);
|
|
dst_nd.param2[(start + diff) as usize ..= (end + diff) as usize]
|
|
.clone_from_slice(
|
|
&src_nd.param2[start as usize ..= end as usize]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// Copy an area of node metadata from one mapblock to another.
|
|
pub fn merge_metadata(
|
|
src_meta: &NodeMetadataList,
|
|
dst_meta: &mut NodeMetadataList,
|
|
src_area: Area,
|
|
dst_area: Area
|
|
) {
|
|
assert!(block_parts_valid(&src_area, &dst_area));
|
|
|
|
let offset = dst_area.min - src_area.min;
|
|
// Warning: diff can be negative!
|
|
let diff = offset.x + offset.y * 16 + offset.z * 256;
|
|
|
|
// Delete any existing metadata in the destination block
|
|
let mut to_delete = Vec::with_capacity(dst_meta.len());
|
|
for (&idx, _) in dst_meta.iter() {
|
|
let pos = Vec3::from_u16_key(idx);
|
|
if dst_area.contains(pos) {
|
|
to_delete.push(idx);
|
|
}
|
|
}
|
|
for idx in &to_delete {
|
|
dst_meta.remove(idx);
|
|
}
|
|
|
|
// Copy new metadata
|
|
for (&idx, meta) in src_meta {
|
|
let pos = Vec3::from_u16_key(idx);
|
|
if src_area.contains(pos) {
|
|
dst_meta.insert((idx as i32 + diff) as u16, meta.clone());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// Culls duplicate and unused IDs from the name-ID map and node data.
|
|
pub fn clean_name_id_map(block: &mut MapBlock) {
|
|
let nd = block.node_data.get_mut();
|
|
let id_count = (block.nimap.get_max_id().unwrap() + 1) as usize;
|
|
|
|
// Determine which IDs are used.
|
|
let mut used = vec![false; id_count];
|
|
for id in &nd.nodes {
|
|
used[*id as usize] = true;
|
|
}
|
|
|
|
// Rebuild the name-ID map.
|
|
let mut new_nimap = BTreeMap::new();
|
|
let mut map = vec![0u16; id_count];
|
|
for id in 0..id_count {
|
|
// Skip unused IDs.
|
|
if !used[id] {
|
|
continue;
|
|
}
|
|
|
|
let name = &block.nimap.0[&(id as u16)];
|
|
if let Some(first_id) = new_nimap.iter().position(|(_, v)| v == name) {
|
|
// Name is already in the map; map old, duplicate ID to the
|
|
// existing ID.
|
|
map[id] = first_id as u16;
|
|
} else {
|
|
// Name is not yet in the map; assign it to the next ID.
|
|
new_nimap.insert(new_nimap.len() as u16, name.clone());
|
|
// Map old ID to newly-inserted ID.
|
|
map[id] = new_nimap.len() as u16 - 1;
|
|
}
|
|
}
|
|
block.nimap.0 = new_nimap;
|
|
|
|
// Re-assign node IDs.
|
|
for id in &mut nd.nodes {
|
|
*id = map[*id as usize];
|
|
}
|
|
}
|