// Native Minecraft 1.21.11
package com.xfestudio.mydimensionlord.client.builder;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import com.mojang.serialization.MapCodec;
import com.xfestudio.mydimensionlord.builder.BuilderMode;
import com.xfestudio.mydimensionlord.builder.RealmwrightData;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.item.ItemStackRenderState;
import net.minecraft.client.renderer.special.SpecialModelRenderer;
import net.minecraft.resources.Identifier;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.joml.Vector3f;
import org.joml.Vector3fc;
import java.util.function.Consumer;
public final class RealmwrightScepterRenderer implements SpecialModelRenderer<RealmwrightScepterRenderer.State> {
private static final int EXTENSION_MODEL=1, LOWER_RING_MODEL=2, UPPER_RING_MODEL=3, FLOATING_MODEL=4, FLOATING_SECONDARY_MODEL=5;
private static final String[] PARTS = {"build", "extension", "ring_lower", "ring_upper", "floating", "floating_secondary"};
public record State(ItemStackRenderState[] parts, long millis) {}
public record Unbaked() implements SpecialModelRenderer.Unbaked {
public static final MapCodec<Unbaked> CODEC = MapCodec.unit(new Unbaked());
@Override public SpecialModelRenderer<?> bake(BakingContext context) { return new RealmwrightScepterRenderer(); }
@Override public MapCodec<Unbaked> type() { return CODEC; }
}
@Override public State extractArgument(ItemStack stack) {
var minecraft = Minecraft.getInstance();
var parts = new ItemStackRenderState[PARTS.length];
for (int i=0; i<parts.length; i++) {
String part = i == 0 && RealmwrightData.mode(stack) == BuilderMode.DEMOLISH ? "demolish" : PARTS[i];
parts[i] = new ItemStackRenderState();
minecraft.getModelManager().getItemModel(Identifier.fromNamespaceAndPath("mydimension", "scepter_parts/" + part))
.update(parts[i], stack, minecraft.getItemModelResolver(), ItemDisplayContext.NONE, minecraft.level, minecraft.player, 0);
}
return new State(parts, net.minecraft.util.Util.getMillis());
}
@Override public void getExtents(Consumer<Vector3fc> consumer) {
consumer.accept(new Vector3f(-0.3F, -1.0F, -0.3F));
consumer.accept(new Vector3f(1.3F, 2.2F, 1.3F));
}
// 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;
@Override public void submit(State state, ItemDisplayContext context, PoseStack pose, SubmitNodeCollector collector,
int packedLight, int packedOverlay, boolean foil, int outline) {
if (state == null) return;
long animationMillis = state.millis();
float ringTicks = (animationMillis % 3_600_000L) / 50.0F;
double bobPhase = animationMillis * (0.14D / 50.0D);
renderModel(state, 0, pose, collector, packedLight, packedOverlay, outline);
// 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(state, EXTENSION_MODEL, pose, collector, packedLight, packedOverlay, outline);
pose.popPose();
pose.pushPose();
rotateAroundShaft(pose, LOWER_RING_PIVOT_Y, ringTicks * 4.0F, -5.0F);
renderModel(state, LOWER_RING_MODEL, pose, collector, packedLight, packedOverlay, outline);
pose.popPose();
pose.pushPose();
rotateAroundShaft(pose, UPPER_RING_PIVOT_Y, ringTicks * -5.5F, 7.0F);
renderModel(state, UPPER_RING_MODEL, pose, collector, packedLight, packedOverlay, outline);
pose.popPose();
pose.pushPose();
pose.translate(0.0D, Math.sin(bobPhase) * 0.08D, 0.0D);
renderModel(state, FLOATING_MODEL, pose, collector, packedLight, packedOverlay, outline);
pose.popPose();
pose.pushPose();
pose.translate(0.0D, Math.sin(bobPhase + 2.2D) * 0.065D, 0.0D);
renderModel(state, FLOATING_SECONDARY_MODEL, pose, collector, packedLight, packedOverlay, outline);
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(State state, int part, PoseStack pose, SubmitNodeCollector collector, int light, int overlay, int outline) {
// Each native item layer centers its mesh. The parent special renderer
// already applied that translation; keep the original model-space pivots.
pose.pushPose();
pose.translate(0.5D, 0.5D, 0.5D);
state.parts()[part].submit(pose, collector, light, overlay, outline);
pose.popPose();
}
}
// Native Minecraft 1.21.11
package com.xfestudio.mydimensionlord.client.builder;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import com.mojang.serialization.MapCodec;
import com.xfestudio.mydimensionlord.builder.BuilderMode;
import com.xfestudio.mydimensionlord.builder.RealmwrightData;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.item.ItemStackRenderState;
import net.minecraft.client.renderer.special.SpecialModelRenderer;
import net.minecraft.resources.Identifier;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.joml.Vector3f;
import org.joml.Vector3fc;
import java.util.function.Consumer;
public final class RealmwrightScepterRenderer implements SpecialModelRenderer<RealmwrightScepterRenderer.State> {
private static final int EXTENSION_MODEL=1, LOWER_RING_MODEL=2, UPPER_RING_MODEL=3, FLOATING_MODEL=4, FLOATING_SECONDARY_MODEL=5;
private static final String[] PARTS = {"build", "extension", "ring_lower", "ring_upper", "floating", "floating_secondary"};
public record State(ItemStackRenderState[] parts, long millis) {}
public record Unbaked() implements SpecialModelRenderer.Unbaked {
public static final MapCodec<Unbaked> CODEC = MapCodec.unit(new Unbaked());
@Override public SpecialModelRenderer<?> bake(BakingContext context) { return new RealmwrightScepterRenderer(); }
@Override public MapCodec<Unbaked> type() { return CODEC; }
}
@Override public State extractArgument(ItemStack stack) {
var minecraft = Minecraft.getInstance();
var parts = new ItemStackRenderState[PARTS.length];
for (int i=0; i<parts.length; i++) {
String part = i == 0 && RealmwrightData.mode(stack) == BuilderMode.DEMOLISH ? "demolish" : PARTS[i];
parts[i] = new ItemStackRenderState();
minecraft.getModelManager().getItemModel(Identifier.fromNamespaceAndPath("mydimension", "scepter_parts/" + part))
.update(parts[i], stack, minecraft.getItemModelResolver(), ItemDisplayContext.NONE, minecraft.level, minecraft.player, 0);
}
return new State(parts, net.minecraft.util.Util.getMillis());
}
@Override public void getExtents(Consumer<Vector3fc> consumer) {
consumer.accept(new Vector3f(-0.3F, -1.0F, -0.3F));
consumer.accept(new Vector3f(1.3F, 2.2F, 1.3F));
}
// 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;
@Override public void submit(State state, ItemDisplayContext context, PoseStack pose, SubmitNodeCollector collector,
int packedLight, int packedOverlay, boolean foil, int outline) {
if (state == null) return;
long animationMillis = state.millis();
float ringTicks = (animationMillis % 3_600_000L) / 50.0F;
double bobPhase = animationMillis * (0.14D / 50.0D);
renderModel(state, 0, pose, collector, packedLight, packedOverlay, outline);
// 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(state, EXTENSION_MODEL, pose, collector, packedLight, packedOverlay, outline);
pose.popPose();
pose.pushPose();
rotateAroundShaft(pose, LOWER_RING_PIVOT_Y, ringTicks * 4.0F, -5.0F);
renderModel(state, LOWER_RING_MODEL, pose, collector, packedLight, packedOverlay, outline);
pose.popPose();
pose.pushPose();
rotateAroundShaft(pose, UPPER_RING_PIVOT_Y, ringTicks * -5.5F, 7.0F);
renderModel(state, UPPER_RING_MODEL, pose, collector, packedLight, packedOverlay, outline);
pose.popPose();
pose.pushPose();
pose.translate(0.0D, Math.sin(bobPhase) * 0.08D, 0.0D);
renderModel(state, FLOATING_MODEL, pose, collector, packedLight, packedOverlay, outline);
pose.popPose();
pose.pushPose();
pose.translate(0.0D, Math.sin(bobPhase + 2.2D) * 0.065D, 0.0D);
renderModel(state, FLOATING_SECONDARY_MODEL, pose, collector, packedLight, packedOverlay, outline);
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(State state, int part, PoseStack pose, SubmitNodeCollector collector, int light, int overlay, int outline) {
// Each native item layer centers its mesh. The parent special renderer
// already applied that translation; keep the original model-space pivots.
pose.pushPose();
pose.translate(0.5D, 0.5D, 0.5D);
state.parts()[part].submit(pose, collector, light, overlay, outline);
pose.popPose();
}
}