diff --git a/Cargo.lock b/Cargo.lock index 114601c..380c904 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1132,7 +1132,7 @@ checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" [[package]] name = "libosu" version = "0.0.12" -source = "git+https://github.com/iptq/libosu?rev=5a8501aec0f4f2232507f80a65b8b31be8756414#5a8501aec0f4f2232507f80a65b8b31be8756414" +source = "git+https://github.com/iptq/libosu?rev=95a87c57e1075ffab473124d9564233028e79423#95a87c57e1075ffab473124d9564233028e79423" dependencies = [ "anyhow", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index edf9bf5..191b418 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,4 @@ num = "0.3.1" [dependencies.libosu] git = "https://github.com/iptq/libosu" -rev = "5a8501aec0f4f2232507f80a65b8b31be8756414" +rev = "95a87c57e1075ffab473124d9564233028e79423" diff --git a/bass-sys/src/lib.rs b/bass-sys/src/lib.rs index 7f41f6f..abe8bbc 100644 --- a/bass-sys/src/lib.rs +++ b/bass-sys/src/lib.rs @@ -17,7 +17,9 @@ extern_log! { pub fn BASS_ChannelPause(handle: DWORD) -> BOOL; pub fn BASS_ChannelGetLength(handle: DWORD, mode: DWORD) -> QWORD; pub fn BASS_ChannelGetPosition(handle: DWORD, mode: DWORD) -> QWORD; + pub fn BASS_ChannelSetPosition(handle: DWORD, pos: QWORD, mode: DWORD); pub fn BASS_ChannelBytes2Seconds(handle: DWORD, pos: QWORD) -> f64; + pub fn BASS_ChannelSeconds2Bytes(handle: DWORD, pos: f64) -> QWORD; pub fn BASS_GetDevice() -> DWORD; pub fn BASS_GetDeviceInfo(device: DWORD, info: *mut BASS_DEVICEINFO) -> BOOL; diff --git a/src/audio.rs b/src/audio.rs index 87ffbb3..70b088c 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -76,4 +76,12 @@ impl Sound { }; Ok(time) } + + pub fn set_position(&self, pos: f64) -> Result<()> { + unsafe { + let pos_bytes = bass::BASS_ChannelSeconds2Bytes(self.handle, pos); + bass::BASS_ChannelSetPosition(self.handle, pos_bytes, BASS_POS_BYTE); + } + Ok(()) + } } diff --git a/src/game.rs b/src/game.rs index 695567e..90c183b 100644 --- a/src/game.rs +++ b/src/game.rs @@ -48,7 +48,10 @@ impl Game { self.beatmap = Beatmap::from_osz(&contents)?; let dir = path.parent().unwrap(); - self.song = Sound::create(dir.join(&self.beatmap.audio_filename)).map(Some)?; + + let song = Sound::create(dir.join(&self.beatmap.audio_filename))?; + song.set_position(113.0)?; + self.song = Some(song); Ok(()) } @@ -82,20 +85,40 @@ impl Game { graphics::queue_text(ctx, &text, [0.0, 0.0], Some(WHITE)); graphics::draw_queued_text(ctx, DrawParam::default(), None, FilterMode::Linear)?; + struct DrawInfo<'a> { + hit_object: &'a HitObject, + opacity: f64, + end_time: f64, + } + let mut visible_hitobjects = Vec::new(); - let approach_time = 0.75; + let preempt = (self.beatmap.difficulty.approach_preempt() as f64) / 1000.0; + let fade_in = (self.beatmap.difficulty.approach_fade_time() as f64) / 1000.0; for ho in self.beatmap.hit_objects.iter() { let ho_time = (ho.start_time.0 as f64) / 1000.0; - let end_time = match ho.kind { - HitObjectKind::Circle => ho_time, + let end_time; + let opacity = if time > ho_time - fade_in { + 1.0 + } else { + // TODO: calculate ease + (time - (ho_time - preempt)) / fade_in + }; + match ho.kind { + HitObjectKind::Circle => end_time = ho_time, HitObjectKind::Slider(_) => { let duration = self.beatmap.get_slider_duration(ho).unwrap(); - ho_time + duration / 1000.0 + end_time = ho_time + duration / 1000.0; } - HitObjectKind::Spinner(SpinnerInfo { end_time }) => (end_time.0 as f64) / 1000.0, + HitObjectKind::Spinner(SpinnerInfo { + end_time: spinner_end, + }) => end_time = (spinner_end.0 as f64) / 1000.0, }; - if ho_time - approach_time < time && time < end_time { - visible_hitobjects.push(ho); + if ho_time - preempt < time && time < end_time { + visible_hitobjects.push(DrawInfo { + hit_object: ho, + opacity, + end_time, + }); } } @@ -104,15 +127,18 @@ impl Game { let cs_osupx = self.beatmap.difficulty.circle_size_osupx(); let cs_real = cs_osupx * osupx_scale_x; - for ho in visible_hitobjects.iter() { + for draw_info in visible_hitobjects.iter() { + let ho = draw_info.hit_object; let ho_time = (ho.start_time.0 as f64) / 1000.0; let pos = [ EDITOR_SCREEN.x + osupx_scale_x * ho.pos.0 as f32, EDITOR_SCREEN.y + osupx_scale_y * ho.pos.1 as f32, ]; + let color = graphics::Color::new(1.0, 1.0, 1.0, draw_info.opacity as f32); if let HitObjectKind::Slider(_) = ho.kind { - render_slider(ctx, EDITOR_SCREEN, &self.beatmap, ho)?; + let color = graphics::Color::new(1.0, 1.0, 1.0, 0.6 * draw_info.opacity as f32); + render_slider(ctx, EDITOR_SCREEN, &self.beatmap, ho, color)?; } let circ = Mesh::new_circle( @@ -121,21 +147,23 @@ impl Game { pos, cs_real, 1.0, - WHITE, + color, )?; graphics::draw(ctx, &circ, DrawParam::default())?; - let time_diff = ho_time - time; - let approach_r = cs_real * (1.0 + 2.0 * time_diff as f32 / 0.75); - let approach = Mesh::new_circle( - ctx, - DrawMode::Stroke(StrokeOptions::default()), - pos, - approach_r, - 1.0, - WHITE, - )?; - graphics::draw(ctx, &approach, DrawParam::default())?; + if time < ho_time { + let time_diff = ho_time - time; + let approach_r = cs_real * (1.0 + 2.0 * time_diff as f32 / 0.75); + let approach = Mesh::new_circle( + ctx, + DrawMode::Stroke(StrokeOptions::default().with_line_width(2.0)), + pos, + approach_r, + 1.0, + WHITE, + )?; + graphics::draw(ctx, &approach, DrawParam::default())?; + } } graphics::present(ctx)?; diff --git a/src/slider_render.rs b/src/slider_render.rs index 32cf7d2..af4534b 100644 --- a/src/slider_render.rs +++ b/src/slider_render.rs @@ -17,6 +17,7 @@ pub fn render_slider( rect: Rect, beatmap: &Beatmap, slider: &HitObject, + color: Color, ) -> Result<()> { let mut control_points = vec![slider.pos]; let slider_info = match &slider.kind { @@ -62,12 +63,7 @@ pub fn render_slider( .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, - Color::new(1.0, 1.0, 1.0, 0.5), - )?; + let body = Mesh::new_polyline(ctx, DrawMode::Stroke(opts), &spline_mapped, color)?; graphics::draw(ctx, &body, DrawParam::default())?; let frame = Mesh::new_polyline( @@ -153,11 +149,12 @@ fn get_spline( c } SliderSplineKind::Bezier => { + // split the curve by red-anchors let mut idx = 0; let mut whole = Vec::new(); for i in 1..points.len() { if points[i].0 == points[i - 1].0 && points[i].1 == points[i - 1].1 { - let spline = calculate_bezier(&points[idx..i - 1]); + let spline = calculate_bezier(&points[idx..i]); whole.extend(spline); idx = i; continue;