/* * GNU Lesser General Public License v3 * Copyright (C) 2024 Tschipp * mrtschipp@gmail.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package tschipp.carryon.client.render; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Axis; import net.minecraft.client.Minecraft; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.util.Mth; import net.minecraft.util.ProblemReporter; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.TagValueInput; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; import org.joml.Quaternionf; import tschipp.carryon.Constants; import tschipp.carryon.client.modeloverride.ModelOverride; import tschipp.carryon.client.modeloverride.ModelOverrideHandler; import tschipp.carryon.common.carry.CarryOnData; import tschipp.carryon.common.carry.CarryOnData.CarryType; import tschipp.carryon.common.carry.CarryOnDataManager; import tschipp.carryon.common.scripting.CarryOnScript; import tschipp.carryon.common.scripting.CarryOnScript.ScriptRender; import java.util.Optional; public class CarryRenderHelper { public static Vec3 getExactPos(Entity entity, float partialticks) { return new Vec3(entity.xOld + (entity.getX() - entity.xOld) * partialticks, entity.yOld + (entity.getY() - entity.yOld) * partialticks, entity.zOld + (entity.getZ() - entity.zOld) * partialticks); } public static float getExactBodyRotationDegrees(LivingEntity entity, float partialticks) { if (entity.getVehicle() != null && entity.getVehicle() instanceof LivingEntity vehicle) if(vehicle instanceof Player player) return -(player.yBodyRotO + (player.yBodyRot - player.yBodyRotO) * partialticks); else return -(entity.yHeadRotO + (entity.yHeadRot - entity.yHeadRotO) * partialticks); else return -(entity.yBodyRotO + (entity.yBodyRot - entity.yBodyRotO) * partialticks); } public static Quaternionf getExactBodyRotation(LivingEntity entity, float partialticks) { return Axis.YP.rotationDegrees(getExactBodyRotationDegrees(entity, partialticks)); } public static void applyGeneralTransformations(Player player, float partialticks, PoseStack matrix) { int perspective = CarryRenderHelper.getPerspective(); Quaternionf playerrot = CarryRenderHelper.getExactBodyRotation(player, partialticks); Vec3 playerpos = CarryRenderHelper.getExactPos(player, partialticks); Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); Vec3 offset = playerpos.subtract(cameraPos); Pose pose = player.getPose(); matrix.pushPose(); matrix.translate(offset.x, offset.y, offset.z); if (perspective == 2) playerrot.mul(Axis.YP.rotationDegrees(180)); matrix.mulPose(playerrot); matrix.pushPose(); matrix.scale(0.6f, 0.6f, 0.6f); if (perspective == 2) matrix.translate(0, 0, -1.35); if (doSneakCheck(player)) { matrix.translate(0, -0.4, 0); } if (pose == Pose.SWIMMING) { float f = player.getSwimAmount(partialticks); float f3 = player.isInWater() ? -90.0F - player.xRotO : -90.0F; float f4 = Mth.lerp(f, 0.0F, f3); if (perspective == 2) { matrix.translate(0, 0, 1.35); matrix.mulPose(Axis.XP.rotationDegrees(f4)); } else matrix.mulPose(Axis.XN.rotationDegrees(f4)); matrix.translate(0, -1.5, -1.848); if (perspective == 2) matrix.translate(0, 0, 2.38); } if (pose == Pose.FALL_FLYING) { float f1 = player.getFallFlyingTicks() + partialticks; float f2 = Mth.clamp(f1 * f1 / 100.0F, 0.0F, 1.0F); if (!player.isAutoSpinAttack()) { if (perspective == 2) matrix.translate(0, 0, 1.35); if (perspective == 2) matrix.mulPose(Axis.XP.rotationDegrees(f2 * (-90.0F - player.xRotO))); else matrix.mulPose(Axis.XN.rotationDegrees(f2 * (-90.0F - player.xRotO))); } Vec3 viewVector = player.getViewVector(partialticks); Vec3 deltaMovement = player.getDeltaMovement(); double d0 = deltaMovement.horizontalDistanceSqr(); double d1 = deltaMovement.horizontalDistanceSqr(); if (d0 > 0.0D && d1 > 0.0D) { double d2 = (deltaMovement.x * viewVector.x + deltaMovement.z * viewVector.z) / (Math.sqrt(d0) * Math.sqrt(d1)); double d3 = deltaMovement.x * viewVector.z - deltaMovement.z * viewVector.x; matrix.mulPose(Axis.YP.rotation((float) (Math.signum(d3) * Math.acos(d2)))); } if (perspective != 2) matrix.translate(0, 0, -1.35); matrix.translate(0, -0.2, 0); } matrix.translate(0, 1.6, 0.65); } public static void applyBlockTransformations(Player player, float partialticks, PoseStack matrix, Block block) { //int perspective = CarryRenderHelper.getPerspective(); applyGeneralTransformations(player, partialticks, matrix); if (Constants.CLIENT_CONFIG.facePlayer != CarryRenderHelper.isChest(block)) { //TODO: RealFirstPersonRender //if ((ModList.get().isLoaded("realrender") || ModList.get().isLoaded("rfpr")) && perspective == 0) // matrix.translate(0, 0, -0.4); matrix.mulPose(Axis.YP.rotationDegrees(180)); } // if(perspective == 1) // { // matrix.pushPose(); // //matrix.mulPose(Axis.YP.rotationDegrees(180)); // matrix.popPose(); // } //else if ((ModList.get().isLoaded("realrender") || ModList.get().isLoaded("rfpr")) && perspective == 0) // matrix.translate(0, 0, 0.4); //matrix.mulPose(Axis.YP.rotationDegrees(180)); float height = getRenderHeight(player); float offset = (height - 1f) / 1.2f; matrix.translate(0, -offset, 0); } public static void applyEntityTransformations(Player player, float partialticks, PoseStack matrix, Entity entity) { int perspective = CarryRenderHelper.getPerspective(); Pose pose = player.getPose(); applyGeneralTransformations(player, partialticks, matrix); if (perspective == 2) matrix.translate(0, -1.6, 0.65); else matrix.translate(0, -1.6, -0.65); matrix.scale(1.666f, 1.666f, 1.666f); float height = entity.getBbHeight(); float width = entity.getBbWidth(); float multiplier = height * width; entity.yo = 0.0f; entity.yRotO = 0.0f; entity.setYHeadRot(0.0f); entity.xo = 0.0f; entity.xRotO = 0.0f; if (perspective == 2) matrix.mulPose(Axis.YP.rotationDegrees(180)); matrix.scale((10 - multiplier) * 0.08f, (10 - multiplier) * 0.08f, (10 - multiplier) * 0.08f); matrix.translate(0.0, height / 2 + -(height / 4) + 1, width - 0.1 < 0.7 ? width - 0.1 + (0.7 - (width - 0.1)) : width - 0.1); if (pose == Pose.SWIMMING || pose == Pose.FALL_FLYING) { matrix.mulPose(Axis.XN.rotationDegrees(90)); matrix.translate(0, -0.2 * height, 0); if (pose == Pose.FALL_FLYING) matrix.translate(0, 0, 0.2); } if(Constants.CLIENT_CONFIG.rotateEntitiesSideways) matrix.mulPose(Axis.YP.rotationDegrees(90)); } public static void performScriptTransformation(PoseStack matrix, CarryOnScript script) { int perspective = getPerspective(); ScriptRender render = script.scriptRender(); Vec3 translation = render.renderTranslation().getVec(); Vec3 rotation = render.renderRotation().getVec(); Vec3 scale = render.renderscale().getVec(1, 1, 1); Quaternionf rot = Axis.XP.rotationDegrees((float) rotation.x); rot.mul(Axis.YP.rotationDegrees((float) rotation.y)); rot.mul(Axis.ZP.rotationDegrees((float) rotation.z)); matrix.mulPose(rot); matrix.translate(translation.x, translation.y, perspective == 1 && script.isBlock() ? -translation.z : translation.z); matrix.scale((float) scale.x, (float) scale.y, (float) scale.z); } /* @Deprecated public static void renderBakedModel(ItemStack stack, PoseStack matrix, MultiBufferSource buffer, int light, BakedModel model) { ItemStackRenderState state = new ItemStackRenderState(); try { ItemStackRenderState.LayerRenderState layer = state.newLayer(); if(stack.hasFoil()) layer.setFoilType(ItemStackRenderState.FoilType.STANDARD); layer.setupBlockModel(model, RenderType.translucent()); state.render(matrix, buffer, light, OverlayTexture.NO_OVERLAY); ItemRenderer renderer = Minecraft.getInstance().getItemRenderer(); renderer.renderStatic(stack, ItemDisplayContext.NONE, light, OverlayTexture.NO_OVERLAY, matrix, buffer, null, model, 0); renderer.render(stack, ItemDisplayContext.NONE, false, matrix, buffer, light, OverlayTexture.NO_OVERLAY, model); } catch (Exception e) { } } */ public static ItemStack getRenderItemStack(Player player) { CarryOnData carry = CarryOnDataManager.getCarryData(player); BlockState state = carry.getBlock().getBlock().defaultBlockState(); if(carry.getActiveScript().isPresent()) { ScriptRender render = carry.getActiveScript().get().scriptRender(); if(render.renderNameBlock().isPresent()) { state = BuiltInRegistries.BLOCK.get(render.renderNameBlock().get()).get().value().defaultBlockState(); } } ItemStack renderStack = ItemStack.EMPTY; Optional ov = ModelOverrideHandler.getModelOverride(state, carry.getContentNbt()); if(ov.isPresent()) { var renderObj = ov.get().getRenderObject(); if(renderObj.right().isPresent()) state = renderObj.right().get(); else if (renderObj.left().isPresent()) renderStack = renderObj.left().get(); } if(renderStack.isEmpty()) renderStack = new ItemStack(state.getBlock()); return renderStack; } public static BlockState getRenderState(Player player) { CarryOnData carry = CarryOnDataManager.getCarryData(player); BlockState state = carry.getBlock().getBlock().defaultBlockState(); if(carry.getActiveScript().isPresent()) { ScriptRender render = carry.getActiveScript().get().scriptRender(); if(render.renderNameBlock().isPresent()) { state = BuiltInRegistries.BLOCK.get(render.renderNameBlock().get()).get().value().defaultBlockState(); } } Optional ov = ModelOverrideHandler.getModelOverride(state, carry.getContentNbt()); if(ov.isPresent()) { var renderObj = ov.get().getRenderObject(); if(renderObj.right().isPresent()) state = renderObj.right().get(); } return state; } /* @Deprecated public static BakedModel getRenderBlock(Player player) { CarryOnData carry = CarryOnDataManager.getCarryData(player); ItemRenderer renderer = Minecraft.getInstance().getItemRenderer(); Minecraft.getInstance().getModelManager().specialBlockModelRenderer().get(). BlockState state = getRenderState(player); BakedModel model = Minecraft.getInstance().getBlockRenderer().getBlockModel(state); if(state.getRenderShape() != RenderShape.MODEL || model.isCustomRenderer() || model.getQuads(state, null, RandomSource.create()).size() <= 0) { ItemStack stack = new ItemStack(state.getBlock()); model = renderer.getModel(stack, player.level(), player, 0); } Optional ov = ModelOverrideHandler.getModelOverride(state, carry.getContentNbt()); if(ov.isPresent()) { var renderObj = ov.get().getRenderObject(); if(renderObj.left().isPresent()) model = renderer.getModel(renderObj.left().get(), player.level(), player, 0); } return model; } */ public static Entity getRenderEntity(Player player) { CarryOnData carry = CarryOnDataManager.getCarryData(player); Entity entity = carry.getEntity(player.level()); if(carry.getActiveScript().isPresent()) { CarryOnScript script = carry.getActiveScript().get(); ScriptRender render = script.scriptRender(); if(render.renderNameEntity().isPresent()) entity = BuiltInRegistries.ENTITY_TYPE.get(render.renderNameEntity().get()).get().value().create(player.level(), EntitySpawnReason.EVENT); if(render.renderNBT().isPresent()) { ValueInput input = TagValueInput.create(new ProblemReporter.ScopedCollector(Constants.LOG), player.registryAccess(), render.renderNBT().get()); entity.load(input); } } return entity; } public static float getRenderWidth(Player player) { CarryOnData carry = CarryOnDataManager.getCarryData(player); if(carry.isCarrying(CarryType.BLOCK)) { BlockState state = getRenderState(player); VoxelShape shape = state.getShape(player.level(), player.blockPosition()); if(shape == null || shape.isEmpty()) return 1f; Optional ov = ModelOverrideHandler.getModelOverride(state, carry.getContentNbt()); if(ov.isPresent()) { var renderObj = ov.get().getRenderObject(); if(renderObj.left().isPresent()) return 0.8f; } float width = (float)Math.abs(shape.bounds().maxX - shape.bounds().minX); return width; } else if(carry.isCarrying(CarryType.ENTITY)) { Entity entity = getRenderEntity(player); float w = entity.getBbWidth(); if (Constants.CLIENT_CONFIG.rotateEntitiesSideways) return w - (w*w) * 0.35f; return w * 0.9f; } else return 1f; } public static float getRenderHeight(Player player) { CarryOnData carry = CarryOnDataManager.getCarryData(player); if(carry.isCarrying(CarryType.BLOCK)) { BlockState state = getRenderState(player); VoxelShape shape = state.getShape(player.level(), player.blockPosition()); if(shape == null || shape.isEmpty()) return 1f; Optional ov = ModelOverrideHandler.getModelOverride(state, carry.getContentNbt()); if(ov.isPresent()) { var renderObj = ov.get().getRenderObject(); if(renderObj.left().isPresent()) return 0.5f; } float height = (float)Math.abs(shape.bounds().maxY - shape.bounds().minY); return height; } else if(carry.isCarrying(CarryType.ENTITY)) { Entity entity = getRenderEntity(player); return entity.getBbHeight(); } else return 1f; } @SuppressWarnings("resource") public static int getPerspective() { boolean isThirdPerson = !Minecraft.getInstance().options.getCameraType().isFirstPerson(); // isThirdPerson boolean isThirdPersonReverse = Minecraft.getInstance().options.getCameraType().isMirrored(); if (!isThirdPerson && !isThirdPersonReverse) return 0; if (isThirdPerson && !isThirdPersonReverse) return 1; return 2; } public static boolean doSneakCheck(Player player) { if (player.getAbilities().flying) return false; return player.isShiftKeyDown() || player.isCrouching(); } public static boolean isChest(Block block) { return block == Blocks.CHEST || block == Blocks.ENDER_CHEST || block == Blocks.TRAPPED_CHEST; } }