mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Draw the elytra's wings from both sides in the inventory preview (fixes #7000)
This commit is contained in:
1 parent
b8b268e51d
commit
5c314bd4ab
1 file changed
+59
-8
@@ -779,7 +779,12 @@ impl Game<'_> {
|
||||
240 | 240 << 8,
|
||||
&mut verts,
|
||||
);
|
||||
// `WingsLayer` draws through `RenderTypes.armorCutoutNoCull`: the
|
||||
// wings' faces show from both sides (the elytra texture is empty on
|
||||
// the face that looks at the body).
|
||||
let wings_start = verts.len();
|
||||
self.player_elytra_geometry(&m, chest, camera, 1.0, 240 | 240 << 8, &mut verts);
|
||||
let wings = wings_start..verts.len();
|
||||
self.shoulder_parrots_geometry(&m, camera, 1.0, 240 | 240 << 8, &mut verts);
|
||||
self.held_item_geometry(&m, camera, 1.0, 240 | 240 << 8, false, &mut verts);
|
||||
self.head_item_geometry(&m, camera, 1.0, 240 | 240 << 8, false, &mut verts);
|
||||
@@ -796,7 +801,7 @@ impl Game<'_> {
|
||||
.as_ref()
|
||||
.filter(|p| p.fire_ticks > 0 && !p.spectator)
|
||||
.map(|p| (p.dimensions.width, p.dimensions.height));
|
||||
let quads = player_preview_quads(verts, gui, fire, &atlas.sprites);
|
||||
let quads = player_preview_quads_no_cull(verts, gui, fire, &atlas.sprites, wings);
|
||||
let start = batch.vertices.len();
|
||||
for (_, p, uv, color) in quads {
|
||||
batch.quad_3d(p, uv, color);
|
||||
@@ -1164,10 +1169,23 @@ fn entity_in_ui_shade(normal: glam::Vec3) -> f32 {
|
||||
}
|
||||
|
||||
pub(super) fn player_preview_quads(
|
||||
verts: Vec<horizon_client::mesher::Vertex>,
|
||||
gui: glam::Mat4,
|
||||
fire: Option<(f32, f32)>,
|
||||
sprites: &std::collections::HashMap<String, horizon_client::hud::Rect>,
|
||||
) -> Vec<(f32, [[f32; 3]; 4], [[f32; 2]; 4], u32)> {
|
||||
player_preview_quads_no_cull(verts, gui, fire, sprites, 0..0)
|
||||
}
|
||||
|
||||
/// `player_preview_quads` where the faces of the vertices in `no_cull` are
|
||||
/// kept when they look away (a `NoCull` render type); their light still comes
|
||||
/// from the face's own outward normal, as the vertex normal gives it.
|
||||
pub(super) fn player_preview_quads_no_cull(
|
||||
mut verts: Vec<horizon_client::mesher::Vertex>,
|
||||
gui: glam::Mat4,
|
||||
fire: Option<(f32, f32)>,
|
||||
sprites: &std::collections::HashMap<String, horizon_client::hud::Rect>,
|
||||
no_cull: std::ops::Range<usize>,
|
||||
) -> Vec<(f32, [[f32; 3]; 4], [[f32; 2]; 4], u32)> {
|
||||
let flame_start = verts.len();
|
||||
if let Some((width, height)) = fire {
|
||||
@@ -1188,7 +1206,8 @@ pub(super) fn player_preview_quads(
|
||||
std::array::from_fn(|i| gui.transform_point3(glam::Vec3::from_array(q[i].pos)));
|
||||
let n = (p[1] - p[0]).cross(p[2] - p[0]);
|
||||
// The winding gives inward normals: faces with n.z >= 0 point away.
|
||||
if n.z >= 0.0 {
|
||||
let back = n.z >= 0.0;
|
||||
if back && !no_cull.contains(&(index * 4)) {
|
||||
continue;
|
||||
}
|
||||
let n = n.normalize_or_zero();
|
||||
@@ -1201,12 +1220,13 @@ pub(super) fn player_preview_quads(
|
||||
let ch = |s: u32| ((((c >> s) & 255) as f32 * shade) as u32) << s;
|
||||
let color = (c & 0xFF00_0000) | ch(0) | ch(8) | ch(16);
|
||||
let depth = p.iter().map(|v| v.z).sum::<f32>() / 4.0;
|
||||
quads.push((
|
||||
depth,
|
||||
p.map(|v| v.to_array()),
|
||||
std::array::from_fn(|i| q[i].uv),
|
||||
color,
|
||||
));
|
||||
let (mut pos, mut uv) = (p.map(|v| v.to_array()), std::array::from_fn(|i| q[i].uv));
|
||||
if back {
|
||||
// Seen from behind: the same quad in the opposite winding.
|
||||
pos.reverse();
|
||||
uv.reverse();
|
||||
}
|
||||
quads.push((depth, pos, uv, color));
|
||||
}
|
||||
// Keep far-to-near order for translucent faces; opaque overlap uses depth.
|
||||
quads.sort_by(|a, b| a.0.total_cmp(&b.0));
|
||||
@@ -1382,6 +1402,37 @@ mod preview_tests {
|
||||
assert_eq!(batch.runs(), vec![(0..12, Blend::Depth([1, 0, 1, 1]))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_cull_faces_stay_in_the_preview_when_they_look_away() {
|
||||
use horizon_client::mesher::Vertex;
|
||||
// The wings' `armorCutoutNoCull`: a face turned from the viewer is
|
||||
// dropped by the preview's culling unless its vertices are no-cull.
|
||||
let face = |z: f32| {
|
||||
[[-1.0, -1.0], [-1.0, 1.0], [1.0, 1.0], [1.0, -1.0]].map(|[x, y]| Vertex {
|
||||
pos: [x, y, z],
|
||||
uv: [x, y],
|
||||
layer: 0,
|
||||
color: u32::MAX,
|
||||
light: 0,
|
||||
normal: [0; 4],
|
||||
glint_uv: [0.0, 0.0],
|
||||
})
|
||||
};
|
||||
// Wound one way a face looks at the viewer, the other way it looks away.
|
||||
let mut away = face(0.0);
|
||||
away.reverse();
|
||||
let verts: Vec<_> = face(0.0).into_iter().chain(away).collect();
|
||||
let sprites = Default::default();
|
||||
let gui = glam::Mat4::IDENTITY;
|
||||
let culled = player_preview_quads(verts.clone(), gui, None, &sprites);
|
||||
assert_eq!(culled.len(), 1);
|
||||
let kept = player_preview_quads_no_cull(verts.clone(), gui, None, &sprites, 4..8);
|
||||
assert_eq!(kept.len(), 2);
|
||||
// The first face is outside the range and still culled when it looks away.
|
||||
let first_away = player_preview_quads_no_cull(verts, gui, None, &sprites, 0..0);
|
||||
assert_eq!(first_away.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_inventory_preview_is_lit_as_entity_in_ui() {
|
||||
// A face looking at the viewer, up, down, to the right and to the left.
|
||||
|
||||
Reference in new issue
Block a user