310 lines
8.9 KiB
Java
310 lines
8.9 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.block.model.TextureSlots.Data.Builder;
|
|
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 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;
|
|
}
|
|
}
|
|
}
|