mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'master' of https://gitlab.com/veloren/veloren
Former-commit-id: 9306d50f9ba615dd6ca091753ff26688767b6291
This commit is contained in:
commit
e8c91f753e
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,3 +19,4 @@ voxygen/keybinds.toml
|
||||
settings.toml
|
||||
*.rar
|
||||
*.log
|
||||
run.sh
|
||||
|
@ -28,6 +28,9 @@ before_script:
|
||||
- if [ ! -z "${SOURCE_PROJECT}" -a "${SOURCE_PROJECT}" != " " ]; then
|
||||
echo "THIS SEEMS TO BE A MERGE PIPELINE FROM ${SOURCE_PROJECT}/${SOURCE_BRANCH}";
|
||||
git pull "https://gitlab.com/${SOURCE_PROJECT}/veloren.git" "${SOURCE_BRANCH}";
|
||||
git lfs install;
|
||||
git lfs fetch;
|
||||
git lfs checkout;
|
||||
fi;
|
||||
- git status
|
||||
- if [ -d target ]; then
|
||||
|
@ -10,6 +10,10 @@ members = [
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 2
|
||||
overflow-checks = false
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
|
BIN
assets/voxygen/element/buttons/grid.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/buttons/grid.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/buttons/x.vox
(Stored with Git LFS)
BIN
assets/voxygen/element/buttons/x.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/divider_charwindow.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/frames/divider_charwindow.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/frames/tab_bg.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/frames/tab_bg.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/frames/tab_small_closed.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/frames/tab_small_closed.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/frames/tab_small_open.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/frames/tab_small_open.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/frames/window2.vox
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/window2.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/window_3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/frames/window_3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/frames/xp_charwindow.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/frames/xp_charwindow.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/back.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/back.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/belt.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/belt.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/chest.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/chest.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/feet.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/feet.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/gem.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/gem.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/hands.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/hands.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/head.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/head.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/legs.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/legs.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/mainhand.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/mainhand.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/necklace.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/necklace.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/offhand.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/offhand.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/ring.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/ring.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/shoulders.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/shoulders.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/tabard.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/tabard.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/misc_bg/charwindow.png
(Stored with Git LFS)
BIN
assets/voxygen/element/misc_bg/charwindow.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/misc_bg/crosshair.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/misc_bg/crosshair.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/Wood Training 2h.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/Wood Training 2h.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sword.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/sword.vox
(Stored with Git LFS)
Binary file not shown.
@ -1,16 +1,20 @@
|
||||
use specs::{Component, VecStorage};
|
||||
use specs::{Component, Entity as EcsEntity, VecStorage};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Agent {
|
||||
Wanderer(Vec2<f32>),
|
||||
Pet {
|
||||
target: EcsEntity,
|
||||
offset: Vec2<f32>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Component for Agent {
|
||||
type Storage = VecStorage<Self>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Control {
|
||||
pub move_dir: Vec2<f32>,
|
||||
pub jumping: bool,
|
||||
|
@ -140,6 +140,16 @@ pub struct AnimationHistory {
|
||||
pub time: f64,
|
||||
}
|
||||
|
||||
impl AnimationHistory {
|
||||
pub fn new(animation: Animation) -> Self {
|
||||
Self {
|
||||
last: None,
|
||||
current: animation,
|
||||
time: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Animation {
|
||||
Idle,
|
||||
|
@ -226,7 +226,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
for _ in 0..100 {
|
||||
match outgoing_chunks.pop_front() {
|
||||
Some(mut chunk) => match stream.write(&chunk) {
|
||||
Ok(n) => if n == chunk.len() {},
|
||||
Ok(n) if n == chunk.len() => {}
|
||||
Ok(n) => {
|
||||
outgoing_chunks.push_front(chunk.split_off(n));
|
||||
break;
|
||||
|
@ -9,6 +9,7 @@ pub struct Ray<'a, V: ReadVol, F: RayUntil<V::Vox>> {
|
||||
to: Vec3<f32>,
|
||||
until: F,
|
||||
max_iter: usize,
|
||||
ignore_error: bool,
|
||||
}
|
||||
|
||||
impl<'a, V: ReadVol, F: RayUntil<V::Vox>> Ray<'a, V, F> {
|
||||
@ -19,6 +20,7 @@ impl<'a, V: ReadVol, F: RayUntil<V::Vox>> Ray<'a, V, F> {
|
||||
to,
|
||||
until,
|
||||
max_iter: 100,
|
||||
ignore_error: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +33,11 @@ impl<'a, V: ReadVol, F: RayUntil<V::Vox>> Ray<'a, V, F> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ignore_error(mut self) -> Self {
|
||||
self.ignore_error = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn cast(mut self) -> (f32, Result<Option<&'a V::Vox>, V::Err>) {
|
||||
// TODO: Fully test this!
|
||||
|
||||
@ -47,17 +54,17 @@ impl<'a, V: ReadVol, F: RayUntil<V::Vox>> Ray<'a, V, F> {
|
||||
pos = self.from + dir * dist;
|
||||
ipos = pos.map(|e| e.floor() as i32);
|
||||
|
||||
match self.vol.get(ipos).map(|vox| (vox, (self.until)(vox))) {
|
||||
Ok((vox, true)) => return (dist, Ok(Some(vox))),
|
||||
Ok((_, false)) => {}
|
||||
Err(err) => return (dist, Err(err)),
|
||||
}
|
||||
|
||||
// Allow one iteration above max
|
||||
if dist > max {
|
||||
break;
|
||||
}
|
||||
|
||||
match self.vol.get(ipos).map(|vox| (vox, (self.until)(vox))) {
|
||||
Ok((vox, true)) => return (dist, Ok(Some(vox))),
|
||||
Err(err) if !self.ignore_error => return (dist, Err(err)),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let deltas =
|
||||
(dir.map(|e| if e < 0.0 { 0.0 } else { 1.0 }) - pos.map(|e| e.abs().fract())) / dir;
|
||||
|
||||
|
@ -36,7 +36,7 @@ pub struct DeltaTime(pub f32);
|
||||
/// too fast, we'd skip important physics events like collisions. This constant determines what
|
||||
/// the upper limit is. If delta time exceeds this value, the game's physics will begin to produce
|
||||
/// time lag. Ideally, we'd avoid such a situation.
|
||||
const MAX_DELTA_TIME: f32 = 0.2;
|
||||
const MAX_DELTA_TIME: f32 = 0.05;
|
||||
|
||||
pub struct Changes {
|
||||
pub new_chunks: HashSet<Vec3<i32>>,
|
||||
|
@ -15,14 +15,12 @@ impl<'a> System<'a> for Sys {
|
||||
WriteStorage<'a, Control>,
|
||||
);
|
||||
|
||||
fn run(&mut self, (mut agents, pos, mut controls): Self::SystemData) {
|
||||
for (mut agent, pos, mut control) in (&mut agents, &pos, &mut controls).join() {
|
||||
fn run(&mut self, (mut agents, positions, mut controls): Self::SystemData) {
|
||||
for (mut agent, pos, mut control) in (&mut agents, &positions, &mut controls).join() {
|
||||
match agent {
|
||||
Agent::Wanderer(bearing) => {
|
||||
*bearing += Vec2::new(
|
||||
rand::random::<f32>().fract() - 0.5,
|
||||
rand::random::<f32>().fract() - 0.5,
|
||||
) * 0.1
|
||||
*bearing += Vec2::new(rand::random::<f32>() - 0.5, rand::random::<f32>() - 0.5)
|
||||
* 0.1
|
||||
- *bearing * 0.01
|
||||
- pos.0 * 0.0002;
|
||||
|
||||
@ -30,6 +28,35 @@ impl<'a> System<'a> for Sys {
|
||||
control.move_dir = bearing.normalized();
|
||||
}
|
||||
}
|
||||
Agent::Pet { target, offset } => {
|
||||
// Run towards target
|
||||
match positions.get(*target) {
|
||||
Some(tgt_pos) => {
|
||||
let tgt_pos = tgt_pos.0 + *offset;
|
||||
|
||||
// Jump with target
|
||||
control.jumping = tgt_pos.z > pos.0.z + 1.0;
|
||||
|
||||
// Move towards the target
|
||||
let dist = tgt_pos.distance(pos.0);
|
||||
control.move_dir = if dist > 5.0 {
|
||||
Vec2::from(tgt_pos - pos.0).normalized()
|
||||
} else if dist < 1.5 && pos.0 != tgt_pos {
|
||||
Vec2::from(pos.0 - tgt_pos).normalized()
|
||||
} else {
|
||||
Vec2::zero()
|
||||
};
|
||||
}
|
||||
_ => control.move_dir = Vec2::zero(),
|
||||
}
|
||||
|
||||
// Change offset occasionally
|
||||
if rand::random::<f32>() < 0.003 {
|
||||
*offset =
|
||||
Vec2::new(rand::random::<f32>() - 0.5, rand::random::<f32>() - 0.5)
|
||||
* 10.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,13 @@ impl<'a> System<'a> for Sys {
|
||||
// Movement
|
||||
pos.0 += vel.0 * dt.0;
|
||||
|
||||
// Don't fall into the void
|
||||
// TODO: This shouldn't be needed when we have proper physics and chunk loading
|
||||
if pos.0.z < 0.0 {
|
||||
pos.0.z = 0.0;
|
||||
vel.0.z = 0.0;
|
||||
}
|
||||
|
||||
// Basic collision with terrain
|
||||
let mut i = 0;
|
||||
while terrain
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use crate::Server;
|
||||
use common::{comp, msg::ServerMsg};
|
||||
use specs::{join::Join, Entity as EcsEntity};
|
||||
use specs::{Builder, Entity as EcsEntity, Join};
|
||||
use vek::*;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
@ -72,9 +72,15 @@ lazy_static! {
|
||||
ChatCommand::new(
|
||||
"tp",
|
||||
"{}",
|
||||
"/tp <alias>: Teleport to another player",
|
||||
"/tp <alias> : Teleport to another player",
|
||||
handle_tp
|
||||
),
|
||||
ChatCommand::new(
|
||||
"pet",
|
||||
"{}",
|
||||
"/pet : Spawn a test pet NPC",
|
||||
handle_pet
|
||||
),
|
||||
ChatCommand::new("help", "", "/help: Display this message", handle_help)
|
||||
];
|
||||
}
|
||||
@ -179,6 +185,35 @@ fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &Chat
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_pet(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||
match server
|
||||
.state
|
||||
.read_component_cloned::<comp::phys::Pos>(entity)
|
||||
{
|
||||
Some(pos) => {
|
||||
let mut current = entity;
|
||||
|
||||
for _ in 0..1 {
|
||||
current = server
|
||||
.create_npc(comp::Character::random())
|
||||
.with(comp::Control::default())
|
||||
.with(comp::Agent::Pet {
|
||||
target: current,
|
||||
offset: Vec2::zero(),
|
||||
})
|
||||
.with(pos)
|
||||
.build();
|
||||
}
|
||||
server
|
||||
.clients
|
||||
.notify(entity, ServerMsg::Chat("Pet spawned!".to_owned()));
|
||||
}
|
||||
None => server
|
||||
.clients
|
||||
.notify(entity, ServerMsg::Chat("You have no position!".to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) {
|
||||
for cmd in CHAT_COMMANDS.iter() {
|
||||
server
|
||||
|
@ -82,8 +82,8 @@ impl Server {
|
||||
|
||||
for i in 0..4 {
|
||||
this.create_npc(comp::Character::random())
|
||||
.with(comp::Agent::Wanderer(Vec2::zero()))
|
||||
.with(comp::Control::default())
|
||||
.with(comp::Agent::Wanderer(Vec2::zero()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -121,6 +121,7 @@ impl Server {
|
||||
.with(comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0)))
|
||||
.with(comp::phys::Vel(Vec3::zero()))
|
||||
.with(comp::phys::Dir(Vec3::unit_y()))
|
||||
.with(comp::AnimationHistory::new(Animation::Idle))
|
||||
.with(character)
|
||||
}
|
||||
|
||||
@ -138,14 +139,7 @@ impl Server {
|
||||
state.write_component(entity, comp::phys::ForceUpdate);
|
||||
|
||||
// Set initial animation
|
||||
state.write_component(
|
||||
entity,
|
||||
comp::AnimationHistory {
|
||||
last: None,
|
||||
current: Animation::Idle,
|
||||
time: 0.0,
|
||||
},
|
||||
);
|
||||
state.write_component(entity, comp::AnimationHistory::new(Animation::Idle));
|
||||
|
||||
// Tell the client his request was successful
|
||||
client.notify(ServerMsg::StateAnswer(Ok(ClientState::Character)));
|
||||
@ -230,7 +224,9 @@ impl Server {
|
||||
.join()
|
||||
{
|
||||
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
|
||||
let dist = (chunk_pos - key).map(|e| e.abs()).reduce_max();
|
||||
let dist = Vec2::from(chunk_pos - key)
|
||||
.map(|e: i32| e.abs())
|
||||
.reduce_max();
|
||||
min_dist = min_dist.min(dist);
|
||||
}
|
||||
|
||||
@ -272,7 +268,7 @@ impl Server {
|
||||
// (All components Sphynx tracks)
|
||||
client.notify(ServerMsg::InitialSync {
|
||||
ecs_state: self.state.ecs().gen_state_package(),
|
||||
entity_uid: self.state.ecs().uid_from_entity(entity).unwrap().into(),
|
||||
entity_uid: self.state.ecs().uid_from_entity(entity).unwrap().into(), // Can't fail
|
||||
});
|
||||
|
||||
self.clients.add(entity, client);
|
||||
|
@ -14,7 +14,7 @@ void main() {
|
||||
tgt_color = f_color * vec4(1.0, 1.0, 1.0, texture(u_tex, f_uv).a);
|
||||
// Image
|
||||
} else if (f_mode == uint(1)) {
|
||||
tgt_color = texture(u_tex, f_uv);
|
||||
tgt_color = f_color * texture(u_tex, f_uv);
|
||||
// 2D Geometry
|
||||
} else if (f_mode == uint(2)) {
|
||||
tgt_color = f_color;
|
||||
|
@ -32,12 +32,12 @@ impl Animation for IdleAnimation {
|
||||
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
||||
|
||||
let head_look = Vec2::new(
|
||||
((global_time + anim_time) as f32 / 5.0)
|
||||
((global_time + anim_time) as f32 / 8.0)
|
||||
.floor()
|
||||
.mul(7331.0)
|
||||
.sin()
|
||||
* 0.5,
|
||||
((global_time + anim_time) as f32 / 5.0)
|
||||
((global_time + anim_time) as f32 / 8.0)
|
||||
.floor()
|
||||
.mul(1337.0)
|
||||
.sin()
|
||||
|
@ -2,18 +2,18 @@ use super::{img_ids::Imgs, Fonts, TEXT_COLOR, XP_COLOR};
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Image, Rectangle, Text},
|
||||
widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
widget_ids! {
|
||||
pub struct Ids {
|
||||
charwindow,
|
||||
charwindow_bg,
|
||||
charwindow_gradient,
|
||||
charwindow_close,
|
||||
charwindow_exp_progress_rectangle,
|
||||
charwindow_exp_rectangle,
|
||||
charwindow_frame,
|
||||
charwindow_icon,
|
||||
content_align,
|
||||
charwindow_rectangle,
|
||||
charwindow_tab1,
|
||||
charwindow_tab1_exp,
|
||||
@ -23,6 +23,45 @@ widget_ids! {
|
||||
charwindow_tab1_stats,
|
||||
charwindow_tab_bg,
|
||||
charwindow_title,
|
||||
window_3,
|
||||
tab_bg,
|
||||
tab_small_open,
|
||||
tab_small_closed,
|
||||
xp_charwindow,
|
||||
divider,
|
||||
head_bg,
|
||||
shoulders_bg,
|
||||
hands_bg,
|
||||
belt_bg,
|
||||
legs_bg,
|
||||
feet_bg,
|
||||
ring_r_bg,
|
||||
ring_l_bg,
|
||||
tabard_bg,
|
||||
chest_bg,
|
||||
back_bg,
|
||||
gem_bg,
|
||||
necklace_bg,
|
||||
mainhand_bg,
|
||||
offhand_bg,
|
||||
charwindow_bg,
|
||||
head_grid,
|
||||
shoulders_grid,
|
||||
hands_grid,
|
||||
belt_grid,
|
||||
legs_grid,
|
||||
feet_grid,
|
||||
ring_r_grid,
|
||||
ring_l_grid,
|
||||
tabard_grid,
|
||||
chest_grid,
|
||||
back_grid,
|
||||
gem_grid,
|
||||
necklace_grid,
|
||||
mainhand_grid,
|
||||
offhand_grid,
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,24 +108,24 @@ impl<'a> Widget for CharacterWindow<'a> {
|
||||
let xp_percentage = 0.4;
|
||||
|
||||
// Frame
|
||||
Image::new(self.imgs.window_frame)
|
||||
Image::new(self.imgs.window_3)
|
||||
.middle_of(id)
|
||||
.top_left_with_margins_on(ui.window, 200.0, 215.0)
|
||||
.w_h(107.0 * 4.0, 125.0 * 4.0)
|
||||
.top_left_with_margins_on(ui.window, 212.0, 215.0)
|
||||
.w_h(103.0 * 4.0, 122.0 * 4.0)
|
||||
.set(state.charwindow_frame, ui);
|
||||
|
||||
// Icon
|
||||
Image::new(self.imgs.charwindow_icon)
|
||||
.w_h(40.0, 40.0)
|
||||
.top_left_with_margins_on(state.charwindow_frame, 4.0, 4.0)
|
||||
.set(state.charwindow_icon, ui);
|
||||
//Image::new(self.imgs.charwindow_icon)
|
||||
//.w_h(40.0, 40.0)
|
||||
//.top_left_with_margins_on(state.charwindow_frame, 4.0, 4.0)
|
||||
//.set(state.charwindow_icon, ui);
|
||||
|
||||
// X-Button
|
||||
if Button::image(self.imgs.close_button)
|
||||
.w_h(28.0, 28.0)
|
||||
.hover_image(self.imgs.close_button_hover)
|
||||
.press_image(self.imgs.close_button_press)
|
||||
.top_right_with_margins_on(state.charwindow_frame, 12.0, 0.0)
|
||||
.top_right_with_margins_on(state.charwindow_frame, 0.0, 0.0)
|
||||
.set(state.charwindow_close, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
@ -95,31 +134,199 @@ impl<'a> Widget for CharacterWindow<'a> {
|
||||
|
||||
// Title
|
||||
Text::new("Character Name") // Add in actual Character Name
|
||||
.mid_top_with_margin_on(state.charwindow_frame, 17.0)
|
||||
.mid_top_with_margin_on(state.charwindow_frame, 6.0)
|
||||
.font_id(self.fonts.metamorph)
|
||||
.font_size(14)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.charwindow_title, ui);
|
||||
|
||||
// Content Alignment
|
||||
Rectangle::fill_with([95.0 * 4.0, 108.0 * 4.0], color::TRANSPARENT)
|
||||
.mid_top_with_margin_on(state.charwindow_frame, 40.0)
|
||||
.set(state.content_align, ui);
|
||||
|
||||
// Gradient BG
|
||||
Image::new(self.imgs.charwindow_gradient)
|
||||
.w_h(95.0 * 4.0, 108.0 * 4.0)
|
||||
.middle_of(state.content_align)
|
||||
.set(state.charwindow_gradient, ui);
|
||||
|
||||
// Contents
|
||||
|
||||
//Head
|
||||
Image::new(self.imgs.head_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.mid_top_with_margin_on(state.content_align, 5.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.head_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.head_bg)
|
||||
.set(state.head_grid, ui);
|
||||
|
||||
// Ring R
|
||||
Image::new(self.imgs.ring_r_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.bottom_right_with_margins_on(state.content_align, 20.0, 20.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.ring_r_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.ring_r_bg)
|
||||
.set(state.ring_r_grid, ui);
|
||||
// Feet
|
||||
Image::new(self.imgs.feet_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.up_from(state.ring_r_bg, 10.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.feet_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.feet_bg)
|
||||
.set(state.feet_grid, ui);
|
||||
// Legs
|
||||
Image::new(self.imgs.legs_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.up_from(state.feet_bg, 10.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.legs_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.legs_bg)
|
||||
.set(state.legs_grid, ui);
|
||||
// Belt
|
||||
Image::new(self.imgs.belt_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.up_from(state.legs_bg, 10.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.belt_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.belt_bg)
|
||||
.set(state.belt_grid, ui);
|
||||
// Hands
|
||||
Image::new(self.imgs.hands_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.up_from(state.belt_bg, 10.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.hands_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.hands_bg)
|
||||
.set(state.hands_grid, ui);
|
||||
// Shoulders
|
||||
Image::new(self.imgs.shoulders_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.up_from(state.hands_bg, 10.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.shoulders_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.shoulders_bg)
|
||||
.set(state.shoulders_grid, ui);
|
||||
// Ring L
|
||||
Image::new(self.imgs.ring_l_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.bottom_left_with_margins_on(state.content_align, 20.0, 20.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.ring_l_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.ring_l_bg)
|
||||
.set(state.ring_l_grid, ui);
|
||||
// Tabard
|
||||
Image::new(self.imgs.tabard_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.up_from(state.ring_l_bg, 10.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.tabard_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.tabard_bg)
|
||||
.set(state.tabard_grid, ui);
|
||||
// Chest
|
||||
Image::new(self.imgs.chest_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.up_from(state.tabard_bg, 10.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.chest_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.chest_bg)
|
||||
.set(state.chest_grid, ui);
|
||||
// Back
|
||||
Image::new(self.imgs.back_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.up_from(state.chest_bg, 10.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.back_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.back_bg)
|
||||
.set(state.back_grid, ui);
|
||||
// Gem
|
||||
Image::new(self.imgs.gem_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.up_from(state.back_bg, 10.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.gem_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.gem_bg)
|
||||
.set(state.gem_grid, ui);
|
||||
// Necklace
|
||||
Image::new(self.imgs.necklace_bg)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.up_from(state.gem_bg, 10.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.necklace_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||
.middle_of(state.necklace_bg)
|
||||
.set(state.necklace_grid, ui);
|
||||
|
||||
// Weapon Main Hand
|
||||
Image::new(self.imgs.mainhand_bg)
|
||||
.w_h(28.0 * 2.2, 28.0 * 2.2)
|
||||
.bottom_right_with_margins_on(state.ring_l_bg, 0.0, -115.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.mainhand_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 2.2, 28.0 * 2.2)
|
||||
.middle_of(state.mainhand_bg)
|
||||
.set(state.mainhand_grid, ui);
|
||||
// Weapon Off-Hand
|
||||
Image::new(self.imgs.offhand_bg)
|
||||
.w_h(28.0 * 2.2, 28.0 * 2.2)
|
||||
.bottom_left_with_margins_on(state.ring_r_bg, 0.0, -115.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.1)))
|
||||
.set(state.offhand_bg, ui);
|
||||
Button::image(self.imgs.grid)
|
||||
.w_h(28.0 * 2.2, 28.0 * 2.2)
|
||||
.middle_of(state.offhand_bg)
|
||||
.set(state.offhand_grid, ui);
|
||||
|
||||
// Stats Tab
|
||||
|
||||
// Tab BG
|
||||
Image::new(self.imgs.charwindow_tab_bg)
|
||||
.w_h(205.0, 412.0)
|
||||
.mid_left_with_margin_on(state.charwindow_frame, -205.0)
|
||||
Image::new(self.imgs.tab_bg)
|
||||
.w_h(51.0 * 4.0, 115.0 * 4.0)
|
||||
.top_left_with_margins_on(state.charwindow_frame, 28.0, -200.0)
|
||||
.set(state.charwindow_tab_bg, ui);
|
||||
|
||||
// Tab Rectangle
|
||||
Rectangle::fill_with([192.0, 371.0], color::rgba(0.0, 0.0, 0.0, 0.8))
|
||||
.top_right_with_margins_on(state.charwindow_tab_bg, 20.0, 0.0)
|
||||
Rectangle::fill_with([45.0 * 4.0, 104.0 * 4.0], color::TRANSPARENT)
|
||||
.top_left_with_margins_on(state.charwindow_tab_bg, 7.0 * 4.0, 4.0 * 4.0)
|
||||
.set(state.charwindow_rectangle, ui);
|
||||
|
||||
// Tab Button
|
||||
Button::image(self.imgs.charwindow_tab)
|
||||
.w_h(65.0, 23.0)
|
||||
.top_left_with_margins_on(state.charwindow_tab_bg, -18.0, 2.0)
|
||||
.label("Stats")
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_font_size(14)
|
||||
.set(state.charwindow_tab1, ui);
|
||||
// Tab Button -> Add that back in when we have multiple tabs
|
||||
// Button::image(self.imgs.charwindow_tab)
|
||||
//.w_h(65.0, 23.0)
|
||||
//.top_left_with_margins_on(state.charwindow_tab_bg, -18.0, 1.8)
|
||||
//.label("Stats")
|
||||
//.label_color(TEXT_COLOR)
|
||||
//.label_font_size(14)
|
||||
//.set(state.charwindow_tab1, ui);
|
||||
|
||||
Text::new("1") //Add in actual Character Level
|
||||
.mid_top_with_margin_on(state.charwindow_rectangle, 10.0)
|
||||
@ -152,6 +359,13 @@ impl<'a> Widget for CharacterWindow<'a> {
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.charwindow_tab1_exp, ui);
|
||||
|
||||
// Divider
|
||||
|
||||
Image::new(self.imgs.divider)
|
||||
.w_h(38.0 * 4.0, 5.0 * 4.0)
|
||||
.mid_top_with_margin_on(state.charwindow_tab1_exp, 30.0)
|
||||
.set(state.divider, ui);
|
||||
|
||||
// Stats
|
||||
Text::new(
|
||||
"Stamina\n\
|
||||
@ -162,7 +376,7 @@ impl<'a> Widget for CharacterWindow<'a> {
|
||||
\n\
|
||||
Intelligence",
|
||||
)
|
||||
.top_left_with_margins_on(state.charwindow_rectangle, 100.0, 20.0)
|
||||
.top_left_with_margins_on(state.charwindow_rectangle, 140.0, 5.0)
|
||||
.font_id(self.fonts.opensans)
|
||||
.font_size(16)
|
||||
.color(TEXT_COLOR)
|
||||
@ -177,7 +391,7 @@ impl<'a> Widget for CharacterWindow<'a> {
|
||||
\n\
|
||||
124124",
|
||||
)
|
||||
.right_from(state.charwindow_tab1_statnames, 10.0)
|
||||
.top_right_with_margins_on(state.charwindow_rectangle, 140.0, 5.0)
|
||||
.font_id(self.fonts.opensans)
|
||||
.font_size(16)
|
||||
.color(TEXT_COLOR)
|
||||
|
@ -1,13 +1,59 @@
|
||||
use crate::ui::{BlankGraphic, ImageGraphic, VoxelGraphic};
|
||||
use crate::ui::img_ids::{BlankGraphic, ImageGraphic, VoxelGraphic, VoxelMs9Graphic};
|
||||
|
||||
image_ids! {
|
||||
pub struct Imgs {
|
||||
<VoxelGraphic>
|
||||
|
||||
// Bag
|
||||
bag_contents: "/voxygen/element/frames/bag.vox",
|
||||
inv_grid: "/voxygen/element/frames/inv_grid.vox",
|
||||
inv_slot: "/voxygen/element/buttons/inv_slot.vox",
|
||||
|
||||
// Window Parts
|
||||
window_3: "/voxygen/element/frames/window_3.vox",
|
||||
tab_bg: "/voxygen/element/frames/tab_bg.vox",
|
||||
tab_small_open: "/voxygen/element/frames/tab_small_open.vox",
|
||||
tab_small_closed: "/voxygen/element/frames/tab_small_closed.vox",
|
||||
|
||||
// MiniMap
|
||||
mmap_frame: "/voxygen/element/frames/mmap.vox",
|
||||
mmap_frame_closed: "/voxygen/element/frames/mmap_closed.vox",
|
||||
|
||||
// Missing: Buff Frame Animation .gif ?! we could do animation in ui.maintain, or in shader?
|
||||
window_frame: "/voxygen/element/frames/window2.vox",
|
||||
|
||||
// Settings Window
|
||||
settings_frame_r: "/voxygen/element/frames/settings_r.vox",
|
||||
settings_frame_l: "/voxygen/element/frames/settings_l.vox",
|
||||
settings_button: "/voxygen/element/buttons/settings_button.vox",
|
||||
settings_button_pressed: "/voxygen/element/buttons/settings_button_pressed.vox",
|
||||
settings_button_hover: "/voxygen/element/buttons/settings_button_hover.vox",
|
||||
settings_button_press: "/voxygen/element/buttons/settings_button_press.vox",
|
||||
check: "/voxygen/element/buttons/check/no.vox",
|
||||
check_mo: "/voxygen/element/buttons/check/no_mo.vox",
|
||||
check_press: "/voxygen/element/buttons/check/press.vox",
|
||||
check_checked: "/voxygen/element/buttons/check/yes.vox",
|
||||
check_checked_mo: "/voxygen/element/buttons/check/yes_mo.vox",
|
||||
slider: "/voxygen/element/slider/track.vox",
|
||||
slider_indicator: "/voxygen/element/slider/indicator.vox",
|
||||
|
||||
// Map Window
|
||||
map_frame_l: "/voxygen/element/frames/map_l.vox",
|
||||
map_frame_r: "/voxygen/element/frames/map_r.vox",
|
||||
map_frame_bl: "/voxygen/element/frames/map_bl.vox",
|
||||
map_frame_br: "/voxygen/element/frames/map_br.vox",
|
||||
|
||||
// Chat-Arrows
|
||||
chat_arrow: "/voxygen/element/buttons/arrow_down.vox",
|
||||
chat_arrow_mo: "/voxygen/element/buttons/arrow_down_hover.vox",
|
||||
chat_arrow_press: "/voxygen/element/buttons/arrow_down_press.vox",
|
||||
|
||||
// Crosshair
|
||||
crosshair: "/voxygen/element/misc_bg/crosshair.vox",
|
||||
|
||||
|
||||
<VoxelMs9Graphic>
|
||||
|
||||
// Buttons
|
||||
mmap_closed: "/voxygen/element/buttons/button_mmap_closed.vox",
|
||||
mmap_closed_hover: "/voxygen/element/buttons/button_mmap_closed_hover.vox",
|
||||
@ -16,6 +62,11 @@ image_ids! {
|
||||
mmap_open_hover: "/voxygen/element/buttons/button_mmap_open_hover.vox",
|
||||
mmap_open_press: "/voxygen/element/buttons/button_mmap_open_press.vox",
|
||||
|
||||
// Grid
|
||||
grid: "/voxygen/element/buttons/grid.vox",
|
||||
grid_hover: "/voxygen/element/buttons/grid.vox",
|
||||
grid_press: "/voxygen/element/buttons/grid.vox",
|
||||
|
||||
settings: "/voxygen/element/buttons/settings.vox",
|
||||
settings_hover: "/voxygen/element/buttons/settings_hover.vox",
|
||||
settings_press: "/voxygen/element/buttons/settings_press.vox",
|
||||
@ -40,6 +91,24 @@ image_ids! {
|
||||
qlog_hover: "/voxygen/element/buttons/qlog_hover.vox",
|
||||
qlog_press: "/voxygen/element/buttons/qlog_press.vox",
|
||||
|
||||
// Charwindow
|
||||
xp_charwindow: "/voxygen/element/frames/xp_charwindow.vox",
|
||||
divider: "/voxygen/element/frames/divider_charwindow.vox",
|
||||
head_bg: "/voxygen/element/icons/head.vox",
|
||||
shoulders_bg: "/voxygen/element/icons/shoulders.vox",
|
||||
hands_bg: "/voxygen/element/icons/hands.vox",
|
||||
belt_bg: "/voxygen/element/icons/belt.vox",
|
||||
legs_bg: "/voxygen/element/icons/legs.vox",
|
||||
feet_bg: "/voxygen/element/icons/feet.vox",
|
||||
ring_r_bg: "/voxygen/element/icons/ring.vox",
|
||||
ring_l_bg: "/voxygen/element/icons/ring.vox",
|
||||
tabard_bg: "/voxygen/element/icons/tabard.vox",
|
||||
chest_bg: "/voxygen/element/icons/chest.vox",
|
||||
back_bg: "/voxygen/element/icons/back.vox",
|
||||
gem_bg: "/voxygen/element/icons/gem.vox",
|
||||
necklace_bg: "/voxygen/element/icons/necklace.vox",
|
||||
mainhand_bg: "/voxygen/element/icons/mainhand.vox",
|
||||
offhand_bg: "/voxygen/element/icons/offhand.vox",
|
||||
|
||||
// Close button
|
||||
close_button: "/voxygen/element/buttons/x.vox",
|
||||
@ -52,44 +121,11 @@ image_ids! {
|
||||
button_hover: "/voxygen/element/buttons/button_hover.vox",
|
||||
button_press: "/voxygen/element/buttons/button_press.vox",
|
||||
|
||||
// MiniMap
|
||||
mmap_frame: "/voxygen/element/frames/mmap.vox",
|
||||
mmap_frame_closed: "/voxygen/element/frames/mmap_closed.vox",
|
||||
|
||||
|
||||
// Missing: Buff Frame Animation .gif ?! we could do animation in ui.maintain, or in shader?
|
||||
window_frame: "/voxygen/element/frames/window2.vox",
|
||||
|
||||
// Settings Window
|
||||
settings_frame_r: "/voxygen/element/frames/settings_r.vox",
|
||||
settings_frame_l: "/voxygen/element/frames/settings_l.vox",
|
||||
settings_button: "/voxygen/element/buttons/settings_button.vox",
|
||||
settings_button_pressed: "/voxygen/element/buttons/settings_button_pressed.vox",
|
||||
settings_button_hover: "/voxygen/element/buttons/settings_button_hover.vox",
|
||||
settings_button_press: "/voxygen/element/buttons/settings_button_press.vox",
|
||||
check: "/voxygen/element/buttons/check/no.vox",
|
||||
check_mo: "/voxygen/element/buttons/check/no_mo.vox",
|
||||
check_press: "/voxygen/element/buttons/check/press.vox",
|
||||
check_checked: "/voxygen/element/buttons/check/yes.vox",
|
||||
check_checked_mo: "/voxygen/element/buttons/check/yes_mo.vox",
|
||||
slider: "/voxygen/element/slider/track.vox",
|
||||
slider_indicator: "/voxygen/element/slider/indicator.vox",
|
||||
|
||||
|
||||
// Map Window
|
||||
map_frame_l: "/voxygen/element/frames/map_l.vox",
|
||||
map_frame_r: "/voxygen/element/frames/map_r.vox",
|
||||
map_frame_bl: "/voxygen/element/frames/map_bl.vox",
|
||||
map_frame_br: "/voxygen/element/frames/map_br.vox",
|
||||
|
||||
|
||||
// Chat-Arrows
|
||||
chat_arrow: "/voxygen/element/buttons/arrow_down.vox",
|
||||
chat_arrow_mo: "/voxygen/element/buttons/arrow_down_hover.vox",
|
||||
chat_arrow_press: "/voxygen/element/buttons/arrow_down_press.vox",
|
||||
|
||||
<ImageGraphic>
|
||||
|
||||
charwindow_gradient:"/voxygen/element/misc_bg/charwindow.png",
|
||||
|
||||
// Spell Book Window
|
||||
spellbook_bg: "/voxygen/element/misc_bg/small_bg.png",
|
||||
spellbook_icon: "/voxygen/element/icons/spellbook.png",
|
||||
|
@ -71,6 +71,7 @@ impl<'a> Widget for Map<'a> {
|
||||
.scroll_kids()
|
||||
.scroll_kids_vertically()
|
||||
.set(state.ids.map_bg, ui);
|
||||
|
||||
// Frame
|
||||
Image::new(self.imgs.map_frame_l)
|
||||
.top_left_with_margins_on(state.ids.map_bg, 0.0, 0.0)
|
||||
@ -95,12 +96,6 @@ impl<'a> Widget for Map<'a> {
|
||||
.top_left_with_margins_on(state.ids.map_frame, -10.0, -10.0)
|
||||
.set(state.ids.map_icon, ui);
|
||||
|
||||
// Icon
|
||||
Image::new(self.imgs.map_icon)
|
||||
.w_h(224.0 / 3.0, 224.0 / 3.0)
|
||||
.top_left_with_margins_on(state.ids.map_frame, -10.0, -10.0)
|
||||
.set(state.ids.map_icon, ui);
|
||||
|
||||
// X-Button
|
||||
if Button::image(self.imgs.close_button)
|
||||
.w_h(28.0, 28.0)
|
||||
|
@ -306,7 +306,7 @@ impl Hud {
|
||||
.w_h(100.0 * 0.2, 100.0 * 0.2)
|
||||
.hover_image(self.imgs.close_button_hover)
|
||||
.press_image(self.imgs.close_button_press)
|
||||
.top_right_with_margins_on(self.ids.help_bg, 8.0, 3.0)
|
||||
.top_right_with_margins_on(self.ids.help_bg, 4.0, 4.0)
|
||||
.set(self.ids.button_help2, ui_widgets)
|
||||
.was_clicked()
|
||||
{
|
||||
@ -475,6 +475,13 @@ impl Hud {
|
||||
self.show.toggle_ui();
|
||||
true
|
||||
}
|
||||
WinEvent::KeyDown(Key::ToggleCursor) => {
|
||||
self.force_ungrab = !self.force_ungrab;
|
||||
if self.force_ungrab {
|
||||
global_state.window.grab_cursor(false);
|
||||
}
|
||||
true
|
||||
}
|
||||
_ if !self.show.ui => false,
|
||||
WinEvent::Zoom(_) => !cursor_grabbed && !self.ui.no_widget_capturing_mouse(),
|
||||
WinEvent::KeyDown(Key::Enter) => {
|
||||
@ -527,13 +534,6 @@ impl Hud {
|
||||
self.show.toggle_help();
|
||||
true
|
||||
}
|
||||
Key::ToggleCursor => {
|
||||
self.force_ungrab = !self.force_ungrab;
|
||||
if self.force_ungrab {
|
||||
global_state.window.grab_cursor(false);
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
WinEvent::KeyDown(key) | WinEvent::KeyUp(key) => match key {
|
||||
|
@ -21,6 +21,7 @@ widget_ids! {
|
||||
test,
|
||||
xp_bar,
|
||||
xp_bar_progress,
|
||||
crosshair,
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,8 +70,14 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
|
||||
// TODO: Read from parameter / character struct
|
||||
let xp_percentage = 0.4;
|
||||
let hp_percentage = 0.4;
|
||||
let mana_percentage = 0.4;
|
||||
let hp_percentage = 1.0;
|
||||
let mana_percentage = 1.0;
|
||||
|
||||
// Crosshair TODO: Only show while aiming with a bow or when casting a spell
|
||||
// Image::new(self.imgs.crosshair)
|
||||
// .w_h(101.0 * 0.5, 101.0 * 0.5)
|
||||
// .mid_top_with_margin_on(ui.window, 500.0)
|
||||
// .set(state.ids.crosshair, ui);
|
||||
|
||||
// Experience-Bar
|
||||
Image::new(self.imgs.xp_bar)
|
||||
|
@ -136,7 +136,7 @@ Voxygen has logged information about the problem (including this message) to the
|
||||
The information below is intended for developers and testers.
|
||||
|
||||
Panic Payload: {:?}
|
||||
PanicInfo: {:?}", settings_clone.log.file, reason, panic_info);
|
||||
PanicInfo: {}", settings_clone.log.file, reason, panic_info);
|
||||
|
||||
log::error!("VOXYGEN HAS PANICKED\n\n{}", msg);
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
mod scene;
|
||||
mod ui;
|
||||
|
||||
use crate::{
|
||||
@ -7,6 +8,7 @@ use crate::{
|
||||
};
|
||||
use client::{self, Client};
|
||||
use common::{clock::Clock, msg::ClientMsg};
|
||||
use scene::Scene;
|
||||
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||
use ui::CharSelectionUi;
|
||||
use vek::*;
|
||||
@ -16,6 +18,7 @@ const FPS: u64 = 60;
|
||||
pub struct CharSelectionState {
|
||||
char_selection_ui: CharSelectionUi,
|
||||
client: Rc<RefCell<Client>>,
|
||||
scene: Scene,
|
||||
}
|
||||
|
||||
impl CharSelectionState {
|
||||
@ -24,6 +27,7 @@ impl CharSelectionState {
|
||||
Self {
|
||||
char_selection_ui: CharSelectionUi::new(window),
|
||||
client,
|
||||
scene: Scene::new(window.renderer_mut()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -82,6 +86,14 @@ impl PlayState for CharSelectionState {
|
||||
}
|
||||
}
|
||||
|
||||
// Maintain the scene
|
||||
self.scene
|
||||
.maintain(global_state.window.renderer_mut(), &self.client.borrow());
|
||||
|
||||
// Render the scene
|
||||
self.scene
|
||||
.render(global_state.window.renderer_mut(), &self.client.borrow());
|
||||
|
||||
// Draw the UI to the screen
|
||||
self.char_selection_ui
|
||||
.render(global_state.window.renderer_mut());
|
||||
|
134
voxygen/src/menu/char_selection/scene.rs
Normal file
134
voxygen/src/menu/char_selection/scene.rs
Normal file
@ -0,0 +1,134 @@
|
||||
use crate::{
|
||||
anim::{
|
||||
character::{CharacterSkeleton, IdleAnimation},
|
||||
Animation, Skeleton,
|
||||
},
|
||||
render::{
|
||||
create_pp_mesh, create_skybox_mesh, Consts, FigurePipeline, Globals, Model,
|
||||
PostProcessLocals, PostProcessPipeline, Renderer, SkyboxLocals, SkyboxPipeline,
|
||||
},
|
||||
scene::{
|
||||
camera::Camera,
|
||||
figure::{FigureModelCache, FigureState},
|
||||
},
|
||||
};
|
||||
use client::Client;
|
||||
use common::{comp::Character, figure::Segment};
|
||||
use vek::*;
|
||||
|
||||
struct Skybox {
|
||||
model: Model<SkyboxPipeline>,
|
||||
locals: Consts<SkyboxLocals>,
|
||||
}
|
||||
|
||||
struct PostProcess {
|
||||
model: Model<PostProcessPipeline>,
|
||||
locals: Consts<PostProcessLocals>,
|
||||
}
|
||||
|
||||
pub struct Scene {
|
||||
globals: Consts<Globals>,
|
||||
camera: Camera,
|
||||
|
||||
skybox: Skybox,
|
||||
postprocess: PostProcess,
|
||||
backdrop_model: Model<FigurePipeline>,
|
||||
backdrop_state: FigureState<CharacterSkeleton>,
|
||||
|
||||
figure_model_cache: FigureModelCache,
|
||||
figure_state: FigureState<CharacterSkeleton>,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new(renderer: &mut Renderer) -> Self {
|
||||
let resolution = renderer.get_resolution().map(|e| e as f32);
|
||||
|
||||
Self {
|
||||
globals: renderer.create_consts(&[Globals::default()]).unwrap(),
|
||||
camera: Camera::new(resolution.x / resolution.y),
|
||||
|
||||
skybox: Skybox {
|
||||
model: renderer.create_model(&create_skybox_mesh()).unwrap(),
|
||||
locals: renderer.create_consts(&[SkyboxLocals::default()]).unwrap(),
|
||||
},
|
||||
postprocess: PostProcess {
|
||||
model: renderer.create_model(&create_pp_mesh()).unwrap(),
|
||||
locals: renderer
|
||||
.create_consts(&[PostProcessLocals::default()])
|
||||
.unwrap(),
|
||||
},
|
||||
figure_model_cache: FigureModelCache::new(),
|
||||
figure_state: FigureState::new(renderer, CharacterSkeleton::new()),
|
||||
|
||||
backdrop_model: renderer
|
||||
.create_model(&FigureModelCache::load_mesh("knight.vox", Vec3::zero()))
|
||||
.unwrap(),
|
||||
backdrop_state: FigureState::new(renderer, CharacterSkeleton::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||
self.camera.set_focus_pos(Vec3::unit_z() * 1.75);
|
||||
self.camera.update(client.state().get_time());
|
||||
self.camera.set_distance(4.0);
|
||||
self.camera
|
||||
.set_orientation(Vec3::new(client.state().get_time() as f32 * 0.2, 0.3, 0.0));
|
||||
|
||||
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client);
|
||||
|
||||
renderer.update_consts(
|
||||
&mut self.globals,
|
||||
&[Globals::new(
|
||||
view_mat,
|
||||
proj_mat,
|
||||
cam_pos,
|
||||
self.camera.get_focus_pos(),
|
||||
100.0,
|
||||
client.state().get_time_of_day(),
|
||||
client.state().get_time(),
|
||||
renderer.get_resolution(),
|
||||
)],
|
||||
);
|
||||
|
||||
self.figure_model_cache.clean(client.get_tick());
|
||||
|
||||
let tgt_skeleton = IdleAnimation::update_skeleton(
|
||||
self.figure_state.skeleton_mut(),
|
||||
client.state().get_time(),
|
||||
client.state().get_time(),
|
||||
);
|
||||
self.figure_state.skeleton_mut().interpolate(&tgt_skeleton);
|
||||
|
||||
self.figure_state
|
||||
.update(renderer, Vec3::zero(), -Vec3::unit_y());
|
||||
}
|
||||
|
||||
pub fn render(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||
renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals);
|
||||
|
||||
let model = self.figure_model_cache.get_or_create_model(
|
||||
renderer,
|
||||
Character::random(),
|
||||
client.get_tick(),
|
||||
);
|
||||
renderer.render_figure(
|
||||
model,
|
||||
&self.globals,
|
||||
self.figure_state.locals(),
|
||||
self.figure_state.bone_consts(),
|
||||
);
|
||||
|
||||
renderer.render_figure(
|
||||
&self.backdrop_model,
|
||||
&self.globals,
|
||||
self.backdrop_state.locals(),
|
||||
self.backdrop_state.bone_consts(),
|
||||
);
|
||||
|
||||
renderer.render_post_process(
|
||||
&self.postprocess.model,
|
||||
&self.globals,
|
||||
&self.postprocess.locals,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,17 +1,18 @@
|
||||
use crate::{
|
||||
render::Renderer,
|
||||
ui::{self, BlankGraphic, Graphic, ImageGraphic, ScaleMode, Ui, VoxelGraphic},
|
||||
ui::{
|
||||
self,
|
||||
img_ids::{ImageGraphic, VoxelGraphic},
|
||||
ScaleMode, Ui,
|
||||
},
|
||||
window::Window,
|
||||
};
|
||||
use common::{
|
||||
assets,
|
||||
comp::character::{Belt, Character, Chest, Foot, Gender, Hand, Head, Pants, Race, Weapon},
|
||||
use common::comp::character::{
|
||||
Belt, Character, Chest, Foot, Gender, Hand, Head, Pants, Race, Weapon,
|
||||
};
|
||||
use conrod_core::{
|
||||
color,
|
||||
color::TRANSPARENT,
|
||||
image::Id as ImgId,
|
||||
text::font::Id as FontId,
|
||||
widget::{text_box::Event as TextBoxEvent, Button, Image, Rectangle, Text, TextBox},
|
||||
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||
};
|
||||
@ -154,7 +155,6 @@ widget_ids! {
|
||||
warpaint_slider_indicator,
|
||||
warpaint_slider_range,
|
||||
warpaint_slider_text,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,13 +315,13 @@ impl CharSelectionUi {
|
||||
|
||||
// Background Image
|
||||
if !self.character_creation {
|
||||
Image::new(self.imgs.bg_selection)
|
||||
.middle_of(ui_widgets.window)
|
||||
.set(self.ids.bg_selection, ui_widgets);
|
||||
//Image::new(self.imgs.bg_selection)
|
||||
// .middle_of(ui_widgets.window)
|
||||
// .set(self.ids.bg_selection, ui_widgets);
|
||||
|
||||
// Logout_Button
|
||||
if Button::image(self.imgs.button)
|
||||
.bottom_left_with_margins_on(self.ids.bg_selection, 10.0, 10.0)
|
||||
.bottom_left_with_margins_on(ui_widgets.window, 10.0, 10.0)
|
||||
.w_h(150.0, 40.0)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
@ -337,7 +337,7 @@ impl CharSelectionUi {
|
||||
|
||||
// Create Character Button
|
||||
if Button::image(self.imgs.button)
|
||||
.mid_bottom_with_margin_on(self.ids.bg_selection, 10.0)
|
||||
.mid_bottom_with_margin_on(ui_widgets.window, 10.0)
|
||||
.w_h(270.0, 50.0)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
@ -353,7 +353,7 @@ impl CharSelectionUi {
|
||||
}
|
||||
// Test Characters
|
||||
if Button::image(self.imgs.test_char_l_button)
|
||||
.bottom_left_with_margins_on(self.ids.bg_selection, 395.0, 716.0)
|
||||
.bottom_left_with_margins_on(ui_widgets.window, 395.0, 716.0)
|
||||
.w_h(95.0, 130.0)
|
||||
.hover_image(self.imgs.test_char_l_button)
|
||||
.press_image(self.imgs.test_char_l_button)
|
||||
@ -375,7 +375,7 @@ impl CharSelectionUi {
|
||||
.set(self.ids.version, ui_widgets);
|
||||
// Click Character to Login <-- Temporary!
|
||||
Image::new(self.imgs.window_frame_2)
|
||||
.mid_top_with_margin_on(self.ids.bg_selection, 60.0)
|
||||
.mid_top_with_margin_on(ui_widgets.window, 60.0)
|
||||
.w_h(700.0, 70.0)
|
||||
.set(self.ids.help_text_bg, ui_widgets);
|
||||
Text::new("Click character to select it")
|
||||
@ -445,12 +445,13 @@ impl CharSelectionUi {
|
||||
// Character_Creation //////////////
|
||||
else {
|
||||
// Background
|
||||
Image::new(self.imgs.bg_creation)
|
||||
.middle_of(ui_widgets.window)
|
||||
.set(self.ids.bg_creation, ui_widgets);
|
||||
//Image::new(self.imgs.bg_creation)
|
||||
// .middle_of(ui_widgets.window)
|
||||
// .set(self.ids.bg_creation, ui_widgets);
|
||||
|
||||
// Back Button
|
||||
if Button::image(self.imgs.button)
|
||||
.bottom_left_with_margins_on(self.ids.bg_creation, 10.0, 10.0)
|
||||
.bottom_left_with_margins_on(ui_widgets.window, 10.0, 10.0)
|
||||
.w_h(150.0, 40.0)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
@ -465,7 +466,7 @@ impl CharSelectionUi {
|
||||
}
|
||||
// Create Button
|
||||
if Button::image(self.imgs.button)
|
||||
.bottom_right_with_margins_on(self.ids.bg_creation, 10.0, 10.0)
|
||||
.bottom_right_with_margins_on(ui_widgets.window, 10.0, 10.0)
|
||||
.w_h(150.0, 40.0)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
@ -481,7 +482,7 @@ impl CharSelectionUi {
|
||||
}
|
||||
// Character Name Input
|
||||
Rectangle::fill_with([320.0, 50.0], color::rgba(0.0, 0.0, 0.0, 0.99))
|
||||
.mid_bottom_with_margin_on(self.ids.bg_creation, 20.0)
|
||||
.mid_bottom_with_margin_on(ui_widgets.window, 20.0)
|
||||
.set(self.ids.name_input_bg, ui_widgets);
|
||||
Button::image(self.imgs.name_input)
|
||||
.w_h(337.0, 67.0)
|
||||
@ -513,7 +514,7 @@ impl CharSelectionUi {
|
||||
self.imgs.creation_window
|
||||
})
|
||||
.w_h(628.0, 814.0)
|
||||
.top_left_with_margins_on(self.ids.bg_creation, 60.0, 30.0)
|
||||
.top_left_with_margins_on(ui_widgets.window, 60.0, 30.0)
|
||||
.set(self.ids.creation_window, ui_widgets);
|
||||
|
||||
// Arrows
|
||||
|
@ -1,15 +1,16 @@
|
||||
use crate::{
|
||||
render::Renderer,
|
||||
ui::{self, BlankGraphic, Graphic, ImageGraphic, ScaleMode, Ui, VoxelGraphic},
|
||||
ui::{
|
||||
self,
|
||||
img_ids::{ImageGraphic, VoxelGraphic},
|
||||
ScaleMode, Ui,
|
||||
},
|
||||
GlobalState, DEFAULT_PUBLIC_SERVER,
|
||||
};
|
||||
use common::assets;
|
||||
use conrod_core::{
|
||||
color,
|
||||
color::TRANSPARENT,
|
||||
image::Id as ImgId,
|
||||
position::Relative,
|
||||
text::font::Id as FontId,
|
||||
widget::{text_box::Event as TextBoxEvent, Button, Image, List, Rectangle, Text, TextBox},
|
||||
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ mod util;
|
||||
pub use self::{
|
||||
consts::Consts,
|
||||
mesh::{Mesh, Quad, Tri},
|
||||
model::Model,
|
||||
model::{DynamicModel, Model},
|
||||
pipelines::{
|
||||
figure::{BoneData as FigureBoneData, FigurePipeline, Locals as FigureLocals},
|
||||
postprocess::{
|
||||
@ -40,6 +40,7 @@ pub enum RenderError {
|
||||
UpdateError(gfx::UpdateError<usize>),
|
||||
TexUpdateError(gfx::UpdateError<[u16; 3]>),
|
||||
CombinedError(gfx::CombinedError),
|
||||
BufferCreationError(gfx::buffer::CreationError),
|
||||
}
|
||||
|
||||
/// Used to represent a specific rendering configuration.
|
||||
|
@ -1,8 +1,11 @@
|
||||
// Library
|
||||
use gfx::{self, traits::FactoryExt};
|
||||
|
||||
// Local
|
||||
use super::{gfx_backend, mesh::Mesh, Pipeline};
|
||||
use super::{gfx_backend, mesh::Mesh, Pipeline, RenderError};
|
||||
use gfx::{
|
||||
buffer::Role,
|
||||
memory::{Bind, Usage},
|
||||
traits::FactoryExt,
|
||||
Factory,
|
||||
};
|
||||
use std::ops::Range;
|
||||
|
||||
/// Represents a mesh that has been sent to the GPU.
|
||||
pub struct Model<P: Pipeline> {
|
||||
@ -24,3 +27,43 @@ impl<P: Pipeline> Model<P> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a mesh on the GPU which can be updated dynamically
|
||||
pub struct DynamicModel<P: Pipeline> {
|
||||
pub vbuf: gfx::handle::Buffer<gfx_backend::Resources, P::Vertex>,
|
||||
}
|
||||
|
||||
impl<P: Pipeline> DynamicModel<P> {
|
||||
pub fn new(factory: &mut gfx_backend::Factory, size: usize) -> Result<Self, RenderError> {
|
||||
Ok(Self {
|
||||
vbuf: factory
|
||||
.create_buffer(size, Role::Vertex, Usage::Dynamic, Bind::empty())
|
||||
.map_err(|err| RenderError::BufferCreationError(err))?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a model with a slice of a portion of this model to send to the renderer
|
||||
pub fn submodel(&self, range: Range<usize>) -> Model<P> {
|
||||
Model {
|
||||
vbuf: self.vbuf.clone(),
|
||||
slice: gfx::Slice {
|
||||
start: range.start as u32,
|
||||
end: range.end as u32,
|
||||
base_vertex: 0,
|
||||
instances: None,
|
||||
buffer: gfx::IndexBuffer::Auto,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&self,
|
||||
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
|
||||
mesh: &Mesh<P>,
|
||||
offset: usize,
|
||||
) -> Result<(), RenderError> {
|
||||
encoder
|
||||
.update_buffer(&self.vbuf, mesh.vertices(), offset)
|
||||
.map_err(|err| RenderError::UpdateError(err))
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use super::{
|
||||
consts::Consts,
|
||||
gfx_backend,
|
||||
mesh::Mesh,
|
||||
model::Model,
|
||||
model::{DynamicModel, Model},
|
||||
pipelines::{figure, postprocess, skybox, terrain, ui, Globals},
|
||||
texture::Texture,
|
||||
Pipeline, RenderError,
|
||||
@ -12,7 +12,6 @@ use gfx::{
|
||||
handle::Sampler,
|
||||
traits::{Device, Factory, FactoryExt},
|
||||
};
|
||||
use image;
|
||||
use vek::*;
|
||||
|
||||
/// Represents the format of the pre-processed color target.
|
||||
@ -246,6 +245,24 @@ impl Renderer {
|
||||
Ok(Model::new(&mut self.factory, mesh))
|
||||
}
|
||||
|
||||
/// Create a new dynamic model with the specified size
|
||||
pub fn create_dynamic_model<P: Pipeline>(
|
||||
&mut self,
|
||||
size: usize,
|
||||
) -> Result<DynamicModel<P>, RenderError> {
|
||||
DynamicModel::new(&mut self.factory, size)
|
||||
}
|
||||
|
||||
/// Update a dynamic model with a mesh and a offset
|
||||
pub fn update_model<P: Pipeline>(
|
||||
&mut self,
|
||||
model: &DynamicModel<P>,
|
||||
mesh: &Mesh<P>,
|
||||
offset: usize,
|
||||
) -> Result<(), RenderError> {
|
||||
model.update(&mut self.encoder, mesh, offset)
|
||||
}
|
||||
|
||||
/// Create a new texture from the provided image.
|
||||
pub fn create_texture<P: Pipeline>(
|
||||
&mut self,
|
||||
|
@ -54,7 +54,14 @@ impl Camera {
|
||||
) * self.dist),
|
||||
);
|
||||
|
||||
match client.state().terrain().ray(start, end).cast() {
|
||||
match client
|
||||
.state()
|
||||
.terrain()
|
||||
.ray(start, end)
|
||||
.ignore_error()
|
||||
.max_iter(500)
|
||||
.cast()
|
||||
{
|
||||
(d, Ok(Some(_))) => f32::min(d - 1.0, self.dist),
|
||||
(_, Ok(None)) => self.dist,
|
||||
(_, Err(_)) => self.dist,
|
||||
@ -88,12 +95,27 @@ impl Camera {
|
||||
self.ori.z = (self.ori.z + delta.z) % (2.0 * PI);
|
||||
}
|
||||
|
||||
/// Set the orientation of the camera about its focus
|
||||
pub fn set_orientation(&mut self, orientation: Vec3<f32>) {
|
||||
// Wrap camera yaw
|
||||
self.ori.x = orientation.x % (2.0 * PI);
|
||||
// Clamp camera pitch to the vertical limits
|
||||
self.ori.y = orientation.y.min(PI / 2.0).max(-PI / 2.0);
|
||||
// Wrap camera roll
|
||||
self.ori.z = orientation.z % (2.0 * PI);
|
||||
}
|
||||
|
||||
/// Zoom the camera by the given delta, limiting the input accordingly.
|
||||
pub fn zoom_by(&mut self, delta: f32) {
|
||||
// Clamp camera dist to the 0 <= x <= infinity range
|
||||
self.tgt_dist = (self.tgt_dist + delta).max(0.0);
|
||||
}
|
||||
|
||||
/// Set the distance of the camera from the target (i.e: zoom)
|
||||
pub fn set_distance(&mut self, dist: f32) {
|
||||
self.tgt_dist = dist;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, time: f64) {
|
||||
// This is horribly frame time dependent, but so is most of the game
|
||||
let delta = self.last_time.replace(time).map_or(0.0, |t| time - t);
|
||||
|
@ -24,31 +24,29 @@ use specs::{Component, Entity as EcsEntity, Join, VecStorage};
|
||||
use std::{collections::HashMap, f32};
|
||||
use vek::*;
|
||||
|
||||
pub struct FigureCache {
|
||||
pub struct FigureModelCache {
|
||||
models: HashMap<Character, (Model<FigurePipeline>, u64)>,
|
||||
states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
|
||||
}
|
||||
|
||||
impl FigureCache {
|
||||
impl FigureModelCache {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
models: HashMap::new(),
|
||||
states: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_or_create_model<'a>(
|
||||
models: &'a mut HashMap<Character, (Model<FigurePipeline>, u64)>,
|
||||
pub fn get_or_create_model(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
tick: u64,
|
||||
character: Character,
|
||||
) -> &'a (Model<FigurePipeline>, u64) {
|
||||
match models.get_mut(&character) {
|
||||
tick: u64,
|
||||
) -> &Model<FigurePipeline> {
|
||||
match self.models.get_mut(&character) {
|
||||
Some((model, last_used)) => {
|
||||
*last_used = tick;
|
||||
}
|
||||
None => {
|
||||
models.insert(
|
||||
self.models.insert(
|
||||
character,
|
||||
(
|
||||
{
|
||||
@ -91,7 +89,7 @@ impl FigureCache {
|
||||
}
|
||||
}
|
||||
|
||||
&models[&character]
|
||||
&self.models[&character].0
|
||||
}
|
||||
|
||||
pub fn clean(&mut self, tick: u64) {
|
||||
@ -100,7 +98,8 @@ impl FigureCache {
|
||||
.retain(|_, (_, last_used)| *last_used + 60 > tick);
|
||||
}
|
||||
|
||||
fn load_mesh(filename: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
||||
// TODO: Don't make this public
|
||||
pub fn load_mesh(filename: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
||||
let fullpath: String = ["/voxygen/voxel/", filename].concat();
|
||||
Segment::from(assets::load_expect::<DotVoxData>(fullpath.as_str()).as_ref())
|
||||
.generate_mesh(position)
|
||||
@ -218,9 +217,28 @@ impl FigureCache {
|
||||
// )
|
||||
// }
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) {
|
||||
}
|
||||
|
||||
pub struct FigureMgr {
|
||||
model_cache: FigureModelCache,
|
||||
states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
|
||||
}
|
||||
|
||||
impl FigureMgr {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
model_cache: FigureModelCache::new(),
|
||||
states: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clean(&mut self, tick: u64) {
|
||||
self.model_cache.clean(tick);
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||
let time = client.state().get_time();
|
||||
let ecs = client.state_mut().ecs_mut();
|
||||
let ecs = client.state().ecs();
|
||||
for (entity, pos, vel, dir, character, animation_history) in (
|
||||
&ecs.entities(),
|
||||
&ecs.read_storage::<comp::phys::Pos>(),
|
||||
@ -238,17 +256,17 @@ impl FigureCache {
|
||||
|
||||
let target_skeleton = match animation_history.current {
|
||||
comp::character::Animation::Idle => IdleAnimation::update_skeleton(
|
||||
&mut state.skeleton,
|
||||
state.skeleton_mut(),
|
||||
time,
|
||||
animation_history.time,
|
||||
),
|
||||
comp::character::Animation::Run => RunAnimation::update_skeleton(
|
||||
&mut state.skeleton,
|
||||
state.skeleton_mut(),
|
||||
(vel.0.magnitude(), time),
|
||||
animation_history.time,
|
||||
),
|
||||
comp::character::Animation::Jump => JumpAnimation::update_skeleton(
|
||||
&mut state.skeleton,
|
||||
state.skeleton_mut(),
|
||||
time,
|
||||
animation_history.time,
|
||||
),
|
||||
@ -271,13 +289,15 @@ impl FigureCache {
|
||||
) {
|
||||
let tick = client.get_tick();
|
||||
let ecs = client.state().ecs();
|
||||
let models = &mut self.models;
|
||||
|
||||
for (entity, &character) in (&ecs.entities(), &ecs.read_storage::<comp::Character>()).join()
|
||||
{
|
||||
let model = Self::get_or_create_model(models, renderer, tick, character);
|
||||
let state = self.states.get(&entity).unwrap();
|
||||
renderer.render_figure(&model.0, globals, &state.locals, &state.bone_consts);
|
||||
if let Some(state) = self.states.get(&entity) {
|
||||
let model = self
|
||||
.model_cache
|
||||
.get_or_create_model(renderer, character, tick);
|
||||
renderer.render_figure(model, globals, &state.locals(), state.bone_consts());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -299,7 +319,7 @@ impl<S: Skeleton> FigureState<S> {
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, renderer: &mut Renderer, pos: Vec3<f32>, dir: Vec3<f32>) {
|
||||
pub fn update(&mut self, renderer: &mut Renderer, pos: Vec3<f32>, dir: Vec3<f32>) {
|
||||
let mat = Mat4::<f32>::identity()
|
||||
* Mat4::translation_3d(pos)
|
||||
* Mat4::rotation_z(-dir.x.atan2(dir.y)); // + f32::consts::PI / 2.0);
|
||||
@ -311,4 +331,16 @@ impl<S: Skeleton> FigureState<S> {
|
||||
.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn locals(&self) -> &Consts<FigureLocals> {
|
||||
&self.locals
|
||||
}
|
||||
|
||||
pub fn bone_consts(&self) -> &Consts<FigureBoneData> {
|
||||
&self.bone_consts
|
||||
}
|
||||
|
||||
pub fn skeleton_mut(&mut self) -> &mut S {
|
||||
&mut self.skeleton
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ pub mod camera;
|
||||
pub mod figure;
|
||||
pub mod terrain;
|
||||
|
||||
use self::{camera::Camera, figure::FigureCache, terrain::Terrain};
|
||||
use self::{camera::Camera, figure::FigureMgr, terrain::Terrain};
|
||||
use crate::{
|
||||
anim::{
|
||||
character::{CharacterSkeleton, RunAnimation},
|
||||
@ -41,7 +41,7 @@ pub struct Scene {
|
||||
postprocess: PostProcess,
|
||||
terrain: Terrain,
|
||||
|
||||
figure_cache: FigureCache,
|
||||
figure_mgr: FigureMgr,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
@ -64,7 +64,7 @@ impl Scene {
|
||||
.unwrap(),
|
||||
},
|
||||
terrain: Terrain::new(),
|
||||
figure_cache: FigureCache::new(),
|
||||
figure_mgr: FigureMgr::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ impl Scene {
|
||||
}
|
||||
|
||||
/// Maintain data such as GPU constant buffers, models, etc. To be called once per tick.
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) {
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||
// Get player position
|
||||
let player_pos = client
|
||||
.state()
|
||||
@ -144,10 +144,10 @@ impl Scene {
|
||||
self.terrain.maintain(renderer, client);
|
||||
|
||||
// Maintain the figures
|
||||
self.figure_cache.maintain(renderer, client);
|
||||
self.figure_mgr.maintain(renderer, client);
|
||||
|
||||
// Remove unused figures
|
||||
self.figure_cache.clean(client.get_tick());
|
||||
self.figure_mgr.clean(client.get_tick());
|
||||
}
|
||||
|
||||
/// Render the scene using the provided `Renderer`
|
||||
@ -157,7 +157,7 @@ impl Scene {
|
||||
|
||||
// Render terrain and figures
|
||||
self.terrain.render(renderer, &self.globals);
|
||||
self.figure_cache.render(renderer, client, &self.globals);
|
||||
self.figure_mgr.render(renderer, client, &self.globals);
|
||||
|
||||
renderer.render_post_process(
|
||||
&self.postprocess.model,
|
||||
|
54
voxygen/src/ui/cache.rs
Normal file
54
voxygen/src/ui/cache.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use super::graphic::{Graphic, GraphicCache, Id as GraphicId};
|
||||
use crate::{
|
||||
render::{Renderer, Texture, UiPipeline},
|
||||
Error,
|
||||
};
|
||||
use conrod_core::text::GlyphCache;
|
||||
use vek::*;
|
||||
|
||||
pub struct Cache {
|
||||
glyph_cache: GlyphCache<'static>,
|
||||
glyph_cache_tex: Texture<UiPipeline>,
|
||||
graphic_cache: GraphicCache,
|
||||
graphic_cache_tex: Texture<UiPipeline>,
|
||||
}
|
||||
|
||||
// TODO: Should functions be returning UiError instead of Error?
|
||||
impl Cache {
|
||||
pub fn new(renderer: &mut Renderer) -> Result<Self, Error> {
|
||||
let (w, h) = renderer.get_resolution().into_tuple();
|
||||
const SCALE_TOLERANCE: f32 = 0.1;
|
||||
const POSITION_TOLERANCE: f32 = 0.1;
|
||||
|
||||
let graphic_cache_dims = Vec2::new(w * 4, h * 4);
|
||||
Ok(Self {
|
||||
glyph_cache: GlyphCache::builder()
|
||||
.dimensions(w as u32, h as u32)
|
||||
.scale_tolerance(SCALE_TOLERANCE)
|
||||
.position_tolerance(POSITION_TOLERANCE)
|
||||
.build(),
|
||||
glyph_cache_tex: renderer.create_dynamic_texture((w, h).into())?,
|
||||
graphic_cache: GraphicCache::new(graphic_cache_dims),
|
||||
graphic_cache_tex: renderer.create_dynamic_texture(graphic_cache_dims)?,
|
||||
})
|
||||
}
|
||||
pub fn glyph_cache_tex(&self) -> &Texture<UiPipeline> {
|
||||
&self.glyph_cache_tex
|
||||
}
|
||||
pub fn glyph_cache_mut_and_tex(&mut self) -> (&mut GlyphCache<'static>, &Texture<UiPipeline>) {
|
||||
(&mut self.glyph_cache, &self.glyph_cache_tex)
|
||||
}
|
||||
pub fn graphic_cache_tex(&self) -> &Texture<UiPipeline> {
|
||||
&self.graphic_cache_tex
|
||||
}
|
||||
pub fn graphic_cache_mut_and_tex(&mut self) -> (&mut GraphicCache, &Texture<UiPipeline>) {
|
||||
(&mut self.graphic_cache, &self.graphic_cache_tex)
|
||||
}
|
||||
pub fn add_graphic(&mut self, graphic: Graphic) -> GraphicId {
|
||||
self.graphic_cache.add_graphic(graphic)
|
||||
}
|
||||
pub fn clear_graphic_cache(&mut self, renderer: &mut Renderer, new_size: Vec2<u16>) {
|
||||
self.graphic_cache.clear_cache(new_size);
|
||||
self.graphic_cache_tex = renderer.create_dynamic_texture(new_size).unwrap();
|
||||
}
|
||||
}
|
47
voxygen/src/ui/event.rs
Normal file
47
voxygen/src/ui/event.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use conrod_core::{event::Input, input::Button};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Event(pub Input);
|
||||
impl Event {
|
||||
pub fn try_from(event: glutin::Event, window: &glutin::GlWindow) -> Option<Self> {
|
||||
use conrod_winit::*;
|
||||
use winit;
|
||||
// A wrapper around the winit window that allows us to implement the trait necessary for enabling
|
||||
// the winit <-> conrod conversion functions.
|
||||
struct WindowRef<'a>(&'a winit::Window);
|
||||
|
||||
// Implement the `WinitWindow` trait for `WindowRef` to allow for generating compatible conversion
|
||||
// functions.
|
||||
impl<'a> conrod_winit::WinitWindow for WindowRef<'a> {
|
||||
fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
winit::Window::get_inner_size(&self.0).map(Into::into)
|
||||
}
|
||||
fn hidpi_factor(&self) -> f32 {
|
||||
winit::Window::get_hidpi_factor(&self.0) as _
|
||||
}
|
||||
}
|
||||
convert_event!(event, &WindowRef(window.window())).map(|input| Self(input))
|
||||
}
|
||||
pub fn is_keyboard_or_mouse(&self) -> bool {
|
||||
match self.0 {
|
||||
Input::Press(_)
|
||||
| Input::Release(_)
|
||||
| Input::Motion(_)
|
||||
| Input::Touch(_)
|
||||
| Input::Text(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_keyboard(&self) -> bool {
|
||||
match self.0 {
|
||||
Input::Press(Button::Keyboard(_))
|
||||
| Input::Release(Button::Keyboard(_))
|
||||
| Input::Text(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn new_resize(dims: Vec2<f64>) -> Self {
|
||||
Self(Input::Resize(dims.x, dims.y))
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ use vek::*;
|
||||
|
||||
pub enum Graphic {
|
||||
Image(Arc<DynamicImage>),
|
||||
Voxel(Arc<DotVoxData>),
|
||||
Voxel(Arc<DotVoxData>, Option<u8>),
|
||||
Blank,
|
||||
}
|
||||
|
||||
@ -94,9 +94,8 @@ impl GraphicCache {
|
||||
.pixels()
|
||||
.map(|p| p.data)
|
||||
.collect::<Vec<[u8; 4]>>(),
|
||||
Graphic::Voxel(ref vox) => {
|
||||
super::renderer::draw_vox(&vox.as_ref().into(), aabr.size().into())
|
||||
}
|
||||
Graphic::Voxel(ref vox, min_samples) =>
|
||||
super::renderer::draw_vox(&vox.as_ref().into(), aabr.size().into(), *min_samples),
|
||||
Graphic::Blank => return None,
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@ use common::{
|
||||
vol::{ReadVol, SizedVol, Vox},
|
||||
};
|
||||
use euc::{buffer::Buffer2d, rasterizer, Pipeline};
|
||||
use image::{DynamicImage, RgbaImage};
|
||||
use vek::*;
|
||||
|
||||
struct Voxel {
|
||||
@ -56,8 +57,13 @@ impl<'a> Pipeline for Voxel {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_vox(segment: &Segment, output_size: Vec2<u16>) -> Vec<[u8; 4]> {
|
||||
let dims = output_size.map(|e| e as usize).into_array();
|
||||
pub fn draw_vox(
|
||||
segment: &Segment,
|
||||
output_size: Vec2<u16>,
|
||||
min_samples: Option<u8>,
|
||||
) -> Vec<[u8; 4]> {
|
||||
let scale = min_samples.map_or(1.0, |s| s as f32).sqrt().ceil() as usize;
|
||||
let dims = output_size.map(|e| e as usize * scale).into_array();
|
||||
let mut color = Buffer2d::new(dims, [0; 4]);
|
||||
let mut depth = Buffer2d::new(dims, 1.0);
|
||||
|
||||
@ -79,8 +85,33 @@ pub fn draw_vox(segment: &Segment, output_size: Vec2<u16>) -> Vec<[u8; 4]> {
|
||||
&mut depth,
|
||||
);
|
||||
|
||||
// TODO: remove this clone
|
||||
if scale > 1 {
|
||||
DynamicImage::ImageRgba8(
|
||||
RgbaImage::from_vec(
|
||||
dims[0] as u32,
|
||||
dims[1] as u32,
|
||||
color
|
||||
.as_ref()
|
||||
.iter()
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect::<Vec<u8>>(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.resize_exact(
|
||||
output_size.x as u32,
|
||||
output_size.y as u32,
|
||||
image::FilterType::Triangle,
|
||||
)
|
||||
.to_rgba()
|
||||
.pixels()
|
||||
.map(|p| p.data)
|
||||
.collect::<Vec<[u8; 4]>>()
|
||||
} else {
|
||||
// TODO: remove clone
|
||||
color.as_ref().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
fn ao_level(side1: bool, corner: bool, side2: bool) -> u8 {
|
||||
|
@ -3,9 +3,8 @@ use common::assets::{load, Error};
|
||||
use dot_vox::DotVoxData;
|
||||
use image::DynamicImage;
|
||||
|
||||
pub struct BlankGraphic;
|
||||
pub struct ImageGraphic;
|
||||
pub struct VoxelGraphic;
|
||||
pub enum BlankGraphic {}
|
||||
pub enum ImageGraphic {}
|
||||
|
||||
pub trait GraphicCreator<'a> {
|
||||
type Specifier;
|
||||
@ -23,10 +22,37 @@ impl<'a> GraphicCreator<'a> for ImageGraphic {
|
||||
Ok(Graphic::Image(load::<DynamicImage>(specifier)?))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum VoxelGraphic {}
|
||||
pub enum VoxelMsGraphic {}
|
||||
pub enum VoxelMs4Graphic {}
|
||||
pub enum VoxelMs9Graphic {}
|
||||
|
||||
impl<'a> GraphicCreator<'a> for VoxelGraphic {
|
||||
type Specifier = &'a str;
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(load::<DotVoxData>(specifier)?))
|
||||
Ok(Graphic::Voxel(load::<DotVoxData>(specifier)?, None))
|
||||
}
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for VoxelMsGraphic {
|
||||
type Specifier = (&'a str, u8);
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(
|
||||
load::<DotVoxData>(specifier.0)?,
|
||||
Some(specifier.1),
|
||||
))
|
||||
}
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for VoxelMs4Graphic {
|
||||
type Specifier = &'a str;
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(load::<DotVoxData>(specifier)?, Some(4)))
|
||||
}
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for VoxelMs9Graphic {
|
||||
type Specifier = &'a str;
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(load::<DotVoxData>(specifier)?, Some(9)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,9 +85,9 @@ macro_rules! image_ids {
|
||||
|
||||
impl $Ids {
|
||||
pub fn load(ui: &mut crate::ui::Ui) -> Result<Self, common::assets::Error> {
|
||||
use crate::ui::GraphicCreator;
|
||||
use crate::ui::img_ids::GraphicCreator;
|
||||
Ok(Self {
|
||||
$($( $name: ui.add_graphic(<$T>::new_graphic($specifier)?), )*)*
|
||||
$($( $name: ui.add_graphic(<$T as GraphicCreator>::new_graphic($specifier)?), )*)*
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,134 +1,50 @@
|
||||
mod cache;
|
||||
mod event;
|
||||
mod graphic;
|
||||
mod scale;
|
||||
mod util;
|
||||
mod widgets;
|
||||
#[macro_use]
|
||||
mod img_ids;
|
||||
pub mod img_ids;
|
||||
#[macro_use]
|
||||
mod font_ids;
|
||||
|
||||
pub use event::Event;
|
||||
pub use graphic::Graphic;
|
||||
pub use img_ids::{BlankGraphic, GraphicCreator, ImageGraphic, VoxelGraphic};
|
||||
pub(self) use util::{linear_to_srgb, srgb_to_linear};
|
||||
pub use scale::ScaleMode;
|
||||
pub use widgets::toggle_button::ToggleButton;
|
||||
|
||||
use crate::{
|
||||
render::{
|
||||
create_ui_quad, create_ui_tri, Mesh, Model, RenderError, Renderer, Texture, UiMode,
|
||||
create_ui_quad, create_ui_tri, DynamicModel, Mesh, RenderError, Renderer, UiMode,
|
||||
UiPipeline,
|
||||
},
|
||||
window::Window,
|
||||
Error,
|
||||
};
|
||||
use cache::Cache;
|
||||
use common::assets;
|
||||
use conrod_core::{
|
||||
event::Input,
|
||||
graph::Graph,
|
||||
image::{Id as ImgId, Map},
|
||||
input::{touch::Touch, Button, Motion, Widget},
|
||||
input::{touch::Touch, Motion, Widget},
|
||||
render::Primitive,
|
||||
text::{self, GlyphCache},
|
||||
text::{self, font},
|
||||
widget::{id::Generator, Id as WidgId},
|
||||
Ui as CrUi, UiBuilder, UiCell,
|
||||
};
|
||||
use graphic::{GraphicCache, Id as GraphicId};
|
||||
use graphic::Id as GraphicId;
|
||||
use scale::Scale;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
use util::{linear_to_srgb, srgb_to_linear};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UiError {
|
||||
RenderError(RenderError),
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Event(Input);
|
||||
impl Event {
|
||||
pub fn try_from(event: glutin::Event, window: &glutin::GlWindow) -> Option<Self> {
|
||||
use conrod_winit::*;
|
||||
use winit;
|
||||
// A wrapper around the winit window that allows us to implement the trait necessary for enabling
|
||||
// the winit <-> conrod conversion functions.
|
||||
struct WindowRef<'a>(&'a winit::Window);
|
||||
|
||||
// Implement the `WinitWindow` trait for `WindowRef` to allow for generating compatible conversion
|
||||
// functions.
|
||||
impl<'a> conrod_winit::WinitWindow for WindowRef<'a> {
|
||||
fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
winit::Window::get_inner_size(&self.0).map(Into::into)
|
||||
}
|
||||
fn hidpi_factor(&self) -> f32 {
|
||||
winit::Window::get_hidpi_factor(&self.0) as _
|
||||
}
|
||||
}
|
||||
convert_event!(event, &WindowRef(window.window())).map(|input| Self(input))
|
||||
}
|
||||
pub fn is_keyboard_or_mouse(&self) -> bool {
|
||||
match self.0 {
|
||||
Input::Press(_)
|
||||
| Input::Release(_)
|
||||
| Input::Motion(_)
|
||||
| Input::Touch(_)
|
||||
| Input::Text(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_keyboard(&self) -> bool {
|
||||
match self.0 {
|
||||
Input::Press(Button::Keyboard(_))
|
||||
| Input::Release(Button::Keyboard(_))
|
||||
| Input::Text(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn new_resize(dims: Vec2<f64>) -> Self {
|
||||
Self(Input::Resize(dims.x, dims.y))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Cache {
|
||||
glyph_cache: GlyphCache<'static>,
|
||||
glyph_cache_tex: Texture<UiPipeline>,
|
||||
graphic_cache: graphic::GraphicCache,
|
||||
graphic_cache_tex: Texture<UiPipeline>,
|
||||
}
|
||||
|
||||
// TODO: Should functions be returning UiError instead of Error?
|
||||
impl Cache {
|
||||
pub fn new(renderer: &mut Renderer) -> Result<Self, Error> {
|
||||
let (w, h) = renderer.get_resolution().into_tuple();
|
||||
const SCALE_TOLERANCE: f32 = 0.1;
|
||||
const POSITION_TOLERANCE: f32 = 0.1;
|
||||
|
||||
let graphic_cache_dims = Vec2::new(w * 4, h * 4);
|
||||
Ok(Self {
|
||||
glyph_cache: GlyphCache::builder()
|
||||
.dimensions(w as u32, h as u32)
|
||||
.scale_tolerance(SCALE_TOLERANCE)
|
||||
.position_tolerance(POSITION_TOLERANCE)
|
||||
.build(),
|
||||
glyph_cache_tex: renderer.create_dynamic_texture((w, h).into())?,
|
||||
graphic_cache: GraphicCache::new(graphic_cache_dims),
|
||||
graphic_cache_tex: renderer.create_dynamic_texture(graphic_cache_dims)?,
|
||||
})
|
||||
}
|
||||
pub fn glyph_cache_tex(&self) -> &Texture<UiPipeline> {
|
||||
&self.glyph_cache_tex
|
||||
}
|
||||
pub fn glyph_cache_mut_and_tex(&mut self) -> (&mut GlyphCache<'static>, &Texture<UiPipeline>) {
|
||||
(&mut self.glyph_cache, &self.glyph_cache_tex)
|
||||
}
|
||||
pub fn graphic_cache_tex(&self) -> &Texture<UiPipeline> {
|
||||
&self.graphic_cache_tex
|
||||
}
|
||||
pub fn graphic_cache_mut_and_tex(&mut self) -> (&mut GraphicCache, &Texture<UiPipeline>) {
|
||||
(&mut self.graphic_cache, &self.graphic_cache_tex)
|
||||
}
|
||||
pub fn add_graphic(&mut self, graphic: Graphic) -> GraphicId {
|
||||
self.graphic_cache.add_graphic(graphic)
|
||||
}
|
||||
pub fn clear_graphic_cache(&mut self, renderer: &mut Renderer, new_size: Vec2<u16>) {
|
||||
self.graphic_cache.clear_cache(new_size);
|
||||
self.graphic_cache_tex = renderer.create_dynamic_texture(new_size).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
enum DrawKind {
|
||||
Image,
|
||||
@ -136,90 +52,24 @@ enum DrawKind {
|
||||
Plain,
|
||||
}
|
||||
enum DrawCommand {
|
||||
Draw {
|
||||
kind: DrawKind,
|
||||
model: Model<UiPipeline>,
|
||||
},
|
||||
Draw { kind: DrawKind, verts: Range<usize> },
|
||||
Scissor(Aabr<u16>),
|
||||
}
|
||||
impl DrawCommand {
|
||||
fn image(model: Model<UiPipeline>) -> DrawCommand {
|
||||
fn image(verts: Range<usize>) -> DrawCommand {
|
||||
DrawCommand::Draw {
|
||||
kind: DrawKind::Image,
|
||||
model,
|
||||
verts,
|
||||
}
|
||||
}
|
||||
fn plain(model: Model<UiPipeline>) -> DrawCommand {
|
||||
fn plain(verts: Range<usize>) -> DrawCommand {
|
||||
DrawCommand::Draw {
|
||||
kind: DrawKind::Plain,
|
||||
model,
|
||||
verts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// How to scale the ui
|
||||
pub enum ScaleMode {
|
||||
// Scale against physical size
|
||||
Absolute(f64),
|
||||
// Use the dpi factor provided by the windowing system (i.e. use logical size)
|
||||
DpiFactor,
|
||||
// Scale based on the window's physical size, but maintain aspect ratio of widgets
|
||||
// Contains width and height of the "default" window size (ie where there should be no scaling)
|
||||
RelativeToWindow(Vec2<f64>),
|
||||
}
|
||||
|
||||
struct Scale {
|
||||
// Type of scaling to use
|
||||
mode: ScaleMode,
|
||||
// Current dpi factor
|
||||
dpi_factor: f64,
|
||||
// Current logical window size
|
||||
window_dims: Vec2<f64>,
|
||||
}
|
||||
|
||||
impl Scale {
|
||||
fn new(window: &Window, mode: ScaleMode) -> Self {
|
||||
let window_dims = window.logical_size();
|
||||
let dpi_factor = window.renderer().get_resolution().x as f64 / window_dims.x;
|
||||
Scale {
|
||||
mode,
|
||||
dpi_factor,
|
||||
window_dims,
|
||||
}
|
||||
}
|
||||
// Change the scaling mode
|
||||
pub fn scaling_mode(&mut self, mode: ScaleMode) {
|
||||
self.mode = mode;
|
||||
}
|
||||
// Calculate factor to transform between logical coordinates and our scaled coordinates
|
||||
fn scale_factor_logical(&self) -> f64 {
|
||||
match self.mode {
|
||||
ScaleMode::Absolute(scale) => scale / self.dpi_factor,
|
||||
ScaleMode::DpiFactor => 1.0,
|
||||
ScaleMode::RelativeToWindow(dims) => {
|
||||
(self.window_dims.x / dims.x).min(self.window_dims.y / dims.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Calculate factor to transform between physical coordinates and our scaled coordinates
|
||||
fn scale_factor_physical(&self) -> f64 {
|
||||
self.scale_factor_logical() * self.dpi_factor
|
||||
}
|
||||
// Updates internal window size (and/or dpi_factor)
|
||||
fn window_resized(&mut self, new_dims: Vec2<f64>, renderer: &Renderer) {
|
||||
self.dpi_factor = renderer.get_resolution().x as f64 / new_dims.x;
|
||||
self.window_dims = new_dims;
|
||||
}
|
||||
// Get scaled window size
|
||||
fn scaled_window_size(&self) -> Vec2<f64> {
|
||||
self.window_dims / self.scale_factor_logical()
|
||||
}
|
||||
// Transform point from logical to scaled coordinates
|
||||
fn scale_point(&self, point: Vec2<f64>) -> Vec2<f64> {
|
||||
point / self.scale_factor_logical()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Font(text::Font);
|
||||
impl assets::Asset for Font {
|
||||
fn load(specifier: &str) -> Result<Self, assets::Error> {
|
||||
@ -235,6 +85,8 @@ pub struct Ui {
|
||||
cache: Cache,
|
||||
// Draw commands for the next render
|
||||
draw_commands: Vec<DrawCommand>,
|
||||
// Model for drawing the ui
|
||||
model: DynamicModel<UiPipeline>,
|
||||
// Stores new window size for updating scaling
|
||||
window_resized: Option<Vec2<f64>>,
|
||||
// Scaling of the ui
|
||||
@ -250,8 +102,9 @@ impl Ui {
|
||||
ui: UiBuilder::new(win_dims).build(),
|
||||
image_map: Map::new(),
|
||||
cache: Cache::new(window.renderer_mut())?,
|
||||
window_resized: None,
|
||||
draw_commands: vec![],
|
||||
model: window.renderer_mut().create_dynamic_model(100)?,
|
||||
window_resized: None,
|
||||
scale,
|
||||
})
|
||||
}
|
||||
@ -268,7 +121,7 @@ impl Ui {
|
||||
self.image_map.insert(self.cache.add_graphic(graphic))
|
||||
}
|
||||
|
||||
pub fn new_font(&mut self, mut font: Arc<Font>) -> text::font::Id {
|
||||
pub fn new_font(&mut self, mut font: Arc<Font>) -> font::Id {
|
||||
self.ui.fonts.insert(font.as_ref().0.clone())
|
||||
}
|
||||
|
||||
@ -309,7 +162,9 @@ impl Ui {
|
||||
}
|
||||
pub fn handle_event(&mut self, event: Event) {
|
||||
match event.0 {
|
||||
Input::Resize(w, h) => self.window_resized = Some(Vec2::new(w, h)),
|
||||
Input::Resize(w, h) if w > 1.0 && h > 1.0 => {
|
||||
self.window_resized = Some(Vec2::new(w, h))
|
||||
}
|
||||
Input::Touch(touch) => self.ui.handle_event(Input::Touch(Touch {
|
||||
xy: self.scale.scale_point(touch.xy.into()).into_array(),
|
||||
..touch
|
||||
@ -338,9 +193,12 @@ impl Ui {
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer) {
|
||||
let ref mut ui = self.ui;
|
||||
// Regenerate draw commands and associated models only if the ui changed
|
||||
if let Some(mut primitives) = ui.draw_if_changed() {
|
||||
let mut primitives = match self.ui.draw_if_changed() {
|
||||
Some(primitives) => primitives,
|
||||
None => return,
|
||||
};
|
||||
|
||||
self.draw_commands.clear();
|
||||
let mut mesh = Mesh::new();
|
||||
|
||||
@ -352,6 +210,7 @@ impl Ui {
|
||||
};
|
||||
|
||||
let mut current_state = State::Plain;
|
||||
let mut start = 0;
|
||||
|
||||
let window_scizzor = default_scissor(renderer);
|
||||
let mut current_scizzor = window_scizzor;
|
||||
@ -362,9 +221,9 @@ impl Ui {
|
||||
() => {
|
||||
if let State::Image = current_state {
|
||||
self.draw_commands
|
||||
.push(DrawCommand::image(renderer.create_model(&mesh).unwrap()));
|
||||
mesh.clear();
|
||||
.push(DrawCommand::image(start..mesh.vertices().len()));
|
||||
current_state = State::Plain;
|
||||
start = mesh.vertices().len();
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -375,8 +234,8 @@ impl Ui {
|
||||
let Primitive {
|
||||
kind,
|
||||
scizzor,
|
||||
id: _id,
|
||||
rect,
|
||||
..
|
||||
} = prim;
|
||||
|
||||
// Check for a change in the scizzor
|
||||
@ -385,8 +244,8 @@ impl Ui {
|
||||
// Calculate minimum x and y coordinates while
|
||||
// - flipping y axis (from +up to +down)
|
||||
// - moving origin to top-left corner (from middle)
|
||||
let min_x = ui.win_w / 2.0 + l;
|
||||
let min_y = ui.win_h / 2.0 - b - h;
|
||||
let min_x = self.ui.win_w / 2.0 + l;
|
||||
let min_y = self.ui.win_h / 2.0 - b - h;
|
||||
Aabr {
|
||||
min: Vec2 {
|
||||
x: (min_x * p_scale_factor) as u16,
|
||||
@ -402,10 +261,10 @@ impl Ui {
|
||||
if new_scizzor != current_scizzor {
|
||||
// Finish the current command
|
||||
self.draw_commands.push(match current_state {
|
||||
State::Plain => DrawCommand::plain(renderer.create_model(&mesh).unwrap()),
|
||||
State::Image => DrawCommand::image(renderer.create_model(&mesh).unwrap()),
|
||||
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
|
||||
State::Image => DrawCommand::image(start..mesh.vertices().len()),
|
||||
});
|
||||
mesh.clear();
|
||||
start = mesh.vertices().len();
|
||||
|
||||
// Update the scizzor and produce a command.
|
||||
current_scizzor = new_scizzor;
|
||||
@ -413,8 +272,10 @@ impl Ui {
|
||||
}
|
||||
|
||||
// Functions for converting for conrod scalar coords to GL vertex coords (-1.0 to 1.0)
|
||||
let vx = |x: f64| (x / ui.win_w * 2.0) as f32;
|
||||
let vy = |y: f64| (y / ui.win_h * 2.0) as f32;
|
||||
let ui_win_w = self.ui.win_w;
|
||||
let ui_win_h = self.ui.win_h;
|
||||
let vx = |x: f64| (x / ui_win_w * 2.0) as f32;
|
||||
let vy = |y: f64| (y / ui_win_h * 2.0) as f32;
|
||||
let gl_aabr = |rect: conrod_core::Rect| {
|
||||
let (l, r, b, t) = rect.l_r_b_t();
|
||||
Aabr {
|
||||
@ -441,21 +302,20 @@ impl Ui {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Switch to the `Image` state for this image if we're not in it already.
|
||||
// Switch to the image state if we are not in it already
|
||||
if let State::Plain = current_state {
|
||||
self.draw_commands
|
||||
.push(DrawCommand::plain(renderer.create_model(&mesh).unwrap()));
|
||||
mesh.clear();
|
||||
.push(DrawCommand::plain(start..mesh.vertices().len()));
|
||||
start = mesh.vertices().len();
|
||||
current_state = State::Image;
|
||||
}
|
||||
|
||||
let color = srgb_to_linear(
|
||||
color.unwrap_or(conrod_core::color::WHITE).to_fsa().into(),
|
||||
);
|
||||
let color =
|
||||
srgb_to_linear(color.unwrap_or(conrod_core::color::WHITE).to_fsa().into());
|
||||
|
||||
let resolution = Vec2::new(
|
||||
(rect.w() * p_scale_factor) as u16,
|
||||
(rect.h() * p_scale_factor) as u16,
|
||||
(rect.w() * p_scale_factor).round() as u16,
|
||||
(rect.h() * p_scale_factor).round() as u16,
|
||||
);
|
||||
// Transform the source rectangle into uv coordinate
|
||||
// TODO: make sure this is right
|
||||
@ -502,12 +362,7 @@ impl Ui {
|
||||
None => continue,
|
||||
};
|
||||
|
||||
mesh.push_quad(create_ui_quad(
|
||||
gl_aabr(rect),
|
||||
uv_aabr,
|
||||
color,
|
||||
UiMode::Image,
|
||||
));
|
||||
mesh.push_quad(create_ui_quad(gl_aabr(rect), uv_aabr, color, UiMode::Image));
|
||||
}
|
||||
PrimitiveKind::Text {
|
||||
color,
|
||||
@ -519,7 +374,7 @@ impl Ui {
|
||||
let (screen_w, screen_h) =
|
||||
renderer.get_resolution().map(|e| e as f32).into_tuple();
|
||||
// Calculate dpi factor
|
||||
let dpi_factor = screen_w / ui.win_w as f32;
|
||||
let dpi_factor = screen_w / ui_win_w as f32;
|
||||
|
||||
let positioned_glyphs = text.positioned_glyphs(dpi_factor);
|
||||
let (glyph_cache, cache_tex) = self.cache.glyph_cache_mut_and_tex();
|
||||
@ -599,8 +454,7 @@ impl Ui {
|
||||
let p2 = Vec2::new(vx(tri[1][0]), vy(tri[1][1]));
|
||||
let p3 = Vec2::new(vx(tri[2][0]), vy(tri[2][1]));
|
||||
// If triangle is clockwise reverse it
|
||||
let (v1, v2): (Vec3<f32>, Vec3<f32>) =
|
||||
((p2 - p1).into(), (p3 - p1).into());
|
||||
let (v1, v2): (Vec3<f32>, Vec3<f32>) = ((p2 - p1).into(), (p3 - p1).into());
|
||||
let triangle = if v1.cross(v2).z > 0.0 {
|
||||
[p1.into_array(), p2.into_array(), p3.into_array()]
|
||||
} else {
|
||||
@ -622,10 +476,19 @@ impl Ui {
|
||||
}
|
||||
// Enter the final command
|
||||
self.draw_commands.push(match current_state {
|
||||
State::Plain => DrawCommand::plain(renderer.create_model(&mesh).unwrap()),
|
||||
State::Image => DrawCommand::image(renderer.create_model(&mesh).unwrap()),
|
||||
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
|
||||
State::Image => DrawCommand::image(start..mesh.vertices().len()),
|
||||
});
|
||||
|
||||
// create a larger dynamic model if the mesh is larger than the current model size
|
||||
if self.model.vbuf.len() < mesh.vertices().len() {
|
||||
self.model = renderer
|
||||
.create_dynamic_model(mesh.vertices().len() * 4 / 3)
|
||||
.unwrap();
|
||||
}
|
||||
renderer.update_model(&self.model, &mesh, 0).unwrap();
|
||||
// Update model with new mesh
|
||||
|
||||
// Handle window resizing
|
||||
if let Some(new_dims) = self.window_resized.take() {
|
||||
self.scale.window_resized(new_dims, renderer);
|
||||
@ -641,7 +504,6 @@ impl Ui {
|
||||
// TODO: probably need to resize glyph cache, see conrod's gfx backend for reference
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&self, renderer: &mut Renderer) {
|
||||
let mut scissor = default_scissor(renderer);
|
||||
@ -650,11 +512,12 @@ impl Ui {
|
||||
DrawCommand::Scissor(scizzor) => {
|
||||
scissor = *scizzor;
|
||||
}
|
||||
DrawCommand::Draw { kind, model } => {
|
||||
DrawCommand::Draw { kind, verts } => {
|
||||
let tex = match kind {
|
||||
DrawKind::Image => self.cache.graphic_cache_tex(),
|
||||
DrawKind::Plain => self.cache.glyph_cache_tex(),
|
||||
};
|
||||
let model = self.model.submodel(verts.clone());
|
||||
renderer.render_ui_element(&model, &tex, scissor);
|
||||
}
|
||||
}
|
||||
|
65
voxygen/src/ui/scale.rs
Normal file
65
voxygen/src/ui/scale.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use crate::{render::Renderer, window::Window};
|
||||
use vek::*;
|
||||
|
||||
// How the ui is scaled
|
||||
pub enum ScaleMode {
|
||||
// Scale against physical size
|
||||
Absolute(f64),
|
||||
// Use the dpi factor provided by the windowing system (i.e. use logical size)
|
||||
DpiFactor,
|
||||
// Scale based on the window's physical size, but maintain aspect ratio of widgets
|
||||
// Contains width and height of the "default" window size (ie where there should be no scaling)
|
||||
RelativeToWindow(Vec2<f64>),
|
||||
}
|
||||
|
||||
pub struct Scale {
|
||||
// Type of scaling to use
|
||||
mode: ScaleMode,
|
||||
// Current dpi factor
|
||||
dpi_factor: f64,
|
||||
// Current logical window size
|
||||
window_dims: Vec2<f64>,
|
||||
}
|
||||
|
||||
impl Scale {
|
||||
pub fn new(window: &Window, mode: ScaleMode) -> Self {
|
||||
let window_dims = window.logical_size();
|
||||
let dpi_factor = window.renderer().get_resolution().x as f64 / window_dims.x;
|
||||
Scale {
|
||||
mode,
|
||||
dpi_factor,
|
||||
window_dims,
|
||||
}
|
||||
}
|
||||
// Change the scaling mode
|
||||
pub fn scaling_mode(&mut self, mode: ScaleMode) {
|
||||
self.mode = mode;
|
||||
}
|
||||
// Calculate factor to transform between logical coordinates and our scaled coordinates
|
||||
pub fn scale_factor_logical(&self) -> f64 {
|
||||
match self.mode {
|
||||
ScaleMode::Absolute(scale) => scale / self.dpi_factor,
|
||||
ScaleMode::DpiFactor => 1.0,
|
||||
ScaleMode::RelativeToWindow(dims) => {
|
||||
(self.window_dims.x / dims.x).min(self.window_dims.y / dims.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Calculate factor to transform between physical coordinates and our scaled coordinates
|
||||
pub fn scale_factor_physical(&self) -> f64 {
|
||||
self.scale_factor_logical() * self.dpi_factor
|
||||
}
|
||||
// Updates internal window size (and/or dpi_factor)
|
||||
pub fn window_resized(&mut self, new_dims: Vec2<f64>, renderer: &Renderer) {
|
||||
self.dpi_factor = renderer.get_resolution().x as f64 / new_dims.x;
|
||||
self.window_dims = new_dims;
|
||||
}
|
||||
// Get scaled window size
|
||||
pub fn scaled_window_size(&self) -> Vec2<f64> {
|
||||
self.window_dims / self.scale_factor_logical()
|
||||
}
|
||||
// Transform point from logical to scaled coordinates
|
||||
pub fn scale_point(&self, point: Vec2<f64>) -> Vec2<f64> {
|
||||
point / self.scale_factor_logical()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user