minecraft-src/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java
2025-07-04 03:45:38 +03:00

477 lines
20 KiB
Java

package net.minecraft.client.renderer.entity;
import com.google.common.collect.ImmutableMap;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import java.util.Map;
import java.util.function.Supplier;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Options;
import net.minecraft.client.gui.Font;
import net.minecraft.client.model.geom.EntityModelSet;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.ItemInHandRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MapRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.ShapeRenderer;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.debug.DebugRenderer;
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.renderer.entity.state.PlayerRenderState;
import net.minecraft.client.renderer.entity.state.ServerHitboxesRenderState;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.resources.PlayerSkin.Model;
import net.minecraft.client.resources.model.EquipmentAssetManager;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
import net.minecraft.util.ARGB;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
import org.joml.Quaternionf;
import org.joml.Vector3f;
@Environment(EnvType.CLIENT)
public class EntityRenderDispatcher implements ResourceManagerReloadListener {
private static final RenderType SHADOW_RENDER_TYPE = RenderType.entityShadow(ResourceLocation.withDefaultNamespace("textures/misc/shadow.png"));
private static final float MAX_SHADOW_RADIUS = 32.0F;
private static final float SHADOW_POWER_FALLOFF_Y = 0.5F;
private Map<EntityType<?>, EntityRenderer<?, ?>> renderers = ImmutableMap.of();
/**
* lists the various player skin types with their associated Renderer class instances.
*/
private Map<Model, EntityRenderer<? extends Player, ?>> playerRenderers = Map.of();
public final TextureManager textureManager;
private Level level;
public Camera camera;
private Quaternionf cameraOrientation;
public Entity crosshairPickEntity;
private final ItemModelResolver itemModelResolver;
private final MapRenderer mapRenderer;
private final BlockRenderDispatcher blockRenderDispatcher;
private final ItemInHandRenderer itemInHandRenderer;
private final Font font;
public final Options options;
private final Supplier<EntityModelSet> entityModels;
private final EquipmentAssetManager equipmentAssets;
private boolean shouldRenderShadow = true;
private boolean renderHitBoxes;
public <E extends Entity> int getPackedLightCoords(E entity, float partialTicks) {
return this.getRenderer(entity).getPackedLightCoords(entity, partialTicks);
}
public EntityRenderDispatcher(
Minecraft minecraft,
TextureManager textureManager,
ItemModelResolver itemModelResolver,
ItemRenderer itemRenderer,
MapRenderer mapRenderer,
BlockRenderDispatcher blockRenderDispatcher,
Font font,
Options options,
Supplier<EntityModelSet> entityModels,
EquipmentAssetManager equipmentModels
) {
this.textureManager = textureManager;
this.itemModelResolver = itemModelResolver;
this.mapRenderer = mapRenderer;
this.itemInHandRenderer = new ItemInHandRenderer(minecraft, this, itemRenderer, itemModelResolver);
this.blockRenderDispatcher = blockRenderDispatcher;
this.font = font;
this.options = options;
this.entityModels = entityModels;
this.equipmentAssets = equipmentModels;
}
public <T extends Entity> EntityRenderer<? super T, ?> getRenderer(T entity) {
if (entity instanceof AbstractClientPlayer abstractClientPlayer) {
Model model = abstractClientPlayer.getSkin().model();
EntityRenderer<? extends Player, ?> entityRenderer = (EntityRenderer<? extends Player, ?>)this.playerRenderers.get(model);
return (EntityRenderer<? super T, ?>)(entityRenderer != null ? entityRenderer : (EntityRenderer)this.playerRenderers.get(Model.WIDE));
} else {
return (EntityRenderer<? super T, ?>)this.renderers.get(entity.getType());
}
}
public <S extends EntityRenderState> EntityRenderer<?, ? super S> getRenderer(S renderState) {
if (renderState instanceof PlayerRenderState playerRenderState) {
Model model = playerRenderState.skin.model();
EntityRenderer<? extends Player, ?> entityRenderer = (EntityRenderer<? extends Player, ?>)this.playerRenderers.get(model);
return (EntityRenderer<?, ? super S>)(entityRenderer != null ? entityRenderer : (EntityRenderer)this.playerRenderers.get(Model.WIDE));
} else {
return (EntityRenderer<?, ? super S>)this.renderers.get(renderState.entityType);
}
}
public void prepare(Level level, Camera activeRenderInfo, Entity entity) {
this.level = level;
this.camera = activeRenderInfo;
this.cameraOrientation = activeRenderInfo.rotation();
this.crosshairPickEntity = entity;
}
public void overrideCameraOrientation(Quaternionf cameraOrientation) {
this.cameraOrientation = cameraOrientation;
}
public void setRenderShadow(boolean renderShadow) {
this.shouldRenderShadow = renderShadow;
}
public void setRenderHitBoxes(boolean debugBoundingBox) {
this.renderHitBoxes = debugBoundingBox;
}
public boolean shouldRenderHitBoxes() {
return this.renderHitBoxes;
}
public <E extends Entity> boolean shouldRender(E entity, Frustum frustum, double camX, double camY, double camZ) {
EntityRenderer<? super E, ?> entityRenderer = this.getRenderer(entity);
return entityRenderer.shouldRender(entity, frustum, camX, camY, camZ);
}
public <E extends Entity> void render(
E entity, double xOffset, double yOffset, double zOffset, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight
) {
EntityRenderer<? super E, ?> entityRenderer = this.getRenderer(entity);
this.render(entity, xOffset, yOffset, zOffset, partialTick, poseStack, bufferSource, packedLight, entityRenderer);
}
private <E extends Entity, S extends EntityRenderState> void render(
E entity,
double xOffset,
double yOffset,
double zOffset,
float partialTick,
PoseStack poseStack,
MultiBufferSource bufferSource,
int packedLight,
EntityRenderer<? super E, S> renderer
) {
S entityRenderState;
try {
entityRenderState = renderer.createRenderState(entity, partialTick);
} catch (Throwable var19) {
CrashReport crashReport = CrashReport.forThrowable(var19, "Extracting render state for an entity in world");
CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being extracted");
entity.fillCrashReportCategory(crashReportCategory);
CrashReportCategory crashReportCategory2 = this.fillRendererDetails(xOffset, yOffset, zOffset, renderer, crashReport);
crashReportCategory2.setDetail("Delta", partialTick);
throw new ReportedException(crashReport);
}
try {
this.render(entityRenderState, xOffset, yOffset, zOffset, poseStack, bufferSource, packedLight, renderer);
} catch (Throwable var18) {
CrashReport crashReport = CrashReport.forThrowable(var18, "Rendering entity in world");
CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being rendered");
entity.fillCrashReportCategory(crashReportCategory);
throw new ReportedException(crashReport);
}
}
public <S extends EntityRenderState> void render(
S renderState, double xOffset, double yOffset, double zOffset, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight
) {
EntityRenderer<?, ? super S> entityRenderer = this.getRenderer(renderState);
this.render(renderState, xOffset, yOffset, zOffset, poseStack, bufferSource, packedLight, entityRenderer);
}
private <S extends EntityRenderState> void render(
S renderState,
double xOffset,
double yOffset,
double zOffset,
PoseStack poseStack,
MultiBufferSource bufferSource,
int packedLight,
EntityRenderer<?, S> renderer
) {
try {
Vec3 vec3 = renderer.getRenderOffset(renderState);
double d = xOffset + vec3.x();
double e = yOffset + vec3.y();
double f = zOffset + vec3.z();
poseStack.pushPose();
poseStack.translate(d, e, f);
renderer.render(renderState, poseStack, bufferSource, packedLight);
if (renderState.displayFireAnimation) {
this.renderFlame(poseStack, bufferSource, renderState, Mth.rotationAroundAxis(Mth.Y_AXIS, this.cameraOrientation, new Quaternionf()));
}
if (renderState instanceof PlayerRenderState) {
poseStack.translate(-vec3.x(), -vec3.y(), -vec3.z());
}
if (this.options.entityShadows().get() && this.shouldRenderShadow && !renderState.isInvisible) {
float g = renderer.getShadowRadius(renderState);
if (g > 0.0F) {
double h = renderState.distanceToCameraSq;
float i = (float)((1.0 - h / 256.0) * renderer.getShadowStrength(renderState));
if (i > 0.0F) {
renderShadow(poseStack, bufferSource, renderState, i, this.level, Math.min(g, 32.0F));
}
}
}
if (!(renderState instanceof PlayerRenderState)) {
poseStack.translate(-vec3.x(), -vec3.y(), -vec3.z());
}
if (renderState.hitboxesRenderState != null) {
this.renderHitboxes(poseStack, renderState, renderState.hitboxesRenderState, bufferSource);
}
poseStack.popPose();
} catch (Throwable var23) {
CrashReport crashReport = CrashReport.forThrowable(var23, "Rendering entity in world");
CrashReportCategory crashReportCategory = crashReport.addCategory("EntityRenderState being rendered");
renderState.fillCrashReportCategory(crashReportCategory);
this.fillRendererDetails(xOffset, yOffset, zOffset, renderer, crashReport);
throw new ReportedException(crashReport);
}
}
private <S extends EntityRenderState> CrashReportCategory fillRendererDetails(
double xOffset, double yOffset, double zOffset, EntityRenderer<?, S> renderer, CrashReport crashReport
) {
CrashReportCategory crashReportCategory = crashReport.addCategory("Renderer details");
crashReportCategory.setDetail("Assigned renderer", renderer);
crashReportCategory.setDetail("Location", CrashReportCategory.formatLocation(this.level, xOffset, yOffset, zOffset));
return crashReportCategory;
}
private void renderHitboxes(PoseStack poseStack, EntityRenderState renderState, HitboxesRenderState hitboxesRenderState, MultiBufferSource bufferSource) {
VertexConsumer vertexConsumer = bufferSource.getBuffer(RenderType.lines());
renderHitboxesAndViewVector(poseStack, hitboxesRenderState, vertexConsumer, renderState.eyeHeight);
ServerHitboxesRenderState serverHitboxesRenderState = renderState.serverHitboxesRenderState;
if (serverHitboxesRenderState != null) {
if (serverHitboxesRenderState.missing()) {
HitboxRenderState hitboxRenderState = (HitboxRenderState)hitboxesRenderState.hitboxes().getFirst();
DebugRenderer.renderFloatingText(poseStack, bufferSource, "Missing", renderState.x, hitboxRenderState.y1() + 1.5, renderState.z, -65536);
} else if (serverHitboxesRenderState.hitboxes() != null) {
poseStack.pushPose();
poseStack.translate(
serverHitboxesRenderState.serverEntityX() - renderState.x,
serverHitboxesRenderState.serverEntityY() - renderState.y,
serverHitboxesRenderState.serverEntityZ() - renderState.z
);
renderHitboxesAndViewVector(poseStack, serverHitboxesRenderState.hitboxes(), vertexConsumer, serverHitboxesRenderState.eyeHeight());
Vec3 vec3 = new Vec3(serverHitboxesRenderState.deltaMovementX(), serverHitboxesRenderState.deltaMovementY(), serverHitboxesRenderState.deltaMovementZ());
ShapeRenderer.renderVector(poseStack, vertexConsumer, new Vector3f(), vec3, -256);
poseStack.popPose();
}
}
}
private static void renderHitboxesAndViewVector(PoseStack poseStack, HitboxesRenderState hitboxesRenderState, VertexConsumer consumer, float eyeHeight) {
for (HitboxRenderState hitboxRenderState : hitboxesRenderState.hitboxes()) {
renderHitbox(poseStack, consumer, hitboxRenderState);
}
Vec3 vec3 = new Vec3(hitboxesRenderState.viewX(), hitboxesRenderState.viewY(), hitboxesRenderState.viewZ());
ShapeRenderer.renderVector(poseStack, consumer, new Vector3f(0.0F, eyeHeight, 0.0F), vec3.scale(2.0), -16776961);
}
private static void renderHitbox(PoseStack posStack, VertexConsumer consumer, HitboxRenderState hitbox) {
posStack.pushPose();
posStack.translate(hitbox.offsetX(), hitbox.offsetY(), hitbox.offsetZ());
ShapeRenderer.renderLineBox(
posStack, consumer, hitbox.x0(), hitbox.y0(), hitbox.z0(), hitbox.x1(), hitbox.y1(), hitbox.z1(), hitbox.red(), hitbox.green(), hitbox.blue(), 1.0F
);
posStack.popPose();
}
private void renderFlame(PoseStack poseStack, MultiBufferSource bufferSource, EntityRenderState renderState, Quaternionf quaternion) {
TextureAtlasSprite textureAtlasSprite = ModelBakery.FIRE_0.sprite();
TextureAtlasSprite textureAtlasSprite2 = ModelBakery.FIRE_1.sprite();
poseStack.pushPose();
float f = renderState.boundingBoxWidth * 1.4F;
poseStack.scale(f, f, f);
float g = 0.5F;
float h = 0.0F;
float i = renderState.boundingBoxHeight / f;
float j = 0.0F;
poseStack.mulPose(quaternion);
poseStack.translate(0.0F, 0.0F, 0.3F - (int)i * 0.02F);
float k = 0.0F;
int l = 0;
VertexConsumer vertexConsumer = bufferSource.getBuffer(Sheets.cutoutBlockSheet());
for (PoseStack.Pose pose = poseStack.last(); i > 0.0F; l++) {
TextureAtlasSprite textureAtlasSprite3 = l % 2 == 0 ? textureAtlasSprite : textureAtlasSprite2;
float m = textureAtlasSprite3.getU0();
float n = textureAtlasSprite3.getV0();
float o = textureAtlasSprite3.getU1();
float p = textureAtlasSprite3.getV1();
if (l / 2 % 2 == 0) {
float q = o;
o = m;
m = q;
}
fireVertex(pose, vertexConsumer, -g - 0.0F, 0.0F - j, k, o, p);
fireVertex(pose, vertexConsumer, g - 0.0F, 0.0F - j, k, m, p);
fireVertex(pose, vertexConsumer, g - 0.0F, 1.4F - j, k, m, n);
fireVertex(pose, vertexConsumer, -g - 0.0F, 1.4F - j, k, o, n);
i -= 0.45F;
j -= 0.45F;
g *= 0.9F;
k -= 0.03F;
}
poseStack.popPose();
}
private static void fireVertex(PoseStack.Pose matrixEntry, VertexConsumer buffer, float x, float y, float z, float texU, float texV) {
buffer.addVertex(matrixEntry, x, y, z).setColor(-1).setUv(texU, texV).setUv1(0, 10).setLight(240).setNormal(matrixEntry, 0.0F, 1.0F, 0.0F);
}
private static void renderShadow(
PoseStack poseStack, MultiBufferSource bufferSource, EntityRenderState renderState, float strength, LevelReader level, float size
) {
float f = Math.min(strength / 0.5F, size);
int i = Mth.floor(renderState.x - size);
int j = Mth.floor(renderState.x + size);
int k = Mth.floor(renderState.y - f);
int l = Mth.floor(renderState.y);
int m = Mth.floor(renderState.z - size);
int n = Mth.floor(renderState.z + size);
PoseStack.Pose pose = poseStack.last();
VertexConsumer vertexConsumer = bufferSource.getBuffer(SHADOW_RENDER_TYPE);
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
for (int o = m; o <= n; o++) {
for (int p = i; p <= j; p++) {
mutableBlockPos.set(p, 0, o);
ChunkAccess chunkAccess = level.getChunk(mutableBlockPos);
for (int q = k; q <= l; q++) {
mutableBlockPos.setY(q);
float g = strength - (float)(renderState.y - mutableBlockPos.getY()) * 0.5F;
renderBlockShadow(pose, vertexConsumer, chunkAccess, level, mutableBlockPos, renderState.x, renderState.y, renderState.z, size, g);
}
}
}
}
private static void renderBlockShadow(
PoseStack.Pose pose, VertexConsumer consumer, ChunkAccess chunk, LevelReader level, BlockPos pos, double x, double y, double z, float size, float weight
) {
BlockPos blockPos = pos.below();
BlockState blockState = chunk.getBlockState(blockPos);
if (blockState.getRenderShape() != RenderShape.INVISIBLE && level.getMaxLocalRawBrightness(pos) > 3) {
if (blockState.isCollisionShapeFullBlock(chunk, blockPos)) {
VoxelShape voxelShape = blockState.getShape(chunk, blockPos);
if (!voxelShape.isEmpty()) {
float f = LightTexture.getBrightness(level.dimensionType(), level.getMaxLocalRawBrightness(pos));
float g = weight * 0.5F * f;
if (g >= 0.0F) {
if (g > 1.0F) {
g = 1.0F;
}
int i = ARGB.color(Mth.floor(g * 255.0F), 255, 255, 255);
AABB aABB = voxelShape.bounds();
double d = pos.getX() + aABB.minX;
double e = pos.getX() + aABB.maxX;
double h = pos.getY() + aABB.minY;
double j = pos.getZ() + aABB.minZ;
double k = pos.getZ() + aABB.maxZ;
float l = (float)(d - x);
float m = (float)(e - x);
float n = (float)(h - y);
float o = (float)(j - z);
float p = (float)(k - z);
float q = -l / 2.0F / size + 0.5F;
float r = -m / 2.0F / size + 0.5F;
float s = -o / 2.0F / size + 0.5F;
float t = -p / 2.0F / size + 0.5F;
shadowVertex(pose, consumer, i, l, n, o, q, s);
shadowVertex(pose, consumer, i, l, n, p, q, t);
shadowVertex(pose, consumer, i, m, n, p, r, t);
shadowVertex(pose, consumer, i, m, n, o, r, s);
}
}
}
}
}
private static void shadowVertex(PoseStack.Pose pose, VertexConsumer consumer, int color, float offsetX, float offsetY, float offsetZ, float u, float v) {
Vector3f vector3f = pose.pose().transformPosition(offsetX, offsetY, offsetZ, new Vector3f());
consumer.addVertex(vector3f.x(), vector3f.y(), vector3f.z(), color, u, v, OverlayTexture.NO_OVERLAY, 15728880, 0.0F, 1.0F, 0.0F);
}
/**
* World sets this RenderManager's worldObj to the world provided
*/
public void setLevel(@Nullable Level level) {
this.level = level;
if (level == null) {
this.camera = null;
}
}
public double distanceToSqr(Entity entity) {
return this.camera.getPosition().distanceToSqr(entity.position());
}
public double distanceToSqr(double x, double y, double z) {
return this.camera.getPosition().distanceToSqr(x, y, z);
}
public Quaternionf cameraOrientation() {
return this.cameraOrientation;
}
public ItemInHandRenderer getItemInHandRenderer() {
return this.itemInHandRenderer;
}
@Override
public void onResourceManagerReload(ResourceManager resourceManager) {
Context context = new Context(
this,
this.itemModelResolver,
this.mapRenderer,
this.blockRenderDispatcher,
resourceManager,
(EntityModelSet)this.entityModels.get(),
this.equipmentAssets,
this.font
);
this.renderers = EntityRenderers.createEntityRenderers(context);
this.playerRenderers = EntityRenderers.createPlayerRenderers(context);
}
}