Draw no layers on a spectating player (armour, held items, head item, cape, wings)

This commit is contained in:
DUzzL committed 2026-11-02 11:07:31 +01:00
1 parent a04ae40674
commit 3f79ea0d00
4 files changed
+85 -48

No files matched your search

+8 -4
View File
@@ -6449,7 +6449,9 @@ impl Game<'_> {
v.layer |= u32::from(tier) << 16;
}
}
if is_player {
// AvatarRenderer.shouldRenderLayers: a spectator draws no layers.
let layers = horizon_client::mobs::render::layers_shown(mob);
if is_player && layers {
// PlayerRenderer's CapeLayer, WingsLayer, ParrotOnShoulderLayer
// and SpinAttackEffectLayer. Only the cape hides with the body;
// the others have no `isInvisible` check.
@@ -6470,9 +6472,11 @@ impl Game<'_> {
self.stuck_in_body_geometry(mob, mob.id as i32, camera, alpha as f32, packed, out);
}
// CustomHeadLayer (a pumpkin, skull or head item) included.
self.mob_block_geometry(mob, camera, alpha, packed, out);
// `native_mob_layer`: a native mod's layer, over the vanilla ones.
self.mod_mob_layer_geometry(mob, who, alpha as f32, camera, packed, out);
if layers {
self.mob_block_geometry(mob, camera, alpha, packed, out);
// `native_mob_layer`: a native mod's layer, over the vanilla ones.
self.mod_mob_layer_geometry(mob, who, alpha as f32, camera, packed, out);
}
if let Some(rgb) = outline {
self.mark_outline(&out[entity_start..], rgb);
if mob.invisible {
+26 -23
View File
@@ -3021,30 +3021,33 @@ impl Game<'_> {
),
out,
);
self.player_elytra_geometry(
m,
m.extra.chest.as_deref(),
camera,
alpha as f32,
packed,
out,
);
if let Some(g) = host.hub.guest(*conn) {
self.player_ears_geometry(m, &g.name, camera, alpha as f32, packed, out);
// AvatarRenderer.shouldRenderLayers: a spectator draws no layers.
if horizon_client::mobs::render::layers_shown(m) {
self.player_elytra_geometry(
m,
m.extra.chest.as_deref(),
camera,
alpha as f32,
packed,
out,
);
if let Some(g) = host.hub.guest(*conn) {
self.player_ears_geometry(m, &g.name, camera, alpha as f32, packed, out);
}
self.spin_attack_geometry(m, camera, alpha as f32, packed, out);
if let Some(g) = host.hub.guest(*conn) {
self.stuck_in_body_geometry(m, g.entity_id, camera, alpha as f32, packed, out);
}
self.held_item_geometry(m, camera, alpha, packed, true, out);
self.player_pumpkin_geometry(
m,
host.hub.guest(*conn).and_then(|g| g.armor[0].as_ref()),
camera,
alpha,
packed,
out,
);
}
self.spin_attack_geometry(m, camera, alpha as f32, packed, out);
if let Some(g) = host.hub.guest(*conn) {
self.stuck_in_body_geometry(m, g.entity_id, camera, alpha as f32, packed, out);
}
self.held_item_geometry(m, camera, alpha, packed, true, out);
self.player_pumpkin_geometry(
m,
host.hub.guest(*conn).and_then(|g| g.armor[0].as_ref()),
camera,
alpha,
packed,
out,
);
// LivingEntity.isCurrentlyGlowing: the guest's Glowing effect.
let guest = host.hub.guest(*conn);
if m.glowing || guest.is_some_and(|g| g.effects.get("glowing").is_some()) {
+43 -21
View File
@@ -659,28 +659,31 @@ impl Game<'_> {
&self.player_armor_layers(),
out,
);
let chest = self.armor[1].as_ref().map(|s| &*s.item);
// CapeLayer: no cape on an invisible player (`isInvisible`, whoever looks).
if !self.own_invisible() {
self.player_cape_geometry(m, chest, camera, alpha as f32, packed, out);
// AvatarRenderer.shouldRenderLayers: a spectator draws no layers.
if horizon_client::mobs::render::layers_shown(m) {
let chest = self.armor[1].as_ref().map(|s| &*s.item);
// CapeLayer: no cape on an invisible player (`isInvisible`, whoever looks).
if !self.own_invisible() {
self.player_cape_geometry(m, chest, camera, alpha as f32, packed, out);
}
self.player_ears_geometry(
m,
&self.own_profile_name(),
camera,
alpha as f32,
packed,
out,
);
self.player_elytra_geometry(m, chest, camera, alpha as f32, packed, out);
self.spin_attack_geometry(m, camera, alpha as f32, packed, out);
self.shoulder_parrots_geometry(m, camera, alpha as f32, packed, out);
self.stuck_in_body_geometry(m, self.own_entity_id(), camera, alpha as f32, packed, out);
self.held_item_geometry(m, camera, alpha, packed, true, out);
// CustomHeadLayer: a pumpkin, skull or other head item.
self.head_item_geometry(m, camera, alpha, packed, true, out);
// `native_mob_layer`: a native mod's layer, over the vanilla ones.
self.mod_mob_layer_geometry(m, Some(who), alpha as f32, camera, packed, out);
}
self.player_ears_geometry(
m,
&self.own_profile_name(),
camera,
alpha as f32,
packed,
out,
);
self.player_elytra_geometry(m, chest, camera, alpha as f32, packed, out);
self.spin_attack_geometry(m, camera, alpha as f32, packed, out);
self.shoulder_parrots_geometry(m, camera, alpha as f32, packed, out);
self.stuck_in_body_geometry(m, self.own_entity_id(), camera, alpha as f32, packed, out);
self.held_item_geometry(m, camera, alpha, packed, true, out);
// CustomHeadLayer: a pumpkin, skull or other head item.
self.head_item_geometry(m, camera, alpha, packed, true, out);
// `native_mob_layer`: a native mod's layer, over the vanilla ones.
self.mod_mob_layer_geometry(m, Some(who), alpha as f32, camera, packed, out);
// LivingEntity.isCurrentlyGlowing: the Glowing effect.
if self.potions.effects.get("glowing").is_some() {
let color = super::glowing::team_color(self.own_team());
@@ -1499,6 +1502,25 @@ mod preview_tests {
assert!(!hidden.is_empty(), "the armour still draws");
assert!(hidden.iter().all(|v| v.uv[0] >= 0.5), "only the armour");
assert_eq!(hidden.len() * 2, shown.len());
// AvatarRenderer.shouldRenderLayers: a spectator has no armour layer.
let mut ghost = m.clone();
ghost.extra.spectator = true;
assert!(!horizon_client::mobs::render::layers_shown(&ghost));
let mut with_armour = Vec::new();
let mut without = Vec::new();
for (armour, out) in [(&armour[..], &mut with_armour), (&[][..], &mut without)] {
horizon_client::mobs::render::render_layers(
&ghost,
1.0,
glam::DVec3::ZERO,
&atlas,
0xF0F0,
&data,
armour,
out,
);
}
assert!(without.is_empty() && with_armour.is_empty(), "no armour");
}
#[test]
+8
View File
@@ -197,6 +197,12 @@ fn climate(m: &Mob) -> &'static str {
fn avatar(m: &Mob) -> bool {
matches!(&*m.kind.id, "minecraft:player" | "minecraft:mannequin")
}
/// `AvatarRenderer.shouldRenderLayers`: a spectating player or mannequin
/// draws no layers at all (armour, held items, head item, cape, wings,
/// parrots, ears, stuck arrows), only its translucent head.
pub fn layers_shown(m: &Mob) -> bool {
!(avatar(m) && m.extra.spectator)
}
/// The model layer key and texture for the mob's body.
pub fn body(m: &Mob) -> (String, String) {
use Species as S;
@@ -1796,6 +1802,8 @@ fn render_visible(
out: &mut Vec<Vertex>,
skin: Option<&mut super::skin::Skin>,
) {
// The armour is a layer: none for a spectator.
let extra = if layers_shown(m) { extra } else { &[] };
let overlay = u32::from(red_overlay(m));
let s = anim::state(m, alpha);
let (model_key, texture) = body(m);