390 lines
14 KiB
Java
390 lines
14 KiB
Java
package net.minecraft.client.renderer.entity;
|
|
|
|
import com.google.common.collect.ImmutableList.Builder;
|
|
import com.mojang.blaze3d.vertex.PoseStack;
|
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
|
import net.fabricmc.api.EnvType;
|
|
import net.fabricmc.api.Environment;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.gui.Font;
|
|
import net.minecraft.client.gui.Font.DisplayMode;
|
|
import net.minecraft.client.renderer.LightTexture;
|
|
import net.minecraft.client.renderer.MultiBufferSource;
|
|
import net.minecraft.client.renderer.RenderType;
|
|
import net.minecraft.client.renderer.culling.Frustum;
|
|
import net.minecraft.client.renderer.entity.EntityRendererProvider.Context;
|
|
import net.minecraft.client.renderer.entity.state.EntityRenderState;
|
|
import net.minecraft.client.renderer.entity.state.HitboxRenderState;
|
|
import net.minecraft.client.renderer.entity.state.HitboxesRenderState;
|
|
import net.minecraft.client.server.IntegratedServer;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntityAttachment;
|
|
import net.minecraft.world.entity.Leashable;
|
|
import net.minecraft.world.entity.vehicle.AbstractMinecart;
|
|
import net.minecraft.world.entity.vehicle.NewMinecartBehavior;
|
|
import net.minecraft.world.level.LightLayer;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.joml.Matrix4f;
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public abstract class EntityRenderer<T extends Entity, S extends EntityRenderState> {
|
|
protected static final float NAMETAG_SCALE = 0.025F;
|
|
public static final int LEASH_RENDER_STEPS = 24;
|
|
protected final EntityRenderDispatcher entityRenderDispatcher;
|
|
private final Font font;
|
|
protected float shadowRadius;
|
|
protected float shadowStrength = 1.0F;
|
|
private final S reusedState = this.createRenderState();
|
|
|
|
protected EntityRenderer(Context context) {
|
|
this.entityRenderDispatcher = context.getEntityRenderDispatcher();
|
|
this.font = context.getFont();
|
|
}
|
|
|
|
public final int getPackedLightCoords(T entity, float partialTicks) {
|
|
BlockPos blockPos = BlockPos.containing(entity.getLightProbePosition(partialTicks));
|
|
return LightTexture.pack(this.getBlockLightLevel(entity, blockPos), this.getSkyLightLevel(entity, blockPos));
|
|
}
|
|
|
|
protected int getSkyLightLevel(T entity, BlockPos pos) {
|
|
return entity.level().getBrightness(LightLayer.SKY, pos);
|
|
}
|
|
|
|
protected int getBlockLightLevel(T entity, BlockPos pos) {
|
|
return entity.isOnFire() ? 15 : entity.level().getBrightness(LightLayer.BLOCK, pos);
|
|
}
|
|
|
|
public boolean shouldRender(T livingEntity, Frustum camera, double camX, double camY, double camZ) {
|
|
if (!livingEntity.shouldRender(camX, camY, camZ)) {
|
|
return false;
|
|
} else if (!this.affectedByCulling(livingEntity)) {
|
|
return true;
|
|
} else {
|
|
AABB aABB = this.getBoundingBoxForCulling(livingEntity).inflate(0.5);
|
|
if (aABB.hasNaN() || aABB.getSize() == 0.0) {
|
|
aABB = new AABB(
|
|
livingEntity.getX() - 2.0,
|
|
livingEntity.getY() - 2.0,
|
|
livingEntity.getZ() - 2.0,
|
|
livingEntity.getX() + 2.0,
|
|
livingEntity.getY() + 2.0,
|
|
livingEntity.getZ() + 2.0
|
|
);
|
|
}
|
|
|
|
if (camera.isVisible(aABB)) {
|
|
return true;
|
|
} else {
|
|
if (livingEntity instanceof Leashable leashable) {
|
|
Entity entity = leashable.getLeashHolder();
|
|
if (entity != null) {
|
|
return camera.isVisible(this.entityRenderDispatcher.getRenderer(entity).getBoundingBoxForCulling(entity));
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected AABB getBoundingBoxForCulling(T minecraft) {
|
|
return minecraft.getBoundingBox();
|
|
}
|
|
|
|
protected boolean affectedByCulling(T display) {
|
|
return true;
|
|
}
|
|
|
|
public Vec3 getRenderOffset(S renderState) {
|
|
return renderState.passengerOffset != null ? renderState.passengerOffset : Vec3.ZERO;
|
|
}
|
|
|
|
public void render(S renderState, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
|
|
EntityRenderState.LeashState leashState = renderState.leashState;
|
|
if (leashState != null) {
|
|
renderLeash(poseStack, bufferSource, leashState);
|
|
}
|
|
|
|
if (renderState.nameTag != null) {
|
|
this.renderNameTag(renderState, renderState.nameTag, poseStack, bufferSource, packedLight);
|
|
}
|
|
}
|
|
|
|
private static void renderLeash(PoseStack poseStack, MultiBufferSource buffer, EntityRenderState.LeashState leashState) {
|
|
float f = 0.025F;
|
|
float g = (float)(leashState.end.x - leashState.start.x);
|
|
float h = (float)(leashState.end.y - leashState.start.y);
|
|
float i = (float)(leashState.end.z - leashState.start.z);
|
|
float j = Mth.invSqrt(g * g + i * i) * 0.025F / 2.0F;
|
|
float k = i * j;
|
|
float l = g * j;
|
|
poseStack.pushPose();
|
|
poseStack.translate(leashState.offset);
|
|
VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.leash());
|
|
Matrix4f matrix4f = poseStack.last().pose();
|
|
|
|
for (int m = 0; m <= 24; m++) {
|
|
addVertexPair(
|
|
vertexConsumer,
|
|
matrix4f,
|
|
g,
|
|
h,
|
|
i,
|
|
leashState.startBlockLight,
|
|
leashState.endBlockLight,
|
|
leashState.startSkyLight,
|
|
leashState.endSkyLight,
|
|
0.025F,
|
|
0.025F,
|
|
k,
|
|
l,
|
|
m,
|
|
false
|
|
);
|
|
}
|
|
|
|
for (int m = 24; m >= 0; m--) {
|
|
addVertexPair(
|
|
vertexConsumer,
|
|
matrix4f,
|
|
g,
|
|
h,
|
|
i,
|
|
leashState.startBlockLight,
|
|
leashState.endBlockLight,
|
|
leashState.startSkyLight,
|
|
leashState.endSkyLight,
|
|
0.025F,
|
|
0.0F,
|
|
k,
|
|
l,
|
|
m,
|
|
true
|
|
);
|
|
}
|
|
|
|
poseStack.popPose();
|
|
}
|
|
|
|
private static void addVertexPair(
|
|
VertexConsumer buffer,
|
|
Matrix4f pose,
|
|
float startX,
|
|
float startY,
|
|
float startZ,
|
|
int entityBlockLight,
|
|
int holderBlockLight,
|
|
int entitySkyLight,
|
|
int holderSkyLight,
|
|
float yOffset,
|
|
float dy,
|
|
float dx,
|
|
float dz,
|
|
int index,
|
|
boolean reverse
|
|
) {
|
|
float f = index / 24.0F;
|
|
int i = (int)Mth.lerp(f, (float)entityBlockLight, (float)holderBlockLight);
|
|
int j = (int)Mth.lerp(f, (float)entitySkyLight, (float)holderSkyLight);
|
|
int k = LightTexture.pack(i, j);
|
|
float g = index % 2 == (reverse ? 1 : 0) ? 0.7F : 1.0F;
|
|
float h = 0.5F * g;
|
|
float l = 0.4F * g;
|
|
float m = 0.3F * g;
|
|
float n = startX * f;
|
|
float o = startY > 0.0F ? startY * f * f : startY - startY * (1.0F - f) * (1.0F - f);
|
|
float p = startZ * f;
|
|
buffer.addVertex(pose, n - dx, o + dy, p + dz).setColor(h, l, m, 1.0F).setLight(k);
|
|
buffer.addVertex(pose, n + dx, o + yOffset - dy, p - dz).setColor(h, l, m, 1.0F).setLight(k);
|
|
}
|
|
|
|
protected boolean shouldShowName(T entity, double distanceToCameraSq) {
|
|
return entity.shouldShowName() || entity.hasCustomName() && entity == this.entityRenderDispatcher.crosshairPickEntity;
|
|
}
|
|
|
|
/**
|
|
* Returns the font renderer from the set render manager
|
|
*/
|
|
public Font getFont() {
|
|
return this.font;
|
|
}
|
|
|
|
protected void renderNameTag(S renderState, Component displayName, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
|
|
Vec3 vec3 = renderState.nameTagAttachment;
|
|
if (vec3 != null) {
|
|
boolean bl = !renderState.isDiscrete;
|
|
int i = "deadmau5".equals(displayName.getString()) ? -10 : 0;
|
|
poseStack.pushPose();
|
|
poseStack.translate(vec3.x, vec3.y + 0.5, vec3.z);
|
|
poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation());
|
|
poseStack.scale(0.025F, -0.025F, 0.025F);
|
|
Matrix4f matrix4f = poseStack.last().pose();
|
|
Font font = this.getFont();
|
|
float f = -font.width(displayName) / 2.0F;
|
|
int j = (int)(Minecraft.getInstance().options.getBackgroundOpacity(0.25F) * 255.0F) << 24;
|
|
font.drawInBatch(displayName, f, (float)i, -2130706433, false, matrix4f, bufferSource, bl ? DisplayMode.SEE_THROUGH : DisplayMode.NORMAL, j, packedLight);
|
|
if (bl) {
|
|
font.drawInBatch(displayName, f, (float)i, -1, false, matrix4f, bufferSource, DisplayMode.NORMAL, 0, LightTexture.lightCoordsWithEmission(packedLight, 2));
|
|
}
|
|
|
|
poseStack.popPose();
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
protected Component getNameTag(T entity) {
|
|
return entity.getDisplayName();
|
|
}
|
|
|
|
protected float getShadowRadius(S renderState) {
|
|
return this.shadowRadius;
|
|
}
|
|
|
|
protected float getShadowStrength(S renderState) {
|
|
return this.shadowStrength;
|
|
}
|
|
|
|
public abstract S createRenderState();
|
|
|
|
public final S createRenderState(T entity, float partialTick) {
|
|
S entityRenderState = this.reusedState;
|
|
this.extractRenderState(entity, entityRenderState, partialTick);
|
|
return entityRenderState;
|
|
}
|
|
|
|
public void extractRenderState(T entity, S reusedState, float partialTick) {
|
|
reusedState.entityType = entity.getType();
|
|
reusedState.x = Mth.lerp((double)partialTick, entity.xOld, entity.getX());
|
|
reusedState.y = Mth.lerp((double)partialTick, entity.yOld, entity.getY());
|
|
reusedState.z = Mth.lerp((double)partialTick, entity.zOld, entity.getZ());
|
|
reusedState.isInvisible = entity.isInvisible();
|
|
reusedState.ageInTicks = entity.tickCount + partialTick;
|
|
reusedState.boundingBoxWidth = entity.getBbWidth();
|
|
reusedState.boundingBoxHeight = entity.getBbHeight();
|
|
reusedState.eyeHeight = entity.getEyeHeight();
|
|
if (entity.isPassenger()
|
|
&& entity.getVehicle() instanceof AbstractMinecart abstractMinecart
|
|
&& abstractMinecart.getBehavior() instanceof NewMinecartBehavior newMinecartBehavior
|
|
&& newMinecartBehavior.cartHasPosRotLerp()) {
|
|
double d = Mth.lerp((double)partialTick, abstractMinecart.xOld, abstractMinecart.getX());
|
|
double e = Mth.lerp((double)partialTick, abstractMinecart.yOld, abstractMinecart.getY());
|
|
double f = Mth.lerp((double)partialTick, abstractMinecart.zOld, abstractMinecart.getZ());
|
|
reusedState.passengerOffset = newMinecartBehavior.getCartLerpPosition(partialTick).subtract(new Vec3(d, e, f));
|
|
} else {
|
|
reusedState.passengerOffset = null;
|
|
}
|
|
|
|
reusedState.distanceToCameraSq = this.entityRenderDispatcher.distanceToSqr(entity);
|
|
boolean bl = reusedState.distanceToCameraSq < 4096.0 && this.shouldShowName(entity, reusedState.distanceToCameraSq);
|
|
if (bl) {
|
|
reusedState.nameTag = this.getNameTag(entity);
|
|
reusedState.nameTagAttachment = entity.getAttachments().getNullable(EntityAttachment.NAME_TAG, 0, entity.getYRot(partialTick));
|
|
} else {
|
|
reusedState.nameTag = null;
|
|
}
|
|
|
|
reusedState.isDiscrete = entity.isDiscrete();
|
|
Entity entity2 = entity instanceof Leashable leashable ? leashable.getLeashHolder() : null;
|
|
if (entity2 != null) {
|
|
float g = entity.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0);
|
|
Vec3 vec3 = entity.getLeashOffset(partialTick).yRot(-g);
|
|
BlockPos blockPos = BlockPos.containing(entity.getEyePosition(partialTick));
|
|
BlockPos blockPos2 = BlockPos.containing(entity2.getEyePosition(partialTick));
|
|
if (reusedState.leashState == null) {
|
|
reusedState.leashState = new EntityRenderState.LeashState();
|
|
}
|
|
|
|
EntityRenderState.LeashState leashState = reusedState.leashState;
|
|
leashState.offset = vec3;
|
|
leashState.start = entity.getPosition(partialTick).add(vec3);
|
|
leashState.end = entity2.getRopeHoldPosition(partialTick);
|
|
leashState.startBlockLight = this.getBlockLightLevel(entity, blockPos);
|
|
leashState.endBlockLight = this.entityRenderDispatcher.getRenderer(entity2).getBlockLightLevel(entity2, blockPos2);
|
|
leashState.startSkyLight = entity.level().getBrightness(LightLayer.SKY, blockPos);
|
|
leashState.endSkyLight = entity.level().getBrightness(LightLayer.SKY, blockPos2);
|
|
} else {
|
|
reusedState.leashState = null;
|
|
}
|
|
|
|
reusedState.displayFireAnimation = entity.displayFireAnimation();
|
|
Minecraft minecraft = Minecraft.getInstance();
|
|
if (minecraft.getEntityRenderDispatcher().shouldRenderHitBoxes() && !reusedState.isInvisible && !minecraft.showOnlyReducedInfo()) {
|
|
this.extractHitboxes(entity, reusedState, partialTick);
|
|
} else {
|
|
reusedState.hitboxesRenderState = null;
|
|
reusedState.serverHitboxesRenderState = null;
|
|
}
|
|
}
|
|
|
|
private void extractHitboxes(T entity, S reusedState, float partialTick) {
|
|
reusedState.hitboxesRenderState = this.extractHitboxes(entity, partialTick, false);
|
|
reusedState.serverHitboxesRenderState = null;
|
|
}
|
|
|
|
private HitboxesRenderState extractHitboxes(T entity, float partialTick, boolean green) {
|
|
Builder<HitboxRenderState> builder = new Builder<>();
|
|
AABB aABB = entity.getBoundingBox();
|
|
HitboxRenderState hitboxRenderState;
|
|
if (green) {
|
|
hitboxRenderState = new HitboxRenderState(
|
|
aABB.minX - entity.getX(),
|
|
aABB.minY - entity.getY(),
|
|
aABB.minZ - entity.getZ(),
|
|
aABB.maxX - entity.getX(),
|
|
aABB.maxY - entity.getY(),
|
|
aABB.maxZ - entity.getZ(),
|
|
0.0F,
|
|
1.0F,
|
|
0.0F
|
|
);
|
|
} else {
|
|
hitboxRenderState = new HitboxRenderState(
|
|
aABB.minX - entity.getX(),
|
|
aABB.minY - entity.getY(),
|
|
aABB.minZ - entity.getZ(),
|
|
aABB.maxX - entity.getX(),
|
|
aABB.maxY - entity.getY(),
|
|
aABB.maxZ - entity.getZ(),
|
|
1.0F,
|
|
1.0F,
|
|
1.0F
|
|
);
|
|
}
|
|
|
|
builder.add(hitboxRenderState);
|
|
Entity entity2 = entity.getVehicle();
|
|
if (entity2 != null) {
|
|
float f = Math.min(entity2.getBbWidth(), entity.getBbWidth()) / 2.0F;
|
|
float g = 0.0625F;
|
|
Vec3 vec3 = entity2.getPassengerRidingPosition(entity).subtract(entity.position());
|
|
HitboxRenderState hitboxRenderState2 = new HitboxRenderState(vec3.x - f, vec3.y, vec3.z - f, vec3.x + f, vec3.y + 0.0625, vec3.z + f, 1.0F, 1.0F, 0.0F);
|
|
builder.add(hitboxRenderState2);
|
|
}
|
|
|
|
this.extractAdditionalHitboxes(entity, builder, partialTick);
|
|
Vec3 vec32 = entity.getViewVector(partialTick);
|
|
return new HitboxesRenderState(vec32.x, vec32.y, vec32.z, builder.build());
|
|
}
|
|
|
|
protected void extractAdditionalHitboxes(T entity, Builder<HitboxRenderState> hitboxes, float partialTick) {
|
|
}
|
|
|
|
@Nullable
|
|
private static Entity getServerSideEntity(Entity entity) {
|
|
IntegratedServer integratedServer = Minecraft.getInstance().getSingleplayerServer();
|
|
if (integratedServer != null) {
|
|
ServerLevel serverLevel = integratedServer.getLevel(entity.level().dimension());
|
|
if (serverLevel != null) {
|
|
return serverLevel.getEntity(entity.getId());
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|