package net.minecraft.client.renderer.block.model; import com.mojang.math.Quadrant; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.annotation.Nullable; import net.minecraft.client.renderer.texture.SpriteContents; import net.minecraft.client.resources.model.Material; import net.minecraft.client.resources.model.ModelBaker; import net.minecraft.client.resources.model.ModelDebugName; import net.minecraft.client.resources.model.ModelState; import net.minecraft.client.resources.model.QuadCollection; import net.minecraft.client.resources.model.SpriteGetter; import net.minecraft.client.resources.model.UnbakedGeometry; import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.joml.Vector3f; @OnlyIn(Dist.CLIENT) public class ItemModelGenerator implements UnbakedModel { public static final ResourceLocation GENERATED_ITEM_MODEL_ID = ResourceLocation.withDefaultNamespace("builtin/generated"); public static final List LAYERS = List.of("layer0", "layer1", "layer2", "layer3", "layer4"); private static final float MIN_Z = 7.5F; private static final float MAX_Z = 8.5F; private static final TextureSlots.Data TEXTURE_SLOTS = new TextureSlots.Data.Builder().addReference("particle", "layer0").build(); private static final BlockElementFace.UVs SOUTH_FACE_UVS = new BlockElementFace.UVs(0.0F, 0.0F, 16.0F, 16.0F); private static final BlockElementFace.UVs NORTH_FACE_UVS = new BlockElementFace.UVs(16.0F, 0.0F, 0.0F, 16.0F); @Override public TextureSlots.Data textureSlots() { return TEXTURE_SLOTS; } @Override public UnbakedGeometry geometry() { return ItemModelGenerator::bake; } @Nullable @Override public UnbakedModel.GuiLight guiLight() { return UnbakedModel.GuiLight.FRONT; } private static QuadCollection bake(TextureSlots p_377946_, ModelBaker p_392334_, ModelState p_375548_, ModelDebugName p_397121_) { return bake(p_377946_, p_392334_.sprites(), p_375548_, p_397121_); } private static QuadCollection bake(TextureSlots p_378742_, SpriteGetter p_393812_, ModelState p_377118_, ModelDebugName p_396432_) { List list = new ArrayList<>(); for (int i = 0; i < LAYERS.size(); i++) { String s = LAYERS.get(i); Material material = p_378742_.getMaterial(s); if (material == null) { break; } SpriteContents spritecontents = p_393812_.get(material, p_396432_).contents(); list.addAll(processFrames(i, s, spritecontents)); } return SimpleUnbakedGeometry.bake(list, p_378742_, p_393812_, p_377118_, p_396432_); } private static List processFrames(int p_111639_, String p_111640_, SpriteContents p_251768_) { Map map = Map.of( Direction.SOUTH, new BlockElementFace(null, p_111639_, p_111640_, SOUTH_FACE_UVS, Quadrant.R0), Direction.NORTH, new BlockElementFace(null, p_111639_, p_111640_, NORTH_FACE_UVS, Quadrant.R0) ); List list = new ArrayList<>(); list.add(new BlockElement(new Vector3f(0.0F, 0.0F, 7.5F), new Vector3f(16.0F, 16.0F, 8.5F), map)); list.addAll(createSideElements(p_251768_, p_111640_, p_111639_)); return list; } private static List createSideElements(SpriteContents p_248810_, String p_111663_, int p_111664_) { float f = p_248810_.width(); float f1 = p_248810_.height(); List list = new ArrayList<>(); for (ItemModelGenerator.Span itemmodelgenerator$span : getSpans(p_248810_)) { float f2 = 0.0F; float f3 = 0.0F; float f4 = 0.0F; float f5 = 0.0F; float f6 = 0.0F; float f7 = 0.0F; float f8 = 0.0F; float f9 = 0.0F; float f10 = 16.0F / f; float f11 = 16.0F / f1; float f12 = itemmodelgenerator$span.getMin(); float f13 = itemmodelgenerator$span.getMax(); float f14 = itemmodelgenerator$span.getAnchor(); ItemModelGenerator.SpanFacing itemmodelgenerator$spanfacing = itemmodelgenerator$span.getFacing(); switch (itemmodelgenerator$spanfacing) { case UP: f6 = f12; f2 = f12; f4 = f7 = f13 + 1.0F; f8 = f14; f3 = f14; f5 = f14; f9 = f14 + 1.0F; break; case DOWN: f8 = f14; f9 = f14 + 1.0F; f6 = f12; f2 = f12; f4 = f7 = f13 + 1.0F; f3 = f14 + 1.0F; f5 = f14 + 1.0F; break; case LEFT: f6 = f14; f2 = f14; f4 = f14; f7 = f14 + 1.0F; f9 = f12; f3 = f12; f5 = f8 = f13 + 1.0F; break; case RIGHT: f6 = f14; f7 = f14 + 1.0F; f2 = f14 + 1.0F; f4 = f14 + 1.0F; f9 = f12; f3 = f12; f5 = f8 = f13 + 1.0F; } f2 *= f10; f4 *= f10; f3 *= f11; f5 *= f11; f3 = 16.0F - f3; f5 = 16.0F - f5; f6 *= f10; f7 *= f10; f8 *= f11; f9 *= f11; Map map = Map.of( itemmodelgenerator$spanfacing.getDirection(), new BlockElementFace(null, p_111664_, p_111663_, new BlockElementFace.UVs(f6, f8, f7, f9), Quadrant.R0) ); switch (itemmodelgenerator$spanfacing) { case UP: list.add(new BlockElement(new Vector3f(f2, f3, 7.5F), new Vector3f(f4, f3, 8.5F), map)); break; case DOWN: list.add(new BlockElement(new Vector3f(f2, f5, 7.5F), new Vector3f(f4, f5, 8.5F), map)); break; case LEFT: list.add(new BlockElement(new Vector3f(f2, f3, 7.5F), new Vector3f(f2, f5, 8.5F), map)); break; case RIGHT: list.add(new BlockElement(new Vector3f(f4, f3, 7.5F), new Vector3f(f4, f5, 8.5F), map)); } } return list; } private static List getSpans(SpriteContents p_250338_) { int i = p_250338_.width(); int j = p_250338_.height(); List list = new ArrayList<>(); p_250338_.getUniqueFrames().forEach(p_389479_ -> { for (int k = 0; k < j; k++) { for (int l = 0; l < i; l++) { boolean flag = !isTransparent(p_250338_, p_389479_, l, k, i, j); checkTransition(ItemModelGenerator.SpanFacing.UP, list, p_250338_, p_389479_, l, k, i, j, flag); checkTransition(ItemModelGenerator.SpanFacing.DOWN, list, p_250338_, p_389479_, l, k, i, j, flag); checkTransition(ItemModelGenerator.SpanFacing.LEFT, list, p_250338_, p_389479_, l, k, i, j, flag); checkTransition(ItemModelGenerator.SpanFacing.RIGHT, list, p_250338_, p_389479_, l, k, i, j, flag); } } }); return list; } private static void checkTransition( ItemModelGenerator.SpanFacing p_251572_, List p_248882_, SpriteContents p_249847_, int p_250616_, int p_251416_, int p_249664_, int p_250174_, int p_250897_, boolean p_248773_ ) { boolean flag = isTransparent(p_249847_, p_250616_, p_251416_ + p_251572_.getXOffset(), p_249664_ + p_251572_.getYOffset(), p_250174_, p_250897_) && p_248773_; if (flag) { createOrExpandSpan(p_248882_, p_251572_, p_251416_, p_249664_); } } private static void createOrExpandSpan(List p_111666_, ItemModelGenerator.SpanFacing p_111667_, int p_111668_, int p_111669_) { ItemModelGenerator.Span itemmodelgenerator$span = null; for (ItemModelGenerator.Span itemmodelgenerator$span1 : p_111666_) { if (itemmodelgenerator$span1.getFacing() == p_111667_) { int i = p_111667_.isHorizontal() ? p_111669_ : p_111668_; if (itemmodelgenerator$span1.getAnchor() == i) { itemmodelgenerator$span = itemmodelgenerator$span1; break; } } } int j = p_111667_.isHorizontal() ? p_111669_ : p_111668_; int k = p_111667_.isHorizontal() ? p_111668_ : p_111669_; if (itemmodelgenerator$span == null) { p_111666_.add(new ItemModelGenerator.Span(p_111667_, k, j)); } else { itemmodelgenerator$span.expand(k); } } private static boolean isTransparent(SpriteContents p_249650_, int p_250692_, int p_251914_, int p_252343_, int p_250258_, int p_248997_) { return p_251914_ >= 0 && p_252343_ >= 0 && p_251914_ < p_250258_ && p_252343_ < p_248997_ ? p_249650_.isTransparent(p_250692_, p_251914_, p_252343_) : true; } @OnlyIn(Dist.CLIENT) static class Span { private final ItemModelGenerator.SpanFacing facing; private int min; private int max; private final int anchor; public Span(ItemModelGenerator.SpanFacing p_111680_, int p_111681_, int p_111682_) { this.facing = p_111680_; this.min = p_111681_; this.max = p_111681_; this.anchor = p_111682_; } public void expand(int p_111685_) { if (p_111685_ < this.min) { this.min = p_111685_; } else if (p_111685_ > this.max) { this.max = p_111685_; } } public ItemModelGenerator.SpanFacing getFacing() { return this.facing; } public int getMin() { return this.min; } public int getMax() { return this.max; } public int getAnchor() { return this.anchor; } } @OnlyIn(Dist.CLIENT) static enum SpanFacing { UP(Direction.UP, 0, -1), DOWN(Direction.DOWN, 0, 1), LEFT(Direction.EAST, -1, 0), RIGHT(Direction.WEST, 1, 0); private final Direction direction; private final int xOffset; private final int yOffset; private SpanFacing(final Direction p_111701_, final int p_111702_, final int p_111703_) { this.direction = p_111701_; this.xOffset = p_111702_; this.yOffset = p_111703_; } public Direction getDirection() { return this.direction; } public int getXOffset() { return this.xOffset; } public int getYOffset() { return this.yOffset; } boolean isHorizontal() { return this == DOWN || this == UP; } } }