Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to avoid duplicating the RGB333 tint palette per tinted model. The client-position tint fix still stores a per-quad quantized tint index, but all biome-tinted models now point at one shared 512-entry RGB333 palette, making the added palette VRAM fixed at about 2 KB total.

Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@
// this _quarters_ the memory requirements for the texture atlas!!! WHICH IS HUGE saving
public class ModelFactory {
public static final int MODEL_TEXTURE_SIZE = 16;

// Position-aware biome tint compatibility mode.
// Biome-tinted LOD models share one fixed 512-entry RGB333 palette.
// RenderDataFactory writes the palette index produced from BlockColors
// at the actual LOD sample world position into the quad biome-id bits.
public static final boolean USE_POSITION_TINT_PALETTE = true;
public static final int POSITION_TINT_PALETTE_SIZE = 512;
public static final int POSITION_TINT_PALETTE_INDEX = 0;
public static final int POSITION_TINT_FALLBACK_INDEX = POSITION_TINT_PALETTE_SIZE - 1;

public static final int LAYERS = Integer.numberOfTrailingZeros(MODEL_TEXTURE_SIZE);

//TODO: replace the fluid BlockState with a client model id integer of the fluidState, requires looking up
Expand Down Expand Up @@ -131,6 +141,8 @@ public ModelEntry(ColourDepthTextureData[] textures, int fluidBlockStateId, int

private final ConcurrentLinkedDeque<ResultUploader> uploadResults = new ConcurrentLinkedDeque<>();

private boolean positionTintPaletteUploaded = false;

private Object2IntMap<BlockState> customBlockStateIdMapping;

//TODO: NOTE!!! is it worth even uploading as a 16x16 texture, since automatic lod selection... doing 8x8 textures might be perfectly ok!!!
Expand Down Expand Up @@ -649,6 +661,12 @@ private ModelBakeResultUpload processTextureBakeResult(int blockId, BlockState b
MemoryUtil.memPutInt(uploadPtr, -1);//Set the default to nothing so that its faster on the gpu
} else if (!isBiomeColourDependent) {
MemoryUtil.memPutInt(uploadPtr, entry.tintingColour);
} else if (USE_POSITION_TINT_PALETTE) {
// Position-aware tinting mode: use the quad biome-id bits as an RGB333 palette index.
// All tinted models point at the same shared palette; RenderDataFactory computes
// the per-quad palette index from the actual LOD sample world position.
MemoryUtil.memPutInt(uploadPtr, POSITION_TINT_PALETTE_INDEX);
this.queuePositionTintPaletteUpload(uploadResult);
} else {
//Populate the list of biomes for the model state
int biomeIndex = this.modelsRequiringBiomeColours.size() * this.biomes.size();
Expand Down Expand Up @@ -730,6 +748,63 @@ public int getMinBuildHeight() {
return Math.clamp(state.getLightEmission(),0,15);
}

private void queuePositionTintPaletteUpload(ModelBakeResultUpload uploadResult) {
if (this.positionTintPaletteUploaded) {
return;
}
this.positionTintPaletteUploaded = true;
uploadResult.biomeUploadIndex = POSITION_TINT_PALETTE_INDEX;
long clrUploadPtr = (uploadResult.biomeUpload = new MemoryBuffer(4L * POSITION_TINT_PALETTE_SIZE)).address;
writePositionTintPalette(clrUploadPtr);
}

private static void writePositionTintPalette(long ptr) {
for (int i = 0; i < POSITION_TINT_PALETTE_SIZE; i++) {
MemoryUtil.memPutInt(ptr + i * 4L, positionTintPaletteColour(i) | 0xFF000000);
}
}

private static int positionTintPaletteColour(int index) {
int r = (index >> 6) & 7;
int g = (index >> 3) & 7;
int b = index & 7;
return ((r * 255 / 7) << 16) | ((g * 255 / 7) << 8) | (b * 255 / 7);
}

public int getPositionTintPaletteIndex(int blockId, BlockPos pos) {
if (!USE_POSITION_TINT_PALETTE) {
return -1;
}
var level = Minecraft.getInstance().level;
if (level == null) {
return POSITION_TINT_FALLBACK_INDEX;
}
BlockState state = this.mapper.getBlockStateFromBlockId(blockId);
int color;
try {
color = Minecraft.getInstance().getBlockColors().getColor(state, level, pos, 0);
if (color == -1) {
color = Minecraft.getInstance().getBlockColors().getColor(state, level, pos, 1);
}
} catch (Throwable throwable) {
return POSITION_TINT_FALLBACK_INDEX;
}
if (color == -1) {
return POSITION_TINT_FALLBACK_INDEX;
}
return quantizePositionTintColour(color);
}

private static int quantizePositionTintColour(int color) {
int r = (color >> 16) & 255;
int g = (color >> 8) & 255;
int b = color & 255;
int r3 = (r * 7 + 127) / 255;
int g3 = (g * 7 + 127) / 255;
int b3 = (b * 7 + 127) / 255;
return (r3 << 6) | (g3 << 3) | b3;
}

private static final class BiomeUploadResult implements ResultUploader {
private final MemoryBuffer biomeColourBuffer;
private final MemoryBuffer modelBiomeIndexPairs;
Expand Down Expand Up @@ -781,6 +856,10 @@ private BiomeUploadResult addBiome0(int id, Biome biome) {
return null;
}

// In position-tint-palette mode, biome-coloured model LUTs are fixed RGB333 palettes.
// New biome registrations do not require rebuilding/reuploading those model colour LUTs.
if (USE_POSITION_TINT_PALETTE) return null;

if (this.modelsRequiringBiomeColours.isEmpty()) return null;

var result = new BiomeUploadResult(this.biomes.size(), this.modelsRequiringBiomeColours.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import me.cortex.voxy.common.world.WorldSection;
import me.cortex.voxy.common.world.other.Mapper;
import me.cortex.voxy.commonImpl.VoxyCommon;
import net.minecraft.core.BlockPos;
import org.lwjgl.system.MemoryUtil;

import java.util.Arrays;
Expand Down Expand Up @@ -201,9 +202,18 @@ private static long getQuadTyping(long metadata) {//2 bits
return 0b111L&(0b000_000_010_100L>>(ModelQueries._isTranslucent(metadata)*6+ModelQueries._isDoubleSided(metadata)*3));
}

private static long packPartialQuadData(int modelId, long state, long metadata) {
private long packPartialQuadData(int modelId, long state, long metadata, WorldSection section, int localIndex) {
//This uses hardcoded data to shuffle things
long lightAndBiome = (state&((0x1FFL<<47)|(0xFFL<<56)))>>>1;

if (ModelFactory.USE_POSITION_TINT_PALETTE && ModelQueries.isBiomeColoured(metadata)) {
int paletteIndex = this.modelMan.getPositionTintPaletteIndex(Mapper.getBlockId(state), getWorldSamplePos(section, localIndex));
if (paletteIndex >= 0) {
lightAndBiome &= ~(0x1FFL << 46);
lightAndBiome |= (Integer.toUnsignedLong(paletteIndex) & 0x1FFL) << 46;
}
}

lightAndBiome &= ~(ModelQueries._notIsBiomeColoured(metadata) * (0x1FFL << 46));//46 not 47 because is already shifted by 1 THIS WASTED 4 HOURS ;-; aaaaaAAAAAA
lightAndBiome &= ~(ModelQueries._isFullyOpaque(metadata)*(0xFFL << 55));//If its fully opaque it always uses neighbor light?

Expand All @@ -213,7 +223,20 @@ private static long packPartialQuadData(int modelId, long state, long metadata)
return quadData;
}

private int prepareSectionData(final long[] rawSectionData) {
private static BlockPos getWorldSamplePos(WorldSection section, int localIndex) {
int localX = localIndex & 31;
int localZ = (localIndex >> 5) & 31;
int localY = (localIndex >> 10) & 31;
int scale = 1 << section.lvl;
int offset = scale >> 1;
return new BlockPos(
((section.x << 5) + localX) * scale + offset,
((section.y << 5) + localY) * scale + offset,
((section.z << 5) + localZ) * scale + offset
);
}

private int prepareSectionData(final long[] rawSectionData, WorldSection section) {
final var sectionData = this.sectionData;
final var rawModelIds = this.modelMan._unsafeRawAccess();
long opaque = 0;
Expand Down Expand Up @@ -242,7 +265,7 @@ private int prepareSectionData(final long[] rawSectionData) {

long modelMetadata = this.modelMan.getModelMetadataFromClientId(modelId);

sectionData[i * 2] = packPartialQuadData(modelId, block, modelMetadata);
sectionData[i * 2] = packPartialQuadData(modelId, block, modelMetadata, section, i);
sectionData[i * 2 + 1] = modelMetadata;

notEmpty |= 1L << j;
Expand Down Expand Up @@ -1727,7 +1750,7 @@ public BuiltSection generateMesh(WorldSection section) {
Arrays.fill(this.fluidMasks, 0);

//Prepare everything
int neighborMskAndFlags = this.prepareSectionData(section._unsafeGetRawDataArray());
int neighborMskAndFlags = this.prepareSectionData(section._unsafeGetRawDataArray(), section);
if ((neighborMskAndFlags&(1<<31))!=0) {//We failed to get everything so throw exception
throw new IdNotYetComputedException(neighborMskAndFlags&((1<<20)-1), true);
}
Expand Down