blueprint small fix

This commit is contained in:
2026-03-25 15:34:18 +01:00
parent a678f3dd02
commit d44fb3ca9a
13 changed files with 123 additions and 104 deletions

View File

@@ -6,7 +6,7 @@ minecraft_version=1.20.1
yarn_mappings=1.20.1+build.10 yarn_mappings=1.20.1+build.10
loader_version=0.18.3 loader_version=0.18.3
# Mod Properties # Mod Properties
mod_version=26.3.25 mod_version=26.3.25.1
maven_group=dev.tggamesyt maven_group=dev.tggamesyt
archives_base_name=szar archives_base_name=szar
# Dependencies # Dependencies

View File

@@ -11,6 +11,7 @@ import net.minecraft.client.render.block.entity.BlockEntityRendererFactory;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
public class BlueprintBlockEntityRenderer implements BlockEntityRenderer<BlueprintBlockEntity> { public class BlueprintBlockEntityRenderer implements BlockEntityRenderer<BlueprintBlockEntity> {
@@ -31,43 +32,19 @@ public class BlueprintBlockEntityRenderer implements BlockEntityRenderer<Bluepri
BlockState storedState = block.getDefaultState(); BlockState storedState = block.getDefaultState();
if (storedState.getRenderType() == BlockRenderType.INVISIBLE) return; if (storedState.getRenderType() == BlockRenderType.INVISIBLE) return;
// Get the shape/state of the blueprint block itself
BlockState blueprintState = entity.getCachedState(); BlockState blueprintState = entity.getCachedState();
// We want to render the stored block's TEXTURE but on the blueprint block's SHAPE.
// The way to do this: find the model for the blueprint block shape,
// but swap in the stored block's sprite via a custom render layer.
// Simplest approach: render the blueprint block's model with the stored block's
// textures by remapping the sprite.
BlockRenderManager renderer = MinecraftClient.getInstance().getBlockRenderManager(); BlockRenderManager renderer = MinecraftClient.getInstance().getBlockRenderManager();
matrices.push();
// Render the blueprint shape using the stored block's texture
// by temporarily using the stored block's model sprites on our shape
renderWithStoredTexture(entity, blueprintState, storedState, matrices, vertexConsumers, light, overlay, renderer);
matrices.pop();
}
private void renderWithStoredTexture(BlueprintBlockEntity entity, BlockState blueprintState,
BlockState storedState, MatrixStack matrices,
VertexConsumerProvider vertexConsumers, int light, int overlay,
BlockRenderManager renderer) {
// Get the first (main) sprite from the stored block's model
var storedModel = renderer.getModel(storedState); var storedModel = renderer.getModel(storedState);
var blueprintModel = renderer.getModel(blueprintState); var blueprintModel = renderer.getModel(blueprintState);
var particleSprite = storedModel.getParticleSprite();
var sprites = storedModel.getParticleSprite(); // main texture of stored block
// Render blueprint model quads, replacing its texture with stored block's sprite
var random = Random.create(); var random = Random.create();
random.setSeed(42L); var layer = net.minecraft.client.render.RenderLayers.getBlockLayer(storedState);
var consumer = vertexConsumers.getBuffer(layer);
var bufferSource = vertexConsumers; matrices.push();
var layer = net.minecraft.client.render.RenderLayers.getBlockLayer(blueprintState); // No scaling here anymore
var consumer = bufferSource.getBuffer(layer);
for (var direction : new net.minecraft.util.math.Direction[]{ for (var direction : new net.minecraft.util.math.Direction[]{
null, null,
@@ -81,22 +58,68 @@ public class BlueprintBlockEntityRenderer implements BlockEntityRenderer<Bluepri
random.setSeed(42L); random.setSeed(42L);
var quads = blueprintModel.getQuads(blueprintState, direction, random); var quads = blueprintModel.getQuads(blueprintState, direction, random);
for (var quad : quads) { for (var quad : quads) {
// Emit the quad but with the stored block's sprite UV remapped int[] vertexData = quad.getVertexData().clone();
emitQuadWithSprite(consumer, matrices, quad, sprites, light, overlay); remapUVs(vertexData, quad.getSprite(), particleSprite);
offsetVertsAlongNormal(vertexData, quad.getFace(), 0.001f);
consumer.quad(
matrices.peek(),
new net.minecraft.client.render.model.BakedQuad(
vertexData,
quad.getColorIndex(),
quad.getFace(),
particleSprite,
quad.hasShade()
),
1f, 1f, 1f, light, overlay
);
} }
} }
matrices.pop();
}
private void remapUVs(int[] vertexData,
net.minecraft.client.texture.Sprite fromSprite,
net.minecraft.client.texture.Sprite toSprite) {
// Vertex format: X, Y, Z, COLOR, U, V, UV2, NORMAL — each vertex is 8 ints
int vertexSize = 8;
for (int i = 0; i < 4; i++) {
int uvOffset = i * vertexSize + 4;
// Unpack UV floats from int bits
float u = Float.intBitsToFloat(vertexData[uvOffset]);
float v = Float.intBitsToFloat(vertexData[uvOffset + 1]);
// Normalize UV from the source sprite's atlas space to 0-1
float normalizedU = (u - fromSprite.getMinU()) / (fromSprite.getMaxU() - fromSprite.getMinU());
float normalizedV = (v - fromSprite.getMinV()) / (fromSprite.getMaxV() - fromSprite.getMinV());
// Remap to target sprite's atlas space
float newU = toSprite.getMinU() + normalizedU * (toSprite.getMaxU() - toSprite.getMinU());
float newV = toSprite.getMinV() + normalizedV * (toSprite.getMaxV() - toSprite.getMinV());
vertexData[uvOffset] = Float.floatToRawIntBits(newU);
vertexData[uvOffset + 1] = Float.floatToRawIntBits(newV);
}
} }
private void emitQuadWithSprite(net.minecraft.client.render.VertexConsumer consumer, private void offsetVertsAlongNormal(int[] vertexData, net.minecraft.util.math.Direction face, float amount) {
MatrixStack matrices, float dx = face.getOffsetX() * amount;
net.minecraft.client.render.model.BakedQuad quad, float dy = face.getOffsetY() * amount;
net.minecraft.client.texture.Sprite sprite, float dz = face.getOffsetZ() * amount;
int light, int overlay) {
// Re-emit the quad geometry but remap UVs to the new sprite int vertexSize = 8;
consumer.quad(matrices.peek(), quad, 1f, 1f, 1f, light, overlay); for (int i = 0; i < 4; i++) {
// Note: this uses the quad's original UVs which point to the blueprint texture. int base = i * vertexSize;
// For full texture remapping you'd need to manually rewrite vertex data. float x = Float.intBitsToFloat(vertexData[base]);
// This gives correct shape with blueprint texture as fallback — float y = Float.intBitsToFloat(vertexData[base + 1]);
// see note below for full UV remapping. float z = Float.intBitsToFloat(vertexData[base + 2]);
vertexData[base] = Float.floatToRawIntBits(x + dx);
vertexData[base + 1] = Float.floatToRawIntBits(y + dy);
vertexData[base + 2] = Float.floatToRawIntBits(z + dz);
}
} }
} }

View File

@@ -29,13 +29,12 @@ public class BlueprintBehavior {
ItemStack held = player.getStackInHand(hand); ItemStack held = player.getStackInHand(hand);
if (held.isEmpty()) { if (held.isEmpty() && player.isSneaking()) {
// Clear stored block
blueprint.clearStoredBlock(); blueprint.clearStoredBlock();
return ActionResult.SUCCESS; return ActionResult.SUCCESS;
} }
if (held.getItem() instanceof BlockItem blockItem) { if (!held.isEmpty() && held.getItem() instanceof BlockItem blockItem && !blueprint.hasStoredBlock()) {
String id = Registries.BLOCK.getId(blockItem.getBlock()).toString(); String id = Registries.BLOCK.getId(blockItem.getBlock()).toString();
blueprint.setStoredBlock(id); blueprint.setStoredBlock(id);
if (!player.isCreative()) held.decrement(1); if (!player.isCreative()) held.decrement(1);

View File

@@ -1,5 +1,6 @@
package dev.tggamesyt.szar; package dev.tggamesyt.szar;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType; import net.minecraft.block.entity.BlockEntityType;
@@ -16,8 +17,19 @@ public class BlueprintBlockEntity extends BlockEntity {
@Nullable @Nullable
private String storedBlockId = null; private String storedBlockId = null;
public BlueprintBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) { public BlueprintBlockEntity(BlockPos pos, BlockState state) {
super(type, pos, state); super(getBEType(state), pos, state);
}
private static BlockEntityType<BlueprintBlockEntity> getBEType(BlockState state) {
Block block = state.getBlock();
if (block instanceof BlueprintStairsBlock) return BlueprintBlocks.BLUEPRINT_STAIRS_BE_TYPE;
if (block instanceof BlueprintSlabBlock) return BlueprintBlocks.BLUEPRINT_SLAB_BE_TYPE;
if (block instanceof BlueprintDoorBlock) return BlueprintBlocks.BLUEPRINT_DOOR_BE_TYPE;
if (block instanceof BlueprintTrapDoorBlock) return BlueprintBlocks.BLUEPRINT_TRAPDOOR_BE_TYPE;
if (block instanceof BlueprintWallBlock) return BlueprintBlocks.BLUEPRINT_WALL_BE_TYPE;
if (block instanceof BlueprintFenceBlock) return BlueprintBlocks.BLUEPRINT_FENCE_BE_TYPE;
throw new IllegalStateException("Unknown blueprint block: " + block);
} }
public boolean hasStoredBlock() { public boolean hasStoredBlock() {

View File

@@ -4,6 +4,7 @@ import dev.tggamesyt.szar.Szar;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.minecraft.block.AbstractBlock; import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.MapColor; import net.minecraft.block.MapColor;
import net.minecraft.block.entity.BlockEntityType; import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
@@ -38,47 +39,44 @@ public class BlueprintBlocks {
public static final BlueprintWallBlock BLUEPRINT_WALL = public static final BlueprintWallBlock BLUEPRINT_WALL =
Registry.register(Registries.BLOCK, new Identifier(Szar.MOD_ID, "blueprint_wall"), Registry.register(Registries.BLOCK, new Identifier(Szar.MOD_ID, "blueprint_wall"),
new BlueprintWallBlock(settings())); new BlueprintWallBlock(AbstractBlock.Settings.copy(Blocks.STONE_BRICK_WALL)));
public static final BlueprintFenceBlock BLUEPRINT_FENCE = public static final BlueprintFenceBlock BLUEPRINT_FENCE =
Registry.register(Registries.BLOCK, new Identifier(Szar.MOD_ID, "blueprint_fence"), Registry.register(Registries.BLOCK, new Identifier(Szar.MOD_ID, "blueprint_fence"),
new BlueprintFenceBlock(settings())); new BlueprintFenceBlock(AbstractBlock.Settings.copy(Blocks.OAK_FENCE)));
public static final BlockEntityType<BlueprintBlockEntity> BLUEPRINT_STAIRS_BE_TYPE = public static final BlockEntityType<BlueprintBlockEntity> BLUEPRINT_STAIRS_BE_TYPE;
Registry.register(Registries.BLOCK_ENTITY_TYPE, new Identifier(Szar.MOD_ID, "blueprint_stairs_be"), public static final BlockEntityType<BlueprintBlockEntity> BLUEPRINT_SLAB_BE_TYPE;
FabricBlockEntityTypeBuilder.create( public static final BlockEntityType<BlueprintBlockEntity> BLUEPRINT_DOOR_BE_TYPE;
(pos, state) -> new BlueprintBlockEntity(null, pos, state), public static final BlockEntityType<BlueprintBlockEntity> BLUEPRINT_TRAPDOOR_BE_TYPE;
BLUEPRINT_STAIRS).build()); public static final BlockEntityType<BlueprintBlockEntity> BLUEPRINT_WALL_BE_TYPE;
public static final BlockEntityType<BlueprintBlockEntity> BLUEPRINT_FENCE_BE_TYPE;
public static final BlockEntityType<BlueprintBlockEntity> BLUEPRINT_SLAB_BE_TYPE = static {
Registry.register(Registries.BLOCK_ENTITY_TYPE, new Identifier(Szar.MOD_ID, "blueprint_slab_be"), BLUEPRINT_STAIRS_BE_TYPE = Registry.register(Registries.BLOCK_ENTITY_TYPE,
FabricBlockEntityTypeBuilder.create( new Identifier(Szar.MOD_ID, "blueprint_stairs_be"),
(pos, state) -> new BlueprintBlockEntity(null, pos, state), FabricBlockEntityTypeBuilder.create(BlueprintBlockEntity::new, BLUEPRINT_STAIRS).build());
BLUEPRINT_SLAB).build());
public static final BlockEntityType<BlueprintBlockEntity> BLUEPRINT_DOOR_BE_TYPE = BLUEPRINT_SLAB_BE_TYPE = Registry.register(Registries.BLOCK_ENTITY_TYPE,
Registry.register(Registries.BLOCK_ENTITY_TYPE, new Identifier(Szar.MOD_ID, "blueprint_door_be"), new Identifier(Szar.MOD_ID, "blueprint_slab_be"),
FabricBlockEntityTypeBuilder.create( FabricBlockEntityTypeBuilder.create(BlueprintBlockEntity::new, BLUEPRINT_SLAB).build());
(pos, state) -> new BlueprintBlockEntity(null, pos, state),
BLUEPRINT_DOOR).build());
public static final BlockEntityType<BlueprintBlockEntity> BLUEPRINT_TRAPDOOR_BE_TYPE = BLUEPRINT_DOOR_BE_TYPE = Registry.register(Registries.BLOCK_ENTITY_TYPE,
Registry.register(Registries.BLOCK_ENTITY_TYPE, new Identifier(Szar.MOD_ID, "blueprint_trapdoor_be"), new Identifier(Szar.MOD_ID, "blueprint_door_be"),
FabricBlockEntityTypeBuilder.create( FabricBlockEntityTypeBuilder.create(BlueprintBlockEntity::new, BLUEPRINT_DOOR).build());
(pos, state) -> new BlueprintBlockEntity(null, pos, state),
BLUEPRINT_TRAPDOOR).build());
public static final BlockEntityType<BlueprintBlockEntity> BLUEPRINT_WALL_BE_TYPE = BLUEPRINT_TRAPDOOR_BE_TYPE = Registry.register(Registries.BLOCK_ENTITY_TYPE,
Registry.register(Registries.BLOCK_ENTITY_TYPE, new Identifier(Szar.MOD_ID, "blueprint_wall_be"), new Identifier(Szar.MOD_ID, "blueprint_trapdoor_be"),
FabricBlockEntityTypeBuilder.create( FabricBlockEntityTypeBuilder.create(BlueprintBlockEntity::new, BLUEPRINT_TRAPDOOR).build());
(pos, state) -> new BlueprintBlockEntity(null, pos, state),
BLUEPRINT_WALL).build());
public static final BlockEntityType<BlueprintBlockEntity> BLUEPRINT_FENCE_BE_TYPE = BLUEPRINT_WALL_BE_TYPE = Registry.register(Registries.BLOCK_ENTITY_TYPE,
Registry.register(Registries.BLOCK_ENTITY_TYPE, new Identifier(Szar.MOD_ID, "blueprint_fence_be"), new Identifier(Szar.MOD_ID, "blueprint_wall_be"),
FabricBlockEntityTypeBuilder.create( FabricBlockEntityTypeBuilder.create(BlueprintBlockEntity::new, BLUEPRINT_WALL).build());
(pos, state) -> new BlueprintBlockEntity(null, pos, state),
BLUEPRINT_FENCE).build()); BLUEPRINT_FENCE_BE_TYPE = Registry.register(Registries.BLOCK_ENTITY_TYPE,
new Identifier(Szar.MOD_ID, "blueprint_fence_be"),
FabricBlockEntityTypeBuilder.create(BlueprintBlockEntity::new, BLUEPRINT_FENCE).build());
}
public static final BlockItem BLUEPRINT_STAIRS_ITEM = Registry.register(Registries.ITEM, public static final BlockItem BLUEPRINT_STAIRS_ITEM = Registry.register(Registries.ITEM,
new Identifier(Szar.MOD_ID, "blueprint_stairs"), new Identifier(Szar.MOD_ID, "blueprint_stairs"),
new BlockItem(BLUEPRINT_STAIRS, new Item.Settings())); new BlockItem(BLUEPRINT_STAIRS, new Item.Settings()));

View File

@@ -19,7 +19,7 @@ public class BlueprintDoorBlock extends DoorBlock implements BlockEntityProvider
@Override @Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new BlueprintBlockEntity(BlueprintBlocks.BLUEPRINT_DOOR_BE_TYPE, pos, state); return new BlueprintBlockEntity(pos, state);
} }
@Override @Override

View File

@@ -18,7 +18,7 @@ public class BlueprintFenceBlock extends FenceBlock implements BlockEntityProvid
@Override @Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new BlueprintBlockEntity(BlueprintBlocks.BLUEPRINT_FENCE_BE_TYPE, pos, state); return new BlueprintBlockEntity(pos, state);
} }
@Override @Override

View File

@@ -18,7 +18,7 @@ public class BlueprintSlabBlock extends SlabBlock implements BlockEntityProvider
@Override @Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new BlueprintBlockEntity(BlueprintBlocks.BLUEPRINT_SLAB_BE_TYPE, pos, state); return new BlueprintBlockEntity(pos, state);
} }
@Override @Override

View File

@@ -18,7 +18,7 @@ public class BlueprintStairsBlock extends StairsBlock implements BlockEntityProv
@Override @Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new BlueprintBlockEntity(BlueprintBlocks.BLUEPRINT_STAIRS_BE_TYPE, pos, state); return new BlueprintBlockEntity(pos, state);
} }
@Override @Override

View File

@@ -18,7 +18,7 @@ public class BlueprintTrapDoorBlock extends TrapdoorBlock implements BlockEntity
@Override @Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new BlueprintBlockEntity(BlueprintBlocks.BLUEPRINT_TRAPDOOR_BE_TYPE, pos, state); return new BlueprintBlockEntity(pos, state);
} }
@Override @Override

View File

@@ -18,7 +18,7 @@ public class BlueprintWallBlock extends WallBlock implements BlockEntityProvider
@Override @Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new BlueprintBlockEntity(BlueprintBlocks.BLUEPRINT_WALL_BE_TYPE, pos, state); return new BlueprintBlockEntity(pos, state);
} }
@Override @Override

View File

@@ -1,22 +1,9 @@
{ {
"multipart": [ "multipart": [
{ "apply": { "model": "szar:block/blueprint_wall_post" }, { "apply": { "model": "szar:block/blueprint_wall_post" } },
"when": { "up": "true" } }, { "apply": { "model": "szar:block/blueprint_wall_side", "uvlock": true } },
{ "apply": { "model": "szar:block/blueprint_wall_side", "uvlock": true }, { "apply": { "model": "szar:block/blueprint_wall_side", "y": 90, "uvlock": true } },
"when": { "north": "low" } }, { "apply": { "model": "szar:block/blueprint_wall_side", "y": 180, "uvlock": true } },
{ "apply": { "model": "szar:block/blueprint_wall_side", "y": 90, "uvlock": true }, { "apply": { "model": "szar:block/blueprint_wall_side", "y": 270, "uvlock": true } }
"when": { "east": "low" } },
{ "apply": { "model": "szar:block/blueprint_wall_side", "y": 180, "uvlock": true },
"when": { "south": "low" } },
{ "apply": { "model": "szar:block/blueprint_wall_side", "y": 270, "uvlock": true },
"when": { "west": "low" } },
{ "apply": { "model": "szar:block/blueprint_wall_side_tall", "uvlock": true },
"when": { "north": "tall" } },
{ "apply": { "model": "szar:block/blueprint_wall_side_tall", "y": 90, "uvlock": true },
"when": { "east": "tall" } },
{ "apply": { "model": "szar:block/blueprint_wall_side_tall", "y": 180, "uvlock": true },
"when": { "south": "tall" } },
{ "apply": { "model": "szar:block/blueprint_wall_side_tall", "y": 270, "uvlock": true },
"when": { "west": "tall" } }
] ]
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B