mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-01-28 01:44:48 +00:00
Refactor the turtle model code a little
Should make it easier to use in a multi-loader setting. This probably still needs a bit more work: Dinnerbone turtles are still very broken.
This commit is contained in:
parent
cc73fcd85d
commit
34c7fcf750
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.client.model.turtle;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dan200.computercraft.client.render.TransformedBakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemTransforms;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.client.model.BakedModelWrapper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TurtleModel extends BakedModelWrapper<BakedModel> {
|
||||
private final TurtleModelParts parts;
|
||||
private final Map<TurtleModelParts.Combination, List<BakedModel>> cachedModels = new HashMap<>();
|
||||
|
||||
public TurtleModel(BakedModel familyModel, BakedModel colourModel) {
|
||||
super(familyModel);
|
||||
parts = new TurtleModelParts(familyModel, colourModel, TransformedBakedModel::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModel applyTransform(ItemTransforms.TransformType cameraTransformType, PoseStack poseStack, boolean applyLeftHandTransform) {
|
||||
originalModel.applyTransform(cameraTransformType, poseStack, applyLeftHandTransform);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BakedModel> getRenderPasses(ItemStack stack, boolean fabulous) {
|
||||
return cachedModels.computeIfAbsent(parts.getCombination(stack), parts::buildModel);
|
||||
}
|
||||
}
|
@ -3,46 +3,51 @@
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.client.render;
|
||||
package dan200.computercraft.client.model.turtle;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Transformation;
|
||||
import dan200.computercraft.api.client.TransformedModel;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
|
||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||
import dan200.computercraft.shared.turtle.items.ItemTurtle;
|
||||
import dan200.computercraft.shared.util.Holiday;
|
||||
import dan200.computercraft.shared.util.HolidayUtil;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.block.model.ItemTransforms;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.client.model.BakedModelWrapper;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class TurtleSmartItemModel extends BakedModelWrapper<BakedModel> {
|
||||
/**
|
||||
* Combines several individual models together to form a turtle.
|
||||
*/
|
||||
public final class TurtleModelParts {
|
||||
private static final Transformation identity, flip;
|
||||
|
||||
static {
|
||||
var stack = new PoseStack();
|
||||
stack.scale(0, -1, 0);
|
||||
stack.translate(0, 0, 1);
|
||||
stack.translate(0.5f, 0.5f, 0.5f);
|
||||
stack.scale(1, -1, 1);
|
||||
stack.translate(-0.5f, -0.5f, -0.5f);
|
||||
|
||||
identity = Transformation.identity();
|
||||
flip = new Transformation(stack.last().pose());
|
||||
}
|
||||
|
||||
private record TurtleModelCombination(
|
||||
public record Combination(
|
||||
boolean colour,
|
||||
ITurtleUpgrade leftUpgrade,
|
||||
ITurtleUpgrade rightUpgrade,
|
||||
ResourceLocation overlay,
|
||||
@Nullable ITurtleUpgrade leftUpgrade,
|
||||
@Nullable ITurtleUpgrade rightUpgrade,
|
||||
@Nullable ResourceLocation overlay,
|
||||
boolean christmas,
|
||||
boolean flip
|
||||
) {
|
||||
@ -50,25 +55,21 @@ public class TurtleSmartItemModel extends BakedModelWrapper<BakedModel> {
|
||||
|
||||
private final BakedModel familyModel;
|
||||
private final BakedModel colourModel;
|
||||
private final Function<TransformedModel, BakedModel> transformer;
|
||||
|
||||
private final Map<TurtleModelCombination, List<BakedModel>> cachedModels = new HashMap<>();
|
||||
/**
|
||||
* A cache of {@link TransformedModel} to the transformed {@link BakedModel}. This helps us pool the transformed
|
||||
* instances, reducing memory usage and hopefully ensuring their caches are hit more often!
|
||||
*/
|
||||
private final Map<TransformedModel, BakedModel> transformCache = new HashMap<>();
|
||||
|
||||
public TurtleSmartItemModel(BakedModel familyModel, BakedModel colourModel) {
|
||||
super(familyModel);
|
||||
public TurtleModelParts(BakedModel familyModel, BakedModel colourModel, ModelTransformer transformer) {
|
||||
this.familyModel = familyModel;
|
||||
this.colourModel = colourModel;
|
||||
this.transformer = x -> transformer.transform(x.getModel(), x.getMatrix());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public BakedModel applyTransform(@Nonnull ItemTransforms.TransformType cameraTransformType, @Nonnull PoseStack poseStack, boolean applyLeftHandTransform) {
|
||||
originalModel.applyTransform(cameraTransformType, poseStack, applyLeftHandTransform);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<BakedModel> getRenderPasses(ItemStack stack, boolean fabulous) {
|
||||
public Combination getCombination(ItemStack stack) {
|
||||
var turtle = (ItemTurtle) stack.getItem();
|
||||
|
||||
var colour = turtle.getColour(stack);
|
||||
@ -79,29 +80,39 @@ public class TurtleSmartItemModel extends BakedModelWrapper<BakedModel> {
|
||||
var label = turtle.getLabel(stack);
|
||||
var flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));
|
||||
|
||||
var combo = new TurtleModelCombination(colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip);
|
||||
return cachedModels.computeIfAbsent(combo, this::buildModel);
|
||||
return new Combination(colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip);
|
||||
}
|
||||
|
||||
private List<BakedModel> buildModel(TurtleModelCombination combo) {
|
||||
public List<BakedModel> buildModel(Combination combo) {
|
||||
var mc = Minecraft.getInstance();
|
||||
var modelManager = mc.getItemRenderer().getItemModelShaper().getModelManager();
|
||||
|
||||
var transformation = combo.flip ? flip : identity;
|
||||
var parts = new ArrayList<BakedModel>(4);
|
||||
parts.add(new TransformedBakedModel(combo.colour() ? colourModel : familyModel, transformation));
|
||||
parts.add(transform(combo.colour() ? colourModel : familyModel, transformation));
|
||||
|
||||
var overlayModelLocation = TileEntityTurtleRenderer.getTurtleOverlayModel(combo.overlay(), combo.christmas());
|
||||
if (overlayModelLocation != null) {
|
||||
parts.add(new TransformedBakedModel(modelManager.getModel(overlayModelLocation), transformation));
|
||||
parts.add(transform(modelManager.getModel(overlayModelLocation), transformation));
|
||||
}
|
||||
if (combo.leftUpgrade() != null) {
|
||||
parts.add(new TransformedBakedModel(TurtleUpgradeModellers.getModel(combo.leftUpgrade(), null, TurtleSide.LEFT)).composeWith(transformation));
|
||||
var model = TurtleUpgradeModellers.getModel(combo.leftUpgrade(), null, TurtleSide.LEFT);
|
||||
parts.add(transform(model.getModel(), transformation.compose(model.getMatrix())));
|
||||
}
|
||||
if (combo.rightUpgrade() != null) {
|
||||
parts.add(new TransformedBakedModel(TurtleUpgradeModellers.getModel(combo.rightUpgrade(), null, TurtleSide.RIGHT)).composeWith(transformation));
|
||||
var model = TurtleUpgradeModellers.getModel(combo.rightUpgrade(), null, TurtleSide.RIGHT);
|
||||
parts.add(transform(model.getModel(), transformation.compose(model.getMatrix())));
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
public BakedModel transform(BakedModel model, Transformation transformation) {
|
||||
if (transformation.equals(Transformation.identity())) return model;
|
||||
return transformCache.computeIfAbsent(new TransformedModel(model, transformation), transformer);
|
||||
}
|
||||
|
||||
public interface ModelTransformer {
|
||||
BakedModel transform(BakedModel model, Transformation transformation);
|
||||
}
|
||||
}
|
@ -29,7 +29,6 @@ import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraftforge.client.model.data.ModelData;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
@ -145,9 +144,9 @@ public class TileEntityTurtleRenderer implements BlockEntityRenderer<TileTurtle>
|
||||
|
||||
private void renderModel(@Nonnull PoseStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, BakedModel model, int[] tints) {
|
||||
random.setSeed(0);
|
||||
renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, null, random, ModelData.EMPTY, null), tints);
|
||||
renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, null, random), tints);
|
||||
for (var facing : DirectionUtil.FACINGS) {
|
||||
renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, facing, random, ModelData.EMPTY, null), tints);
|
||||
renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, facing, random), tints);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.math.Transformation;
|
||||
import dan200.computercraft.api.client.TransformedModel;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
@ -16,39 +15,41 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraftforge.client.model.BakedModelWrapper;
|
||||
import net.minecraftforge.client.model.QuadTransformers;
|
||||
import net.minecraftforge.client.model.data.ModelData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public class TransformedBakedModel extends BakedModelWrapper<BakedModel> {
|
||||
private final Transformation transformation;
|
||||
private final boolean isIdentity;
|
||||
private final boolean invert;
|
||||
private @Nullable TransformedQuads cache;
|
||||
|
||||
public TransformedBakedModel(BakedModel model, Transformation transformation) {
|
||||
super(model);
|
||||
this.transformation = transformation;
|
||||
isIdentity = transformation.isIdentity();
|
||||
invert = transformation.getNormalMatrix().determinant() < 0;
|
||||
}
|
||||
|
||||
public TransformedBakedModel(TransformedModel model) {
|
||||
this(model.getModel(), model.getMatrix());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull RandomSource rand) {
|
||||
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand) {
|
||||
return getQuads(state, side, rand, ModelData.EMPTY, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @NotNull RandomSource rand, @NotNull ModelData extraData, @Nullable RenderType renderType) {
|
||||
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, ModelData extraData, @Nullable RenderType renderType) {
|
||||
var cache = this.cache;
|
||||
var quads = originalModel.getQuads(state, side, rand, extraData, renderType);
|
||||
return isIdentity ? quads : QuadTransformers.applying(transformation).process(quads);
|
||||
if (quads.isEmpty()) return List.of();
|
||||
|
||||
// We do some basic caching here to avoid recomputing every frame. Most turtle models don't have culled faces,
|
||||
// so it's not worth being smarter here.
|
||||
if (cache != null && quads.equals(cache.original())) return cache.transformed();
|
||||
|
||||
var transformed = QuadTransformers.applying(transformation).process(quads);
|
||||
this.cache = new TransformedQuads(quads, transformed);
|
||||
return transformed;
|
||||
}
|
||||
|
||||
public TransformedBakedModel composeWith(Transformation other) {
|
||||
return new TransformedBakedModel(originalModel, other.compose(transformation));
|
||||
private record TransformedQuads(List<BakedQuad> original, List<BakedQuad> transformed) {
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.model.turtle.TurtleModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.*;
|
||||
@ -24,7 +25,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class TurtleModelLoader implements IGeometryLoader<TurtleModelLoader.TurtleModel> {
|
||||
public final class TurtleModelLoader implements IGeometryLoader<TurtleModelLoader.Unbaked> {
|
||||
private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_colour");
|
||||
|
||||
public static final TurtleModelLoader INSTANCE = new TurtleModelLoader();
|
||||
@ -34,15 +35,15 @@ public final class TurtleModelLoader implements IGeometryLoader<TurtleModelLoade
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public TurtleModel read(@Nonnull JsonObject modelContents, @Nonnull JsonDeserializationContext deserializationContext) {
|
||||
public Unbaked read(@Nonnull JsonObject modelContents, @Nonnull JsonDeserializationContext deserializationContext) {
|
||||
var model = new ResourceLocation(GsonHelper.getAsString(modelContents, "model"));
|
||||
return new TurtleModel(model);
|
||||
return new Unbaked(model);
|
||||
}
|
||||
|
||||
public static final class TurtleModel implements IUnbakedGeometry<TurtleModel> {
|
||||
public static final class Unbaked implements IUnbakedGeometry<Unbaked> {
|
||||
private final ResourceLocation family;
|
||||
|
||||
private TurtleModel(ResourceLocation family) {
|
||||
private Unbaked(ResourceLocation family) {
|
||||
this.family = family;
|
||||
}
|
||||
|
||||
@ -56,7 +57,7 @@ public final class TurtleModelLoader implements IGeometryLoader<TurtleModelLoade
|
||||
|
||||
@Override
|
||||
public BakedModel bake(IGeometryBakingContext owner, ModelBakery bakery, Function<Material, TextureAtlasSprite> spriteGetter, ModelState transform, ItemOverrides overrides, ResourceLocation modelLocation) {
|
||||
return new TurtleSmartItemModel(
|
||||
return new TurtleModel(
|
||||
bakery.bake(family, transform, spriteGetter),
|
||||
bakery.bake(COLOUR_TURTLE_MODEL, transform, spriteGetter)
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user