mirror of
https://github.com/SK8-ENGINE/skate-3-rust-engine.git
synced 2026-10-26 19:47:44 +00:00
Render HUD at window resolution and restore authored clean glow
This commit is contained in:
1 parent
142fe97a6d
commit
d194ceff05
7 files changed
+123
-14
No files matched your search
@@ -61,6 +61,13 @@ fn main() -> Result<(), String> {
|
||||
runtime
|
||||
.update(input.clone(), new_trick, false, tick % 180 == 120)
|
||||
.map_err(|e| format!("Data frame {tick}: {e}"))?;
|
||||
if tick == 10 {
|
||||
if !runtime.vm.get(runtime.controller, "mLastClean").truth()
|
||||
|| runtime.vm.get(runtime.controller, "mLastSketchy").truth()
|
||||
{
|
||||
return Err("Clean landing selected the sketchy color timeline".into());
|
||||
}
|
||||
}
|
||||
let draws = apt_scene::draw(&runtime.bindings.movie, &runtime.vm, &shapes)?;
|
||||
max_draws = max_draws.max(draws.len());
|
||||
saw_native_shadow_pair |= draws.windows(2).any(|pair| {
|
||||
|
||||
@@ -36,6 +36,7 @@ fn frame(
|
||||
fakie: false,
|
||||
nollie: false,
|
||||
body_flip: false,
|
||||
suspend_air: false,
|
||||
landing: Default::default(),
|
||||
teleported: false,
|
||||
reverting: false,
|
||||
@@ -131,8 +132,23 @@ fn main() -> Result<(), String> {
|
||||
if expired.hud_input().sequence_score != 0 || expired.hud_input().line_time != 0. {
|
||||
return Err("Expired line retained its HUD score".into());
|
||||
}
|
||||
// Native Air452 suspends continuous air metrics without removing the carrier.
|
||||
let mut suspended = scoring_runtime::Runtime::load(&data)?;
|
||||
suspended.advance(frame(0, FilteredCategory::Ground, None))?;
|
||||
for tick in 1..61 {
|
||||
suspended.advance(frame(tick, FilteredCategory::Air, Some(kickflip)))?;
|
||||
}
|
||||
let held_score = suspended.hud_input().sequence_score;
|
||||
let mut freeze = frame(61, FilteredCategory::Air, Some(kickflip));
|
||||
freeze.suspend_air = true;
|
||||
freeze.position = [100., 100., 100.];
|
||||
freeze.body_flip = true;
|
||||
suspended.advance(freeze)?;
|
||||
if suspended.hud_input().sequence_score != held_score {
|
||||
return Err("Air452 suspension accrued distance/height reward".into());
|
||||
}
|
||||
println!(
|
||||
"Scoring data audit: authored kickflip credited once; landing display persists; timer uses seconds; line expiry clears score; teleport cancels pending rewards and multiplier"
|
||||
"Scoring data audit: authored kickflip credited once; landing display persists; timer uses seconds; line expiry clears score; teleport cancels pending rewards and multiplier; Air452 freezes continuous metrics"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -106,8 +106,10 @@ impl Host for Bindings {
|
||||
[
|
||||
Value::Number(self.input.sequence_score as f64),
|
||||
Value::Text(score(self.input.sequence_score)),
|
||||
Value::Bool(self.input.clean),
|
||||
// 825C2D90 exposes backend152/153; the shipped
|
||||
// UpdateTrickScoring reads [2] as sketchy, [3] as clean.
|
||||
Value::Bool(self.input.sketchy),
|
||||
Value::Bool(self.input.clean),
|
||||
Value::Number(self.input.sequence_timer as f64),
|
||||
Value::Number(self.input.line_score as f64),
|
||||
Value::Text(score(self.input.line_score)),
|
||||
|
||||
@@ -237,6 +237,7 @@ pub(super) fn advance(
|
||||
forward:deck_frame.basis.columns[2],
|
||||
switch:skater.animation.packet.riding_switch,fakie:skater.animation.packet.riding_fakie,
|
||||
nollie:skater.animation.packet.weight_forwards,body_flip:skater.player_input.physical.air.flag_441!=0,
|
||||
suspend_air:skater.player_input.physical.air.use_air_reckoning_452!=0,
|
||||
landing:skater.landing_quality,teleported,reverting:skater.player_state.state_flags[70-52],
|
||||
})?;
|
||||
super::climbing::approach::advance(physics, skater, controls);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use crate::{apt_scene, config::Config, hud_runtime, physics::SkaterRuntime};
|
||||
use bevy::{
|
||||
asset::{RenderAssetUsages, embedded_asset},
|
||||
camera::{RenderTarget, visibility::RenderLayers},
|
||||
camera::{RenderTarget, ScalingMode, visibility::RenderLayers},
|
||||
prelude::*,
|
||||
render::render_resource::{
|
||||
AsBindGroup, BlendState, Extent3d, PrimitiveTopology, RenderPipelineDescriptor, ShaderType,
|
||||
@@ -11,6 +11,7 @@ use bevy::{
|
||||
shader::ShaderRef,
|
||||
sprite_render::{AlphaMode2d, Material2d, Material2dPlugin, MeshMaterial2d},
|
||||
ui_render::UiMaterialPlugin,
|
||||
window::PrimaryWindow,
|
||||
};
|
||||
use std::{collections::BTreeMap, path::PathBuf};
|
||||
|
||||
@@ -62,6 +63,7 @@ struct Slot {
|
||||
}
|
||||
#[derive(Resource)]
|
||||
struct Hud {
|
||||
target: Handle<Image>,
|
||||
runtime: hud_runtime::Runtime,
|
||||
source: serde_json::Value,
|
||||
shapes: apt_scene::Shapes,
|
||||
@@ -91,7 +93,9 @@ impl Plugin for ScoringHudPlugin {
|
||||
)
|
||||
.add_systems(
|
||||
Update,
|
||||
(reset, render).chain().after(crate::app::FrameSet::Physics),
|
||||
(resize_target, reset, render)
|
||||
.chain()
|
||||
.after(crate::app::FrameSet::Physics),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -100,6 +104,7 @@ fn setup(
|
||||
config: Res<Config>,
|
||||
skater: Res<SkaterRuntime>,
|
||||
cameras: Query<Entity, With<IsDefaultUiCamera>>,
|
||||
window: Single<&Window, With<PrimaryWindow>>,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
mut composites: ResMut<Assets<HudComposite>>,
|
||||
) {
|
||||
@@ -151,6 +156,7 @@ fn setup(
|
||||
textures.insert(path, images.add(image));
|
||||
}
|
||||
Ok(Hud {
|
||||
target: Handle::default(),
|
||||
runtime,
|
||||
source,
|
||||
shapes,
|
||||
@@ -161,15 +167,23 @@ fn setup(
|
||||
})
|
||||
})();
|
||||
match result {
|
||||
Ok(hud) => {
|
||||
Ok(mut hud) => {
|
||||
let target = images.add(Image::new_target_texture(
|
||||
1280,
|
||||
720,
|
||||
window.physical_width().max(1),
|
||||
window.physical_height().max(1),
|
||||
TextureFormat::Rgba8UnormSrgb,
|
||||
None,
|
||||
));
|
||||
hud.target = target.clone();
|
||||
commands.spawn((
|
||||
Camera2d,
|
||||
Projection::Orthographic(OrthographicProjection {
|
||||
scaling_mode: ScalingMode::Fixed {
|
||||
width: 1280.,
|
||||
height: 720.,
|
||||
},
|
||||
..OrthographicProjection::default_2d()
|
||||
}),
|
||||
Camera {
|
||||
order: -1,
|
||||
clear_color: ClearColorConfig::Custom(Color::NONE),
|
||||
@@ -197,6 +211,28 @@ fn setup(
|
||||
Err(error) => error!("Original scoring HUD could not load: {error}"),
|
||||
}
|
||||
}
|
||||
// Rasterize at output pixel resolution; retain the original 1280x720 APT
|
||||
// coordinate space. This avoids a second enlargement of every glyph/glow.
|
||||
fn resize_target(
|
||||
hud: Option<Res<Hud>>,
|
||||
window: Single<&Window, With<PrimaryWindow>>,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
) {
|
||||
let Some(hud) = hud else {
|
||||
return;
|
||||
};
|
||||
let Some(image) = images.get_mut(&hud.target) else {
|
||||
return;
|
||||
};
|
||||
let size = Extent3d {
|
||||
width: window.physical_width().max(1),
|
||||
height: window.physical_height().max(1),
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
if image.texture_descriptor.size != size {
|
||||
image.resize(size);
|
||||
}
|
||||
}
|
||||
fn reset(
|
||||
hud: Option<ResMut<Hud>>,
|
||||
skater: Res<SkaterRuntime>,
|
||||
|
||||
@@ -36,6 +36,7 @@ pub(crate) struct Frame {
|
||||
pub fakie: bool,
|
||||
pub nollie: bool,
|
||||
pub body_flip: bool,
|
||||
pub suspend_air: bool,
|
||||
pub landing: skate_core::animation::landing_quality::Output,
|
||||
pub teleported: bool,
|
||||
pub reverting: bool,
|
||||
@@ -48,6 +49,7 @@ pub(crate) struct Runtime {
|
||||
held: [f32; 4],
|
||||
distance: [f32; 4],
|
||||
metric_rewards: [f32; 4],
|
||||
metric_started: [bool; 4],
|
||||
start: [f32; 3],
|
||||
previous: [f32; 3],
|
||||
previous_heading: f32,
|
||||
@@ -83,6 +85,7 @@ impl Runtime {
|
||||
held: [0.; 4],
|
||||
distance: [0.; 4],
|
||||
metric_rewards: [0.; 4],
|
||||
metric_started: [false; 4],
|
||||
start: [0.; 3],
|
||||
previous: [0.; 3],
|
||||
previous_heading: 0.,
|
||||
@@ -175,6 +178,7 @@ impl Runtime {
|
||||
}
|
||||
}
|
||||
if !keep_metric {
|
||||
self.metric_started[slot] = false;
|
||||
self.held[slot] = 0.;
|
||||
self.distance[slot] = 0.;
|
||||
self.metric_rewards[slot] = 0.;
|
||||
@@ -269,8 +273,16 @@ impl Runtime {
|
||||
self.air_repetition_set = true;
|
||||
}
|
||||
}
|
||||
if c.announced {
|
||||
self.held[slot] += f.dt;
|
||||
let distance_metric = self.collector == Collector::Grind
|
||||
|| self.collector == Collector::Ground && slot < 3;
|
||||
let was_active = self.metric_started[slot];
|
||||
self.metric_started[slot] = true;
|
||||
if c.announced && !(self.collector == Collector::Air && f.suspend_air) {
|
||||
// 82DB0588 starts a distance collector at time/distance zero.
|
||||
// Every active frame still updates its reference position.
|
||||
if !distance_metric || was_active {
|
||||
self.held[slot] += f.dt;
|
||||
}
|
||||
let previous_distance = self.distance[slot];
|
||||
let displacement =
|
||||
std::array::from_fn::<_, 3, _>(|i| f.position[i] - self.previous[i]);
|
||||
@@ -283,7 +295,7 @@ impl Runtime {
|
||||
} else {
|
||||
(displacement.iter().map(|x| x * x).sum::<f32>()).sqrt()
|
||||
};
|
||||
if delta > f32::from_bits(0x3ba3d70a) {
|
||||
if was_active && delta > f32::from_bits(0x3ba3d70a) {
|
||||
self.distance[slot] += delta;
|
||||
}
|
||||
let curve = match self.collector {
|
||||
@@ -409,7 +421,9 @@ impl Runtime {
|
||||
}
|
||||
}
|
||||
let mut ids = [None; 4];
|
||||
self.collector_ticks = self.collector_ticks.saturating_add(1);
|
||||
if !(self.collector == Collector::Air && f.suspend_air) {
|
||||
self.collector_ticks = self.collector_ticks.saturating_add(1);
|
||||
}
|
||||
match self.collector {
|
||||
Collector::Ground => {
|
||||
ids[0] = if f.flags & 0x80000000 != 0 {
|
||||
@@ -459,7 +473,7 @@ impl Runtime {
|
||||
for (slot, id) in ids.into_iter().enumerate() {
|
||||
self.carrier(slot, id, &f)?;
|
||||
}
|
||||
if self.collector == Collector::Air {
|
||||
if self.collector == Collector::Air && !f.suspend_air {
|
||||
if self.collector_ticks > 5 || f.flags & 0x01000000 != 0 {
|
||||
self.sequence_active = true;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ It is a usable integration for testing, **not a finished native-parity port**.
|
||||
|
||||
The HUD executes the owned trickdisplay ActionScript and timelines, including
|
||||
score, multiplier, line meter, stance and trick-name updates. It renders original
|
||||
shape atlases and font glyphs through a separate 1280x720 camera with inherited
|
||||
shape atlases and font glyphs through a separate camera with inherited
|
||||
multiplicative/additive colors. Movie objects are collected and mesh/material
|
||||
slots reused. Fixed updates pause with gameplay; map-generation changes recreate
|
||||
the movie even while paused. Missing assets or unsupported actions are logged.
|
||||
@@ -98,7 +98,8 @@ The copied build and launcher are in ignored `logs/scoring/build`. The launcher
|
||||
uses this worktree's HUD cache and the existing owned asset installation and
|
||||
starts paused. It checks required paths before running and retains failures on
|
||||
screen. The executable SHA256 is
|
||||
`9b3458045e7078dcd6daca3e5bea89d16298315b751b4cac4f82f8a6531cd7c0`.
|
||||
`cecfff20b647a56253f0aad3e9a37655143121a1ae8c883e7fc3dfc88e6e3de7`.
|
||||
The launcher now selects `skate3rust-hud-quality.exe`.
|
||||
|
||||
The missing-HUD startup defect is fixed: HUD setup now depends on presentation
|
||||
setup, so Bevy applies the deferred camera spawn before the HUD queries it.
|
||||
@@ -145,6 +146,38 @@ finite geometry (33 maximum batches). Release compilation passes. User
|
||||
screenshots guided the investigation but did not supply rendering constants;
|
||||
no game, reference executable or GPU capture was launched for validation.
|
||||
|
||||
## Output resolution and authored blue glow
|
||||
|
||||
The HUD target now uses the window's physical pixel dimensions and resizes with
|
||||
the window. Its orthographic projection retains the authored 1280x720 coordinate
|
||||
space. Previously the adapter rasterized at 720p and enlarged the finished image,
|
||||
adding blur on larger windows. This change improves rasterization without
|
||||
inventing higher-resolution source artwork.
|
||||
|
||||
A data-only inventory of `fedata.big`, `fedynamic.big`, `fetexture.big` and
|
||||
`miscboot.big` found byte-identical duplicate trickdisplay APT/CONST/GEO/RX2
|
||||
assets and 512x512 Futura font atlases. No higher-resolution variant was found
|
||||
in those banks. The inventory and hashes are retained in ignored
|
||||
`logs/scoring/hud-resolution-inventory.json`.
|
||||
|
||||
`GetGeneralInfo` had clean and sketchy reversed. The original
|
||||
`UpdateTrickScoring` action stream reads slot2 as sketchy (0199D2) and slot3
|
||||
as clean (019A30), matching backend bytes152/153 returned by `825C2D90`.
|
||||
Correcting those slots selects the authored clean color animation. The native
|
||||
FontPS/Font8PS programs contain texture/color modulation; the glow is supplied
|
||||
by the authored layers, not a replacement procedural effect.
|
||||
|
||||
Additional collector fixes follow `82DA8A70`/`82DA93D8`: published Air452
|
||||
suspends continuous air metrics and grab accrual while preserving the carrier.
|
||||
Distance collectors also start their first active frame at zero elapsed
|
||||
time/distance, following `82DB0588`.
|
||||
|
||||
Validation: the 1,800-frame supplied-data HUD audit asserts that clean input
|
||||
selects mLastClean rather than mLastSketchy; the scoring-flow audit verifies
|
||||
Air452 suspension in addition to landing lifetime, expiry and cancellation.
|
||||
Both pass, and the static-CRT release build succeeds. The game was not launched;
|
||||
pixel appearance and GPU resize behavior still require interactive verification.
|
||||
|
||||
## Native parity gaps
|
||||
|
||||
These are implementation gaps, not merely missing gameplay validation:
|
||||
|
||||
Reference in new issue
Block a user