XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

MyDimension

【Java】我的维度模组

公开
关注 0 Fork 0 Star 0
UTF-8
package com.xfestudio.mydimension.client.builder;

import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import com.xfestudio.mydimension.MyDimension;
import com.xfestudio.mydimension.builder.BuilderMode;
import com.xfestudio.mydimension.builder.RealmwrightData;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.model.geom.EntityModelSet;
import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.client.event.ModelEvent;

/** Renders the static scepter body plus lightweight, independently animated model components. */
public final class RealmwrightScepterRenderer extends BlockEntityWithoutLevelRenderer {
    private static final ResourceLocation BUILD_MODEL = model("realmwright_scepter_build");
    private static final ResourceLocation DEMOLISH_MODEL = model("realmwright_scepter_demolish");
    private static final ResourceLocation EXTENSION_MODEL = model("realmwright_scepter_extension");
    private static final ResourceLocation LOWER_RING_MODEL = model("realmwright_scepter_ring_lower");
    private static final ResourceLocation UPPER_RING_MODEL = model("realmwright_scepter_ring_upper");
    private static final ResourceLocation FLOATING_MODEL = model("realmwright_scepter_floating");
    private static final ResourceLocation FLOATING_SECONDARY_MODEL = model("realmwright_scepter_floating_secondary");

    // Both orbit rings live on the upper shaft so neither is hidden by the hand in first person.
    // The former lower ring is now the higher, slightly smaller orbit.
    private static final float LOWER_RING_PIVOT_Y = 12.5F / 16.0F;
    private static final float UPPER_RING_PIVOT_Y = 8.25F / 16.0F;

    public RealmwrightScepterRenderer(BlockEntityRenderDispatcher dispatcher, EntityModelSet modelSet) {
        super(dispatcher, modelSet);
    }

    public static void registerAdditionalModels(ModelEvent.RegisterAdditional event) {
        event.register(BUILD_MODEL);
        event.register(DEMOLISH_MODEL);
        event.register(EXTENSION_MODEL);
        event.register(LOWER_RING_MODEL);
        event.register(UPPER_RING_MODEL);
        event.register(FLOATING_MODEL);
        event.register(FLOATING_SECONDARY_MODEL);
    }

    @Override
    public void renderByItem(ItemStack stack, ItemDisplayContext context, PoseStack pose,
                             MultiBufferSource buffers, int packedLight, int packedOverlay) {
        Minecraft minecraft = Minecraft.getInstance();
        long animationMillis = Util.getMillis();
        float ringTicks = (animationMillis % 3_600_000L) / 50.0F;
        // Keep bobbing phase in double precision instead of sharing the hourly ring wrap. The
        // ring speeds complete an integral number of turns at that wrap, while a sine wave would
        // otherwise make the floating fragments visibly jump once per long play session.
        double bobPhase = animationMillis * (0.14D / 50.0D);

        ResourceLocation bodyModel = RealmwrightData.mode(stack) == BuilderMode.DEMOLISH
                ? DEMOLISH_MODEL : BUILD_MODEL;
        renderModel(minecraft, bodyModel, stack, pose, buffers, packedLight, packedOverlay);

        // Vanilla item-model elements are restricted to [-16, 32] on every axis. The requested
        // 1.5x shaft exceeds that span, so its lower section is baked as a second valid model and
        // translated back into the exact continuous position here instead of invalidating the
        // whole body into Minecraft's magenta/black missing-model cube.
        pose.pushPose();
        pose.translate(0.0D, -14.55D / 16.0D, 0.0D);
        renderModel(minecraft, EXTENSION_MODEL, stack, pose, buffers, packedLight, packedOverlay);
        pose.popPose();

        pose.pushPose();
        rotateAroundShaft(pose, LOWER_RING_PIVOT_Y, ringTicks * 4.0F, -5.0F);
        renderModel(minecraft, LOWER_RING_MODEL, stack, pose, buffers, packedLight, packedOverlay);
        pose.popPose();

        pose.pushPose();
        rotateAroundShaft(pose, UPPER_RING_PIVOT_Y, ringTicks * -5.5F, 7.0F);
        renderModel(minecraft, UPPER_RING_MODEL, stack, pose, buffers, packedLight, packedOverlay);
        pose.popPose();

        pose.pushPose();
        pose.translate(0.0D, Math.sin(bobPhase) * 0.08D, 0.0D);
        renderModel(minecraft, FLOATING_MODEL, stack, pose, buffers, packedLight, packedOverlay);
        pose.popPose();

        pose.pushPose();
        pose.translate(0.0D, Math.sin(bobPhase + 2.2D) * 0.065D, 0.0D);
        renderModel(minecraft, FLOATING_SECONDARY_MODEL, stack, pose, buffers, packedLight, packedOverlay);
        pose.popPose();
    }

    private static void rotateAroundShaft(PoseStack pose, float pivotY, float degrees, float tiltDegrees) {
        pose.translate(0.5D, pivotY, 0.5D);
        pose.mulPose(Axis.YP.rotationDegrees(degrees));
        pose.mulPose(Axis.ZP.rotationDegrees(tiltDegrees));
        pose.translate(-0.5D, -pivotY, -0.5D);
    }

    private static void renderModel(Minecraft minecraft, ResourceLocation location, ItemStack stack,
                                    PoseStack pose, MultiBufferSource buffers,
                                    int packedLight, int packedOverlay) {
        BakedModel model = minecraft.getModelManager().getModel(location);
        ItemRenderer itemRenderer = minecraft.getItemRenderer();
        for (BakedModel pass : model.getRenderPasses(stack, true)) {
            for (RenderType renderType : pass.getRenderTypes(stack, true)) {
                VertexConsumer vertexConsumer = ItemRenderer.getFoilBufferDirect(
                        buffers, renderType, true, stack.hasFoil());
                itemRenderer.renderModelLists(pass, stack, packedLight, packedOverlay, pose, vertexConsumer);
            }
        }
    }

    private static ResourceLocation model(String name) {
        return new ResourceLocation(MyDimension.MOD_ID, "item/" + name);
    }
}