Add more model patches (#373)

This commit is contained in:
ah-OOG-ah authored and GitHub committed 2026-05-30 09:12:32 +02:00
1 parent d9d69087a5
commit cab49dc8de
24 files changed
+237 -169

No files matched your search

@@ -29,10 +29,12 @@ import com.gtnewhorizon.gtnhlib.network.PacketMessageAboveHotbar;
import com.gtnewhorizon.gtnhlib.network.PacketViewDistance;
import com.gtnewhorizon.gtnhlib.teams.TeamAdminCommand;
import com.gtnewhorizon.gtnhlib.teams.TeamCommand;
import com.gtnewhorizon.gtnhlib.test.block.BlockRngTest;
import com.gtnewhorizon.gtnhlib.test.block.BlockTest;
import com.gtnewhorizon.gtnhlib.test.block.BlockTestLectern;
import com.gtnewhorizon.gtnhlib.test.block.BlockTestTint;
import com.gtnewhorizon.gtnhlib.test.block.BlockTestTintMul;
import com.gtnewhorizon.gtnhlib.test.block.BlockWeightedRngTest;
import com.gtnewhorizon.gtnhlib.test.item.TestItem;
import com.gtnewhorizon.gtnhlib.util.numberformatting.NumberFormatConfig;
import com.gtnewhorizon.gtnhlib.util.numberformatting.NumberFormatUtil;
@@ -66,6 +68,8 @@ public class CommonProxy {
BlockTestLectern.register();
BlockTestTint.register();
BlockTestTintMul.register();
BlockRngTest.register();
BlockWeightedRngTest.register();
}
if (GTNHLibConfig.enableTestItems) {
@@ -1,7 +1,11 @@
package com.gtnewhorizon.gtnhlib.client.model;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8.*;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing.*;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8.unpackX;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8.unpackY;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8.unpackZ;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing.DIRECTIONS;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing.VALUES;
import static com.gtnewhorizon.gtnhlib.util.CoordinatePacker.pack;
import static java.lang.Float.intBitsToFloat;
import static java.lang.Math.max;
import static net.minecraftforge.client.IItemRenderer.ItemRenderType.ENTITY;
@@ -18,7 +22,6 @@ import net.minecraft.client.renderer.Tessellator;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.IItemRenderer;
@@ -38,6 +41,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.ModelQuadView;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing;
import com.gtnewhorizon.gtnhlib.core.fml.transformers.BlockIconTransformer;
import com.gtnewhorizon.gtnhlib.util.StdLCG;
import com.gtnewhorizons.angelica.api.ThreadSafeISBRH;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
@@ -53,7 +57,7 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
/// implement {@link BlockModelInfo} instead, and simply always return true.
public static final int JSON_ISBRH_ID = RenderingRegistry.getNextAvailableRenderId();
private final Random RAND = new Random();
private final Random RAND = new StdLCG();
private final WorldContext worldContext = new WorldContext();
private final ItemContext itemContext = new ItemContext();
@@ -66,26 +70,21 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
@Override
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId,
RenderBlocks renderer) {
final Random random = world instanceof World worldIn ? worldIn.rand : RAND;
final Tessellator tesselator = TessellatorManager.get();
// Get the model!
final int meta = world.getBlockMetadata(x, y, z);
worldContext.world = world;
worldContext.x = x;
worldContext.y = y;
worldContext.z = z;
worldContext.blockState = BlockPropertyRegistry.getBlockState(world, x, y, z);
worldContext.random = random;
worldContext.set(world, x, y, z, RAND);
final BakedModel model = ModelRegistry.getBakedModel(worldContext);
int color = model.getColor(world, x, y, z, block, meta, random);
int color = model.getColor(world, x, y, z, block, meta, RAND);
var rendered = false;
for (var dir : VALUES) {
worldContext.quadFacing = dir;
worldContext.random.setSeed(pack(x, y, z));
final var quads = model.getQuads(worldContext);
if (quads.isEmpty()) continue;
if (dir.isDirection() && !renderer.renderAllFaces
@@ -230,7 +229,7 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
}
// The face is inset to some degree, pick self light (if transparent)
if (block.getLightOpacity(world, x, y, z) != 0) {
if (block.getLightOpacity(world, x, y, z) < 255) {
return getBrightness(block, quad, world, x, y, z);
}
@@ -306,6 +305,8 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
itemContext.stack = stack;
itemContext.blockState = BlockPropertyRegistry.getBlockState(stack);
itemContext.random = RAND;
// I mean, I *could* pack 0, 0, 0. But that seems like a waste when I know the answer...
itemContext.random.setSeed(0);
final BakedModel model = ModelRegistry.getBakedModel(itemContext);
@@ -318,6 +319,9 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
int color = model.getColor(null, 0, 0, 0, block, meta, RAND);
for (ModelQuadFacing dir : VALUES) {
// I don't *like* reseeding the RNG before each quad get, but it seems like the only way to ensure a
// consistent RNG state for each side.
itemContext.random.setSeed(0);
itemContext.quadFacing = dir;
final var quads = model.getQuads(itemContext);
@@ -16,16 +16,16 @@ public final class Weighted<T> {
}
public static <T> T selectOne(ObjectList<Weighted<T>> heavyThings, Random rand) {
var maxWeight = 0;
for (var v : heavyThings) {
maxWeight += v.weight();
}
final var selector = rand.nextInt(maxWeight) + 1;
var weight = 0;
for (var v : heavyThings) {
weight += v.weight();
}
final var selector = rand.nextInt(weight);
weight = 0;
for (var v : heavyThings) {
if (selector <= weight) return v.thing();
weight += v.weight();
}
throw new IllegalStateException("Randomly selected beyond the list!");
@@ -1,5 +1,7 @@
package com.gtnewhorizon.gtnhlib.client.model;
import static com.gtnewhorizon.gtnhlib.util.CoordinatePacker.pack;
import java.util.Random;
import java.util.function.Supplier;
@@ -8,6 +10,7 @@ import net.minecraft.world.IBlockAccess;
import org.jetbrains.annotations.Nullable;
import com.gtnewhorizon.gtnhlib.blockstate.core.BlockState;
import com.gtnewhorizon.gtnhlib.blockstate.registry.BlockPropertyRegistry;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.ModelQuadViewMutable;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing;
@@ -29,6 +32,16 @@ public class WorldContext implements BakedModelQuadContext.World {
this.quadPool = null;
}
public void set(IBlockAccess world, int x, int y, int z, Random random) {
this.world = world;
this.x = x;
this.y = y;
this.z = z;
this.blockState = BlockPropertyRegistry.getBlockState(world, x, y, z);
this.random = random;
random.setSeed(pack(x, y, z));
}
@Override
public IBlockAccess getWorld() {
return world;
@@ -1,5 +1,6 @@
package com.gtnewhorizon.gtnhlib.client.model.baked;
import static com.gtnewhorizon.gtnhlib.client.model.loading.ModelDeserializer.Position.ModelDisplay.DEFAULT;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing.NEG_X;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing.NEG_Y;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing.NEG_Z;
@@ -18,6 +19,7 @@ import org.jetbrains.annotations.Nullable;
import com.gtnewhorizon.gtnhlib.client.model.BakedModelQuadContext;
import com.gtnewhorizon.gtnhlib.client.model.loading.ModelDeserializer.Position;
import com.gtnewhorizon.gtnhlib.client.model.loading.ModelDeserializer.Position.ModelDisplay;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.ModelQuadView;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing;
@@ -26,17 +28,17 @@ import it.unimi.dsi.fastutil.objects.ObjectImmutableList;
public final class PileOfQuads implements BakedModel {
private final SidedQuadStore sidedQuadStore;
private final Map<Position, Position.ModelDisplay> display;
private final Map<Position, ModelDisplay> display;
private final IIcon particle;
public PileOfQuads(SidedQuadStore sidedQuadStore, Map<Position, Position.ModelDisplay> display, IIcon particle) {
public PileOfQuads(SidedQuadStore sidedQuadStore, Map<Position, ModelDisplay> display, IIcon particle) {
this.sidedQuadStore = sidedQuadStore;
this.display = display;
this.particle = particle;
}
public PileOfQuads(Map<ModelQuadFacing, ArrayList<ModelQuadView>> sidedQuadStore,
Map<Position, Position.ModelDisplay> display, IIcon particle) {
Map<Position, ModelDisplay> display, IIcon particle) {
this(new SidedQuadStore(sidedQuadStore), display, particle);
}
@@ -46,8 +48,9 @@ public final class PileOfQuads implements BakedModel {
}
@Override
public Position.ModelDisplay getDisplay(Position pos, BakedModelQuadContext context) {
return display.getOrDefault(pos, Position.ModelDisplay.DEFAULT);
public ModelDisplay getDisplay(Position pos, BakedModelQuadContext context) {
if (display == null) return DEFAULT;
return display.getOrDefault(pos, DEFAULT);
}
@Override
@@ -68,8 +71,8 @@ public final class PileOfQuads implements BakedModel {
private final ObjectImmutableList<ModelQuadView> unknown;
public SidedQuadStore(Map<ModelQuadFacing, ArrayList<ModelQuadView>> sidedQuadStore) {
up = lockList(sidedQuadStore.get(POS_Y));
down = lockList(sidedQuadStore.get(NEG_Y));
up = lockList(sidedQuadStore.get(POS_Y));
north = lockList(sidedQuadStore.get(NEG_Z));
south = lockList(sidedQuadStore.get(POS_Z));
west = lockList(sidedQuadStore.get(NEG_X));
@@ -79,8 +82,8 @@ public final class PileOfQuads implements BakedModel {
public List<ModelQuadView> getQuads(ModelQuadFacing dir) {
return switch (dir) {
case POS_Y -> up;
case NEG_Y -> down;
case POS_Y -> up;
case NEG_Z -> north;
case POS_Z -> south;
case NEG_X -> west;
@@ -103,10 +103,7 @@ public class ModelRegistry {
// Caching this would be a little pointless, since an UnbakedModel here would map directly to the BakedModel
// missing from the cache... that's why we're loading one from scratch. The JSONModel *used* by the UnbakedModel
// will be cached, however.
final var dough = smm.selectModel(state);
if (dough == null) return MISSING_MODEL.bake();
return dough.bake();
return smm.selectModel(state).bake();
}
private static StateModelMap getStateModelMap(Block block) {
@@ -116,9 +113,7 @@ public class ModelRegistry {
}
private static JSONModel loadAndResolveJSONModel(ResourceLoc.ModelLoc loc) {
final var m = loc.load(() -> MISSING_MODEL, GSON);
m.resolveParents(JSON_MODEL_CACHE::get);
return m;
return loc.load(() -> MISSING_MODEL, GSON).resolveParents(JSON_MODEL_CACHE::get);
}
private static final class BlockName {
@@ -1,5 +1,9 @@
package com.gtnewhorizon.gtnhlib.client.model.state;
import static com.gtnewhorizon.gtnhlib.client.model.unbaked.MissingModel.MISSING_MODEL;
import org.jetbrains.annotations.NotNull;
import com.gtnewhorizon.gtnhlib.blockstate.core.BlockState;
import com.gtnewhorizon.gtnhlib.client.model.unbaked.UnbakedModel;
@@ -8,7 +12,7 @@ public class MissingState implements StateModelMap {
public static final MissingState MISSING_STATE_MAP = new MissingState();
@Override
public UnbakedModel selectModel(BlockState state) {
return null;
public @NotNull UnbakedModel selectModel(BlockState state) {
return MISSING_MODEL;
}
}
@@ -1,8 +1,12 @@
package com.gtnewhorizon.gtnhlib.client.model.state;
import static com.gtnewhorizon.gtnhlib.client.model.unbaked.MissingModel.MISSING_MODEL;
import java.util.Map;
import java.util.Objects;
import org.jetbrains.annotations.NotNull;
import com.gtnewhorizon.gtnhlib.blockstate.core.BlockState;
import com.gtnewhorizon.gtnhlib.client.model.JSONVariant;
import com.gtnewhorizon.gtnhlib.client.model.Weighted;
@@ -24,7 +28,7 @@ public class MonopartState implements StateModelMap {
}
@Override
public UnbakedModel selectModel(BlockState state) {
public @NotNull UnbakedModel selectModel(BlockState state) {
Map<String, String> properties = state.toMap();
final var iter = Object2ObjectMaps.fastIterator(variants);
@@ -34,7 +38,7 @@ public class MonopartState implements StateModelMap {
if (match.matches(properties)) return new MonopartDough(e.getValue());
}
return null;
return MISSING_MODEL;
}
static class StateMatch {
@@ -3,6 +3,7 @@ package com.gtnewhorizon.gtnhlib.client.model.state;
import java.util.Map;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import com.gtnewhorizon.gtnhlib.blockstate.core.BlockState;
import com.gtnewhorizon.gtnhlib.client.model.BakeData;
@@ -26,7 +27,7 @@ public class MultipartState implements StateModelMap, UnbakedModel {
}
@Override
public UnbakedModel selectModel(BlockState state) {
public @NotNull UnbakedModel selectModel(BlockState state) {
return this;
}
@@ -1,16 +1,14 @@
package com.gtnewhorizon.gtnhlib.client.model.state;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
import com.gtnewhorizon.gtnhlib.blockstate.core.BlockState;
import com.gtnewhorizon.gtnhlib.client.model.unbaked.UnbakedModel;
public interface StateModelMap {
/**
* @param state The blockstate properties as strings
* @return The appropriate model, or null if none match
*/
@Nullable
/// @param state The blockstate properties as strings
/// @return The appropriate model, or [MissingModel#MISSING_MODEL] if none match
@NotNull
UnbakedModel selectModel(BlockState state);
}
@@ -3,7 +3,10 @@ package com.gtnewhorizon.gtnhlib.client.model.unbaked;
import static com.gtnewhorizon.gtnhlib.client.model.loading.ModelDeserializer.ModelElement.Rotation.NOOP;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing.NEG_Y;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing.POS_Y;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing.UNASSIGNED;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing.fromForgeDir;
import static com.gtnewhorizon.gtnhlib.core.GTNHLibCore.MODEL_LOGGER;
import static com.gtnewhorizon.gtnhlib.util.DirectionUtil.rotateFacing;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static org.joml.Math.fma;
@@ -39,7 +42,6 @@ import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.ModelQuad;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.ModelQuadView;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.ModelQuadViewMutable;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.util.MathUtil;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
@@ -55,12 +57,14 @@ public class JSONModel implements UnbakedModel {
protected final Map<Position, ModelDisplay> display;
@NotNull
protected final Object2ObjectMap<String, String> textures;
@NotNull
protected List<ModelDeserializer.ModelElement> elements;
protected static final Vector4f DEFAULT_UV = new Vector4f(0, 0, 16, 16);
public JSONModel(@Nullable ModelLoc parentId, boolean useAO, Map<Position, ModelDisplay> display,
@NotNull Object2ObjectMap<String, String> textures, List<ModelDeserializer.ModelElement> elements) {
@NotNull Object2ObjectMap<String, String> textures,
@NotNull List<ModelDeserializer.ModelElement> elements) {
this.parentId = parentId;
this.useAO = useAO;
this.display = display;
@@ -178,11 +182,10 @@ public class JSONModel implements UnbakedModel {
Z = max(Z, vert.z);
}
// Set shading and lighting
// Set shading properties
quad.setEmissiveness(e.lightEmission());
quad.setDirectionalShading(e.shade());
quad.setHasAmbientOcclusion(this.useAO);
quad.setLightFace(ModelQuadFacing.fromForgeDir(f.name()));
// Set UV
Vector4f uv = Objects.firstNonNull(f.uv(), DEFAULT_UV);
@@ -214,6 +217,11 @@ public class JSONModel implements UnbakedModel {
// Set the sprite
var texKey = f.texture();
var texName = textures.get(texKey);
if (texName == null) {
MODEL_LOGGER.warn("Model {} has no texture for variable {}!", this, texKey);
texName = "minecraft:missing";
}
if (texName.startsWith("#")) {
MODEL_LOGGER.warn("Model {} has unflattened texture variable {} when baking!", this, texName);
textures.put(texKey, "minecraft:missing");
@@ -224,35 +232,13 @@ public class JSONModel implements UnbakedModel {
// Set the tint index
quad.setColorIndex(f.tintIndex());
// Bake and add it
// Rotate the cull face and lighting face
final var cullFace = rotateFacing(fromForgeDir(f.cullFace()), vRot);
final var lightFace = rotateFacing(fromForgeDir(f.name()), vRot);
// Light face may not be unassigned
quad.setLightFace(lightFace == UNASSIGNED ? POS_Y : lightFace);
ModelQuadFacing cullFace = ModelQuadFacing.fromForgeDir(f.cullFace());
if (cullFace.isDirection()) {
// If cullface is not unassigned, we rotate it by the affine matrix, so that way the direction we
// check for culling also is rotated.
Vector3f facing = new Vector3f(cullFace.getStepX(), cullFace.getStepY(), cullFace.getStepZ())
.mulDirection(vRot);
// Only one of these three vector coordinates should be a value other than 0. Error handling should
// be done where the vRot's data is serialized.
if (MathUtil.roughlyEqual(facing.x, 1)) {
cullFace = ModelQuadFacing.POS_X;
}
if (MathUtil.roughlyEqual(facing.x, -1)) {
cullFace = ModelQuadFacing.NEG_X;
}
if (MathUtil.roughlyEqual(facing.y, 1)) {
cullFace = ModelQuadFacing.POS_Y;
}
if (MathUtil.roughlyEqual(facing.y, -1)) {
cullFace = ModelQuadFacing.NEG_Y;
}
if (MathUtil.roughlyEqual(facing.z, 1)) {
cullFace = ModelQuadFacing.POS_Z;
}
if (MathUtil.roughlyEqual(facing.z, -1)) {
cullFace = ModelQuadFacing.NEG_Z;
}
}
// Add the quad to the sided store
sidedQuadStore.computeIfAbsent(cullFace, d -> new ArrayList<>()).add(quad);
}
}
@@ -277,23 +263,23 @@ public class JSONModel implements UnbakedModel {
}
}
public void resolveParents(Function<ModelLoc, JSONModel> modelLoader) {
/// @return A JSON model which is the result of model resolution. Note that while this is *usually* the caller, it's
/// not always - missing parents, for example, will propogate a missing model down.
public JSONModel resolveParents(Function<ModelLoc, JSONModel> modelLoader) {
if (this.parentId == null || this.parent != null) {
return;
return this;
}
final JSONModel p = modelLoader.apply(this.parentId);
p.resolveParents(modelLoader);
// Inherit properties
this.parent = p;
this.parent = modelLoader.apply(this.parentId).resolveParents(modelLoader);
if (parent instanceof MissingModel) return parent;
if (this.elements.isEmpty()) this.elements = this.parent.elements;
// Resolve texture variables
// Add parent texture mappings, but prioritize ours.
for (Map.Entry<String, String> e : this.parent.textures.entrySet()) {
this.textures.putIfAbsent(e.getKey(), e.getValue());
}
@@ -320,11 +306,12 @@ public class JSONModel implements UnbakedModel {
}
if (this.parent != null && this.parent.display != null) {
for (Map.Entry<Position, ModelDisplay> e : this.parent.display.entrySet()) {
this.display.putIfAbsent(e.getKey(), e.getValue());
}
}
return this;
}
protected IIcon getParticle() {
@@ -1,25 +1,20 @@
package com.gtnewhorizon.gtnhlib.client.model.unbaked;
import static com.gtnewhorizon.gtnhlib.client.model.loading.ModelDeserializer.ModelElement.Rotation.NOOP;
import static net.minecraftforge.common.util.ForgeDirection.DOWN;
import static net.minecraftforge.common.util.ForgeDirection.EAST;
import static net.minecraftforge.common.util.ForgeDirection.NORTH;
import static net.minecraftforge.common.util.ForgeDirection.SOUTH;
import static net.minecraftforge.common.util.ForgeDirection.UP;
import static net.minecraftforge.common.util.ForgeDirection.WEST;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.util.IIcon;
import net.minecraftforge.common.util.ForgeDirection;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.joml.Vector4f;
import com.gtnewhorizon.gtnhlib.client.model.BakeData;
import com.gtnewhorizon.gtnhlib.client.model.baked.BakedModel;
import com.gtnewhorizon.gtnhlib.client.model.baked.PileOfQuads;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.ModelQuad;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.ModelQuadView;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing;
import com.gtnewhorizon.gtnhlib.client.model.loading.ModelDeserializer.ModelElement;
import com.gtnewhorizon.gtnhlib.client.model.loading.ModelDeserializer.ModelElement.Face;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
@@ -28,78 +23,17 @@ public class MissingModel extends JSONModel {
public static final MissingModel MISSING_MODEL = new MissingModel();
public MissingModel() {
super(null, false, null, Object2ObjectMaps.emptyMap(), null);
}
super(null, true, null, Object2ObjectMaps.emptyMap(), new ArrayList<>());
@Override
public BakedModel bake() {
return super.bake();
}
final var uv = new Vector4f(0, 0, 16, 16);
final var faces = new ArrayList<Face>();
faces.add(new Face(DOWN, uv, "missingno", DOWN, 0, 0));
faces.add(new Face(UP, uv, "missingno", UP, 0, 0));
faces.add(new Face(WEST, uv, "missingno", WEST, 0, 0));
faces.add(new Face(EAST, uv, "missingno", EAST, 0, 0));
faces.add(new Face(NORTH, uv, "missingno", NORTH, 0, 0));
faces.add(new Face(SOUTH, uv, "missingno", SOUTH, 0, 0));
/**
* Overrides the default baking logic to bake a hardcoded cube. Used when a state not specified in the blockstate
* file is chosen to render. Also appears when the model provided is missing.
* <p>
* TODO: Possible memory leak since a new one is baked for every missing state. Perhaps we could store a singleton
* that's cached on first bake, that is reset on resource reload. Would that need a threadlocal?
*/
@Override
public BakedModel bake(BakeData data) {
final var vRot = data.getAffineMatrix();
final var sidedQuadStore = new HashMap<ModelQuadFacing, ArrayList<ModelQuadView>>(7);
for (ForgeDirection facing : ForgeDirection.VALID_DIRECTIONS) {
ModelQuadFacing quadFacing = ModelQuadFacing.fromForgeDir(facing);
final Matrix4f rot = NOOP.getAffineMatrix();
// The from vector is the minimum corner of the face's element
final Vector3f from = switch (quadFacing) {
case POS_X -> new Vector3f(1, 0, 0);
case POS_Y -> new Vector3f(0, 1, 0);
case POS_Z -> new Vector3f(0, 0, 1);
case NEG_X, NEG_Y, NEG_Z -> new Vector3f(0, 0, 0);
case UNASSIGNED -> null;
};
// The to vector is the maximum corner of the face's element
final Vector3f to = switch (quadFacing) {
// POS_X (Fixed X=1, Y/Z span 0->1)
case POS_X, POS_Y, POS_Z -> new Vector3f(1, 1, 1);
case NEG_X -> new Vector3f(0, 1, 1);
case NEG_Y -> new Vector3f(1, 0, 1);
case NEG_Z -> new Vector3f(1, 1, 0);
case UNASSIGNED -> null;
};
// Assign vertexes
final var quad = new ModelQuad();
for (int i = 0; i < 4; ++i) {
final Vector3f vert = mapSideToVertex(from, to, i, facing).mulPosition(rot).mulPosition(vRot);
quad.setX(i, vert.x);
quad.setY(i, vert.y);
quad.setZ(i, vert.z);
}
// Set culling and nominal faces
quad.setLightFace(quadFacing);
quad.setColorIndex(-1);
quad.setDirectionalShading(true);
quad.setEmissiveness(0);
// Set UV
final Vector4f uv = DEFAULT_UV;
setUV(quad, 0, uv.x, uv.y);
setUV(quad, 1, uv.x, uv.w);
setUV(quad, 2, uv.z, uv.w);
setUV(quad, 3, uv.z, uv.y);
bakeSprite(quad, "missingno");
// Bake and add it
sidedQuadStore.computeIfAbsent(quadFacing, d -> new ArrayList<>()).add(quad);
}
final IIcon missing = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite("missingno");
// Add them to the model
return new PileOfQuads(sidedQuadStore, Collections.emptyMap(), missing);
elements.add(new ModelElement("cube", new Vector3f(), new Vector3f(1, 1, 1), NOOP, true, 0, faces));
}
}
@@ -0,0 +1,19 @@
package com.gtnewhorizon.gtnhlib.test.block;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import cpw.mods.fml.common.registry.GameRegistry;
public class BlockRngTest extends Block {
public BlockRngTest() {
super(Material.wood);
}
public static void register() {
final var testBlock = new BlockRngTest();
testBlock.setBlockName("rng_test");
GameRegistry.registerBlock(testBlock, "rng_test");
}
}
@@ -0,0 +1,19 @@
package com.gtnewhorizon.gtnhlib.test.block;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import cpw.mods.fml.common.registry.GameRegistry;
public class BlockWeightedRngTest extends Block {
public BlockWeightedRngTest() {
super(Material.wood);
}
public static void register() {
final var testBlock = new BlockWeightedRngTest();
testBlock.setBlockName("weighted_rng_test");
GameRegistry.registerBlock(testBlock, "weighted_rng_test");
}
}
@@ -1,7 +1,14 @@
package com.gtnewhorizon.gtnhlib.util;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.MathUtil.roughlyEqual;
import net.minecraftforge.common.util.ForgeDirection;
import org.joml.Matrix4fc;
import org.joml.Vector3f;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing;
public class DirectionUtil {
public static ForgeDirection yawToDirection(float yaw) {
@@ -12,4 +19,29 @@ public class DirectionUtil {
default -> ForgeDirection.NORTH;
};
}
public static ModelQuadFacing rotateFacing(ModelQuadFacing facing, Matrix4fc rotation) {
// We can't meaningfully rotate an unknown vector
if (!facing.isDirection()) return ModelQuadFacing.UNASSIGNED;
final var vecFacing = new Vector3f(facing.getStepX(), facing.getStepY(), facing.getStepZ())
.mulDirection(rotation);
// Only one of these three vector coordinates should be a value other than near-zero.
if (roughlyEqual(vecFacing.x, 1)) {
return ModelQuadFacing.POS_X;
} else if (roughlyEqual(vecFacing.x, -1)) {
return ModelQuadFacing.NEG_X;
} else if (roughlyEqual(vecFacing.y, 1)) {
return ModelQuadFacing.POS_Y;
} else if (roughlyEqual(vecFacing.y, -1)) {
return ModelQuadFacing.NEG_Y;
} else if (roughlyEqual(vecFacing.z, 1)) {
return ModelQuadFacing.POS_Z;
} else if (roughlyEqual(vecFacing.z, -1)) {
return ModelQuadFacing.NEG_Z;
}
// We rotated it and didn't get something axis-aligned!
return ModelQuadFacing.UNASSIGNED;
}
}
@@ -0,0 +1,15 @@
{
"variants": {
"": [
{
"model": "gtnhlib:blocks/test_one"
},
{
"model": "gtnhlib:blocks/test_two"
},
{
"model": "gtnhlib:blocks/test_three"
}
]
}
}
@@ -0,0 +1,16 @@
{
"variants": {
"": [
{
"model": "gtnhlib:blocks/test_one",
"weight": 8
},
{
"model": "gtnhlib:blocks/test_two"
},
{
"model": "gtnhlib:blocks/test_three"
}
]
}
}
@@ -3,6 +3,8 @@ tile.model_test.name=Test Block
tile.model_test_tint.name=Test Block (Tint)
tile.model_test_tint_mul.name=Test Block (Tint Mul)
tile.model_test_lectern.name=Lectern Test Block
tile.rng_test.name=RNG Test Block
tile.weighted_rng_test.name=Weighted RNG Test Block
# Configuration
fml.configgui.tooltip.default=[default: %s]
@@ -0,0 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "gtnhlib:test_one"
}
}
@@ -0,0 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "gtnhlib:test_three"
}
}
@@ -0,0 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "gtnhlib:test_two"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B