diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 3dbe6f0b0..768944eb4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -59,6 +59,8 @@ body: label: Mod Version description: The version of the mod you were using when the bug occured options: + - "0.6.10" + - "0.6.9" - "0.6.8.a" - "0.6.8" - "0.6.7" @@ -94,6 +96,7 @@ body: label: Minecraft Version description: The version of Minecraft you were using when the bug occured options: + - "1.20.1" - "1.19.2" - "1.18.2" - "1.18.1" diff --git a/README.md b/README.md index 95d7ba6ba..b42d833ec 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@
-Logo by @voxel_dani on Twitter -

Flywheel

-
A modern engine for modded Minecraft.
-Jenkins -Discord -Curseforge Downloads -
+ Logo by @voxel_dani on Twitter +

Flywheel

+
A modern engine for modded Minecraft.
+ Jenkins + License + Discord + Curseforge Downloads + Modrinth +

### About @@ -16,13 +18,13 @@ graphics programming. ### Instancing -Flywheel provides an alternate, unified path for entity and tile entity rendering that takes advantage of GPU +Flywheel provides an alternate, unified path for entity and block entity rendering that takes advantage of GPU instancing. In doing so, Flywheel gives the developer the flexibility to define their own vertex and instance formats, and write custom shaders to ingest that data. ### Shaders -To accomodate the developer and leave more in the hands of the engine, Flywheel provides a custom shader loading and +To accommodate the developer and leave more in the hands of the engine, Flywheel provides a custom shader loading and templating system to hide the details of the CPU/GPU interface. This system is a work in progress. There will be breaking changes, and I make no guarantees of backwards compatibility. @@ -46,14 +48,15 @@ repositories { } dependencies { - implementation fg.deobf("com.jozufozu.flywheel:Flywheel-Forge:${flywheel_version}") + implementation fg.deobf("com.jozufozu.flywheel:flywheel-forge-${flywheel_minecraft_version}:${flywheel_version}") } ``` -`${flywheel_version}` gets replaced by the version of Flywheel you want to use, eg. `1.18-0.3.0.3` +`${flywheel_version}` gets replaced by the version of Flywheel you want to use, eg. `0.6.9`. +`${flywheel_minecraft_version}` gets replaced by the version of Minecraft, eg. `1.18.2`. -For a list of available Flywheel versions, you can check [the maven](https://maven.tterrag.com/com/jozufozu/flywheel/Flywheel-Forge/). +For a list of available Flywheel versions, you can check [the maven](https://maven.tterrag.com/com/jozufozu/flywheel/). -If you aren't using mixed mappings (or just want to be safe), add the following properties to your run configurations: +If you aren't using Mojang mappings (or just want to be safe), add the following properties to your run configurations: ```groovy property 'mixin.env.remapRefMap', 'true' property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" diff --git a/build.gradle b/build.gradle index 935c07203..1e22b1359 100644 --- a/build.gradle +++ b/build.gradle @@ -52,6 +52,7 @@ minecraft { property 'flw.loadRenderDoc', 'true' arg '-mixin.config=flywheel.mixins.json' + arg '-mixin.config=flywheel.sodium.mixins.json' mods { flywheel { @@ -67,6 +68,7 @@ minecraft { property 'forge.logging.console.level', 'debug' arg '-mixin.config=flywheel.mixins.json' + arg '-mixin.config=flywheel.sodium.mixins.json' mods { flywheel { @@ -159,7 +161,7 @@ jar { 'Implementation-Version' : project.jar.archiveVersion, //'Implementation-Vendor': 'flywheel authors', 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), - 'MixinConfigs' : 'flywheel.mixins.json' + 'MixinConfigs' : 'flywheel.mixins.json,flywheel.sodium.mixins.json' ]) } } diff --git a/gradle.properties b/gradle.properties index 4ee0ef488..3fbe7df91 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs = -Xmx3G org.gradle.daemon = false # mod version info -mod_version = 0.6.9 +mod_version = 0.6.10 artifact_minecraft_version = 1.19.2 minecraft_version = 1.19.2 diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java index 6f8f77e3d..bc51354d3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java @@ -15,6 +15,7 @@ import com.jozufozu.flywheel.util.WorldAttached; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraftforge.api.distmarker.Dist; @@ -119,6 +120,21 @@ public class InstancedRenderDispatcher { .loadEntities(world); } + public static boolean tryAddBlockEntity(T blockEntity) { + Level level = blockEntity.getLevel(); + if (!Backend.canUseInstancing(level)) { + return false; + } + + if (!InstancedRenderRegistry.canInstance(blockEntity.getType())) { + return false; + } + + getBlockEntities(level).queueAdd(blockEntity); + + return InstancedRenderRegistry.shouldSkipRender(blockEntity); + } + public static void getDebugString(List debug) { debug.add(""); debug.add("Flywheel: " + Flywheel.getVersion()); diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java index 5b0027bb9..a43b31716 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java @@ -17,6 +17,7 @@ import it.unimi.dsi.fastutil.longs.LongSets; import it.unimi.dsi.fastutil.shorts.ShortList; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; +import net.minecraft.core.SectionPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ChunkPos; @@ -36,13 +37,11 @@ import net.minecraft.world.ticks.BlackholeTickAccess; import net.minecraft.world.ticks.TickContainerAccess; public class VirtualChunk extends ChunkAccess { + public final VirtualRenderWorld world; - final VirtualRenderWorld world; - boolean needsLight; - final int x; - final int z; + private final VirtualChunkSection[] sections; - private final LevelChunkSection[] sections; + private boolean needsLight; public VirtualChunk(VirtualRenderWorld world, int x, int z) { super(new ChunkPos(x, z), UpgradeData.EMPTY, world, world.registryAccess() @@ -50,34 +49,39 @@ public class VirtualChunk extends ChunkAccess { .orElseThrow(), 0L, null, null); this.world = world; - this.needsLight = true; - this.x = x; - this.z = z; int sectionCount = world.getSectionsCount(); - this.sections = new LevelChunkSection[sectionCount]; + this.sections = new VirtualChunkSection[sectionCount]; for (int i = 0; i < sectionCount; i++) { sections[i] = new VirtualChunkSection(this, i << 4); } + this.needsLight = true; + Mods.STARLIGHT.executeIfInstalled(() -> () -> { - ((ExtendedChunk)this).setBlockNibbles(StarLightEngine.getFilledEmptyLight(this)); - ((ExtendedChunk)this).setSkyNibbles(StarLightEngine.getFilledEmptyLight(this)); + ((ExtendedChunk) this).setBlockNibbles(StarLightEngine.getFilledEmptyLight(this)); + ((ExtendedChunk) this).setSkyNibbles(StarLightEngine.getFilledEmptyLight(this)); }); } @Override - public Stream getLights() { - return world.blocksAdded.entrySet() - .stream() - .filter(it -> { - BlockPos blockPos = it.getKey(); - boolean chunkContains = blockPos.getX() >> 4 == x && blockPos.getZ() >> 4 == z; - return chunkContains && it.getValue() - .getLightEmission(world, blockPos) != 0; - }) - .map(Map.Entry::getKey); + @Nullable + public BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving) { + return null; + } + + @Override + public void setBlockEntity(BlockEntity blockEntity) { + } + + @Override + public void addEntity(Entity entity) { + } + + @Override + public Set getBlockEntitiesPos() { + return Collections.emptySet(); } @Override @@ -85,113 +89,25 @@ public class VirtualChunk extends ChunkAccess { return sections; } - @Override - public ChunkStatus getStatus() { - return ChunkStatus.LIGHT; - } - - @Nullable - @Override - public BlockState setBlockState(BlockPos p_177436_1_, BlockState p_177436_2_, boolean p_177436_3_) { - return null; - } - - @Override - public void setBlockEntity(BlockEntity p_177426_2_) {} - - @Override - public void addEntity(Entity p_76612_1_) {} - - @Override - public Set getBlockEntitiesPos() { - return null; - } - @Override public Collection> getHeightmaps() { + return Collections.emptySet(); + } + + @Override + public void setHeightmap(Heightmap.Types type, long[] data) { + } + + @Override + public Heightmap getOrCreateHeightmapUnprimed(Heightmap.Types type) { return null; } @Override - public void setHeightmap(Heightmap.Types p_201607_1_, long[] p_201607_2_) {} - - @Override - public Heightmap getOrCreateHeightmapUnprimed(Heightmap.Types p_217303_1_) { - return null; - } - - @Override - public int getHeight(Heightmap.Types p_201576_1_, int p_201576_2_, int p_201576_3_) { + public int getHeight(Heightmap.Types type, int x, int z) { return 0; } - @Override - public void setUnsaved(boolean p_177427_1_) {} - - @Override - public boolean isUnsaved() { - return false; - } - - @Override - public void removeBlockEntity(BlockPos p_177425_1_) {} - - @Override - public ShortList[] getPostProcessing() { - return new ShortList[0]; - } - - @Nullable - @Override - public CompoundTag getBlockEntityNbt(BlockPos p_201579_1_) { - return null; - } - - @Nullable - @Override - public CompoundTag getBlockEntityNbtForSaving(BlockPos p_223134_1_) { - return null; - } - - @Override - public UpgradeData getUpgradeData() { - return null; - } - - @Override - public void setInhabitedTime(long p_177415_1_) {} - - @Override - public long getInhabitedTime() { - return 0; - } - - @Override - public boolean isLightCorrect() { - return needsLight; - } - - @Override - public void setLightCorrect(boolean needsLight) { - this.needsLight = needsLight; - } - - @Nullable - @Override - public BlockEntity getBlockEntity(BlockPos pos) { - return null; - } - - @Override - public BlockState getBlockState(BlockPos pos) { - return world.getBlockState(pos); - } - - @Override - public FluidState getFluidState(BlockPos p_204610_1_) { - return null; - } - @Override @Nullable public StructureStart getStartForStructure(Structure structure) { @@ -199,7 +115,7 @@ public class VirtualChunk extends ChunkAccess { } @Override - public void setStartForStructure(Structure structure, StructureStart start) { + public void setStartForStructure(Structure structure, StructureStart structureStart) { } @Override @@ -212,7 +128,7 @@ public class VirtualChunk extends ChunkAccess { } @Override - public LongSet getReferencesForStructure(Structure structure) { + public LongSet getReferencesForStructure(Structure pStructure) { return LongSets.emptySet(); } @@ -226,17 +142,60 @@ public class VirtualChunk extends ChunkAccess { } @Override - public void setAllReferences(Map structureReferences) { + public void setAllReferences(Map structureReferencesMap) { } @Override - public int getHeight() { - return world.getHeight(); + public void setUnsaved(boolean unsaved) { } @Override - public int getMinBuildHeight() { - return world.getMinBuildHeight(); + public boolean isUnsaved() { + return false; + } + + @Override + public ChunkStatus getStatus() { + return ChunkStatus.LIGHT; + } + + @Override + public void removeBlockEntity(BlockPos pos) { + } + + @Override + public ShortList[] getPostProcessing() { + return new ShortList[0]; + } + + @Override + @Nullable + public CompoundTag getBlockEntityNbt(BlockPos pos) { + return null; + } + + @Override + @Nullable + public CompoundTag getBlockEntityNbtForSaving(BlockPos pos) { + return null; + } + + @Override + public Stream getLights() { + return world.blockStates.entrySet() + .stream() + .filter(it -> { + BlockPos blockPos = it.getKey(); + boolean chunkContains = SectionPos.blockToSectionCoord(blockPos.getX()) == chunkPos.x && SectionPos.blockToSectionCoord(blockPos.getZ()) == chunkPos.z; + return chunkContains && it.getValue() + .getLightEmission(world, blockPos) != 0; + }) + .map(Map.Entry::getKey); + } + + @Override + public TickContainerAccess getBlockTicks() { + return BlackholeTickAccess.emptyContainer(); } @Override @@ -246,12 +205,41 @@ public class VirtualChunk extends ChunkAccess { @Override public TicksToSave getTicksForSerialization() { - return null; + throw new UnsupportedOperationException(); } @Override - public TickContainerAccess getBlockTicks() { - return BlackholeTickAccess.emptyContainer(); + public long getInhabitedTime() { + return 0; } + @Override + public void setInhabitedTime(long amount) { + } + + @Override + public boolean isLightCorrect() { + return needsLight; + } + + @Override + public void setLightCorrect(boolean lightCorrect) { + this.needsLight = lightCorrect; + } + + @Override + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return world.getBlockEntity(pos); + } + + @Override + public BlockState getBlockState(BlockPos pos) { + return world.getBlockState(pos); + } + + @Override + public FluidState getFluidState(BlockPos pos) { + return world.getFluidState(pos); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSection.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSection.java index a3d4f1952..01414b065 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSection.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSection.java @@ -3,10 +3,10 @@ package com.jozufozu.flywheel.core.virtual; import net.minecraft.core.Registry; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.material.FluidState; public class VirtualChunkSection extends LevelChunkSection { - - public VirtualChunk owner; + public final VirtualChunk owner; public final int xStart; public final int yStart; @@ -32,8 +32,12 @@ public class VirtualChunkSection extends LevelChunkSection { } @Override - public BlockState setBlockState(int p_177484_1_, int p_177484_2_, int p_177484_3_, BlockState p_177484_4_, - boolean p_177484_5_) { - throw new IllegalStateException("Chunk sections should not be mutated in a fake world."); + public FluidState getFluidState(int x, int y, int z) { + return getBlockState(x, y, z).getFluidState(); + } + + @Override + public BlockState setBlockState(int x, int y, int z, BlockState state, boolean useLocks) { + throw new UnsupportedOperationException("Chunk sections cannot be mutated in a fake world."); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSource.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSource.java index 7fa73d38d..ff91f12da 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSource.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSource.java @@ -1,62 +1,65 @@ package com.jozufozu.flywheel.core.virtual; -import java.util.HashMap; import java.util.function.BooleanSupplier; -import net.minecraft.world.level.BlockGetter; +import org.jetbrains.annotations.Nullable; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.lighting.LevelLightEngine; public class VirtualChunkSource extends ChunkSource { private final VirtualRenderWorld world; - - public final HashMap chunks = new HashMap<>(); + private final Long2ObjectMap chunks = new Long2ObjectOpenHashMap<>(); public VirtualChunkSource(VirtualRenderWorld world) { this.world = world; } - @Override - public BlockGetter getChunkForLighting(int x, int z) { - return getChunk(x, z); - } - @Override public Level getLevel() { return world; } - @Override - public ChunkAccess getChunk(int x, int z, ChunkStatus status, boolean p_212849_4_) { - return getChunk(x, z); - } - public ChunkAccess getChunk(int x, int z) { long pos = ChunkPos.asLong(x, z); - return chunks.computeIfAbsent(pos, $ -> new VirtualChunk(world, x, z)); } + @Override + @Nullable + public LevelChunk getChunk(int x, int z, boolean load) { + return null; + } + + @Override + @Nullable + public ChunkAccess getChunk(int x, int z, ChunkStatus status, boolean load) { + return getChunk(x, z); + } + + @Override + public void tick(BooleanSupplier hasTimeLeft, boolean tickChunks) { + } + @Override public String gatherStats() { - return "WrappedChunkProvider"; - } - - @Override - public LevelLightEngine getLightEngine() { - return world.getLightEngine(); - } - - @Override - public void tick(BooleanSupplier p_202162_, boolean p_202163_) { + return "VirtualChunkSource"; } @Override public int getLoadedChunksCount() { return 0; } + + @Override + public LevelLightEngine getLightEngine() { + return world.getLightEngine(); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualEmptyBlockGetter.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualEmptyBlockGetter.java index ebfbe9389..8785d3ada 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualEmptyBlockGetter.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualEmptyBlockGetter.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.core.virtual; +import org.jetbrains.annotations.Nullable; + import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -32,6 +34,7 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter { } @Override + @Nullable default BlockEntity getBlockEntity(BlockPos pos) { return null; } @@ -99,7 +102,7 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter { } @Override - public void onBlockEmissionIncrease(BlockPos pos, int p_164456_) { + public void onBlockEmissionIncrease(BlockPos pos, int emissionLevel) { } @Override @@ -108,16 +111,16 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter { } @Override - public int runUpdates(int p_164449_, boolean p_164450_, boolean p_164451_) { - return p_164449_; + public int runUpdates(int pos, boolean isQueueEmpty, boolean updateBlockLight) { + return pos; } @Override - public void updateSectionStatus(SectionPos pos, boolean p_75838_) { + public void updateSectionStatus(SectionPos pos, boolean isQueueEmpty) { } @Override - public void enableLightSources(ChunkPos pos, boolean p_164453_) { + public void enableLightSources(ChunkPos pos, boolean isQueueEmpty) { } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualLevelEntityGetter.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualLevelEntityGetter.java index 2edd3fdf3..67ce31b48 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualLevelEntityGetter.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualLevelEntityGetter.java @@ -10,14 +10,13 @@ import net.minecraft.world.level.entity.LevelEntityGetter; import net.minecraft.world.phys.AABB; public class VirtualLevelEntityGetter implements LevelEntityGetter { - @Override - public T get(int p_156931_) { + public T get(int id) { return null; } @Override - public T get(UUID pUuid) { + public T get(UUID uuid) { return null; } @@ -27,15 +26,14 @@ public class VirtualLevelEntityGetter implements LevelEn } @Override - public void get(EntityTypeTest p_156935_, Consumer p_156936_) { + public void get(EntityTypeTest test, Consumer consumer) { } @Override - public void get(AABB p_156937_, Consumer p_156938_) { + public void get(AABB boundingBox, Consumer consumer) { } @Override - public void get(EntityTypeTest p_156932_, AABB p_156933_, Consumer p_156934_) { + public void get(EntityTypeTest test, AABB bounds, Consumer consumer) { } - } diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java index af5e7da6e..d8396f313 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java @@ -3,16 +3,15 @@ package com.jozufozu.flywheel.core.virtual; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.function.Predicate; import javax.annotation.Nullable; import com.jozufozu.flywheel.api.FlywheelWorld; +import it.unimi.dsi.fastutil.objects.Object2ShortMap; +import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; @@ -33,12 +32,15 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.entity.LevelEntityGetter; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gameevent.GameEvent.Context; import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.saveddata.maps.MapItemSavedData; import net.minecraft.world.level.storage.WritableLevelData; import net.minecraft.world.phys.Vec3; @@ -46,37 +48,40 @@ import net.minecraft.world.scores.Scoreboard; import net.minecraft.world.ticks.LevelTickAccess; public class VirtualRenderWorld extends Level implements FlywheelWorld { - public final Map blocksAdded = new HashMap<>(); - public final Map besAdded = new HashMap<>(); - public final Set spannedSections = new HashSet<>(); - private final BlockPos.MutableBlockPos scratch = new BlockPos.MutableBlockPos(); - protected final Level level; - protected final LevelLightEngine lighter; - protected final VirtualChunkSource chunkSource; - protected final LevelEntityGetter entityGetter = new VirtualLevelEntityGetter<>(); - - protected final int height; protected final int minBuildHeight; + protected final int height; protected final Vec3i biomeOffset; + protected final VirtualChunkSource chunkSource; + protected final LevelLightEngine lightEngine; + + protected final Map blockStates = new HashMap<>(); + protected final Map blockEntities = new HashMap<>(); + protected final Object2ShortMap nonEmptyBlockCounts = new Object2ShortOpenHashMap<>(); + + protected final LevelEntityGetter entityGetter = new VirtualLevelEntityGetter<>(); + + protected final BlockPos.MutableBlockPos scratchPos = new BlockPos.MutableBlockPos(); + public VirtualRenderWorld(Level level) { - this(level, Vec3i.ZERO, level.getHeight(), level.getMinBuildHeight()); + this(level, Vec3i.ZERO); } public VirtualRenderWorld(Level level, Vec3i biomeOffset) { - this(level, biomeOffset, level.getHeight(), level.getMinBuildHeight()); + this(level, level.getMinBuildHeight(), level.getHeight(), biomeOffset); } - public VirtualRenderWorld(Level level, Vec3i biomeOffset, int height, int minBuildHeight) { - super((WritableLevelData) level.getLevelData(), level.dimension(), level.dimensionTypeRegistration(), level::getProfiler, + public VirtualRenderWorld(Level level, int minBuildHeight, int height, Vec3i biomeOffset) { + super((WritableLevelData) level.getLevelData(), level.dimension(), level.dimensionTypeRegistration(), level.getProfilerSupplier(), true, false, 0, 0); - this.biomeOffset = biomeOffset; this.level = level; - this.height = nextMultipleOf16(height); this.minBuildHeight = nextMultipleOf16(minBuildHeight); + this.height = nextMultipleOf16(height); + this.biomeOffset = biomeOffset; + this.chunkSource = new VirtualChunkSource(this); - this.lighter = new LevelLightEngine(chunkSource, true, false); + this.lightEngine = new LevelLightEngine(chunkSource, true, false); } /** @@ -91,67 +96,145 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { } } - /** - * Run this after you're done using setBlock(). - */ - public void runLightingEngine() { - for (Map.Entry entry : blocksAdded.entrySet()) { - BlockPos pos = entry.getKey(); - BlockState state = entry.getValue(); - int light = state.getLightEmission(this, pos); - if (light > 0) { - lighter.onBlockEmissionIncrease(pos, light); - } - } + public void clear() { + blockStates.clear(); + blockEntities.clear(); - lighter.runUpdates(Integer.MAX_VALUE, false, false); + nonEmptyBlockCounts.forEach((sectionPos, nonEmptyBlockCount) -> { + if (nonEmptyBlockCount > 0) { + lightEngine.updateSectionStatus(sectionPos, true); + } + }); + + nonEmptyBlockCounts.clear(); + + runLightEngine(); } public void setBlockEntities(Collection blockEntities) { - besAdded.clear(); - blockEntities.forEach(be -> besAdded.put(be.getBlockPos(), be)); + this.blockEntities.clear(); + blockEntities.forEach(this::setBlockEntity); } - public void clear() { - blocksAdded.clear(); + /** + * Run this after you're done using setBlock(). + */ + public void runLightEngine() { + blockStates.forEach((pos, state) -> { + int light = state.getLightEmission(this, pos); + if (light > 0) { + lightEngine.onBlockEmissionIncrease(pos, light); + } + }); + + lightEngine.runUpdates(Integer.MAX_VALUE, false, false); } // MEANINGFUL OVERRIDES @Override - public boolean setBlock(BlockPos pos, BlockState newState, int flags) { - blocksAdded.put(pos, newState); + public LevelChunk getChunk(int x, int z) { + throw new UnsupportedOperationException(); + } + + public ChunkAccess actuallyGetChunk(int x, int z) { + return getChunk(x, z, ChunkStatus.FULL); + } + + @Override + public ChunkAccess getChunk(BlockPos pos) { + return actuallyGetChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); + } + + @Override + public boolean setBlock(BlockPos pos, BlockState newState, int flags, int recursionLeft) { + if (isOutsideBuildHeight(pos)) { + return false; + } + + pos = pos.immutable(); + + BlockState oldState = getBlockState(pos); + if (oldState == newState) { + return false; + } + + blockStates.put(pos, newState); SectionPos sectionPos = SectionPos.of(pos); - if (spannedSections.add(sectionPos)) { - lighter.updateSectionStatus(sectionPos, false); + short nonEmptyBlockCount = nonEmptyBlockCounts.getShort(sectionPos); + boolean prevEmpty = nonEmptyBlockCount == 0; + if (!oldState.isAir()) { + --nonEmptyBlockCount; + } + if (!newState.isAir()) { + ++nonEmptyBlockCount; + } + nonEmptyBlockCounts.put(sectionPos, nonEmptyBlockCount); + boolean nowEmpty = nonEmptyBlockCount == 0; + + if (prevEmpty != nowEmpty) { + lightEngine.updateSectionStatus(sectionPos, nowEmpty); } if ((flags & Block.UPDATE_SUPPRESS_LIGHT) == 0) { - lighter.checkBlock(pos); + lightEngine.checkBlock(pos); } return true; } - @Override - public int getHeight() { - return height; - } - - @Override - public int getMinBuildHeight() { - return minBuildHeight; - } - - @Override - public ChunkSource getChunkSource() { - return chunkSource; - } - @Override public LevelLightEngine getLightEngine() { - return lighter; + return lightEngine; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + if (isOutsideBuildHeight(pos)) { + return Blocks.VOID_AIR.defaultBlockState(); + } + BlockState state = blockStates.get(pos); + if (state != null) { + return state; + } + return Blocks.AIR.defaultBlockState(); + } + + public BlockState getBlockState(int x, int y, int z) { + return getBlockState(scratchPos.set(x, y, z)); + } + + @Override + public FluidState getFluidState(BlockPos pos) { + if (isOutsideBuildHeight(pos)) { + return Fluids.EMPTY.defaultFluidState(); + } + return getBlockState(pos).getFluidState(); + } + + @Override + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + if (!isOutsideBuildHeight(pos)) { + return blockEntities.get(pos); + } + return null; + } + + @Override + public void setBlockEntity(BlockEntity blockEntity) { + BlockPos pos = blockEntity.getBlockPos(); + if (!isOutsideBuildHeight(pos)) { + blockEntities.put(pos, blockEntity); + } + } + + @Override + public void removeBlockEntity(BlockPos pos) { + if (!isOutsideBuildHeight(pos)) { + blockEntities.remove(pos); + } } @Override @@ -160,52 +243,39 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { } @Override - public BlockState getBlockState(@Nullable BlockPos pos) { - BlockState state = blocksAdded.get(pos); - if (state != null) - return state; - return Blocks.AIR.defaultBlockState(); + public ChunkSource getChunkSource() { + return chunkSource; } @Override - public boolean setBlockAndUpdate(BlockPos pos, BlockState state) { - return setBlock(pos, state, 0); + public int getMinBuildHeight() { + return minBuildHeight; } @Override - @Nullable - public BlockEntity getBlockEntity(BlockPos pos) { - return besAdded.get(pos); - } - - @Override - public boolean isStateAtPosition(BlockPos pos, Predicate condition) { - return condition.test(getBlockState(pos)); - } - - public BlockState getBlockState(int x, int y, int z) { - return getBlockState(scratch.set(x, y, z)); + public int getHeight() { + return height; } // BIOME OFFSET @Override - public Holder getBiome(BlockPos pPos) { - return super.getBiome(pPos.offset(biomeOffset)); + public Holder getBiome(BlockPos pos) { + return super.getBiome(pos.offset(biomeOffset)); } @Override - public Holder getUncachedNoiseBiome(int pX, int pY, int pZ) { + public Holder getNoiseBiome(int x, int y, int z) { // Control flow should never reach this method, // so we add biomeOffset in case some other mod calls this directly. - return level.getUncachedNoiseBiome(pX + biomeOffset.getX(), pY + biomeOffset.getY(), pZ + biomeOffset.getZ()); + return level.getNoiseBiome(x + biomeOffset.getX(), y + biomeOffset.getY(), z + biomeOffset.getZ()); } @Override - public Holder getNoiseBiome(int pX, int pY, int pZ) { + public Holder getUncachedNoiseBiome(int x, int y, int z) { // Control flow should never reach this method, // so we add biomeOffset in case some other mod calls this directly. - return level.getNoiseBiome(pX + biomeOffset.getX(), pY + biomeOffset.getY(), pZ + biomeOffset.getZ()); + return level.getUncachedNoiseBiome(x + biomeOffset.getX(), y + biomeOffset.getY(), z + biomeOffset.getZ()); } // RENDERING CONSTANTS @@ -216,22 +286,27 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { } @Override - public float getShade(Direction p_230487_1_, boolean p_230487_2_) { + public float getShade(Direction direction, boolean shade) { return 1f; } - // THIN WRAPPERS AHEAD + // THIN WRAPPERS + + @Override + public Scoreboard getScoreboard() { + return level.getScoreboard(); + } + + @Override + public RecipeManager getRecipeManager() { + return level.getRecipeManager(); + } @Override public BiomeManager getBiomeManager() { return level.getBiomeManager(); } - @Override - public RegistryAccess registryAccess() { - return level.registryAccess(); - } - @Override public LevelTickAccess getBlockTicks() { return level.getBlockTicks(); @@ -243,21 +318,46 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { } @Override - public RecipeManager getRecipeManager() { - return level.getRecipeManager(); + public RegistryAccess registryAccess() { + return level.registryAccess(); + } + + // ADDITIONAL OVERRRIDES + + @Override + public void updateNeighbourForOutputSignal(BlockPos pos, Block block) { } @Override - public int getFreeMapId() { - return level.getFreeMapId(); + public boolean isLoaded(BlockPos pos) { + return true; } @Override - public Scoreboard getScoreboard() { - return level.getScoreboard(); + public boolean isAreaLoaded(BlockPos center, int range) { + return true; } - // UNIMPORTANT CONSTANTS + // UNIMPORTANT IMPLEMENTATIONS + + @Override + public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) { + } + + @Override + public void playSeededSound(Player player, double x, double y, double z, SoundEvent soundEvent, + SoundSource soundSource, float volume, float pitch, long seed) { + } + + @Override + public void playSeededSound(Player player, Entity entity, SoundEvent soundEvent, SoundSource soundSource, + float volume, float pitch, long seed) { + } + + @Override + public String gatherChunkSourceStats() { + return ""; + } @Override @Nullable @@ -272,13 +372,24 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { } @Override - public boolean isLoaded(BlockPos pos) { - return true; + public void setMapData(String mapId, MapItemSavedData data) { } @Override - public boolean isAreaLoaded(BlockPos center, int range) { - return true; + public int getFreeMapId() { + return 0; + } + + @Override + public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) { + } + + @Override + public void levelEvent(@Nullable Player player, int type, BlockPos pos, int data) { + } + + @Override + public void gameEvent(GameEvent event, Vec3 position, Context context) { } @Override @@ -286,50 +397,6 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { return Collections.emptyList(); } - @Override - public String gatherChunkSourceStats() { - return ""; - } - - // NOOP - - @Override - public void levelEvent(@Nullable Player player, int type, BlockPos pos, int data) {} - - @Override - public void gameEvent(GameEvent p_220404_, Vec3 p_220405_, Context p_220406_) {} - - @Override - public void playSeededSound(Player p_220363_, double p_220364_, double p_220365_, double p_220366_, - SoundEvent p_220367_, SoundSource p_220368_, float p_220369_, float p_220370_, long p_220371_) {} - - @Override - public void playSeededSound(Player p_220372_, Entity p_220373_, SoundEvent p_220374_, SoundSource p_220375_, - float p_220376_, float p_220377_, long p_220378_) {} - - @Override - public void playSound(@Nullable Player player, double x, double y, double z, SoundEvent soundIn, - SoundSource category, float volume, float pitch) {} - - @Override - public void playSound(@Nullable Player p_217384_1_, Entity p_217384_2_, SoundEvent p_217384_3_, - SoundSource p_217384_4_, float p_217384_5_, float p_217384_6_) {} - - @Override - public void setMapData(String pMapId, MapItemSavedData pData) {} - - @Override - public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) {} - - @Override - public void updateNeighbourForOutputSignal(BlockPos p_175666_1_, Block p_175666_2_) {} - - @Override - public void gameEvent(@Nullable Entity pEntity, GameEvent pEvent, BlockPos pPos) {} - - @Override - public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) {} - // Override Starlight's ExtendedWorld interface methods: public LevelChunk getChunkAtImmediately(final int chunkX, final int chunkZ) { diff --git a/src/main/java/com/jozufozu/flywheel/mixin/FixFabulousDepthMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/FixFabulousDepthMixin.java deleted file mode 100644 index 9c0978197..000000000 --- a/src/main/java/com/jozufozu/flywheel/mixin/FixFabulousDepthMixin.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.jozufozu.flywheel.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.math.Matrix4f; - -import net.minecraft.client.Camera; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.client.renderer.LevelRenderer; -import net.minecraft.client.renderer.LightTexture; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -@OnlyIn(Dist.CLIENT) -@Mixin(LevelRenderer.class) -public class FixFabulousDepthMixin { - - @Inject(method = "renderLevel", at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/client/renderer/PostChain;process(F)V")) - private void disableTransparencyShaderDepth(PoseStack p_228426_1_, float p_228426_2_, long p_228426_3_, boolean p_228426_5_, Camera p_228426_6_, GameRenderer p_228426_7_, LightTexture p_228426_8_, Matrix4f p_228426_9_, CallbackInfo ci) { - GlStateManager._depthMask(false); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java index f4895c7f8..7f29eef6d 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java @@ -8,7 +8,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent; @@ -24,8 +23,6 @@ import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.RenderBuffers; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.culling.Frustum; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.MinecraftForge; @@ -33,7 +30,6 @@ import net.minecraftforge.common.MinecraftForge; @OnlyIn(Dist.CLIENT) @Mixin(value = LevelRenderer.class, priority = 1001) // Higher priority to go after sodium public class LevelRendererMixin { - @Shadow private ClientLevel level; @@ -63,17 +59,4 @@ public class LevelRendererMixin { private void renderBlockBreaking(PoseStack stack, float p_228426_2_, long p_228426_3_, boolean p_228426_5_, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f p_228426_9_, CallbackInfo ci) { CrumblingRenderer.render(level, camera, stack); } - - // Instancing - - /** - * This gets called when a block is marked for rerender by vanilla. - */ - @Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V") - private void checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) { - if (Backend.isOn()) { - InstancedRenderDispatcher.getBlockEntities(level) - .update(level.getBlockEntity(pos)); - } - } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/PausedPartialTickAccessor.java b/src/main/java/com/jozufozu/flywheel/mixin/PausedPartialTickAccessor.java index ab5e5aa18..8e196640b 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/PausedPartialTickAccessor.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/PausedPartialTickAccessor.java @@ -7,7 +7,6 @@ import net.minecraft.client.Minecraft; @Mixin(Minecraft.class) public interface PausedPartialTickAccessor { - @Accessor("pausePartialTick") - float flywheel$getPartialTicksPaused(); + float flywheel$getPausePartialTick(); } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/fix/FixFabulousDepthMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/fix/FixFabulousDepthMixin.java new file mode 100644 index 000000000..a119b1e09 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/fix/FixFabulousDepthMixin.java @@ -0,0 +1,18 @@ +package com.jozufozu.flywheel.mixin.fix; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.mojang.blaze3d.platform.GlStateManager; + +import net.minecraft.client.renderer.LevelRenderer; + +@Mixin(LevelRenderer.class) +public class FixFabulousDepthMixin { + @Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/PostChain;process(F)V", ordinal = 1)) + private void flywheel$disableTransparencyShaderDepth(CallbackInfo ci) { + GlStateManager._depthMask(false); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/fix/FixNormalScalingMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/fix/FixNormalScalingMixin.java new file mode 100644 index 000000000..144d6efa9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/fix/FixNormalScalingMixin.java @@ -0,0 +1,36 @@ +package com.jozufozu.flywheel.mixin.fix; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.mojang.blaze3d.vertex.PoseStack; + +@Mixin(PoseStack.class) +public class FixNormalScalingMixin { + /** + * Minecraft negates the normal matrix if all scales are equal and negative, but + * does not return afterward. This allows the rest of the method's logic to be + * applied, which negates the matrix again, resulting in the matrix being the + * same as in the beginning. + */ + @Inject(method = "scale(FFF)V", at = @At(value = "INVOKE", target = "Lcom/mojang/math/Matrix3f;mul(F)V", shift = Shift.AFTER), cancellable = true) + private void flywheel$returnAfterNegate(float x, float y, float z, CallbackInfo ci) { + ci.cancel(); + } + + /** + * Minecraft takes the inverse cube root of the product of all scales to provide a + * rough estimate for normalization so that it does not need to be done later. It + * does not make sense for this "normalization factor" to be negative though, as + * that would invert all normals. Additionally, Minecraft's fastInvCubeRoot method + * does not work for negative numbers. + */ + @ModifyArg(method = "scale(FFF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;fastInvCubeRoot(F)F")) + private float flywheel$absInvCbrtInput(float input) { + return Math.abs(input); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRebuildHooksMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRebuildHooksMixin.java index 599c7fc9a..775caab6f 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRebuildHooksMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRebuildHooksMixin.java @@ -1,82 +1,21 @@ package com.jozufozu.flywheel.mixin.instancemanage; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Coerce; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; -import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; -import com.jozufozu.flywheel.util.RenderChunkExtension; -import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; -import net.minecraft.client.renderer.chunk.RenderChunkRegion; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -@OnlyIn(Dist.CLIENT) @Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$RenderChunk$RebuildTask") -public abstract class ChunkRebuildHooksMixin { - @Unique - private Level flywheel$level; - - @Inject(method = "(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;Lnet/minecraft/world/level/ChunkPos;DLnet/minecraft/client/renderer/chunk/RenderChunkRegion;Z)V", at = @At("RETURN")) - private void setLevel(ChunkRenderDispatcher.RenderChunk this$1, ChunkPos pos, double p_194427_, RenderChunkRegion region, boolean p_194429_, CallbackInfo ci) { - flywheel$level = ((RenderChunkExtension) this$1).flywheel$getLevel(); - } - - @Redirect(method = "doTask", at = @At(value = "INVOKE", target = "Ljava/util/List;addAll(Ljava/util/Collection;)Z")) - private boolean addAndFilterBEs(List self, Collection es) { - if (!Backend.canUseInstancing(flywheel$level)) { - return self.addAll(es); +public class ChunkRebuildHooksMixin { + @Inject(method = "handleBlockEntity(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk$RebuildTask$CompileResults;Lnet/minecraft/world/level/block/entity/BlockEntity;)V", at = @At("HEAD"), cancellable = true) + private void flywheel$tryAddBlockEntity(@Coerce Object compileResults, BlockEntity blockEntity, CallbackInfo ci) { + if (InstancedRenderDispatcher.tryAddBlockEntity(blockEntity)) { + ci.cancel(); } - - boolean added = false; - var instanced = new ArrayList(); - for (E be : es) { - if (InstancedRenderRegistry.canInstance(be.getType())) { - instanced.add(be); - } - - if (!InstancedRenderRegistry.shouldSkipRender(be)) { - self.add(be); - added = true; - } - } - InstancedRenderDispatcher.getBlockEntities(flywheel$level).queueAddAll(instanced); - return added; - } - - @Redirect(method = "doTask", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;updateGlobalBlockEntities(Ljava/util/Collection;)V")) - private void addAndFilterBEs(ChunkRenderDispatcher.RenderChunk self, Collection bes) { - if (!Backend.canUseInstancing(flywheel$level)) { - ((RenderChunkAccessor) self).flywheel$updateGlobalBlockEntities(bes); - return; - } - - var global = new ArrayList(); - var instanced = new ArrayList(); - for (BlockEntity be : bes) { - if (InstancedRenderRegistry.canInstance(be.getType())) { - instanced.add(be); - } - - if (!InstancedRenderRegistry.shouldSkipRender(be)) { - global.add(be); - } - } - - InstancedRenderDispatcher.getBlockEntities(flywheel$level).queueAddAll(instanced); - ((RenderChunkAccessor) self).flywheel$updateGlobalBlockEntities(global); } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRenderDispatcherAccessor.java b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRenderDispatcherAccessor.java deleted file mode 100644 index 7e8e0de6d..000000000 --- a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRenderDispatcherAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.jozufozu.flywheel.mixin.instancemanage; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; - -@Mixin(ChunkRenderDispatcher.class) -public interface ChunkRenderDispatcherAccessor { - - @Accessor - ClientLevel getLevel(); -} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/InstanceUpdateMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/InstanceUpdateMixin.java new file mode 100644 index 000000000..88136cd63 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/InstanceUpdateMixin.java @@ -0,0 +1,40 @@ +package com.jozufozu.flywheel.mixin.instancemanage; + +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; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +@Mixin(LevelRenderer.class) +public class InstanceUpdateMixin { + @Shadow + private ClientLevel level; + + /** + * This gets called when a block is marked for rerender by vanilla. + */ + @Inject(method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V", at = @At("TAIL")) + private void flywheel$checkUpdate(BlockPos pos, BlockState oldState, BlockState newState, CallbackInfo ci) { + if (!Backend.isOn()) { + return; + } + + BlockEntity blockEntity = level.getBlockEntity(pos); + if (blockEntity == null) { + return; + } + + InstancedRenderDispatcher.getBlockEntities(level) + .queueUpdate(blockEntity); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/RenderChunkAccessor.java b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/RenderChunkAccessor.java deleted file mode 100644 index 9bd5a2d3a..000000000 --- a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/RenderChunkAccessor.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jozufozu.flywheel.mixin.instancemanage; - -import java.util.Collection; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; -import net.minecraft.world.level.block.entity.BlockEntity; - -/** - * For use in {@link ChunkRebuildHooksMixin#addAndFilterBEs(ChunkRenderDispatcher.RenderChunk, Collection)} - */ -@Mixin(ChunkRenderDispatcher.RenderChunk.class) -public interface RenderChunkAccessor { - - @Invoker("updateGlobalBlockEntities") - void flywheel$updateGlobalBlockEntities(Collection blockEntities); -} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/RenderChunkMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/RenderChunkMixin.java deleted file mode 100644 index 3c42d337c..000000000 --- a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/RenderChunkMixin.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.jozufozu.flywheel.mixin.instancemanage; - -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; - -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; - -@Mixin(ChunkRenderDispatcher.RenderChunk.class) -public class RenderChunkMixin implements com.jozufozu.flywheel.util.RenderChunkExtension { - - @Shadow(aliases = "this$0") // Optifine does not use the obfuscated name so the mapped name must be included as an alias - @Final - private ChunkRenderDispatcher this$0; - - @Override - public ClientLevel flywheel$getLevel() { - return ((ChunkRenderDispatcherAccessor) this$0).getLevel(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/SodiumChunkRenderDataMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/SodiumChunkRenderDataMixin.java deleted file mode 100644 index d2ec94a62..000000000 --- a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/SodiumChunkRenderDataMixin.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.jozufozu.flywheel.mixin.instancemanage; - -import java.util.ArrayList; -import java.util.List; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; -import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; - -import me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderData; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -@OnlyIn(Dist.CLIENT) -@Mixin(targets = "me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderData$Builder", remap = false) -public class SodiumChunkRenderDataMixin { - - @Unique - private List flywheel$blockEntities; - @Unique - private Level flywheel$level; - - @Inject(method = "addBlockEntity", at = @At("HEAD"), cancellable = true, require = 0) - private void flywheel$onAddBlockEntity(BlockEntity be, boolean cull, CallbackInfo ci) { - if (flywheel$level == null) { - flywheel$level = be.getLevel(); - } - - if (!Backend.canUseInstancing(flywheel$level)) { - return; - } - - if (InstancedRenderRegistry.canInstance(be.getType())) { - if (flywheel$blockEntities == null) { - flywheel$blockEntities = new ArrayList<>(); - } - - // Collect BEs in a temporary list to avoid excessive synchronization in InstancedRenderDispatcher. - flywheel$blockEntities.add(be); - } - - if (InstancedRenderRegistry.shouldSkipRender(be)) { - ci.cancel(); - } - } - - @Inject(method = "build", at = @At("HEAD")) - private void flywheel$onBuild(CallbackInfoReturnable cir) { - if (flywheel$level == null || flywheel$blockEntities == null || !Backend.canUseInstancing(flywheel$level)) { - return; - } - - InstancedRenderDispatcher.getBlockEntities(flywheel$level) - .queueAddAll(flywheel$blockEntities); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/light/LightUpdateMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/light/LightUpdateMixin.java index b4b97fe20..d11c29ede 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/light/LightUpdateMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/light/LightUpdateMixin.java @@ -1,6 +1,8 @@ package com.jozufozu.flywheel.mixin.light; +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; @@ -12,12 +14,12 @@ import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.SectionPos; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.chunk.ChunkSource; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -@OnlyIn(Dist.CLIENT) @Mixin(ClientChunkCache.class) public abstract class LightUpdateMixin extends ChunkSource { + @Shadow + @Final + ClientLevel level; /** * JUSTIFICATION: This method is called after a lighting tick once per subchunk where a @@ -25,12 +27,9 @@ public abstract class LightUpdateMixin extends ChunkSource { * the rendering system that it needs to redraw a chunk. It does all that work asynchronously, * and we should too. */ - @Inject(at = @At("HEAD"), method = "onLightUpdate") - private void onLightUpdate(LightLayer type, SectionPos pos, CallbackInfo ci) { - ClientChunkCache thi = ((ClientChunkCache) (Object) this); - ClientLevel world = (ClientLevel) thi.getLevel(); - - LightUpdater.get(world) - .onLightUpdate(type, pos.asLong()); + @Inject(method = "onLightUpdate(Lnet/minecraft/world/level/LightLayer;Lnet/minecraft/core/SectionPos;)V", at = @At("HEAD")) + private void flywheel$onLightUpdate(LightLayer layer, SectionPos pos, CallbackInfo ci) { + LightUpdater.get(level) + .onLightUpdate(layer, pos.asLong()); } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/light/NetworkLightUpdateMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/light/NetworkLightUpdateMixin.java index d38d18fdc..2c1867fa8 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/light/NetworkLightUpdateMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/light/NetworkLightUpdateMixin.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.mixin.light; 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; @@ -8,25 +9,42 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.jozufozu.flywheel.backend.RenderWork; import com.jozufozu.flywheel.light.LightUpdater; -import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket; @Mixin(ClientPacketListener.class) public class NetworkLightUpdateMixin { + @Shadow + private ClientLevel level; - @Inject(at = @At("TAIL"), method = "handleLightUpdatePacket") - private void onLightPacket(ClientboundLightUpdatePacket packet, CallbackInfo ci) { + @Inject(method = "handleLevelChunkWithLight(Lnet/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket;)V", at = @At("TAIL")) + private void flywheel$onLevelChunkWithLight(ClientboundLevelChunkWithLightPacket packet, CallbackInfo ci) { RenderWork.enqueue(() -> { - ClientLevel world = Minecraft.getInstance().level; + ClientLevel level = this.level; - if (world == null) return; + if (level == null) return; int chunkX = packet.getX(); int chunkZ = packet.getZ(); - LightUpdater.get(world) + LightUpdater.get(level) + .onLightPacket(chunkX, chunkZ); + }); + } + + @Inject(method = "handleLightUpdatePacket(Lnet/minecraft/network/protocol/game/ClientboundLightUpdatePacket;)V", at = @At("TAIL")) + private void flywheel$onLightUpdatePacket(ClientboundLightUpdatePacket packet, CallbackInfo ci) { + RenderWork.enqueue(() -> { + ClientLevel level = this.level; + + if (level == null) return; + + int chunkX = packet.getX(); + int chunkZ = packet.getZ(); + + LightUpdater.get(level) .onLightPacket(chunkX, chunkZ); }); } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/sodium/ChunkRenderRebuildTaskMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/sodium/ChunkRenderRebuildTaskMixin.java new file mode 100644 index 000000000..782bfc482 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/sodium/ChunkRenderRebuildTaskMixin.java @@ -0,0 +1,23 @@ +package com.jozufozu.flywheel.mixin.sodium; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; + +import me.jellysquid.mods.sodium.client.render.chunk.tasks.ChunkRenderRebuildTask; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.world.level.block.entity.BlockEntity; + +@Mixin(value = ChunkRenderRebuildTask.class, remap = false) +public class ChunkRenderRebuildTaskMixin { + @Redirect(method = "performBuild", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderDispatcher;getRenderer(Lnet/minecraft/world/level/block/entity/BlockEntity;)Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderer;")) + private BlockEntityRenderer flywheel$redirectGetRenderer(BlockEntityRenderDispatcher dispatcher, BlockEntity blockEntity) { + if (InstancedRenderDispatcher.tryAddBlockEntity(blockEntity)) { + return null; + } + return dispatcher.getRenderer(blockEntity); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/sodium/SodiumMixinPlugin.java b/src/main/java/com/jozufozu/flywheel/mixin/sodium/SodiumMixinPlugin.java new file mode 100644 index 000000000..6107e740d --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/sodium/SodiumMixinPlugin.java @@ -0,0 +1,48 @@ +package com.jozufozu.flywheel.mixin.sodium; + +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import com.google.common.base.Suppliers; + +import net.minecraftforge.fml.loading.LoadingModList; + +public class SodiumMixinPlugin implements IMixinConfigPlugin { + private static final Supplier IS_SODIUM_LOADED = Suppliers.memoize(() -> LoadingModList.get().getModFileById("rubidium") != null); + + @Override + public void onLoad(String mixinPackage) { + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return IS_SODIUM_LOADED.get(); + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + } + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + } +} diff --git a/src/main/java/com/jozufozu/flywheel/util/AnimationTickHolder.java b/src/main/java/com/jozufozu/flywheel/util/AnimationTickHolder.java index 20ed918df..79d4f5442 100644 --- a/src/main/java/com/jozufozu/flywheel/util/AnimationTickHolder.java +++ b/src/main/java/com/jozufozu/flywheel/util/AnimationTickHolder.java @@ -37,7 +37,7 @@ public class AnimationTickHolder { public static float getPartialTicks() { Minecraft mc = Minecraft.getInstance(); - return (mc.isPaused() ? ((PausedPartialTickAccessor) mc).flywheel$getPartialTicksPaused() : mc.getFrameTime()); + return (mc.isPaused() ? ((PausedPartialTickAccessor) mc).flywheel$getPausePartialTick() : mc.getFrameTime()); } // Unused but might be useful for debugging. diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index f2adeef64..c1c1e8a52 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -11,7 +11,7 @@ displayName = "Flywheel" logoFile = "logo.png" displayURL = "https://www.curseforge.com/minecraft/mc-mods/flywheel" updateJSONURL = "https://api.modrinth.com/updates/flywheel/forge_updates.json" -authors = "Jozufozu" +authors = "Jozufozu, PepperCode1" description = ''' A modern engine for modded minecraft.''' diff --git a/src/main/resources/flywheel.mixins.json b/src/main/resources/flywheel.mixins.json index 51e1a8235..e019e719f 100644 --- a/src/main/resources/flywheel.mixins.json +++ b/src/main/resources/flywheel.mixins.json @@ -12,7 +12,6 @@ "ClientLevelMixin", "ClientMainMixin", "EntityTypeMixin", - "FixFabulousDepthMixin", "FrustumMixin", "GlStateManagerMixin", "LevelRendererAccessor", @@ -20,13 +19,12 @@ "PausedPartialTickAccessor", "RenderTexturesMixin", "RenderTypeMixin", + "fix.FixFabulousDepthMixin", + "fix.FixNormalScalingMixin", "instancemanage.ChunkRebuildHooksMixin", - "instancemanage.ChunkRenderDispatcherAccessor", "instancemanage.InstanceAddMixin", "instancemanage.InstanceRemoveMixin", - "instancemanage.RenderChunkAccessor", - "instancemanage.RenderChunkMixin", - "instancemanage.SodiumChunkRenderDataMixin", + "instancemanage.InstanceUpdateMixin", "light.LightUpdateMixin", "light.NetworkLightUpdateMixin", "matrix.Matrix3fMixin", diff --git a/src/main/resources/flywheel.sodium.mixins.json b/src/main/resources/flywheel.sodium.mixins.json new file mode 100644 index 000000000..dfeaae397 --- /dev/null +++ b/src/main/resources/flywheel.sodium.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.jozufozu.flywheel.mixin.sodium", + "compatibilityLevel": "JAVA_17", + "refmap": "flywheel.refmap.json", + "plugin": "com.jozufozu.flywheel.mixin.sodium.SodiumMixinConfigPlugin", + "client": [ + "ChunkRenderRebuildTaskMixin" + ], + "injectors": { + "defaultRequire": 1 + } +}