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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
use crate::{
    global::{EntityId, Vec3},
    internal::{
        conversion::{FromBindgen, IntoBindgen},
        wit,
    },
};
use glam::Mat4;

/// Applies a `force` (a [Vec3]) to the `entity` (an [EntityId]) specified.
///
/// `force` is a vector in world space, which means it has both direction and magnitude. To push objects upwards
/// (positive Z) with strength 3,000, you would supply a force of `vec3(0.0, 0.0, 3_000.0)` or
/// `Vec3::Z * 3_000.0` (either are equivalent.)
pub fn add_force(entity: EntityId, force: Vec3) {
    wit::server_physics::add_force(entity.into_bindgen(), force.into_bindgen())
}

/// Applies an `impulse` (a [Vec3]) to the `entity` (an [EntityId]) specified.
///
/// `impulse` is a vector in world space, which means it has both direction and magnitude. To push objects upwards
/// (positive Z) with strength 3,000, you would supply an impulse of `vec3(0.0, 0.0, 3_000.0)` or
/// `Vec3::Z * 3_000.0` (either are equivalent.)
pub fn add_impulse(entity: EntityId, impulse: Vec3) {
    wit::server_physics::add_force(entity.into_bindgen(), impulse.into_bindgen())
}

/// Whether or not to apply a falloff to the strength of [add_radial_impulse].
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum FalloffRadius {
    /// No falloff. The impulse will be of equal strength for all entities.
    #[default]
    None,
    /// Applies a falloff to the strength of the impulse, so that it drops out the further out the object is,
    /// until it reaches a strength of 0 at `falloff_radius`.
    FalloffToZeroAt(f32),
}
impl From<FalloffRadius> for Option<f32> {
    fn from(radius: FalloffRadius) -> Option<f32> {
        match radius {
            FalloffRadius::None => None,
            FalloffRadius::FalloffToZeroAt(r) => Some(r),
        }
    }
}

/// Applies an `impulse` (a [f32]) outwards to all entitities within `radius` of the `position`, with
/// an optional `falloff_radius`.
pub fn add_radial_impulse(
    position: Vec3,
    impulse: f32,
    radius: f32,
    falloff_radius: FalloffRadius,
) {
    wit::server_physics::add_radial_impulse(
        position.into_bindgen(),
        impulse,
        radius,
        falloff_radius.into(),
    )
}

/// Applies a `force` (a [Vec3]) at a given `position` (a [Vec3]) to the `entity` (an [EntityId]) specified.
///
/// `force` is a vector in world space, which means it has both direction and magnitude. To push objects upwards
/// (positive Z) with strength 3,000, you would supply a force of `vec3(0.0, 0.0, 3_000.0)` or
/// `Vec3::Z * 3_000.0` (either are equivalent.)
///
/// `position` is a position in world space, it typically should fall on the surface or interior of an object for
/// realistic results.
pub fn add_force_at_position(entity: EntityId, force: Vec3, position: Vec3) {
    wit::server_physics::add_force_at_position(
        entity.into_bindgen(),
        force.into_bindgen(),
        position.into_bindgen(),
    )
}

/// Applies an `impulse` (a [Vec3]) at given `position` (a [Vec3]) to the `entity` (an [EntityId]) specified.
///
/// `impulse` is a vector in world space, which means it has both direction and magnitude. To push objects upwards
/// (positive Z) with strength 3,000, you would supply an impulse of `vec3(0.0, 0.0, 3_000.0)` or
/// `Vec3::Z * 3_000.0` (either are equivalent.)
///
/// `position` is a position in world space, it typically should fall on the surface or interior of an object for
/// realistic results.
pub fn add_impulse_at_position(entity: EntityId, impulse: Vec3, position: Vec3) {
    wit::server_physics::add_impulse_at_position(
        entity.into_bindgen(),
        impulse.into_bindgen(),
        position.into_bindgen(),
    )
}

/// Gets the velocity (a [Vec3]) at a given `position` (a [Vec3]) of an `entity` (an [EntityId]) taking its
/// angular velocity into account.
///
/// `position` is a position in world space, it typically should fall on the surface or interior of an object.
pub fn get_velocity_at_position(entity: EntityId, position: Vec3) -> Vec3 {
    wit::server_physics::get_velocity_at_position(entity.into_bindgen(), position.into_bindgen())
        .from_bindgen()
}

/// Sets the gravity of the entire world to `gravity`. The default `gravity` is `vec3(0.0, 0.0, -9.82)`.
///
/// This can be used to simulate a different gravity from Earth's, or to create unconventional gameplay.
pub fn set_gravity(gravity: Vec3) {
    wit::server_physics::set_gravity(gravity.into_bindgen())
}

/// Unfreezes a frozen `entity`, so that it can move around. Does nothing if the entity wasn't frozen.
pub fn unfreeze(entity: EntityId) {
    wit::server_physics::unfreeze(entity.into_bindgen())
}

/// Freezes an `entity`, so that it cannot move around. Does nothing if the entity was already frozen.
pub fn freeze(entity: EntityId) {
    wit::server_physics::freeze(entity.into_bindgen())
}

/// Starts a motor on `entity` with `velocity`. Does nothing if the motor has already been started.
pub fn start_motor(entity: EntityId, velocity: f32) {
    wit::server_physics::start_motor(entity.into_bindgen(), velocity)
}

/// Stops a motor on `entity`. Does nothing if the motor is not running.
pub fn stop_motor(entity: EntityId) {
    wit::server_physics::stop_motor(entity.into_bindgen())
}

/// Creates a revolute joint. entity0 or entity1 can either be `EntityId::null()` to bind this to the world frame.
pub fn create_revolute_joint(
    entity0: EntityId,
    transform0: Mat4,
    entity1: EntityId,
    transform1: Mat4,
) {
    wit::server_physics::create_revolute_joint(
        entity0.into_bindgen(),
        transform0.into_bindgen(),
        entity1.into_bindgen(),
        transform1.into_bindgen(),
    )
}

/// Where a [raycast] hit.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RaycastHit {
    /// The position of the hit.
    pub position: Vec3,
    /// The distance from the origin to the hit.
    pub distance: f32,
    /// The entity that was hit.
    pub entity: EntityId,
}
/// Casts a ray from `origin` in `direction`, and returns the [RaycastHit]s along the way.
///
/// `direction` must be normalized.
pub fn raycast(origin: Vec3, direction: Vec3) -> Vec<RaycastHit> {
    wit::server_physics::raycast(origin.into_bindgen(), direction.into_bindgen())
        .into_iter()
        .map(|(entity, distance)| raycast_result_to_hit(origin, direction, entity, distance))
        .collect()
}
/// Casts a ray from `origin` in `direction`, and returns the first [RaycastHit] if it hits.
///
/// `direction` must be normalized.
pub fn raycast_first(origin: Vec3, direction: Vec3) -> Option<RaycastHit> {
    wit::server_physics::raycast_first(origin.into_bindgen(), direction.into_bindgen())
        .map(|(entity, distance)| raycast_result_to_hit(origin, direction, entity, distance))
}
fn raycast_result_to_hit(
    origin: Vec3,
    direction: Vec3,
    entity: wit::types::EntityId,
    distance: f32,
) -> RaycastHit {
    RaycastHit {
        position: origin + direction * distance,
        distance,
        entity: entity.from_bindgen(),
    }
}

/// Collision results when using [move_character].
pub struct CharacterCollision {
    /// Side
    pub side: bool,
    /// Up
    pub up: bool,
    /// Down
    pub down: bool,
}

/// Move an entity with a character collider on it, by sweeping the collider.
/// This will ensure that it collides with any objects in its path.
///
/// A character collider can be added to an entity using the `character_controller` concept.
///
/// You can also update the entity's [translation](crate::core::transform::components::translation) component,
/// but this will teleport it to that location.
///
/// Arguments:
///  - `displacement`: The displacement to move the character by.
///  - `min_dist`: The minimum travelled distance to consider. If travelled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small.
///  - `elapsed_time`: The elapsed time since last call to this function.
pub fn move_character(
    entity: EntityId,
    displacement: Vec3,
    min_dist: f32,
    elapsed_time: f32,
) -> CharacterCollision {
    let res = wit::server_physics::move_character(
        entity.into_bindgen(),
        displacement.into_bindgen(),
        min_dist,
        elapsed_time,
    );
    CharacterCollision {
        side: res.side,
        up: res.up,
        down: res.down,
    }
}

/// Set character controller position
pub fn set_character_position(entity: EntityId, position: Vec3) {
    wit::server_physics::set_character_position(entity.into_bindgen(), position.into_bindgen());
}

/// Set character controller foot position
pub fn set_character_foot_position(entity: EntityId, position: Vec3) {
    wit::server_physics::set_character_foot_position(
        entity.into_bindgen(),
        position.into_bindgen(),
    );
}