2016-03-26 18:21:47 -04:00
|
|
|
|
2018-11-04 14:48:03 -05:00
|
|
|
use crate::ecs;
|
2016-03-26 18:21:47 -04:00
|
|
|
use super::{
|
|
|
|
Position,
|
2016-04-07 14:30:42 -04:00
|
|
|
TargetPosition,
|
2016-03-26 18:21:47 -04:00
|
|
|
Velocity,
|
|
|
|
Rotation,
|
2016-04-07 14:30:42 -04:00
|
|
|
TargetRotation,
|
2016-03-26 18:21:47 -04:00
|
|
|
Gravity,
|
|
|
|
Bounds,
|
|
|
|
GameInfo,
|
2016-04-08 06:08:21 -04:00
|
|
|
Light
|
2016-03-26 18:21:47 -04:00
|
|
|
};
|
2018-11-04 14:48:03 -05:00
|
|
|
use crate::world;
|
|
|
|
use crate::render;
|
|
|
|
use crate::render::model::{self, FormatState};
|
|
|
|
use crate::types::Gamemode;
|
2016-03-26 18:21:47 -04:00
|
|
|
use collision::{Aabb, Aabb3};
|
2018-10-27 21:11:26 -04:00
|
|
|
use cgmath::{self, Point3, Vector3, Matrix4, Decomposed, Rotation3, Rad, Quaternion};
|
2016-03-26 18:21:47 -04:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::hash::BuildHasherDefault;
|
2019-01-26 16:00:19 -05:00
|
|
|
use std::time::Instant;
|
2018-11-04 14:48:03 -05:00
|
|
|
use crate::types::hash::FNVHash;
|
|
|
|
use crate::settings::Stevenkey;
|
|
|
|
use crate::shared::Position as BPosition;
|
|
|
|
use crate::format;
|
2016-03-26 18:21:47 -04:00
|
|
|
|
|
|
|
pub fn add_systems(m: &mut ecs::Manager) {
|
|
|
|
let sys = MovementHandler::new(m);
|
2016-04-08 19:40:35 -04:00
|
|
|
m.add_system(sys);
|
2016-03-28 09:15:21 -04:00
|
|
|
let sys = PlayerRenderer::new(m);
|
|
|
|
m.add_render_system(sys);
|
2016-03-26 18:21:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn create_local(m: &mut ecs::Manager) -> ecs::Entity {
|
|
|
|
let entity = m.create_entity();
|
2016-03-27 12:08:38 -04:00
|
|
|
m.add_component_direct(entity, Position::new(0.0, 0.0, 0.0));
|
2016-04-08 20:11:33 -04:00
|
|
|
let mut tpos = TargetPosition::new(0.0, 0.0, 0.0);
|
|
|
|
tpos.lerp_amount = 1.0 / 3.0;
|
|
|
|
m.add_component_direct(entity, tpos);
|
2016-03-26 18:21:47 -04:00
|
|
|
m.add_component_direct(entity, Rotation::new(0.0, 0.0));
|
|
|
|
m.add_component_direct(entity, Velocity::new(0.0, 0.0, 0.0));
|
|
|
|
m.add_component_direct(entity, Gamemode::Survival);
|
|
|
|
m.add_component_direct(entity, Gravity::new());
|
|
|
|
m.add_component_direct(entity, PlayerMovement::new());
|
|
|
|
m.add_component_direct(entity, Bounds::new(Aabb3::new(
|
|
|
|
Point3::new(-0.3, 0.0, -0.3),
|
|
|
|
Point3::new(0.3, 1.8, 0.3)
|
|
|
|
)));
|
2016-04-07 16:30:20 -04:00
|
|
|
m.add_component_direct(entity, PlayerModel::new("", false, false, true));
|
2016-04-08 06:08:21 -04:00
|
|
|
m.add_component_direct(entity, Light::new());
|
2016-03-26 18:21:47 -04:00
|
|
|
entity
|
|
|
|
}
|
|
|
|
|
2016-04-07 16:30:20 -04:00
|
|
|
pub fn create_remote(m: &mut ecs::Manager, name: &str) -> ecs::Entity {
|
2016-04-07 14:30:42 -04:00
|
|
|
let entity = m.create_entity();
|
|
|
|
m.add_component_direct(entity, Position::new(0.0, 0.0, 0.0));
|
|
|
|
m.add_component_direct(entity, TargetPosition::new(0.0, 0.0, 0.0));
|
|
|
|
m.add_component_direct(entity, Rotation::new(0.0, 0.0));
|
|
|
|
m.add_component_direct(entity, TargetRotation::new(0.0, 0.0));
|
|
|
|
m.add_component_direct(entity, Velocity::new(0.0, 0.0, 0.0));
|
|
|
|
m.add_component_direct(entity, Bounds::new(Aabb3::new(
|
|
|
|
Point3::new(-0.3, 0.0, -0.3),
|
|
|
|
Point3::new(0.3, 1.8, 0.3)
|
|
|
|
)));
|
2016-04-07 16:30:20 -04:00
|
|
|
m.add_component_direct(entity, PlayerModel::new(name, true, true, false));
|
2016-04-08 06:08:21 -04:00
|
|
|
m.add_component_direct(entity, Light::new());
|
2016-04-07 14:30:42 -04:00
|
|
|
entity
|
|
|
|
}
|
|
|
|
|
2016-03-28 09:15:21 -04:00
|
|
|
|
|
|
|
pub struct PlayerModel {
|
|
|
|
model: Option<model::ModelKey>,
|
2016-04-07 10:55:03 -04:00
|
|
|
skin_url: Option<String>,
|
|
|
|
dirty: bool,
|
2016-04-07 16:30:20 -04:00
|
|
|
name: String,
|
2016-03-28 09:15:21 -04:00
|
|
|
|
|
|
|
has_head: bool,
|
|
|
|
has_name_tag: bool,
|
|
|
|
first_person: bool,
|
|
|
|
|
|
|
|
dir: i32,
|
|
|
|
time: f64,
|
|
|
|
still_time: f64,
|
|
|
|
idle_time: f64,
|
|
|
|
arm_time: f64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PlayerModel {
|
2016-04-07 16:30:20 -04:00
|
|
|
pub fn new(name: &str, has_head: bool, has_name_tag: bool, first_person: bool) -> PlayerModel {
|
2016-03-28 09:15:21 -04:00
|
|
|
PlayerModel {
|
|
|
|
model: None,
|
2016-04-07 10:55:03 -04:00
|
|
|
skin_url: None,
|
|
|
|
dirty: false,
|
2016-04-07 16:30:20 -04:00
|
|
|
name: name.to_owned(),
|
2016-03-28 09:15:21 -04:00
|
|
|
|
2018-11-04 16:43:30 -05:00
|
|
|
has_head,
|
|
|
|
has_name_tag,
|
|
|
|
first_person,
|
2016-03-28 09:15:21 -04:00
|
|
|
|
|
|
|
dir: 0,
|
|
|
|
time: 0.0,
|
|
|
|
still_time: 0.0,
|
|
|
|
idle_time: 0.0,
|
|
|
|
arm_time: 0.0,
|
|
|
|
}
|
|
|
|
}
|
2016-04-07 10:55:03 -04:00
|
|
|
|
|
|
|
pub fn set_skin(&mut self, skin: Option<String>) {
|
|
|
|
self.skin_url = skin;
|
|
|
|
self.dirty = true;
|
|
|
|
}
|
2016-03-28 09:15:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
struct PlayerRenderer {
|
|
|
|
filter: ecs::Filter,
|
|
|
|
player_model: ecs::Key<PlayerModel>,
|
|
|
|
position: ecs::Key<Position>,
|
|
|
|
rotation: ecs::Key<Rotation>,
|
|
|
|
game_info: ecs::Key<GameInfo>,
|
2016-04-08 06:08:21 -04:00
|
|
|
light: ecs::Key<Light>,
|
2016-03-28 09:15:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PlayerRenderer {
|
|
|
|
fn new(m: &mut ecs::Manager) -> PlayerRenderer {
|
|
|
|
let player_model = m.get_key();
|
|
|
|
let position = m.get_key();
|
|
|
|
let rotation = m.get_key();
|
2016-04-08 06:08:21 -04:00
|
|
|
let light = m.get_key();
|
2016-03-28 09:15:21 -04:00
|
|
|
PlayerRenderer {
|
|
|
|
filter: ecs::Filter::new()
|
|
|
|
.with(player_model)
|
|
|
|
.with(position)
|
2016-04-08 06:08:21 -04:00
|
|
|
.with(rotation)
|
|
|
|
.with(light),
|
2018-11-04 16:43:30 -05:00
|
|
|
player_model,
|
|
|
|
position,
|
|
|
|
rotation,
|
2016-03-28 09:15:21 -04:00
|
|
|
game_info: m.get_key(),
|
2018-11-04 16:43:30 -05:00
|
|
|
light,
|
2016-03-28 09:15:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum PlayerModelPart {
|
|
|
|
Head = 0,
|
|
|
|
Body = 1,
|
|
|
|
LegLeft = 2,
|
|
|
|
LegRight = 3,
|
|
|
|
ArmLeft = 4,
|
|
|
|
ArmRight = 5,
|
2016-04-07 16:30:20 -04:00
|
|
|
NameTag = 6,
|
2018-09-29 20:53:01 -04:00
|
|
|
//Cape = 7, // TODO
|
2016-03-28 09:15:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Setup culling
|
|
|
|
impl ecs::System for PlayerRenderer {
|
|
|
|
|
|
|
|
fn filter(&self) -> &ecs::Filter {
|
|
|
|
&self.filter
|
|
|
|
}
|
|
|
|
|
2016-04-07 10:55:03 -04:00
|
|
|
fn update(&mut self, m: &mut ecs::Manager, world: &mut world::World, renderer: &mut render::Renderer) {
|
2016-03-28 09:15:21 -04:00
|
|
|
use std::f32::consts::PI;
|
|
|
|
use std::f64::consts::PI as PI64;
|
|
|
|
let world_entity = m.get_world();
|
|
|
|
let delta = m.get_component_mut(world_entity, self.game_info).unwrap().delta;
|
|
|
|
for e in m.find(&self.filter) {
|
|
|
|
let player_model = m.get_component_mut(e, self.player_model).unwrap();
|
|
|
|
let position = m.get_component_mut(e, self.position).unwrap();
|
|
|
|
let rotation = m.get_component_mut(e, self.rotation).unwrap();
|
2016-04-08 06:08:21 -04:00
|
|
|
let light = m.get_component(e, self.light).unwrap();
|
2016-03-28 09:15:21 -04:00
|
|
|
|
2016-04-07 10:55:03 -04:00
|
|
|
if player_model.dirty {
|
|
|
|
self.entity_removed(m, e, world, renderer);
|
|
|
|
self.entity_added(m, e, world, renderer);
|
|
|
|
}
|
|
|
|
|
2016-03-28 09:15:21 -04:00
|
|
|
if let Some(pmodel) = player_model.model {
|
|
|
|
let mdl = renderer.model.get_model(pmodel).unwrap();
|
2016-04-08 06:08:21 -04:00
|
|
|
|
|
|
|
mdl.block_light = light.block_light;
|
|
|
|
mdl.sky_light = light.sky_light;
|
|
|
|
|
2016-03-28 09:15:21 -04:00
|
|
|
let offset = if player_model.first_person {
|
|
|
|
let ox = (rotation.yaw - PI64/2.0).cos() * 0.25;
|
|
|
|
let oz = -(rotation.yaw - PI64/2.0).sin() * 0.25;
|
|
|
|
Vector3::new(
|
|
|
|
position.position.x as f32 - ox as f32,
|
|
|
|
-position.position.y as f32,
|
|
|
|
position.position.z as f32 - oz as f32,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
Vector3::new(
|
|
|
|
position.position.x as f32,
|
|
|
|
-position.position.y as f32,
|
|
|
|
position.position.z as f32,
|
|
|
|
)
|
|
|
|
};
|
|
|
|
let offset_matrix = Matrix4::from(Decomposed {
|
|
|
|
scale: 1.0,
|
2018-10-27 21:11:26 -04:00
|
|
|
rot: Quaternion::from_angle_y(Rad(PI + rotation.yaw as f32)),
|
2016-03-28 09:15:21 -04:00
|
|
|
disp: offset,
|
|
|
|
});
|
|
|
|
|
2016-04-07 16:30:20 -04:00
|
|
|
// TODO This sucks
|
|
|
|
if player_model.has_name_tag {
|
|
|
|
let ang = (position.position.x - renderer.camera.pos.x).atan2(position.position.z - renderer.camera.pos.z) as f32;
|
|
|
|
mdl.matrix[PlayerModelPart::NameTag as usize] = Matrix4::from(Decomposed {
|
|
|
|
scale: 1.0,
|
2018-10-27 21:11:26 -04:00
|
|
|
rot: Quaternion::from_angle_y(Rad(ang)),
|
2016-04-07 16:30:20 -04:00
|
|
|
disp: offset + Vector3::new(0.0, (-24.0/16.0) - 0.6, 0.0),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-03-28 09:15:21 -04:00
|
|
|
mdl.matrix[PlayerModelPart::Head as usize] = offset_matrix * Matrix4::from(Decomposed {
|
|
|
|
scale: 1.0,
|
2018-10-27 21:11:26 -04:00
|
|
|
rot: Quaternion::from_angle_x(Rad(-rotation.pitch as f32)),
|
2016-03-28 09:15:21 -04:00
|
|
|
disp: Vector3::new(0.0, -12.0/16.0 - 12.0/16.0, 0.0),
|
|
|
|
});
|
|
|
|
mdl.matrix[PlayerModelPart::Body as usize] = offset_matrix * Matrix4::from(Decomposed {
|
|
|
|
scale: 1.0,
|
2018-10-27 21:11:26 -04:00
|
|
|
rot: Quaternion::from_angle_x(Rad(0.0)),
|
2016-03-28 09:15:21 -04:00
|
|
|
disp: Vector3::new(0.0, -12.0/16.0 - 6.0/16.0, 0.0),
|
|
|
|
});
|
|
|
|
|
|
|
|
let mut time = player_model.time;
|
|
|
|
let mut dir = player_model.dir;
|
|
|
|
if dir == 0 {
|
|
|
|
dir = 1;
|
|
|
|
time = 15.0;
|
|
|
|
}
|
|
|
|
let ang = ((time / 15.0) - 1.0) * (PI64 / 4.0);
|
|
|
|
|
|
|
|
mdl.matrix[PlayerModelPart::LegRight as usize] = offset_matrix * Matrix4::from(Decomposed {
|
|
|
|
scale: 1.0,
|
2018-10-27 21:11:26 -04:00
|
|
|
rot: Quaternion::from_angle_x(Rad(ang as f32)),
|
2016-03-28 09:15:21 -04:00
|
|
|
disp: Vector3::new(2.0/16.0, -12.0/16.0, 0.0),
|
|
|
|
});
|
|
|
|
mdl.matrix[PlayerModelPart::LegLeft as usize] = offset_matrix * Matrix4::from(Decomposed {
|
|
|
|
scale: 1.0,
|
2018-10-27 21:11:26 -04:00
|
|
|
rot: Quaternion::from_angle_x(Rad(-ang as f32)),
|
2016-03-28 09:15:21 -04:00
|
|
|
disp: Vector3::new(-2.0/16.0, -12.0/16.0, 0.0),
|
|
|
|
});
|
|
|
|
|
|
|
|
let mut i_time = player_model.idle_time;
|
|
|
|
i_time += delta * 0.02;
|
|
|
|
if i_time > PI64 * 2.0 {
|
|
|
|
i_time -= PI64 * 2.0;
|
|
|
|
}
|
|
|
|
player_model.idle_time = i_time;
|
|
|
|
|
|
|
|
if player_model.arm_time <= 0.0 {
|
|
|
|
player_model.arm_time = 0.0;
|
|
|
|
} else {
|
|
|
|
player_model.arm_time -= delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
mdl.matrix[PlayerModelPart::ArmRight as usize] = offset_matrix * Matrix4::from_translation(
|
|
|
|
Vector3::new(6.0/16.0, -12.0/16.0-12.0/16.0, 0.0)
|
2018-10-27 21:11:26 -04:00
|
|
|
) * Matrix4::from(Quaternion::from_angle_x(Rad(-(ang * 0.75) as f32)))
|
|
|
|
* Matrix4::from(Quaternion::from_angle_z(Rad((i_time.cos() * 0.06 - 0.06) as f32)))
|
|
|
|
* Matrix4::from(Quaternion::from_angle_x(Rad((i_time.sin() * 0.06 - ((7.5 - (player_model.arm_time-7.5).abs()) / 7.5)) as f32)));
|
2016-03-28 09:15:21 -04:00
|
|
|
|
|
|
|
mdl.matrix[PlayerModelPart::ArmLeft as usize] = offset_matrix * Matrix4::from_translation(
|
|
|
|
Vector3::new(-6.0/16.0, -12.0/16.0-12.0/16.0, 0.0)
|
2018-10-27 21:11:26 -04:00
|
|
|
) * Matrix4::from(Quaternion::from_angle_x(Rad((ang * 0.75) as f32)))
|
|
|
|
* Matrix4::from(Quaternion::from_angle_z(Rad(-(i_time.cos() * 0.06 - 0.06) as f32)))
|
|
|
|
* Matrix4::from(Quaternion::from_angle_x(Rad(-(i_time.sin() * 0.06) as f32)));
|
2016-03-28 09:15:21 -04:00
|
|
|
|
|
|
|
let mut update = true;
|
2016-04-02 20:26:31 -04:00
|
|
|
if position.moved {
|
|
|
|
player_model.still_time = 0.0;
|
2016-04-08 12:45:05 -04:00
|
|
|
} else if player_model.still_time > 2.0 {
|
2016-04-02 20:26:31 -04:00
|
|
|
if (time - 15.0).abs() <= 1.5 * delta {
|
|
|
|
time = 15.0;
|
|
|
|
update = false;
|
2016-03-28 09:15:21 -04:00
|
|
|
}
|
2016-04-02 20:26:31 -04:00
|
|
|
dir = (15.0 - time).signum() as i32;
|
2016-03-28 09:15:21 -04:00
|
|
|
} else {
|
2016-04-02 20:26:31 -04:00
|
|
|
player_model.still_time += delta;
|
2016-03-28 09:15:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if update {
|
|
|
|
time += delta * 1.5 * (dir as f64);
|
|
|
|
if time > 30.0 {
|
|
|
|
time = 30.0;
|
|
|
|
dir = -1;
|
|
|
|
} else if time < 0.0 {
|
|
|
|
time = 0.0;
|
|
|
|
dir = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
player_model.time = time;
|
|
|
|
player_model.dir = dir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn entity_added(&mut self, m: &mut ecs::Manager, e: ecs::Entity, _: &mut world::World, renderer: &mut render::Renderer) {
|
|
|
|
let player_model = m.get_component_mut(e, self.player_model).unwrap();
|
|
|
|
|
2016-04-07 10:55:03 -04:00
|
|
|
player_model.dirty = false;
|
|
|
|
|
|
|
|
let skin = if let Some(url) = player_model.skin_url.as_ref() {
|
2016-09-15 10:15:52 -04:00
|
|
|
renderer.get_skin(renderer.get_textures_ref(), url)
|
2016-04-07 10:55:03 -04:00
|
|
|
} else {
|
|
|
|
render::Renderer::get_texture(renderer.get_textures_ref(), "entity/steve")
|
|
|
|
};
|
2016-03-28 09:15:21 -04:00
|
|
|
|
|
|
|
macro_rules! srel {
|
|
|
|
($x:expr, $y:expr, $w:expr, $h:expr) => (
|
|
|
|
Some(skin.relative(($x) / 64.0, ($y) / 64.0, ($w) / 64.0, ($h) / 64.0))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut head_verts = vec![];
|
|
|
|
if player_model.has_head {
|
|
|
|
model::append_box(&mut head_verts, -4.0/16.0, 0.0, -4.0/16.0, 8.0/16.0, 8.0/16.0, 8.0/16.0, [
|
|
|
|
srel!(16.0, 0.0, 8.0, 8.0), // Down
|
2016-04-08 12:55:30 -04:00
|
|
|
srel!(8.0, 0.0, 8.0, 8.0), // Up
|
2016-03-28 09:15:21 -04:00
|
|
|
srel!(8.0, 8.0, 8.0, 8.0), // North
|
|
|
|
srel!(24.0, 8.0, 8.0, 8.0), // South
|
|
|
|
srel!(16.0, 8.0, 8.0, 8.0), // West
|
|
|
|
srel!(0.0, 8.0, 8.0, 8.0), // East
|
|
|
|
]);
|
2016-04-16 16:36:37 -04:00
|
|
|
model::append_box(&mut head_verts, -4.2/16.0, -0.2/16.0, -4.2/16.0, 8.4/16.0, 8.4/16.0, 8.4/16.0, [
|
2016-03-28 09:15:21 -04:00
|
|
|
srel!((16.0 + 32.0), 0.0, 8.0, 8.0), // Down
|
2016-04-08 12:55:30 -04:00
|
|
|
srel!((8.0 + 32.0), 0.0, 8.0, 8.0), // Up
|
2016-03-28 09:15:21 -04:00
|
|
|
srel!((8.0 + 32.0), 8.0, 8.0, 8.0), // North
|
|
|
|
srel!((24.0 + 32.0), 8.0, 8.0, 8.0), // South
|
|
|
|
srel!((16.0 + 32.0), 8.0, 8.0, 8.0), // West
|
|
|
|
srel!((0.0 + 32.0), 8.0, 8.0, 8.0), // East
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Cape
|
|
|
|
let mut body_verts = vec![];
|
|
|
|
model::append_box(&mut body_verts, -4.0/16.0, -6.0/16.0, -2.0/16.0, 8.0/16.0, 12.0/16.0, 4.0/16.0, [
|
|
|
|
srel!(28.0, 16.0, 8.0, 4.0), // Down
|
2016-04-08 12:55:30 -04:00
|
|
|
srel!(20.0, 16.0, 8.0, 4.0), // Up
|
2016-03-28 09:15:21 -04:00
|
|
|
srel!(20.0, 20.0, 8.0, 12.0), // North
|
|
|
|
srel!(32.0, 20.0, 8.0, 12.0), // South
|
|
|
|
srel!(16.0, 20.0, 4.0, 12.0), // West
|
|
|
|
srel!(28.0, 20.0, 4.0, 12.0), // East
|
|
|
|
]);
|
|
|
|
model::append_box(&mut body_verts, -4.2/16.0, -6.2/16.0, -2.2/16.0, 8.4/16.0, 12.4/16.0, 4.4/16.0, [
|
|
|
|
srel!(28.0, 16.0 + 16.0, 8.0, 4.0), // Down
|
2016-04-08 12:55:30 -04:00
|
|
|
srel!(20.0, 16.0 + 16.0, 8.0, 4.0), // Up
|
2016-03-28 09:15:21 -04:00
|
|
|
srel!(20.0, 20.0 + 16.0, 8.0, 12.0), // North
|
|
|
|
srel!(32.0, 20.0 + 16.0, 8.0, 12.0), // South
|
|
|
|
srel!(16.0, 20.0 + 16.0, 4.0, 12.0), // West
|
|
|
|
srel!(28.0, 20.0 + 16.0, 4.0, 12.0), // East
|
|
|
|
]);
|
|
|
|
|
|
|
|
let mut part_verts = vec![vec![]; 4];
|
|
|
|
|
|
|
|
for (i, offsets) in [
|
2016-04-16 16:36:37 -04:00
|
|
|
[16.0, 48.0, 0.0, 48.0], // Left left
|
|
|
|
[0.0, 16.0, 0.0, 32.0], // Right Leg
|
|
|
|
[32.0, 48.0, 48.0, 48.0], // Left arm
|
|
|
|
[40.0, 16.0, 40.0, 32.0], // Right arm
|
2020-01-04 22:09:00 -05:00
|
|
|
].iter().enumerate() {
|
2016-03-28 09:15:21 -04:00
|
|
|
let (ox, oy) = (offsets[0], offsets[1]);
|
|
|
|
model::append_box(&mut part_verts[i], -2.0/16.0, -12.0/16.0, -2.0/16.0, 4.0/16.0, 12.0/16.0, 4.0/16.0, [
|
|
|
|
srel!(ox + 8.0, oy + 0.0, 4.0, 4.0), // Down
|
2016-04-08 12:55:30 -04:00
|
|
|
srel!(ox + 4.0, oy + 0.0, 4.0, 4.0), // Up
|
2016-03-28 09:15:21 -04:00
|
|
|
srel!(ox + 4.0, oy + 4.0, 4.0, 12.0), // North
|
|
|
|
srel!(ox + 12.0, oy + 4.0, 4.0, 12.0), // South
|
2016-04-16 16:36:37 -04:00
|
|
|
srel!(ox + 8.0, oy + 4.0, 4.0, 12.0), // West
|
|
|
|
srel!(ox + 0.0, oy + 4.0, 4.0, 12.0), // East
|
2016-03-28 09:15:21 -04:00
|
|
|
]);
|
|
|
|
let (ox, oy) = (offsets[2], offsets[3]);
|
|
|
|
model::append_box(&mut part_verts[i], -2.2/16.0, -12.2/16.0, -2.2/16.0, 4.4/16.0, 12.4/16.0, 4.4/16.0, [
|
|
|
|
srel!(ox + 8.0, oy + 0.0, 4.0, 4.0), // Down
|
2016-04-08 12:55:30 -04:00
|
|
|
srel!(ox + 4.0, oy + 0.0, 4.0, 4.0), // Up
|
2016-03-28 09:15:21 -04:00
|
|
|
srel!(ox + 4.0, oy + 4.0, 4.0, 12.0), // North
|
|
|
|
srel!(ox + 12.0, oy + 4.0, 4.0, 12.0), // South
|
2016-04-16 16:36:37 -04:00
|
|
|
srel!(ox + 8.0, oy + 4.0, 4.0, 12.0), // West
|
|
|
|
srel!(ox + 0.0, oy + 4.0, 4.0, 12.0), // East
|
2016-03-28 09:15:21 -04:00
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2016-04-07 16:30:20 -04:00
|
|
|
let mut name_verts = vec![];
|
|
|
|
if player_model.has_name_tag {
|
|
|
|
let mut state = FormatState {
|
|
|
|
width: 0.0,
|
|
|
|
offset: 0.0,
|
|
|
|
text: Vec::new(),
|
2018-11-04 16:43:30 -05:00
|
|
|
renderer,
|
2016-04-07 16:30:20 -04:00
|
|
|
y_scale: 0.16,
|
|
|
|
x_scale: 0.01,
|
|
|
|
};
|
|
|
|
let mut name = format::Component::Text(format::TextComponent::new(&player_model.name));
|
|
|
|
format::convert_legacy(&mut name);
|
|
|
|
state.build(&name, format::Color::Black);
|
|
|
|
let width = state.width;
|
|
|
|
// Center align text
|
|
|
|
for vert in &mut state.text {
|
|
|
|
vert.x += width * 0.5;
|
|
|
|
vert.r = 64;
|
|
|
|
vert.g = 64;
|
|
|
|
vert.b = 64;
|
|
|
|
}
|
|
|
|
name_verts.extend_from_slice(&state.text);
|
|
|
|
for vert in &mut state.text {
|
|
|
|
vert.x -= 0.01;
|
|
|
|
vert.y -= 0.01;
|
|
|
|
vert.z -= 0.05;
|
|
|
|
vert.r = 255;
|
|
|
|
vert.g = 255;
|
|
|
|
vert.b = 255;
|
|
|
|
}
|
|
|
|
name_verts.extend_from_slice(&state.text);
|
|
|
|
}
|
|
|
|
|
2016-03-28 09:15:21 -04:00
|
|
|
player_model.model = Some(renderer.model.create_model(
|
|
|
|
model::DEFAULT,
|
|
|
|
vec![
|
|
|
|
head_verts,
|
|
|
|
body_verts,
|
|
|
|
part_verts[0].clone(),
|
|
|
|
part_verts[1].clone(),
|
|
|
|
part_verts[2].clone(),
|
|
|
|
part_verts[3].clone(),
|
2016-04-07 16:30:20 -04:00
|
|
|
name_verts
|
2016-03-28 09:15:21 -04:00
|
|
|
]
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn entity_removed(&mut self, m: &mut ecs::Manager, e: ecs::Entity, _: &mut world::World, renderer: &mut render::Renderer) {
|
|
|
|
let player_model = m.get_component_mut(e, self.player_model).unwrap();
|
|
|
|
if let Some(model) = player_model.model.take() {
|
|
|
|
renderer.model.remove_model(model);
|
2016-04-07 11:33:08 -04:00
|
|
|
if let Some(url) = player_model.skin_url.as_ref() {
|
2016-09-15 10:15:52 -04:00
|
|
|
renderer.get_textures_ref().read().unwrap().release_skin(url);
|
2016-04-07 11:33:08 -04:00
|
|
|
}
|
2016-04-07 10:55:03 -04:00
|
|
|
}
|
2016-03-28 09:15:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-15 10:15:52 -04:00
|
|
|
#[derive(Default)]
|
2016-03-26 18:21:47 -04:00
|
|
|
pub struct PlayerMovement {
|
|
|
|
pub flying: bool,
|
2019-01-26 16:00:19 -05:00
|
|
|
pub want_to_fly: bool,
|
|
|
|
pub when_last_jump_pressed: Option<Instant>,
|
|
|
|
pub when_last_jump_released: Option<Instant>,
|
2016-03-26 18:21:47 -04:00
|
|
|
pub did_touch_ground: bool,
|
2016-04-09 04:56:55 -04:00
|
|
|
pub pressed_keys: HashMap<Stevenkey, bool, BuildHasherDefault<FNVHash>>,
|
2016-03-26 18:21:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PlayerMovement {
|
2016-09-15 10:15:52 -04:00
|
|
|
pub fn new() -> PlayerMovement { Default::default() }
|
2016-03-26 18:21:47 -04:00
|
|
|
|
|
|
|
fn calculate_movement(&self, player_yaw: f64) -> (f64, f64) {
|
|
|
|
use std::f64::consts::PI;
|
|
|
|
let mut forward = 0.0f64;
|
|
|
|
let mut yaw = player_yaw - (PI/2.0);
|
2016-04-09 04:56:55 -04:00
|
|
|
if self.is_key_pressed(Stevenkey::Forward) || self.is_key_pressed(Stevenkey::Backward) {
|
2016-03-26 18:21:47 -04:00
|
|
|
forward = 1.0;
|
2016-04-09 04:56:55 -04:00
|
|
|
if self.is_key_pressed(Stevenkey::Backward) {
|
2016-03-26 18:21:47 -04:00
|
|
|
yaw += PI;
|
|
|
|
}
|
|
|
|
}
|
2016-09-15 10:15:52 -04:00
|
|
|
let change = if self.is_key_pressed(Stevenkey::Left) {
|
|
|
|
(PI / 2.0) / (forward.abs() + 1.0)
|
|
|
|
} else if self.is_key_pressed(Stevenkey::Right) {
|
|
|
|
-(PI / 2.0) / (forward.abs() + 1.0)
|
|
|
|
} else { 0.0 };
|
2016-04-09 04:56:55 -04:00
|
|
|
if self.is_key_pressed(Stevenkey::Left) || self.is_key_pressed(Stevenkey::Right) {
|
2016-03-26 18:21:47 -04:00
|
|
|
forward = 1.0;
|
|
|
|
}
|
2016-04-09 04:56:55 -04:00
|
|
|
if self.is_key_pressed(Stevenkey::Backward) {
|
2016-03-26 18:21:47 -04:00
|
|
|
yaw -= change;
|
|
|
|
} else {
|
|
|
|
yaw += change;
|
|
|
|
}
|
|
|
|
|
|
|
|
(forward, yaw)
|
|
|
|
}
|
|
|
|
|
2016-04-09 04:56:55 -04:00
|
|
|
fn is_key_pressed(&self, key: Stevenkey) -> bool {
|
2016-03-26 18:21:47 -04:00
|
|
|
self.pressed_keys.get(&key).map_or(false, |v| *v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct MovementHandler {
|
|
|
|
filter: ecs::Filter,
|
|
|
|
movement: ecs::Key<PlayerMovement>,
|
|
|
|
gravity: ecs::Key<Gravity>,
|
|
|
|
gamemode: ecs::Key<Gamemode>,
|
2016-04-08 19:40:35 -04:00
|
|
|
position: ecs::Key<TargetPosition>,
|
2016-03-26 18:21:47 -04:00
|
|
|
velocity: ecs::Key<Velocity>,
|
|
|
|
bounds: ecs::Key<Bounds>,
|
|
|
|
rotation: ecs::Key<Rotation>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MovementHandler {
|
|
|
|
pub fn new(m: &mut ecs::Manager) -> MovementHandler {
|
|
|
|
let movement = m.get_key();
|
|
|
|
let position = m.get_key();
|
|
|
|
let velocity = m.get_key();
|
|
|
|
let bounds = m.get_key();
|
|
|
|
let rotation = m.get_key();
|
|
|
|
MovementHandler {
|
|
|
|
filter: ecs::Filter::new()
|
|
|
|
.with(movement)
|
|
|
|
.with(position)
|
|
|
|
.with(velocity)
|
|
|
|
.with(bounds)
|
|
|
|
.with(rotation),
|
2018-11-04 16:43:30 -05:00
|
|
|
movement,
|
2016-03-26 18:21:47 -04:00
|
|
|
gravity: m.get_key(),
|
|
|
|
gamemode: m.get_key(),
|
2018-11-04 16:43:30 -05:00
|
|
|
position,
|
|
|
|
velocity,
|
|
|
|
bounds,
|
|
|
|
rotation,
|
2016-03-26 18:21:47 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ecs::System for MovementHandler {
|
2016-03-27 08:27:31 -04:00
|
|
|
|
2016-03-27 09:25:34 -04:00
|
|
|
fn filter(&self) -> &ecs::Filter {
|
|
|
|
&self.filter
|
|
|
|
}
|
|
|
|
|
2016-03-27 12:45:12 -04:00
|
|
|
fn update(&mut self, m: &mut ecs::Manager, world: &mut world::World, _: &mut render::Renderer) {
|
2016-03-26 18:21:47 -04:00
|
|
|
for e in m.find(&self.filter) {
|
|
|
|
let movement = m.get_component_mut(e, self.movement).unwrap();
|
|
|
|
if movement.flying && m.get_component(e, self.gravity).is_some() {
|
|
|
|
m.remove_component(e, self.gravity);
|
|
|
|
} else if !movement.flying && m.get_component(e, self.gravity).is_none() {
|
|
|
|
m.add_component(e, self.gravity, Gravity::new());
|
|
|
|
}
|
|
|
|
let gamemode = m.get_component(e, self.gamemode).unwrap();
|
|
|
|
movement.flying |= gamemode.always_fly();
|
|
|
|
|
2019-01-26 16:00:19 -05:00
|
|
|
// Detect double-tapping jump to toggle creative flight
|
|
|
|
if movement.is_key_pressed(Stevenkey::Jump) {
|
|
|
|
if movement.when_last_jump_pressed.is_none() {
|
|
|
|
movement.when_last_jump_pressed = Some(Instant::now());
|
|
|
|
if !movement.when_last_jump_released.is_none() {
|
|
|
|
let dt = movement.when_last_jump_pressed.unwrap() - movement.when_last_jump_released.unwrap();
|
|
|
|
if dt.as_secs() == 0 && dt.subsec_millis() <= crate::settings::DOUBLE_JUMP_MS {
|
|
|
|
movement.want_to_fly = !movement.want_to_fly;
|
2019-05-22 19:01:14 -04:00
|
|
|
//info!("double jump! dt={:?} toggle want_to_fly = {}", dt, movement.want_to_fly);
|
2019-01-26 16:00:19 -05:00
|
|
|
|
|
|
|
if gamemode.can_fly() && !gamemode.always_fly() {
|
|
|
|
movement.flying = movement.want_to_fly;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if !movement.when_last_jump_pressed.is_none() {
|
|
|
|
movement.when_last_jump_released = Some(Instant::now());
|
|
|
|
movement.when_last_jump_pressed = None;
|
|
|
|
}
|
|
|
|
|
2016-03-26 18:21:47 -04:00
|
|
|
let position = m.get_component_mut(e, self.position).unwrap();
|
|
|
|
let rotation = m.get_component(e, self.rotation).unwrap();
|
|
|
|
let velocity = m.get_component_mut(e, self.velocity).unwrap();
|
|
|
|
let gravity = m.get_component_mut(e, self.gravity);
|
|
|
|
|
|
|
|
let player_bounds = m.get_component(e, self.bounds).unwrap().bounds;
|
|
|
|
|
2016-04-08 19:40:35 -04:00
|
|
|
let mut last_position = position.position;
|
2016-03-28 09:15:21 -04:00
|
|
|
|
2016-03-26 18:21:47 -04:00
|
|
|
if world.is_chunk_loaded((position.position.x as i32) >> 4, (position.position.z as i32) >> 4) {
|
|
|
|
let (forward, yaw) = movement.calculate_movement(rotation.yaw);
|
2016-09-15 10:15:52 -04:00
|
|
|
let mut speed = if movement.is_key_pressed(Stevenkey::Sprint) {
|
|
|
|
0.2806
|
|
|
|
} else { 0.21585 };
|
2016-03-26 18:21:47 -04:00
|
|
|
if movement.flying {
|
|
|
|
speed *= 2.5;
|
|
|
|
|
2016-04-09 04:56:55 -04:00
|
|
|
if movement.is_key_pressed(Stevenkey::Jump) {
|
2016-04-08 19:40:35 -04:00
|
|
|
position.position.y += speed;
|
2016-03-26 18:21:47 -04:00
|
|
|
}
|
2016-04-09 04:56:55 -04:00
|
|
|
if movement.is_key_pressed(Stevenkey::Sneak) {
|
2016-04-08 19:40:35 -04:00
|
|
|
position.position.y -= speed;
|
2016-03-26 18:21:47 -04:00
|
|
|
}
|
|
|
|
} else if gravity.as_ref().map_or(false, |v| v.on_ground) {
|
2016-04-09 04:56:55 -04:00
|
|
|
if movement.is_key_pressed(Stevenkey::Jump) && velocity.velocity.y.abs() < 0.001 {
|
2016-04-08 19:40:35 -04:00
|
|
|
velocity.velocity.y = 0.42;
|
2016-03-26 18:21:47 -04:00
|
|
|
}
|
|
|
|
} else {
|
2016-04-08 19:40:35 -04:00
|
|
|
velocity.velocity.y -= 0.08;
|
|
|
|
if velocity.velocity.y < -3.92 {
|
|
|
|
velocity.velocity.y = -3.92;
|
2016-03-26 18:21:47 -04:00
|
|
|
}
|
|
|
|
}
|
2016-04-08 19:40:35 -04:00
|
|
|
velocity.velocity.y *= 0.98;
|
|
|
|
position.position.x += forward * yaw.cos() * speed;
|
|
|
|
position.position.z -= forward * yaw.sin() * speed;
|
|
|
|
position.position.y += velocity.velocity.y;
|
2016-03-26 18:21:47 -04:00
|
|
|
|
2016-03-31 19:34:22 -04:00
|
|
|
if !gamemode.noclip() {
|
|
|
|
let mut target = position.position;
|
2016-04-08 19:40:35 -04:00
|
|
|
position.position.y = last_position.y;
|
|
|
|
position.position.z = last_position.z;
|
2016-03-31 19:34:22 -04:00
|
|
|
|
|
|
|
// We handle each axis separately to allow for a sliding
|
|
|
|
// effect when pushing up against walls.
|
|
|
|
|
2016-04-08 19:40:35 -04:00
|
|
|
let (bounds, xhit) = check_collisions(world, position, &last_position, player_bounds);
|
2016-03-31 19:34:22 -04:00
|
|
|
position.position.x = bounds.min.x + 0.3;
|
2016-04-08 19:40:35 -04:00
|
|
|
last_position.x = position.position.x;
|
2016-03-31 19:34:22 -04:00
|
|
|
|
2016-03-26 18:21:47 -04:00
|
|
|
position.position.z = target.z;
|
2016-04-08 19:40:35 -04:00
|
|
|
let (bounds, zhit) = check_collisions(world, position, &last_position, player_bounds);
|
2016-03-31 19:34:22 -04:00
|
|
|
position.position.z = bounds.min.z + 0.3;
|
2016-04-08 19:40:35 -04:00
|
|
|
last_position.z = position.position.z;
|
2016-03-31 19:34:22 -04:00
|
|
|
|
|
|
|
// Half block jumps
|
|
|
|
// Minecraft lets you 'jump' up 0.5 blocks
|
|
|
|
// for slabs and stairs (or smaller blocks).
|
|
|
|
// Currently we implement this as a teleport to the
|
|
|
|
// top of the block if we could move there
|
|
|
|
// but this isn't smooth.
|
|
|
|
if (xhit || zhit) && gravity.as_ref().map_or(false, |v| v.on_ground) {
|
|
|
|
let mut ox = position.position.x;
|
|
|
|
let mut oz = position.position.z;
|
|
|
|
position.position.x = target.x;
|
|
|
|
position.position.z = target.z;
|
|
|
|
for offset in 1 .. 9 {
|
|
|
|
let mini = player_bounds.add_v(cgmath::Vector3::new(0.0, offset as f64 / 16.0, 0.0));
|
2016-04-08 19:40:35 -04:00
|
|
|
let (_, hit) = check_collisions(world, position, &last_position, mini);
|
2016-03-31 19:34:22 -04:00
|
|
|
if !hit {
|
|
|
|
target.y += offset as f64 / 16.0;
|
|
|
|
ox = target.x;
|
|
|
|
oz = target.z;
|
|
|
|
break;
|
|
|
|
}
|
2016-03-26 18:21:47 -04:00
|
|
|
}
|
2016-03-31 19:34:22 -04:00
|
|
|
position.position.x = ox;
|
|
|
|
position.position.z = oz;
|
2016-03-26 18:21:47 -04:00
|
|
|
}
|
|
|
|
|
2016-03-31 19:34:22 -04:00
|
|
|
position.position.y = target.y;
|
2016-04-08 19:40:35 -04:00
|
|
|
let (bounds, yhit) = check_collisions(world, position, &last_position, player_bounds);
|
2016-03-31 19:34:22 -04:00
|
|
|
position.position.y = bounds.min.y;
|
2016-04-08 19:40:35 -04:00
|
|
|
last_position.y = position.position.y;
|
2016-03-31 19:34:22 -04:00
|
|
|
if yhit {
|
|
|
|
velocity.velocity.y = 0.0;
|
|
|
|
}
|
2016-03-26 18:21:47 -04:00
|
|
|
|
2016-03-31 19:34:22 -04:00
|
|
|
if let Some(gravity) = gravity {
|
|
|
|
let ground = Aabb3::new(
|
2016-04-08 19:40:35 -04:00
|
|
|
Point3::new(-0.3, -0.005, -0.3),
|
2016-03-31 19:34:22 -04:00
|
|
|
Point3::new(0.3, 0.0, 0.3)
|
|
|
|
);
|
|
|
|
let prev = gravity.on_ground;
|
2016-04-08 19:40:35 -04:00
|
|
|
let (_, hit) = check_collisions(world, position, &last_position, ground);
|
2016-03-31 19:34:22 -04:00
|
|
|
gravity.on_ground = hit;
|
|
|
|
if !prev && gravity.on_ground {
|
|
|
|
movement.did_touch_ground = true;
|
|
|
|
}
|
2016-03-26 18:21:47 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-08 19:40:35 -04:00
|
|
|
fn check_collisions(world: &world::World, position: &mut TargetPosition, last_position: &Vector3<f64>, bounds: Aabb3<f64>) -> (Aabb3<f64>, bool) {
|
2016-03-26 18:21:47 -04:00
|
|
|
let mut bounds = bounds.add_v(position.position);
|
|
|
|
|
2016-04-08 19:40:35 -04:00
|
|
|
let dir = position.position - last_position;
|
2016-03-26 18:21:47 -04:00
|
|
|
|
|
|
|
let min_x = (bounds.min.x - 1.0) as i32;
|
|
|
|
let min_y = (bounds.min.y - 1.0) as i32;
|
|
|
|
let min_z = (bounds.min.z - 1.0) as i32;
|
|
|
|
let max_x = (bounds.max.x + 1.0) as i32;
|
|
|
|
let max_y = (bounds.max.y + 1.0) as i32;
|
|
|
|
let max_z = (bounds.max.z + 1.0) as i32;
|
|
|
|
|
|
|
|
let mut hit = false;
|
|
|
|
for y in min_y .. max_y {
|
|
|
|
for z in min_z .. max_z {
|
|
|
|
for x in min_x .. max_x {
|
2016-04-03 15:53:40 -04:00
|
|
|
let block = world.get_block(BPosition::new(x, y, z));
|
2016-04-21 16:47:39 -04:00
|
|
|
if block.get_material().collidable {
|
|
|
|
for bb in block.get_collision_boxes() {
|
|
|
|
let bb = bb.add_v(cgmath::Vector3::new(x as f64, y as f64, z as f64));
|
|
|
|
if bb.collides(&bounds) {
|
|
|
|
bounds = bounds.move_out_of(bb, dir);
|
|
|
|
hit = true;
|
|
|
|
}
|
2016-03-26 18:21:47 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(bounds, hit)
|
|
|
|
}
|
|
|
|
|
|
|
|
trait Collidable<T> {
|
|
|
|
fn collides(&self, t: &T) -> bool;
|
|
|
|
fn move_out_of(self, other: Self, dir: cgmath::Vector3<f64>) -> Self;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Collidable<Aabb3<f64>> for Aabb3<f64> {
|
|
|
|
fn collides(&self, t: &Aabb3<f64>) -> bool {
|
|
|
|
!(
|
|
|
|
t.min.x >= self.max.x ||
|
|
|
|
t.max.x <= self.min.x ||
|
|
|
|
t.min.y >= self.max.y ||
|
|
|
|
t.max.y <= self.min.y ||
|
|
|
|
t.min.z >= self.max.z ||
|
|
|
|
t.max.z <= self.min.z
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn move_out_of(mut self, other: Self, dir: cgmath::Vector3<f64>) -> Self {
|
|
|
|
if dir.x != 0.0 {
|
|
|
|
if dir.x > 0.0 {
|
|
|
|
let ox = self.max.x;
|
|
|
|
self.max.x = other.min.x - 0.0001;
|
|
|
|
self.min.x += self.max.x - ox;
|
|
|
|
} else {
|
|
|
|
let ox = self.min.x;
|
|
|
|
self.min.x = other.max.x + 0.0001;
|
|
|
|
self.max.x += self.min.x - ox;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if dir.y != 0.0 {
|
|
|
|
if dir.y > 0.0 {
|
|
|
|
let oy = self.max.y;
|
|
|
|
self.max.y = other.min.y - 0.0001;
|
|
|
|
self.min.y += self.max.y - oy;
|
|
|
|
} else {
|
|
|
|
let oy = self.min.y;
|
|
|
|
self.min.y = other.max.y + 0.0001;
|
|
|
|
self.max.y += self.min.y - oy;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if dir.z != 0.0 {
|
|
|
|
if dir.z > 0.0 {
|
|
|
|
let oz = self.max.z;
|
|
|
|
self.max.z = other.min.z - 0.0001;
|
|
|
|
self.min.z += self.max.z - oz;
|
|
|
|
} else {
|
|
|
|
let oz = self.min.z;
|
|
|
|
self.min.z = other.max.z + 0.0001;
|
|
|
|
self.max.z += self.min.z - oz;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|