230 lines
7.1 KiB
Rust
230 lines
7.1 KiB
Rust
use crate::ecs;
|
|
use crate::format::{self, Component};
|
|
use crate::render;
|
|
use crate::render::model::{self, FormatState};
|
|
use crate::shared::{Direction, Position};
|
|
use crate::world;
|
|
use crate::world::block::Block;
|
|
|
|
pub fn add_systems(m: &mut ecs::Manager) {
|
|
let sys = SignRenderer::new(m);
|
|
m.add_render_system(sys);
|
|
}
|
|
|
|
pub fn init_entity(m: &mut ecs::Manager, e: ecs::Entity) {
|
|
m.add_component_direct(
|
|
e,
|
|
SignInfo {
|
|
model: None,
|
|
lines: [
|
|
Component::Text(format::TextComponent::new("")),
|
|
Component::Text(format::TextComponent::new("")),
|
|
Component::Text(format::TextComponent::new("")),
|
|
Component::Text(format::TextComponent::new("")),
|
|
],
|
|
offset_x: 0.0,
|
|
offset_y: 0.0,
|
|
offset_z: 0.0,
|
|
has_stand: false,
|
|
rotation: 0.0,
|
|
dirty: false,
|
|
},
|
|
);
|
|
}
|
|
|
|
pub struct SignInfo {
|
|
model: Option<model::ModelKey>,
|
|
|
|
pub lines: [format::Component; 4],
|
|
pub dirty: bool,
|
|
|
|
offset_x: f64,
|
|
offset_y: f64,
|
|
offset_z: f64,
|
|
has_stand: bool,
|
|
rotation: f64,
|
|
}
|
|
|
|
struct SignRenderer {
|
|
filter: ecs::Filter,
|
|
position: ecs::Key<Position>,
|
|
sign_info: ecs::Key<SignInfo>,
|
|
}
|
|
|
|
impl SignRenderer {
|
|
fn new(m: &mut ecs::Manager) -> SignRenderer {
|
|
let sign_info = m.get_key();
|
|
let position = m.get_key();
|
|
SignRenderer {
|
|
filter: ecs::Filter::new().with(position).with(sign_info),
|
|
position,
|
|
sign_info,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ecs::System for SignRenderer {
|
|
fn filter(&self) -> &ecs::Filter {
|
|
&self.filter
|
|
}
|
|
|
|
fn update(
|
|
&mut self,
|
|
m: &mut ecs::Manager,
|
|
world: &mut world::World,
|
|
renderer: &mut render::Renderer,
|
|
) {
|
|
for e in m.find(&self.filter) {
|
|
let position = *m.get_component(e, self.position).unwrap();
|
|
let info = m.get_component_mut(e, self.sign_info).unwrap();
|
|
if info.dirty {
|
|
self.entity_removed(m, e, world, renderer);
|
|
self.entity_added(m, e, world, renderer);
|
|
}
|
|
if let Some(model) = info.model {
|
|
let mdl = renderer.model.get_model(model).unwrap();
|
|
mdl.block_light = world.get_block_light(position) as f32;
|
|
mdl.sky_light = world.get_sky_light(position) as f32;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn entity_added(
|
|
&mut self,
|
|
m: &mut ecs::Manager,
|
|
e: ecs::Entity,
|
|
world: &mut world::World,
|
|
renderer: &mut render::Renderer,
|
|
) {
|
|
use cgmath::{Decomposed, Matrix4, Quaternion, Rad, Rotation3, Vector3};
|
|
use std::f64::consts::PI;
|
|
let position = *m.get_component(e, self.position).unwrap();
|
|
let info = m.get_component_mut(e, self.sign_info).unwrap();
|
|
info.dirty = false;
|
|
match world.get_block(position) {
|
|
Block::WallSign { facing, .. } => {
|
|
info.offset_z = 7.5 / 16.0;
|
|
match facing {
|
|
Direction::North => {}
|
|
Direction::South => info.rotation = PI,
|
|
Direction::West => info.rotation = PI / 2.0,
|
|
Direction::East => info.rotation = -PI / 2.0,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
Block::StandingSign { rotation, .. } => {
|
|
info.offset_y = 5.0 / 16.0;
|
|
info.has_stand = true;
|
|
info.rotation = -(rotation.data() as f64 / 16.0) * PI * 2.0 + PI;
|
|
}
|
|
_ => return,
|
|
}
|
|
let tex = render::Renderer::get_texture(renderer.get_textures_ref(), "entity/sign");
|
|
|
|
macro_rules! rel {
|
|
($x:expr, $y:expr, $w:expr, $h:expr) => {
|
|
Some(tex.relative(($x) / 64.0, ($y) / 32.0, ($w) / 64.0, ($h) / 32.0))
|
|
};
|
|
}
|
|
|
|
let mut verts = vec![];
|
|
// Backboard
|
|
model::append_box(
|
|
&mut verts,
|
|
-0.5,
|
|
-4.0 / 16.0,
|
|
-0.5 / 16.0,
|
|
1.0,
|
|
8.0 / 16.0,
|
|
1.0 / 16.0,
|
|
[
|
|
rel!(26.0, 0.0, 24.0, 2.0), // Down
|
|
rel!(2.0, 0.0, 24.0, 2.0), // Up
|
|
rel!(2.0, 2.0, 24.0, 12.0), // North
|
|
rel!(26.0, 2.0, 24.0, 12.0), // South
|
|
rel!(0.0, 2.0, 2.0, 12.0), // West
|
|
rel!(50.0, 2.0, 2.0, 12.0), // East
|
|
],
|
|
);
|
|
if info.has_stand {
|
|
model::append_box(
|
|
&mut verts,
|
|
-0.5 / 16.0,
|
|
-0.25 - 9.0 / 16.0,
|
|
-0.5 / 16.0,
|
|
1.0 / 16.0,
|
|
9.0 / 16.0,
|
|
1.0 / 16.0,
|
|
[
|
|
rel!(4.0, 14.0, 2.0, 2.0), // Down
|
|
rel!(2.0, 14.0, 2.0, 2.0), // Up
|
|
rel!(2.0, 16.0, 2.0, 12.0), // North
|
|
rel!(6.0, 16.0, 2.0, 12.0), // South
|
|
rel!(0.0, 16.0, 2.0, 12.0), // West
|
|
rel!(4.0, 16.0, 2.0, 12.0), // East
|
|
],
|
|
);
|
|
}
|
|
|
|
for (i, line) in info.lines.iter().enumerate() {
|
|
const Y_SCALE: f32 = (6.0 / 16.0) / 4.0;
|
|
const X_SCALE: f32 = Y_SCALE / 16.0;
|
|
let mut state = FormatState {
|
|
width: 0.0,
|
|
offset: 0.0,
|
|
text: Vec::new(),
|
|
renderer,
|
|
y_scale: Y_SCALE,
|
|
x_scale: X_SCALE,
|
|
};
|
|
state.build(line, format::Color::Black);
|
|
let width = state.width;
|
|
// Center align text
|
|
for vert in &mut state.text {
|
|
vert.x += width * 0.5;
|
|
vert.y -= (Y_SCALE + 0.4 / 16.0) * (i as f32);
|
|
}
|
|
verts.extend_from_slice(&state.text);
|
|
}
|
|
|
|
let model = renderer.model.create_model(model::DEFAULT, vec![verts]);
|
|
|
|
{
|
|
let mdl = renderer.model.get_model(model).unwrap();
|
|
mdl.radius = 2.0;
|
|
mdl.x = position.x as f32 + 0.5;
|
|
mdl.y = position.y as f32 + 0.5;
|
|
mdl.z = position.z as f32 + 0.5;
|
|
mdl.matrix[0] = Matrix4::from(Decomposed {
|
|
scale: 1.0,
|
|
rot: Quaternion::from_angle_y(Rad(info.rotation as f32)),
|
|
disp: Vector3::new(
|
|
position.x as f32 + 0.5,
|
|
-position.y as f32 - 0.5,
|
|
position.z as f32 + 0.5,
|
|
),
|
|
}) * Matrix4::from_translation(Vector3::new(
|
|
info.offset_x as f32,
|
|
-info.offset_y as f32,
|
|
info.offset_z as f32,
|
|
));
|
|
}
|
|
|
|
info.model = Some(model);
|
|
}
|
|
|
|
fn entity_removed(
|
|
&mut self,
|
|
m: &mut ecs::Manager,
|
|
e: ecs::Entity,
|
|
_: &mut world::World,
|
|
renderer: &mut render::Renderer,
|
|
) {
|
|
let info = m.get_component_mut(e, self.sign_info).unwrap();
|
|
if let Some(model) = info.model {
|
|
renderer.model.remove_model(model);
|
|
}
|
|
info.model = None;
|
|
}
|
|
}
|