fix blueprint lighting

This commit is contained in:
2026-03-25 18:33:10 +01:00
parent 7e5700d723
commit 8869dc4087
5 changed files with 193 additions and 79 deletions

View File

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

View File

@@ -4,10 +4,13 @@ import dev.tggamesyt.szar.BlueprintBlockEntity;
import net.minecraft.block.BlockRenderType;
import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.render.block.BlockRenderManager;
import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier;
@@ -21,6 +24,7 @@ public class BlueprintBlockEntityRenderer implements BlockEntityRenderer<Bluepri
@Override
public void render(BlueprintBlockEntity entity, float tickDelta, MatrixStack matrices,
VertexConsumerProvider vertexConsumers, int light, int overlay) {
if (!entity.hasStoredBlock()) return;
String storedId = entity.getStoredBlockId();
@@ -33,93 +37,37 @@ public class BlueprintBlockEntityRenderer implements BlockEntityRenderer<Bluepri
if (storedState.getRenderType() == BlockRenderType.INVISIBLE) return;
BlockState blueprintState = entity.getCachedState();
BlockRenderManager renderer = MinecraftClient.getInstance().getBlockRenderManager();
var renderer = MinecraftClient.getInstance().getBlockRenderManager();
var storedModel = renderer.getModel(storedState);
var blueprintModel = renderer.getModel(blueprintState);
var particleSprite = storedModel.getParticleSprite();
var random = Random.create();
var wrappedModel = new BlueprintWrappedModel(
blueprintModel,
storedModel,
blueprintState,
storedState, entity
);
var layer = net.minecraft.client.render.RenderLayers.getBlockLayer(storedState);
var consumer = vertexConsumers.getBuffer(layer);
matrices.push();
// No scaling here anymore
for (var direction : new net.minecraft.util.math.Direction[]{
null,
net.minecraft.util.math.Direction.UP,
net.minecraft.util.math.Direction.DOWN,
net.minecraft.util.math.Direction.NORTH,
net.minecraft.util.math.Direction.SOUTH,
net.minecraft.util.math.Direction.EAST,
net.minecraft.util.math.Direction.WEST
}) {
random.setSeed(42L);
var quads = blueprintModel.getQuads(blueprintState, direction, random);
for (var quad : quads) {
int[] vertexData = quad.getVertexData().clone();
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
);
}
}
renderer.getModelRenderer().render(
entity.getWorld(),
wrappedModel,
storedState,
entity.getPos(),
matrices,
consumer,
false,
net.minecraft.util.math.random.Random.create(),
42L,
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 offsetVertsAlongNormal(int[] vertexData, net.minecraft.util.math.Direction face, float amount) {
float dx = face.getOffsetX() * amount;
float dy = face.getOffsetY() * amount;
float dz = face.getOffsetZ() * amount;
int vertexSize = 8;
for (int i = 0; i < 4; i++) {
int base = i * vertexSize;
float x = Float.intBitsToFloat(vertexData[base]);
float y = Float.intBitsToFloat(vertexData[base + 1]);
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

@@ -0,0 +1,165 @@
package dev.tggamesyt.szar.client;
import dev.tggamesyt.szar.BlueprintBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.json.ModelOverrideList;
import net.minecraft.client.render.model.json.ModelTransformation;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random;
import java.util.ArrayList;
import java.util.List;
public class BlueprintWrappedModel implements BakedModel {
private final BakedModel blueprint;
private final BakedModel stored;
private final BlockState blueprintState;
private final BlockState storedState;
private final BlueprintBlockEntity entity;
public BlueprintWrappedModel(BakedModel blueprint, BakedModel stored,
BlockState blueprintState, BlockState storedState, BlueprintBlockEntity entity) {
this.blueprint = blueprint;
this.stored = stored;
this.blueprintState = blueprintState;
this.storedState = storedState;
this.entity = entity;
}
@Override
public List<BakedQuad> getQuads(BlockState state, Direction face, Random random) {
List<BakedQuad> original = blueprint.getQuads(blueprintState, face, random);
List<BakedQuad> result = new ArrayList<>(original.size());
for (BakedQuad quad : original) {
int[] data = quad.getVertexData().clone();
Direction quadFace = quad.getFace();
Sprite target = getSpriteForFace(stored, storedState, quadFace);
remapUVs(data, quad.getSprite(), target);
BlockPos pos = entity.getPos();
Camera camera = MinecraftClient.getInstance().gameRenderer.getCamera();
Vec3d camPos = camera.getPos();
double dx = pos.getX() + 0.5 - camPos.x;
double dy = pos.getY() + 0.5 - camPos.y;
double dz = pos.getZ() + 0.5 - camPos.z;
double distance = Math.sqrt(dx*dx + dy*dy + dz*dz);
float offsetAmount = 0.0001f + (float)distance * 1e-5f;
offsetAmount = Math.min(offsetAmount, 0.001f); // clamp max
offsetVertsAlongNormal(data, quad.getFace(), offsetAmount);
result.add(new BakedQuad(
data,
quad.getColorIndex(),
quadFace,
target,
quad.hasShade()
));
}
return result;
}
// --- IMPORTANT: delegate everything else properly ---
@Override
public boolean useAmbientOcclusion() {
return stored.useAmbientOcclusion(); // important for lighting
}
@Override
public boolean hasDepth() {
return stored.hasDepth();
}
@Override
public boolean isSideLit() {
return stored.isSideLit();
}
@Override
public boolean isBuiltin() {
return false;
}
@Override
public Sprite getParticleSprite() {
return stored.getParticleSprite();
}
@Override
public ModelTransformation getTransformation() {
return stored.getTransformation();
}
@Override
public ModelOverrideList getOverrides() {
return stored.getOverrides();
}
// --- helpers ---
private Sprite getSpriteForFace(BakedModel model, BlockState state, Direction face) {
Random rand = Random.create(42L);
List<BakedQuad> quads = model.getQuads(state, face, rand);
if (!quads.isEmpty()) return quads.get(0).getSprite();
quads = model.getQuads(state, null, rand);
for (BakedQuad q : quads) {
if (q.getFace() == face) return q.getSprite();
}
return model.getParticleSprite();
}
private void remapUVs(int[] vertexData, Sprite from, Sprite to) {
int stride = 8;
for (int i = 0; i < 4; i++) {
int uvIndex = i * stride + 4;
float u = Float.intBitsToFloat(vertexData[uvIndex]);
float v = Float.intBitsToFloat(vertexData[uvIndex + 1]);
float nu = (u - from.getMinU()) / (from.getMaxU() - from.getMinU());
float nv = (v - from.getMinV()) / (from.getMaxV() - from.getMinV());
float newU = to.getMinU() + nu * (to.getMaxU() - to.getMinU());
float newV = to.getMinV() + nv * (to.getMaxV() - to.getMinV());
vertexData[uvIndex] = Float.floatToRawIntBits(newU);
vertexData[uvIndex + 1] = Float.floatToRawIntBits(newV);
}
}
private void offsetVertsAlongNormal(int[] vertexData, Direction face, float amount) {
if (face == null) return; // skip general quads
float dx = face.getOffsetX() * amount;
float dy = face.getOffsetY() * amount;
float dz = face.getOffsetZ() * amount;
int vertexSize = 8; // X,Y,Z,COLOR,U,V,UV2,NORMAL
for (int i = 0; i < 4; i++) {
int base = i * vertexSize;
float x = Float.intBitsToFloat(vertexData[base]);
float y = Float.intBitsToFloat(vertexData[base + 1]);
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);
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

After

Width:  |  Height:  |  Size: 880 B

View File

@@ -7,3 +7,4 @@ accessible field net/minecraft/server/network/ServerPlayerInteractionManager pla
accessible method net/minecraft/client/render/entity/LivingEntityRenderer addFeature (Lnet/minecraft/client/render/entity/feature/FeatureRenderer;)Z
accessible field net/minecraft/client/render/entity/EntityRenderDispatcher renderers Ljava/util/Map;
accessible method net/minecraft/world/GameRules$BooleanRule create (ZLjava/util/function/BiConsumer;)Lnet/minecraft/world/GameRules$Type;
accessible method net/minecraft/client/render/block/BlockModelRenderer renderQuad (Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/client/render/VertexConsumer;Lnet/minecraft/client/util/math/MatrixStack$Entry;Lnet/minecraft/client/render/model/BakedQuad;FFFFIIIII)V