This commit is contained in:
mcrcortex
2024-02-16 00:24:52 +10:00
parent f91755b043
commit 6ac4071f1b
9 changed files with 191 additions and 67 deletions

View File

@@ -3,9 +3,11 @@ package me.cortex.voxy.client.core.model;
import com.mojang.blaze3d.platform.GlConst;
import com.mojang.blaze3d.platform.GlStateManager;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import me.cortex.voxy.client.core.IGetVoxelCore;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.world.other.Mapper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
@@ -53,9 +55,9 @@ import static org.lwjgl.opengl.GL45C.glTextureSubImage2D;
public class ModelManager {
//TODO: replace the fluid BlockState with a client model id integer of the fluidState, requires looking up
// the fluid state in the mipper
private record ModelEntry(List<ColourDepthTextureData> textures, BlockState fluidBlockState){
private ModelEntry(ColourDepthTextureData[] textures, BlockState fluidBlockState) {
this(Stream.of(textures).map(ColourDepthTextureData::clone).toList(), fluidBlockState);
private record ModelEntry(List<ColourDepthTextureData> textures, int fluidBlockStateId){
private ModelEntry(ColourDepthTextureData[] textures, int fluidBlockStateId) {
this(Stream.of(textures).map(ColourDepthTextureData::clone).toList(), fluidBlockStateId);
}
}
@@ -96,6 +98,7 @@ public class ModelManager {
// this has an issue with scaffolding i believe tho, so maybe make it a probability to render??? idk
private final long[] metadataCache;
private final int[] fluidStateLUT;
//Provides a map from id -> model id as multiple ids might have the same internal model id
private final int[] idMappings;
@@ -116,8 +119,10 @@ public class ModelManager {
//TODO: figure out how to do mipping :blobfox_pineapple:
this.textures = new GlTexture().store(GL_RGBA8, 4, modelTextureSize*3*256,modelTextureSize*2*256);
this.metadataCache = new long[1<<16];
this.fluidStateLUT = new int[1<<16];
this.idMappings = new int[1<<20];//Max of 1 million blockstates mapping to 65k model states
Arrays.fill(this.idMappings, -1);
Arrays.fill(this.fluidStateLUT, -1);
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
@@ -145,8 +150,24 @@ public class ModelManager {
int modelId = -1;
var textureData = this.bakery.renderFaces(blockState, 123456, isFluid);
int clientFluidStateId = -1;
if ((!isFluid) && (!blockState.getFluidState().isEmpty())) {
//Insert into the fluid LUT
var fluidState = blockState.getFluidState().getBlockState();
//TODO:FIXME: PASS IN THE Mapper instead of grabbing it!!! THIS IS CRTICIAL TO FIX
int fluidStateId = ((IGetVoxelCore)MinecraftClient.getInstance().worldRenderer).getVoxelCore().getWorldEngine().getMapper().getIdForBlockState(fluidState);
clientFluidStateId = this.idMappings[fluidStateId];
if (clientFluidStateId == -1) {
clientFluidStateId = this.addEntry(fluidStateId, fluidState);
}
}
{//Deduplicate same entries
var entry = new ModelEntry(textureData, isFluid||blockState.getFluidState().isEmpty()?null:blockState.getFluidState().getBlockState());
var entry = new ModelEntry(textureData, clientFluidStateId);
int possibleDuplicate = this.modelTexture2id.getInt(entry);
if (possibleDuplicate != -1) {//Duplicate found
this.idMappings[blockId] = possibleDuplicate;
@@ -159,6 +180,12 @@ public class ModelManager {
}
}
if (isFluid) {
this.fluidStateLUT[modelId] = modelId;
} else if (clientFluidStateId != -1) {
this.fluidStateLUT[modelId] = clientFluidStateId;
}
var colourProvider = MinecraftClient.getInstance().getBlockColors().providers.get(Registries.BLOCK.getRawId(blockState.getBlock()));
@@ -205,6 +232,7 @@ public class ModelManager {
metadata |= blockRenderLayer == RenderLayer.getTranslucent()?2:0;
metadata |= needsDoubleSidedQuads?4:0;
metadata |= (!blockState.getFluidState().isEmpty())?8:0;//Has a fluid state accosiacted with it
metadata |= isFluid?16:0;//Is a fluid
//TODO: add a bunch of control config options for overriding/setting options of metadata for each face of each type
for (int face = 5; face != -1; face--) {//In reverse order to make indexing into the metadata long easier
@@ -464,11 +492,6 @@ public class ModelManager {
return ((metadata>>(8*face))&0b1000) != 0;
}
public static boolean isColoured(long metadata) {
//TODO: THIS
return false;
}
public static boolean isDoubleSided(long metadata) {
return ((metadata>>(8*6))&4) != 0;
}
@@ -481,6 +504,10 @@ public class ModelManager {
return ((metadata>>(8*6))&8) != 0;
}
public static boolean isFluid(long metadata) {
return ((metadata>>(8*6))&16) != 0;
}
public static boolean isBiomeColoured(long metadata) {
return ((metadata>>(8*6))&1) != 0;
}
@@ -538,6 +565,10 @@ public class ModelManager {
//}
}
public long getModelMetadataFromClientId(int clientId) {
return this.metadataCache[clientId];
}
public int getModelId(int blockId) {
int map = this.idMappings[blockId];
if (map == -1) {
@@ -546,6 +577,14 @@ public class ModelManager {
return map;
}
public int getFluidClientStateId(int clientBlockStateId) {
int map = this.fluidStateLUT[clientBlockStateId];
if (map == -1) {
throw new IdNotYetComputedException(clientBlockStateId);
}
return map;
}
private void putTextures(int id, ColourDepthTextureData[] textures) {
int X = (id&0xFF) * this.modelTextureSize*3;
int Y = ((id>>8)&0xFF) * this.modelTextureSize*2;

View File

@@ -141,8 +141,8 @@ public class RenderTracker {
//Enqueues a renderTask for a section to cache the result
public void addCache(int lvl, int x, int y, int z) {
this.renderGen.markCache(lvl, x, y, z);
this.renderGen.enqueueTask(lvl, x, y, z, ((lvl1, x1, y1, z1) -> true));//TODO: replace the true identity lambda with a callback check to the render cache
//this.renderGen.markCache(lvl, x, y, z);
//this.renderGen.enqueueTask(lvl, x, y, z, ((lvl1, x1, y1, z1) -> true));//TODO: replace the true identity lambda with a callback check to the render cache
}
//Removes the position from the cache

View File

@@ -20,6 +20,8 @@ public class RenderDataFactory {
private final Mesher2D negativeMesher = new Mesher2D(5, 15);
private final Mesher2D positiveMesher = new Mesher2D(5, 15);
private final Mesher2D negativeFluidMesher = new Mesher2D(5, 15);
private final Mesher2D positiveFluidMesher = new Mesher2D(5, 15);
private final long[] sectionCache = new long[32*32*32];
private final long[] connectedSectionCache = new long[32*32*32];
@@ -45,10 +47,12 @@ public class RenderDataFactory {
// instead of needing to regen the entire thing
//section is already acquired and gets released by the parent
//Ok so the idea for fluid rendering is to make it use a seperate mesher and use a different code path for it
// since fluid states are explicitly overlays over the base block
// can do funny stuff like double rendering
//buildMask in the lower 6 bits contains the faces to build, the next 6 bits are whether the edge face builds against
// its neigbor or not (0 if it does 1 if it doesnt (0 is default behavior))
//section is already acquired and gets released by the parent
public BuiltSection generateMesh(WorldSection section) {
section.copyDataTo(this.sectionCache);
this.translucentQuadCollector.clear();
@@ -116,6 +120,7 @@ public class RenderDataFactory {
}
//TODO: FIXME: a block can have a face even if it doesnt, cause of if it has a fluid state
private void generateMeshForAxis(WorldSection section, int axisId) {
int aX = axisId==2?1:0;
int aY = axisId==0?1:0;
@@ -130,6 +135,8 @@ public class RenderDataFactory {
for (int primary = 0; primary < 32; primary++) {
this.negativeMesher.reset();
this.positiveMesher.reset();
this.negativeFluidMesher.reset();
this.positiveFluidMesher.reset();
for (int a = 0; a < 32; a++) {
for (int b = 0; b < 32; b++) {
@@ -147,8 +154,7 @@ public class RenderDataFactory {
boolean putFace = false;
//Branch into 2 paths, the + direction and -direction, doing it at once makes it much faster as it halves the number of loops
if (ModelManager.faceExists(selfMetadata, axisId<<1)) {//- direction
if (ModelManager.faceExists(selfMetadata, axisId<<1) || ModelManager.containsFluid(selfMetadata)) {//- direction
long facingState = Mapper.AIR;
//Need to access the other connecting section
if (primary == 0) {
@@ -167,9 +173,14 @@ public class RenderDataFactory {
facingState = this.sectionCache[WorldSection.getIndex(x-aX, y-aY, z-aZ)];
}
if (!ModelManager.isFluid(selfMetadata)) {
putFace |= this.putFaceIfCan(this.negativeMesher, (axisId << 1), (axisId << 1)|1, self, selfMetadata, selfBlockId, facingState, a, b);
}
if (ModelManager.faceExists(selfMetadata, axisId<<1)) {//+ direction
if (ModelManager.containsFluid(selfMetadata)) {
putFace |= this.putFluidFaceIfCan(this.negativeFluidMesher, (axisId << 1), (axisId << 1)|1, self, selfMetadata, selfBlockId, facingState, a, b);
}
}
if (ModelManager.faceExists(selfMetadata, (axisId<<1)|1) || ModelManager.containsFluid(selfMetadata)) {//+ direction
long facingState = Mapper.AIR;
//Need to access the other connecting section
if (primary == 31) {
@@ -187,9 +198,13 @@ public class RenderDataFactory {
} else {
facingState = this.sectionCache[WorldSection.getIndex(x+aX, y+aY, z+aZ)];
}
if (!ModelManager.isFluid(selfMetadata)) {
putFace |= this.putFaceIfCan(this.positiveMesher, (axisId << 1) | 1, (axisId << 1), self, selfMetadata, selfBlockId, facingState, a, b);
}
if (ModelManager.containsFluid(selfMetadata)) {
putFace |= this.putFluidFaceIfCan(this.positiveFluidMesher, (axisId << 1) | 1, (axisId << 1), self, selfMetadata, selfBlockId, facingState, a, b);
}
}
if (putFace) {
this.minX = Math.min(this.minX, x);
@@ -204,9 +219,54 @@ public class RenderDataFactory {
processMeshedFace(this.negativeMesher, axisId<<1, primary, this.directionalQuadCollectors[(axisId<<1)]);
processMeshedFace(this.positiveMesher, (axisId<<1)|1, primary, this.directionalQuadCollectors[(axisId<<1)|1]);
processMeshedFace(this.negativeFluidMesher, axisId<<1, primary, this.directionalQuadCollectors[(axisId<<1)]);
processMeshedFace(this.positiveFluidMesher, (axisId<<1)|1, primary, this.directionalQuadCollectors[(axisId<<1)|1]);
}
}
//Returns true if a face was placed
private boolean putFluidFaceIfCan(Mesher2D mesher, int face, int opposingFace, long self, long metadata, int selfBlockId, long facingState, int a, int b) {
long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState));
int selfFluidClientId = this.modelMan.getFluidClientStateId(this.modelMan.getModelId(selfBlockId));
long selfFluidMetadata = this.modelMan.getModelMetadataFromClientId(selfFluidClientId);
int facingFluidClientId = -1;
if (ModelManager.containsFluid(facingMetadata)) {
facingFluidClientId = this.modelMan.getFluidClientStateId(this.modelMan.getModelId(Mapper.getBlockId(facingState)));
}
//If both of the states are the same, then dont render the fluid face
if (selfFluidClientId == facingFluidClientId) {
return false;
}
if (facingFluidClientId != -1) {
if (this.world.getMapper().getBlockStateFromId(selfBlockId).getFluidState().getFluid() == this.world.getMapper().getBlockStateFromId(facingState).getFluidState().getFluid()) {
return false;
}
}
if (ModelManager.faceOccludes(facingMetadata, opposingFace)) {
return false;
}
//NOTE: if the model has a fluid state but is not a liquid need to see if the solid state had a face rendered and that face is occluding, if so, dont render the fluid state face
//TODO:FIXME FINISH
long otherFlags = 0;
otherFlags |= ModelManager.isTranslucent(selfFluidMetadata)?1L<<33:0;
otherFlags |= ModelManager.isDoubleSided(selfFluidMetadata)?1L<<34:0;
mesher.put(a, b, ((long)selfFluidClientId) | (((long) Mapper.getLightId(ModelManager.faceUsesSelfLighting(selfFluidMetadata, face)?self:facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelManager.isBiomeColoured(selfFluidMetadata)?1:0)) | otherFlags);
return true;
}
//Returns true if a face was placed
private boolean putFaceIfCan(Mesher2D mesher, int face, int opposingFace, long self, long metadata, int selfBlockId, long facingState, int a, int b) {
long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState));
@@ -222,36 +282,6 @@ public class RenderDataFactory {
}
//TODO: if the model has a fluid state, it should compute if a fluid face needs to be injected
// fluid face of type this.world.getMapper().getBlockStateFromId(self).getFluidState() and block type
// this.world.getMapper().getBlockStateFromId(self).getFluidState().getBlockState()
//If we are a fluid
if (ModelManager.containsFluid(metadata)) {
var selfBS = this.world.getMapper().getBlockStateFromId(self);
if (ModelManager.containsFluid(facingMetadata)) {//and the oposing face is also a fluid need to make a closer check
var faceBS = this.world.getMapper().getBlockStateFromId(facingState);
//If we are a fluid block that means our face is a fluid face, waterlogged blocks dont include fluid faces in the model data
if (selfBS.getBlock() instanceof FluidBlock) {
//If the fluid state of both blocks are the same we dont emit extra geometry
if (selfBS.getFluidState().getBlockState().equals(faceBS.getFluidState().getBlockState())) {
return false;
}
} else {//If we are not a fluid block, we might need to emit extra geometry (fluid faces) to the auxliery mesher
boolean shouldEmitFluidFace = !selfBS.getFluidState().getBlockState().equals(faceBS.getFluidState().getBlockState());
//TODO: THIS
int aa = 0;
}
} else if (!(selfBS.getBlock() instanceof FluidBlock)) {//If we are not a fluid block but we contain a fluid we might need to emit extra geometry
//Basicly need to get the fluid state and run putFaceIfCan using the fluid state as the self state and keep the same facing state
//TODO: THIS
int aa = 0;
}
}
int clientModelId = this.modelMan.getModelId(selfBlockId);
long otherFlags = 0;
otherFlags |= ModelManager.isTranslucent(metadata)?1L<<33:0;

View File

@@ -62,7 +62,7 @@ public class RenderGenerationService {
mesh = factory.generateMesh(section);
} catch (IdNotYetComputedException e) {
//We need to reinsert the build task into the queue
System.err.println("Render task failed to complete due to un-computed client id");
//System.err.println("Render task failed to complete due to un-computed client id");
synchronized (this.taskQueue) {
this.taskQueue.computeIfAbsent(section.key, key->{this.taskCounter.release(); return task;});
}

View File

@@ -10,6 +10,7 @@ public class Mesher2D {
private final long[] data;
private final BitSet setset;
private int[] quadCache;
private boolean isEmpty = true;
public Mesher2D(int sizeBits, int maxSize) {
this.size = sizeBits;
this.maxSize = maxSize;
@@ -27,6 +28,7 @@ public class Mesher2D {
}
public Mesher2D put(int x, int z, long data) {
this.isEmpty = false;
int idx = this.getIdx(x, z);
this.data[idx] = data;
this.setset.set(idx);
@@ -61,6 +63,10 @@ public class Mesher2D {
//Returns the number of compacted quads
public int process() {
if (this.isEmpty) {
return 0;
}
int[] quads = this.quadCache;
int idxCount = 0;
@@ -144,9 +150,12 @@ public class Mesher2D {
}
public void reset() {
if (!this.isEmpty) {
this.isEmpty = true;
this.setset.clear();
Arrays.fill(this.data, 0);
}
}
public long getDataFromQuad(int quad) {
return this.getData(getX(quad), getZ(quad));

View File

@@ -23,7 +23,7 @@ public class MixinClientChunkManager {
private void injectUnload(ChunkPos pos, CallbackInfo ci, int index, WorldChunk worldChunk) {
var core = ((IGetVoxelCore)(world.worldRenderer)).getVoxelCore();
if (core != null && VoxyConfig.CONFIG.ingestEnabled) {
core.enqueueIngest(worldChunk);
//core.enqueueIngest(worldChunk);
}
}
}

View File

@@ -0,0 +1,25 @@
package me.cortex.voxy.client.mixin.sodium;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.IGetVoxelCore;
import me.jellysquid.mods.sodium.client.render.chunk.RenderSectionManager;
import net.minecraft.client.world.ClientWorld;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = RenderSectionManager.class, remap = false)
public class MixinRenderSectionManager {
@Shadow @Final private ClientWorld world;
@Inject(method = "onChunkRemoved", at = @At("HEAD"))
private void injectIngest(int x, int z, CallbackInfo ci) {
var core = ((IGetVoxelCore)(world.worldRenderer)).getVoxelCore();
if (core != null && VoxyConfig.CONFIG.ingestEnabled) {
core.enqueueIngest(world.getChunk(x, z));
}
}
}

View File

@@ -10,7 +10,15 @@ import java.util.ArrayList;
import java.util.List;
public class TranslocatingStorageAdaptor extends DelegatingStorageAdaptor {
public record BoxTransform(int x1, int y1, int z1, int x2, int y2, int z2, int dx, int dy, int dz) {
public enum Mode {
BOX_ONLY,
PRIORITY_BOX,
PRIORITY_ORIGINAL
}
public record BoxTransform(int x1, int y1, int z1, int x2, int y2, int z2, int dx, int dy, int dz, Mode mode) {
public BoxTransform(int x1, int y1, int z1, int x2, int y2, int z2, int dx, int dy, int dz) {
this(x1, y1, z1, x2, y2, z2, dx, dy, dz, Mode.BOX_ONLY);
}
public long transformIfInBox(long pos) {
int lvl = WorldEngine.getLevel(pos);
int x = WorldEngine.getX(pos);
@@ -38,19 +46,29 @@ public class TranslocatingStorageAdaptor extends DelegatingStorageAdaptor {
this.transforms = transforms;
}
private long transformPosition(long pos) {
for (var transform : this.transforms) {
long tpos = transform.transformIfInBox(pos);
if (tpos != -1) {
return tpos;
}
}
return pos;
}
@Override
public ByteBuffer getSectionData(long key) {
return super.getSectionData(this.transformPosition(key));
for (var transform : this.transforms) {
long tpos = transform.transformIfInBox(key);
if (tpos != -1) {
if (transform.mode == Mode.BOX_ONLY || transform.mode == null) {
return super.getSectionData(tpos);
} else if (transform.mode == Mode.PRIORITY_BOX) {
var data = super.getSectionData(tpos);
if (data == null) {
return super.getSectionData(key);
}
} else if (transform.mode == Mode.PRIORITY_ORIGINAL) {
var data = super.getSectionData(key);
if (data == null) {
return super.getSectionData(tpos);
}
} else {
throw new IllegalStateException();
}
}
}
return super.getSectionData(key);
}
@Override

View File

@@ -14,5 +14,8 @@
],
"injectors": {
"defaultRequire": 1
}
},
"mixins": [
"sodium.MixinRenderSectionManager"
]
}