summaryrefslogtreecommitdiff
path: root/src/sim/sim_manager/utils.rs
blob: 506844c61593536dd7722d7c9eac7a33d79e1a59 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use glam::IVec2;

use crate::{
    content::materials::MaterialId,
    sim::{cell::Cell, entity::EntityId, sim_manager::SimManager},
};

fn write_entity_to_world(sim: &mut SimManager, entity_id: EntityId) -> Vec<(u8, u8, i32, i32)> {
    let mut cells_written: Vec<(u8, u8, i32, i32)> = Vec::new();
    if let Some(entity) = sim.entities.get(&entity_id)
        && let Some(cells) = &entity.data.cells
        && let Some((pos, (cos, sin))) = entity.data._transform(&sim.rb_manager)
    {
        let (half_size_x, half_size_y) = (cells.size.x as f32 / 2.0, cells.size.y as f32 / 2.0);
        // half-extent of the rotated grid's axis-aligned bounding box, plus a cell of margin
        let (radius_x, radius_y) = (
            half_size_x * (cos.abs() + sin.abs()) + 1.0,
            half_size_y * (cos.abs() + sin.abs()) + 1.0,
        );

        let world_xl = (pos.x - radius_x).floor() as i32;
        let world_xu = (pos.x + radius_x).ceil() as i32;
        let world_yl = (pos.y - radius_y).floor() as i32;
        let world_yu = (pos.y + radius_y).ceil() as i32;

        for world_x in world_xl..=world_xu {
            for world_y in world_yl..=world_yu {
                if let Some(cur_world_cell) = sim
                    .cell_manager
                    .get_cell_from_game_position(world_x, world_y)
                    && cur_world_cell.material == MaterialId::Void
                {
                    // same as shader
                    let d = (world_x as f32 + 0.5 - pos.x, world_y as f32 + 0.5 - pos.y);
                    let q = (d.0.floor() + 0.5, d.1.floor() + 0.5);
                    let (lx, ly) = (
                        (q.0 * cos + q.1 * sin + half_size_x).floor() as i32,
                        (-q.0 * sin + q.1 * cos + half_size_y).floor() as i32,
                    );

                    if lx < 0 || ly < 0 || lx >= cells.size.x || ly >= cells.size.y {
                        continue;
                    }

                    let mut cell = cells.get_cell_at_local_position(IVec2::new(lx, ly));
                    if cell.material == MaterialId::Void {
                        continue;
                    }

                    cell.match_parity(sim.cell_manager.seqno);

                    // TODO: OPTIMIZE!!
                    sim.cell_manager
                        .set_cell_from_game_position(world_x, world_y, cell, false);
                    cells_written.push((lx as u8, ly as u8, world_x, world_y));
                }
            }
        }
    }
    cells_written
}

pub fn write_entities_to_world(
    sim: &mut SimManager,
    // (entity_x, entity_y, cell_x, cell_y)
) -> Vec<(EntityId, Vec<(u8, u8, i32, i32)>)> {
    // TODO optimize
    let entity_ids: Vec<EntityId> = sim.entities.keys().copied().collect();
    let mut cells_written_by_entity: Vec<(EntityId, Vec<(u8, u8, i32, i32)>)> = Vec::new();

    for entity_id in entity_ids {
        let cells_written = write_entity_to_world(sim, entity_id);
        cells_written_by_entity.push((entity_id, cells_written));
    }

    cells_written_by_entity
}

// TODO optimize
pub fn read_back_entities_from_world(
    sim: &mut SimManager,
    cells_written_by_entity: Vec<(EntityId, Vec<(u8, u8, i32, i32)>)>,
) {
    for (entity_id, cells_written) in cells_written_by_entity {
        if let Some(entity) = sim.entities.get_mut(&entity_id) {
            let entity_cells = entity.data.cells.as_mut().unwrap();
            let mut should_update_entity = false;

            for (lx, ly, x, y) in cells_written {
                // update the entity
                // TODO optimize
                let new_local_cell = sim.cell_manager.get_cell_from_game_position(x, y).unwrap();
                if !new_local_cell.entity_integrated() {
                    // this means that the entity changed in some way, so we should recompute its shape
                    // TODO wait N frames to debounce this
                    should_update_entity = true;
                }
                // we do this unconditionally because it's cheaper than checking if it actually needs to be updated
                // and because we don't have a good way to track changes to cell state
                entity_cells
                    .set_cell_at_local_position(IVec2::new(lx as i32, ly as i32), new_local_cell);

                // update the world
                // TODO optimize
                sim.cell_manager
                    .set_cell_from_game_position(x, y, Cell::void(), false);
            }

            if should_update_entity {
                if let Some(new_collider) = entity.compute_collider() {
                    sim.rb_manager
                        .physics_manager
                        .world
                        .remove_collider(entity.data.collider_h.unwrap());

                    let new_handle = sim
                        .rb_manager
                        .physics_manager
                        .world
                        .insert_collider(new_collider, Some(entity.data.rb_h.unwrap()));

                    entity.data.collider_h = Some(new_handle);
                }
            }
        }
    }
}