summaryrefslogtreecommitdiff
path: root/src/sim/sim_manager/utils.rs
blob: 413e7099a8c9286b5ed3bcc43e740212f5118b92 (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
128
129
130
131
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(&super::SimCtx {
            cell_manager: &mut sim.cell_manager,
            rb_manager: &mut sim.rb_manager,
            particle_manager: &mut sim.particle_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);
                }
            }
        }
    }
}