From 1cb15f76be86920af5dde287ca6403b5657145ea Mon Sep 17 00:00:00 2001 From: makamys Date: Thu, 9 Jun 2022 13:01:33 +0200 Subject: Rebrand pt.2: Rename things in code --- src/main/java/makamys/neodymium/LODMod.java | 239 ------- src/main/java/makamys/neodymium/Neodymium.java | 239 +++++++ .../makamys/neodymium/mixin/MixinChunkCache.java | 8 +- .../neodymium/mixin/MixinEntityRenderer.java | 10 +- .../makamys/neodymium/mixin/MixinRenderBlocks.java | 6 +- .../makamys/neodymium/mixin/MixinRenderGlobal.java | 8 +- .../mixin/MixinRenderGlobal_OptiFine.java | 4 +- .../neodymium/mixin/MixinWorldRenderer.java | 20 +- .../java/makamys/neodymium/renderer/ChunkMesh.java | 6 +- .../neodymium/renderer/GPUMemoryManager.java | 6 +- .../java/makamys/neodymium/renderer/LODChunk.java | 184 ------ .../java/makamys/neodymium/renderer/LODRegion.java | 195 ------ .../makamys/neodymium/renderer/LODRenderer.java | 730 --------------------- .../java/makamys/neodymium/renderer/NeoChunk.java | 184 ++++++ .../java/makamys/neodymium/renderer/NeoRegion.java | 195 ++++++ .../makamys/neodymium/renderer/NeoRenderer.java | 730 +++++++++++++++++++++ .../neodymium/renderer/SimpleChunkMesh.java | 6 +- src/main/java/makamys/neodymium/util/Util.java | 4 +- 18 files changed, 1387 insertions(+), 1387 deletions(-) delete mode 100644 src/main/java/makamys/neodymium/LODMod.java create mode 100644 src/main/java/makamys/neodymium/Neodymium.java delete mode 100644 src/main/java/makamys/neodymium/renderer/LODChunk.java delete mode 100644 src/main/java/makamys/neodymium/renderer/LODRegion.java delete mode 100644 src/main/java/makamys/neodymium/renderer/LODRenderer.java create mode 100644 src/main/java/makamys/neodymium/renderer/NeoChunk.java create mode 100644 src/main/java/makamys/neodymium/renderer/NeoRegion.java create mode 100644 src/main/java/makamys/neodymium/renderer/NeoRenderer.java diff --git a/src/main/java/makamys/neodymium/LODMod.java b/src/main/java/makamys/neodymium/LODMod.java deleted file mode 100644 index 47c948b..0000000 --- a/src/main/java/makamys/neodymium/LODMod.java +++ /dev/null @@ -1,239 +0,0 @@ -package makamys.neodymium; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.gui.ScaledResolution; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.world.World; -import net.minecraftforge.client.event.EntityViewRenderEvent; -import net.minecraftforge.client.event.RenderGameOverlayEvent; -import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.common.config.Configuration; -import net.minecraftforge.event.world.ChunkEvent; -import net.minecraftforge.event.world.WorldEvent; - -import java.io.File; -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.lwjgl.input.Keyboard; -import org.lwjgl.opengl.GL11; - -import cpw.mods.fml.common.FMLCommonHandler; -import cpw.mods.fml.common.Mod; -import cpw.mods.fml.common.Mod.EventHandler; -import cpw.mods.fml.common.event.FMLInitializationEvent; -import cpw.mods.fml.common.event.FMLPreInitializationEvent; -import cpw.mods.fml.common.eventhandler.SubscribeEvent; -import cpw.mods.fml.common.gameevent.TickEvent; -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; -import makamys.neodymium.renderer.LODRenderer; -import makamys.neodymium.util.SpriteUtil; - -@Mod(modid = LODMod.MODID, version = LODMod.VERSION) -public class LODMod -{ - public static final String MODID = "lodmod"; - public static final String VERSION = "0.0"; - - public static final Logger LOGGER = LogManager.getLogger("lodmod"); - - public static LODRenderer renderer; - - public static boolean enabled; - public static int chunkLoadsPerTick; - public static List blockClassBlacklist; - public static double fogStart; - public static double fogEnd; - public static double farPlaneDistanceMultiplier; - public static float maxSimpleMeshHeight; - public static boolean forceVanillaBiomeTemperature; - public static boolean hideUnderVanillaChunks; - public static boolean disableChunkMeshes; - public static boolean disableSimpleMeshes; - public static boolean saveMeshes; - public static boolean optimizeChunkMeshes; - public static int maxMeshesPerFrame; - public static int sortFrequency; - public static int gcRate; - public static int VRAMSize; - public static int debugPrefix; - - private File configFile; - - public static boolean fogEventWasPosted; - - public static boolean ofFastRender; - public static boolean enableFog; - - @EventHandler - public void preInit(FMLPreInitializationEvent event) - { - configFile = event.getSuggestedConfigurationFile(); - reloadConfig(); - } - - private void reloadConfig() { - Configuration config = new Configuration(configFile); - - config.load(); - enabled = config.get("General", "enabled", true).getBoolean(); - chunkLoadsPerTick = config.get("General", "chunkLoadsPerTick", 64).getInt(); - blockClassBlacklist = Arrays.stream(config.get("General", "blockClassBlacklist", "net.minecraft.block.BlockRotatedPillar;biomesoplenty.common.blocks.BlockBOPLog;gregapi.block.multitileentity.MultiTileEntityBlock").getString().split(";")) - .map(className -> { - try { - return Class.forName(className); - } catch (ClassNotFoundException e) { - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - fogStart = config.get("Fog", "fogStart", "0.4").getDouble(); - fogEnd = config.get("Fog", "fogEnd", "0.8").getDouble(); - farPlaneDistanceMultiplier = config.get("Fog", "farPlaneDistanceMultiplier", "1.0").getDouble(); - - maxSimpleMeshHeight = (float)config.get("Debug", "maxSimpleMeshHeight", 1000.0).getDouble(); - - forceVanillaBiomeTemperature = config.get("Simple mesh generation", "forceVanillaBiomeTemperature", true).getBoolean(); - - hideUnderVanillaChunks = config.getBoolean("hideUnderVanillaChunks", "render", true, ""); - disableChunkMeshes = config.getBoolean("disableChunkMeshes", "render", true, ""); - disableSimpleMeshes = config.getBoolean("disableSimpleMeshes", "render", false, ""); - optimizeChunkMeshes = config.getBoolean("optimizeChunkMeshes", "render", true, ""); - saveMeshes = config.getBoolean("saveMeshes", "render", false, ""); - maxMeshesPerFrame = config.getInt("maxMeshesPerFrame", "render", -1, -1, Integer.MAX_VALUE, ""); - sortFrequency = config.getInt("sortFrequency", "render", 1, 1, Integer.MAX_VALUE, ""); - gcRate = config.getInt("gcRate", "render", 1, 1, Integer.MAX_VALUE, "Maximum number of meshes to relocate each frame."); - VRAMSize = config.getInt("VRAMSize", "render", 1024, 1, Integer.MAX_VALUE, "VRAM buffer size (MB)."); - enableFog = config.getBoolean("enableFog", "render", true, ""); - debugPrefix = config.getInt("debugPrefix", "debug", Keyboard.KEY_F4, -1, Integer.MAX_VALUE, "This key has to be held down while pressing the debug keybinds. LWJGL keycode. Setting this to 0 will make the keybinds usable without holding anything else down. Setting this to -1 will disable debug keybinds entirely."); - - if(config.hasChanged()) { - config.save(); - } - } - - @EventHandler - public void init(FMLInitializationEvent event) - { - FMLCommonHandler.instance().bus().register(this); - MinecraftForge.EVENT_BUS.register(this); - } - - private void onPlayerWorldChanged(World newWorld) { - if(getRendererWorld() == null && newWorld != null) { - reloadConfig(); - if(enabled) { - SpriteUtil.init(); - } - } - if(renderer != null) { - renderer.destroy(); - renderer = null; - } - if(enabled && newWorld != null) { - renderer = new LODRenderer(newWorld); - } - } - - @SubscribeEvent - @SideOnly(Side.CLIENT) - public void onWorldUnload(WorldEvent.Unload event) { - if(event.world == getRendererWorld()) { - onPlayerWorldChanged(null); - } - } - - @SubscribeEvent - public void onChunkLoad(ChunkEvent.Load event) { - if(!event.world.isRemote) return; - - if(isActive()) { - renderer.onChunkLoad(event); - } - } - - public static boolean isActive() { - return renderer != null && renderer.hasInited && !renderer.destroyPending; - } - - private World getRendererWorld() { - return renderer != null ? renderer.world : null; - } - - @SubscribeEvent - public void onClientTick(TickEvent.ClientTickEvent event) { - if(event.phase == TickEvent.Phase.START) { - EntityPlayer player = Minecraft.getMinecraft().thePlayer; - World world = player != null ? player.worldObj : null; - if(world != getRendererWorld()) { - onPlayerWorldChanged(world); - } - - if(MixinConfigPlugin.isOptiFinePresent()) { - try { - ofFastRender = (boolean)Class.forName("Config").getMethod("isFastRender").invoke(null); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException - | SecurityException | ClassNotFoundException e) { - // oops - } - } - } - } - - @SubscribeEvent - public void onServerTick(TickEvent.ServerTickEvent event) { - if(event.phase == TickEvent.Phase.START) { - if(isActive()) { - renderer.serverTick(); - } - } - } - - @SubscribeEvent - public void onRenderTick(TickEvent.RenderTickEvent event) { - if(event.phase == TickEvent.Phase.END) { - if(isActive()) { - renderer.onRenderTickEnd(); - } - } - } - - @SubscribeEvent - public void onRenderOverlay(RenderGameOverlayEvent event) { - FontRenderer fontRenderer = RenderManager.instance.getFontRenderer(); - if(isActive() && event.type == ElementType.TEXT && fontRenderer != null && Minecraft.getMinecraft().gameSettings.showDebugInfo) - { - Minecraft mc = Minecraft.getMinecraft(); - ScaledResolution scaledresolution = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); - int w = scaledresolution.getScaledWidth(); - int h = scaledresolution.getScaledHeight(); - - int yOffset = 0; - for(String s : renderer.getDebugText()) { - fontRenderer.drawStringWithShadow(s, w - fontRenderer.getStringWidth(s) - 10, 80 + yOffset, 0xFFFFFF); - yOffset += 10; - } - } - } - - - @SubscribeEvent - public void onRenderFog(EntityViewRenderEvent.RenderFogEvent event) { - fogEventWasPosted = true; - } - - public static boolean shouldRenderVanillaWorld() { - return !isActive() || (isActive() && renderer.renderWorld && !renderer.rendererActive); - } - -} diff --git a/src/main/java/makamys/neodymium/Neodymium.java b/src/main/java/makamys/neodymium/Neodymium.java new file mode 100644 index 0000000..236d85b --- /dev/null +++ b/src/main/java/makamys/neodymium/Neodymium.java @@ -0,0 +1,239 @@ +package makamys.neodymium; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.world.World; +import net.minecraftforge.client.event.EntityViewRenderEvent; +import net.minecraftforge.client.event.RenderGameOverlayEvent; +import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.event.world.ChunkEvent; +import net.minecraftforge.event.world.WorldEvent; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.lwjgl.input.Keyboard; +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.Mod; +import cpw.mods.fml.common.Mod.EventHandler; +import cpw.mods.fml.common.event.FMLInitializationEvent; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.TickEvent; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import makamys.neodymium.renderer.NeoRenderer; +import makamys.neodymium.util.SpriteUtil; + +@Mod(modid = Neodymium.MODID, version = Neodymium.VERSION) +public class Neodymium +{ + public static final String MODID = "neodymium"; + public static final String VERSION = "0.0"; + + public static final Logger LOGGER = LogManager.getLogger("neodymium"); + + public static NeoRenderer renderer; + + public static boolean enabled; + public static int chunkLoadsPerTick; + public static List blockClassBlacklist; + public static double fogStart; + public static double fogEnd; + public static double farPlaneDistanceMultiplier; + public static float maxSimpleMeshHeight; + public static boolean forceVanillaBiomeTemperature; + public static boolean hideUnderVanillaChunks; + public static boolean disableChunkMeshes; + public static boolean disableSimpleMeshes; + public static boolean saveMeshes; + public static boolean optimizeChunkMeshes; + public static int maxMeshesPerFrame; + public static int sortFrequency; + public static int gcRate; + public static int VRAMSize; + public static int debugPrefix; + + private File configFile; + + public static boolean fogEventWasPosted; + + public static boolean ofFastRender; + public static boolean enableFog; + + @EventHandler + public void preInit(FMLPreInitializationEvent event) + { + configFile = event.getSuggestedConfigurationFile(); + reloadConfig(); + } + + private void reloadConfig() { + Configuration config = new Configuration(configFile); + + config.load(); + enabled = config.get("General", "enabled", true).getBoolean(); + chunkLoadsPerTick = config.get("General", "chunkLoadsPerTick", 64).getInt(); + blockClassBlacklist = Arrays.stream(config.get("General", "blockClassBlacklist", "net.minecraft.block.BlockRotatedPillar;biomesoplenty.common.blocks.BlockBOPLog;gregapi.block.multitileentity.MultiTileEntityBlock").getString().split(";")) + .map(className -> { + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + fogStart = config.get("Fog", "fogStart", "0.4").getDouble(); + fogEnd = config.get("Fog", "fogEnd", "0.8").getDouble(); + farPlaneDistanceMultiplier = config.get("Fog", "farPlaneDistanceMultiplier", "1.0").getDouble(); + + maxSimpleMeshHeight = (float)config.get("Debug", "maxSimpleMeshHeight", 1000.0).getDouble(); + + forceVanillaBiomeTemperature = config.get("Simple mesh generation", "forceVanillaBiomeTemperature", true).getBoolean(); + + hideUnderVanillaChunks = config.getBoolean("hideUnderVanillaChunks", "render", true, ""); + disableChunkMeshes = config.getBoolean("disableChunkMeshes", "render", true, ""); + disableSimpleMeshes = config.getBoolean("disableSimpleMeshes", "render", false, ""); + optimizeChunkMeshes = config.getBoolean("optimizeChunkMeshes", "render", true, ""); + saveMeshes = config.getBoolean("saveMeshes", "render", false, ""); + maxMeshesPerFrame = config.getInt("maxMeshesPerFrame", "render", -1, -1, Integer.MAX_VALUE, ""); + sortFrequency = config.getInt("sortFrequency", "render", 1, 1, Integer.MAX_VALUE, ""); + gcRate = config.getInt("gcRate", "render", 1, 1, Integer.MAX_VALUE, "Maximum number of meshes to relocate each frame."); + VRAMSize = config.getInt("VRAMSize", "render", 1024, 1, Integer.MAX_VALUE, "VRAM buffer size (MB)."); + enableFog = config.getBoolean("enableFog", "render", true, ""); + debugPrefix = config.getInt("debugPrefix", "debug", Keyboard.KEY_F4, -1, Integer.MAX_VALUE, "This key has to be held down while pressing the debug keybinds. LWJGL keycode. Setting this to 0 will make the keybinds usable without holding anything else down. Setting this to -1 will disable debug keybinds entirely."); + + if(config.hasChanged()) { + config.save(); + } + } + + @EventHandler + public void init(FMLInitializationEvent event) + { + FMLCommonHandler.instance().bus().register(this); + MinecraftForge.EVENT_BUS.register(this); + } + + private void onPlayerWorldChanged(World newWorld) { + if(getRendererWorld() == null && newWorld != null) { + reloadConfig(); + if(enabled) { + SpriteUtil.init(); + } + } + if(renderer != null) { + renderer.destroy(); + renderer = null; + } + if(enabled && newWorld != null) { + renderer = new NeoRenderer(newWorld); + } + } + + @SubscribeEvent + @SideOnly(Side.CLIENT) + public void onWorldUnload(WorldEvent.Unload event) { + if(event.world == getRendererWorld()) { + onPlayerWorldChanged(null); + } + } + + @SubscribeEvent + public void onChunkLoad(ChunkEvent.Load event) { + if(!event.world.isRemote) return; + + if(isActive()) { + renderer.onChunkLoad(event); + } + } + + public static boolean isActive() { + return renderer != null && renderer.hasInited && !renderer.destroyPending; + } + + private World getRendererWorld() { + return renderer != null ? renderer.world : null; + } + + @SubscribeEvent + public void onClientTick(TickEvent.ClientTickEvent event) { + if(event.phase == TickEvent.Phase.START) { + EntityPlayer player = Minecraft.getMinecraft().thePlayer; + World world = player != null ? player.worldObj : null; + if(world != getRendererWorld()) { + onPlayerWorldChanged(world); + } + + if(MixinConfigPlugin.isOptiFinePresent()) { + try { + ofFastRender = (boolean)Class.forName("Config").getMethod("isFastRender").invoke(null); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException + | SecurityException | ClassNotFoundException e) { + // oops + } + } + } + } + + @SubscribeEvent + public void onServerTick(TickEvent.ServerTickEvent event) { + if(event.phase == TickEvent.Phase.START) { + if(isActive()) { + renderer.serverTick(); + } + } + } + + @SubscribeEvent + public void onRenderTick(TickEvent.RenderTickEvent event) { + if(event.phase == TickEvent.Phase.END) { + if(isActive()) { + renderer.onRenderTickEnd(); + } + } + } + + @SubscribeEvent + public void onRenderOverlay(RenderGameOverlayEvent event) { + FontRenderer fontRenderer = RenderManager.instance.getFontRenderer(); + if(isActive() && event.type == ElementType.TEXT && fontRenderer != null && Minecraft.getMinecraft().gameSettings.showDebugInfo) + { + Minecraft mc = Minecraft.getMinecraft(); + ScaledResolution scaledresolution = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + int w = scaledresolution.getScaledWidth(); + int h = scaledresolution.getScaledHeight(); + + int yOffset = 0; + for(String s : renderer.getDebugText()) { + fontRenderer.drawStringWithShadow(s, w - fontRenderer.getStringWidth(s) - 10, 80 + yOffset, 0xFFFFFF); + yOffset += 10; + } + } + } + + + @SubscribeEvent + public void onRenderFog(EntityViewRenderEvent.RenderFogEvent event) { + fogEventWasPosted = true; + } + + public static boolean shouldRenderVanillaWorld() { + return !isActive() || (isActive() && renderer.renderWorld && !renderer.rendererActive); + } + +} diff --git a/src/main/java/makamys/neodymium/mixin/MixinChunkCache.java b/src/main/java/makamys/neodymium/mixin/MixinChunkCache.java index b4a7368..331e3cf 100644 --- a/src/main/java/makamys/neodymium/mixin/MixinChunkCache.java +++ b/src/main/java/makamys/neodymium/mixin/MixinChunkCache.java @@ -4,9 +4,9 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import makamys.neodymium.LODMod; +import makamys.neodymium.Neodymium; import makamys.neodymium.renderer.FarChunkCache; -import makamys.neodymium.renderer.LODRenderer; +import makamys.neodymium.renderer.NeoRenderer; import net.minecraft.world.ChunkCache; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; @@ -17,8 +17,8 @@ abstract class MixinChunkCache { @Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;getChunkFromChunkCoords(II)Lnet/minecraft/world/chunk/Chunk;")) private Chunk redirectGetChunkFromChunkCoords(World world, int p1, int p2) { Chunk chunk = world.getChunkFromChunkCoords(p1, p2); - if(LODMod.isActive() && FarChunkCache.class.isInstance(this.getClass()) && chunk.isEmpty()) { - Chunk myChunk = LODMod.renderer.getChunkFromChunkCoords(p1, p2); + if(Neodymium.isActive() && FarChunkCache.class.isInstance(this.getClass()) && chunk.isEmpty()) { + Chunk myChunk = Neodymium.renderer.getChunkFromChunkCoords(p1, p2); if(myChunk != null) { chunk = myChunk; } diff --git a/src/main/java/makamys/neodymium/mixin/MixinEntityRenderer.java b/src/main/java/makamys/neodymium/mixin/MixinEntityRenderer.java index 66d95ae..6e5cf7b 100644 --- a/src/main/java/makamys/neodymium/mixin/MixinEntityRenderer.java +++ b/src/main/java/makamys/neodymium/mixin/MixinEntityRenderer.java @@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import makamys.neodymium.LODMod; +import makamys.neodymium.Neodymium; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.EntityRenderer; import net.minecraft.entity.EntityLivingBase; @@ -21,15 +21,15 @@ abstract class MixinEntityRenderer { @Inject(method = "setupCameraTransform", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/EntityRenderer;farPlaneDistance:F", shift = At.Shift.AFTER, ordinal = 1)) private void onConstructed(CallbackInfo ci) { - if(LODMod.isActive()) { - farPlaneDistance *= LODMod.renderer.getFarPlaneDistanceMultiplier(); + if(Neodymium.isActive()) { + farPlaneDistance *= Neodymium.renderer.getFarPlaneDistanceMultiplier(); } } @Inject(method = "setupFog", at = @At(value = "RETURN")) private void afterSetupFog(int mode, float alpha, CallbackInfo ci) { - if(LODMod.isActive()) { - LODMod.renderer.afterSetupFog(mode, alpha, farPlaneDistance); + if(Neodymium.isActive()) { + Neodymium.renderer.afterSetupFog(mode, alpha, farPlaneDistance); } } } diff --git a/src/main/java/makamys/neodymium/mixin/MixinRenderBlocks.java b/src/main/java/makamys/neodymium/mixin/MixinRenderBlocks.java index 0f8ff41..fdfbe65 100644 --- a/src/main/java/makamys/neodymium/mixin/MixinRenderBlocks.java +++ b/src/main/java/makamys/neodymium/mixin/MixinRenderBlocks.java @@ -6,7 +6,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; -import makamys.neodymium.LODMod; +import makamys.neodymium.Neodymium; import makamys.neodymium.renderer.FarChunkCache; import net.minecraft.block.Block; import net.minecraft.client.renderer.RenderBlocks; @@ -19,8 +19,8 @@ abstract class MixinRenderBlocks { @Redirect(method = "renderBlockLiquid", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;shouldSideBeRendered(Lnet/minecraft/world/IBlockAccess;IIII)Z")) public boolean shouldSideBeRendered(Block block, IBlockAccess ba, int x, int y, int z, int w) { - if(LODMod.isActive()) { - return LODMod.renderer.shouldSideBeRendered(block, ba, x, y, z, w); + if(Neodymium.isActive()) { + return Neodymium.renderer.shouldSideBeRendered(block, ba, x, y, z, w); } else { return block.shouldSideBeRendered(ba, x, y, z, w); } diff --git a/src/main/java/makamys/neodymium/mixin/MixinRenderGlobal.java b/src/main/java/makamys/neodymium/mixin/MixinRenderGlobal.java index dc561a2..03b8aac 100644 --- a/src/main/java/makamys/neodymium/mixin/MixinRenderGlobal.java +++ b/src/main/java/makamys/neodymium/mixin/MixinRenderGlobal.java @@ -12,7 +12,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import makamys.neodymium.LODMod; +import makamys.neodymium.Neodymium; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderGlobal; import net.minecraft.client.renderer.WorldRenderer; @@ -26,15 +26,15 @@ abstract class MixinRenderGlobal { @Redirect(method = "renderSortedRenderers", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;renderAllRenderLists(ID)V")) private void redirectRenderAllRenderLists(RenderGlobal thiz, int p1, double p2) { - if(LODMod.shouldRenderVanillaWorld()) { + if(Neodymium.shouldRenderVanillaWorld()) { thiz.renderAllRenderLists(p1, p2); } } @Inject(method = "renderSortedRenderers", at = @At(value = "HEAD")) public void preRenderSortedRenderers(int startRenderer, int numRenderers, int renderPass, double partialTickTime, CallbackInfoReturnable cir) { - if(LODMod.isActive()) { - LODMod.renderer.preRenderSortedRenderers(renderPass, partialTickTime, sortedWorldRenderers); + if(Neodymium.isActive()) { + Neodymium.renderer.preRenderSortedRenderers(renderPass, partialTickTime, sortedWorldRenderers); } } } diff --git a/src/main/java/makamys/neodymium/mixin/MixinRenderGlobal_OptiFine.java b/src/main/java/makamys/neodymium/mixin/MixinRenderGlobal_OptiFine.java index dad164c..d63e3ad 100644 --- a/src/main/java/makamys/neodymium/mixin/MixinRenderGlobal_OptiFine.java +++ b/src/main/java/makamys/neodymium/mixin/MixinRenderGlobal_OptiFine.java @@ -7,7 +7,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import makamys.neodymium.LODMod; +import makamys.neodymium.Neodymium; import net.minecraft.client.renderer.RenderBlocks; import net.minecraft.client.renderer.RenderGlobal; @@ -17,7 +17,7 @@ abstract class MixinRenderGlobal_OptiFine { // for OptiFine's Fast Render option @Redirect(method = "renderSortedRenderersFast", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallLists(Ljava/nio/IntBuffer;)V"), remap=false) private void redirectRenderAllRenderLists(IntBuffer buffer) { - if(LODMod.shouldRenderVanillaWorld()) { + if(Neodymium.shouldRenderVanillaWorld()) { GL11.glCallLists(buffer); } } diff --git a/src/main/java/makamys/neodymium/mixin/MixinWorldRenderer.java b/src/main/java/makamys/neodymium/mixin/MixinWorldRenderer.java index 47450c3..7d1ffd8 100644 --- a/src/main/java/makamys/neodymium/mixin/MixinWorldRenderer.java +++ b/src/main/java/makamys/neodymium/mixin/MixinWorldRenderer.java @@ -12,12 +12,12 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import makamys.neodymium.LODMod; +import makamys.neodymium.Neodymium; import makamys.neodymium.ducks.IWorldRenderer; import makamys.neodymium.renderer.ChunkMesh; import makamys.neodymium.renderer.FarChunkCache; import makamys.neodymium.renderer.FarWorldRenderer; -import makamys.neodymium.renderer.LODRenderer; +import makamys.neodymium.renderer.NeoRenderer; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.WorldRenderer; @@ -71,7 +71,7 @@ abstract class MixinWorldRenderer implements IWorldRenderer { private void preUpdateRenderer(CallbackInfo ci) { saveDrawnStatus(); - if(LODMod.isActive()) { + if(Neodymium.isActive()) { if(needsUpdate) { if(chunkMeshes != null) { chunkMeshes.clear(); @@ -88,9 +88,9 @@ abstract class MixinWorldRenderer implements IWorldRenderer { private void postUpdateRenderer(CallbackInfo ci) { notifyIfDrawnStatusChanged(); - if(LODMod.isActive()) { + if(Neodymium.isActive()) { if(chunkMeshes != null) { - LODMod.renderer.onWorldRendererPost(WorldRenderer.class.cast(this)); + Neodymium.renderer.onWorldRendererPost(WorldRenderer.class.cast(this)); chunkMeshes.clear(); } } @@ -98,7 +98,7 @@ abstract class MixinWorldRenderer implements IWorldRenderer { @Inject(method = "postRenderBlocks", at = @At(value = "HEAD")) private void prePostRenderBlocks(int pass, EntityLivingBase entity, CallbackInfo ci) { - if(LODMod.isActive() && !LODMod.disableChunkMeshes) { + if(Neodymium.isActive() && !Neodymium.disableChunkMeshes) { if(chunkMeshes != null) { chunkMeshes.add(ChunkMesh.fromTessellator(pass, WorldRenderer.class.cast(this), Tessellator.instance)); } @@ -143,8 +143,8 @@ abstract class MixinWorldRenderer implements IWorldRenderer { @Inject(method = "setDontDraw", at = @At(value = "HEAD")) private void preSetDontDraw(CallbackInfo ci) { - if(LODMod.isActive()) { - LODMod.renderer.onWorldRendererChanged(WorldRenderer.class.cast(this), LODRenderer.WorldRendererChange.DELETED); + if(Neodymium.isActive()) { + Neodymium.renderer.onWorldRendererChanged(WorldRenderer.class.cast(this), NeoRenderer.WorldRendererChange.DELETED); } } @@ -169,8 +169,8 @@ abstract class MixinWorldRenderer implements IWorldRenderer { private void notifyIfDrawnStatusChanged() { boolean drawn = isDrawn(); - if(LODMod.isActive() && drawn != savedDrawnStatus) { - LODMod.renderer.onWorldRendererChanged(WorldRenderer.class.cast(this), drawn ? LODRenderer.WorldRendererChange.VISIBLE : LODRenderer.WorldRendererChange.INVISIBLE); + if(Neodymium.isActive() && drawn != savedDrawnStatus) { + Neodymium.renderer.onWorldRendererChanged(WorldRenderer.class.cast(this), drawn ? NeoRenderer.WorldRendererChange.VISIBLE : NeoRenderer.WorldRendererChange.INVISIBLE); } } diff --git a/src/main/java/makamys/neodymium/renderer/ChunkMesh.java b/src/main/java/makamys/neodymium/renderer/ChunkMesh.java index 6c4cd59..6d5dc2d 100644 --- a/src/main/java/makamys/neodymium/renderer/ChunkMesh.java +++ b/src/main/java/makamys/neodymium/renderer/ChunkMesh.java @@ -14,7 +14,7 @@ import java.util.stream.Collectors; import org.lwjgl.BufferUtils; -import makamys.neodymium.LODMod; +import makamys.neodymium.Neodymium; import makamys.neodymium.MixinConfigPlugin; import makamys.neodymium.ducks.IWorldRenderer; import makamys.neodymium.util.BufferWriter; @@ -78,12 +78,12 @@ public class ChunkMesh extends Mesh { int yOffset = wr.posY; int zOffset = wr.posZ; - boolean fr = MixinConfigPlugin.isOptiFinePresent() && LODMod.ofFastRender; + boolean fr = MixinConfigPlugin.isOptiFinePresent() && Neodymium.ofFastRender; int tessellatorXOffset = fr ? xOffset : 0; int tessellatorYOffset = fr ? yOffset : 0; int tessellatorZOffset = fr ? zOffset : 0; - boolean optimize = LODMod.optimizeChunkMeshes; + boolean optimize = Neodymium.optimizeChunkMeshes; ChunkMesh.Flags flags = new ChunkMesh.Flags(t.hasTexture, t.hasBrightness, t.hasColor, t.hasNormals); diff --git a/src/main/java/makamys/neodymium/renderer/GPUMemoryManager.java b/src/main/java/makamys/neodymium/renderer/GPUMemoryManager.java index 75b1f64..1bb2623 100644 --- a/src/main/java/makamys/neodymium/renderer/GPUMemoryManager.java +++ b/src/main/java/makamys/neodymium/renderer/GPUMemoryManager.java @@ -6,7 +6,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; -import makamys.neodymium.LODMod; +import makamys.neodymium.Neodymium; import makamys.neodymium.renderer.Mesh.GPUStatus; import makamys.neodymium.util.GuiHelper; @@ -23,7 +23,7 @@ public class GPUMemoryManager { public GPUMemoryManager() { VBO = glGenBuffers(); - bufferSize = LODMod.VRAMSize * 1024 * 1024; + bufferSize = Neodymium.VRAMSize * 1024 * 1024; glBindBuffer(GL_ARRAY_BUFFER, VBO); @@ -109,7 +109,7 @@ public class GPUMemoryManager { if(end() + mesh.bufferSize() >= bufferSize) { System.out.println("VRAM is full! Try increasing the allocated VRAM in the config, if possible. Reverting to vanilla renderer."); - LODMod.renderer.destroyPending = true; + Neodymium.renderer.destroyPending = true; // TODO restart renderer with more VRAM allocated when this happens. return; } diff --git a/src/main/java/makamys/neodymium/renderer/LODChunk.java b/src/main/java/makamys/neodymium/renderer/LODChunk.java deleted file mode 100644 index 5dd3762..0000000 --- a/src/main/java/makamys/neodymium/renderer/LODChunk.java +++ /dev/null @@ -1,184 +0,0 @@ -package makamys.neodymium.renderer; - -import java.util.List; - -import makamys.neodymium.LODMod; -import net.minecraft.entity.Entity; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagEnd; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.world.chunk.Chunk; - -public class LODChunk { - - int x, z; - public boolean needsChunk = true; - int lod = 0; - boolean visible; - boolean dirty; - boolean discardedMesh; - - SimpleChunkMesh[] simpleMeshes = new SimpleChunkMesh[2]; - ChunkMesh[] chunkMeshes = new ChunkMesh[32]; - - public boolean[] isSectionVisible = new boolean[16]; - - LODRenderer renderer = LODMod.renderer; - - public LODChunk(int x, int z) { - this.x = x; - this.z = z; - } - /* - public LODChunk(NBTTagCompound nbt, List spriteList) { - this.x = nbt.getInteger("x"); - this.z = nbt.getInteger("z"); - - loadChunkMeshesNBT(nbt.getCompoundTag("chunkMeshes"), spriteList); - } - - private void loadChunkMeshesNBT(NBTTagCompound chunkMeshesCompound, List spriteList) { - for(Object o : chunkMeshesCompound.func_150296_c()) { - String key = (String)o; - int keyInt = Integer.parseInt(key); - - byte[] data = chunkMeshesCompound.getByteArray(key); - - chunkMeshes[keyInt] = new ChunkMesh(x, keyInt / 2, z, new ChunkMesh.Flags(true, true, true, false), data.length / (2 + 4 * (3 + 2 + 2 + 4)), data, spriteList, keyInt % 2); - } - } - */ - @Override - public String toString() { - return "LODChunk(" + x + ", " + z + ")"; - } - - public double distSq(Entity entity) { - return Math.pow(entity.posX - x * 16, 2) + Math.pow(entity.posZ - z * 16, 2); - } - - public void putChunkMeshes(int cy, List newChunkMeshes) { - for(int i = 0; i < 2; i++) { - ChunkMesh newChunkMesh = newChunkMeshes.size() > i ? newChunkMeshes.get(i) : null; - if(chunkMeshes[cy * 2 + i] != null) { - if(newChunkMesh != null) { - newChunkMesh.pass = i; - } - - renderer.removeMesh(chunkMeshes[cy * 2 + i]); - chunkMeshes[cy * 2 + i].destroy(); - } - chunkMeshes[cy * 2 + i] = newChunkMesh; - } - LODMod.renderer.lodChunkChanged(this); - dirty = true; - discardedMesh = false; - } - - // nice copypasta - public void putSimpleMeshes(List newSimpleMeshes) { - for(int i = 0; i < 2; i++) { - SimpleChunkMesh newSimpleMesh = newSimpleMeshes.size() > i ? newSimpleMeshes.get(i) : null; - if(simpleMeshes[i] != null) { - if(newSimpleMesh != null) { - newSimpleMesh.pass = i; - } - - renderer.setMeshVisible(simpleMeshes[i], false); - simpleMeshes[i].destroy(); - } - simpleMeshes[i] = newSimpleMesh; - } - LODMod.renderer.lodChunkChanged(this); - } - - public boolean hasChunkMeshes() { - for(ChunkMesh cm : chunkMeshes) { - if(cm != null) { - return true; - } - } - return false; - } - - public void tick(Entity player) { - double distSq = distSq(player); - if(LODMod.disableSimpleMeshes || distSq < Math.pow((LODMod.renderer.renderRange / 2) * 16, 2)) { - setLOD(2); - } else if(distSq < Math.pow((LODMod.renderer.renderRange) * 16, 2)) { - setLOD(1); - } else { - setLOD(0); - } - } - - public void setLOD(int lod) { - if(lod == this.lod) return; - - this.lod = lod; - LODMod.renderer.lodChunkChanged(this); - if(!dirty) { - if(lod < 2) { - for(int i = 0; i < chunkMeshes.length; i++) { - if(chunkMeshes[i] != null) { - chunkMeshes[i].destroy(); - chunkMeshes[i] = null; - discardedMesh = true; - } - } - } - } - } - /* - public NBTTagCompound saveToNBT(NBTTagCompound oldNbt, List oldStringTable) { - NBTTagCompound nbt = new NBTTagCompound(); - nbt.setInteger("x", x); - nbt.setInteger("z", z); - - NBTTagCompound chunkMeshesCompound = oldNbt == null ? new NBTTagCompound() : oldNbt.getCompoundTag("chunkMeshes"); - if(!discardedMesh) { - for(int i = 0; i < chunkMeshes.length; i++) { - if(chunkMeshes[i] != null) { - chunkMeshesCompound.setTag(String.valueOf(i), chunkMeshes[i].nbtData); - } - } - } else if(oldNbt != null && discardedMesh && lod == 2) { - loadChunkMeshesNBT(chunkMeshesCompound, oldStringTable); - LODMod.renderer.lodChunkChanged(this); - } - nbt.setTag("chunkMeshes", chunkMeshesCompound); - dirty = false; - return nbt; - } - */ - public void destroy() { - for(SimpleChunkMesh scm: simpleMeshes) { - if(scm != null) { - scm.destroy(); - } - } - for(ChunkMesh cm: chunkMeshes) { - if(cm != null) { - cm.destroy(); - } - } - LODMod.renderer.setVisible(this, false); - } - - public void receiveChunk(Chunk chunk) { - if(!LODMod.disableSimpleMeshes) { - putSimpleMeshes(SimpleChunkMesh.generateSimpleMeshes(chunk)); - } - } - - public boolean isFullyVisible() { - if(!visible) return false; - for(boolean b : isSectionVisible) { - if(!b) { - return false; - } - } - return true; - } - -} diff --git a/src/main/java/makamys/neodymium/renderer/LODRegion.java b/src/main/java/makamys/neodymium/renderer/LODRegion.java deleted file mode 100644 index 22316f7..0000000 --- a/src/main/java/makamys/neodymium/renderer/LODRegion.java +++ /dev/null @@ -1,195 +0,0 @@ -package makamys.neodymium.renderer; - -import java.io.BufferedOutputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import makamys.neodymium.LODMod; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.texture.TextureMap; -import net.minecraft.entity.Entity; -import net.minecraft.nbt.CompressedStreamTools; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.world.chunk.Chunk; -import net.minecraftforge.common.util.Constants.NBT; - -public class LODRegion { - - private LODChunk[][] data = new LODChunk[32][32]; - - int regionX, regionZ; - - public LODRegion(int regionX, int regionZ) { - this.regionX = regionX; - this.regionZ = regionZ; - - for(int i = 0; i < 32; i++) { - for(int j = 0; j < 32; j++) { - data[i][j] = new LODChunk(regionX * 32 + i, regionZ * 32 + j); - } - } - } - /* - public LODRegion(int regionX, int regionZ, NBTTagCompound nbt) { - this.regionX = regionX; - this.regionZ = regionZ; - - NBTTagList list = nbt.getTagList("chunks", NBT.TAG_COMPOUND); - List stringTable = Arrays.asList(nbt.getString("stringTable").split("\\n")); - - int idx = 0; - for(int i = 0; i < 32; i++) { - for(int j = 0; j < 32; j++) { - data[i][j] = new LODChunk(list.getCompoundTagAt(idx++), stringTable); - if(data[i][j].hasChunkMeshes()) { - LODMod.renderer.setVisible(data[i][j], true); - } - } - } - } - */ - public static LODRegion load(Path saveDir, int regionX, int regionZ) { - /*if(!(LODMod.disableChunkMeshes || !LODMod.saveMeshes)) { - File saveFile = getSavePath(saveDir, regionX, regionZ).toFile(); - if(saveFile.exists()) { - try { - NBTTagCompound nbt = CompressedStreamTools.readCompressed(new FileInputStream(saveFile)); - return new LODRegion(regionX, regionZ, nbt); - } catch (Exception e) { - e.printStackTrace(); - } - } - }*/ - return new LODRegion(regionX, regionZ); - } - /* - private static Path getSavePath(Path saveDir, int regionX, int regionZ) { - return saveDir.resolve("lod").resolve(regionX + "," + regionZ + ".lod"); - } - - public void save(Path saveDir) { - if(LODMod.disableChunkMeshes || !LODMod.saveMeshes) return; - - try { - File saveFile = getSavePath(saveDir, regionX, regionZ).toFile(); - saveFile.getParentFile().mkdirs(); - - NBTTagCompound oldNbt = null; - NBTTagList oldList = null; - List oldStringTable = null; - if(saveFile.exists()) { - oldNbt = CompressedStreamTools.readCompressed(new FileInputStream(saveFile)); - oldList = oldNbt.getTagList("chunks", NBT.TAG_COMPOUND);; - oldStringTable = Arrays.asList(oldNbt.getString("stringTable").split("\\n")); - } - - NBTTagCompound nbt = new NBTTagCompound(); - nbt.setByte("V", (byte)0); - nbt.setString("stringTable", String.join("\n", (List) ((TextureMap)Minecraft.getMinecraft().getTextureManager().getTexture(TextureMap.locationBlocksTexture)).mapUploadedSprites.keySet().stream().collect(Collectors.toList()))); - - NBTTagList list = new NBTTagList(); - - int idx = 0; - for(int i = 0; i < 32; i++) { - for(int j = 0; j < 32; j++) { - list.appendTag(data[i][j].saveToNBT(oldNbt == null ? null : oldList.getCompoundTagAt(idx++), - oldNbt == null? null : oldStringTable)); - } - } - nbt.setTag("chunks", list); - - new Thread( - new Runnable() { - - @Override - public void run() { - try { - CompressedStreamTools.writeCompressed(nbt, new FileOutputStream(saveFile)); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - }).start(); - - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - */ - public LODChunk getChunkAbsolute(int chunkXAbs, int chunkZAbs) { - return getChunk(chunkXAbs - regionX * 32, chunkZAbs - regionZ * 32); - } - - public LODChunk getChunk(int x, int z) { - if(x >= 0 && x < 32 && z >= 0 && z < 32) { - return data[x][z]; - } else { - return null; - } - } - - public LODChunk putChunk(Chunk chunk) { - int relX = chunk.xPosition - regionX * 32; - int relZ = chunk.zPosition - regionZ * 32; - - if(relX >= 0 && relX < 32 && relZ >= 0 && relZ < 32) { - data[relX][relZ].receiveChunk(chunk); - return data[relX][relZ]; - } - return null; - } - - public boolean tick(Entity player) { - int visibleChunks = 0; - for(int i = 0; i < 32; i++) { - for(int j = 0; j < 32; j++) { - LODChunk chunk = data[i][j]; - if(chunk != null) { - chunk.tick(player); - if(chunk.visible) { - visibleChunks++; - } - } - } - } - return visibleChunks > 0; - } - - public void destroy(Path saveDir) { - //save(saveDir); - for(int i = 0; i < 32; i++) { - for(int j = 0; j < 32; j++) { - LODChunk chunk = data[i][j]; - if(chunk != null) { - chunk.destroy(); - } - } - } - } - - public double distanceTaxicab(Entity entity) { - double centerX = ((regionX * 32) + 16) * 16; - double centerZ = ((regionZ * 32) + 16) * 16; - - return Math.max(Math.abs(centerX - entity.posX), Math.abs(centerZ - entity.posZ)); - - } - - @Override - public String toString() { - return "LODRegion(" + regionX + ", " + regionZ + ")"; - } - -} diff --git a/src/main/java/makamys/neodymium/renderer/LODRenderer.java b/src/main/java/makamys/neodymium/renderer/LODRenderer.java deleted file mode 100644 index 974c408..0000000 --- a/src/main/java/makamys/neodymium/renderer/LODRenderer.java +++ /dev/null @@ -1,730 +0,0 @@ -package makamys.neodymium.renderer; - -import net.minecraft.block.Block; -import net.minecraft.block.material.Material; -import net.minecraft.client.Minecraft; -import net.minecraft.client.particle.EntityFX; -import net.minecraft.client.renderer.WorldRenderer; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.renderer.texture.TextureMap; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.util.EnumFacing; -import net.minecraft.world.ChunkCoordIntPair; -import net.minecraft.world.IBlockAccess; -import net.minecraft.world.World; -import net.minecraft.world.biome.BiomeGenBase; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.gen.ChunkProviderServer; -import net.minecraftforge.event.world.ChunkEvent; - -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; - -import org.apache.commons.lang3.ArrayUtils; -import org.lwjgl.BufferUtils; -import org.lwjgl.input.Keyboard; -import org.lwjgl.opengl.GL11; -import org.lwjgl.util.vector.Matrix4f; - -import makamys.neodymium.LODMod; -import makamys.neodymium.ducks.IWorldRenderer; -import makamys.neodymium.renderer.Mesh.GPUStatus; -import makamys.neodymium.util.GuiHelper; -import makamys.neodymium.util.Util; - -import static org.lwjgl.opengl.GL11.*; -import static org.lwjgl.opengl.GL14.*; -import static org.lwjgl.opengl.GL15.*; -import static org.lwjgl.opengl.GL20.*; -import static org.lwjgl.opengl.GL30.*; - -public class LODRenderer { - - public boolean hasInited = false; - public boolean destroyPending; - - private boolean[] wasDown = new boolean[256]; - private int renderQuads = 0; - - public boolean renderWorld; - public boolean rendererActive; - private boolean showMemoryDebugger; - - private static int MAX_MESHES = 100000; - - private int VAO, shaderProgram; - private IntBuffer[] piFirst = new IntBuffer[2]; - private IntBuffer[] piCount = new IntBuffer[2]; - private List[] sentMeshes = (List[])new ArrayList[] {new ArrayList(), new ArrayList()}; - GPUMemoryManager mem; - - List myChunks = new ArrayList(); - List pendingLODChunks = new ArrayList<>(); - - private boolean hasServerInited = false; - private Map loadedRegionsMap = new HashMap<>(); - - public World world; - - // TODO make these packets to make this work on dedicated servers - Queue farChunks = new ConcurrentLinkedQueue<>(); - - List serverChunkLoadQueue = new ArrayList<>(); - - private double lastSortX = Double.NaN; - private double lastSortY = Double.NaN; - private double lastSortZ = Double.NaN; - - private long lastGCTime = -1; - private long lastSaveTime = -1; - private long gcInterval = 10 * 1000; - private long saveInterval = 60 * 1000; - - private int renderedMeshes; - private int frameCount; - - public int renderRange = 48; - - private boolean freezeMeshes; - - public LODRenderer(World world){ - this.world = world; - if(shouldRenderInWorld(world)) { - hasInited = init(); - } - - renderWorld = true; - rendererActive = true; - } - - public void preRenderSortedRenderers(int renderPass, double alpha, WorldRenderer[] sortedWorldRenderers) { - if(renderPass != 0) return; - - LODMod.fogEventWasPosted = false; - - renderedMeshes = 0; - - Minecraft.getMinecraft().entityRenderer.enableLightmap((double)alpha); - - if(hasInited) { - mainLoop(); - if(Minecraft.getMinecraft().currentScreen == null) { - handleKeyboard(); - } - if(frameCount % 2 == 0) { - mem.runGC(false); - } - lastGCTime = System.currentTimeMillis(); - if(lastSaveTime == -1 || (System.currentTimeMillis() - lastSaveTime) > saveInterval && LODMod.saveMeshes) { - onSave(); - lastSaveTime = System.currentTimeMillis(); - } - - if(rendererActive && renderWorld) { - if(frameCount % LODMod.sortFrequency == 0) { - sort(); - } - - updateMeshes(); - initIndexBuffers(); - render(alpha); - } - } - - frameCount++; - - Minecraft.getMinecraft().entityRenderer.disableLightmap((double)alpha); - } - - public void onRenderTickEnd() { - if(destroyPending) { - LODMod.renderer = null; - return; - } - if(showMemoryDebugger && mem != null) { - GuiHelper.begin(); - mem.drawInfo(); - GuiHelper.end(); - } - } - - private void sort() { - Entity player = Minecraft.getMinecraft().renderViewEntity; - for(List list : sentMeshes) { - list.sort(new MeshDistanceComparator(player.posX / 16, player.posY / 16, player.posZ / 16)); - } - } - - private void updateMeshes() { - for(List list : sentMeshes) { - for(Mesh mesh : list) { - mesh.update(); - } - } - } - - private void initIndexBuffers() { - for(int i = 0; i < 2; i++) { - piFirst[i].limit(sentMeshes[i].size()); - piCount[i].limit(sentMeshes[i].size()); - for(Mesh mesh : sentMeshes[i]) { - if(mesh.visible && (LODMod.maxMeshesPerFrame == -1 || renderedMeshes < LODMod.maxMeshesPerFrame)) { - renderedMeshes++; - piFirst[i].put(mesh.iFirst); - piCount[i].put(mesh.iCount); - } - } - piFirst[i].flip(); - piCount[i].flip(); - } - } - - private void mainLoop() { - while(!farChunks.isEmpty()) { - LODChunk lodChunk = receiveFarChunk(farChunks.remove()); - sendChunkToGPU(lodChunk); - } - - if(Minecraft.getMinecraft().playerController.netClientHandler.doneLoadingTerrain) { - Entity player = Minecraft.getMinecraft().renderViewEntity; - - List newServerChunkLoadQueue = new ArrayList<>(); - - if(Double.isNaN(lastSortX) || getLastSortDistanceSq(player) > 16 * 16) { - int centerX = (int)Math.floor(player.posX / 16.0); - int centerZ = (int)Math.floor(player.posZ / 16.0); - - for(int x = -renderRange; x <= renderRange; x++) { - for(int z = -renderRange; z <= renderRange; z++) { - if(x * x + z * z < renderRange * renderRange) { - int chunkX = centerX + x; - int chunkZ = centerZ + z; - - if(getLODChunk(chunkX, chunkZ).needsChunk) { - newServerChunkLoadQueue.add(new ChunkCoordIntPair(chunkX, chunkZ)); - getLODChunk(chunkX, chunkZ).needsChunk = false; - } - } - } - } - Collections.sort(newServerChunkLoadQueue, new ChunkCoordDistanceComparator(player.posX, player.posY, player.posZ)); - addToServerChunkLoadQueue(newServerChunkLoadQueue); - - lastSortX = player.posX; - lastSortY = player.posY; - lastSortZ = player.posZ; - for(Iterator it = loadedRegionsMap.keySet().iterator(); it.hasNext();) { - ChunkCoordIntPair k = it.next(); - LODRegion v = loadedRegionsMap.get(k); - - if(v.distanceTaxicab(player) > renderRange * 16 + 16 * 16) { - System.out.println("unloading " + v); - v.destroy(getSaveDir()); - it.remove(); - } else { - v.tick(player); - } - } - } - } - } - - public float getFarPlaneDistanceMultiplier() { - return (float)LODMod.farPlaneDistanceMultiplier; - } - - public void afterSetupFog(int mode, float alpha, float farPlaneDistance) { - EntityLivingBase entity = Minecraft.getMinecraft().renderViewEntity; - if(LODMod.fogEventWasPosted && !Minecraft.getMinecraft().theWorld.provider.doesXZShowFog((int)entity.posX, (int)entity.posZ)) { - GL11.glFogf(GL11.GL_FOG_START, mode < 0 ? 0 : farPlaneDistance * (float)LODMod.fogStart); - GL11.glFogf(GL11.GL_FOG_END, mode < 0 ? farPlaneDistance/4 : farPlaneDistance * (float)LODMod.fogEnd); - } - } - - private void handleKeyboard() { - if(LODMod.debugPrefix == 0 || (LODMod.debugPrefix != -1 && Keyboard.isKeyDown(LODMod.debugPrefix))) { - if(Keyboard.isKeyDown(Keyboard.KEY_F) && !wasDown[Keyboard.KEY_F]) { - rendererActive = !rendererActive; - } - if(Keyboard.isKeyDown(Keyboard.KEY_V) && !wasDown[Keyboard.KEY_V]) { - renderWorld = !renderWorld; - } - if(Keyboard.isKeyDown(Keyboard.KEY_R) && !wasDown[Keyboard.KEY_R]) { - loadShader(); - } - if(Keyboard.isKeyDown(Keyboard.KEY_M) && !wasDown[Keyboard.KEY_M]) { - showMemoryDebugger = !showMemoryDebugger; - //LODChunk chunk = getLODChunk(9, -18); - //setMeshVisible(chunk.chunkMeshes[7], false, true); - //freezeMeshes = false; - //chunk.chunkMeshes[7].quadCount = 256; - //setMeshVisible(chunk.chunkMeshes[7], true, true); - } - } - for(int i = 0; i < 256; i++) { - wasDown[i] = Keyboard.isKeyDown(i); - } - } - - FloatBuffer modelView = BufferUtils.createFloatBuffer(16); - FloatBuffer projBuf = BufferUtils.createFloatBuffer(16); - IntBuffer viewportBuf = BufferUtils.createIntBuffer(16); - FloatBuffer projInvBuf = BufferUtils.createFloatBuffer(16); - FloatBuffer fogColorBuf = BufferUtils.createFloatBuffer(16); - FloatBuffer fogStartEnd = BufferUtils.createFloatBuffer(2); - Matrix4f projMatrix = new Matrix4f(); - - private void render(double alpha) { - GL11.glPushAttrib(GL11.GL_ENABLE_BIT); - GL11.glDisable(GL11.GL_TEXTURE_2D); - - glUseProgram(shaderProgram); - - int u_modelView = glGetUniformLocation(shaderProgram, "modelView"); - int u_proj = glGetUniformLocation(shaderProgram, "proj"); - int u_playerPos = glGetUniformLocation(shaderProgram, "playerPos"); - int u_light = glGetUniformLocation(shaderProgram, "lightTex"); - int u_viewport = glGetUniformLocation(shaderProgram, "viewport"); - int u_projInv = glGetUniformLocation(shaderProgram, "projInv"); - int u_fogColor = glGetUniformLocation(shaderProgram, "fogColor"); - int u_fogStartEnd = glGetUniformLocation(shaderProgram, "fogStartEnd"); - - if(false && (u_modelView == -1 || u_proj == -1 || u_playerPos == -1 || u_light == -1 || u_viewport == -1 || u_projInv == -1 || u_fogColor == -1 || u_fogStartEnd == -1)) { - System.out.println("failed to get the uniform"); - } else { - glGetFloat(GL_MODELVIEW_MATRIX, modelView); - - glGetFloat(GL_PROJECTION_MATRIX, projBuf); - - glGetInteger(GL_VIEWPORT, viewportBuf); - - projMatrix.load(projBuf); - projBuf.flip(); - projMatrix.invert(); - projMatrix.store(projInvBuf); - projInvBuf.flip(); - - fogColorBuf.limit(16); - glGetFloat(GL_FOG_COLOR, fogColorBuf); - fogColorBuf.limit(4); - - fogStartEnd.put(glGetFloat(GL_FOG_START)); - fogStartEnd.put(glGetFloat(GL_FOG_END)); - fogStartEnd.flip(); - - glUniformMatrix4(u_modelView, false, modelView); - glUniformMatrix4(u_proj, false, projBuf); - glUniformMatrix4(u_projInv, false, projInvBuf); - glUniform4f(u_viewport, viewportBuf.get(0),viewportBuf.get(1),viewportBuf.get(2),viewportBuf.get(3)); - glUniform4(u_fogColor, fogColorBuf); - glUniform2(u_fogStartEnd, fogStartEnd); - - float originX = 0; - float originY = 0; - float originZ = 0; - - Entity rve = Minecraft.getMinecraft().renderViewEntity; - double interpX = rve.lastTickPosX + (rve.posX - rve.lastTickPosX) * alpha; - double interpY = rve.lastTickPosY + (rve.posY - rve.lastTickPosY) * alpha + rve.getEyeHeight(); - double interpZ = rve.lastTickPosZ + (rve.posZ - rve.lastTickPosZ) * alpha; - - glUniform3f(u_playerPos, (float)interpX - originX, (float)interpY - originY, (float)interpZ - originZ); - - glUniform1i(u_light, 1); - - modelView.position(0); - projBuf.position(0); - viewportBuf.position(0); - projInvBuf.position(0); - fogColorBuf.position(0); - fogStartEnd.position(0); - } - - glBindVertexArray(VAO); - GL11.glDisable(GL11.GL_BLEND); - glMultiDrawArrays(GL_TRIANGLES, piFirst[0], piCount[0]); - GL11.glEnable(GL11.GL_BLEND); - GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - glMultiDrawArrays(GL_TRIANGLES, piFirst[1], piCount[1]); - - glBindVertexArray(0); - glUseProgram(0); - - GL11.glDepthMask(true); - GL11.glPopAttrib(); - - - } - - public boolean init() { - Map uploadedSprites = ((TextureMap)Minecraft.getMinecraft().getTextureManager().getTexture(TextureMap.locationBlocksTexture)).mapUploadedSprites; - - loadShader(); - - VAO = glGenVertexArrays(); - glBindVertexArray(VAO); - - mem = new GPUMemoryManager(); - - glBindBuffer(GL_ARRAY_BUFFER, mem.VBO); - - int stride = 7 * 4; - - glVertexAttribPointer(0, 3, GL_FLOAT, false, stride, 0); - glVertexAttribPointer(1, 2, GL_FLOAT, false, stride, 3 * 4); - glVertexAttribPointer(2, 2, GL_SHORT, false, stride, 5 * 4); - glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, false, stride, 6 * 4); - - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glEnableVertexAttribArray(2); - glEnableVertexAttribArray(3); - - for(int i = 0; i < 2; i++) { - piFirst[i] = BufferUtils.createIntBuffer(MAX_MESHES); - piFirst[i].flip(); - piCount[i] = BufferUtils.createIntBuffer(MAX_MESHES); - piCount[i].flip(); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - - return true; - } - - private void loadShader() { - int vertexShader; - vertexShader = glCreateShader(GL_VERTEX_SHADER); - - glShaderSource(vertexShader, Util.readFile("shaders/chunk.vert")); - glCompileShader(vertexShader); - - if(glGetShaderi(vertexShader, GL_COMPILE_STATUS) == 0) { - System.out.println("Error compiling vertex shader: " + glGetShaderInfoLog(vertexShader, 256)); - } - - int fragmentShader; - fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - - glShaderSource(fragmentShader, Util.readFile(LODMod.enableFog ? "shaders/chunk_fog.frag" : "shaders/chunk.frag")); - glCompileShader(fragmentShader); - - if(glGetShaderi(fragmentShader, GL_COMPILE_STATUS) == 0) { - System.out.println("Error compiling fragment shader: " + glGetShaderInfoLog(fragmentShader, 256)); - } - - shaderProgram = glCreateProgram(); - glAttachShader(shaderProgram, vertexShader); - glAttachShader(shaderProgram, fragmentShader); - glLinkProgram(shaderProgram); - - if(glGetProgrami(shaderProgram, GL_LINK_STATUS) == 0) { - System.out.println("Error linking shader: " + glGetShaderInfoLog(shaderProgram, 256)); - } - - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); - } - - public void destroy() { - onSave(); - - glDeleteProgram(shaderProgram); - glDeleteVertexArrays(VAO); - mem.destroy(); - - SimpleChunkMesh.instances = 0; - SimpleChunkMesh.usedRAM = 0; - ChunkMesh.instances = 0; - ChunkMesh.usedRAM = 0; - } - - public void onWorldRendererChanged(WorldRenderer wr, WorldRendererChange change) { - int x = Math.floorDiv(wr.posX, 16); - int y = Math.floorDiv(wr.posY, 16); - int z = Math.floorDiv(wr.posZ, 16); - LODChunk lodChunk = getLODChunk(x, z); - - lodChunk.isSectionVisible[y] = change == WorldRendererChange.VISIBLE; - if(change == WorldRendererChange.DELETED) { - removeMesh(lodChunk.chunkMeshes[y]); - } - lodChunkChanged(lodChunk); - } - - public void onWorldRendererPost(WorldRenderer wr) { - if(LODMod.disableChunkMeshes) return; - - int x = Math.floorDiv(wr.posX, 16); - int y = Math.floorDiv(wr.posY, 16); - int z = Math.floorDiv(wr.posZ, 16); - - if(Minecraft.getMinecraft().theWorld.getChunkFromChunkCoords(x, z).isChunkLoaded) { - LODChunk lodChunk = getLODChunk(x, z); - lodChunk.isSectionVisible[y] = ((IWorldRenderer)wr).isDrawn(); - lodChunk.putChunkMeshes(y, ((IWorldRenderer)wr).getChunkMeshes()); - } - } - - private double getLastSortDistanceSq(Entity player) { - return Math.pow(lastSortX - player.posX, 2) + Math.pow(lastSortZ - player.posZ, 2); - } - - private synchronized void addToServerChunkLoadQueue(List coords) { - serverChunkLoadQueue.addAll(coords); - } - - private LODChunk receiveFarChunk(Chunk chunk) { - LODRegion region = getRegionContaining(chunk.xPosition, chunk.zPosition); - return region.putChunk(chunk); - } - - private LODChunk getLODChunk(int chunkX, int chunkZ) { - return getRegionContaining(chunkX, chunkZ).getChunkAbsolute(chunkX, chunkZ); - } - - public void onStopServer() { - - } - - public synchronized void serverTick() { - int chunkLoadsRemaining = LODMod.chunkLoadsPerTick; - while(!serverChunkLoadQueue.isEmpty() && chunkLoadsRemaining-- > 0) { - ChunkCoordIntPair coords = serverChunkLoadQueue.remove(0); - ChunkProviderServer chunkProviderServer = Minecraft.getMinecraft().getIntegratedServer().worldServerForDimension(world.provider.dimensionId).theChunkProviderServer; - Chunk chunk = chunkProviderServer.currentChunkProvider.provideChunk(coords.chunkXPos, coords.chunkZPos); - SimpleChunkMesh.prepareFarChunkOnServer(chunk); - farChunks.add(chunk); - } - } - - private LODRegion getRegionContaining(int chunkX, int chunkZ) { - ChunkCoordIntPair key = new ChunkCoordIntPair(Math.floorDiv(chunkX , 32), Math.floorDiv(chunkZ, 32)); - LODRegion region = loadedRegionsMap.get(key); - if(region == null) { - region = LODRegion.load(getSaveDir(), Math.floorDiv(chunkX , 32), Math.floorDiv(chunkZ , 32)); - loadedRegionsMap.put(key, region); - } - return region; - } - - private void sendChunkToGPU(LODChunk lodChunk) { - Entity player = Minecraft.getMinecraft().renderViewEntity; - - lodChunk.tick(player); - setVisible(lodChunk, true, true); - } - - public void setVisible(LODChunk chunk, boolean visible) { - setVisible(chunk, visible, false); - } - - public void setVisible(LODChunk lodChunk, boolean visible, boolean forceCheck) { - if(!forceCheck && visible == lodChunk.visible) return; - - lodChunk.visible = visible; - lodChunkChanged(lodChunk); - } - - public void lodChunkChanged(LODChunk lodChunk) { - int newLOD = (!lodChunk.hasChunkMeshes() && lodChunk.lod == 2) ? (LODMod.disableSimpleMeshes ? 0 : 1) : lodChunk.lod; - for(SimpleChunkMesh sm : lodChunk.simpleMeshes) { - if(sm != null) { - if(lodChunk.isFullyVisible() && newLOD == 1) { - if(!sm.visible) { - setMeshVisible(sm, true); - } - } else { - if(sm.visible) { - setMeshVisible(sm, false); - } - } - } - } - for(int y = 0; y < 16; y++) { - for(int pass = 0; pass < 2; pass++) { - ChunkMesh cm = lodChunk.chunkMeshes[y * 2 + pass]; - if(cm != null) { - if(lodChunk.isSectionVisible[y] && newLOD == 2) { - if(!cm.visible) { - setMeshVisible(cm, true); - } - } else { - if(cm.visible) { - setMeshVisible(cm, false); - } - } - } - } - } - } - - protected void setMeshVisible(Mesh mesh, boolean visible) { - setMeshVisible(mesh, visible, false); - } - - protected void setMeshVisible(Mesh mesh, boolean visible, boolean force) { - if((!force && freezeMeshes) || mesh == null) return; - - if(mesh.visible != visible) { - mesh.visible = visible; - - if(mesh.gpuStatus == GPUStatus.UNSENT) { - mem.sendMeshToGPU(mesh); - sentMeshes[mesh.pass].add(mesh); - } - } - } - - public void removeMesh(Mesh mesh) { - if(mesh == null) return; - - mem.deleteMeshFromGPU(mesh); - sentMeshes[mesh.pass].remove(mesh); - setMeshVisible(mesh, false); - } - - public Chunk getChunkFromChunkCoords(int x, int z) { - for(Chunk chunk : myChunks) { - if(chunk.xPosition == x && chunk.zPosition == z) { - return chunk; - } - } - return null; - } - - public boolean shouldSideBeRendered(Block block, IBlockAccess ba, int x, int y, int z, int w) { - EnumFacing facing = EnumFacing.values()[w]; - if(block.getMaterial() == Material.water && facing != EnumFacing.UP && facing != EnumFacing.DOWN && !Minecraft.getMinecraft().theWorld.getChunkFromBlockCoords(x, z).isChunkLoaded) { - return false; - } else { - return block.shouldSideBeRendered(ba, x, y, z, w); - } - } - - public List getDebugText() { - List text = new ArrayList<>(); - text.addAll(mem.getDebugText()); - text.addAll(Arrays.asList( - "Simple meshes: " + SimpleChunkMesh.instances + " (" + SimpleChunkMesh.usedRAM / 1024 / 1024 + "MB)", - "Full meshes: " + ChunkMesh.instances + " (" + ChunkMesh.usedRAM / 1024 / 1024 + "MB)", - "Total RAM used: " + ((SimpleChunkMesh.usedRAM + ChunkMesh.usedRAM) / 1024 / 1024) + " MB", - "Rendered: " + renderedMeshes - )); - return text; - } - - public void onSave() { - System.out.println("Saving LOD regions..."); - long t0 = System.currentTimeMillis(); - //loadedRegionsMap.forEach((k, v) -> v.save(getSaveDir())); - System.out.println("Finished saving LOD regions in " + ((System.currentTimeMillis() - t0) / 1000.0) + "s"); - } - - public void onChunkLoad(ChunkEvent.Load event) { - farChunks.add(event.getChunk()); - } - - private Path getSaveDir(){ - return Minecraft.getMinecraft().mcDataDir.toPath().resolve("lodmod").resolve(Minecraft.getMinecraft().getIntegratedServer().getFolderName()); - } - - private boolean shouldRenderInWorld(World world) { - return world != null && !world.provider.isHellWorld; - } - - public static class LODChunkComparator implements Comparator { - Entity player; - - public LODChunkComparator(Entity player) { - this.player = player; - } - - @Override - public int compare(LODChunk p1, LODChunk p2) { - int distSq1 = distSq(p1); - int distSq2 = distSq(p2); - return distSq1 < distSq2 ? -1 : distSq1 > distSq2 ? 1 : 0; - } - - int distSq(LODChunk p) { - return (int)( - Math.pow(((p.x * 16) - player.chunkCoordX), 2) + - Math.pow(((p.z * 16) - player.chunkCoordZ), 2) - ); - } - } - - public static class ChunkCoordDistanceComparator implements Comparator { - double x, y, z; - - public ChunkCoordDistanceComparator(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public int compare(ChunkCoordIntPair p1, ChunkCoordIntPair p2) { - int distSq1 = distSq(p1); - int distSq2 = distSq(p2); - return distSq1 < distSq2 ? -1 : distSq1 > distSq2 ? 1 : 0; - } - - int distSq(ChunkCoordIntPair p) { - return (int)( - Math.pow(((p.chunkXPos * 16) - x), 2) + - Math.pow(((p.chunkZPos * 16) - z), 2) - ); - } - } - - public static class MeshDistanceComparator implements Comparator { - double x, y, z; - - MeshDistanceComparator(double x, double y, double z){ - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public int compare(Mesh a, Mesh b) { - if(a.pass < b.pass) { - return -1; - } else if(a.pass > b.pass) { - return 1; - } else { - double distSqA = a.distSq(x, y, z); - double distSqB = b.distSq(x, y, z); - if(distSqA > distSqB) { - return 1; - } else if(distSqA < distSqB) { - return -1; - } else { - return 0; - } - } - } - - } - - public static enum WorldRendererChange { - VISIBLE, INVISIBLE, DELETED - } -} \ No newline at end of file diff --git a/src/main/java/makamys/neodymium/renderer/NeoChunk.java b/src/main/java/makamys/neodymium/renderer/NeoChunk.java new file mode 100644 index 0000000..1184d84 --- /dev/null +++ b/src/main/java/makamys/neodymium/renderer/NeoChunk.java @@ -0,0 +1,184 @@ +package makamys.neodymium.renderer; + +import java.util.List; + +import makamys.neodymium.Neodymium; +import net.minecraft.entity.Entity; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagEnd; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.world.chunk.Chunk; + +public class NeoChunk { + + int x, z; + public boolean needsChunk = true; + int lod = 0; + boolean visible; + boolean dirty; + boolean discardedMesh; + + SimpleChunkMesh[] simpleMeshes = new SimpleChunkMesh[2]; + ChunkMesh[] chunkMeshes = new ChunkMesh[32]; + + public boolean[] isSectionVisible = new boolean[16]; + + NeoRenderer renderer = Neodymium.renderer; + + public NeoChunk(int x, int z) { + this.x = x; + this.z = z; + } + /* + public LODChunk(NBTTagCompound nbt, List spriteList) { + this.x = nbt.getInteger("x"); + this.z = nbt.getInteger("z"); + + loadChunkMeshesNBT(nbt.getCompoundTag("chunkMeshes"), spriteList); + } + + private void loadChunkMeshesNBT(NBTTagCompound chunkMeshesCompound, List spriteList) { + for(Object o : chunkMeshesCompound.func_150296_c()) { + String key = (String)o; + int keyInt = Integer.parseInt(key); + + byte[] data = chunkMeshesCompound.getByteArray(key); + + chunkMeshes[keyInt] = new ChunkMesh(x, keyInt / 2, z, new ChunkMesh.Flags(true, true, true, false), data.length / (2 + 4 * (3 + 2 + 2 + 4)), data, spriteList, keyInt % 2); + } + } + */ + @Override + public String toString() { + return "LODChunk(" + x + ", " + z + ")"; + } + + public double distSq(Entity entity) { + return Math.pow(entity.posX - x * 16, 2) + Math.pow(entity.posZ - z * 16, 2); + } + + public void putChunkMeshes(int cy, List newChunkMeshes) { + for(int i = 0; i < 2; i++) { + ChunkMesh newChunkMesh = newChunkMeshes.size() > i ? newChunkMeshes.get(i) : null; + if(chunkMeshes[cy * 2 + i] != null) { + if(newChunkMesh != null) { + newChunkMesh.pass = i; + } + + renderer.removeMesh(chunkMeshes[cy * 2 + i]); + chunkMeshes[cy * 2 + i].destroy(); + } + chunkMeshes[cy * 2 + i] = newChunkMesh; + } + Neodymium.renderer.lodChunkChanged(this); + dirty = true; + discardedMesh = false; + } + + // nice copypasta + public void putSimpleMeshes(List newSimpleMeshes) { + for(int i = 0; i < 2; i++) { + SimpleChunkMesh newSimpleMesh = newSimpleMeshes.size() > i ? newSimpleMeshes.get(i) : null; + if(simpleMeshes[i] != null) { + if(newSimpleMesh != null) { + newSimpleMesh.pass = i; + } + + renderer.setMeshVisible(simpleMeshes[i], false); + simpleMeshes[i].destroy(); + } + simpleMeshes[i] = newSimpleMesh; + } + Neodymium.renderer.lodChunkChanged(this); + } + + public boolean hasChunkMeshes() { + for(ChunkMesh cm : chunkMeshes) { + if(cm != null) { + return true; + } + } + return false; + } + + public void tick(Entity player) { + double distSq = distSq(player); + if(Neodymium.disableSimpleMeshes || distSq < Math.pow((Neodymium.renderer.renderRange / 2) * 16, 2)) { + setLOD(2); + } else if(distSq < Math.pow((Neodymium.renderer.renderRange) * 16, 2)) { + setLOD(1); + } else { + setLOD(0); + } + } + + public void setLOD(int lod) { + if(lod == this.lod) return; + + this.lod = lod; + Neodymium.renderer.lodChunkChanged(this); + if(!dirty) { + if(lod < 2) { + for(int i = 0; i < chunkMeshes.length; i++) { + if(chunkMeshes[i] != null) { + chunkMeshes[i].destroy(); + chunkMeshes[i] = null; + discardedMesh = true; + } + } + } + } + } + /* + public NBTTagCompound saveToNBT(NBTTagCompound oldNbt, List oldStringTable) { + NBTTagCompound nbt = new NBTTagCompound(); + nbt.setInteger("x", x); + nbt.setInteger("z", z); + + NBTTagCompound chunkMeshesCompound = oldNbt == null ? new NBTTagCompound() : oldNbt.getCompoundTag("chunkMeshes"); + if(!discardedMesh) { + for(int i = 0; i < chunkMeshes.length; i++) { + if(chunkMeshes[i] != null) { + chunkMeshesCompound.setTag(String.valueOf(i), chunkMeshes[i].nbtData); + } + } + } else if(oldNbt != null && discardedMesh && lod == 2) { + loadChunkMeshesNBT(chunkMeshesCompound, oldStringTable); + Neodymium.renderer.lodChunkChanged(this); + } + nbt.setTag("chunkMeshes", chunkMeshesCompound); + dirty = false; + return nbt; + } + */ + public void destroy() { + for(SimpleChunkMesh scm: simpleMeshes) { + if(scm != null) { + scm.destroy(); + } + } + for(ChunkMesh cm: chunkMeshes) { + if(cm != null) { + cm.destroy(); + } + } + Neodymium.renderer.setVisible(this, false); + } + + public void receiveChunk(Chunk chunk) { + if(!Neodymium.disableSimpleMeshes) { + putSimpleMeshes(SimpleChunkMesh.generateSimpleMeshes(chunk)); + } + } + + public boolean isFullyVisible() { + if(!visible) return false; + for(boolean b : isSectionVisible) { + if(!b) { + return false; + } + } + return true; + } + +} diff --git a/src/main/java/makamys/neodymium/renderer/NeoRegion.java b/src/main/java/makamys/neodymium/renderer/NeoRegion.java new file mode 100644 index 0000000..c670525 --- /dev/null +++ b/src/main/java/makamys/neodymium/renderer/NeoRegion.java @@ -0,0 +1,195 @@ +package makamys.neodymium.renderer; + +import java.io.BufferedOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import makamys.neodymium.Neodymium; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.entity.Entity; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.world.chunk.Chunk; +import net.minecraftforge.common.util.Constants.NBT; + +public class NeoRegion { + + private NeoChunk[][] data = new NeoChunk[32][32]; + + int regionX, regionZ; + + public NeoRegion(int regionX, int regionZ) { + this.regionX = regionX; + this.regionZ = regionZ; + + for(int i = 0; i < 32; i++) { + for(int j = 0; j < 32; j++) { + data[i][j] = new NeoChunk(regionX * 32 + i, regionZ * 32 + j); + } + } + } + /* + public LODRegion(int regionX, int regionZ, NBTTagCompound nbt) { + this.regionX = regionX; + this.regionZ = regionZ; + + NBTTagList list = nbt.getTagList("chunks", NBT.TAG_COMPOUND); + List stringTable = Arrays.asList(nbt.getString("stringTable").split("\\n")); + + int idx = 0; + for(int i = 0; i < 32; i++) { + for(int j = 0; j < 32; j++) { + data[i][j] = new LODChunk(list.getCompoundTagAt(idx++), stringTable); + if(data[i][j].hasChunkMeshes()) { + Neodymium.renderer.setVisible(data[i][j], true); + } + } + } + } + */ + public static NeoRegion load(Path saveDir, int regionX, int regionZ) { + /*if(!(.disableChunkMeshes || !.saveMeshes)) { + File saveFile = getSavePath(saveDir, regionX, regionZ).toFile(); + if(saveFile.exists()) { + try { + NBTTagCompound nbt = CompressedStreamTools.readCompressed(new FileInputStream(saveFile)); + return new LODRegion(regionX, regionZ, nbt); + } catch (Exception e) { + e.printStackTrace(); + } + } + }*/ + return new NeoRegion(regionX, regionZ); + } + /* + private static Path getSavePath(Path saveDir, int regionX, int regionZ) { + return saveDir.resolve("lod").resolve(regionX + "," + regionZ + ".lod"); + } + + public void save(Path saveDir) { + if(.disableChunkMeshes || !.saveMeshes) return; + + try { + File saveFile = getSavePath(saveDir, regionX, regionZ).toFile(); + saveFile.getParentFile().mkdirs(); + + NBTTagCompound oldNbt = null; + NBTTagList oldList = null; + List oldStringTable = null; + if(saveFile.exists()) { + oldNbt = CompressedStreamTools.readCompressed(new FileInputStream(saveFile)); + oldList = oldNbt.getTagList("chunks", NBT.TAG_COMPOUND);; + oldStringTable = Arrays.asList(oldNbt.getString("stringTable").split("\\n")); + } + + NBTTagCompound nbt = new NBTTagCompound(); + nbt.setByte("V", (byte)0); + nbt.setString("stringTable", String.join("\n", (List) ((TextureMap)Minecraft.getMinecraft().getTextureManager().getTexture(TextureMap.locationBlocksTexture)).mapUploadedSprites.keySet().stream().collect(Collectors.toList()))); + + NBTTagList list = new NBTTagList(); + + int idx = 0; + for(int i = 0; i < 32; i++) { + for(int j = 0; j < 32; j++) { + list.appendTag(data[i][j].saveToNBT(oldNbt == null ? null : oldList.getCompoundTagAt(idx++), + oldNbt == null? null : oldStringTable)); + } + } + nbt.setTag("chunks", list); + + new Thread( + new Runnable() { + + @Override + public void run() { + try { + CompressedStreamTools.writeCompressed(nbt, new FileOutputStream(saveFile)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }).start(); + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + */ + public NeoChunk getChunkAbsolute(int chunkXAbs, int chunkZAbs) { + return getChunk(chunkXAbs - regionX * 32, chunkZAbs - regionZ * 32); + } + + public NeoChunk getChunk(int x, int z) { + if(x >= 0 && x < 32 && z >= 0 && z < 32) { + return data[x][z]; + } else { + return null; + } + } + + public NeoChunk putChunk(Chunk chunk) { + int relX = chunk.xPosition - regionX * 32; + int relZ = chunk.zPosition - regionZ * 32; + + if(relX >= 0 && relX < 32 && relZ >= 0 && relZ < 32) { + data[relX][relZ].receiveChunk(chunk); + return data[relX][relZ]; + } + return null; + } + + public boolean tick(Entity player) { + int visibleChunks = 0; + for(int i = 0; i < 32; i++) { + for(int j = 0; j < 32; j++) { + NeoChunk chunk = data[i][j]; + if(chunk != null) { + chunk.tick(player); + if(chunk.visible) { + visibleChunks++; + } + } + } + } + return visibleChunks > 0; + } + + public void destroy(Path saveDir) { + //save(saveDir); + for(int i = 0; i < 32; i++) { + for(int j = 0; j < 32; j++) { + NeoChunk chunk = data[i][j]; + if(chunk != null) { + chunk.destroy(); + } + } + } + } + + public double distanceTaxicab(Entity entity) { + double centerX = ((regionX * 32) + 16) * 16; + double centerZ = ((regionZ * 32) + 16) * 16; + + return Math.max(Math.abs(centerX - entity.posX), Math.abs(centerZ - entity.posZ)); + + } + + @Override + public String toString() { + return "LODRegion(" + regionX + ", " + regionZ + ")"; + } + +} diff --git a/src/main/java/makamys/neodymium/renderer/NeoRenderer.java b/src/main/java/makamys/neodymium/renderer/NeoRenderer.java new file mode 100644 index 0000000..708a9db --- /dev/null +++ b/src/main/java/makamys/neodymium/renderer/NeoRenderer.java @@ -0,0 +1,730 @@ +package makamys.neodymium.renderer; + +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.gen.ChunkProviderServer; +import net.minecraftforge.event.world.ChunkEvent; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.apache.commons.lang3.ArrayUtils; +import org.lwjgl.BufferUtils; +import org.lwjgl.input.Keyboard; +import org.lwjgl.opengl.GL11; +import org.lwjgl.util.vector.Matrix4f; + +import makamys.neodymium.Neodymium; +import makamys.neodymium.ducks.IWorldRenderer; +import makamys.neodymium.renderer.Mesh.GPUStatus; +import makamys.neodymium.util.GuiHelper; +import makamys.neodymium.util.Util; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL14.*; +import static org.lwjgl.opengl.GL15.*; +import static org.lwjgl.opengl.GL20.*; +import static org.lwjgl.opengl.GL30.*; + +public class NeoRenderer { + + public boolean hasInited = false; + public boolean destroyPending; + + private boolean[] wasDown = new boolean[256]; + private int renderQuads = 0; + + public boolean renderWorld; + public boolean rendererActive; + private boolean showMemoryDebugger; + + private static int MAX_MESHES = 100000; + + private int VAO, shaderProgram; + private IntBuffer[] piFirst = new IntBuffer[2]; + private IntBuffer[] piCount = new IntBuffer[2]; + private List[] sentMeshes = (List[])new ArrayList[] {new ArrayList(), new ArrayList()}; + GPUMemoryManager mem; + + List myChunks = new ArrayList(); + List pendingLODChunks = new ArrayList<>(); + + private boolean hasServerInited = false; + private Map loadedRegionsMap = new HashMap<>(); + + public World world; + + // TODO make these packets to make this work on dedicated servers + Queue farChunks = new ConcurrentLinkedQueue<>(); + + List serverChunkLoadQueue = new ArrayList<>(); + + private double lastSortX = Double.NaN; + private double lastSortY = Double.NaN; + private double lastSortZ = Double.NaN; + + private long lastGCTime = -1; + private long lastSaveTime = -1; + private long gcInterval = 10 * 1000; + private long saveInterval = 60 * 1000; + + private int renderedMeshes; + private int frameCount; + + public int renderRange = 48; + + private boolean freezeMeshes; + + public NeoRenderer(World world){ + this.world = world; + if(shouldRenderInWorld(world)) { + hasInited = init(); + } + + renderWorld = true; + rendererActive = true; + } + + public void preRenderSortedRenderers(int renderPass, double alpha, WorldRenderer[] sortedWorldRenderers) { + if(renderPass != 0) return; + + Neodymium.fogEventWasPosted = false; + + renderedMeshes = 0; + + Minecraft.getMinecraft().entityRenderer.enableLightmap((double)alpha); + + if(hasInited) { + mainLoop(); + if(Minecraft.getMinecraft().currentScreen == null) { + handleKeyboard(); + } + if(frameCount % 2 == 0) { + mem.runGC(false); + } + lastGCTime = System.currentTimeMillis(); + if(lastSaveTime == -1 || (System.currentTimeMillis() - lastSaveTime) > saveInterval && Neodymium.saveMeshes) { + onSave(); + lastSaveTime = System.currentTimeMillis(); + } + + if(rendererActive && renderWorld) { + if(frameCount % Neodymium.sortFrequency == 0) { + sort(); + } + + updateMeshes(); + initIndexBuffers(); + render(alpha); + } + } + + frameCount++; + + Minecraft.getMinecraft().entityRenderer.disableLightmap((double)alpha); + } + + public void onRenderTickEnd() { + if(destroyPending) { + Neodymium.renderer = null; + return; + } + if(showMemoryDebugger && mem != null) { + GuiHelper.begin(); + mem.drawInfo(); + GuiHelper.end(); + } + } + + private void sort() { + Entity player = Minecraft.getMinecraft().renderViewEntity; + for(List list : sentMeshes) { + list.sort(new MeshDistanceComparator(player.posX / 16, player.posY / 16, player.posZ / 16)); + } + } + + private void updateMeshes() { + for(List list : sentMeshes) { + for(Mesh mesh : list) { + mesh.update(); + } + } + } + + private void initIndexBuffers() { + for(int i = 0; i < 2; i++) { + piFirst[i].limit(sentMeshes[i].size()); + piCount[i].limit(sentMeshes[i].size()); + for(Mesh mesh : sentMeshes[i]) { + if(mesh.visible && (Neodymium.maxMeshesPerFrame == -1 || renderedMeshes < Neodymium.maxMeshesPerFrame)) { + renderedMeshes++; + piFirst[i].put(mesh.iFirst); + piCount[i].put(mesh.iCount); + } + } + piFirst[i].flip(); + piCount[i].flip(); + } + } + + private void mainLoop() { + while(!farChunks.isEmpty()) { + NeoChunk lodChunk = receiveFarChunk(farChunks.remove()); + sendChunkToGPU(lodChunk); + } + + if(Minecraft.getMinecraft().playerController.netClientHandler.doneLoadingTerrain) { + Entity player = Minecraft.getMinecraft().renderViewEntity; + + List newServerChunkLoadQueue = new ArrayList<>(); + + if(Double.isNaN(lastSortX) || getLastSortDistanceSq(player) > 16 * 16) { + int centerX = (int)Math.floor(player.posX / 16.0); + int centerZ = (int)Math.floor(player.posZ / 16.0); + + for(int x = -renderRange; x <= renderRange; x++) { + for(int z = -renderRange; z <= renderRange; z++) { + if(x * x + z * z < renderRange * renderRange) { + int chunkX = centerX + x; + int chunkZ = centerZ + z; + + if(getLODChunk(chunkX, chunkZ).needsChunk) { + newServerChunkLoadQueue.add(new ChunkCoordIntPair(chunkX, chunkZ)); + getLODChunk(chunkX, chunkZ).needsChunk = false; + } + } + } + } + Collections.sort(newServerChunkLoadQueue, new ChunkCoordDistanceComparator(player.posX, player.posY, player.posZ)); + addToServerChunkLoadQueue(newServerChunkLoadQueue); + + lastSortX = player.posX; + lastSortY = player.posY; + lastSortZ = player.posZ; + for(Iterator it = loadedRegionsMap.keySet().iterator(); it.hasNext();) { + ChunkCoordIntPair k = it.next(); + NeoRegion v = loadedRegionsMap.get(k); + + if(v.distanceTaxicab(player) > renderRange * 16 + 16 * 16) { + System.out.println("unloading " + v); + v.destroy(getSaveDir()); + it.remove(); + } else { + v.tick(player); + } + } + } + } + } + + public float getFarPlaneDistanceMultiplier() { + return (float)Neodymium.farPlaneDistanceMultiplier; + } + + public void afterSetupFog(int mode, float alpha, float farPlaneDistance) { + EntityLivingBase entity = Minecraft.getMinecraft().renderViewEntity; + if(Neodymium.fogEventWasPosted && !Minecraft.getMinecraft().theWorld.provider.doesXZShowFog((int)entity.posX, (int)entity.posZ)) { + GL11.glFogf(GL11.GL_FOG_START, mode < 0 ? 0 : farPlaneDistance * (float)Neodymium.fogStart); + GL11.glFogf(GL11.GL_FOG_END, mode < 0 ? farPlaneDistance/4 : farPlaneDistance * (float)Neodymium.fogEnd); + } + } + + private void handleKeyboard() { + if(Neodymium.debugPrefix == 0 || (Neodymium.debugPrefix != -1 && Keyboard.isKeyDown(Neodymium.debugPrefix))) { + if(Keyboard.isKeyDown(Keyboard.KEY_F) && !wasDown[Keyboard.KEY_F]) { + rendererActive = !rendererActive; + } + if(Keyboard.isKeyDown(Keyboard.KEY_V) && !wasDown[Keyboard.KEY_V]) { + renderWorld = !renderWorld; + } + if(Keyboard.isKeyDown(Keyboard.KEY_R) && !wasDown[Keyboard.KEY_R]) { + loadShader(); + } + if(Keyboard.isKeyDown(Keyboard.KEY_M) && !wasDown[Keyboard.KEY_M]) { + showMemoryDebugger = !showMemoryDebugger; + //LODChunk chunk = getLODChunk(9, -18); + //setMeshVisible(chunk.chunkMeshes[7], false, true); + //freezeMeshes = false; + //chunk.chunkMeshes[7].quadCount = 256; + //setMeshVisible(chunk.chunkMeshes[7], true, true); + } + } + for(int i = 0; i < 256; i++) { + wasDown[i] = Keyboard.isKeyDown(i); + } + } + + FloatBuffer modelView = BufferUtils.createFloatBuffer(16); + FloatBuffer projBuf = BufferUtils.createFloatBuffer(16); + IntBuffer viewportBuf = BufferUtils.createIntBuffer(16); + FloatBuffer projInvBuf = BufferUtils.createFloatBuffer(16); + FloatBuffer fogColorBuf = BufferUtils.createFloatBuffer(16); + FloatBuffer fogStartEnd = BufferUtils.createFloatBuffer(2); + Matrix4f projMatrix = new Matrix4f(); + + private void render(double alpha) { + GL11.glPushAttrib(GL11.GL_ENABLE_BIT); + GL11.glDisable(GL11.GL_TEXTURE_2D); + + glUseProgram(shaderProgram); + + int u_modelView = glGetUniformLocation(shaderProgram, "modelView"); + int u_proj = glGetUniformLocation(shaderProgram, "proj"); + int u_playerPos = glGetUniformLocation(shaderProgram, "playerPos"); + int u_light = glGetUniformLocation(shaderProgram, "lightTex"); + int u_viewport = glGetUniformLocation(shaderProgram, "viewport"); + int u_projInv = glGetUniformLocation(shaderProgram, "projInv"); + int u_fogColor = glGetUniformLocation(shaderProgram, "fogColor"); + int u_fogStartEnd = glGetUniformLocation(shaderProgram, "fogStartEnd"); + + if(false && (u_modelView == -1 || u_proj == -1 || u_playerPos == -1 || u_light == -1 || u_viewport == -1 || u_projInv == -1 || u_fogColor == -1 || u_fogStartEnd == -1)) { + System.out.println("failed to get the uniform"); + } else { + glGetFloat(GL_MODELVIEW_MATRIX, modelView); + + glGetFloat(GL_PROJECTION_MATRIX, projBuf); + + glGetInteger(GL_VIEWPORT, viewportBuf); + + projMatrix.load(projBuf); + projBuf.flip(); + projMatrix.invert(); + projMatrix.store(projInvBuf); + projInvBuf.flip(); + + fogColorBuf.limit(16); + glGetFloat(GL_FOG_COLOR, fogColorBuf); + fogColorBuf.limit(4); + + fogStartEnd.put(glGetFloat(GL_FOG_START)); + fogStartEnd.put(glGetFloat(GL_FOG_END)); + fogStartEnd.flip(); + + glUniformMatrix4(u_modelView, false, modelView); + glUniformMatrix4(u_proj, false, projBuf); + glUniformMatrix4(u_projInv, false, projInvBuf); + glUniform4f(u_viewport, viewportBuf.get(0),viewportBuf.get(1),viewportBuf.get(2),viewportBuf.get(3)); + glUniform4(u_fogColor, fogColorBuf); + glUniform2(u_fogStartEnd, fogStartEnd); + + float originX = 0; + float originY = 0; + float originZ = 0; + + Entity rve = Minecraft.getMinecraft().renderViewEntity; + double interpX = rve.lastTickPosX + (rve.posX - rve.lastTickPosX) * alpha; + double interpY = rve.lastTickPosY + (rve.posY - rve.lastTickPosY) * alpha + rve.getEyeHeight(); + double interpZ = rve.lastTickPosZ + (rve.posZ - rve.lastTickPosZ) * alpha; + + glUniform3f(u_playerPos, (float)interpX - originX, (float)interpY - originY, (float)interpZ - originZ); + + glUniform1i(u_light, 1); + + modelView.position(0); + projBuf.position(0); + viewportBuf.position(0); + projInvBuf.position(0); + fogColorBuf.position(0); + fogStartEnd.position(0); + } + + glBindVertexArray(VAO); + GL11.glDisable(GL11.GL_BLEND); + glMultiDrawArrays(GL_TRIANGLES, piFirst[0], piCount[0]); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + glMultiDrawArrays(GL_TRIANGLES, piFirst[1], piCount[1]); + + glBindVertexArray(0); + glUseProgram(0); + + GL11.glDepthMask(true); + GL11.glPopAttrib(); + + + } + + public boolean init() { + Map uploadedSprites = ((TextureMap)Minecraft.getMinecraft().getTextureManager().getTexture(TextureMap.locationBlocksTexture)).mapUploadedSprites; + + loadShader(); + + VAO = glGenVertexArrays(); + glBindVertexArray(VAO); + + mem = new GPUMemoryManager(); + + glBindBuffer(GL_ARRAY_BUFFER, mem.VBO); + + int stride = 7 * 4; + + glVertexAttribPointer(0, 3, GL_FLOAT, false, stride, 0); + glVertexAttribPointer(1, 2, GL_FLOAT, false, stride, 3 * 4); + glVertexAttribPointer(2, 2, GL_SHORT, false, stride, 5 * 4); + glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, false, stride, 6 * 4); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + + for(int i = 0; i < 2; i++) { + piFirst[i] = BufferUtils.createIntBuffer(MAX_MESHES); + piFirst[i].flip(); + piCount[i] = BufferUtils.createIntBuffer(MAX_MESHES); + piCount[i].flip(); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + return true; + } + + private void loadShader() { + int vertexShader; + vertexShader = glCreateShader(GL_VERTEX_SHADER); + + glShaderSource(vertexShader, Util.readFile("shaders/chunk.vert")); + glCompileShader(vertexShader); + + if(glGetShaderi(vertexShader, GL_COMPILE_STATUS) == 0) { + System.out.println("Error compiling vertex shader: " + glGetShaderInfoLog(vertexShader, 256)); + } + + int fragmentShader; + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource(fragmentShader, Util.readFile(Neodymium.enableFog ? "shaders/chunk_fog.frag" : "shaders/chunk.frag")); + glCompileShader(fragmentShader); + + if(glGetShaderi(fragmentShader, GL_COMPILE_STATUS) == 0) { + System.out.println("Error compiling fragment shader: " + glGetShaderInfoLog(fragmentShader, 256)); + } + + shaderProgram = glCreateProgram(); + glAttachShader(shaderProgram, vertexShader); + glAttachShader(shaderProgram, fragmentShader); + glLinkProgram(shaderProgram); + + if(glGetProgrami(shaderProgram, GL_LINK_STATUS) == 0) { + System.out.println("Error linking shader: " + glGetShaderInfoLog(shaderProgram, 256)); + } + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + } + + public void destroy() { + onSave(); + + glDeleteProgram(shaderProgram); + glDeleteVertexArrays(VAO); + mem.destroy(); + + SimpleChunkMesh.instances = 0; + SimpleChunkMesh.usedRAM = 0; + ChunkMesh.instances = 0; + ChunkMesh.usedRAM = 0; + } + + public void onWorldRendererChanged(WorldRenderer wr, WorldRendererChange change) { + int x = Math.floorDiv(wr.posX, 16); + int y = Math.floorDiv(wr.posY, 16); + int z = Math.floorDiv(wr.posZ, 16); + NeoChunk lodChunk = getLODChunk(x, z); + + lodChunk.isSectionVisible[y] = change == WorldRendererChange.VISIBLE; + if(change == WorldRendererChange.DELETED) { + removeMesh(lodChunk.chunkMeshes[y]); + } + lodChunkChanged(lodChunk); + } + + public void onWorldRendererPost(WorldRenderer wr) { + if(Neodymium.disableChunkMeshes) return; + + int x = Math.floorDiv(wr.posX, 16); + int y = Math.floorDiv(wr.posY, 16); + int z = Math.floorDiv(wr.posZ, 16); + + if(Minecraft.getMinecraft().theWorld.getChunkFromChunkCoords(x, z).isChunkLoaded) { + NeoChunk lodChunk = getLODChunk(x, z); + lodChunk.isSectionVisible[y] = ((IWorldRenderer)wr).isDrawn(); + lodChunk.putChunkMeshes(y, ((IWorldRenderer)wr).getChunkMeshes()); + } + } + + private double getLastSortDistanceSq(Entity player) { + return Math.pow(lastSortX - player.posX, 2) + Math.pow(lastSortZ - player.posZ, 2); + } + + private synchronized void addToServerChunkLoadQueue(List coords) { + serverChunkLoadQueue.addAll(coords); + } + + private NeoChunk receiveFarChunk(Chunk chunk) { + NeoRegion region = getRegionContaining(chunk.xPosition, chunk.zPosition); + return region.putChunk(chunk); + } + + private NeoChunk getLODChunk(int chunkX, int chunkZ) { + return getRegionContaining(chunkX, chunkZ).getChunkAbsolute(chunkX, chunkZ); + } + + public void onStopServer() { + + } + + public synchronized void serverTick() { + int chunkLoadsRemaining = Neodymium.chunkLoadsPerTick; + while(!serverChunkLoadQueue.isEmpty() && chunkLoadsRemaining-- > 0) { + ChunkCoordIntPair coords = serverChunkLoadQueue.remove(0); + ChunkProviderServer chunkProviderServer = Minecraft.getMinecraft().getIntegratedServer().worldServerForDimension(world.provider.dimensionId).theChunkProviderServer; + Chunk chunk = chunkProviderServer.currentChunkProvider.provideChunk(coords.chunkXPos, coords.chunkZPos); + SimpleChunkMesh.prepareFarChunkOnServer(chunk); + farChunks.add(chunk); + } + } + + private NeoRegion getRegionContaining(int chunkX, int chunkZ) { + ChunkCoordIntPair key = new ChunkCoordIntPair(Math.floorDiv(chunkX , 32), Math.floorDiv(chunkZ, 32)); + NeoRegion region = loadedRegionsMap.get(key); + if(region == null) { + region = NeoRegion.load(getSaveDir(), Math.floorDiv(chunkX , 32), Math.floorDiv(chunkZ , 32)); + loadedRegionsMap.put(key, region); + } + return region; + } + + private void sendChunkToGPU(NeoChunk lodChunk) { + Entity player = Minecraft.getMinecraft().renderViewEntity; + + lodChunk.tick(player); + setVisible(lodChunk, true, true); + } + + public void setVisible(NeoChunk chunk, boolean visible) { + setVisible(chunk, visible, false); + } + + public void setVisible(NeoChunk lodChunk, boolean visible, boolean forceCheck) { + if(!forceCheck && visible == lodChunk.visible) return; + + lodChunk.visible = visible; + lodChunkChanged(lodChunk); + } + + public void lodChunkChanged(NeoChunk lodChunk) { + int newLOD = (!lodChunk.hasChunkMeshes() && lodChunk.lod == 2) ? (Neodymium.disableSimpleMeshes ? 0 : 1) : lodChunk.lod; + for(SimpleChunkMesh sm : lodChunk.simpleMeshes) { + if(sm != null) { + if(lodChunk.isFullyVisible() && newLOD == 1) { + if(!sm.visible) { + setMeshVisible(sm, true); + } + } else { + if(sm.visible) { + setMeshVisible(sm, false); + } + } + } + } + for(int y = 0; y < 16; y++) { + for(int pass = 0; pass < 2; pass++) { + ChunkMesh cm = lodChunk.chunkMeshes[y * 2 + pass]; + if(cm != null) { + if(lodChunk.isSectionVisible[y] && newLOD == 2) { + if(!cm.visible) { + setMeshVisible(cm, true); + } + } else { + if(cm.visible) { + setMeshVisible(cm, false); + } + } + } + } + } + } + + protected void setMeshVisible(Mesh mesh, boolean visible) { + setMeshVisible(mesh, visible, false); + } + + protected void setMeshVisible(Mesh mesh, boolean visible, boolean force) { + if((!force && freezeMeshes) || mesh == null) return; + + if(mesh.visible != visible) { + mesh.visible = visible; + + if(mesh.gpuStatus == GPUStatus.UNSENT) { + mem.sendMeshToGPU(mesh); + sentMeshes[mesh.pass].add(mesh); + } + } + } + + public void removeMesh(Mesh mesh) { + if(mesh == null) return; + + mem.deleteMeshFromGPU(mesh); + sentMeshes[mesh.pass].remove(mesh); + setMeshVisible(mesh, false); + } + + public Chunk getChunkFromChunkCoords(int x, int z) { + for(Chunk chunk : myChunks) { + if(chunk.xPosition == x && chunk.zPosition == z) { + return chunk; + } + } + return null; + } + + public boolean shouldSideBeRendered(Block block, IBlockAccess ba, int x, int y, int z, int w) { + EnumFacing facing = EnumFacing.values()[w]; + if(block.getMaterial() == Material.water && facing != EnumFacing.UP && facing != EnumFacing.DOWN && !Minecraft.getMinecraft().theWorld.getChunkFromBlockCoords(x, z).isChunkLoaded) { + return false; + } else { + return block.shouldSideBeRendered(ba, x, y, z, w); + } + } + + public List getDebugText() { + List text = new ArrayList<>(); + text.addAll(mem.getDebugText()); + text.addAll(Arrays.asList( + "Simple meshes: " + SimpleChunkMesh.instances + " (" + SimpleChunkMesh.usedRAM / 1024 / 1024 + "MB)", + "Full meshes: " + ChunkMesh.instances + " (" + ChunkMesh.usedRAM / 1024 / 1024 + "MB)", + "Total RAM used: " + ((SimpleChunkMesh.usedRAM + ChunkMesh.usedRAM) / 1024 / 1024) + " MB", + "Rendered: " + renderedMeshes + )); + return text; + } + + public void onSave() { + System.out.println("Saving LOD regions..."); + long t0 = System.currentTimeMillis(); + //loadedRegionsMap.forEach((k, v) -> v.save(getSaveDir())); + System.out.println("Finished saving LOD regions in " + ((System.currentTimeMillis() - t0) / 1000.0) + "s"); + } + + public void onChunkLoad(ChunkEvent.Load event) { + farChunks.add(event.getChunk()); + } + + private Path getSaveDir(){ + return Minecraft.getMinecraft().mcDataDir.toPath().resolve("neodymium").resolve(Minecraft.getMinecraft().getIntegratedServer().getFolderName()); + } + + private boolean shouldRenderInWorld(World world) { + return world != null && !world.provider.isHellWorld; + } + + public static class LODChunkComparator implements Comparator { + Entity player; + + public LODChunkComparator(Entity player) { + this.player = player; + } + + @Override + public int compare(NeoChunk p1, NeoChunk p2) { + int distSq1 = distSq(p1); + int distSq2 = distSq(p2); + return distSq1 < distSq2 ? -1 : distSq1 > distSq2 ? 1 : 0; + } + + int distSq(NeoChunk p) { + return (int)( + Math.pow(((p.x * 16) - player.chunkCoordX), 2) + + Math.pow(((p.z * 16) - player.chunkCoordZ), 2) + ); + } + } + + public static class ChunkCoordDistanceComparator implements Comparator { + double x, y, z; + + public ChunkCoordDistanceComparator(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public int compare(ChunkCoordIntPair p1, ChunkCoordIntPair p2) { + int distSq1 = distSq(p1); + int distSq2 = distSq(p2); + return distSq1 < distSq2 ? -1 : distSq1 > distSq2 ? 1 : 0; + } + + int distSq(ChunkCoordIntPair p) { + return (int)( + Math.pow(((p.chunkXPos * 16) - x), 2) + + Math.pow(((p.chunkZPos * 16) - z), 2) + ); + } + } + + public static class MeshDistanceComparator implements Comparator { + double x, y, z; + + MeshDistanceComparator(double x, double y, double z){ + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public int compare(Mesh a, Mesh b) { + if(a.pass < b.pass) { + return -1; + } else if(a.pass > b.pass) { + return 1; + } else { + double distSqA = a.distSq(x, y, z); + double distSqB = b.distSq(x, y, z); + if(distSqA > distSqB) { + return 1; + } else if(distSqA < distSqB) { + return -1; + } else { + return 0; + } + } + } + + } + + public static enum WorldRendererChange { + VISIBLE, INVISIBLE, DELETED + } +} \ No newline at end of file diff --git a/src/main/java/makamys/neodymium/renderer/SimpleChunkMesh.java b/src/main/java/makamys/neodymium/renderer/SimpleChunkMesh.java index 964dec8..44031d4 100644 --- a/src/main/java/makamys/neodymium/renderer/SimpleChunkMesh.java +++ b/src/main/java/makamys/neodymium/renderer/SimpleChunkMesh.java @@ -16,7 +16,7 @@ import java.util.List; import org.lwjgl.BufferUtils; -import makamys.neodymium.LODMod; +import makamys.neodymium.Neodymium; import makamys.neodymium.util.MCUtil; import net.minecraft.block.Block; import net.minecraft.block.BlockGrass; @@ -45,7 +45,7 @@ public class SimpleChunkMesh extends Mesh { } private static boolean isBad(Block block) { - for(Class clazz : LODMod.blockClassBlacklist) { + for(Class clazz : Neodymium.blockClassBlacklist) { if(clazz.isInstance(block)) { return true; } @@ -126,7 +126,7 @@ public class SimpleChunkMesh extends Mesh { } color = (0xFF << 24) | ((color >> 16 & 0xFF) << 0) | ((color >> 8 & 0xFF) << 8) | ((color >> 0 & 0xFF) << 16); - if((LODMod.forceVanillaBiomeTemperature ? MCUtil.getBiomeTemperatureVanilla(biome, worldX, y, worldZ) + if((Neodymium.forceVanillaBiomeTemperature ? MCUtil.getBiomeTemperatureVanilla(biome, worldX, y, worldZ) : biome.getFloatTemperature(worldX, y, worldZ)) < 0.15f) { builder.addCube(divX, divZ, worldY + 0.2f, 1f, Blocks.snow_layer.getIcon(1, 0), 0xFFFFFFFF, brightnessMult); diff --git a/src/main/java/makamys/neodymium/util/Util.java b/src/main/java/makamys/neodymium/util/Util.java index 2507a78..d39ccc7 100644 --- a/src/main/java/makamys/neodymium/util/Util.java +++ b/src/main/java/makamys/neodymium/util/Util.java @@ -15,11 +15,11 @@ import net.minecraft.launchwrapper.Launch; public class Util { - private static boolean allowResourceOverrides = Boolean.parseBoolean(System.getProperty("lodmod.allowResourceOverrides", "false")); + private static boolean allowResourceOverrides = Boolean.parseBoolean(System.getProperty("neodymium.allowResourceOverrides", "false")); public static Path getResourcePath(String relPath) { if(allowResourceOverrides) { - File overrideFile = new File(new File(Launch.minecraftHome, "lodmod/resources"), relPath); + File overrideFile = new File(new File(Launch.minecraftHome, "neodymium/resources"), relPath); if(overrideFile.exists()) { return overrideFile.toPath(); } -- cgit