diff --git a/crates/gc-engine/src/main.rs b/crates/gc-engine/src/main.rs index 72b0480..77aa541 100644 --- a/crates/gc-engine/src/main.rs +++ b/crates/gc-engine/src/main.rs @@ -19,7 +19,7 @@ use std::path::PathBuf; use anyhow::{Context, Result}; use bevy::asset::RenderAssetUsages; -use bevy::image::{ImageFilterMode, ImageSampler, ImageSamplerDescriptor}; +use bevy::image::{ImageAddressMode, ImageFilterMode, ImageSampler, ImageSamplerDescriptor}; use bevy::prelude::*; use bevy::render::mesh::{Indices, PrimitiveTopology}; use bevy::render::render_resource::{Extent3d, TextureDimension, TextureFormat}; @@ -386,11 +386,20 @@ fn image_of(t: &gc_formats::texture::Texture) -> Image { TextureFormat::Rgba8UnormSrgb, RenderAssetUsages::default(), ); - // Only mip level 0 is decoded, so minification must clamp rather than - // invent a level the disc does not have here. + // The GS clamps; bevy's default is Repeat. This was `..default()` with a + // comment claiming the sampler clamped, which it did not -- the default is + // ImageAddressMode::Repeat. A tile edge therefore wrapped onto its opposite + // edge instead of repeating the border texel, which is the seam you see when + // a patch runs off its texture. See gc_formats::gs::clamp01. + // + // Only mip level 0 is decoded, so there is no chain to interpolate: the + // mipmap filter is Nearest because it samples that single level either way, + // and gs::mip_level would return 0 for every patch until levels are decoded. image.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor { label: Some("tfrag texture".into()), - mipmap_filter: ImageFilterMode::Linear, + address_mode_u: ImageAddressMode::ClampToEdge, + address_mode_v: ImageAddressMode::ClampToEdge, + mipmap_filter: ImageFilterMode::Nearest, mag_filter: ImageFilterMode::Linear, ..default() }); diff --git a/docs/roadmap.md b/docs/roadmap.md index ad7d32f..3f2f2e3 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -717,9 +717,11 @@ lookup, and it is the main thing inherited from ReRAC. the GS's `AFAIL` bit reads as "fail if above the reference" and on the hardware means the opposite -- it discards where alpha is **below**; and the opaque bit must be kept out of the comparison, or `0x80` satisfies every - threshold and nothing is ever discarded. **No wgpu work has started** -- - this is the rule layer the renderer will feed it, done first because it is - checkable without a GPU. + threshold and nothing is ever discarded. **The engine is on bevy 0.19, not raw wgpu**, so these rules reach the GPU + through bevy's sampler rather than wgpu directly. One is now wired: the + texture sampler was `..default()` with a comment claiming it clamped, and + bevy's default is `Repeat` -- see `image_of` in `crates/gc-engine`. + **No draw-path or shader work has started.** - [ ] VU0/VU1 skinning and lighting on the PS2 float model - [ ] LOD/morph/cull per `TfragProc` and `TieProc` - [ ] Occlusion culling, fog, shadows