seeker + make slider nodes red
This commit is contained in:
parent
a21bdbcafe
commit
dad5f77816
4 changed files with 190 additions and 118 deletions
|
@ -69,6 +69,14 @@ impl Sound {
|
||||||
self.handle
|
self.handle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> Result<f64> {
|
||||||
|
let length = unsafe {
|
||||||
|
let bytes = bass::BASS_ChannelGetLength(self.handle, BASS_POS_BYTE);
|
||||||
|
bass::BASS_ChannelBytes2Seconds(self.handle, bytes)
|
||||||
|
};
|
||||||
|
Ok(length)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn position(&self) -> Result<f64> {
|
pub fn position(&self) -> Result<f64> {
|
||||||
let time = unsafe {
|
let time = unsafe {
|
||||||
let pos_bytes = bass::BASS_ChannelGetPosition(self.handle, BASS_POS_BYTE);
|
let pos_bytes = bass::BASS_ChannelGetPosition(self.handle, BASS_POS_BYTE);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
mod grid;
|
mod grid;
|
||||||
|
mod seeker;
|
||||||
mod sliders;
|
mod sliders;
|
||||||
mod timeline;
|
mod timeline;
|
||||||
|
|
||||||
|
@ -25,10 +26,7 @@ use crate::beatmap::{BeatmapExt, STACK_DISTANCE};
|
||||||
use crate::hitobject::HitObjectExt;
|
use crate::hitobject::HitObjectExt;
|
||||||
use crate::skin::Skin;
|
use crate::skin::Skin;
|
||||||
|
|
||||||
use self::sliders::render_slider;
|
|
||||||
|
|
||||||
pub const PLAYFIELD_BOUNDS: Rect = Rect::new(112.0, 112.0, 800.0, 600.0);
|
pub const PLAYFIELD_BOUNDS: Rect = Rect::new(112.0, 112.0, 800.0, 600.0);
|
||||||
pub const SEEKER_BOUNDS: Rect = Rect::new(46.0, 722.0, 932.0, 36.0);
|
|
||||||
|
|
||||||
pub type SliderCache = HashMap<Vec<Point<i32>>, Spline>;
|
pub type SliderCache = HashMap<Vec<Point<i32>>, Spline>;
|
||||||
|
|
||||||
|
@ -185,17 +183,22 @@ impl Game {
|
||||||
|
|
||||||
let mut slider_info = None;
|
let mut slider_info = None;
|
||||||
if let HitObjectKind::Slider(info) = &ho.inner.kind {
|
if let HitObjectKind::Slider(info) = &ho.inner.kind {
|
||||||
|
let mut control_points = vec![ho.inner.pos];
|
||||||
|
control_points.extend(&info.control_points);
|
||||||
|
|
||||||
let mut color = color.clone();
|
let mut color = color.clone();
|
||||||
color.a = 0.6 * draw_info.opacity as f32;
|
color.a = 0.6 * draw_info.opacity as f32;
|
||||||
let spline = render_slider(
|
let spline = Game::render_slider(
|
||||||
&mut self.slider_cache,
|
&mut self.slider_cache,
|
||||||
|
info,
|
||||||
|
control_points.as_ref(),
|
||||||
ctx,
|
ctx,
|
||||||
PLAYFIELD_BOUNDS,
|
PLAYFIELD_BOUNDS,
|
||||||
&self.beatmap.inner,
|
&self.beatmap.inner,
|
||||||
&ho.inner,
|
&ho.inner,
|
||||||
color,
|
color,
|
||||||
)?;
|
)?;
|
||||||
slider_info = Some((info, spline));
|
slider_info = Some((info, control_points, spline));
|
||||||
|
|
||||||
let end_pos = ho.inner.end_pos().unwrap();
|
let end_pos = ho.inner.end_pos().unwrap();
|
||||||
let end_pos = [
|
let end_pos = [
|
||||||
|
@ -225,7 +228,9 @@ impl Game {
|
||||||
DrawParam::default().dest(pos),
|
DrawParam::default().dest(pos),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some((info, spline)) = slider_info {
|
if let Some((info, control_points, spline)) = slider_info {
|
||||||
|
Game::render_slider_wireframe(ctx, &control_points, PLAYFIELD_BOUNDS)?;
|
||||||
|
|
||||||
if time > ho_time && time < draw_info.end_time {
|
if time > ho_time && time < draw_info.end_time {
|
||||||
let elapsed_time = time - ho_time;
|
let elapsed_time = time - ho_time;
|
||||||
let total_duration = draw_info.end_time - ho_time;
|
let total_duration = draw_info.end_time - ho_time;
|
||||||
|
@ -263,6 +268,8 @@ impl Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.draw_seeker(ctx)?;
|
||||||
|
|
||||||
graphics::present(ctx)?;
|
graphics::present(ctx)?;
|
||||||
self.frame += 1;
|
self.frame += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
42
src/game/seeker.rs
Normal file
42
src/game/seeker.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use ggez::{
|
||||||
|
graphics::{self, DrawParam, Mesh, Rect},
|
||||||
|
nalgebra::Point2,
|
||||||
|
Context,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::Game;
|
||||||
|
|
||||||
|
pub const BOUNDS: Rect = Rect::new(46.0, 722.0, 932.0, 36.0);
|
||||||
|
|
||||||
|
impl Game {
|
||||||
|
pub(super) fn draw_seeker(&self, ctx: &mut Context) -> Result<()> {
|
||||||
|
let line_y = BOUNDS.y + BOUNDS.h / 2.0;
|
||||||
|
let line = Mesh::new_line(
|
||||||
|
ctx,
|
||||||
|
&[
|
||||||
|
Point2::new(BOUNDS.x, line_y),
|
||||||
|
Point2::new(BOUNDS.x + BOUNDS.w, line_y),
|
||||||
|
],
|
||||||
|
1.0,
|
||||||
|
graphics::WHITE,
|
||||||
|
)?;
|
||||||
|
graphics::draw(ctx, &line, DrawParam::default())?;
|
||||||
|
|
||||||
|
if let Some(song) = &self.song {
|
||||||
|
let percent = song.position()? / song.length()?;
|
||||||
|
let x = BOUNDS.x + percent as f32 * BOUNDS.w;
|
||||||
|
let line = Mesh::new_line(
|
||||||
|
ctx,
|
||||||
|
&[
|
||||||
|
Point2::new(x, BOUNDS.y + 0.2 * BOUNDS.h),
|
||||||
|
Point2::new(x, BOUNDS.y + 0.8 * BOUNDS.h),
|
||||||
|
],
|
||||||
|
4.0,
|
||||||
|
graphics::WHITE,
|
||||||
|
)?;
|
||||||
|
graphics::draw(ctx, &line, DrawParam::default())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ggez::{
|
use ggez::{
|
||||||
conf::NumSamples,
|
|
||||||
graphics::{
|
graphics::{
|
||||||
self, Canvas, Color, DrawMode, DrawParam, FillOptions, LineCap, LineJoin, Mesh, Rect,
|
self, Canvas, Color, DrawMode, DrawParam, FillOptions, LineCap, LineJoin, Mesh, Rect,
|
||||||
StrokeOptions,
|
StrokeOptions,
|
||||||
|
@ -10,127 +9,143 @@ use ggez::{
|
||||||
};
|
};
|
||||||
use libosu::{
|
use libosu::{
|
||||||
beatmap::Beatmap,
|
beatmap::Beatmap,
|
||||||
hitobject::{HitObject, HitObjectKind},
|
hitobject::{HitObject, SliderInfo},
|
||||||
|
math::Point,
|
||||||
spline::Spline,
|
spline::Spline,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::game::SliderCache;
|
use super::{Game, SliderCache};
|
||||||
|
|
||||||
pub fn render_slider<'a>(
|
impl Game {
|
||||||
slider_cache: &'a mut SliderCache,
|
pub fn render_slider<'a>(
|
||||||
ctx: &mut Context,
|
slider_cache: &'a mut SliderCache,
|
||||||
rect: Rect,
|
slider_info: &SliderInfo,
|
||||||
beatmap: &Beatmap,
|
control_points: &[Point<i32>],
|
||||||
slider: &HitObject,
|
ctx: &mut Context,
|
||||||
color: Color,
|
rect: Rect,
|
||||||
) -> Result<&'a Spline> {
|
beatmap: &Beatmap,
|
||||||
let mut control_points = vec![slider.pos];
|
slider: &HitObject,
|
||||||
let slider_info = match &slider.kind {
|
color: Color,
|
||||||
HitObjectKind::Slider(info) => info,
|
) -> Result<&'a Spline> {
|
||||||
_ => unreachable!("retard"),
|
let spline = if slider_cache.contains_key(control_points) {
|
||||||
};
|
slider_cache.get(control_points).unwrap()
|
||||||
control_points.extend(&slider_info.control_points);
|
} else {
|
||||||
|
let new_spline =
|
||||||
|
Spline::from_control(slider_info.kind, control_points, slider_info.pixel_length);
|
||||||
|
slider_cache.insert(control_points.to_vec(), new_spline);
|
||||||
|
slider_cache.get(control_points).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
let spline = if slider_cache.contains_key(&control_points) {
|
let cs_scale = rect.w / 640.0;
|
||||||
slider_cache.get(&control_points).unwrap()
|
let osupx_scale_x = rect.w as f64 / 512.0;
|
||||||
} else {
|
let osupx_scale_y = rect.h as f64 / 384.0;
|
||||||
let new_spline =
|
let cs_osupx = beatmap.difficulty.circle_size_osupx() as f64;
|
||||||
Spline::from_control(slider_info.kind, &control_points, slider_info.pixel_length);
|
let cs_real = cs_osupx * cs_scale as f64;
|
||||||
slider_cache.insert(control_points.clone(), new_spline);
|
|
||||||
slider_cache.get(&control_points).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let cs_scale = rect.w / 640.0;
|
let (mut boundx, mut boundy, mut boundw, mut boundh) = (f64::MAX, f64::MAX, 0.0f64, 0.0f64);
|
||||||
let osupx_scale_x = rect.w as f64 / 512.0;
|
let spline_mapped = spline
|
||||||
let osupx_scale_y = rect.h as f64 / 384.0;
|
.spline_points
|
||||||
let cs_osupx = beatmap.difficulty.circle_size_osupx() as f64;
|
.iter()
|
||||||
let cs_real = cs_osupx * cs_scale as f64;
|
.map(|point| {
|
||||||
|
let (x, y) = (point.0, point.1);
|
||||||
|
let x2 = rect.x as f64 + osupx_scale_x * x;
|
||||||
|
let y2 = rect.y as f64 + osupx_scale_y * y;
|
||||||
|
boundx = boundx.min(x2 - cs_osupx);
|
||||||
|
boundy = boundy.min(y2 - cs_osupx);
|
||||||
|
boundw = boundw.max(x2 + cs_osupx - boundx);
|
||||||
|
boundh = boundh.max(y2 + cs_osupx - boundy);
|
||||||
|
[x2 as f32, y2 as f32].into()
|
||||||
|
})
|
||||||
|
.collect::<Vec<Point2<f32>>>();
|
||||||
|
|
||||||
let points_mapped = control_points
|
// draw slider border
|
||||||
.iter()
|
let canvas = Canvas::with_window_size(ctx)?;
|
||||||
.map(|point| {
|
let opts = StrokeOptions::default()
|
||||||
let (x, y) = (point.0 as f64, point.1 as f64);
|
.with_line_cap(LineCap::Round)
|
||||||
let x2 = rect.x as f64 + osupx_scale_x * x;
|
.with_line_join(LineJoin::Round)
|
||||||
let y2 = rect.y as f64 + osupx_scale_y * y;
|
.with_line_width(cs_real as f32 * 2.0);
|
||||||
[x2 as f32, y2 as f32].into()
|
let body = Mesh::new_polyline(
|
||||||
})
|
|
||||||
.collect::<Vec<Point2<_>>>();
|
|
||||||
|
|
||||||
let (mut boundx, mut boundy, mut boundw, mut boundh) = (f64::MAX, f64::MAX, 0.0f64, 0.0f64);
|
|
||||||
let spline_mapped = spline
|
|
||||||
.spline_points
|
|
||||||
.iter()
|
|
||||||
.map(|point| {
|
|
||||||
let (x, y) = (point.0, point.1);
|
|
||||||
let x2 = rect.x as f64 + osupx_scale_x * x;
|
|
||||||
let y2 = rect.y as f64 + osupx_scale_y * y;
|
|
||||||
boundx = boundx.min(x2 - cs_osupx);
|
|
||||||
boundy = boundy.min(y2 - cs_osupx);
|
|
||||||
boundw = boundw.max(x2 + cs_osupx - boundx);
|
|
||||||
boundh = boundh.max(y2 + cs_osupx - boundy);
|
|
||||||
[x2 as f32, y2 as f32].into()
|
|
||||||
})
|
|
||||||
.collect::<Vec<Point2<f32>>>();
|
|
||||||
|
|
||||||
// draw slider border
|
|
||||||
let canvas = Canvas::with_window_size(ctx)?;
|
|
||||||
let opts = StrokeOptions::default()
|
|
||||||
.with_line_cap(LineCap::Round)
|
|
||||||
.with_line_join(LineJoin::Round)
|
|
||||||
.with_line_width(cs_real as f32 * 2.0);
|
|
||||||
let body = Mesh::new_polyline(
|
|
||||||
ctx,
|
|
||||||
DrawMode::Stroke(opts),
|
|
||||||
spline_mapped.as_ref(),
|
|
||||||
graphics::WHITE,
|
|
||||||
)?;
|
|
||||||
graphics::set_canvas(ctx, Some(&canvas));
|
|
||||||
graphics::clear(ctx, Color::new(0.0, 0.0, 0.0, 0.0));
|
|
||||||
graphics::draw(ctx, &body, DrawParam::default())?;
|
|
||||||
graphics::set_canvas(ctx, None);
|
|
||||||
let mut border_color = graphics::WHITE;
|
|
||||||
border_color.a = color.a;
|
|
||||||
graphics::draw(ctx, &canvas, DrawParam::default().color(border_color))?;
|
|
||||||
|
|
||||||
// draw slider body
|
|
||||||
let canvas = Canvas::with_window_size(ctx)?;
|
|
||||||
let opts = StrokeOptions::default()
|
|
||||||
.with_line_cap(LineCap::Round)
|
|
||||||
.with_line_join(LineJoin::Round)
|
|
||||||
.with_line_width(cs_real as f32 * 1.8);
|
|
||||||
let body = Mesh::new_polyline(
|
|
||||||
ctx,
|
|
||||||
DrawMode::Stroke(opts),
|
|
||||||
spline_mapped.as_ref(),
|
|
||||||
graphics::WHITE,
|
|
||||||
)?;
|
|
||||||
graphics::set_canvas(ctx, Some(&canvas));
|
|
||||||
graphics::clear(ctx, Color::new(0.0, 0.0, 0.0, 0.0));
|
|
||||||
graphics::draw(ctx, &body, DrawParam::default())?;
|
|
||||||
graphics::set_canvas(ctx, None);
|
|
||||||
graphics::draw(ctx, &canvas, DrawParam::default().color(color))?;
|
|
||||||
|
|
||||||
// draw control points wireframe
|
|
||||||
let frame = Mesh::new_polyline(
|
|
||||||
ctx,
|
|
||||||
DrawMode::Stroke(StrokeOptions::default()),
|
|
||||||
&points_mapped,
|
|
||||||
graphics::WHITE,
|
|
||||||
)?;
|
|
||||||
graphics::draw(ctx, &frame, DrawParam::default())?;
|
|
||||||
|
|
||||||
// draw points on wireframe
|
|
||||||
for point in points_mapped {
|
|
||||||
let size = 5.0;
|
|
||||||
let rect = Rect::new(point.x - size, point.y - size, size * 2.0, size * 2.0);
|
|
||||||
let rect = Mesh::new_rectangle(
|
|
||||||
ctx,
|
ctx,
|
||||||
DrawMode::Fill(FillOptions::default()),
|
DrawMode::Stroke(opts),
|
||||||
rect,
|
spline_mapped.as_ref(),
|
||||||
graphics::WHITE,
|
graphics::WHITE,
|
||||||
)?;
|
)?;
|
||||||
graphics::draw(ctx, &rect, DrawParam::default())?;
|
graphics::set_canvas(ctx, Some(&canvas));
|
||||||
|
graphics::clear(ctx, Color::new(0.0, 0.0, 0.0, 0.0));
|
||||||
|
graphics::draw(ctx, &body, DrawParam::default())?;
|
||||||
|
graphics::set_canvas(ctx, None);
|
||||||
|
let mut border_color = graphics::WHITE;
|
||||||
|
border_color.a = color.a;
|
||||||
|
graphics::draw(ctx, &canvas, DrawParam::default().color(border_color))?;
|
||||||
|
|
||||||
|
// draw slider body
|
||||||
|
let canvas = Canvas::with_window_size(ctx)?;
|
||||||
|
let opts = StrokeOptions::default()
|
||||||
|
.with_line_cap(LineCap::Round)
|
||||||
|
.with_line_join(LineJoin::Round)
|
||||||
|
.with_line_width(cs_real as f32 * 1.8);
|
||||||
|
let body = Mesh::new_polyline(
|
||||||
|
ctx,
|
||||||
|
DrawMode::Stroke(opts),
|
||||||
|
spline_mapped.as_ref(),
|
||||||
|
graphics::WHITE,
|
||||||
|
)?;
|
||||||
|
graphics::set_canvas(ctx, Some(&canvas));
|
||||||
|
graphics::clear(ctx, Color::new(0.0, 0.0, 0.0, 0.0));
|
||||||
|
graphics::draw(ctx, &body, DrawParam::default())?;
|
||||||
|
graphics::set_canvas(ctx, None);
|
||||||
|
graphics::draw(ctx, &canvas, DrawParam::default().color(color))?;
|
||||||
|
|
||||||
|
Ok(spline)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(spline)
|
pub fn render_slider_wireframe(
|
||||||
|
ctx: &mut Context,
|
||||||
|
control_points: &[Point<i32>],
|
||||||
|
rect: Rect,
|
||||||
|
) -> Result<()> {
|
||||||
|
let osupx_scale_x = rect.w as f64 / 512.0;
|
||||||
|
let osupx_scale_y = rect.h as f64 / 384.0;
|
||||||
|
|
||||||
|
let points_mapped = control_points
|
||||||
|
.iter()
|
||||||
|
.map(|point| {
|
||||||
|
let (x, y) = (point.0 as f64, point.1 as f64);
|
||||||
|
let x2 = rect.x as f64 + osupx_scale_x * x;
|
||||||
|
let y2 = rect.y as f64 + osupx_scale_y * y;
|
||||||
|
[x2 as f32, y2 as f32].into()
|
||||||
|
})
|
||||||
|
.collect::<Vec<Point2<_>>>();
|
||||||
|
|
||||||
|
// draw control points wireframe
|
||||||
|
let frame = Mesh::new_polyline(
|
||||||
|
ctx,
|
||||||
|
DrawMode::Stroke(StrokeOptions::default()),
|
||||||
|
&points_mapped,
|
||||||
|
graphics::WHITE,
|
||||||
|
)?;
|
||||||
|
graphics::draw(ctx, &frame, DrawParam::default())?;
|
||||||
|
|
||||||
|
// draw points on wireframe
|
||||||
|
let mut i = 0;
|
||||||
|
while i < points_mapped.len() {
|
||||||
|
let fst = points_mapped[i];
|
||||||
|
let mut color = graphics::WHITE;
|
||||||
|
if i < points_mapped.len() - 1 {
|
||||||
|
let snd = points_mapped[i + 1];
|
||||||
|
if fst.eq(&snd) {
|
||||||
|
i += 1;
|
||||||
|
color = Color::new(1.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let size = 5.0;
|
||||||
|
let rect = Rect::new(fst.x - size, fst.y - size, size * 2.0, size * 2.0);
|
||||||
|
let rect =
|
||||||
|
Mesh::new_rectangle(ctx, DrawMode::Fill(FillOptions::default()), rect, color)?;
|
||||||
|
graphics::draw(ctx, &rect, DrawParam::default())?;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue