Sodium

Sodium

35M Downloads

LinearColorBlender breaks any case where a precise position is required in a BlockColorProvider.

devpelux opened this issue ยท 2 comments

commented

Version information

mc1.19-0.4.2+build.16

Expected Behavior

I've implemented a new block that requires its precise position to get data to calculate the color.
It needs its precise actual position to get its associated block entity, then get an item stored inside the block entity.

ColorProviderRegistry.BLOCK.register(TunBlock::getContainedFluidColor, TUN);
public static int getContainedFluidColor(BlockState state, BlockRenderView view, BlockPos pos, int i) {
    if (view.getBlockEntity(pos) instanceof TunBlockEntity tunEntity) {
        if (tunEntity.hasContained()) {
            return containable.get(tunEntity.getContained()).getColor(state, view, pos, i);
        }
    }

    return -1;
}

2022-06-07_13 45 14

Actual Behavior

With sodium installed, the color provider receives the neighbors block positions but not the actual block position.

This is caused by LinearColorBlender that requires the neighbors block colors to blend them.

The result, in by specific case, is that the color provider cannot get the block entity, and the result obtained is null, so it returns the empty color.

2022-06-07_13 47 02

Calling trace

calling

Reproduction Steps

Create a new block, then register a color provider for the block.
Start a debug session, with a breakpoint inside the method for getting the color.
Check the BlockPos parameter.
Check the actual block pos of the block (the block position, not the player position!) with F3 in the game.

Remember to add sodium into the mods folder into your run folder!

Example:

ColorProviderRegistry.BLOCK.register(MyBlock::getPartColor, MYBLOCK);
public static int getPartColor(BlockState state, BlockRenderView view, BlockPos pos, int i) {
    if (view.getBlockEntity(pos) instanceof MyBlockEntity entity) {  //<<< Place the breakpoint here.
        //Calculating color from the block-entity...
    }

    return -1;
}

Java version

Java 17

CPU

Intel(R) Core(TM) i7-9750H

GPU

NVIDIA RTX 2080

Additional information

No response

commented

Workaround

A workaround is to add a mixin to LinearColorBlender.getVertexColor(), to get the color from the actual block position without blending.

Create a registry to register all the blocks needing the precise position:

/**
 * All the registered blocks in this registry
 * will ignore sodium color blending optimizations.<br>
 * This is a singleton.
 */
public class IgnoreColorBlendingRegistry {
    /** Gets the instance. */
    private static final IgnoreColorBlendingRegistry INSTANCE = new IgnoreColorBlendingRegistry();

    private final Set<Identifier> blockIds = new HashSet<>();

    /** Initializes a new {@link IgnoreColorBlendingRegistry}. */
    private IgnoreColorBlendingRegistry() {}

    /** Registers a block into the registry. */
    public static void register(Block block) {
        INSTANCE.blockIds.add(Registry.BLOCK.getId(block));
    }

    /** Checks if a block is into the registry. */
    public static boolean isRegistered(Block block) {
        return INSTANCE.blockIds.contains(Registry.BLOCK.getId(block));
    }
}

Then register the blocks that should avoid blending to work correctly:

IgnoreColorBlendingRegistry.register(MYBLOCK);

Lastly add a client-side mixin to LinearColorBlender.getVertexColor():

@Mixin(LinearColorBlender.class)
public abstract class LinearColorBlenderMixin {
    @Shadow protected abstract <T> int getBlockColor(BlockRenderView world, T state, ColorSampler<T> sampler, int x, int y, int z, int colorIdx);

    @Inject(method = "getVertexColor", at = @At("HEAD"), cancellable = true)
    private <T> void injectGetVertexColor(BlockRenderView world, BlockPos origin, ModelQuadView quad, ColorSampler<T> sampler,
                                      T state, int vertexIdx, CallbackInfoReturnable<Integer> cir) {
        if (state instanceof BlockState blockState) {
            if (IgnoreColorBlendingRegistry.isRegistered(blockState.getBlock())) {
                int color = getBlockColor(world, state, sampler, origin.getX(), origin.getY(), origin.getZ(), quad.getColorIndex());
                cir.setReturnValue(ColorARGB.toABGR(color));
            }
        }
    }
}

This is for blocks, you can make a similar behaviour with fluids or other "state" types (for fluids, the state will be FluidState).

commented

Closes because is a duplicate of #895