mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-15 14:07:38 +00:00
Finish lectern rendering
- Register our models via Minecraft's layer system. I confess, I don't fully understand what purpose this really solves (it's not data-driven or anything!), but makes it a bit more consistent with the rest of vanilla's code. - Update the lectern code to submit models correctly.
This commit is contained in:
@@ -25,7 +25,7 @@ repositories {
|
||||
name = "Fabric"
|
||||
content {
|
||||
includeGroup("net.fabricmc")
|
||||
includeGroup("net.fabricmc.unpick")
|
||||
includeGroup("net.fabricmc.unpick")
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -40,8 +40,8 @@ emi = "1.1.7+1.21"
|
||||
fabricPermissions = "0.3.3"
|
||||
iris-fabric = "1.9.1+1.21.7-fabric"
|
||||
iris-forge = "1.9.1+1.21.7-neoforge"
|
||||
jei = "23.1.0.4"
|
||||
modmenu = "13.0.2"
|
||||
jei = "26.0.0.1"
|
||||
modmenu = "16.0.0-rc.1"
|
||||
moreRed = "6.0.0.3"
|
||||
rei = "18.0.800"
|
||||
sodium-fabric = "mc1.21.6-0.6.13-fabric"
|
||||
@@ -113,9 +113,9 @@ fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-l
|
||||
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
|
||||
iris-fabric = { module = "maven.modrinth:iris", version.ref = "iris-fabric" }
|
||||
iris-forge = { module = "maven.modrinth:iris", version.ref = "iris-forge" }
|
||||
jei-api = { module = "mezz.jei:jei-1.21.7-common-api", version.ref = "jei" }
|
||||
jei-fabric = { module = "mezz.jei:jei-1.21.7-fabric", version.ref = "jei" }
|
||||
jei-forge = { module = "mezz.jei:jei-1.21.7-neoforge", version.ref = "jei" }
|
||||
jei-api = { module = "mezz.jei:jei-1.21.10-common-api", version.ref = "jei" }
|
||||
jei-fabric = { module = "mezz.jei:jei-1.21.10-fabric", version.ref = "jei" }
|
||||
jei-forge = { module = "mezz.jei:jei-1.21.10-neoforge", version.ref = "jei" }
|
||||
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
||||
mixinExtra = { module = "io.github.llamalad7:mixinextras-common", version.ref = "mixinExtra" }
|
||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
||||
|
@@ -142,19 +142,21 @@ public final class StandaloneModel {
|
||||
/**
|
||||
* Render the model directly.
|
||||
*
|
||||
* @param transform The current pose stack transformations.
|
||||
* @param collector The node collector to render to.
|
||||
* @param light The current light texture coordinate.
|
||||
* @param overlay The current overlay texture coordinate.
|
||||
* @param tints The tints for this model.
|
||||
* @param transform The current pose stack transformations.
|
||||
* @param collector The node collector to render to.
|
||||
* @param light The current light texture coordinate.
|
||||
* @param overlay The current overlay texture coordinate.
|
||||
* @param tints The tints for this model.
|
||||
* @param crumblingOverlay The current breaking progress.
|
||||
*/
|
||||
public void submit(PoseStack transform, SubmitNodeCollector collector, int light, int overlay, int @Nullable [] tints, ModelFeatureRenderer.@Nullable CrumblingOverlay crumblingOverlay) {
|
||||
collector.submitCustomGeometry(transform, renderType, (pose, buffer) -> render(pose, buffer, tints, light, overlay));
|
||||
|
||||
if (crumblingOverlay != null && renderType.affectsCrumbling()) {
|
||||
// FIXME: We need a custom hook here, which renders to crumblingBufferSource. Currently the DESTROY_TYPES
|
||||
// buffer gets flushed before the main model gets rendered.
|
||||
collector.submitCustomGeometry(transform, ModelBakery.DESTROY_TYPES.get(crumblingOverlay.progress()), (pose, buffer) ->
|
||||
// FIXME: This does not work. Should we have a custom hook for this instead?
|
||||
render(pose, new SheetedDecalTextureGenerator(buffer, crumblingOverlay.cameraPose(), 1.0f), tints, light, overlay)
|
||||
render(pose, new SheetedDecalTextureGenerator(buffer, crumblingOverlay.cameraPose(), 1.0f), null, light, overlay)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@ package dan200.computercraft.client;
|
||||
import com.mojang.blaze3d.audio.Channel;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Axis;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||
import dan200.computercraft.client.render.CableHighlightRenderer;
|
||||
import dan200.computercraft.client.render.ExtendedItemFrameRenderState;
|
||||
@@ -22,9 +21,7 @@ import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock;
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||
import dan200.computercraft.shared.util.PauseAwareTimer;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
import net.minecraft.client.Camera;
|
||||
@@ -41,8 +38,6 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Event listeners for client-only code.
|
||||
* <p>
|
||||
@@ -72,6 +67,7 @@ public final class ClientHooks {
|
||||
}
|
||||
|
||||
public static boolean drawHighlight(PoseStack transform, MultiBufferSource bufferSource, Camera camera, BlockHitResult hit) {
|
||||
// TODO: Reconsider this API once https://github.com/FabricMC/fabric/pull/4906/ is merged.
|
||||
return CableHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit)
|
||||
|| MonitorHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit);
|
||||
}
|
||||
@@ -106,38 +102,6 @@ public final class ClientHooks {
|
||||
SpeakerManager.onPlayStreaming(engine, channel, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional information about the currently targeted block to the debug screen.
|
||||
*
|
||||
* @param addText A callback which adds a single line of text.
|
||||
*/
|
||||
public static void addBlockDebugInfo(Consumer<String> addText) {
|
||||
// TODO(1.21.9): Replacement for this
|
||||
var minecraft = Minecraft.getInstance();
|
||||
if (!minecraft.getDebugOverlay().showDebugScreen() || minecraft.level == null) return;
|
||||
if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return;
|
||||
|
||||
var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos());
|
||||
|
||||
if (tile instanceof MonitorBlockEntity monitor) {
|
||||
addText.accept("");
|
||||
addText.accept(
|
||||
String.format("Targeted monitor: (%d, %d), %d x %d", monitor.getXIndex(), monitor.getYIndex(), monitor.getWidth(), monitor.getHeight())
|
||||
);
|
||||
} else if (tile instanceof TurtleBlockEntity turtle) {
|
||||
addText.accept("");
|
||||
addText.accept("Targeted turtle:");
|
||||
addText.accept(String.format("Id: %d", turtle.getComputerID()));
|
||||
addTurtleUpgrade(addText, turtle, TurtleSide.LEFT);
|
||||
addTurtleUpgrade(addText, turtle, TurtleSide.RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addTurtleUpgrade(Consumer<String> out, TurtleBlockEntity turtle, TurtleSide side) {
|
||||
var upgrade = turtle.getAccess().getUpgradeWithData(side);
|
||||
if (upgrade != null) out.accept(String.format("Upgrade[%s]: %s", side, upgrade.holder().key().location()));
|
||||
}
|
||||
|
||||
public static BlockState getBlockBreakingState(BlockState state, BlockPos pos) {
|
||||
// Only apply to cables which have both a cable and modem
|
||||
if (state.getBlock() != ModRegistry.Blocks.CABLE.get()
|
||||
|
@@ -12,6 +12,9 @@ import dan200.computercraft.client.item.colour.PocketComputerLight;
|
||||
import dan200.computercraft.client.item.model.TurtleOverlayModel;
|
||||
import dan200.computercraft.client.item.properties.PocketComputerStateProperty;
|
||||
import dan200.computercraft.client.item.properties.TurtleShowElfOverlay;
|
||||
import dan200.computercraft.client.model.LecternBookModel;
|
||||
import dan200.computercraft.client.model.LecternPocketModel;
|
||||
import dan200.computercraft.client.model.LecternPrintoutModel;
|
||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||
import dan200.computercraft.client.platform.ModelKey;
|
||||
import dan200.computercraft.client.render.CustomLecternRenderer;
|
||||
@@ -23,11 +26,14 @@ import dan200.computercraft.client.turtle.TurtleUpgradeModelManager;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import net.minecraft.client.color.item.ItemTintSource;
|
||||
import net.minecraft.client.gui.components.debug.DebugScreenEntry;
|
||||
import net.minecraft.client.gui.render.pip.PictureInPictureRenderer;
|
||||
import net.minecraft.client.gui.render.state.pip.PictureInPictureRenderState;
|
||||
import net.minecraft.client.gui.screens.MenuScreens;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.gui.screens.inventory.MenuAccess;
|
||||
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||
import net.minecraft.client.model.geom.builders.LayerDefinition;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
|
||||
@@ -51,6 +57,7 @@ import java.util.concurrent.Executor;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Registers client-side objects, such as {@link BlockEntityRendererProvider}s and
|
||||
@@ -193,6 +200,12 @@ public final class ClientRegistry {
|
||||
register.accept(TurtleShowElfOverlay.ID, TurtleShowElfOverlay.CODEC);
|
||||
}
|
||||
|
||||
public static void registerLayerDefinitions(BiConsumer<ModelLayerLocation, Supplier<LayerDefinition>> register) {
|
||||
register.accept(LecternBookModel.LAYER, LecternBookModel::createLayer);
|
||||
register.accept(LecternPrintoutModel.LAYER, LecternPrintoutModel::createLayer);
|
||||
register.accept(LecternPocketModel.LAYER, LecternPocketModel::createLayer);
|
||||
}
|
||||
|
||||
public interface RegisterPictureInPictureRenderer {
|
||||
<T extends PictureInPictureRenderState> void register(Class<T> state, Function<MultiBufferSource.BufferSource, PictureInPictureRenderer<T>> factory);
|
||||
}
|
||||
@@ -200,4 +213,8 @@ public final class ClientRegistry {
|
||||
public static void registerPictureInPictureRenderers(RegisterPictureInPictureRenderer register) {
|
||||
register.register(PrintoutScreen.PrintoutRenderState.class, PrintoutScreen.PrintoutPictureRenderer::new);
|
||||
}
|
||||
|
||||
public static void registerDebugScreenEntries(BiConsumer<ResourceLocation, DebugScreenEntry> register) {
|
||||
register.accept(LookingAtBlockEntityDebugEntry.ID, LookingAtBlockEntityDebugEntry.create());
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,96 @@
|
||||
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.debug.DebugEntryLookingAtBlock;
|
||||
import net.minecraft.client.gui.components.debug.DebugScreenDisplayer;
|
||||
import net.minecraft.client.gui.components.debug.DebugScreenEntry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* A {@link DebugScreenEntry} that provides information about the currently looked at block entity.
|
||||
*
|
||||
* @see DebugEntryLookingAtBlock
|
||||
*/
|
||||
public final class LookingAtBlockEntityDebugEntry implements DebugScreenEntry {
|
||||
public static final ResourceLocation ID = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "looking_at_block_entity");
|
||||
|
||||
private final Map<BlockEntityType<?>, BiConsumer<List<String>, BlockEntity>> blockEntityEmitters = new HashMap<>();
|
||||
|
||||
private LookingAtBlockEntityDebugEntry() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(DebugScreenDisplayer displayer, @Nullable Level level, @Nullable LevelChunk clientChunk, @Nullable LevelChunk serverChunk) {
|
||||
var entity = Minecraft.getInstance().getCameraEntity();
|
||||
var trueLevel = SharedConstants.DEBUG_SHOW_SERVER_DEBUG_VALUES ? level : Minecraft.getInstance().level;
|
||||
if (entity == null || trueLevel == null) return;
|
||||
|
||||
var hitResult = entity.pick(20.0, 0.0F, false);
|
||||
if (hitResult.getType() != HitResult.Type.BLOCK) return;
|
||||
|
||||
var blockEntity = trueLevel.getBlockEntity(((BlockHitResult) hitResult).getBlockPos());
|
||||
if (blockEntity == null) return;
|
||||
var emitter = blockEntityEmitters.get(blockEntity.getType());
|
||||
if (emitter == null) return;
|
||||
|
||||
List<String> lines = new ArrayList<>();
|
||||
emitter.accept(lines, blockEntity);
|
||||
displayer.addToGroup(ID, lines);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends BlockEntity> LookingAtBlockEntityDebugEntry register(BlockEntityType<T> type, BiConsumer<List<String>, T> emit) {
|
||||
blockEntityEmitters.put(type, (BiConsumer<List<String>, BlockEntity>) emit);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static DebugScreenEntry create() {
|
||||
return new LookingAtBlockEntityDebugEntry()
|
||||
.register(ModRegistry.BlockEntities.MONITOR_NORMAL.get(), LookingAtBlockEntityDebugEntry::debugMonitor)
|
||||
.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), LookingAtBlockEntityDebugEntry::debugMonitor)
|
||||
.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), LookingAtBlockEntityDebugEntry::debugTurtle)
|
||||
.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), LookingAtBlockEntityDebugEntry::debugTurtle);
|
||||
}
|
||||
|
||||
private static void debugMonitor(List<String> lines, MonitorBlockEntity monitor) {
|
||||
lines.add(
|
||||
String.format("Targeted monitor: (%d, %d), %d x %d", monitor.getXIndex(), monitor.getYIndex(), monitor.getWidth(), monitor.getHeight())
|
||||
);
|
||||
}
|
||||
|
||||
private static void debugTurtle(List<String> lines, TurtleBlockEntity turtle) {
|
||||
lines.add("Targeted turtle:");
|
||||
lines.add(String.format("Id: %d", turtle.getComputerID()));
|
||||
addTurtleUpgrade(lines, turtle, TurtleSide.LEFT);
|
||||
addTurtleUpgrade(lines, turtle, TurtleSide.RIGHT);
|
||||
}
|
||||
|
||||
private static void addTurtleUpgrade(List<String> out, TurtleBlockEntity turtle, TurtleSide side) {
|
||||
var upgrade = turtle.getAccess().getUpgradeWithData(side);
|
||||
if (upgrade != null) out.add(String.format("Upgrade[%s]: %s", side, upgrade.holder().key().location()));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.model;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.client.render.CustomLecternRenderer;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.client.model.Model;
|
||||
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
import net.minecraft.client.model.geom.PartPose;
|
||||
import net.minecraft.client.model.geom.builders.CubeListBuilder;
|
||||
import net.minecraft.client.model.geom.builders.LayerDefinition;
|
||||
import net.minecraft.client.model.geom.builders.MeshDefinition;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.Unit;
|
||||
|
||||
import static dan200.computercraft.client.model.LecternPrintoutModelDefinitions.TEXTURE_HEIGHT;
|
||||
import static dan200.computercraft.client.model.LecternPrintoutModelDefinitions.TEXTURE_WIDTH;
|
||||
|
||||
/**
|
||||
* A model for {@linkplain PrintoutItem printed books} placed on a lectern.
|
||||
*
|
||||
* @see CustomLecternRenderer
|
||||
*/
|
||||
public final class LecternBookModel extends Model<Unit> {
|
||||
public static final ModelLayerLocation LAYER = new ModelLayerLocation(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "lectern_book"), "main");
|
||||
|
||||
public LecternBookModel(ModelPart root) {
|
||||
super(root, RenderType::entitySolid);
|
||||
}
|
||||
|
||||
public static LayerDefinition createLayer() {
|
||||
var mesh = new MeshDefinition();
|
||||
var parts = mesh.getRoot();
|
||||
|
||||
parts.addOrReplaceChild(
|
||||
"spine",
|
||||
CubeListBuilder.create().texOffs(12, 15).addBox(-0.005f, -5.0f, -0.5f, 0, 10, 1.0f),
|
||||
PartPose.ZERO
|
||||
);
|
||||
|
||||
var angle = (float) Math.toRadians(5);
|
||||
parts.addOrReplaceChild(
|
||||
"left",
|
||||
CubeListBuilder.create()
|
||||
.texOffs(0, 10).addBox(0, -5.0f, -6.0f, 0, 10, 6.0f)
|
||||
.texOffs(0, 0).addBox(0.005f, -4.0f, -5.0f, 1.0f, 8.0f, 5.0f),
|
||||
PartPose.offsetAndRotation(-0.005f, 0, -0.5f, 0, -angle, 0)
|
||||
);
|
||||
|
||||
parts.addOrReplaceChild(
|
||||
"right",
|
||||
CubeListBuilder.create()
|
||||
.texOffs(14, 10).addBox(0, -5.0f, 0, 0, 10, 6.0f)
|
||||
.texOffs(0, 0).addBox(0.005f, -4.0f, 0, 1.0f, 8.0f, 5.0f),
|
||||
PartPose.offsetAndRotation(-0.005f, 0, 0.5f, 0, angle, 0)
|
||||
);
|
||||
|
||||
return LayerDefinition.create(mesh, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}
|
||||
}
|
@@ -4,34 +4,43 @@
|
||||
|
||||
package dan200.computercraft.client.model;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.client.pocket.PocketComputerData;
|
||||
import dan200.computercraft.client.render.CustomLecternRenderer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import net.minecraft.client.model.Model;
|
||||
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
import net.minecraft.client.model.geom.PartPose;
|
||||
import net.minecraft.client.model.geom.builders.CubeListBuilder;
|
||||
import net.minecraft.client.model.geom.builders.LayerDefinition;
|
||||
import net.minecraft.client.model.geom.builders.MeshDefinition;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.Sheets;
|
||||
import net.minecraft.client.renderer.SubmitNodeCollector;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.client.resources.model.MaterialSet;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
|
||||
/**
|
||||
* A model for {@linkplain PocketComputerItem pocket computers} placed on a lectern.
|
||||
*
|
||||
* @see CustomLecternRenderer
|
||||
*/
|
||||
public class LecternPocketModel {
|
||||
public static final ResourceLocation TEXTURE_NORMAL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_normal");
|
||||
public static final ResourceLocation TEXTURE_ADVANCED = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_advanced");
|
||||
public static final ResourceLocation TEXTURE_COLOUR = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_colour");
|
||||
public static final ResourceLocation TEXTURE_FRAME = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_frame");
|
||||
public static final ResourceLocation TEXTURE_LIGHT = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_light");
|
||||
public class LecternPocketModel extends Model<Unit> {
|
||||
public static final ModelLayerLocation LAYER = new ModelLayerLocation(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "lectern_pocket"), "main");
|
||||
|
||||
private static final Material MATERIAL_NORMAL = new Material(TextureAtlas.LOCATION_BLOCKS, TEXTURE_NORMAL);
|
||||
private static final Material MATERIAL_ADVANCED = new Material(TextureAtlas.LOCATION_BLOCKS, TEXTURE_ADVANCED);
|
||||
private static final Material MATERIAL_COLOUR = new Material(TextureAtlas.LOCATION_BLOCKS, TEXTURE_COLOUR);
|
||||
private static final Material MATERIAL_FRAME = new Material(TextureAtlas.LOCATION_BLOCKS, TEXTURE_FRAME);
|
||||
private static final Material MATERIAL_LIGHT = new Material(TextureAtlas.LOCATION_BLOCKS, TEXTURE_LIGHT);
|
||||
public static final Material MATERIAL_NORMAL = Sheets.BLOCK_ENTITIES_MAPPER.apply(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_computer_normal"));
|
||||
public static final Material MATERIAL_ADVANCED = Sheets.BLOCK_ENTITIES_MAPPER.apply(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced"));
|
||||
public static final Material MATERIAL_COLOUR = Sheets.BLOCK_ENTITIES_MAPPER.apply(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_computer_colour"));
|
||||
public static final Material MATERIAL_FRAME = Sheets.BLOCK_ENTITIES_MAPPER.apply(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_computer_frame"));
|
||||
public static final Material MATERIAL_LIGHT = Sheets.BLOCK_ENTITIES_MAPPER.apply(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_computer_light"));
|
||||
|
||||
// The size of the terminal within the model.
|
||||
public static final float TERM_WIDTH = 12.0f / 32.0f;
|
||||
@@ -41,13 +50,11 @@ public class LecternPocketModel {
|
||||
private static final int TEXTURE_WIDTH = 48 / 2;
|
||||
private static final int TEXTURE_HEIGHT = 48 / 2;
|
||||
|
||||
private final ModelPart root;
|
||||
|
||||
public LecternPocketModel() {
|
||||
root = buildPages();
|
||||
public LecternPocketModel(ModelPart root) {
|
||||
super(root, RenderType::entityCutout);
|
||||
}
|
||||
|
||||
private static ModelPart buildPages() {
|
||||
public static LayerDefinition createLayer() {
|
||||
var mesh = new MeshDefinition();
|
||||
var parts = mesh.getRoot();
|
||||
parts.addOrReplaceChild(
|
||||
@@ -55,29 +62,43 @@ public class LecternPocketModel {
|
||||
CubeListBuilder.create().texOffs(0, 0).addBox(0f, -5.0f, -4.0f, 1f, 10.0f, 8.0f),
|
||||
PartPose.ZERO
|
||||
);
|
||||
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
return LayerDefinition.create(mesh, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the pocket computer model.
|
||||
*
|
||||
* @param poseStack The current pose stack.
|
||||
* @param bufferSource The buffer source to draw to.
|
||||
* @param packedLight The current light level.
|
||||
* @param packedOverlay The overlay texture (used for entity hurt animation).
|
||||
* @param family The computer family.
|
||||
* @param frameColour The pocket computer's {@linkplain DyedItemColor colour}.
|
||||
* @param lightColour The pocket computer's {@linkplain PocketComputerData#getLightState() light colour}.
|
||||
* @param poseStack The current pose stack.
|
||||
* @param collector The collector to draw to.
|
||||
* @param materials The current materials
|
||||
* @param packedLight The current light level.
|
||||
* @param family The computer family.
|
||||
* @param frameColour The pocket computer's {@linkplain DyedItemColor colour}.
|
||||
* @param lightColour The pocket computer's {@linkplain PocketComputerData#getLightState() light colour}.
|
||||
*/
|
||||
/* public void render(PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay, ComputerFamily family, int frameColour, int lightColour) {
|
||||
public void submit(
|
||||
PoseStack poseStack, SubmitNodeCollector collector, MaterialSet materials, int packedLight, ComputerFamily family, int frameColour, int lightColour
|
||||
) {
|
||||
if (frameColour != -1) {
|
||||
root.render(poseStack, MATERIAL_FRAME.buffer(bufferSource, RenderType::entityCutout), packedLight, packedOverlay);
|
||||
root.render(poseStack, MATERIAL_COLOUR.buffer(bufferSource, RenderType::entityCutout), packedLight, packedOverlay, frameColour);
|
||||
collector.submitModel(
|
||||
this, Unit.INSTANCE, poseStack, MATERIAL_FRAME.renderType(RenderType::entityCutout),
|
||||
packedLight, OverlayTexture.NO_OVERLAY, -1, materials.get(MATERIAL_FRAME), 0, null
|
||||
);
|
||||
collector.submitModel(
|
||||
this, Unit.INSTANCE, poseStack, MATERIAL_COLOUR.renderType(RenderType::entityCutout),
|
||||
packedLight, OverlayTexture.NO_OVERLAY, frameColour, materials.get(MATERIAL_COLOUR), 0, null
|
||||
);
|
||||
} else {
|
||||
var buffer = (family == ComputerFamily.ADVANCED ? MATERIAL_ADVANCED : MATERIAL_NORMAL).buffer(bufferSource, RenderType::entityCutout);
|
||||
root.render(poseStack, buffer, packedLight, packedOverlay);
|
||||
var material = family == ComputerFamily.ADVANCED ? MATERIAL_ADVANCED : MATERIAL_NORMAL;
|
||||
collector.submitModel(
|
||||
this, Unit.INSTANCE, poseStack, material.renderType(RenderType::entityCutout),
|
||||
packedLight, OverlayTexture.NO_OVERLAY, -1, materials.get(material), 0, null
|
||||
);
|
||||
}
|
||||
|
||||
root.render(poseStack, MATERIAL_LIGHT.buffer(bufferSource, RenderType::entityCutout), LightTexture.FULL_BRIGHT, packedOverlay, lightColour);
|
||||
}*/
|
||||
collector.submitModel(
|
||||
this, Unit.INSTANCE, poseStack, MATERIAL_LIGHT.renderType(RenderType::entityCutout),
|
||||
LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, lightColour, materials.get(MATERIAL_LIGHT), 0, null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -4,54 +4,46 @@
|
||||
|
||||
package dan200.computercraft.client.model;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.client.render.CustomLecternRenderer;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.client.model.Model;
|
||||
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
import net.minecraft.client.model.geom.PartPose;
|
||||
import net.minecraft.client.model.geom.builders.CubeListBuilder;
|
||||
import net.minecraft.client.model.geom.builders.LayerDefinition;
|
||||
import net.minecraft.client.model.geom.builders.MeshDefinition;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.SubmitNodeCollector;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static dan200.computercraft.client.model.LecternPrintoutModelDefinitions.TEXTURE_HEIGHT;
|
||||
import static dan200.computercraft.client.model.LecternPrintoutModelDefinitions.TEXTURE_WIDTH;
|
||||
|
||||
/**
|
||||
* A model for {@linkplain PrintoutItem printouts} placed on a lectern.
|
||||
* <p>
|
||||
* This provides two models, {@linkplain #renderPages(PoseStack, VertexConsumer, int, int, int) one for a variable
|
||||
* number of pages}, and {@linkplain #renderBook(PoseStack, VertexConsumer, int, int) one for books}.
|
||||
* A model for {@linkplain PrintoutItem printouts} placed on a lectern. This renders a variable number of pages (1-3),
|
||||
* stored in {@link State#pages}.
|
||||
*
|
||||
* @see CustomLecternRenderer
|
||||
*/
|
||||
public class LecternPrintoutModel {
|
||||
public static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/printout");
|
||||
public static final Material MATERIAL = new Material(TextureAtlas.LOCATION_BLOCKS, TEXTURE);
|
||||
|
||||
private static final int TEXTURE_WIDTH = 32;
|
||||
private static final int TEXTURE_HEIGHT = 32;
|
||||
public final class LecternPrintoutModel extends Model<LecternPrintoutModel.State> {
|
||||
public static final ModelLayerLocation LAYER = new ModelLayerLocation(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "lectern_printout"), "main");
|
||||
|
||||
private static final String PAGE_1 = "page_1";
|
||||
private static final String PAGE_2 = "page_2";
|
||||
private static final String PAGE_3 = "page_3";
|
||||
private static final List<String> PAGES = List.of(PAGE_1, PAGE_2, PAGE_3);
|
||||
|
||||
private final ModelPart pagesRoot;
|
||||
private final ModelPart bookRoot;
|
||||
private final ModelPart[] pages;
|
||||
|
||||
public LecternPrintoutModel() {
|
||||
pagesRoot = buildPages();
|
||||
bookRoot = buildBook();
|
||||
pages = PAGES.stream().map(pagesRoot::getChild).toArray(ModelPart[]::new);
|
||||
public LecternPrintoutModel(ModelPart root) {
|
||||
super(root, RenderType::entitySolid);
|
||||
pages = PAGES.stream().map(root::getChild).toArray(ModelPart[]::new);
|
||||
}
|
||||
|
||||
private static ModelPart buildPages() {
|
||||
public static LayerDefinition createLayer() {
|
||||
var mesh = new MeshDefinition();
|
||||
var parts = mesh.getRoot();
|
||||
parts.addOrReplaceChild(
|
||||
@@ -71,49 +63,20 @@ public class LecternPrintoutModel {
|
||||
PartPose.offsetAndRotation(-0.25f, 0, -1.5f, (float) -Math.PI * (2f / 16), 0, 0)
|
||||
);
|
||||
|
||||
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
return LayerDefinition.create(mesh, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}
|
||||
|
||||
private static ModelPart buildBook() {
|
||||
var mesh = new MeshDefinition();
|
||||
var parts = mesh.getRoot();
|
||||
|
||||
parts.addOrReplaceChild(
|
||||
"spine",
|
||||
CubeListBuilder.create().texOffs(12, 15).addBox(-0.005f, -5.0f, -0.5f, 0, 10, 1.0f),
|
||||
PartPose.ZERO
|
||||
);
|
||||
|
||||
var angle = (float) Math.toRadians(5);
|
||||
parts.addOrReplaceChild(
|
||||
"left",
|
||||
CubeListBuilder.create()
|
||||
.texOffs(0, 10).addBox(0, -5.0f, -6.0f, 0, 10, 6.0f)
|
||||
.texOffs(0, 0).addBox(0.005f, -4.0f, -5.0f, 1.0f, 8.0f, 5.0f),
|
||||
PartPose.offsetAndRotation(-0.005f, 0, -0.5f, 0, -angle, 0)
|
||||
);
|
||||
|
||||
parts.addOrReplaceChild(
|
||||
"right",
|
||||
CubeListBuilder.create()
|
||||
.texOffs(14, 10).addBox(0, -5.0f, 0, 0, 10, 6.0f)
|
||||
.texOffs(0, 0).addBox(0.005f, -4.0f, 0, 1.0f, 8.0f, 5.0f),
|
||||
PartPose.offsetAndRotation(-0.005f, 0, 0.5f, 0, angle, 0)
|
||||
);
|
||||
|
||||
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}
|
||||
|
||||
public void submitBook(PoseStack poseStack, SubmitNodeCollector collector, int packedLight, int packedOverlay) {
|
||||
collector.submitModelPart(bookRoot, poseStack, RenderType.entitySolid(LecternPrintoutModel.MATERIAL.texture()), packedLight, packedOverlay, null);
|
||||
}
|
||||
|
||||
public void renderPages(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, int pageCount) {
|
||||
@Override
|
||||
public void setupAnim(State renderState) {
|
||||
var pageCount = renderState.pages;
|
||||
if (pageCount > pages.length) pageCount = pages.length;
|
||||
|
||||
var i = 0;
|
||||
for (; i < pageCount; i++) pages[i].visible = true;
|
||||
for (; i < pages.length; i++) pages[i].visible = false;
|
||||
}
|
||||
|
||||
pagesRoot.render(poseStack, buffer, packedLight, packedOverlay);
|
||||
public static class State {
|
||||
public int pages;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.model;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import net.minecraft.client.renderer.Sheets;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Definitions for the lectern printout model.
|
||||
*
|
||||
* @see LecternBookModel
|
||||
* @see LecternPrintoutModel
|
||||
*/
|
||||
public final class LecternPrintoutModelDefinitions {
|
||||
public static final Material MATERIAL = Sheets.BLOCK_ENTITIES_MAPPER.apply(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "printout"));
|
||||
|
||||
static final int TEXTURE_WIDTH = 32;
|
||||
static final int TEXTURE_HEIGHT = 32;
|
||||
|
||||
private LecternPrintoutModelDefinitions() {
|
||||
}
|
||||
}
|
@@ -6,17 +6,21 @@ package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Axis;
|
||||
import dan200.computercraft.client.model.LecternBookModel;
|
||||
import dan200.computercraft.client.model.LecternPocketModel;
|
||||
import dan200.computercraft.client.model.LecternPrintoutModel;
|
||||
import dan200.computercraft.client.model.LecternPrintoutModelDefinitions;
|
||||
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
|
||||
import dan200.computercraft.shared.media.items.PrintoutData;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.SubmitNodeCollector;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||
@@ -25,6 +29,9 @@ import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState;
|
||||
import net.minecraft.client.renderer.feature.ModelFeatureRenderer;
|
||||
import net.minecraft.client.renderer.state.CameraRenderState;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.client.resources.model.MaterialSet;
|
||||
import net.minecraft.util.ARGB;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
import net.minecraft.world.level.block.LecternBlock;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@@ -42,12 +49,16 @@ import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FON
|
||||
public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity, CustomLecternRenderer.State> {
|
||||
private static final int POCKET_TERMINAL_RENDER_DISTANCE = 32;
|
||||
|
||||
private final MaterialSet materials;
|
||||
private final LecternPrintoutModel printoutModel;
|
||||
private final LecternBookModel bookModel;
|
||||
private final LecternPocketModel pocketModel;
|
||||
|
||||
public CustomLecternRenderer(BlockEntityRendererProvider.Context context) {
|
||||
printoutModel = new LecternPrintoutModel();
|
||||
pocketModel = new LecternPocketModel();
|
||||
materials = context.materials();
|
||||
bookModel = new LecternBookModel(context.bakeLayer(LecternBookModel.LAYER));
|
||||
printoutModel = new LecternPrintoutModel(context.bakeLayer(LecternPrintoutModel.LAYER));
|
||||
pocketModel = new LecternPocketModel(context.bakeLayer(LecternPocketModel.LAYER));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,7 +77,8 @@ public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternB
|
||||
var computer = ClientPocketComputers.get(item);
|
||||
state.setPocket(
|
||||
pocket.getFamily(), DyedItemColor.getOrDefault(item, -1),
|
||||
computer == null ? -1 : computer.getLightState(),
|
||||
ARGB.opaque(computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState()),
|
||||
// Only render a terminal if we're close to it.
|
||||
computer == null || !Vec3.atCenterOf(lectern.getBlockPos()).closerThan(camera, POCKET_TERMINAL_RENDER_DISTANCE)
|
||||
? null : computer.getTerminal()
|
||||
);
|
||||
@@ -85,35 +97,38 @@ public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternB
|
||||
|
||||
if (state.type == Type.PRINTOUT) {
|
||||
if (state.isBook) {
|
||||
printoutModel.submitBook(poseStack, collector, state.lightCoords, OverlayTexture.NO_OVERLAY);
|
||||
collector.submitModel(
|
||||
bookModel, Unit.INSTANCE, poseStack, LecternPrintoutModelDefinitions.MATERIAL.renderType(RenderType::entitySolid),
|
||||
state.lightCoords, OverlayTexture.NO_OVERLAY, -1,
|
||||
materials.get(LecternPrintoutModelDefinitions.MATERIAL), 0, null
|
||||
);
|
||||
} else {
|
||||
// TODO: printoutModel.renderPages(poseStack, vertexConsumer, packedLight, packedOverlay, PrintoutData.getOrEmpty(item).pages());
|
||||
collector.submitModel(
|
||||
printoutModel, state.printoutState, poseStack, LecternPrintoutModelDefinitions.MATERIAL.renderType(RenderType::entitySolid),
|
||||
state.lightCoords, OverlayTexture.NO_OVERLAY, -1,
|
||||
materials.get(LecternPrintoutModelDefinitions.MATERIAL), 0, null
|
||||
);
|
||||
}
|
||||
} else if (state.type == Type.POCKET_COMPUTER) {
|
||||
// TODO: Pocket model rendering
|
||||
/*pocketModel.render(
|
||||
poseStack, buffer, packedLight, packedOverlay, pocket.getFamily(), DyedItemColor.getOrDefault(item, -1),
|
||||
ARGB.opaque(computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState())
|
||||
);
|
||||
pocketModel.submit(poseStack, collector, materials, state.lightCoords, state.pocketFamily, state.pocketColour, state.pocketLight);
|
||||
|
||||
// Jiggle the terminal about a bit, so (0, 0) is in the top left of the model's terminal hole.
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(90f));
|
||||
poseStack.translate(-0.5 * LecternPocketModel.TERM_WIDTH, 0.5 * LecternPocketModel.TERM_HEIGHT + 1f / 32.0f, 1 / 16.0f);
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(180));
|
||||
|
||||
// Either render the terminal or a black screen, depending on how close we are.
|
||||
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(poseStack, buffer.getBuffer(FixedWidthFontRenderer.TERMINAL_TEXT));
|
||||
// Either render the terminal or a black screen.
|
||||
if (state.pocketTerminal != null) {
|
||||
renderPocketTerminal(poseStack, quadEmitter, state.pocketTerminal);
|
||||
renderPocketTerminal(poseStack, collector, state.pocketTerminal);
|
||||
} else {
|
||||
FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, LecternPocketModel.TERM_WIDTH, LecternPocketModel.TERM_HEIGHT);
|
||||
}*/
|
||||
FixedWidthFontRenderer.drawEmptyTerminal(poseStack, collector, 0, 0, LecternPocketModel.TERM_WIDTH, LecternPocketModel.TERM_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
poseStack.popPose();
|
||||
}
|
||||
|
||||
private static void renderPocketTerminal(PoseStack poseStack, FixedWidthFontRenderer.QuadEmitter quadEmitter, Terminal terminal) {
|
||||
private static void renderPocketTerminal(PoseStack poseStack, SubmitNodeCollector collector, Terminal terminal) {
|
||||
var width = terminal.getWidth() * FONT_WIDTH;
|
||||
var height = terminal.getHeight() * FONT_HEIGHT;
|
||||
|
||||
@@ -127,7 +142,10 @@ public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternB
|
||||
var marginX = ((LecternPocketModel.TERM_WIDTH / scale) - width) / 2;
|
||||
var marginY = ((LecternPocketModel.TERM_HEIGHT / scale) - height) / 2;
|
||||
|
||||
FixedWidthFontRenderer.drawTerminal(quadEmitter, marginX, marginY, terminal, marginY, marginY, marginX, marginX);
|
||||
collector.submitCustomGeometry(poseStack, FixedWidthFontRenderer.TERMINAL_TEXT, (pose, buffer) -> {
|
||||
var quadEmitter = new FixedWidthFontRenderer.QuadEmitter(pose.pose(), buffer);
|
||||
FixedWidthFontRenderer.drawTerminal(quadEmitter, marginX, marginY, terminal, marginY, marginY, marginX, marginX);
|
||||
});
|
||||
}
|
||||
|
||||
private enum Type {
|
||||
@@ -139,7 +157,7 @@ public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternB
|
||||
public static final class State extends BlockEntityRenderState {
|
||||
private Type type = Type.PRINTOUT;
|
||||
private boolean isBook;
|
||||
private int pages;
|
||||
private final LecternPrintoutModel.State printoutState = new LecternPrintoutModel.State();
|
||||
|
||||
private ComputerFamily pocketFamily = ComputerFamily.NORMAL;
|
||||
private int pocketColour;
|
||||
@@ -157,7 +175,7 @@ public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternB
|
||||
private void setPrintout(boolean isBook, int pages) {
|
||||
this.type = Type.PRINTOUT;
|
||||
this.isBook = isBook;
|
||||
this.pages = pages;
|
||||
this.printoutState.pages = pages;
|
||||
|
||||
this.pocketTerminal = null;
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderItem(PoseStack transform, SubmitNodeCollector submit, ItemStack stack, int light) {
|
||||
protected void renderItem(PoseStack transform, SubmitNodeCollector collector, ItemStack stack, int light) {
|
||||
var computer = ClientPocketComputers.get(stack);
|
||||
var terminal = computer == null ? null : computer.getTerminal();
|
||||
|
||||
@@ -76,21 +76,20 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
var family = item.getFamily();
|
||||
var frameColour = DyedItemColor.getOrDefault(stack, -1);
|
||||
|
||||
var matrix = transform.last().pose();
|
||||
renderFrame(transform, submit, family, frameColour, light, width, height);
|
||||
renderFrame(transform, collector, family, frameColour, light, width, height);
|
||||
|
||||
// Render the light
|
||||
var lightColour = computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
|
||||
renderLight(transform, submit, lightColour, width, height);
|
||||
renderLight(transform, collector, lightColour, width, height);
|
||||
|
||||
submit.submitCustomGeometry(transform, FixedWidthFontRenderer.TERMINAL_TEXT, (pose, buffer) -> {
|
||||
var quadEmitter = new FixedWidthFontRenderer.QuadEmitter(pose.pose(), buffer);
|
||||
if (terminal == null) {
|
||||
FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, width, height);
|
||||
} else {
|
||||
if (terminal == null) {
|
||||
FixedWidthFontRenderer.drawEmptyTerminal(transform, collector, 0, 0, width, height);
|
||||
} else {
|
||||
collector.submitCustomGeometry(transform, FixedWidthFontRenderer.TERMINAL_TEXT, (pose, buffer) -> {
|
||||
var quadEmitter = new FixedWidthFontRenderer.QuadEmitter(pose.pose(), buffer);
|
||||
FixedWidthFontRenderer.drawTerminal(quadEmitter, MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
transform.popPose();
|
||||
}
|
||||
|
@@ -10,7 +10,6 @@ import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.SubmitNodeCollector;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
|
||||
/**
|
||||
@@ -21,8 +20,6 @@ import net.minecraft.resources.ResourceLocation;
|
||||
* sheet.
|
||||
*/
|
||||
public class SpriteRenderer {
|
||||
public static final ResourceLocation TEXTURE = ResourceLocation.withDefaultNamespace("textures/atlas/gui.png");
|
||||
|
||||
private final PoseStack transform;
|
||||
private final SubmitNodeCollector submit;
|
||||
private final int light;
|
||||
|
@@ -19,10 +19,8 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||
import dan200.computercraft.shared.util.Holiday;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.renderer.SubmitNodeCollector;
|
||||
import net.minecraft.client.renderer.block.model.ItemTransform;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||
import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState;
|
||||
@@ -44,14 +42,10 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
public static final ResourceLocation ADVANCED_TURTLE_MODEL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_advanced");
|
||||
public static final ResourceLocation COLOUR_TURTLE_MODEL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_colour");
|
||||
|
||||
private final BlockEntityRenderDispatcher renderer;
|
||||
private final ItemModelResolver itemModelResolver;
|
||||
private final Font font;
|
||||
|
||||
public TurtleBlockEntityRenderer(BlockEntityRendererProvider.Context context) {
|
||||
renderer = context.blockEntityRenderDispatcher();
|
||||
itemModelResolver = context.itemModelResolver();
|
||||
font = context.font();
|
||||
}
|
||||
|
||||
public static final class State extends BlockEntityRenderState {
|
||||
@@ -64,10 +58,10 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
private @Nullable StandaloneModel elfOverlay;
|
||||
|
||||
private float leftAngle;
|
||||
private ItemStackRenderState leftUpgrade = new ItemStackRenderState();
|
||||
private final ItemStackRenderState leftUpgrade = new ItemStackRenderState();
|
||||
|
||||
private float rightAngle;
|
||||
private ItemStackRenderState rightUpgrade = new ItemStackRenderState();
|
||||
private final ItemStackRenderState rightUpgrade = new ItemStackRenderState();
|
||||
|
||||
private State() {
|
||||
}
|
||||
|
@@ -104,12 +104,7 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
|
||||
|
||||
transform.popPose();
|
||||
} else {
|
||||
collector.submitCustomGeometry(transform, FixedWidthFontRenderer.TERMINAL_TEXT, (pose, consumer) -> {
|
||||
FixedWidthFontRenderer.drawEmptyTerminal(
|
||||
new FixedWidthFontRenderer.QuadEmitter(pose.pose(), consumer),
|
||||
-MARGIN, MARGIN, (float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2)
|
||||
);
|
||||
});
|
||||
FixedWidthFontRenderer.drawEmptyTerminal(transform, collector, -MARGIN, MARGIN, (float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2));
|
||||
}
|
||||
|
||||
transform.popPose();
|
||||
|
@@ -13,6 +13,7 @@ import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.SubmitNodeCollector;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.ARGB;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -213,8 +214,11 @@ public final class FixedWidthFontRenderer {
|
||||
emitter.poseMatrix().set(transformBackup);
|
||||
}
|
||||
|
||||
public static void drawEmptyTerminal(QuadEmitter emitter, float x, float y, float width, float height) {
|
||||
drawQuad(emitter, x, y, 0, width, height, BLACK, LightTexture.FULL_BRIGHT);
|
||||
public static void drawEmptyTerminal(PoseStack transform, SubmitNodeCollector collector, float x, float y, float width, float height) {
|
||||
collector.submitCustomGeometry(transform, FixedWidthFontRenderer.TERMINAL_TEXT, (pose, buffer) -> {
|
||||
var quadEmitter = new FixedWidthFontRenderer.QuadEmitter(pose.pose(), buffer);
|
||||
drawQuad(quadEmitter, x, y, 0, width, height, BLACK, LightTexture.FULL_BRIGHT);
|
||||
});
|
||||
}
|
||||
|
||||
public record QuadEmitter(Matrix4f poseMatrix, VertexConsumer consumer) {
|
||||
|
@@ -10,7 +10,7 @@ import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.client.gui.GuiSprites;
|
||||
import dan200.computercraft.client.model.LecternPocketModel;
|
||||
import dan200.computercraft.client.model.LecternPrintoutModel;
|
||||
import dan200.computercraft.client.model.LecternPrintoutModelDefinitions;
|
||||
import dan200.computercraft.client.turtle.TurtleOverlay;
|
||||
import dan200.computercraft.data.client.BlockModelProvider;
|
||||
import dan200.computercraft.data.client.ItemModelProvider;
|
||||
@@ -71,9 +71,9 @@ public final class DataProviders {
|
||||
|
||||
generator.addFromCodec("Block atlases", PackOutput.Target.RESOURCE_PACK, "atlases", SpriteSources.FILE_CODEC, out -> {
|
||||
out.accept(AtlasIds.BLOCKS, makeSprites(Stream.of(
|
||||
LecternPrintoutModel.TEXTURE,
|
||||
LecternPocketModel.TEXTURE_NORMAL, LecternPocketModel.TEXTURE_ADVANCED,
|
||||
LecternPocketModel.TEXTURE_COLOUR, LecternPocketModel.TEXTURE_FRAME, LecternPocketModel.TEXTURE_LIGHT
|
||||
LecternPrintoutModelDefinitions.MATERIAL.texture(),
|
||||
LecternPocketModel.MATERIAL_NORMAL.texture(), LecternPocketModel.MATERIAL_ADVANCED.texture(),
|
||||
LecternPocketModel.MATERIAL_COLOUR.texture(), LecternPocketModel.MATERIAL_FRAME.texture(), LecternPocketModel.MATERIAL_LIGHT.texture()
|
||||
)));
|
||||
|
||||
out.accept(AtlasIds.GUI, makeSprites(
|
||||
|
@@ -24,11 +24,13 @@ import net.fabricmc.fabric.api.client.model.loading.v1.PreparableModelLoadingPlu
|
||||
import net.fabricmc.fabric.api.client.model.loading.v1.UnbakedExtraModel;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.BlockRenderLayerMap;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.SpecialGuiElementRegistry;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.color.item.ItemTintSources;
|
||||
import net.minecraft.client.gui.components.debug.DebugScreenEntries;
|
||||
import net.minecraft.client.gui.render.pip.PictureInPictureRenderer;
|
||||
import net.minecraft.client.gui.render.state.pip.PictureInPictureRenderState;
|
||||
import net.minecraft.client.gui.screens.MenuScreens;
|
||||
@@ -62,6 +64,7 @@ public class ComputerCraftClient {
|
||||
ClientRegistry.registerItemColours(ItemTintSources.ID_MAPPER::put);
|
||||
ClientRegistry.registerSelectItemProperties(SelectItemModelProperties.ID_MAPPER::put);
|
||||
ClientRegistry.registerConditionalItemProperties(ConditionalItemModelProperties.ID_MAPPER::put);
|
||||
ClientRegistry.registerLayerDefinitions((id, factory) -> EntityModelLayerRegistry.registerModelLayer(id, factory::get));
|
||||
|
||||
PreparableModelLoadingPlugin.register(
|
||||
(state, executor) -> ClientRegistry.gatherExtraModels(state.resourceManager(), executor),
|
||||
@@ -100,6 +103,8 @@ public class ComputerCraftClient {
|
||||
});
|
||||
*/
|
||||
|
||||
ClientRegistry.registerDebugScreenEntries(DebugScreenEntries::register);
|
||||
|
||||
// Register our open folder command
|
||||
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) ->
|
||||
dispatcher.register(LiteralArgumentBuilder.<FabricClientCommandSource>literal(ComputerCraft.CLIENT_OPEN_FOLDER)
|
||||
|
@@ -53,6 +53,7 @@ neoForge {
|
||||
|
||||
register("client") {
|
||||
client()
|
||||
sourceSet = sourceSets.client
|
||||
}
|
||||
|
||||
register("server") {
|
||||
@@ -77,6 +78,7 @@ neoForge {
|
||||
register("data") {
|
||||
configureForData("computercraft", sourceSets.main.get())
|
||||
loadedMods = listOf(computercraftDatagen.get())
|
||||
sourceSet = sourceSets.datagen
|
||||
}
|
||||
|
||||
fun RunModel.configureForGameTest() {
|
||||
@@ -87,6 +89,7 @@ neoForge {
|
||||
|
||||
programArgument("--mixin.config=computercraft-gametest.mixins.json")
|
||||
loadedMods.add(testMod)
|
||||
sourceSet = sourceSets.testMod
|
||||
|
||||
jvmArgument("-ea")
|
||||
}
|
||||
@@ -119,6 +122,7 @@ neoForge {
|
||||
register("exampleData") {
|
||||
configureForData("examplemod", sourceSets.examples.get())
|
||||
loadedMods.add(exampleMod.get())
|
||||
sourceSet = sourceSets.examples
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -51,11 +51,6 @@ public final class ForgeClientHooks {
|
||||
}*/
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRenderText(CustomizeGuiOverlayEvent.DebugText event) {
|
||||
ClientHooks.addBlockDebugInfo(event.getRight()::add);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRenderInHand(RenderHandEvent event) {
|
||||
if (ClientHooks.onRenderHeldItem(
|
||||
|
@@ -107,11 +107,18 @@ public final class ForgeClientRegistry {
|
||||
});
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefinitions event) {
|
||||
ClientRegistry.registerLayerDefinitions(event::registerLayerDefinition);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void registerPictureInPictureRenderers(RegisterPictureInPictureRenderersEvent event) {
|
||||
ClientRegistry.registerPictureInPictureRenderers(event::register);
|
||||
}
|
||||
|
||||
// TODO(1.21.10): ClientRegistry.registerDebugScreenEntries once https://github.com/neoforged/NeoForge/pull/2699 is merged
|
||||
|
||||
@SubscribeEvent
|
||||
public static void setupClient(FMLClientSetupEvent event) {
|
||||
ClientRegistry.register();
|
||||
|
@@ -12,6 +12,7 @@ import dan200.computercraft.api.network.wired.WiredElementCapability;
|
||||
import dan200.computercraft.api.peripheral.PeripheralCapability;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.impl.Peripherals;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.shared.CommonHooks;
|
||||
@@ -112,8 +113,8 @@ public final class ComputerCraft {
|
||||
ComputerCraftAPI.registerGenericSource(new FluidMethods());
|
||||
ComputerCraftAPI.registerGenericSource(new EnergyMethods());
|
||||
|
||||
ForgeComputerCraftAPI.registerGenericCapability(Capabilities.Item.BLOCK);
|
||||
ForgeComputerCraftAPI.registerGenericCapability(Capabilities.Fluid.BLOCK);
|
||||
Peripherals.addGenericLookup(InventoryMethods::extractContainer);
|
||||
Peripherals.addGenericLookup(FluidMethods::extractContainer);
|
||||
ForgeComputerCraftAPI.registerGenericCapability(Capabilities.Energy.BLOCK);
|
||||
|
||||
ForgeDetailRegistries.FLUID_STACK.addProvider(FluidData::fill);
|
||||
|
@@ -10,9 +10,12 @@ import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.util.CapabilityUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.neoforged.neoforge.capabilities.Capabilities;
|
||||
import net.neoforged.neoforge.transfer.ResourceHandler;
|
||||
@@ -94,6 +97,11 @@ public final class FluidMethods extends AbstractFluidMethods<FluidMethods.Storag
|
||||
return moveFluid(from, to.storage(), fluid, actualLimit);
|
||||
}
|
||||
|
||||
public static @Nullable StorageWrapper extractContainer(ServerLevel level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, @Nullable Direction direction) {
|
||||
var storage = CapabilityUtil.getCapability(level, Capabilities.Fluid.BLOCK, pos, state, blockEntity, direction);
|
||||
return storage == null ? null : new StorageWrapper(storage);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ResourceHandler<FluidResource> extractHandler(IPeripheral peripheral) {
|
||||
var object = peripheral.getTarget();
|
||||
|
@@ -11,10 +11,13 @@ import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.platform.ForgeContainerTransfer;
|
||||
import dan200.computercraft.shared.util.CapabilityUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.neoforged.neoforge.capabilities.Capabilities;
|
||||
import net.neoforged.neoforge.transfer.ResourceHandler;
|
||||
import net.neoforged.neoforge.transfer.item.ItemResource;
|
||||
@@ -120,6 +123,11 @@ public final class InventoryMethods extends AbstractInventoryMethods<InventoryMe
|
||||
return moveItem(from, fromSlot - 1, to.storage(), toSlot.orElse(0) - 1, actualLimit);
|
||||
}
|
||||
|
||||
public static @Nullable StorageWrapper extractContainer(ServerLevel level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, @Nullable Direction direction) {
|
||||
var storage = CapabilityUtil.getCapability(level, Capabilities.Item.BLOCK, pos, state, blockEntity, direction);
|
||||
return storage == null ? null : new StorageWrapper(storage);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ResourceHandler<ItemResource> extractHandler(IPeripheral peripheral) {
|
||||
var object = peripheral.getTarget();
|
||||
|
Reference in New Issue
Block a user