LivingEntityRenderer.java

net.minecraft.client.renderer.entity.LivingEntityRenderer

信息

  • 全限定名:net.minecraft.client.renderer.entity.LivingEntityRenderer
  • 类型:public abstract class
  • 包:net.minecraft.client.renderer.entity
  • 源码路径:src/main/java/net/minecraft/client/renderer/entity/LivingEntityRenderer.java
  • 起始行号:L40
  • 继承:EntityRenderer<T,S>
  • 实现:RenderLayerParent<S,M>
  • 职责:

    TODO

字段/常量

  • EYE_BED_OFFSET

    • 类型: float
    • 修饰符: private static final
    • 源码定位: L43
    • 说明:

      TODO

  • model

    • 类型: M
    • 修饰符: protected
    • 源码定位: L44
    • 说明:

      TODO

  • itemModelResolver

    • 类型: ItemModelResolver
    • 修饰符: protected final
    • 源码定位: L45
    • 说明:

      TODO

  • layers

    • 类型: List<RenderLayer<S,M>>
    • 修饰符: protected final
    • 源码定位: L46
    • 说明:

      TODO

内部类/嵌套类型

构造器

public LivingEntityRenderer(EntityRendererProvider.Context context, M model, float shadow) @ L48

  • 构造器名:LivingEntityRenderer
  • 源码定位:L48
  • 修饰符:public

参数:

  • context: EntityRendererProvider.Context
  • model: M
  • shadow: float

说明:

TODO

方法

下面的方法块按源码顺序生成。

protected final boolean addLayer(RenderLayer<S,M> layer) @ L55

  • 方法名:addLayer
  • 源码定位:L55
  • 返回类型:boolean
  • 修饰符:protected final

参数:

  • layer: RenderLayer<S,M>

说明:

TODO

public M getModel() @ L59

  • 方法名:getModel
  • 源码定位:L59
  • 返回类型:M
  • 修饰符:public

参数:

说明:

TODO

protected AABB getBoundingBoxForCulling(T entity) @ L64

  • 方法名:getBoundingBoxForCulling
  • 源码定位:L64
  • 返回类型:AABB
  • 修饰符:protected

参数:

  • entity: T

说明:

TODO

public void submit(S state, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, CameraRenderState camera) @ L74

  • 方法名:submit
  • 源码定位:L74
  • 返回类型:void
  • 修饰符:public

参数:

  • state: S
  • poseStack: PoseStack
  • submitNodeCollector: SubmitNodeCollector
  • camera: CameraRenderState

说明:

TODO

protected boolean shouldRenderLayers(S state) @ L114

  • 方法名:shouldRenderLayers
  • 源码定位:L114
  • 返回类型:boolean
  • 修饰符:protected

参数:

  • state: S

说明:

TODO

protected int getModelTint(S state) @ L118

  • 方法名:getModelTint
  • 源码定位:L118
  • 返回类型:int
  • 修饰符:protected

参数:

  • state: S

说明:

TODO

public abstract Identifier getTextureLocation(S state) @ L122

  • 方法名:getTextureLocation
  • 源码定位:L122
  • 返回类型:Identifier
  • 修饰符:public abstract

参数:

  • state: S

说明:

TODO

protected RenderType getRenderType(S state, boolean isBodyVisible, boolean forceTransparent, boolean appearGlowing) @ L124

  • 方法名:getRenderType
  • 源码定位:L124
  • 返回类型:RenderType
  • 修饰符:protected

参数:

  • state: S
  • isBodyVisible: boolean
  • forceTransparent: boolean
  • appearGlowing: boolean

说明:

TODO

public static int getOverlayCoords(LivingEntityRenderState state, float whiteOverlayProgress) @ L135

  • 方法名:getOverlayCoords
  • 源码定位:L135
  • 返回类型:int
  • 修饰符:public static

参数:

  • state: LivingEntityRenderState
  • whiteOverlayProgress: float

说明:

TODO

protected boolean isBodyVisible(S state) @ L139

  • 方法名:isBodyVisible
  • 源码定位:L139
  • 返回类型:boolean
  • 修饰符:protected

参数:

  • state: S

说明:

TODO

private static float sleepDirectionToRotation(Direction direction) @ L143

  • 方法名:sleepDirectionToRotation
  • 源码定位:L143
  • 返回类型:float
  • 修饰符:private static

参数:

  • direction: Direction

说明:

TODO

protected boolean isShaking(S state) @ L158

  • 方法名:isShaking
  • 源码定位:L158
  • 返回类型:boolean
  • 修饰符:protected

参数:

  • state: S

说明:

TODO

protected void setupRotations(S state, PoseStack poseStack, float bodyRot, float entityScale) @ L162

  • 方法名:setupRotations
  • 源码定位:L162
  • 返回类型:void
  • 修饰符:protected

参数:

  • state: S
  • poseStack: PoseStack
  • bodyRot: float
  • entityScale: float

说明:

TODO

protected float getFlipDegrees() @ L194

  • 方法名:getFlipDegrees
  • 源码定位:L194
  • 返回类型:float
  • 修饰符:protected

参数:

说明:

TODO

protected float getWhiteOverlayProgress(S state) @ L198

  • 方法名:getWhiteOverlayProgress
  • 源码定位:L198
  • 返回类型:float
  • 修饰符:protected

参数:

  • state: S

说明:

TODO

protected void scale(S state, PoseStack poseStack) @ L202

  • 方法名:scale
  • 源码定位:L202
  • 返回类型:void
  • 修饰符:protected

参数:

  • state: S
  • poseStack: PoseStack

说明:

TODO

protected boolean shouldShowName(T entity, double distanceToCameraSq) @ L205

  • 方法名:shouldShowName
  • 源码定位:L205
  • 返回类型:boolean
  • 修饰符:protected

参数:

  • entity: T
  • distanceToCameraSq: double

说明:

TODO

public boolean isEntityUpsideDown(T mob) @ L239

  • 方法名:isEntityUpsideDown
  • 源码定位:L239
  • 返回类型:boolean
  • 修饰符:public

参数:

  • mob: T

说明:

TODO

protected static boolean isUpsideDownName(String name) @ L244

  • 方法名:isUpsideDownName
  • 源码定位:L244
  • 返回类型:boolean
  • 修饰符:protected static

参数:

  • name: String

说明:

TODO

protected float getShadowRadius(S state) @ L248

  • 方法名:getShadowRadius
  • 源码定位:L248
  • 返回类型:float
  • 修饰符:protected

参数:

  • state: S

说明:

TODO

public void extractRenderState(T entity, S state, float partialTicks) @ L252

  • 方法名:extractRenderState
  • 源码定位:L252
  • 返回类型:void
  • 修饰符:public

参数:

  • entity: T
  • state: S
  • partialTicks: float

说明:

TODO

private static float solveBodyRot(LivingEntity entity, float headRot, float partialTicks) @ L312

  • 方法名:solveBodyRot
  • 源码定位:L312
  • 返回类型:float
  • 修饰符:private static

参数:

  • entity: LivingEntity
  • headRot: float
  • partialTicks: float

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public abstract class LivingEntityRenderer<T extends LivingEntity, S extends LivingEntityRenderState, M extends EntityModel<? super S>>
    extends EntityRenderer<T, S>
    implements RenderLayerParent<S, M> {
    private static final float EYE_BED_OFFSET = 0.1F;
    protected M model;
    protected final ItemModelResolver itemModelResolver;
    protected final List<RenderLayer<S, M>> layers = Lists.newArrayList();
 
    public LivingEntityRenderer(EntityRendererProvider.Context context, M model, float shadow) {
        super(context);
        this.itemModelResolver = context.getItemModelResolver();
        this.model = model;
        this.shadowRadius = shadow;
    }
 
    protected final boolean addLayer(RenderLayer<S, M> layer) {
        return this.layers.add(layer);
    }
 
    @Override
    public M getModel() {
        return this.model;
    }
 
    protected AABB getBoundingBoxForCulling(T entity) {
        AABB aabb = super.getBoundingBoxForCulling(entity);
        if (entity.getItemBySlot(EquipmentSlot.HEAD).is(Items.DRAGON_HEAD)) {
            float extraSize = 0.5F;
            return aabb.inflate(0.5, 0.5, 0.5);
        } else {
            return aabb;
        }
    }
 
    public void submit(S state, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, CameraRenderState camera) {
        poseStack.pushPose();
        if (state.hasPose(Pose.SLEEPING)) {
            Direction bedOrientation = state.bedOrientation;
            if (bedOrientation != null) {
                float headOffset = state.eyeHeight - 0.1F;
                poseStack.translate(-bedOrientation.getStepX() * headOffset, 0.0F, -bedOrientation.getStepZ() * headOffset);
            }
        }
 
        float scale = state.scale;
        poseStack.scale(scale, scale, scale);
        this.setupRotations(state, poseStack, state.bodyRot, scale);
        poseStack.scale(-1.0F, -1.0F, 1.0F);
        this.scale(state, poseStack);
        poseStack.translate(0.0F, -1.501F, 0.0F);
        boolean isBodyVisible = this.isBodyVisible(state);
        boolean forceTransparent = !isBodyVisible && !state.isInvisibleToPlayer;
        RenderType renderType = this.getRenderType(state, isBodyVisible, forceTransparent, state.appearsGlowing());
        if (renderType != null) {
            int overlayCoords = getOverlayCoords(state, this.getWhiteOverlayProgress(state));
            int baseColor = forceTransparent ? 654311423 : -1;
            int tintedColor = ARGB.multiply(baseColor, this.getModelTint(state));
            submitNodeCollector.submitModel(
                this.model, state, poseStack, renderType, state.lightCoords, overlayCoords, tintedColor, null, state.outlineColor, null
            );
        }
 
        if (this.shouldRenderLayers(state) && !this.layers.isEmpty()) {
            this.model.setupAnim(state);
 
            for (RenderLayer<S, M> layer : this.layers) {
                layer.submit(poseStack, submitNodeCollector, state.lightCoords, state, state.yRot, state.xRot);
            }
        }
 
        poseStack.popPose();
        super.submit(state, poseStack, submitNodeCollector, camera);
    }
 
    protected boolean shouldRenderLayers(S state) {
        return true;
    }
 
    protected int getModelTint(S state) {
        return -1;
    }
 
    public abstract Identifier getTextureLocation(final S state);
 
    protected @Nullable RenderType getRenderType(S state, boolean isBodyVisible, boolean forceTransparent, boolean appearGlowing) {
        Identifier texture = this.getTextureLocation(state);
        if (forceTransparent) {
            return RenderTypes.entityTranslucentCullItemTarget(texture);
        } else if (isBodyVisible) {
            return this.model.renderType(texture);
        } else {
            return appearGlowing ? RenderTypes.outline(texture) : null;
        }
    }
 
    public static int getOverlayCoords(LivingEntityRenderState state, float whiteOverlayProgress) {
        return OverlayTexture.pack(OverlayTexture.u(whiteOverlayProgress), OverlayTexture.v(state.hasRedOverlay));
    }
 
    protected boolean isBodyVisible(S state) {
        return !state.isInvisible;
    }
 
    private static float sleepDirectionToRotation(Direction direction) {
        switch (direction) {
            case SOUTH:
                return 90.0F;
            case WEST:
                return 0.0F;
            case NORTH:
                return 270.0F;
            case EAST:
                return 180.0F;
            default:
                return 0.0F;
        }
    }
 
    protected boolean isShaking(S state) {
        return state.isFullyFrozen;
    }
 
    protected void setupRotations(S state, PoseStack poseStack, float bodyRot, float entityScale) {
        if (this.isShaking(state)) {
            bodyRot += (float)(Math.cos(Mth.floor(state.ageInTicks) * 3.25F) * Math.PI * 0.4F);
        }
 
        if (!state.hasPose(Pose.SLEEPING)) {
            poseStack.mulPose(Axis.YP.rotationDegrees(180.0F - bodyRot));
        }
 
        if (state.deathTime > 0.0F) {
            float fall = (state.deathTime - 1.0F) / 20.0F * 1.6F;
            fall = Mth.sqrt(fall);
            if (fall > 1.0F) {
                fall = 1.0F;
            }
 
            poseStack.mulPose(Axis.ZP.rotationDegrees(fall * this.getFlipDegrees()));
        } else if (state.isAutoSpinAttack) {
            poseStack.mulPose(Axis.XP.rotationDegrees(-90.0F - state.xRot));
            poseStack.mulPose(Axis.YP.rotationDegrees(state.ageInTicks * -75.0F));
        } else if (state.hasPose(Pose.SLEEPING)) {
            Direction bedOrientation = state.bedOrientation;
            float angle = bedOrientation != null ? sleepDirectionToRotation(bedOrientation) : bodyRot;
            poseStack.mulPose(Axis.YP.rotationDegrees(angle));
            poseStack.mulPose(Axis.ZP.rotationDegrees(this.getFlipDegrees()));
            poseStack.mulPose(Axis.YP.rotationDegrees(270.0F));
        } else if (state.isUpsideDown) {
            poseStack.translate(0.0F, (state.boundingBoxHeight + 0.1F) / entityScale, 0.0F);
            poseStack.mulPose(Axis.ZP.rotationDegrees(180.0F));
        }
    }
 
    protected float getFlipDegrees() {
        return 90.0F;
    }
 
    protected float getWhiteOverlayProgress(S state) {
        return 0.0F;
    }
 
    protected void scale(S state, PoseStack poseStack) {
    }
 
    protected boolean shouldShowName(T entity, double distanceToCameraSq) {
        if (entity.isDiscrete()) {
            float maxDist = 32.0F;
            if (distanceToCameraSq >= 1024.0) {
                return false;
            }
        }
 
        Minecraft minecraft = Minecraft.getInstance();
        LocalPlayer player = minecraft.player;
        boolean isVisibleToPlayer = !entity.isInvisibleTo(player);
        if (entity != player) {
            Team team = entity.getTeam();
            Team myTeam = player.getTeam();
            if (team != null) {
                Team.Visibility visibility = team.getNameTagVisibility();
                switch (visibility) {
                    case ALWAYS:
                        return isVisibleToPlayer;
                    case NEVER:
                        return false;
                    case HIDE_FOR_OTHER_TEAMS:
                        return myTeam == null ? isVisibleToPlayer : team.isAlliedTo(myTeam) && (team.canSeeFriendlyInvisibles() || isVisibleToPlayer);
                    case HIDE_FOR_OWN_TEAM:
                        return myTeam == null ? isVisibleToPlayer : !team.isAlliedTo(myTeam) && isVisibleToPlayer;
                    default:
                        return true;
                }
            }
        }
 
        return Minecraft.renderNames() && entity != minecraft.getCameraEntity() && isVisibleToPlayer && !entity.isVehicle();
    }
 
    public boolean isEntityUpsideDown(T mob) {
        Component customName = mob.getCustomName();
        return customName != null && isUpsideDownName(customName.getString());
    }
 
    protected static boolean isUpsideDownName(String name) {
        return "Dinnerbone".equals(name) || "Grumm".equals(name);
    }
 
    protected float getShadowRadius(S state) {
        return super.getShadowRadius(state) * state.scale;
    }
 
    public void extractRenderState(T entity, S state, float partialTicks) {
        super.extractRenderState(entity, state, partialTicks);
        float headRot = Mth.rotLerp(partialTicks, entity.yHeadRotO, entity.yHeadRot);
        state.bodyRot = solveBodyRot(entity, headRot, partialTicks);
        state.yRot = Mth.wrapDegrees(headRot - state.bodyRot);
        state.xRot = entity.getXRot(partialTicks);
        state.isUpsideDown = this.isEntityUpsideDown(entity);
        if (state.isUpsideDown) {
            state.xRot *= -1.0F;
            state.yRot *= -1.0F;
        }
 
        if (!entity.isPassenger() && entity.isAlive()) {
            state.walkAnimationPos = entity.walkAnimation.position(partialTicks);
            state.walkAnimationSpeed = entity.walkAnimation.speed(partialTicks);
        } else {
            state.walkAnimationPos = 0.0F;
            state.walkAnimationSpeed = 0.0F;
        }
 
        if (entity.getVehicle() instanceof LivingEntity vehicle) {
            state.wornHeadAnimationPos = vehicle.walkAnimation.position(partialTicks);
        } else {
            state.wornHeadAnimationPos = state.walkAnimationPos;
        }
 
        state.scale = entity.getScale();
        state.ageScale = entity.getAgeScale();
        state.pose = entity.getPose();
        state.bedOrientation = entity.getBedOrientation();
        if (state.bedOrientation != null) {
            state.eyeHeight = entity.getEyeHeight(Pose.STANDING);
        }
 
        state.isFullyFrozen = entity.isFullyFrozen();
        state.isBaby = entity.isBaby();
        state.isInWater = entity.isInWater();
        state.isAutoSpinAttack = entity.isAutoSpinAttack();
        state.ticksSinceKineticHitFeedback = entity.getTicksSinceLastKineticHitFeedback(partialTicks);
        state.hasRedOverlay = entity.hurtTime > 0 || entity.deathTime > 0;
        ItemStack headItem = entity.getItemBySlot(EquipmentSlot.HEAD);
        if (headItem.getItem() instanceof BlockItem blockItem && blockItem.getBlock() instanceof AbstractSkullBlock skullBlock) {
            state.wornHeadType = skullBlock.getType();
            state.wornHeadProfile = headItem.get(DataComponents.PROFILE);
            state.headItem.clear();
        } else {
            state.wornHeadType = null;
            state.wornHeadProfile = null;
            if (!HumanoidArmorLayer.shouldRender(headItem, EquipmentSlot.HEAD)) {
                this.itemModelResolver.updateForLiving(state.headItem, headItem, ItemDisplayContext.HEAD, entity);
            } else {
                state.headItem.clear();
            }
        }
 
        state.deathTime = entity.deathTime > 0 ? entity.deathTime + partialTicks : 0.0F;
        Minecraft minecraft = Minecraft.getInstance();
        state.isInvisibleToPlayer = state.isInvisible && entity.isInvisibleTo(minecraft.player);
    }
 
    private static float solveBodyRot(LivingEntity entity, float headRot, float partialTicks) {
        if (entity.getVehicle() instanceof LivingEntity riding) {
            float bodyRot = Mth.rotLerp(partialTicks, riding.yBodyRotO, riding.yBodyRot);
            float maxHeadDiff = 85.0F;
            float headDiff = Mth.clamp(Mth.wrapDegrees(headRot - bodyRot), -85.0F, 85.0F);
            bodyRot = headRot - headDiff;
            if (Math.abs(headDiff) > 50.0F) {
                bodyRot += headDiff * 0.2F;
            }
 
            return bodyRot;
        } else {
            return Mth.rotLerp(partialTicks, entity.yBodyRotO, entity.yBodyRot);
        }
    }
}

引用的其他类