309 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			309 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| 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 net.fabricmc.api.EnvType;
 | |
| import net.fabricmc.api.Environment;
 | |
| 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 org.jetbrains.annotations.Nullable;
 | |
| import org.joml.Vector3f;
 | |
| 
 | |
| @Environment(EnvType.CLIENT)
 | |
| public class ItemModelGenerator implements UnbakedModel {
 | |
| 	public static final ResourceLocation GENERATED_ITEM_MODEL_ID = ResourceLocation.withDefaultNamespace("builtin/generated");
 | |
| 	public static final List<String> 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 textureSlots, ModelBaker baker, ModelState modelState, ModelDebugName debugName) {
 | |
| 		return bake(textureSlots, baker.sprites(), modelState, debugName);
 | |
| 	}
 | |
| 
 | |
| 	private static QuadCollection bake(TextureSlots textureSlots, SpriteGetter sprites, ModelState modelState, ModelDebugName debugName) {
 | |
| 		List<BlockElement> list = new ArrayList();
 | |
| 
 | |
| 		for (int i = 0; i < LAYERS.size(); i++) {
 | |
| 			String string = (String)LAYERS.get(i);
 | |
| 			Material material = textureSlots.getMaterial(string);
 | |
| 			if (material == null) {
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			SpriteContents spriteContents = sprites.get(material, debugName).contents();
 | |
| 			list.addAll(processFrames(i, string, spriteContents));
 | |
| 		}
 | |
| 
 | |
| 		return SimpleUnbakedGeometry.bake(list, textureSlots, sprites, modelState, debugName);
 | |
| 	}
 | |
| 
 | |
| 	private static List<BlockElement> processFrames(int tintIndex, String texture, SpriteContents sprite) {
 | |
| 		Map<Direction, BlockElementFace> map = Map.of(
 | |
| 			Direction.SOUTH,
 | |
| 			new BlockElementFace(null, tintIndex, texture, SOUTH_FACE_UVS, Quadrant.R0),
 | |
| 			Direction.NORTH,
 | |
| 			new BlockElementFace(null, tintIndex, texture, NORTH_FACE_UVS, Quadrant.R0)
 | |
| 		);
 | |
| 		List<BlockElement> 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(sprite, texture, tintIndex));
 | |
| 		return list;
 | |
| 	}
 | |
| 
 | |
| 	private static List<BlockElement> createSideElements(SpriteContents sprite, String texture, int tintIndex) {
 | |
| 		float f = sprite.width();
 | |
| 		float g = sprite.height();
 | |
| 		List<BlockElement> list = new ArrayList();
 | |
| 
 | |
| 		for (ItemModelGenerator.Span span : getSpans(sprite)) {
 | |
| 			float h = 0.0F;
 | |
| 			float i = 0.0F;
 | |
| 			float j = 0.0F;
 | |
| 			float k = 0.0F;
 | |
| 			float l = 0.0F;
 | |
| 			float m = 0.0F;
 | |
| 			float n = 0.0F;
 | |
| 			float o = 0.0F;
 | |
| 			float p = 16.0F / f;
 | |
| 			float q = 16.0F / g;
 | |
| 			float r = span.getMin();
 | |
| 			float s = span.getMax();
 | |
| 			float t = span.getAnchor();
 | |
| 			ItemModelGenerator.SpanFacing spanFacing = span.getFacing();
 | |
| 			switch (spanFacing) {
 | |
| 				case UP:
 | |
| 					l = r;
 | |
| 					h = r;
 | |
| 					j = m = s + 1.0F;
 | |
| 					n = t;
 | |
| 					i = t;
 | |
| 					k = t;
 | |
| 					o = t + 1.0F;
 | |
| 					break;
 | |
| 				case DOWN:
 | |
| 					n = t;
 | |
| 					o = t + 1.0F;
 | |
| 					l = r;
 | |
| 					h = r;
 | |
| 					j = m = s + 1.0F;
 | |
| 					i = t + 1.0F;
 | |
| 					k = t + 1.0F;
 | |
| 					break;
 | |
| 				case LEFT:
 | |
| 					l = t;
 | |
| 					h = t;
 | |
| 					j = t;
 | |
| 					m = t + 1.0F;
 | |
| 					o = r;
 | |
| 					i = r;
 | |
| 					k = n = s + 1.0F;
 | |
| 					break;
 | |
| 				case RIGHT:
 | |
| 					l = t;
 | |
| 					m = t + 1.0F;
 | |
| 					h = t + 1.0F;
 | |
| 					j = t + 1.0F;
 | |
| 					o = r;
 | |
| 					i = r;
 | |
| 					k = n = s + 1.0F;
 | |
| 			}
 | |
| 
 | |
| 			h *= p;
 | |
| 			j *= p;
 | |
| 			i *= q;
 | |
| 			k *= q;
 | |
| 			i = 16.0F - i;
 | |
| 			k = 16.0F - k;
 | |
| 			l *= p;
 | |
| 			m *= p;
 | |
| 			n *= q;
 | |
| 			o *= q;
 | |
| 			Map<Direction, BlockElementFace> map = Map.of(
 | |
| 				spanFacing.getDirection(), new BlockElementFace(null, tintIndex, texture, new BlockElementFace.UVs(l, n, m, o), Quadrant.R0)
 | |
| 			);
 | |
| 			switch (spanFacing) {
 | |
| 				case UP:
 | |
| 					list.add(new BlockElement(new Vector3f(h, i, 7.5F), new Vector3f(j, i, 8.5F), map));
 | |
| 					break;
 | |
| 				case DOWN:
 | |
| 					list.add(new BlockElement(new Vector3f(h, k, 7.5F), new Vector3f(j, k, 8.5F), map));
 | |
| 					break;
 | |
| 				case LEFT:
 | |
| 					list.add(new BlockElement(new Vector3f(h, i, 7.5F), new Vector3f(h, k, 8.5F), map));
 | |
| 					break;
 | |
| 				case RIGHT:
 | |
| 					list.add(new BlockElement(new Vector3f(j, i, 7.5F), new Vector3f(j, k, 8.5F), map));
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return list;
 | |
| 	}
 | |
| 
 | |
| 	private static List<ItemModelGenerator.Span> getSpans(SpriteContents sprite) {
 | |
| 		int i = sprite.width();
 | |
| 		int j = sprite.height();
 | |
| 		List<ItemModelGenerator.Span> list = new ArrayList();
 | |
| 		sprite.getUniqueFrames().forEach(k -> {
 | |
| 			for (int l = 0; l < j; l++) {
 | |
| 				for (int m = 0; m < i; m++) {
 | |
| 					boolean bl = !isTransparent(sprite, k, m, l, i, j);
 | |
| 					checkTransition(ItemModelGenerator.SpanFacing.UP, list, sprite, k, m, l, i, j, bl);
 | |
| 					checkTransition(ItemModelGenerator.SpanFacing.DOWN, list, sprite, k, m, l, i, j, bl);
 | |
| 					checkTransition(ItemModelGenerator.SpanFacing.LEFT, list, sprite, k, m, l, i, j, bl);
 | |
| 					checkTransition(ItemModelGenerator.SpanFacing.RIGHT, list, sprite, k, m, l, i, j, bl);
 | |
| 				}
 | |
| 			}
 | |
| 		});
 | |
| 		return list;
 | |
| 	}
 | |
| 
 | |
| 	private static void checkTransition(
 | |
| 		ItemModelGenerator.SpanFacing spanFacing,
 | |
| 		List<ItemModelGenerator.Span> listSpans,
 | |
| 		SpriteContents contents,
 | |
| 		int frameIndex,
 | |
| 		int pixelX,
 | |
| 		int pixelY,
 | |
| 		int spriteWidth,
 | |
| 		int spriteHeight,
 | |
| 		boolean transparent
 | |
| 	) {
 | |
| 		boolean bl = isTransparent(contents, frameIndex, pixelX + spanFacing.getXOffset(), pixelY + spanFacing.getYOffset(), spriteWidth, spriteHeight)
 | |
| 			&& transparent;
 | |
| 		if (bl) {
 | |
| 			createOrExpandSpan(listSpans, spanFacing, pixelX, pixelY);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private static void createOrExpandSpan(List<ItemModelGenerator.Span> listSpans, ItemModelGenerator.SpanFacing spanFacing, int pixelX, int pixelY) {
 | |
| 		ItemModelGenerator.Span span = null;
 | |
| 
 | |
| 		for (ItemModelGenerator.Span span2 : listSpans) {
 | |
| 			if (span2.getFacing() == spanFacing) {
 | |
| 				int i = spanFacing.isHorizontal() ? pixelY : pixelX;
 | |
| 				if (span2.getAnchor() == i) {
 | |
| 					span = span2;
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		int j = spanFacing.isHorizontal() ? pixelY : pixelX;
 | |
| 		int k = spanFacing.isHorizontal() ? pixelX : pixelY;
 | |
| 		if (span == null) {
 | |
| 			listSpans.add(new ItemModelGenerator.Span(spanFacing, k, j));
 | |
| 		} else {
 | |
| 			span.expand(k);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private static boolean isTransparent(SpriteContents sprite, int frameIndex, int pixelX, int pixelY, int spriteWidth, int spriteHeight) {
 | |
| 		return pixelX >= 0 && pixelY >= 0 && pixelX < spriteWidth && pixelY < spriteHeight ? sprite.isTransparent(frameIndex, pixelX, pixelY) : true;
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	static class Span {
 | |
| 		private final ItemModelGenerator.SpanFacing facing;
 | |
| 		private int min;
 | |
| 		private int max;
 | |
| 		private final int anchor;
 | |
| 
 | |
| 		public Span(ItemModelGenerator.SpanFacing facing, int minMax, int anchor) {
 | |
| 			this.facing = facing;
 | |
| 			this.min = minMax;
 | |
| 			this.max = minMax;
 | |
| 			this.anchor = anchor;
 | |
| 		}
 | |
| 
 | |
| 		public void expand(int pos) {
 | |
| 			if (pos < this.min) {
 | |
| 				this.min = pos;
 | |
| 			} else if (pos > this.max) {
 | |
| 				this.max = pos;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		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;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.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 direction, final int xOffset, final int yOffset) {
 | |
| 			this.direction = direction;
 | |
| 			this.xOffset = xOffset;
 | |
| 			this.yOffset = yOffset;
 | |
| 		}
 | |
| 
 | |
| 		/**
 | |
| 		 * Gets the direction of the block's facing.
 | |
| 		 */
 | |
| 		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;
 | |
| 		}
 | |
| 	}
 | |
| }
 |