diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..7dafd2758 Binary files /dev/null and b/.gitattributes differ diff --git a/mccore/src/main/java/minecrafttransportsimulator/entities/components/AEntityF_Multipart.java b/mccore/src/main/java/minecrafttransportsimulator/entities/components/AEntityF_Multipart.java index 7d4dcf1b4..238fd7704 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/entities/components/AEntityF_Multipart.java +++ b/mccore/src/main/java/minecrafttransportsimulator/entities/components/AEntityF_Multipart.java @@ -24,6 +24,7 @@ import minecrafttransportsimulator.entities.instances.APart; import minecrafttransportsimulator.entities.instances.EntityBullet; import minecrafttransportsimulator.entities.instances.EntityBullet.HitType; +import minecrafttransportsimulator.jsondefs.JSONBullet.BulletType; import minecrafttransportsimulator.entities.instances.EntityPlacedPart; import minecrafttransportsimulator.items.components.AItemBase; import minecrafttransportsimulator.items.components.AItemPart; @@ -42,6 +43,7 @@ import minecrafttransportsimulator.mcinterface.InterfaceManager; import minecrafttransportsimulator.packets.instances.PacketEntityBulletHitCollision; import minecrafttransportsimulator.packets.instances.PacketEntityBulletHitEntity; +import minecrafttransportsimulator.packets.instances.PacketEntityBulletHitExternalEntity; import minecrafttransportsimulator.packets.instances.PacketEntityBulletHitGeneric; import minecrafttransportsimulator.packets.instances.PacketPartChange_Add; import minecrafttransportsimulator.packets.instances.PacketPartChange_Remove; @@ -358,20 +360,103 @@ public EntityBullet.HitType attackProjectile(Damage damage, EntityBullet bullet, if (hitEntry.box.groupDef != null && (hitEntry.box.groupDef.armorThickness != 0 || hitEntry.box.groupDef.heatArmorThickness != 0)) { hitOperationalHitbox = true; if (bullet != null) { - double armorThickness = hitEntry.box.definition != null ? (bullet.definition.bullet.isHeat && hitEntry.box.groupDef.heatArmorThickness != 0 ? hitEntry.box.groupDef.heatArmorThickness : hitEntry.box.groupDef.armorThickness) : 0; - double penetrationPotential = bullet.definition.bullet.isHeat ? bullet.definition.bullet.armorPenetration : (bullet.definition.bullet.armorPenetration * bullet.velocity / bullet.initialVelocity); + boolean bulletIsHeat = bullet.definition.bullet.types.contains(BulletType.HEAT); + double armorThickness = hitEntry.box.definition != null ? (bulletIsHeat && hitEntry.box.groupDef.heatArmorThickness != 0 ? hitEntry.box.groupDef.heatArmorThickness : hitEntry.box.groupDef.armorThickness) : 0; + double penetrationPotential = bulletIsHeat ? bullet.definition.bullet.armorPenetration : (bullet.definition.bullet.armorPenetration * bullet.velocity / bullet.initialVelocity); + + //Armor-piercing and sub-caliber projectiles lose some of their armor penetration when penetrating the armor boxes. + //Heat projectiles use their base penetration directly without scaling. bullet.armorPenetrated += armorThickness; - bullet.displayDebugMessage("HIT ARMOR OF: " + (int) armorThickness); + bullet.displayDebugMessage("HIT ARMOR: " + (int) armorThickness + " TOTAL ARMOR HIT: " + (int) bullet.armorPenetrated + " / PENETRATED " + (int) penetrationPotential); if (bullet.armorPenetrated > penetrationPotential) { //Bullet hit too much armor. if (world.isClient()) { - InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitGeneric(bullet.gun, bullet.bulletNumber, hitEntry.position, hitEntry.side, HitType.ARMOR)); + InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitGeneric(bullet.gun, bullet.bulletNumber, bullet.position, hitEntry.side, HitType.ARMOR)); + bullet.waitingOnActionPacket = true; + } else { + EntityBullet.performGenericHitLogic(bullet.gun, bullet.bulletNumber, bullet.position, hitEntry.side, HitType.ARMOR); + } + if (bulletIsHeat) { + bullet.displayDebugMessage("HEAT FAILED TO PENETRATE ARMOR. MAX PEN: " + (int) penetrationPotential); + } else { + bullet.displayDebugMessage("HIT TOO MUCH ARMOR. MAX PEN: " + (int) penetrationPotential); + } + return EntityBullet.HitType.ARMOR; + } + + //If the bullet has FRAG type and penetrated armor, deal fragmentation damage to internals. + if (bullet.definition.bullet.types.contains(BulletType.FRAG) && bullet.definition.bullet.fragDamage > 0) { + int fragPartsHit = 0; + int fragEntitiesHit = 0; + float coneAngle = bullet.definition.bullet.fragConeAngle > 0 ? bullet.definition.bullet.fragConeAngle : 45.0f; + float hitProbability = bullet.definition.bullet.fragHitProbability > 0 ? bullet.definition.bullet.fragHitProbability : 0.5f; + float fragDmg = bullet.definition.bullet.fragDamage; + double coneAngleRad = Math.toRadians(coneAngle / 2.0); + double coneRange = bullet.definition.bullet.fragRange > 0 ? bullet.definition.bullet.fragRange : bullet.definition.bullet.diameter / 10.0; + + //Check all parts on this entity for fragmentation hits within the cone. + for (APart part : allParts) { + double distToPart = bullet.position.distanceTo(part.position); + if (distToPart <= coneRange) { + Point3D toPartVector = part.position.copy().subtract(bullet.position); + double toPartLen = toPartVector.length(); + if (toPartLen > 0) { + double angleToPart = Math.acos(toPartVector.dotProduct(bullet.motion, false) / (toPartLen * bullet.motion.length())); + if (angleToPart <= coneAngleRad) { + if (hitProbability >= 1.0f || Math.random() <= hitProbability) { + fragPartsHit++; + Damage fragDamage = new Damage(fragDmg, part.boundingBox, bullet.gun, bullet.gun.lastController, null); + fragDamage.ignoreCooldown = true; + if (world.isClient()) { + InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitEntity(bullet.gun, part, fragDamage)); + } else { + EntityBullet.performEntityHitLogic(part, fragDamage); + } + } + } + } + } + } + + //Also check riders on all parts for fragmentation damage. + for (APart part : allParts) { + if (part.rider != null) { + IWrapperEntity partRider = part.rider; + double distToRider = bullet.position.distanceTo(partRider.getPosition()); + if (distToRider <= coneRange) { + Point3D toRiderVector = partRider.getPosition().copy().subtract(bullet.position); + double toRiderLen = toRiderVector.length(); + if (toRiderLen > 0) { + double angleToRider = Math.acos(toRiderVector.dotProduct(bullet.motion, false) / (toRiderLen * bullet.motion.length())); + if (angleToRider <= coneAngleRad) { + if (hitProbability >= 1.0f || Math.random() <= hitProbability) { + fragEntitiesHit++; + Damage fragDamage = new Damage(fragDmg, part.boundingBox, bullet.gun, bullet.gun.lastController, null); + fragDamage.ignoreCooldown = true; + if (world.isClient()) { + InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitExternalEntity(partRider, fragDamage)); + } else { + EntityBullet.performExternalEntityHitLogic(partRider, fragDamage); + } + } + } + } + } + } + } + bullet.displayDebugMessage("FRAG HIT " + fragPartsHit + " PARTS AND " + fragEntitiesHit + " ENTITIES"); + } + + //HEAT shells detonate on armor contact and do not continue flying. + if (bulletIsHeat) { + bullet.displayDebugMessage("HEAT DETONATED ON ARMOR."); + if (world.isClient()) { + InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitGeneric(bullet.gun, bullet.bulletNumber, bullet.position, hitEntry.side, HitType.ARMOR)); bullet.waitingOnActionPacket = true; } else { - EntityBullet.performGenericHitLogic(bullet.gun, bullet.bulletNumber, hitEntry.position, hitEntry.side, HitType.ARMOR); + EntityBullet.performGenericHitLogic(bullet.gun, bullet.bulletNumber, bullet.position, hitEntry.side, HitType.ARMOR); } - bullet.displayDebugMessage("HIT TOO MUCH ARMOR. MAX PEN: " + (int) penetrationPotential); return EntityBullet.HitType.ARMOR; } } else { @@ -404,14 +489,14 @@ public EntityBullet.HitType attackProjectile(Damage damage, EntityBullet bullet, if (world.isClient()) { InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitEntity(bullet.gun, hitEntity, damage)); if (removeAfterDamage) { - InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitGeneric(bullet.gun, bullet.bulletNumber, hitEntry.position, hitEntry.side, HitType.VEHICLE)); + InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitGeneric(bullet.gun, bullet.bulletNumber, bullet.position, hitEntry.side, HitType.VEHICLE)); bullet.waitingOnActionPacket = true; return EntityBullet.HitType.VEHICLE; } } else { EntityBullet.performEntityHitLogic(hitEntity, damage); if (removeAfterDamage) { - EntityBullet.performGenericHitLogic(bullet.gun, bullet.bulletNumber, hitEntry.position, hitEntry.side, HitType.VEHICLE); + EntityBullet.performGenericHitLogic(bullet.gun, bullet.bulletNumber, bullet.position, hitEntry.side, HitType.VEHICLE); return EntityBullet.HitType.VEHICLE; } } diff --git a/mccore/src/main/java/minecrafttransportsimulator/entities/instances/EntityBullet.java b/mccore/src/main/java/minecrafttransportsimulator/entities/instances/EntityBullet.java index 54db92889..c9cea9877 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/entities/instances/EntityBullet.java +++ b/mccore/src/main/java/minecrafttransportsimulator/entities/instances/EntityBullet.java @@ -315,7 +315,8 @@ public void update() { if (((definition.bullet.isLongRange || !(gun.lastController instanceof IWrapperPlayer)) ^ world.isClient()) && (!world.isClient() || InterfaceManager.clientInterface.getClientPlayer().getID().equals(gun.lastController.getID()))) { //Now that we have an accurate motion, check for collisions. //First get a damage object to try to attack entities with. - double amount = definition.bullet.isHeat ? definition.bullet.damage : (velocity / initialVelocity) * definition.bullet.damage * ConfigSystem.settings.damage.bulletDamageFactor.value * ConfigSystem.settings.damage.packBulletDamageFactors.value.get(gun.lastLoadedBullet.definition.packID); + boolean bulletIsHeat = definition.bullet.types.contains(BulletType.HEAT); + double amount = bulletIsHeat ? definition.bullet.damage : (velocity / initialVelocity) * definition.bullet.damage * ConfigSystem.settings.damage.bulletDamageFactor.value * ConfigSystem.settings.damage.packBulletDamageFactors.value.get(gun.lastLoadedBullet.definition.packID); Damage damage = new Damage(gun, boundingBox, amount); //Declare variables that may be used for hit logic. @@ -417,11 +418,11 @@ public void update() { if (hitExternalEntity != null) { if (world.isClient()) { InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitExternalEntity(hitExternalEntity, damage)); - InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitGeneric(gun, bulletNumber, hitExternalEntity.getPosition(), Axis.getFromVector(motion), HitType.ENTITY)); + InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitGeneric(gun, bulletNumber, position, Axis.getFromVector(motion), HitType.ENTITY)); waitingOnActionPacket = true; } else { performExternalEntityHitLogic(hitExternalEntity, damage); - performGenericHitLogic(gun, bulletNumber, hitExternalEntity.getPosition(), Axis.getFromVector(motion), HitType.ENTITY); + performGenericHitLogic(gun, bulletNumber, position, Axis.getFromVector(motion), HitType.ENTITY); } displayDebugMessage("HIT MC ENTITY " + hitExternalEntity.getName()); return; diff --git a/mccore/src/main/java/minecrafttransportsimulator/jsondefs/JSONBullet.java b/mccore/src/main/java/minecrafttransportsimulator/jsondefs/JSONBullet.java index c2bc5d297..192dbff63 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/jsondefs/JSONBullet.java +++ b/mccore/src/main/java/minecrafttransportsimulator/jsondefs/JSONBullet.java @@ -29,8 +29,17 @@ public static class Bullet { @JSONDescription("Set this to true to make this bullet not spawn, but consume ammo.") public boolean isBlank; - @JSONDescription("If true, then this bullet will be considered a HEAT bullet and will use the HEAT armor value on any collision boxes it finds. If that value isn't defined, it will just use the normal armor value.") - public boolean isHeat; + @JSONDescription("The cone angle, in degrees, for fragmentation spread when a FRAG-type bullet penetrates armor. Fragments are scattered within this cone behind the point of penetration.") + public float fragConeAngle; + + @JSONDescription("The maximum distance, in meters (blocks), that fragments can travel from the point of armor penetration.") + public float fragRange; + + @JSONDescription("The probability (0.0 to 1.0) that a fragment will hit each individual component or rider within the fragmentation cone. If 1.0, every target in the cone is guaranteed to be hit.") + public float fragHitProbability; + + @JSONDescription("The damage each fragment deals when it hits an internal component or rider.") + public float fragDamage; @JSONDescription("Normally, bullet checks are handled only on the client that spawned them. This client then sends the info to the server when it sees a hit. This works best for most bullets, since it prevents the firing player from 'missing' something they hit due to lag. However, this prevents bullets from hitting things that aren't loaded. Setting this to true will make the bullet do checks on the server, which will let them hit anything loaded on the server, but will result in de-syncs between hit position seen and actual hit position if the gun is moving at any significant speed when fired.") public boolean isLongRange; @@ -154,6 +163,9 @@ public static class Bullet { @JSONDescription("The radius (in blocks) within which full armor penetration is applied regardless of distance.") public float maxPenRadius; + + @Deprecated + public boolean isHeat; } public enum BulletType { @@ -165,6 +177,16 @@ public enum BulletType { WATER, @JSONDescription("A bullet that pierces player armor. Useful for pesky super-suits.") ARMOR_PIERCING, + @JSONDescription("If defined, then this bullet will be considered a HEAT bullet and will use the HEAT armor value on any collision boxes it finds. If that value isn't defined, it will just use the normal armor value.") + HEAT, + @JSONDescription("Like armor-piercing ones, but used for categorization purposes.") + SUBCALIBER, + @JSONDescription("Used when fragmentation need to be used for bullets.") + FRAG, + @JSONDescription("Identifies the bullet type as a missile. Used for categorization.") + MISSILE, + @JSONDescription("Identifies the bullet type as a bomb. Used for categorization.") + BOMB, @JSONDescription("A bullet that has a custom function defined in code. Useful for integration with a variety of mods, regardless of version.") CUSTOM } diff --git a/mccore/src/main/java/minecrafttransportsimulator/packloading/LegacyCompatSystem.java b/mccore/src/main/java/minecrafttransportsimulator/packloading/LegacyCompatSystem.java index c39e840b0..1508a5f59 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/packloading/LegacyCompatSystem.java +++ b/mccore/src/main/java/minecrafttransportsimulator/packloading/LegacyCompatSystem.java @@ -30,6 +30,7 @@ import minecrafttransportsimulator.jsondefs.JSONAnimationDefinition; import minecrafttransportsimulator.jsondefs.JSONAnimationDefinition.AnimationComponentType; import minecrafttransportsimulator.jsondefs.JSONBullet; +import minecrafttransportsimulator.jsondefs.JSONBullet.BulletType; import minecrafttransportsimulator.jsondefs.JSONCollisionBox; import minecrafttransportsimulator.jsondefs.JSONCollisionGroup; import minecrafttransportsimulator.jsondefs.JSONCollisionGroup.CollisionType; @@ -2007,6 +2008,15 @@ private static void performBulletLegacyCompats(JSONBullet definition) { if (definition.bullet.damage == 0) { definition.bullet.damage = definition.bullet.diameter / 5F; } + if (definition.bullet.isHeat) { + if (definition.bullet.types == null) { + definition.bullet.types = new ArrayList<>(); + } + if (!definition.bullet.types.contains(BulletType.HEAT)) { + definition.bullet.types.add(BulletType.HEAT); + } + definition.bullet.isHeat = false; + } //Make guided bullets default to active guidance if (definition.bullet.guidanceType == null && definition.bullet.turnRate > 0) { definition.bullet.guidanceType = JSONBullet.GuidanceType.ACTIVE; diff --git a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceClient.java b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceClient.java index 26d306371..af0586506 100644 --- a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceClient.java +++ b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceClient.java @@ -1,133 +1,133 @@ -package mcinterface1122; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import minecrafttransportsimulator.baseclasses.Point3D; -import minecrafttransportsimulator.baseclasses.RotationMatrix; -import minecrafttransportsimulator.guis.components.AGUIBase; -import minecrafttransportsimulator.guis.instances.GUIPackMissing; -import minecrafttransportsimulator.mcinterface.IInterfaceClient; -import minecrafttransportsimulator.mcinterface.IWrapperItemStack; -import minecrafttransportsimulator.mcinterface.IWrapperPlayer; -import minecrafttransportsimulator.mcinterface.InterfaceManager; -import minecrafttransportsimulator.packloading.PackParser; -import minecrafttransportsimulator.systems.CameraSystem.CameraMode; -import minecrafttransportsimulator.systems.ConfigSystem; -import minecrafttransportsimulator.systems.ControlSystem; -import net.minecraft.block.SoundType; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.ScaledResolution; -import net.minecraft.client.renderer.ActiveRenderInfo; -import net.minecraft.client.util.ITooltipFlag; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.SoundCategory; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.text.TextFormatting; -import net.minecraftforge.fluids.Fluid; -import net.minecraftforge.fluids.FluidRegistry; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fml.common.FMLCommonHandler; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.gameevent.TickEvent; -import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; -import net.minecraftforge.fml.relauncher.Side; - -@EventBusSubscriber(Side.CLIENT) -public class InterfaceClient implements IInterfaceClient { - private static CameraMode actualCameraMode; - private static CameraMode cameraModeRequest; - private static int ticksToCullingWarning = 200; - private static BuilderEntityRenderForwarder activeFollower; - private static int ticksSincePlayerJoin; - - @Override - public boolean isGamePaused() { - return Minecraft.getMinecraft().isGamePaused(); - } - - @Override - public String getLanguageName() { - return Minecraft.getMinecraft().gameSettings.language; - } - - @Override - public List getAllLanguages() { - List list = new ArrayList<>(); - Minecraft.getMinecraft().getLanguageManager().getLanguages().forEach(language -> list.add(language.getLanguageCode())); - return list; - } - - @Override - public String getFluidName(String fluidID, String fluidMod) { - Fluid fluid = FluidRegistry.getFluid(fluidID); - return fluid != null ? new FluidStack(fluid, 1).getLocalizedName() : "INVALID"; - } - - @Override - public Map getAllFluidNames() { - Map fluidIDsToNames = new HashMap<>(); - for (String fluidID : FluidRegistry.getRegisteredFluids().keySet()) { - fluidIDsToNames.put(fluidID, new FluidStack(FluidRegistry.getFluid(fluidID), 1).getLocalizedName()); - } - return fluidIDsToNames; - } - - @Override - public boolean isChatOpen() { - return Minecraft.getMinecraft().ingameGUI.getChatGUI().getChatOpen(); - } - - @Override - public boolean isGUIOpen() { - return Minecraft.getMinecraft().currentScreen != null; - } - - @Override - public void displayOverlayMessage(String message) { - Minecraft.getMinecraft().ingameGUI.setOverlayMessage(message, false); - } - - @Override - public CameraMode getCameraMode() { - return actualCameraMode; - } - - @Override - public void setCameraMode(CameraMode mode) { - cameraModeRequest = mode; - } - - @Override - public int getCameraDefaultZoom() { - return 4; - } - - @Override - public long getPackedDisplaySize() { - ScaledResolution screenResolution = new ScaledResolution(Minecraft.getMinecraft()); - return (((long) screenResolution.getScaledWidth()) << Integer.SIZE) | (screenResolution.getScaledHeight() & 0xffffffffL); - } - - @Override - public float getMouseSensitivity() { - return Minecraft.getMinecraft().gameSettings.mouseSensitivity; - } - - @Override - public void setMouseSensitivity(float setting) { - Minecraft.getMinecraft().gameSettings.mouseSensitivity = setting; - } - - @Override - public float getFOV() { - return Minecraft.getMinecraft().gameSettings.fovSetting; - } - +package mcinterface1122; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import minecrafttransportsimulator.baseclasses.Point3D; +import minecrafttransportsimulator.baseclasses.RotationMatrix; +import minecrafttransportsimulator.guis.components.AGUIBase; +import minecrafttransportsimulator.guis.instances.GUIPackMissing; +import minecrafttransportsimulator.mcinterface.IInterfaceClient; +import minecrafttransportsimulator.mcinterface.IWrapperItemStack; +import minecrafttransportsimulator.mcinterface.IWrapperPlayer; +import minecrafttransportsimulator.mcinterface.InterfaceManager; +import minecrafttransportsimulator.packloading.PackParser; +import minecrafttransportsimulator.systems.CameraSystem.CameraMode; +import minecrafttransportsimulator.systems.ConfigSystem; +import minecrafttransportsimulator.systems.ControlSystem; +import net.minecraft.block.SoundType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.ActiveRenderInfo; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; +import net.minecraftforge.fml.relauncher.Side; + +@EventBusSubscriber(Side.CLIENT) +public class InterfaceClient implements IInterfaceClient { + private static CameraMode actualCameraMode; + private static CameraMode cameraModeRequest; + private static int ticksToCullingWarning = 200; + private static BuilderEntityRenderForwarder activeFollower; + private static int ticksSincePlayerJoin; + + @Override + public boolean isGamePaused() { + return Minecraft.getMinecraft().isGamePaused(); + } + + @Override + public String getLanguageName() { + return Minecraft.getMinecraft().gameSettings.language; + } + + @Override + public List getAllLanguages() { + List list = new ArrayList<>(); + Minecraft.getMinecraft().getLanguageManager().getLanguages().forEach(language -> list.add(language.getLanguageCode())); + return list; + } + + @Override + public String getFluidName(String fluidID, String fluidMod) { + Fluid fluid = FluidRegistry.getFluid(fluidID); + return fluid != null ? new FluidStack(fluid, 1).getLocalizedName() : "INVALID"; + } + + @Override + public Map getAllFluidNames() { + Map fluidIDsToNames = new HashMap<>(); + for (String fluidID : FluidRegistry.getRegisteredFluids().keySet()) { + fluidIDsToNames.put(fluidID, new FluidStack(FluidRegistry.getFluid(fluidID), 1).getLocalizedName()); + } + return fluidIDsToNames; + } + + @Override + public boolean isChatOpen() { + return Minecraft.getMinecraft().ingameGUI.getChatGUI().getChatOpen(); + } + + @Override + public boolean isGUIOpen() { + return Minecraft.getMinecraft().currentScreen != null; + } + + @Override + public void displayOverlayMessage(String message) { + Minecraft.getMinecraft().ingameGUI.setOverlayMessage(message, false); + } + + @Override + public CameraMode getCameraMode() { + return actualCameraMode; + } + + @Override + public void setCameraMode(CameraMode mode) { + cameraModeRequest = mode; + } + + @Override + public int getCameraDefaultZoom() { + return 4; + } + + @Override + public long getPackedDisplaySize() { + ScaledResolution screenResolution = new ScaledResolution(Minecraft.getMinecraft()); + return (((long) screenResolution.getScaledWidth()) << Integer.SIZE) | (screenResolution.getScaledHeight() & 0xffffffffL); + } + + @Override + public float getMouseSensitivity() { + return Minecraft.getMinecraft().gameSettings.mouseSensitivity; + } + + @Override + public void setMouseSensitivity(float setting) { + Minecraft.getMinecraft().gameSettings.mouseSensitivity = setting; + } + + @Override + public float getFOV() { + return Minecraft.getMinecraft().gameSettings.fovSetting; + } + @Override public void setFOV(float setting) { Minecraft.getMinecraft().gameSettings.fovSetting = setting; @@ -142,230 +142,230 @@ public boolean isGUIHidden() { public void closeGUI() { Minecraft.getMinecraft().displayGuiScreen(null); } - - @Override - public void setActiveGUI(AGUIBase gui) { - FMLCommonHandler.instance().showGuiScreen(new BuilderGUI(gui)); - } - - @Override - public WrapperWorld getClientWorld() { - return WrapperWorld.getWrapperFor(Minecraft.getMinecraft().world); - } - - @Override - public WrapperPlayer getClientPlayer() { - EntityPlayer player = Minecraft.getMinecraft().player; - return WrapperPlayer.getWrapperFor(player); - } - - @Override - public Point3D getCameraPosition() { - EntityPlayer player = Minecraft.getMinecraft().player; - Vec3d cameraOffset = ActiveRenderInfo.getCameraPosition(); - mutablePosition.set(player.posX + cameraOffset.x, player.posY + cameraOffset.y, player.posZ + cameraOffset.z); - return mutablePosition; - } - - private static final Point3D mutablePosition = new Point3D(); - private static final RotationMatrix cameraProjectionOrientation = new RotationMatrix(); - - @Override - public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight) { - double camX, camY, camZ; - double fwdX, fwdY, fwdZ; - double upX, upY, upZ; - double rgtX, rgtY, rgtZ; - - if (InterfaceEventsEntityRendering.adjustedCamera) { - // Use MTS camera data — includes roll and correct vehicle-relative position. - camX = InterfaceEventsEntityRendering.projectionCameraPosition.x; - camY = InterfaceEventsEntityRendering.projectionCameraPosition.y; - camZ = InterfaceEventsEntityRendering.projectionCameraPosition.z; - RotationMatrix ori = getCameraProjectionOrientation(InterfaceEventsEntityRendering.projectionCameraOrientation); - fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; - upX = ori.m01; upY = ori.m11; upZ = ori.m21; - // MTS (1,0,0) rotated = camera LEFT (not right); negate to get camera right. - rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; - } else { - // 1.12.2 has no Camera class — compute direction from player view angles. - net.minecraft.entity.player.EntityPlayer player = Minecraft.getMinecraft().player; - net.minecraft.util.math.Vec3d camOffset = net.minecraft.client.renderer.ActiveRenderInfo.getCameraPosition(); - camX = player.posX + camOffset.x; - camY = player.posY + camOffset.y; - camZ = player.posZ + camOffset.z; - - float yaw = (float) Math.toRadians(player.rotationYaw); - float pitch = (float) Math.toRadians(player.rotationPitch); - - fwdX = -Math.sin(yaw) * Math.cos(pitch); - fwdY = -Math.sin(pitch); - fwdZ = Math.cos(yaw) * Math.cos(pitch); - rgtX = Math.cos(yaw); - rgtY = 0; - rgtZ = Math.sin(yaw); - upX = fwdY * rgtZ - fwdZ * rgtY; - upY = fwdZ * rgtX - fwdX * rgtZ; - upZ = fwdX * rgtY - fwdY * rgtX; - if (actualCameraMode == CameraMode.THIRD_PERSON_INVERTED) { - fwdX = -fwdX; fwdY = -fwdY; fwdZ = -fwdZ; - rgtX = -rgtX; rgtY = -rgtY; rgtZ = -rgtZ; - } - } - - double dx = worldPos.x - camX; - double dy = worldPos.y - camY; - double dz = worldPos.z - camZ; - - double depth = dx * fwdX + dy * fwdY + dz * fwdZ; - if (depth <= 0.001) return null; - - double xView = dx * rgtX + dy * rgtY + dz * rgtZ; - double yView = dx * upX + dy * upY + dz * upZ; - - double fovRad = Math.toRadians(getFOV()); - double tanHalfFov = Math.tan(fovRad / 2.0); - double aspect = (double) screenWidth / screenHeight; - - double ndcX = xView / (depth * tanHalfFov * aspect); - double ndcY = yView / (depth * tanHalfFov); - - if (ndcX < -1.1 || ndcX > 1.1 || ndcY < -1.1 || ndcY > 1.1) return null; - - screenProjectionResult.set( - (ndcX + 1.0) / 2.0 * screenWidth, - (1.0 - ndcY) / 2.0 * screenHeight, - depth); - return screenProjectionResult; - } - - private static RotationMatrix getCameraProjectionOrientation(RotationMatrix cameraOrientation) { - if (actualCameraMode == CameraMode.THIRD_PERSON_INVERTED) { - cameraOrientation.convertToAngles(); - cameraProjectionOrientation.angles.set(-cameraOrientation.angles.x, cameraOrientation.angles.y - 180, -cameraOrientation.angles.z); - cameraProjectionOrientation.updateToAngles(); - return cameraProjectionOrientation; - } else { - return cameraOrientation; - } - } - - private static final Point3D screenProjectionResult = new Point3D(); - - @Override - public void playBlockBreakSound(Point3D position) { - BlockPos pos = new BlockPos(position.x, position.y, position.z); - if (!Minecraft.getMinecraft().world.isAirBlock(pos)) { - SoundType soundType = Minecraft.getMinecraft().world.getBlockState(pos).getBlock().getSoundType(Minecraft.getMinecraft().world.getBlockState(pos), Minecraft.getMinecraft().player.world, pos, null); - Minecraft.getMinecraft().world.playSound(Minecraft.getMinecraft().player, pos, soundType.getBreakSound(), SoundCategory.BLOCKS, soundType.getVolume(), soundType.getPitch()); - } - } - - @Override - public List getTooltipLines(IWrapperItemStack stack) { - List tooltipText = ((WrapperItemStack) stack).stack.getTooltip(Minecraft.getMinecraft().player, Minecraft.getMinecraft().gameSettings.advancedItemTooltips ? ITooltipFlag.TooltipFlags.ADVANCED : ITooltipFlag.TooltipFlags.NORMAL); - //Add grey formatting text to non-first line tooltips. - for (int i = 1; i < tooltipText.size(); ++i) { - tooltipText.set(i, TextFormatting.GRAY + tooltipText.get(i)); - } - return tooltipText; - } - - /** - * Tick client-side entities like bullets and particles. - * These don't get ticked normally due to the world tick event - * not being called on clients. - */ - @SubscribeEvent - public static void onIVClientTick(TickEvent.ClientTickEvent event) { - IWrapperPlayer player = InterfaceManager.clientInterface.getClientPlayer(); - if (!InterfaceManager.clientInterface.isGamePaused() && player != null) { - WrapperWorld world = WrapperWorld.getWrapperFor(Minecraft.getMinecraft().world); - if (world != null) { - if (event.phase.equals(Phase.START)) { - if (!player.isSpectator()) { - //Handle controls. This has to happen prior to vehicle updates to ensure click handling is based on current position of the player. - ControlSystem.controlGlobal(player); - if (((WrapperPlayer) player).player.ticksExisted % 100 == 0) { - if (!InterfaceManager.clientInterface.isGUIOpen() && !PackParser.arePacksPresent()) { - new GUIPackMissing(); - } - } - } - - world.tickAll(true); - - //Complain about compats at 10 second mark. - if (ConfigSystem.settings.general.performModCompatFunctions.value) { - if (ticksToCullingWarning > 0) { - if (--ticksToCullingWarning == 0) { - //Nothing to complain about here! - } - } - } - - //Check follower. - if (activeFollower != null) { - //Follower exists, check if world is the same and it is actually updating. - //We check basic states, and then the watchdog bit that gets reset every tick. - //This way if we're in the world, but not valid we will know. - EntityPlayer mcPlayer = ((WrapperPlayer) player).player; - if (activeFollower.world != mcPlayer.world || activeFollower.playerFollowing != mcPlayer || mcPlayer.isDead || activeFollower.isDead || activeFollower.idleTickCounter == 20) { - //Follower is not linked. Remove it and re-create in code below. - activeFollower.setDead(); - activeFollower = null; - ticksSincePlayerJoin = 0; - } else { - ++activeFollower.idleTickCounter; - } - } else { - //Follower does not exist, check if player has been present for 3 seconds and spawn it. - if (++ticksSincePlayerJoin == 60) { - activeFollower = new BuilderEntityRenderForwarder(((WrapperPlayer) player).player); - activeFollower.loadedFromSavedNBT = true; - world.world.spawnEntity(activeFollower); - } - } - } else { - world.tickAll(false); - - //Handle camera requests. - if(cameraModeRequest != null) { - switch(cameraModeRequest) { - case FIRST_PERSON:{ - Minecraft.getMinecraft().gameSettings.thirdPersonView = 0; - break; - } - case THIRD_PERSON:{ - Minecraft.getMinecraft().gameSettings.thirdPersonView = 1; - break; - } - case THIRD_PERSON_INVERTED:{ - Minecraft.getMinecraft().gameSettings.thirdPersonView = 2; - break; - } - } - cameraModeRequest = null; - } - - //Update camera state, since this can change depending on tick if we check during renders. - int cameraModeInt = Minecraft.getMinecraft().gameSettings.thirdPersonView; - switch(cameraModeInt) { - case(0):{ - actualCameraMode = CameraMode.FIRST_PERSON; - break; - } - case(1):{ - actualCameraMode = CameraMode.THIRD_PERSON; - break; - } - case(2):{ - actualCameraMode = CameraMode.THIRD_PERSON_INVERTED; - break; - } - } - } - } - } - } -} + + @Override + public void setActiveGUI(AGUIBase gui) { + FMLCommonHandler.instance().showGuiScreen(new BuilderGUI(gui)); + } + + @Override + public WrapperWorld getClientWorld() { + return WrapperWorld.getWrapperFor(Minecraft.getMinecraft().world); + } + + @Override + public WrapperPlayer getClientPlayer() { + EntityPlayer player = Minecraft.getMinecraft().player; + return WrapperPlayer.getWrapperFor(player); + } + + @Override + public Point3D getCameraPosition() { + EntityPlayer player = Minecraft.getMinecraft().player; + Vec3d cameraOffset = ActiveRenderInfo.getCameraPosition(); + mutablePosition.set(player.posX + cameraOffset.x, player.posY + cameraOffset.y, player.posZ + cameraOffset.z); + return mutablePosition; + } + + private static final Point3D mutablePosition = new Point3D(); + private static final RotationMatrix cameraProjectionOrientation = new RotationMatrix(); + + @Override + public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight) { + double camX, camY, camZ; + double fwdX, fwdY, fwdZ; + double upX, upY, upZ; + double rgtX, rgtY, rgtZ; + + if (InterfaceEventsEntityRendering.adjustedCamera) { + // Use MTS camera data — includes roll and correct vehicle-relative position. + camX = InterfaceEventsEntityRendering.projectionCameraPosition.x; + camY = InterfaceEventsEntityRendering.projectionCameraPosition.y; + camZ = InterfaceEventsEntityRendering.projectionCameraPosition.z; + RotationMatrix ori = getCameraProjectionOrientation(InterfaceEventsEntityRendering.projectionCameraOrientation); + fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; + upX = ori.m01; upY = ori.m11; upZ = ori.m21; + // MTS (1,0,0) rotated = camera LEFT (not right); negate to get camera right. + rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; + } else { + // 1.12.2 has no Camera class — compute direction from player view angles. + net.minecraft.entity.player.EntityPlayer player = Minecraft.getMinecraft().player; + net.minecraft.util.math.Vec3d camOffset = net.minecraft.client.renderer.ActiveRenderInfo.getCameraPosition(); + camX = player.posX + camOffset.x; + camY = player.posY + camOffset.y; + camZ = player.posZ + camOffset.z; + + float yaw = (float) Math.toRadians(player.rotationYaw); + float pitch = (float) Math.toRadians(player.rotationPitch); + + fwdX = -Math.sin(yaw) * Math.cos(pitch); + fwdY = -Math.sin(pitch); + fwdZ = Math.cos(yaw) * Math.cos(pitch); + rgtX = Math.cos(yaw); + rgtY = 0; + rgtZ = Math.sin(yaw); + upX = fwdY * rgtZ - fwdZ * rgtY; + upY = fwdZ * rgtX - fwdX * rgtZ; + upZ = fwdX * rgtY - fwdY * rgtX; + if (actualCameraMode == CameraMode.THIRD_PERSON_INVERTED) { + fwdX = -fwdX; fwdY = -fwdY; fwdZ = -fwdZ; + rgtX = -rgtX; rgtY = -rgtY; rgtZ = -rgtZ; + } + } + + double dx = worldPos.x - camX; + double dy = worldPos.y - camY; + double dz = worldPos.z - camZ; + + double depth = dx * fwdX + dy * fwdY + dz * fwdZ; + if (depth <= 0.001) return null; + + double xView = dx * rgtX + dy * rgtY + dz * rgtZ; + double yView = dx * upX + dy * upY + dz * upZ; + + double fovRad = Math.toRadians(getFOV()); + double tanHalfFov = Math.tan(fovRad / 2.0); + double aspect = (double) screenWidth / screenHeight; + + double ndcX = xView / (depth * tanHalfFov * aspect); + double ndcY = yView / (depth * tanHalfFov); + + if (ndcX < -1.1 || ndcX > 1.1 || ndcY < -1.1 || ndcY > 1.1) return null; + + screenProjectionResult.set( + (ndcX + 1.0) / 2.0 * screenWidth, + (1.0 - ndcY) / 2.0 * screenHeight, + depth); + return screenProjectionResult; + } + + private static RotationMatrix getCameraProjectionOrientation(RotationMatrix cameraOrientation) { + if (actualCameraMode == CameraMode.THIRD_PERSON_INVERTED) { + cameraOrientation.convertToAngles(); + cameraProjectionOrientation.angles.set(-cameraOrientation.angles.x, cameraOrientation.angles.y - 180, -cameraOrientation.angles.z); + cameraProjectionOrientation.updateToAngles(); + return cameraProjectionOrientation; + } else { + return cameraOrientation; + } + } + + private static final Point3D screenProjectionResult = new Point3D(); + + @Override + public void playBlockBreakSound(Point3D position) { + BlockPos pos = new BlockPos(position.x, position.y, position.z); + if (!Minecraft.getMinecraft().world.isAirBlock(pos)) { + SoundType soundType = Minecraft.getMinecraft().world.getBlockState(pos).getBlock().getSoundType(Minecraft.getMinecraft().world.getBlockState(pos), Minecraft.getMinecraft().player.world, pos, null); + Minecraft.getMinecraft().world.playSound(Minecraft.getMinecraft().player, pos, soundType.getBreakSound(), SoundCategory.BLOCKS, soundType.getVolume(), soundType.getPitch()); + } + } + + @Override + public List getTooltipLines(IWrapperItemStack stack) { + List tooltipText = ((WrapperItemStack) stack).stack.getTooltip(Minecraft.getMinecraft().player, Minecraft.getMinecraft().gameSettings.advancedItemTooltips ? ITooltipFlag.TooltipFlags.ADVANCED : ITooltipFlag.TooltipFlags.NORMAL); + //Add grey formatting text to non-first line tooltips. + for (int i = 1; i < tooltipText.size(); ++i) { + tooltipText.set(i, TextFormatting.GRAY + tooltipText.get(i)); + } + return tooltipText; + } + + /** + * Tick client-side entities like bullets and particles. + * These don't get ticked normally due to the world tick event + * not being called on clients. + */ + @SubscribeEvent + public static void onIVClientTick(TickEvent.ClientTickEvent event) { + IWrapperPlayer player = InterfaceManager.clientInterface.getClientPlayer(); + if (!InterfaceManager.clientInterface.isGamePaused() && player != null) { + WrapperWorld world = WrapperWorld.getWrapperFor(Minecraft.getMinecraft().world); + if (world != null) { + if (event.phase.equals(Phase.START)) { + if (!player.isSpectator()) { + //Handle controls. This has to happen prior to vehicle updates to ensure click handling is based on current position of the player. + ControlSystem.controlGlobal(player); + if (((WrapperPlayer) player).player.ticksExisted % 100 == 0) { + if (!InterfaceManager.clientInterface.isGUIOpen() && !PackParser.arePacksPresent()) { + new GUIPackMissing(); + } + } + } + + world.tickAll(true); + + //Complain about compats at 10 second mark. + if (ConfigSystem.settings.general.performModCompatFunctions.value) { + if (ticksToCullingWarning > 0) { + if (--ticksToCullingWarning == 0) { + //Nothing to complain about here! + } + } + } + + //Check follower. + if (activeFollower != null) { + //Follower exists, check if world is the same and it is actually updating. + //We check basic states, and then the watchdog bit that gets reset every tick. + //This way if we're in the world, but not valid we will know. + EntityPlayer mcPlayer = ((WrapperPlayer) player).player; + if (activeFollower.world != mcPlayer.world || activeFollower.playerFollowing != mcPlayer || mcPlayer.isDead || activeFollower.isDead || activeFollower.idleTickCounter == 20) { + //Follower is not linked. Remove it and re-create in code below. + activeFollower.setDead(); + activeFollower = null; + ticksSincePlayerJoin = 0; + } else { + ++activeFollower.idleTickCounter; + } + } else { + //Follower does not exist, check if player has been present for 3 seconds and spawn it. + if (++ticksSincePlayerJoin == 60) { + activeFollower = new BuilderEntityRenderForwarder(((WrapperPlayer) player).player); + activeFollower.loadedFromSavedNBT = true; + world.world.spawnEntity(activeFollower); + } + } + } else { + world.tickAll(false); + + //Handle camera requests. + if(cameraModeRequest != null) { + switch(cameraModeRequest) { + case FIRST_PERSON:{ + Minecraft.getMinecraft().gameSettings.thirdPersonView = 0; + break; + } + case THIRD_PERSON:{ + Minecraft.getMinecraft().gameSettings.thirdPersonView = 1; + break; + } + case THIRD_PERSON_INVERTED:{ + Minecraft.getMinecraft().gameSettings.thirdPersonView = 2; + break; + } + } + cameraModeRequest = null; + } + + //Update camera state, since this can change depending on tick if we check during renders. + int cameraModeInt = Minecraft.getMinecraft().gameSettings.thirdPersonView; + switch(cameraModeInt) { + case(0):{ + actualCameraMode = CameraMode.FIRST_PERSON; + break; + } + case(1):{ + actualCameraMode = CameraMode.THIRD_PERSON; + break; + } + case(2):{ + actualCameraMode = CameraMode.THIRD_PERSON_INVERTED; + break; + } + } + } + } + } + } +} diff --git a/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java b/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java index 607f2d4a5..468b1d3ca 100644 --- a/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java +++ b/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java @@ -1,150 +1,150 @@ -package mcinterface1165; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.nio.FloatBuffer; - -import minecrafttransportsimulator.baseclasses.Point3D; -import minecrafttransportsimulator.baseclasses.RotationMatrix; -import minecrafttransportsimulator.entities.instances.EntityFluidTank; -import minecrafttransportsimulator.guis.components.AGUIBase; -import minecrafttransportsimulator.guis.instances.GUIPackMissing; -import minecrafttransportsimulator.mcinterface.AWrapperWorld; -import minecrafttransportsimulator.mcinterface.IInterfaceClient; -import minecrafttransportsimulator.mcinterface.IWrapperItemStack; -import minecrafttransportsimulator.mcinterface.IWrapperPlayer; -import minecrafttransportsimulator.mcinterface.InterfaceManager; -import minecrafttransportsimulator.packloading.PackParser; -import minecrafttransportsimulator.rendering.RenderText; -import minecrafttransportsimulator.systems.CameraSystem.CameraMode; -import minecrafttransportsimulator.systems.ConfigSystem; -import minecrafttransportsimulator.systems.ControlSystem; -import minecrafttransportsimulator.systems.LanguageSystem; -import net.minecraft.block.SoundType; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screen.ChatScreen; -import net.minecraft.client.settings.PointOfView; -import net.minecraft.client.util.ITooltipFlag; -import net.minecraft.fluid.Fluid; -import net.minecraft.util.RegistryKey; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.SoundCategory; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.vector.Vector3d; -import net.minecraft.util.text.Color; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.Style; -import net.minecraft.util.text.StringTextComponent; -import net.minecraft.util.text.TextFormatting; -import net.minecraft.util.text.TranslationTextComponent; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.event.TickEvent.Phase; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber; -import net.minecraftforge.registries.ForgeRegistries; - -@EventBusSubscriber(Dist.CLIENT) -public class InterfaceClient implements IInterfaceClient { - private static CameraMode actualCameraMode; - private static CameraMode cameraModeRequest; - private static int ticksToCullingWarning = 200; - - @Override - public boolean isGamePaused() { - return Minecraft.getInstance().isPaused(); - } - - @Override - public String getLanguageName() { - if (Minecraft.getInstance().getLanguageManager() != null) { - return Minecraft.getInstance().getLanguageManager().getSelected().getCode(); - } else { - return "en_us"; - } - } - - @Override - public List getAllLanguages() { - List list = new ArrayList<>(); - Minecraft.getInstance().getLanguageManager().getLanguages().forEach(language -> list.add(language.getCode())); - return list; - } - - @Override - public String getFluidName(String fluidID, String fluidMod) { - for (Entry, Fluid> fluidEntry : ForgeRegistries.FLUIDS.getEntries()) { - ResourceLocation fluidLocation = fluidEntry.getKey().location(); - if ((fluidMod.equals(EntityFluidTank.WILDCARD_FLUID_MOD) || fluidLocation.getNamespace().equals(fluidMod)) && fluidLocation.getPath().equals(fluidID)) { - return new TranslationTextComponent(fluidEntry.getValue().getAttributes().getTranslationKey()).getString(); - } - } - return "INVALID"; - } - - @Override - public Map getAllFluidNames() { - Map fluidIDsToNames = new HashMap<>(); - for (Fluid fluid : ForgeRegistries.FLUIDS.getValues()) { - fluidIDsToNames.put(fluid.getRegistryName().getPath(), new FluidStack(fluid, 1).getDisplayName().getString()); - } - return fluidIDsToNames; - } - - @Override - public boolean isChatOpen() { - return Minecraft.getInstance().screen instanceof ChatScreen; - } - - @Override - public boolean isGUIOpen() { - return Minecraft.getInstance().screen != null; - } - - @Override - public void displayOverlayMessage(String message) { - Minecraft.getInstance().gui.setOverlayMessage(new StringTextComponent(message), false); - } - - @Override - public CameraMode getCameraMode() { - return actualCameraMode; - } - - @Override - public void setCameraMode(CameraMode mode) { - cameraModeRequest = mode; - } - - @Override - public int getCameraDefaultZoom() { - return 0; - } - - @Override - public long getPackedDisplaySize() { - return (((long) Minecraft.getInstance().getWindow().getGuiScaledWidth()) << Integer.SIZE) | (Minecraft.getInstance().getWindow().getGuiScaledHeight() & 0xffffffffL); - } - - @Override - public float getFOV() { - return (float) Minecraft.getInstance().options.fov; - } - - @Override - public void setFOV(float setting) { - Minecraft.getInstance().options.fov = setting; - } - - @Override - public float getMouseSensitivity() { - return (float) Minecraft.getInstance().options.sensitivity; - } - +package mcinterface1165; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.nio.FloatBuffer; + +import minecrafttransportsimulator.baseclasses.Point3D; +import minecrafttransportsimulator.baseclasses.RotationMatrix; +import minecrafttransportsimulator.entities.instances.EntityFluidTank; +import minecrafttransportsimulator.guis.components.AGUIBase; +import minecrafttransportsimulator.guis.instances.GUIPackMissing; +import minecrafttransportsimulator.mcinterface.AWrapperWorld; +import minecrafttransportsimulator.mcinterface.IInterfaceClient; +import minecrafttransportsimulator.mcinterface.IWrapperItemStack; +import minecrafttransportsimulator.mcinterface.IWrapperPlayer; +import minecrafttransportsimulator.mcinterface.InterfaceManager; +import minecrafttransportsimulator.packloading.PackParser; +import minecrafttransportsimulator.rendering.RenderText; +import minecrafttransportsimulator.systems.CameraSystem.CameraMode; +import minecrafttransportsimulator.systems.ConfigSystem; +import minecrafttransportsimulator.systems.ControlSystem; +import minecrafttransportsimulator.systems.LanguageSystem; +import net.minecraft.block.SoundType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screen.ChatScreen; +import net.minecraft.client.settings.PointOfView; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.fluid.Fluid; +import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.util.text.Color; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.Style; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.util.text.TranslationTextComponent; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.TickEvent.Phase; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import net.minecraftforge.registries.ForgeRegistries; + +@EventBusSubscriber(Dist.CLIENT) +public class InterfaceClient implements IInterfaceClient { + private static CameraMode actualCameraMode; + private static CameraMode cameraModeRequest; + private static int ticksToCullingWarning = 200; + + @Override + public boolean isGamePaused() { + return Minecraft.getInstance().isPaused(); + } + + @Override + public String getLanguageName() { + if (Minecraft.getInstance().getLanguageManager() != null) { + return Minecraft.getInstance().getLanguageManager().getSelected().getCode(); + } else { + return "en_us"; + } + } + + @Override + public List getAllLanguages() { + List list = new ArrayList<>(); + Minecraft.getInstance().getLanguageManager().getLanguages().forEach(language -> list.add(language.getCode())); + return list; + } + + @Override + public String getFluidName(String fluidID, String fluidMod) { + for (Entry, Fluid> fluidEntry : ForgeRegistries.FLUIDS.getEntries()) { + ResourceLocation fluidLocation = fluidEntry.getKey().location(); + if ((fluidMod.equals(EntityFluidTank.WILDCARD_FLUID_MOD) || fluidLocation.getNamespace().equals(fluidMod)) && fluidLocation.getPath().equals(fluidID)) { + return new TranslationTextComponent(fluidEntry.getValue().getAttributes().getTranslationKey()).getString(); + } + } + return "INVALID"; + } + + @Override + public Map getAllFluidNames() { + Map fluidIDsToNames = new HashMap<>(); + for (Fluid fluid : ForgeRegistries.FLUIDS.getValues()) { + fluidIDsToNames.put(fluid.getRegistryName().getPath(), new FluidStack(fluid, 1).getDisplayName().getString()); + } + return fluidIDsToNames; + } + + @Override + public boolean isChatOpen() { + return Minecraft.getInstance().screen instanceof ChatScreen; + } + + @Override + public boolean isGUIOpen() { + return Minecraft.getInstance().screen != null; + } + + @Override + public void displayOverlayMessage(String message) { + Minecraft.getInstance().gui.setOverlayMessage(new StringTextComponent(message), false); + } + + @Override + public CameraMode getCameraMode() { + return actualCameraMode; + } + + @Override + public void setCameraMode(CameraMode mode) { + cameraModeRequest = mode; + } + + @Override + public int getCameraDefaultZoom() { + return 0; + } + + @Override + public long getPackedDisplaySize() { + return (((long) Minecraft.getInstance().getWindow().getGuiScaledWidth()) << Integer.SIZE) | (Minecraft.getInstance().getWindow().getGuiScaledHeight() & 0xffffffffL); + } + + @Override + public float getFOV() { + return (float) Minecraft.getInstance().options.fov; + } + + @Override + public void setFOV(float setting) { + Minecraft.getInstance().options.fov = setting; + } + + @Override + public float getMouseSensitivity() { + return (float) Minecraft.getInstance().options.sensitivity; + } + @Override public void setMouseSensitivity(float setting) { Minecraft.getInstance().options.sensitivity = setting; @@ -159,242 +159,242 @@ public boolean isGUIHidden() { public void closeGUI() { Minecraft.getInstance().setScreen(null); } - - @Override - public void setActiveGUI(AGUIBase gui) { - Minecraft.getInstance().setScreen(new BuilderGUI(gui)); - } - - @Override - public WrapperWorld getClientWorld() { - return WrapperWorld.getWrapperFor(Minecraft.getInstance().level); - } - - @Override - public WrapperPlayer getClientPlayer() { - return WrapperPlayer.getWrapperFor(Minecraft.getInstance().player); - } - - @Override - public Point3D getCameraPosition() { - Vector3d cameraOffset = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); - mutablePosition.set(cameraOffset.x, cameraOffset.y, cameraOffset.z); - return mutablePosition; - } - - private static final Point3D mutablePosition = new Point3D(); - private static final RotationMatrix cameraProjectionOrientation = new RotationMatrix(); - private static final FloatBuffer projectionMatrixBuffer = FloatBuffer.allocate(16); - - @Override - public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight) { - double camX, camY, camZ; - double fwdX, fwdY, fwdZ; - double upX, upY, upZ; - double rgtX, rgtY, rgtZ; - - if (InterfaceEventsEntityRendering.adjustedCamera) { - camX = InterfaceEventsEntityRendering.cameraAdjustedPosition.x; - camY = InterfaceEventsEntityRendering.cameraAdjustedPosition.y; - camZ = InterfaceEventsEntityRendering.cameraAdjustedPosition.z; - RotationMatrix ori = getCameraProjectionOrientation(InterfaceEventsEntityRendering.cameraAdjustedOrientation); - fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; - upX = ori.m01; upY = ori.m11; upZ = ori.m21; - // MTS (1,0,0) rotated = camera LEFT (not right); negate to get camera right. - rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; - } else { - net.minecraft.client.renderer.ActiveRenderInfo camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - net.minecraft.util.math.vector.Vector3d camPos = camera.getPosition(); - camX = camPos.x; camY = camPos.y; camZ = camPos.z; - net.minecraft.util.math.vector.Vector3f look = camera.getLookVector(); - net.minecraft.util.math.vector.Vector3f up = camera.getUpVector(); - fwdX = look.x(); fwdY = look.y(); fwdZ = look.z(); - upX = up.x(); upY = up.y(); upZ = up.z(); - // getLeftVector() does not exist in 1.16.5; compute right = up × look manually. - rgtX = upY * fwdZ - upZ * fwdY; - rgtY = upZ * fwdX - upX * fwdZ; - rgtZ = upX * fwdY - upY * fwdX; - } - - double dx = worldPos.x - camX; - double dy = worldPos.y - camY; - double dz = worldPos.z - camZ; - - double depth = dx * fwdX + dy * fwdY + dz * fwdZ; - if (depth <= 0.001) return null; - - double xView = dx * rgtX + dy * rgtY + dz * rgtZ; - double yView = dx * upX + dy * upY + dz * upZ; - - double fovRad = Math.toRadians(getFOV()); - double tanHalfFov = Math.tan(fovRad / 2.0); - double aspect = (double) screenWidth / screenHeight; - double ndcX = xView / (depth * tanHalfFov * aspect); - double ndcY = yView / (depth * tanHalfFov); - if (InterfaceRender.projectionMatrix != null) { - projectionMatrixBuffer.clear(); - InterfaceRender.projectionMatrix.store(projectionMatrixBuffer); - double projectionScaleX = Math.abs(projectionMatrixBuffer.get(0)); - double projectionScaleY = Math.abs(projectionMatrixBuffer.get(5)); - if (projectionScaleX > 0 && projectionScaleY > 0) { - ndcX = xView * projectionScaleX / depth; - ndcY = yView * projectionScaleY / depth; - } - } - - if (ndcX < -1.1 || ndcX > 1.1 || ndcY < -1.1 || ndcY > 1.1) return null; - - screenProjectionResult.set( - (ndcX + 1.0) / 2.0 * screenWidth, - (1.0 - ndcY) / 2.0 * screenHeight, - depth); - return screenProjectionResult; - } - - private static RotationMatrix getCameraProjectionOrientation(RotationMatrix cameraOrientation) { - if (actualCameraMode == CameraMode.THIRD_PERSON_INVERTED) { - cameraProjectionOrientation.angles.set(-cameraOrientation.angles.x, cameraOrientation.angles.y - 180, -cameraOrientation.angles.z); - cameraProjectionOrientation.updateToAngles(); - return cameraProjectionOrientation; - } else { - return cameraOrientation; - } - } - - private static final Point3D screenProjectionResult = new Point3D(); - - @Override - public void playBlockBreakSound(Point3D position) { - BlockPos pos = new BlockPos(position.x, position.y, position.z); - if (!Minecraft.getInstance().level.isEmptyBlock(pos)) { - SoundType soundType = Minecraft.getInstance().level.getBlockState(pos).getBlock().getSoundType(Minecraft.getInstance().level.getBlockState(pos), Minecraft.getInstance().player.level, pos, null); - Minecraft.getInstance().level.playSound(Minecraft.getInstance().player, pos, soundType.getBreakSound(), SoundCategory.BLOCKS, soundType.getVolume(), soundType.getPitch()); - } - } - - @Override - public List getTooltipLines(IWrapperItemStack stack) { - List tooltipText = new ArrayList<>(); - List tooltipLines = ((WrapperItemStack) stack).stack.getTooltipLines(Minecraft.getInstance().player, Minecraft.getInstance().options.advancedItemTooltips ? ITooltipFlag.TooltipFlags.ADVANCED : ITooltipFlag.TooltipFlags.NORMAL); - //Add grey formatting text to non-first line tooltips. - for (int i = 0; i < tooltipLines.size(); ++i) { - ITextComponent component = tooltipLines.get(i); - Style style = component.getStyle(); - String stringToAdd = ""; - if (style.isBold()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.BOLD_FORMATTING_CHAR; - } - if (style.isItalic()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.ITALIC_FORMATTING_CHAR; - } - if (style.isUnderlined()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.UNDERLINE_FORMATTING_CHAR; - } - if (style.isStrikethrough()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.STRIKETHROUGH_FORMATTING_CHAR; - } - if (style.isObfuscated()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.RANDOM_FORMATTING_CHAR; - } - if (style.getColor() != null) { - TextFormatting legacyColor = null; - for (TextFormatting format : TextFormatting.values()) { - if (format.isColor()) { - if (style.getColor().equals(Color.fromLegacyFormat(format))) { - legacyColor = format; - break; - } - } - } - if (legacyColor != null) { - stringToAdd += RenderText.FORMATTING_CHAR + Integer.toHexString(legacyColor.ordinal()); - } - } - tooltipText.add(stringToAdd + tooltipLines.get(i).getString()); - } - return tooltipText; - } - - /** - * Tick client-side entities like bullets and particles. - * These don't get ticked normally due to the world tick event - * not being called on clients. - */ - @SubscribeEvent - public static void onIVClientTick(TickEvent.ClientTickEvent event) { - IWrapperPlayer player = InterfaceManager.clientInterface.getClientPlayer(); - if (!InterfaceManager.clientInterface.isGamePaused() && player != null) { - AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); - if (world != null) { - if (event.phase.equals(Phase.START)) { - if (!player.isSpectator()) { - //Handle controls. This has to happen prior to vehicle updates to ensure click handling is based on current position of the player. - ControlSystem.controlGlobal(player); - if (((WrapperPlayer) player).player.tickCount % 100 == 0) { - if (!InterfaceManager.clientInterface.isGUIOpen() && !PackParser.arePacksPresent()) { - new GUIPackMissing(); - } - } - } - - //Need to update world brightness since sky darken isn't calculated normally on clients. - ((WrapperWorld) world).world.updateSkyBrightness(); - - world.tickAll(true); - - //Complain about compats at 10 second mark. - if (ConfigSystem.settings.general.performModCompatFunctions.value) { - if(ticksToCullingWarning > 0) { - if(--ticksToCullingWarning == 0) { - if (InterfaceManager.coreInterface.isModPresent("entityculling")) { - player.displayChatMessage(LanguageSystem.SYSTEM_DEBUG, "IV HAS DETECTED THAT ENTITY CULLING MOD IS PRESENT. THIS MOD CULLS ALL IV VEHICLES UNLESS \"mts:builder_existing\", \"mts:builder_rendering\", AND \"mts:builder_seat\" ARE ADDED TO THE WHITELIST."); - } - if (InterfaceManager.coreInterface.isModPresent("modernfix")) { - player.displayChatMessage(LanguageSystem.SYSTEM_DEBUG, "IV HAS DETECTED THAT MODERNFIX MOD IS PRESENT. IF DYNAMIC RESOURCES IS SET TO TRUE IV ITEMS WILL NOT RENDER PROPERLY."); - } - } - } - } - } else { - world.tickAll(false); - - //Handle camera requests. - if(cameraModeRequest != null) { - switch(cameraModeRequest) { - case FIRST_PERSON:{ - Minecraft.getInstance().options.setCameraType(PointOfView.FIRST_PERSON); - break; - } - case THIRD_PERSON:{ - Minecraft.getInstance().options.setCameraType(PointOfView.THIRD_PERSON_BACK); - break; - } - case THIRD_PERSON_INVERTED:{ - Minecraft.getInstance().options.setCameraType(PointOfView.THIRD_PERSON_FRONT); - break; - } - } - cameraModeRequest = null; - } - - //Update camera state, since this can change depending on tick if we check during renders. - PointOfView cameraModeEnum = Minecraft.getInstance().options.getCameraType(); - switch(cameraModeEnum) { - case FIRST_PERSON:{ - actualCameraMode = CameraMode.FIRST_PERSON; - break; - } - case THIRD_PERSON_BACK:{ - actualCameraMode = CameraMode.THIRD_PERSON; - break; - } - case THIRD_PERSON_FRONT:{ - actualCameraMode = CameraMode.THIRD_PERSON_INVERTED; - break; - } - } - } - } - } - } -} + + @Override + public void setActiveGUI(AGUIBase gui) { + Minecraft.getInstance().setScreen(new BuilderGUI(gui)); + } + + @Override + public WrapperWorld getClientWorld() { + return WrapperWorld.getWrapperFor(Minecraft.getInstance().level); + } + + @Override + public WrapperPlayer getClientPlayer() { + return WrapperPlayer.getWrapperFor(Minecraft.getInstance().player); + } + + @Override + public Point3D getCameraPosition() { + Vector3d cameraOffset = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); + mutablePosition.set(cameraOffset.x, cameraOffset.y, cameraOffset.z); + return mutablePosition; + } + + private static final Point3D mutablePosition = new Point3D(); + private static final RotationMatrix cameraProjectionOrientation = new RotationMatrix(); + private static final FloatBuffer projectionMatrixBuffer = FloatBuffer.allocate(16); + + @Override + public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight) { + double camX, camY, camZ; + double fwdX, fwdY, fwdZ; + double upX, upY, upZ; + double rgtX, rgtY, rgtZ; + + if (InterfaceEventsEntityRendering.adjustedCamera) { + camX = InterfaceEventsEntityRendering.cameraAdjustedPosition.x; + camY = InterfaceEventsEntityRendering.cameraAdjustedPosition.y; + camZ = InterfaceEventsEntityRendering.cameraAdjustedPosition.z; + RotationMatrix ori = getCameraProjectionOrientation(InterfaceEventsEntityRendering.cameraAdjustedOrientation); + fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; + upX = ori.m01; upY = ori.m11; upZ = ori.m21; + // MTS (1,0,0) rotated = camera LEFT (not right); negate to get camera right. + rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; + } else { + net.minecraft.client.renderer.ActiveRenderInfo camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + net.minecraft.util.math.vector.Vector3d camPos = camera.getPosition(); + camX = camPos.x; camY = camPos.y; camZ = camPos.z; + net.minecraft.util.math.vector.Vector3f look = camera.getLookVector(); + net.minecraft.util.math.vector.Vector3f up = camera.getUpVector(); + fwdX = look.x(); fwdY = look.y(); fwdZ = look.z(); + upX = up.x(); upY = up.y(); upZ = up.z(); + // getLeftVector() does not exist in 1.16.5; compute right = up × look manually. + rgtX = upY * fwdZ - upZ * fwdY; + rgtY = upZ * fwdX - upX * fwdZ; + rgtZ = upX * fwdY - upY * fwdX; + } + + double dx = worldPos.x - camX; + double dy = worldPos.y - camY; + double dz = worldPos.z - camZ; + + double depth = dx * fwdX + dy * fwdY + dz * fwdZ; + if (depth <= 0.001) return null; + + double xView = dx * rgtX + dy * rgtY + dz * rgtZ; + double yView = dx * upX + dy * upY + dz * upZ; + + double fovRad = Math.toRadians(getFOV()); + double tanHalfFov = Math.tan(fovRad / 2.0); + double aspect = (double) screenWidth / screenHeight; + double ndcX = xView / (depth * tanHalfFov * aspect); + double ndcY = yView / (depth * tanHalfFov); + if (InterfaceRender.projectionMatrix != null) { + projectionMatrixBuffer.clear(); + InterfaceRender.projectionMatrix.store(projectionMatrixBuffer); + double projectionScaleX = Math.abs(projectionMatrixBuffer.get(0)); + double projectionScaleY = Math.abs(projectionMatrixBuffer.get(5)); + if (projectionScaleX > 0 && projectionScaleY > 0) { + ndcX = xView * projectionScaleX / depth; + ndcY = yView * projectionScaleY / depth; + } + } + + if (ndcX < -1.1 || ndcX > 1.1 || ndcY < -1.1 || ndcY > 1.1) return null; + + screenProjectionResult.set( + (ndcX + 1.0) / 2.0 * screenWidth, + (1.0 - ndcY) / 2.0 * screenHeight, + depth); + return screenProjectionResult; + } + + private static RotationMatrix getCameraProjectionOrientation(RotationMatrix cameraOrientation) { + if (actualCameraMode == CameraMode.THIRD_PERSON_INVERTED) { + cameraProjectionOrientation.angles.set(-cameraOrientation.angles.x, cameraOrientation.angles.y - 180, -cameraOrientation.angles.z); + cameraProjectionOrientation.updateToAngles(); + return cameraProjectionOrientation; + } else { + return cameraOrientation; + } + } + + private static final Point3D screenProjectionResult = new Point3D(); + + @Override + public void playBlockBreakSound(Point3D position) { + BlockPos pos = new BlockPos(position.x, position.y, position.z); + if (!Minecraft.getInstance().level.isEmptyBlock(pos)) { + SoundType soundType = Minecraft.getInstance().level.getBlockState(pos).getBlock().getSoundType(Minecraft.getInstance().level.getBlockState(pos), Minecraft.getInstance().player.level, pos, null); + Minecraft.getInstance().level.playSound(Minecraft.getInstance().player, pos, soundType.getBreakSound(), SoundCategory.BLOCKS, soundType.getVolume(), soundType.getPitch()); + } + } + + @Override + public List getTooltipLines(IWrapperItemStack stack) { + List tooltipText = new ArrayList<>(); + List tooltipLines = ((WrapperItemStack) stack).stack.getTooltipLines(Minecraft.getInstance().player, Minecraft.getInstance().options.advancedItemTooltips ? ITooltipFlag.TooltipFlags.ADVANCED : ITooltipFlag.TooltipFlags.NORMAL); + //Add grey formatting text to non-first line tooltips. + for (int i = 0; i < tooltipLines.size(); ++i) { + ITextComponent component = tooltipLines.get(i); + Style style = component.getStyle(); + String stringToAdd = ""; + if (style.isBold()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.BOLD_FORMATTING_CHAR; + } + if (style.isItalic()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.ITALIC_FORMATTING_CHAR; + } + if (style.isUnderlined()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.UNDERLINE_FORMATTING_CHAR; + } + if (style.isStrikethrough()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.STRIKETHROUGH_FORMATTING_CHAR; + } + if (style.isObfuscated()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.RANDOM_FORMATTING_CHAR; + } + if (style.getColor() != null) { + TextFormatting legacyColor = null; + for (TextFormatting format : TextFormatting.values()) { + if (format.isColor()) { + if (style.getColor().equals(Color.fromLegacyFormat(format))) { + legacyColor = format; + break; + } + } + } + if (legacyColor != null) { + stringToAdd += RenderText.FORMATTING_CHAR + Integer.toHexString(legacyColor.ordinal()); + } + } + tooltipText.add(stringToAdd + tooltipLines.get(i).getString()); + } + return tooltipText; + } + + /** + * Tick client-side entities like bullets and particles. + * These don't get ticked normally due to the world tick event + * not being called on clients. + */ + @SubscribeEvent + public static void onIVClientTick(TickEvent.ClientTickEvent event) { + IWrapperPlayer player = InterfaceManager.clientInterface.getClientPlayer(); + if (!InterfaceManager.clientInterface.isGamePaused() && player != null) { + AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); + if (world != null) { + if (event.phase.equals(Phase.START)) { + if (!player.isSpectator()) { + //Handle controls. This has to happen prior to vehicle updates to ensure click handling is based on current position of the player. + ControlSystem.controlGlobal(player); + if (((WrapperPlayer) player).player.tickCount % 100 == 0) { + if (!InterfaceManager.clientInterface.isGUIOpen() && !PackParser.arePacksPresent()) { + new GUIPackMissing(); + } + } + } + + //Need to update world brightness since sky darken isn't calculated normally on clients. + ((WrapperWorld) world).world.updateSkyBrightness(); + + world.tickAll(true); + + //Complain about compats at 10 second mark. + if (ConfigSystem.settings.general.performModCompatFunctions.value) { + if(ticksToCullingWarning > 0) { + if(--ticksToCullingWarning == 0) { + if (InterfaceManager.coreInterface.isModPresent("entityculling")) { + player.displayChatMessage(LanguageSystem.SYSTEM_DEBUG, "IV HAS DETECTED THAT ENTITY CULLING MOD IS PRESENT. THIS MOD CULLS ALL IV VEHICLES UNLESS \"mts:builder_existing\", \"mts:builder_rendering\", AND \"mts:builder_seat\" ARE ADDED TO THE WHITELIST."); + } + if (InterfaceManager.coreInterface.isModPresent("modernfix")) { + player.displayChatMessage(LanguageSystem.SYSTEM_DEBUG, "IV HAS DETECTED THAT MODERNFIX MOD IS PRESENT. IF DYNAMIC RESOURCES IS SET TO TRUE IV ITEMS WILL NOT RENDER PROPERLY."); + } + } + } + } + } else { + world.tickAll(false); + + //Handle camera requests. + if(cameraModeRequest != null) { + switch(cameraModeRequest) { + case FIRST_PERSON:{ + Minecraft.getInstance().options.setCameraType(PointOfView.FIRST_PERSON); + break; + } + case THIRD_PERSON:{ + Minecraft.getInstance().options.setCameraType(PointOfView.THIRD_PERSON_BACK); + break; + } + case THIRD_PERSON_INVERTED:{ + Minecraft.getInstance().options.setCameraType(PointOfView.THIRD_PERSON_FRONT); + break; + } + } + cameraModeRequest = null; + } + + //Update camera state, since this can change depending on tick if we check during renders. + PointOfView cameraModeEnum = Minecraft.getInstance().options.getCameraType(); + switch(cameraModeEnum) { + case FIRST_PERSON:{ + actualCameraMode = CameraMode.FIRST_PERSON; + break; + } + case THIRD_PERSON_BACK:{ + actualCameraMode = CameraMode.THIRD_PERSON; + break; + } + case THIRD_PERSON_FRONT:{ + actualCameraMode = CameraMode.THIRD_PERSON_INVERTED; + break; + } + } + } + } + } + } +} diff --git a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java index cbc2a40cc..8e3464d3e 100644 --- a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java +++ b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java @@ -1,150 +1,150 @@ -package mcinterface1182; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.nio.FloatBuffer; - -import minecrafttransportsimulator.baseclasses.Point3D; -import minecrafttransportsimulator.baseclasses.RotationMatrix; -import minecrafttransportsimulator.entities.instances.EntityFluidTank; -import minecrafttransportsimulator.guis.components.AGUIBase; -import minecrafttransportsimulator.guis.instances.GUIPackMissing; -import minecrafttransportsimulator.mcinterface.AWrapperWorld; -import minecrafttransportsimulator.mcinterface.IInterfaceClient; -import minecrafttransportsimulator.mcinterface.IWrapperItemStack; -import minecrafttransportsimulator.mcinterface.IWrapperPlayer; -import minecrafttransportsimulator.mcinterface.InterfaceManager; -import minecrafttransportsimulator.packloading.PackParser; -import minecrafttransportsimulator.rendering.RenderText; -import minecrafttransportsimulator.systems.CameraSystem.CameraMode; -import minecrafttransportsimulator.systems.ConfigSystem; -import minecrafttransportsimulator.systems.ControlSystem; -import minecrafttransportsimulator.systems.LanguageSystem; -import net.minecraft.ChatFormatting; -import net.minecraft.client.CameraType; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.ChatScreen; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.Style; -import net.minecraft.network.chat.TextComponent; -import net.minecraft.network.chat.TextColor; -import net.minecraft.network.chat.TranslatableComponent; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.item.TooltipFlag; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.phys.Vec3; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.event.TickEvent.Phase; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber; -import net.minecraftforge.registries.ForgeRegistries; - -@EventBusSubscriber(Dist.CLIENT) -public class InterfaceClient implements IInterfaceClient { - private static CameraMode actualCameraMode; - private static CameraMode cameraModeRequest; - private static int ticksToCullingWarning = 200; - - @Override - public boolean isGamePaused() { - return Minecraft.getInstance().isPaused(); - } - - @Override - public String getLanguageName() { - if (Minecraft.getInstance().getLanguageManager() != null) { - return Minecraft.getInstance().getLanguageManager().getSelected().getCode(); - } else { - return "en_us"; - } - } - - @Override - public List getAllLanguages() { - List list = new ArrayList<>(); - Minecraft.getInstance().getLanguageManager().getLanguages().forEach(language -> list.add(language.getCode())); - return list; - } - - @Override - public String getFluidName(String fluidID, String fluidMod) { - for (Entry, Fluid> fluidEntry : ForgeRegistries.FLUIDS.getEntries()) { - ResourceLocation fluidLocation = fluidEntry.getKey().location(); - if ((fluidMod.equals(EntityFluidTank.WILDCARD_FLUID_MOD) || fluidLocation.getNamespace().equals(fluidMod)) && fluidLocation.getPath().equals(fluidID)) { - return new TranslatableComponent(fluidEntry.getValue().getAttributes().getTranslationKey()).getString(); - } - } - return "INVALID"; - } - - @Override - public Map getAllFluidNames() { - Map fluidIDsToNames = new HashMap<>(); - for (Fluid fluid : ForgeRegistries.FLUIDS.getValues()) { - fluidIDsToNames.put(fluid.getRegistryName().getPath(), new FluidStack(fluid, 1).getDisplayName().getString()); - } - return fluidIDsToNames; - } - - @Override - public boolean isChatOpen() { - return Minecraft.getInstance().screen instanceof ChatScreen; - } - - @Override - public boolean isGUIOpen() { - return Minecraft.getInstance().screen != null; - } - - @Override - public void displayOverlayMessage(String message) { - Minecraft.getInstance().gui.setOverlayMessage(new TextComponent(message), false); - } - - @Override - public CameraMode getCameraMode() { - return actualCameraMode; - } - - @Override - public void setCameraMode(CameraMode mode) { - cameraModeRequest = mode; - } - - @Override - public int getCameraDefaultZoom() { - return 0; - } - - @Override - public long getPackedDisplaySize() { - return (((long) Minecraft.getInstance().getWindow().getGuiScaledWidth()) << Integer.SIZE) | (Minecraft.getInstance().getWindow().getGuiScaledHeight() & 0xffffffffL); - } - - @Override - public float getFOV() { - return (float) Minecraft.getInstance().options.fov; - } - - @Override - public void setFOV(float setting) { - Minecraft.getInstance().options.fov = setting; - } - - @Override - public float getMouseSensitivity() { - return (float) Minecraft.getInstance().options.sensitivity; - } - +package mcinterface1182; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.nio.FloatBuffer; + +import minecrafttransportsimulator.baseclasses.Point3D; +import minecrafttransportsimulator.baseclasses.RotationMatrix; +import minecrafttransportsimulator.entities.instances.EntityFluidTank; +import minecrafttransportsimulator.guis.components.AGUIBase; +import minecrafttransportsimulator.guis.instances.GUIPackMissing; +import minecrafttransportsimulator.mcinterface.AWrapperWorld; +import minecrafttransportsimulator.mcinterface.IInterfaceClient; +import minecrafttransportsimulator.mcinterface.IWrapperItemStack; +import minecrafttransportsimulator.mcinterface.IWrapperPlayer; +import minecrafttransportsimulator.mcinterface.InterfaceManager; +import minecrafttransportsimulator.packloading.PackParser; +import minecrafttransportsimulator.rendering.RenderText; +import minecrafttransportsimulator.systems.CameraSystem.CameraMode; +import minecrafttransportsimulator.systems.ConfigSystem; +import minecrafttransportsimulator.systems.ControlSystem; +import minecrafttransportsimulator.systems.LanguageSystem; +import net.minecraft.ChatFormatting; +import net.minecraft.client.CameraType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.ChatScreen; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TextColor; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.TickEvent.Phase; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import net.minecraftforge.registries.ForgeRegistries; + +@EventBusSubscriber(Dist.CLIENT) +public class InterfaceClient implements IInterfaceClient { + private static CameraMode actualCameraMode; + private static CameraMode cameraModeRequest; + private static int ticksToCullingWarning = 200; + + @Override + public boolean isGamePaused() { + return Minecraft.getInstance().isPaused(); + } + + @Override + public String getLanguageName() { + if (Minecraft.getInstance().getLanguageManager() != null) { + return Minecraft.getInstance().getLanguageManager().getSelected().getCode(); + } else { + return "en_us"; + } + } + + @Override + public List getAllLanguages() { + List list = new ArrayList<>(); + Minecraft.getInstance().getLanguageManager().getLanguages().forEach(language -> list.add(language.getCode())); + return list; + } + + @Override + public String getFluidName(String fluidID, String fluidMod) { + for (Entry, Fluid> fluidEntry : ForgeRegistries.FLUIDS.getEntries()) { + ResourceLocation fluidLocation = fluidEntry.getKey().location(); + if ((fluidMod.equals(EntityFluidTank.WILDCARD_FLUID_MOD) || fluidLocation.getNamespace().equals(fluidMod)) && fluidLocation.getPath().equals(fluidID)) { + return new TranslatableComponent(fluidEntry.getValue().getAttributes().getTranslationKey()).getString(); + } + } + return "INVALID"; + } + + @Override + public Map getAllFluidNames() { + Map fluidIDsToNames = new HashMap<>(); + for (Fluid fluid : ForgeRegistries.FLUIDS.getValues()) { + fluidIDsToNames.put(fluid.getRegistryName().getPath(), new FluidStack(fluid, 1).getDisplayName().getString()); + } + return fluidIDsToNames; + } + + @Override + public boolean isChatOpen() { + return Minecraft.getInstance().screen instanceof ChatScreen; + } + + @Override + public boolean isGUIOpen() { + return Minecraft.getInstance().screen != null; + } + + @Override + public void displayOverlayMessage(String message) { + Minecraft.getInstance().gui.setOverlayMessage(new TextComponent(message), false); + } + + @Override + public CameraMode getCameraMode() { + return actualCameraMode; + } + + @Override + public void setCameraMode(CameraMode mode) { + cameraModeRequest = mode; + } + + @Override + public int getCameraDefaultZoom() { + return 0; + } + + @Override + public long getPackedDisplaySize() { + return (((long) Minecraft.getInstance().getWindow().getGuiScaledWidth()) << Integer.SIZE) | (Minecraft.getInstance().getWindow().getGuiScaledHeight() & 0xffffffffL); + } + + @Override + public float getFOV() { + return (float) Minecraft.getInstance().options.fov; + } + + @Override + public void setFOV(float setting) { + Minecraft.getInstance().options.fov = setting; + } + + @Override + public float getMouseSensitivity() { + return (float) Minecraft.getInstance().options.sensitivity; + } + @Override public void setMouseSensitivity(float setting) { Minecraft.getInstance().options.sensitivity = setting; @@ -159,236 +159,236 @@ public boolean isGUIHidden() { public void closeGUI() { Minecraft.getInstance().setScreen(null); } - - @Override - public void setActiveGUI(AGUIBase gui) { - Minecraft.getInstance().setScreen(new BuilderGUI(gui)); - } - - @Override - public WrapperWorld getClientWorld() { - return WrapperWorld.getWrapperFor(Minecraft.getInstance().level); - } - - @Override - public WrapperPlayer getClientPlayer() { - return WrapperPlayer.getWrapperFor(Minecraft.getInstance().player); - } - - @Override - public Point3D getCameraPosition() { - Vec3 cameraOffset = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); - mutablePosition.set(cameraOffset.x, cameraOffset.y, cameraOffset.z); - return mutablePosition; - } - - private static final Point3D mutablePosition = new Point3D(); - private static final RotationMatrix cameraProjectionOrientation = new RotationMatrix(); - private static final FloatBuffer projectionMatrixBuffer = FloatBuffer.allocate(16); - - @Override - public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight) { - double camX, camY, camZ; - double fwdX, fwdY, fwdZ; - double upX, upY, upZ; - double rgtX, rgtY, rgtZ; - - if (InterfaceEventsEntityRendering.adjustedCamera) { - // Use MTS camera data — includes roll and correct vehicle-relative position. - camX = InterfaceEventsEntityRendering.cameraAdjustedPosition.x; - camY = InterfaceEventsEntityRendering.cameraAdjustedPosition.y; - camZ = InterfaceEventsEntityRendering.cameraAdjustedPosition.z; - RotationMatrix ori = getCameraProjectionOrientation(InterfaceEventsEntityRendering.cameraAdjustedOrientation); - fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; - upX = ori.m01; upY = ori.m11; upZ = ori.m21; - // MTS (1,0,0) rotated = camera LEFT (not right); negate to get camera right. - rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; - } else { - net.minecraft.client.Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - net.minecraft.world.phys.Vec3 camPos = camera.getPosition(); - camX = camPos.x(); camY = camPos.y(); camZ = camPos.z(); - com.mojang.math.Vector3f look = camera.getLookVector(); - com.mojang.math.Vector3f up = camera.getUpVector(); - com.mojang.math.Vector3f left = camera.getLeftVector(); - fwdX = look.x(); fwdY = look.y(); fwdZ = look.z(); - upX = up.x(); upY = up.y(); upZ = up.z(); - rgtX = -left.x(); rgtY = -left.y(); rgtZ = -left.z(); - } - - double dx = worldPos.x - camX; - double dy = worldPos.y - camY; - double dz = worldPos.z - camZ; - - double depth = dx * fwdX + dy * fwdY + dz * fwdZ; - if (depth <= 0.001) return null; - - double xView = dx * rgtX + dy * rgtY + dz * rgtZ; - double yView = dx * upX + dy * upY + dz * upZ; - - double fovRad = Math.toRadians(getFOV()); - double tanHalfFov = Math.tan(fovRad / 2.0); - double aspect = (double) screenWidth / screenHeight; - double ndcX = xView / (depth * tanHalfFov * aspect); - double ndcY = yView / (depth * tanHalfFov); - if (InterfaceRender.projectionMatrix != null) { - projectionMatrixBuffer.clear(); - InterfaceRender.projectionMatrix.store(projectionMatrixBuffer); - double projectionScaleX = Math.abs(projectionMatrixBuffer.get(0)); - double projectionScaleY = Math.abs(projectionMatrixBuffer.get(5)); - if (projectionScaleX > 0 && projectionScaleY > 0) { - ndcX = xView * projectionScaleX / depth; - ndcY = yView * projectionScaleY / depth; - } - } - - if (ndcX < -1.1 || ndcX > 1.1 || ndcY < -1.1 || ndcY > 1.1) return null; - - screenProjectionResult.set( - (ndcX + 1.0) / 2.0 * screenWidth, - (1.0 - ndcY) / 2.0 * screenHeight, - depth); - return screenProjectionResult; - } - - private static RotationMatrix getCameraProjectionOrientation(RotationMatrix cameraOrientation) { - if (actualCameraMode == CameraMode.THIRD_PERSON_INVERTED) { - cameraProjectionOrientation.angles.set(-cameraOrientation.angles.x, cameraOrientation.angles.y - 180, -cameraOrientation.angles.z); - cameraProjectionOrientation.updateToAngles(); - return cameraProjectionOrientation; - } else { - return cameraOrientation; - } - } - - private static final Point3D screenProjectionResult = new Point3D(); - - @Override - public void playBlockBreakSound(Point3D position) { - BlockPos pos = new BlockPos(position.x, position.y, position.z); - if (!Minecraft.getInstance().level.isEmptyBlock(pos)) { - SoundType soundType = Minecraft.getInstance().level.getBlockState(pos).getBlock().getSoundType(Minecraft.getInstance().level.getBlockState(pos), Minecraft.getInstance().player.level, pos, null); - Minecraft.getInstance().level.playSound(Minecraft.getInstance().player, pos, soundType.getBreakSound(), SoundSource.BLOCKS, soundType.getVolume(), soundType.getPitch()); - } - } - - @Override - public List getTooltipLines(IWrapperItemStack stack) { - List tooltipText = new ArrayList<>(); - List tooltipLines = ((WrapperItemStack) stack).stack.getTooltipLines(Minecraft.getInstance().player, Minecraft.getInstance().options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL); - //Add grey formatting text to non-first line tooltips. - for (int i = 0; i < tooltipLines.size(); ++i) { - Component component = tooltipLines.get(i); - Style style = component.getStyle(); - String stringToAdd = ""; - if (style.isBold()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.BOLD_FORMATTING_CHAR; - } - if (style.isItalic()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.ITALIC_FORMATTING_CHAR; - } - if (style.isUnderlined()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.UNDERLINE_FORMATTING_CHAR; - } - if (style.isStrikethrough()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.STRIKETHROUGH_FORMATTING_CHAR; - } - if (style.isObfuscated()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.RANDOM_FORMATTING_CHAR; - } - if (style.getColor() != null) { - ChatFormatting legacyColor = null; - for (ChatFormatting format : ChatFormatting.values()) { - if (format.isColor()) { - if (style.getColor().equals(TextColor.fromLegacyFormat(format))) { - legacyColor = format; - break; - } - } - } - if (legacyColor != null) { - stringToAdd += RenderText.FORMATTING_CHAR + Integer.toHexString(legacyColor.ordinal()); - } - } - tooltipText.add(stringToAdd + tooltipLines.get(i).getString()); - } - return tooltipText; - } - - /** - * Tick client-side entities like bullets and particles. - * These don't get ticked normally due to the world tick event - * not being called on clients. - */ - @SubscribeEvent - public static void onIVClientTick(TickEvent.ClientTickEvent event) { - IWrapperPlayer player = InterfaceManager.clientInterface.getClientPlayer(); - if (!InterfaceManager.clientInterface.isGamePaused() && player != null) { - AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); - if (world != null) { - if (event.phase.equals(Phase.START)) { - if (!player.isSpectator()) { - //Handle controls. This has to happen prior to vehicle updates to ensure click handling is based on current position of the player. - ControlSystem.controlGlobal(player); - if (((WrapperPlayer) player).player.tickCount % 100 == 0) { - if (!InterfaceManager.clientInterface.isGUIOpen() && !PackParser.arePacksPresent()) { - new GUIPackMissing(); - } - } - } - - //Need to update world brightness since sky darken isn't calculated normally on clients. - ((WrapperWorld) world).world.updateSkyBrightness(); - - world.tickAll(true); - - //Complain about Entity Culling mod at 10 second mark. - if(ConfigSystem.settings.general.performModCompatFunctions.value && InterfaceManager.coreInterface.isModPresent("entityculling")) { - if(ticksToCullingWarning > 0) { - if(--ticksToCullingWarning == 0) { - player.displayChatMessage(LanguageSystem.SYSTEM_DEBUG, "IV HAS DETECTED THAT ENTITY CULLING MOD IS PRESENT. THIS MOD CULLS ALL IV VEHICLES UNLESS \"mts:builder_existing\", \"mts:builder_rendering\", AND \"mts:builder_seat\" ARE ADDED TO THE WHITELIST."); - } - } - } - } else { - world.tickAll(false); - - //Handle camera requests. - if(cameraModeRequest != null) { - switch(cameraModeRequest) { - case FIRST_PERSON:{ - Minecraft.getInstance().options.setCameraType(CameraType.FIRST_PERSON); - break; - } - case THIRD_PERSON:{ - Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_BACK); - break; - } - case THIRD_PERSON_INVERTED:{ - Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_FRONT); - break; - } - } - cameraModeRequest = null; - } - - //Update camera state, since this can change depending on tick if we check during renders. - CameraType cameraModeEnum = Minecraft.getInstance().options.getCameraType(); - switch(cameraModeEnum) { - case FIRST_PERSON:{ - actualCameraMode = CameraMode.FIRST_PERSON; - break; - } - case THIRD_PERSON_BACK:{ - actualCameraMode = CameraMode.THIRD_PERSON; - break; - } - case THIRD_PERSON_FRONT:{ - actualCameraMode = CameraMode.THIRD_PERSON_INVERTED; - break; - } - } - } - } - } - } -} + + @Override + public void setActiveGUI(AGUIBase gui) { + Minecraft.getInstance().setScreen(new BuilderGUI(gui)); + } + + @Override + public WrapperWorld getClientWorld() { + return WrapperWorld.getWrapperFor(Minecraft.getInstance().level); + } + + @Override + public WrapperPlayer getClientPlayer() { + return WrapperPlayer.getWrapperFor(Minecraft.getInstance().player); + } + + @Override + public Point3D getCameraPosition() { + Vec3 cameraOffset = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); + mutablePosition.set(cameraOffset.x, cameraOffset.y, cameraOffset.z); + return mutablePosition; + } + + private static final Point3D mutablePosition = new Point3D(); + private static final RotationMatrix cameraProjectionOrientation = new RotationMatrix(); + private static final FloatBuffer projectionMatrixBuffer = FloatBuffer.allocate(16); + + @Override + public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight) { + double camX, camY, camZ; + double fwdX, fwdY, fwdZ; + double upX, upY, upZ; + double rgtX, rgtY, rgtZ; + + if (InterfaceEventsEntityRendering.adjustedCamera) { + // Use MTS camera data — includes roll and correct vehicle-relative position. + camX = InterfaceEventsEntityRendering.cameraAdjustedPosition.x; + camY = InterfaceEventsEntityRendering.cameraAdjustedPosition.y; + camZ = InterfaceEventsEntityRendering.cameraAdjustedPosition.z; + RotationMatrix ori = getCameraProjectionOrientation(InterfaceEventsEntityRendering.cameraAdjustedOrientation); + fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; + upX = ori.m01; upY = ori.m11; upZ = ori.m21; + // MTS (1,0,0) rotated = camera LEFT (not right); negate to get camera right. + rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; + } else { + net.minecraft.client.Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + net.minecraft.world.phys.Vec3 camPos = camera.getPosition(); + camX = camPos.x(); camY = camPos.y(); camZ = camPos.z(); + com.mojang.math.Vector3f look = camera.getLookVector(); + com.mojang.math.Vector3f up = camera.getUpVector(); + com.mojang.math.Vector3f left = camera.getLeftVector(); + fwdX = look.x(); fwdY = look.y(); fwdZ = look.z(); + upX = up.x(); upY = up.y(); upZ = up.z(); + rgtX = -left.x(); rgtY = -left.y(); rgtZ = -left.z(); + } + + double dx = worldPos.x - camX; + double dy = worldPos.y - camY; + double dz = worldPos.z - camZ; + + double depth = dx * fwdX + dy * fwdY + dz * fwdZ; + if (depth <= 0.001) return null; + + double xView = dx * rgtX + dy * rgtY + dz * rgtZ; + double yView = dx * upX + dy * upY + dz * upZ; + + double fovRad = Math.toRadians(getFOV()); + double tanHalfFov = Math.tan(fovRad / 2.0); + double aspect = (double) screenWidth / screenHeight; + double ndcX = xView / (depth * tanHalfFov * aspect); + double ndcY = yView / (depth * tanHalfFov); + if (InterfaceRender.projectionMatrix != null) { + projectionMatrixBuffer.clear(); + InterfaceRender.projectionMatrix.store(projectionMatrixBuffer); + double projectionScaleX = Math.abs(projectionMatrixBuffer.get(0)); + double projectionScaleY = Math.abs(projectionMatrixBuffer.get(5)); + if (projectionScaleX > 0 && projectionScaleY > 0) { + ndcX = xView * projectionScaleX / depth; + ndcY = yView * projectionScaleY / depth; + } + } + + if (ndcX < -1.1 || ndcX > 1.1 || ndcY < -1.1 || ndcY > 1.1) return null; + + screenProjectionResult.set( + (ndcX + 1.0) / 2.0 * screenWidth, + (1.0 - ndcY) / 2.0 * screenHeight, + depth); + return screenProjectionResult; + } + + private static RotationMatrix getCameraProjectionOrientation(RotationMatrix cameraOrientation) { + if (actualCameraMode == CameraMode.THIRD_PERSON_INVERTED) { + cameraProjectionOrientation.angles.set(-cameraOrientation.angles.x, cameraOrientation.angles.y - 180, -cameraOrientation.angles.z); + cameraProjectionOrientation.updateToAngles(); + return cameraProjectionOrientation; + } else { + return cameraOrientation; + } + } + + private static final Point3D screenProjectionResult = new Point3D(); + + @Override + public void playBlockBreakSound(Point3D position) { + BlockPos pos = new BlockPos(position.x, position.y, position.z); + if (!Minecraft.getInstance().level.isEmptyBlock(pos)) { + SoundType soundType = Minecraft.getInstance().level.getBlockState(pos).getBlock().getSoundType(Minecraft.getInstance().level.getBlockState(pos), Minecraft.getInstance().player.level, pos, null); + Minecraft.getInstance().level.playSound(Minecraft.getInstance().player, pos, soundType.getBreakSound(), SoundSource.BLOCKS, soundType.getVolume(), soundType.getPitch()); + } + } + + @Override + public List getTooltipLines(IWrapperItemStack stack) { + List tooltipText = new ArrayList<>(); + List tooltipLines = ((WrapperItemStack) stack).stack.getTooltipLines(Minecraft.getInstance().player, Minecraft.getInstance().options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL); + //Add grey formatting text to non-first line tooltips. + for (int i = 0; i < tooltipLines.size(); ++i) { + Component component = tooltipLines.get(i); + Style style = component.getStyle(); + String stringToAdd = ""; + if (style.isBold()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.BOLD_FORMATTING_CHAR; + } + if (style.isItalic()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.ITALIC_FORMATTING_CHAR; + } + if (style.isUnderlined()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.UNDERLINE_FORMATTING_CHAR; + } + if (style.isStrikethrough()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.STRIKETHROUGH_FORMATTING_CHAR; + } + if (style.isObfuscated()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.RANDOM_FORMATTING_CHAR; + } + if (style.getColor() != null) { + ChatFormatting legacyColor = null; + for (ChatFormatting format : ChatFormatting.values()) { + if (format.isColor()) { + if (style.getColor().equals(TextColor.fromLegacyFormat(format))) { + legacyColor = format; + break; + } + } + } + if (legacyColor != null) { + stringToAdd += RenderText.FORMATTING_CHAR + Integer.toHexString(legacyColor.ordinal()); + } + } + tooltipText.add(stringToAdd + tooltipLines.get(i).getString()); + } + return tooltipText; + } + + /** + * Tick client-side entities like bullets and particles. + * These don't get ticked normally due to the world tick event + * not being called on clients. + */ + @SubscribeEvent + public static void onIVClientTick(TickEvent.ClientTickEvent event) { + IWrapperPlayer player = InterfaceManager.clientInterface.getClientPlayer(); + if (!InterfaceManager.clientInterface.isGamePaused() && player != null) { + AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); + if (world != null) { + if (event.phase.equals(Phase.START)) { + if (!player.isSpectator()) { + //Handle controls. This has to happen prior to vehicle updates to ensure click handling is based on current position of the player. + ControlSystem.controlGlobal(player); + if (((WrapperPlayer) player).player.tickCount % 100 == 0) { + if (!InterfaceManager.clientInterface.isGUIOpen() && !PackParser.arePacksPresent()) { + new GUIPackMissing(); + } + } + } + + //Need to update world brightness since sky darken isn't calculated normally on clients. + ((WrapperWorld) world).world.updateSkyBrightness(); + + world.tickAll(true); + + //Complain about Entity Culling mod at 10 second mark. + if(ConfigSystem.settings.general.performModCompatFunctions.value && InterfaceManager.coreInterface.isModPresent("entityculling")) { + if(ticksToCullingWarning > 0) { + if(--ticksToCullingWarning == 0) { + player.displayChatMessage(LanguageSystem.SYSTEM_DEBUG, "IV HAS DETECTED THAT ENTITY CULLING MOD IS PRESENT. THIS MOD CULLS ALL IV VEHICLES UNLESS \"mts:builder_existing\", \"mts:builder_rendering\", AND \"mts:builder_seat\" ARE ADDED TO THE WHITELIST."); + } + } + } + } else { + world.tickAll(false); + + //Handle camera requests. + if(cameraModeRequest != null) { + switch(cameraModeRequest) { + case FIRST_PERSON:{ + Minecraft.getInstance().options.setCameraType(CameraType.FIRST_PERSON); + break; + } + case THIRD_PERSON:{ + Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_BACK); + break; + } + case THIRD_PERSON_INVERTED:{ + Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_FRONT); + break; + } + } + cameraModeRequest = null; + } + + //Update camera state, since this can change depending on tick if we check during renders. + CameraType cameraModeEnum = Minecraft.getInstance().options.getCameraType(); + switch(cameraModeEnum) { + case FIRST_PERSON:{ + actualCameraMode = CameraMode.FIRST_PERSON; + break; + } + case THIRD_PERSON_BACK:{ + actualCameraMode = CameraMode.THIRD_PERSON; + break; + } + case THIRD_PERSON_FRONT:{ + actualCameraMode = CameraMode.THIRD_PERSON_INVERTED; + break; + } + } + } + } + } + } +} diff --git a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java index 00ec24a3a..d7ed09515 100644 --- a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java +++ b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java @@ -1,200 +1,200 @@ -package mcinterface1182; - -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentLinkedQueue; - -import javax.imageio.ImageIO; - -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.platform.NativeImage; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.BufferUploader; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.Tesselator; -import com.mojang.blaze3d.vertex.VertexBuffer; -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.blaze3d.vertex.VertexFormat; -import com.mojang.blaze3d.vertex.VertexFormat.Mode; -import com.mojang.math.Matrix3f; -import com.mojang.math.Matrix4f; - -import minecrafttransportsimulator.baseclasses.Point3D; -import minecrafttransportsimulator.baseclasses.TransformationMatrix; -import minecrafttransportsimulator.entities.components.AEntityC_Renderable; -import minecrafttransportsimulator.guis.components.AGUIBase; -import minecrafttransportsimulator.guis.components.GUIComponentItem; -import minecrafttransportsimulator.mcinterface.AWrapperWorld; -import minecrafttransportsimulator.mcinterface.IInterfaceRender; -import minecrafttransportsimulator.mcinterface.InterfaceManager; -import minecrafttransportsimulator.rendering.GIFParser.GIFImageFrame; -import minecrafttransportsimulator.rendering.GIFParser.ParsedGIF; -import minecrafttransportsimulator.rendering.RenderableData; -import minecrafttransportsimulator.systems.ConfigSystem; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.LightTexture; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderStateShard; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.ShaderInstance; -import net.minecraft.client.renderer.culling.Frustum; -import net.minecraft.client.renderer.entity.EntityRenderer; -import net.minecraft.client.renderer.entity.EntityRendererProvider; -import net.minecraft.client.renderer.texture.DynamicTexture; -import net.minecraft.client.renderer.texture.OverlayTexture; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.Mth; -import net.minecraft.world.level.LightLayer; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.EntityRenderersEvent.RegisterRenderers; -import net.minecraftforge.client.event.RegisterShadersEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber; - -/** - * Interface for the various MC rendering engines. This class has functions for - * binding textures, changing lightmap statuses, etc. - * - * @author don_bruce - */ -@EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD) -public class InterfaceRender implements IInterfaceRender { - private static final Map onlineTextures = new HashMap<>(); - private static final Map animatedGIFs = new HashMap<>(); - private static final Map> animatedGIFFrames = new LinkedHashMap<>(); - - private static final List stacksToRender = new ArrayList<>(); - - private static final Map renderTypes = new HashMap<>(); - private static final Map buffers = new HashMap<>(); - private static final Map> queuedRenders = new HashMap<>(); - private static final ConcurrentLinkedQueue removedRenders = new ConcurrentLinkedQueue<>(); - - @SuppressWarnings("deprecation") - private static final ResourceLocation BLOCK_TEXTURE_LOCATION = TextureAtlas.LOCATION_BLOCKS; - private static RenderStateShard.TextureStateShard MISSING_STATE; - private static RenderStateShard.TextureStateShard BLOCK_STATE; - public static PoseStack matrixStack; - public static Matrix4f projectionMatrix; - public static MultiBufferSource renderBuffer; - public static Point3D renderCameraOffset = new Point3D(); - private static boolean renderingGUI; - private static float[] matrixConvertArray = new float[16]; - - private static ShaderInstance entityLightsShader; - private static ShaderInstance entityCutoutNoshadowsShader; - private static final RenderStateShard.ShaderStateShard MTS_ENTITY_LIGHTS_SHADER = new RenderStateShard.ShaderStateShard(() -> entityLightsShader); - private static final RenderStateShard.ShaderStateShard MTS_ENTITY_CUTOUT_NOSHADOWS_SHADER = new RenderStateShard.ShaderStateShard(() -> entityCutoutNoshadowsShader); - - @SubscribeEvent - public static void onIVRegisterShadersEvent(RegisterShadersEvent event) { - try { - event.registerShader(new ShaderInstance(event.getResourceManager(), new ResourceLocation(InterfaceManager.coreModID, "mts_entity_lights"), DefaultVertexFormat.NEW_ENTITY), (createdShader) -> entityLightsShader = createdShader); - event.registerShader(new ShaderInstance(event.getResourceManager(), new ResourceLocation(InterfaceManager.coreModID, "mts_entity_cutout_noshadows"), DefaultVertexFormat.NEW_ENTITY), (createdShader) -> entityCutoutNoshadowsShader = createdShader); - } catch (IOException e) { - InterfaceManager.coreInterface.logError("COULD NOT LOAD SHADER FOR LIGHTS! THIS WILL END BADLY FOR RENDERING!"); - e.printStackTrace(); - } - } - - /** - * Event that's called to setup the client. We register our render wrapper - * class here. - */ - @SubscribeEvent - public static void onIVRegisterRenderersEvent(RegisterRenderers event) { - //Register the global entity rendering class. - event.registerEntityRenderer(BuilderEntityRenderForwarder.E_TYPE4.get(), manager -> new EntityRenderer(manager) { - @Override - public ResourceLocation getTextureLocation(BuilderEntityRenderForwarder builder) { - return null; - } - - @Override - public boolean shouldRender(BuilderEntityRenderForwarder builder, Frustum camera, double camX, double camY, double camZ) { - //Always render the forwarder, no matter where the camera is. - return true; - } - - @Override - public void render(BuilderEntityRenderForwarder builder, float entityYaw, float partialTicks, PoseStack stack, MultiBufferSource buffer, int packedLight) { - if (builder.playerFollowing == Minecraft.getInstance().player && !ConfigSystem.settings.general.forceRenderLastSolid.value) { - //Set camera offset point for later. - renderCameraOffset.set(Mth.lerp(partialTicks, builder.xOld, builder.getX()), Mth.lerp(partialTicks, builder.yOld, builder.getY()), Mth.lerp(partialTicks, builder.zOld, builder.getZ())); - - //Set the stack variables and render. - matrixStack = stack; - renderBuffer = buffer; - doRenderCall(false, partialTicks); - } - } - }); - - //Register blank classes for the other builders. - //If we don't, the game crashes when trying to render them. - event.registerEntityRenderer(BuilderEntityExisting.E_TYPE2.get(), manager -> new BlankRender(manager)); - event.registerEntityRenderer(BuilderEntityLinkedSeat.E_TYPE3.get(), manager -> new BlankRender(manager)); - } - - @Override - public float[] getBlockBreakTexture(AWrapperWorld world, Point3D position) { - //Get normal model. - BlockPos pos = new BlockPos(position.x, position.y, position.z); - BlockState state = ((WrapperWorld) world).world.getBlockState(pos); - TextureAtlasSprite sprite = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getTexture(state, ((WrapperWorld) world).world, pos); - return new float[] { sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() }; - } - - @Override - public float[] getDefaultBlockTexture(String name) { - TextureAtlasSprite sprite = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getModelManager().getAtlas(BLOCK_TEXTURE_LOCATION).getSprite(new ResourceLocation(name.replace(":", ":blocks/"))); - return new float[] { sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() }; - } - - @Override - public String getDefaultFontTextureFolder() { - return "/assets/minecraft/textures/font"; - } - - @Override - public InputStream getTextureStream(String name) { - try { - String domain = name.substring("/assets/".length(), name.indexOf("/", "/assets/".length())); - String location = name.substring("/assets/".length() + domain.length() + 1); - return Minecraft.getInstance().getResourceManager().getResource(new ResourceLocation(domain, location)).getInputStream(); - } catch (Exception e) { - return null; - } - } - - @Override - public void renderItemModel(GUIComponentItem component) { - stacksToRender.add(component); - } - - @Override - public void renderVertices(RenderableData data, boolean changedSinceLastRender) { - matrixStack.pushPose(); - Matrix4f matrix4f = convertMatrix4f(data.transform); - Matrix3f matrix3f = new Matrix3f(matrix4f); - PoseStack.Pose stackEntry = matrixStack.last(); - stackEntry.pose().multiply(matrix4f); - +package mcinterface1182; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentLinkedQueue; + +import javax.imageio.ImageIO; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.BufferUploader; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.blaze3d.vertex.VertexBuffer; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat.Mode; +import com.mojang.math.Matrix3f; +import com.mojang.math.Matrix4f; + +import minecrafttransportsimulator.baseclasses.Point3D; +import minecrafttransportsimulator.baseclasses.TransformationMatrix; +import minecrafttransportsimulator.entities.components.AEntityC_Renderable; +import minecrafttransportsimulator.guis.components.AGUIBase; +import minecrafttransportsimulator.guis.components.GUIComponentItem; +import minecrafttransportsimulator.mcinterface.AWrapperWorld; +import minecrafttransportsimulator.mcinterface.IInterfaceRender; +import minecrafttransportsimulator.mcinterface.InterfaceManager; +import minecrafttransportsimulator.rendering.GIFParser.GIFImageFrame; +import minecrafttransportsimulator.rendering.GIFParser.ParsedGIF; +import minecrafttransportsimulator.rendering.RenderableData; +import minecrafttransportsimulator.systems.ConfigSystem; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderStateShard; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.EntityRenderersEvent.RegisterRenderers; +import net.minecraftforge.client.event.RegisterShadersEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; + +/** + * Interface for the various MC rendering engines. This class has functions for + * binding textures, changing lightmap statuses, etc. + * + * @author don_bruce + */ +@EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD) +public class InterfaceRender implements IInterfaceRender { + private static final Map onlineTextures = new HashMap<>(); + private static final Map animatedGIFs = new HashMap<>(); + private static final Map> animatedGIFFrames = new LinkedHashMap<>(); + + private static final List stacksToRender = new ArrayList<>(); + + private static final Map renderTypes = new HashMap<>(); + private static final Map buffers = new HashMap<>(); + private static final Map> queuedRenders = new HashMap<>(); + private static final ConcurrentLinkedQueue removedRenders = new ConcurrentLinkedQueue<>(); + + @SuppressWarnings("deprecation") + private static final ResourceLocation BLOCK_TEXTURE_LOCATION = TextureAtlas.LOCATION_BLOCKS; + private static RenderStateShard.TextureStateShard MISSING_STATE; + private static RenderStateShard.TextureStateShard BLOCK_STATE; + public static PoseStack matrixStack; + public static Matrix4f projectionMatrix; + public static MultiBufferSource renderBuffer; + public static Point3D renderCameraOffset = new Point3D(); + private static boolean renderingGUI; + private static float[] matrixConvertArray = new float[16]; + + private static ShaderInstance entityLightsShader; + private static ShaderInstance entityCutoutNoshadowsShader; + private static final RenderStateShard.ShaderStateShard MTS_ENTITY_LIGHTS_SHADER = new RenderStateShard.ShaderStateShard(() -> entityLightsShader); + private static final RenderStateShard.ShaderStateShard MTS_ENTITY_CUTOUT_NOSHADOWS_SHADER = new RenderStateShard.ShaderStateShard(() -> entityCutoutNoshadowsShader); + + @SubscribeEvent + public static void onIVRegisterShadersEvent(RegisterShadersEvent event) { + try { + event.registerShader(new ShaderInstance(event.getResourceManager(), new ResourceLocation(InterfaceManager.coreModID, "mts_entity_lights"), DefaultVertexFormat.NEW_ENTITY), (createdShader) -> entityLightsShader = createdShader); + event.registerShader(new ShaderInstance(event.getResourceManager(), new ResourceLocation(InterfaceManager.coreModID, "mts_entity_cutout_noshadows"), DefaultVertexFormat.NEW_ENTITY), (createdShader) -> entityCutoutNoshadowsShader = createdShader); + } catch (IOException e) { + InterfaceManager.coreInterface.logError("COULD NOT LOAD SHADER FOR LIGHTS! THIS WILL END BADLY FOR RENDERING!"); + e.printStackTrace(); + } + } + + /** + * Event that's called to setup the client. We register our render wrapper + * class here. + */ + @SubscribeEvent + public static void onIVRegisterRenderersEvent(RegisterRenderers event) { + //Register the global entity rendering class. + event.registerEntityRenderer(BuilderEntityRenderForwarder.E_TYPE4.get(), manager -> new EntityRenderer(manager) { + @Override + public ResourceLocation getTextureLocation(BuilderEntityRenderForwarder builder) { + return null; + } + + @Override + public boolean shouldRender(BuilderEntityRenderForwarder builder, Frustum camera, double camX, double camY, double camZ) { + //Always render the forwarder, no matter where the camera is. + return true; + } + + @Override + public void render(BuilderEntityRenderForwarder builder, float entityYaw, float partialTicks, PoseStack stack, MultiBufferSource buffer, int packedLight) { + if (builder.playerFollowing == Minecraft.getInstance().player && !ConfigSystem.settings.general.forceRenderLastSolid.value) { + //Set camera offset point for later. + renderCameraOffset.set(Mth.lerp(partialTicks, builder.xOld, builder.getX()), Mth.lerp(partialTicks, builder.yOld, builder.getY()), Mth.lerp(partialTicks, builder.zOld, builder.getZ())); + + //Set the stack variables and render. + matrixStack = stack; + renderBuffer = buffer; + doRenderCall(false, partialTicks); + } + } + }); + + //Register blank classes for the other builders. + //If we don't, the game crashes when trying to render them. + event.registerEntityRenderer(BuilderEntityExisting.E_TYPE2.get(), manager -> new BlankRender(manager)); + event.registerEntityRenderer(BuilderEntityLinkedSeat.E_TYPE3.get(), manager -> new BlankRender(manager)); + } + + @Override + public float[] getBlockBreakTexture(AWrapperWorld world, Point3D position) { + //Get normal model. + BlockPos pos = new BlockPos(position.x, position.y, position.z); + BlockState state = ((WrapperWorld) world).world.getBlockState(pos); + TextureAtlasSprite sprite = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getTexture(state, ((WrapperWorld) world).world, pos); + return new float[] { sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() }; + } + + @Override + public float[] getDefaultBlockTexture(String name) { + TextureAtlasSprite sprite = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getModelManager().getAtlas(BLOCK_TEXTURE_LOCATION).getSprite(new ResourceLocation(name.replace(":", ":blocks/"))); + return new float[] { sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() }; + } + + @Override + public String getDefaultFontTextureFolder() { + return "/assets/minecraft/textures/font"; + } + + @Override + public InputStream getTextureStream(String name) { + try { + String domain = name.substring("/assets/".length(), name.indexOf("/", "/assets/".length())); + String location = name.substring("/assets/".length() + domain.length() + 1); + return Minecraft.getInstance().getResourceManager().getResource(new ResourceLocation(domain, location)).getInputStream(); + } catch (Exception e) { + return null; + } + } + + @Override + public void renderItemModel(GUIComponentItem component) { + stacksToRender.add(component); + } + + @Override + public void renderVertices(RenderableData data, boolean changedSinceLastRender) { + matrixStack.pushPose(); + Matrix4f matrix4f = convertMatrix4f(data.transform); + Matrix3f matrix3f = new Matrix3f(matrix4f); + PoseStack.Pose stackEntry = matrixStack.last(); + stackEntry.pose().multiply(matrix4f); + if (data.vertexObject.isLines) { stackEntry.normal().mul(matrix3f); VertexConsumer buffer = renderBuffer.getBuffer(RenderType.lines()); @@ -230,509 +230,509 @@ public void renderVertices(RenderableData data, boolean changedSinceLastRender) //Rewind buffer for next read. data.vertexObject.vertices.rewind(); } else { - String typeID = data.texture + data.isTranslucent + data.lightingMode + data.enableBrightBlending; - final RenderType renderType; - if (data.vertexObject.cacheVertices && !renderingGUI && ConfigSystem.client.renderingSettings.renderingMode.value != 2) { - //Get the render type and data buffer for this entity. - renderType = renderTypes.computeIfAbsent(typeID, k -> CustomRenderType.create("mts_entity", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.TRIANGLES, 2097152, true, data.isTranslucent, CustomRenderType.createForObject(data).createCompositeState(false))); - BufferData bufferData = buffers.computeIfAbsent(data, k -> new BufferData(renderType, data)); - - //Reset buffer if it's not ready. - if (changedSinceLastRender) { - bufferData.builder.clear(); - bufferData.isReady = false; - } - if (!bufferData.isReady) { - bufferData.builder.begin(VertexFormat.Mode.TRIANGLES, renderType.format()); - while (data.vertexObject.vertices.hasRemaining()) { - //Need to parse these out first since our order differs. - float normalX = data.vertexObject.vertices.get(); - float normalY = data.vertexObject.vertices.get(); - float normalZ = data.vertexObject.vertices.get(); - float texU = data.vertexObject.vertices.get(); - float texV = data.vertexObject.vertices.get(); - float posX = data.vertexObject.vertices.get(); - float posY = data.vertexObject.vertices.get(); - float posZ = data.vertexObject.vertices.get(); - bufferData.builder.vertex(posX, posY, posZ, data.color.red, data.color.green, data.color.blue, data.alpha, texU, texV, OverlayTexture.NO_OVERLAY, data.worldLightValue, normalX, normalY, normalZ); - } - bufferData.isReady = true; - bufferData.builder.end(); - bufferData.buffer.upload(bufferData.builder); - data.vertexObject.vertices.rewind(); - } - - //Add this buffer to the list to render later. - List renders = queuedRenders.get(renderType); - if (renders == null) { - renders = new ArrayList<>(); - queuedRenders.put(renderType, renders); - } - renders.add(new RenderData(stackEntry.pose(), bufferData)); - } else { - stackEntry.normal().mul(matrix3f); - renderType = renderTypes.computeIfAbsent(typeID, k -> CustomRenderType.create("mts_entity_" + typeID, DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.TRIANGLES, 256, true, data.isTranslucent, CustomRenderType.createForObject(data).createCompositeState(false))); - VertexConsumer buffer = renderBuffer.getBuffer(renderType); - //System.out.println(renderType); - while (data.vertexObject.vertices.hasRemaining()) { - //Need to parse these out first since our order differs. - float normalX = data.vertexObject.vertices.get(); - float normalY = data.vertexObject.vertices.get(); - float normalZ = data.vertexObject.vertices.get(); - float texU = data.vertexObject.vertices.get(); - float texV = data.vertexObject.vertices.get(); - float posX = data.vertexObject.vertices.get(); - float posY = data.vertexObject.vertices.get(); - float posZ = data.vertexObject.vertices.get(); - - //Add the vertex. Yes, we have to multiply this here on the CPU. Yes, it's retarded because the GPU should be doing the matrix math. - //Blaze3d my ass, this is SLOWER than DisplayLists! - buffer.vertex(stackEntry.pose(), posX, posY, posZ); - buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); - buffer.uv(texU, texV); - buffer.overlayCoords(OverlayTexture.NO_OVERLAY); - buffer.uv2(data.worldLightValue); - buffer.normal(stackEntry.normal(), normalX, normalY, normalZ); - buffer.endVertex(); - } - //Rewind buffer for next read. - data.vertexObject.vertices.rewind(); - } - } - matrixStack.popPose(); - } - - public static void doRenderCall(boolean blendingEnabled, float partialTicks) { - AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); - ConcurrentLinkedQueue allEntities = world.renderableEntities; - if (allEntities != null) { - world.beginProfiling("MTSRendering_Setup", true); - - //NOTE: this operation occurs on a ConcurrentLinkedQueue. Therefore, updates will - //not occur one after another. Sanitize your inputs! - for (AEntityC_Renderable entity : allEntities) { - matrixStack.pushPose(); - matrixStack.translate(entity.position.x - renderCameraOffset.x, entity.position.y - renderCameraOffset.y, entity.position.z - renderCameraOffset.z); - entity.render(blendingEnabled, partialTicks); - matrixStack.popPose(); - } - - //Now do the actual render. - if (!queuedRenders.isEmpty()) { - world.beginProfiling("MTSRendering_Batch", false); - renderBuffers(partialTicks); - world.endProfiling(); - } - } - } - - private static void renderBuffers(float partialTicks) { - //Call order is CRITICAL and will lead to random JME faults with no stacktrace if modified! - for (Entry> renderEntry : queuedRenders.entrySet()) { - RenderType renderType = renderEntry.getKey(); - List datas = renderEntry.getValue(); - if (!datas.isEmpty() && ConfigSystem.client.renderingSettings.renderingMode.value == 0) { - //Setup common render states. - renderType.setupRenderState(); - BufferUploader.reset(); - - //Shader is set now, get it and set values. - ShaderInstance shaderInstance = RenderSystem.getShader(); - for (int k = 0; k < 12; ++k) { - int i = RenderSystem.getShaderTexture(k); - shaderInstance.setSampler("Sampler" + k, i); - } - if (shaderInstance.PROJECTION_MATRIX != null) { - shaderInstance.PROJECTION_MATRIX.set(projectionMatrix); - } - if (shaderInstance.INVERSE_VIEW_ROTATION_MATRIX != null) { - shaderInstance.INVERSE_VIEW_ROTATION_MATRIX.set(RenderSystem.getInverseViewRotationMatrix()); - } - if (shaderInstance.COLOR_MODULATOR != null) { - shaderInstance.COLOR_MODULATOR.set(RenderSystem.getShaderColor()); - } - if (shaderInstance.FOG_START != null) { - shaderInstance.FOG_START.set(RenderSystem.getShaderFogStart()); - } - if (shaderInstance.FOG_END != null) { - shaderInstance.FOG_END.set(RenderSystem.getShaderFogEnd()); - } - if (shaderInstance.FOG_COLOR != null) { - shaderInstance.FOG_COLOR.set(RenderSystem.getShaderFogColor()); - } - if (shaderInstance.FOG_SHAPE != null) { - shaderInstance.FOG_SHAPE.set(RenderSystem.getShaderFogShape().getIndex()); - } - RenderSystem.setupShaderLights(shaderInstance); - - //Now render vertices, setting only the states required each render. - for (RenderData data : datas) { - if (shaderInstance.MODEL_VIEW_MATRIX != null) { - shaderInstance.MODEL_VIEW_MATRIX.set(data.matrix); - } - shaderInstance.apply(); - data.bufferData.buffer.drawChunkLayer(); - } - shaderInstance.clear(); - renderType.clearRenderState(); - datas.clear(); - } - - if (!datas.isEmpty()) { - renderType.setupRenderState(); - for (RenderData data : datas) { - data.bufferData.buffer.drawWithShader(data.matrix, projectionMatrix, RenderSystem.getShader()); - } - - renderType.clearRenderState(); - datas.clear(); - } - } - - if (!removedRenders.isEmpty()) { - removedRenders.forEach(render -> render.buffer.close()); - removedRenders.clear(); - } - } - - private static class CustomRenderType extends RenderType { - private CustomRenderType(String name, VertexFormat fmt, Mode glMode, int size, boolean doCrumbling, boolean depthSorting, Runnable onEnable, Runnable onDisable) { - super(name, fmt, glMode, size, doCrumbling, depthSorting, onEnable, onDisable); - throw new IllegalStateException("This class must not be instantiated, this is only here to gain access to the rendering constants."); - } - - private static RenderType.CompositeState.CompositeStateBuilder createForObject(RenderableData data) { - //Create the state builder. Changed states are active, default states are commented to save processing but still show the state. - RenderType.CompositeState.CompositeStateBuilder stateBuilder = RenderType.CompositeState.builder(); - - //Set shader to use. - if (data.lightingMode.disableTextureShadows) { - //This shouldn't use OpenGL lighting, use shader that ignores this. - if (data.lightingMode.disableWorldLighting) { - stateBuilder.setShaderState(MTS_ENTITY_LIGHTS_SHADER); - } else { - stateBuilder.setShaderState(MTS_ENTITY_CUTOUT_NOSHADOWS_SHADER); - } - } else if (data.isTranslucent) { - stateBuilder.setShaderState(RENDERTYPE_ENTITY_SOLID_SHADER); - } else { - stateBuilder.setShaderState(RENDERTYPE_ENTITY_CUTOUT_SHADER); - } - - //Set transparency. - if (data.enableBrightBlending) { - stateBuilder.setTransparencyState(BRIGHTNESS_TRANSPARENCY); - } else if (data.isTranslucent) { - stateBuilder.setTransparencyState(PROPER_TRANSLUCENT_TRANSPARENCY); - } else { - stateBuilder.setTransparencyState(NO_TRANSPARENCY); - } - - //Texture is just based on what is specified. - stateBuilder.setTextureState(data.texture != null ? getTexture(data.texture) : NO_TEXTURE); - //Always need lightmaps for entities per their vertex format. - stateBuilder.setLightmapState(LIGHTMAP); - //Entities go to the main target. - stateBuilder.setOutputState(MAIN_TARGET); - //All entity shaders require overlays, so we use those here. - stateBuilder.setOverlayState(OVERLAY); - //Culling is required for textured surfaces since they have normals and this saves FPS. - stateBuilder.setCullState(CULL); - //Depth test is fine, it ensures translucent things don't render in front of everything. - //stateBuilder.setDepthTestState(LEQUAL_DEPTH_TEST); - //No layering is fine. - //stateBuilder.setLayeringState(NO_LAYERING); - //Default texture application is fine. - //stateBuilder.setTexturingState(DEFAULT_TEXTURING); - //Not sure what this does, but it should be fine as-is? I think this is for the Z-buffer. - //stateBuilder.setWriteMaskState(COLOR_DEPTH_WRITE); - //Don't need to poke lines, there's only one state and we don't change lines for non-line renders. - //stateBuilder.setLineState(DEFAULT_LINE); - - //Return. - return stateBuilder; - } - } - - @Override - public void deleteVertices(RenderableData data) { - if (data.vertexObject.cacheVertices) { - //Add to removed render list, we should only remove renders AFTER they are rendered. - //This ensures they are un-bound, if the were bound prior. - //Make sure we actually bound a buffer; just because the main system asks for a bound buffer, - //doesn't mean we actually can give it one. GUI models are one such case, as they don't work right - //with bound buffers due to matrix differences. - BufferData buffer = buffers.remove(data); - if (buffer != null) { - removedRenders.add(buffer); - } - } - } - - @Override - public int getLightingAtPosition(Point3D position) { - BlockPos pos = new BlockPos(position.x, position.y, position.z); - return LightTexture.pack(Minecraft.getInstance().level.getBrightness(LightLayer.BLOCK, pos), Minecraft.getInstance().level.getBrightness(LightLayer.SKY, pos)); - } - - @Override - public boolean shouldRenderBoundingBoxes() { - return Minecraft.getInstance().getEntityRenderDispatcher().shouldRenderHitBoxes(); - } - - @Override - public boolean bindURLTexture(String textureURL, InputStream stream) { - if (stream != null) { - try { - NativeImage image = NativeImage.read(NativeImage.Format.RGB, stream); - DynamicTexture texture = new DynamicTexture(image); - ResourceLocation textureLocation = Minecraft.getInstance().textureManager.register("mts-url", texture); - onlineTextures.put(textureURL, textureLocation); - return true; - } catch (Exception e) { - return false; - } - } else { - onlineTextures.put(textureURL, null); - return true; - } - } - - @Override - public boolean bindURLGIF(String textureURL, ParsedGIF gif) { - Map gifFrameIndexes = new HashMap<>(); - for (GIFImageFrame frame : gif.frames.values()) { - try { - BufferedImage frameBuffer = frame.getImage(); - ByteArrayOutputStream frameArrayStream = new ByteArrayOutputStream(); - ImageIO.write(frameBuffer, "gif", frameArrayStream); - InputStream frameStream = new ByteArrayInputStream(frameArrayStream.toByteArray()); - - NativeImage image = NativeImage.read(NativeImage.Format.RGB, frameStream); - DynamicTexture texture = new DynamicTexture(image); - ResourceLocation textureLocation = Minecraft.getInstance().textureManager.register("mts-gif", texture); - gifFrameIndexes.put(frame, textureLocation); - } catch (Exception e) { - return false; - } - } - animatedGIFs.put(textureURL, gif); - animatedGIFFrames.put(gif, gifFrameIndexes); - return true; - } - - /** - * Helper function to create a new texture state for the specified texture location. - */ - private static RenderStateShard.TextureStateShard getTexture(String textureLocation) { - //Check to make sure textures exist. We delay creating because some mods screw up this stuff in boot. Cray, looking at you buddy. - if (MISSING_STATE == null) { - MISSING_STATE = new RenderStateShard.TextureStateShard(new ResourceLocation("mts:textures/rendering/missing.png"), false, false); - BLOCK_STATE = new RenderStateShard.TextureStateShard(BLOCK_TEXTURE_LOCATION, false, false); - } - - if (animatedGIFs.containsKey(textureLocation)) { - //Special case for GIFs. - ParsedGIF parsedGIF = animatedGIFs.get(textureLocation); - return new RenderStateShard.TextureStateShard(animatedGIFFrames.get(parsedGIF).get(parsedGIF.getCurrentFrame()), false, false); - } else if (onlineTextures.containsKey(textureLocation)) { - //Online texture. - ResourceLocation onlineTexture = onlineTextures.get(textureLocation); - return onlineTexture != null ? new RenderStateShard.TextureStateShard(onlineTextures.get(textureLocation), false, false) : MISSING_STATE; - } else if (textureLocation.equals(RenderableData.GLOBAL_TEXTURE_NAME)) { - //Default texture. - return BLOCK_STATE; - } else { - //If the texture has a colon, it's a short-hand form that needs to be converted. - String formattedLocation = textureLocation; - if (textureLocation.contains(":")) { - formattedLocation = "/assets/" + textureLocation.replace(":", "/"); - } - - //Check if the texture exists. - if (InterfaceManager.coreInterface.getPackResource(formattedLocation) != null) { - //Convert the classpath-location to a domain-location path for MC. - String domain = formattedLocation.substring("/assets/".length(), formattedLocation.indexOf("/", "/assets/".length())); - String location = formattedLocation.substring("/assets/".length() + domain.length() + 1); - return new RenderStateShard.TextureStateShard(new ResourceLocation(domain, location), false, false); - } else { - InterfaceManager.coreInterface.logError("Could not find texture: " + formattedLocation + " Reverting to fallback texture."); - return MISSING_STATE; - } - } - } - - /** - * Renders the main GUI, setting up any transforms or operations as required. - */ - protected static void renderGUI(PoseStack stack, int mouseX, int mouseY, int screenWidth, int screenHeight, float partialTicks, boolean updateGUIs) { - //Get the buffer for GUI rendering. - matrixStack = stack; - matrixStack.pushPose(); - renderingGUI = true; - MultiBufferSource.BufferSource guiBuffer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); - renderBuffer = guiBuffer; - - //Render GUIs, re-creating their components if needed. - //Set Y-axis to inverted to have correct orientation. - matrixStack.scale(1.0F, -1.0F, 1.0F); - - //Render main pass, then blended pass. - int displayGUIIndex = 0; - for (AGUIBase gui : AGUIBase.activeGUIs) { - if (updateGUIs || gui.components.isEmpty()) { - gui.setupComponentsInit(screenWidth, screenHeight); - } - matrixStack.pushPose(); - if (gui.capturesPlayer()) { - //Translate in front of the main GUI components. - matrixStack.translate(0, 0, 250); - } else { - //Translate far enough to render behind the chat window. - matrixStack.translate(0, 0, -500 + 250 * displayGUIIndex++); - } - gui.render(mouseX, mouseY, false, partialTicks); - guiBuffer.endBatch(); - //Not needed, since we can't draw to custom buffers with GUIs. - //renderBuffers(); - - //Need to use RenderSystem here, since this is a direct buffer. - RenderSystem.enableBlend(); - gui.render(mouseX, mouseY, true, partialTicks); - guiBuffer.endBatch(); - //renderBuffers(); - RenderSystem.disableBlend(); - - //Render all stacks. These have to be in the standard GUI reference frame or they won't render. - matrixStack.scale(1.0F, -1.0F, 1.0F); - - for (GUIComponentItem component : stacksToRender) { - //Double-check the stack is still present, it might have been un-set since this call. - if ((WrapperItemStack) component.stackToRender != null) { - //Apply existing transform. - //Need to translate the z-offset to our value, which includes a -100 for the default added value. - //Blit starts at 200 though, plus 32 for the default item render. - float zOffset = Minecraft.getInstance().getItemRenderer().blitOffset; - Minecraft.getInstance().getItemRenderer().blitOffset = (float) (200 + component.translation.z - 100); - if (component.scale != 1.0) { - //Need to use RenderSystem here, since we can't access the stack directly for rendering scaling. - PoseStack posestack = RenderSystem.getModelViewStack(); - posestack.pushPose(); - posestack.scale(component.scale, component.scale, 1.0F); - RenderSystem.applyModelViewMatrix(); - Minecraft.getInstance().getItemRenderer().renderGuiItem(((WrapperItemStack) component.stackToRender).stack, (int) (component.translation.x / component.scale), (int) (-component.translation.y / component.scale) + 1); - posestack.popPose(); - RenderSystem.applyModelViewMatrix(); - } else { - Minecraft.getInstance().getItemRenderer().renderGuiItem(((WrapperItemStack) component.stackToRender).stack, (int) component.translation.x, (int) -component.translation.y); - } - Minecraft.getInstance().getItemRenderer().blitOffset = zOffset; - } - } - stacksToRender.clear(); - - matrixStack.popPose(); - } - matrixStack.popPose(); - renderingGUI = false; - } - - /** - * Converts internal matrix to new external matrix. - */ - public static Matrix4f convertMatrix4f(TransformationMatrix transform) { - matrixConvertArray[0] = (float) transform.m00; - matrixConvertArray[1] = (float) transform.m01; - matrixConvertArray[2] = (float) transform.m02; - matrixConvertArray[3] = (float) transform.m03; - matrixConvertArray[4] = (float) transform.m10; - matrixConvertArray[5] = (float) transform.m11; - matrixConvertArray[6] = (float) transform.m12; - matrixConvertArray[7] = (float) transform.m13; - matrixConvertArray[8] = (float) transform.m20; - matrixConvertArray[9] = (float) transform.m21; - matrixConvertArray[10] = (float) transform.m22; - matrixConvertArray[11] = (float) transform.m23; - matrixConvertArray[12] = (float) transform.m30; - matrixConvertArray[13] = (float) transform.m31; - matrixConvertArray[14] = (float) transform.m32; - matrixConvertArray[15] = (float) transform.m33; - return new Matrix4f(matrixConvertArray); - } - - - /** Blank render class used to bypass rendering for all other builders.**/ - private static class BlankRender extends EntityRenderer { - - protected BlankRender(EntityRendererProvider.Context p_i46179_1_) { - super(p_i46179_1_); - } - - @Override - public ResourceLocation getTextureLocation(T pEntity) { - return null; - } - - @Override - public boolean shouldRender(T builder, Frustum camera, double camX, double camY, double camZ) { - return false; - } - } - - private static class RenderData { - private final Matrix4f matrix; - private final BufferData bufferData; - - private RenderData(Matrix4f matrix, BufferData bufferData) { - this.matrix = new Matrix4f(matrix); - this.bufferData = bufferData; - } - } - - private static class BufferData { - final BufferBuilder builder; - final VertexBuffer buffer; - boolean isReady; - - private BufferData() { - builder = null; - buffer = null; - } - - private BufferData(RenderType type, RenderableData data) { - int vertices = data.vertexObject.vertices.limit() / 8; - //Convert verts to faces, then back to quad-verts for MC rendering. - //Add one face extra, since MC will want to increase the buffer if sees it can't handle another vert. - vertices = ((vertices / 3) + 1) * 3; - this.builder = new BufferBuilder(type.format().getIntegerSize() * vertices); - this.buffer = new VertexBuffer(); - } - } - - /** - * Proper translucent transparency. MC's one has the wrong blending function. - */ - private static final RenderStateShard.TransparencyStateShard PROPER_TRANSLUCENT_TRANSPARENCY = new RenderStateShard.TransparencyStateShard("proper_translucent_transparency", () -> { - RenderSystem.enableBlend(); - RenderSystem.depthMask(false); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - }, () -> { - RenderSystem.disableBlend(); - RenderSystem.depthMask(true); - RenderSystem.defaultBlendFunc(); - }); - - /** - * Brightness transparency. Does a special blending operation to make things behind - * the object brighter based on the object's alpha value. More alpha means more bright. - */ - private static final RenderStateShard.TransparencyStateShard BRIGHTNESS_TRANSPARENCY = new RenderStateShard.TransparencyStateShard("brightness_transparency", () -> { - RenderSystem.enableBlend(); - RenderSystem.depthMask(false); - RenderSystem.blendFunc(GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_ALPHA); - }, () -> { - RenderSystem.disableBlend(); - RenderSystem.depthMask(true); - RenderSystem.defaultBlendFunc(); - }); -} + String typeID = data.texture + data.isTranslucent + data.lightingMode + data.enableBrightBlending; + final RenderType renderType; + if (data.vertexObject.cacheVertices && !renderingGUI && ConfigSystem.client.renderingSettings.renderingMode.value != 2) { + //Get the render type and data buffer for this entity. + renderType = renderTypes.computeIfAbsent(typeID, k -> CustomRenderType.create("mts_entity", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.TRIANGLES, 2097152, true, data.isTranslucent, CustomRenderType.createForObject(data).createCompositeState(false))); + BufferData bufferData = buffers.computeIfAbsent(data, k -> new BufferData(renderType, data)); + + //Reset buffer if it's not ready. + if (changedSinceLastRender) { + bufferData.builder.clear(); + bufferData.isReady = false; + } + if (!bufferData.isReady) { + bufferData.builder.begin(VertexFormat.Mode.TRIANGLES, renderType.format()); + while (data.vertexObject.vertices.hasRemaining()) { + //Need to parse these out first since our order differs. + float normalX = data.vertexObject.vertices.get(); + float normalY = data.vertexObject.vertices.get(); + float normalZ = data.vertexObject.vertices.get(); + float texU = data.vertexObject.vertices.get(); + float texV = data.vertexObject.vertices.get(); + float posX = data.vertexObject.vertices.get(); + float posY = data.vertexObject.vertices.get(); + float posZ = data.vertexObject.vertices.get(); + bufferData.builder.vertex(posX, posY, posZ, data.color.red, data.color.green, data.color.blue, data.alpha, texU, texV, OverlayTexture.NO_OVERLAY, data.worldLightValue, normalX, normalY, normalZ); + } + bufferData.isReady = true; + bufferData.builder.end(); + bufferData.buffer.upload(bufferData.builder); + data.vertexObject.vertices.rewind(); + } + + //Add this buffer to the list to render later. + List renders = queuedRenders.get(renderType); + if (renders == null) { + renders = new ArrayList<>(); + queuedRenders.put(renderType, renders); + } + renders.add(new RenderData(stackEntry.pose(), bufferData)); + } else { + stackEntry.normal().mul(matrix3f); + renderType = renderTypes.computeIfAbsent(typeID, k -> CustomRenderType.create("mts_entity_" + typeID, DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.TRIANGLES, 256, true, data.isTranslucent, CustomRenderType.createForObject(data).createCompositeState(false))); + VertexConsumer buffer = renderBuffer.getBuffer(renderType); + //System.out.println(renderType); + while (data.vertexObject.vertices.hasRemaining()) { + //Need to parse these out first since our order differs. + float normalX = data.vertexObject.vertices.get(); + float normalY = data.vertexObject.vertices.get(); + float normalZ = data.vertexObject.vertices.get(); + float texU = data.vertexObject.vertices.get(); + float texV = data.vertexObject.vertices.get(); + float posX = data.vertexObject.vertices.get(); + float posY = data.vertexObject.vertices.get(); + float posZ = data.vertexObject.vertices.get(); + + //Add the vertex. Yes, we have to multiply this here on the CPU. Yes, it's retarded because the GPU should be doing the matrix math. + //Blaze3d my ass, this is SLOWER than DisplayLists! + buffer.vertex(stackEntry.pose(), posX, posY, posZ); + buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); + buffer.uv(texU, texV); + buffer.overlayCoords(OverlayTexture.NO_OVERLAY); + buffer.uv2(data.worldLightValue); + buffer.normal(stackEntry.normal(), normalX, normalY, normalZ); + buffer.endVertex(); + } + //Rewind buffer for next read. + data.vertexObject.vertices.rewind(); + } + } + matrixStack.popPose(); + } + + public static void doRenderCall(boolean blendingEnabled, float partialTicks) { + AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); + ConcurrentLinkedQueue allEntities = world.renderableEntities; + if (allEntities != null) { + world.beginProfiling("MTSRendering_Setup", true); + + //NOTE: this operation occurs on a ConcurrentLinkedQueue. Therefore, updates will + //not occur one after another. Sanitize your inputs! + for (AEntityC_Renderable entity : allEntities) { + matrixStack.pushPose(); + matrixStack.translate(entity.position.x - renderCameraOffset.x, entity.position.y - renderCameraOffset.y, entity.position.z - renderCameraOffset.z); + entity.render(blendingEnabled, partialTicks); + matrixStack.popPose(); + } + + //Now do the actual render. + if (!queuedRenders.isEmpty()) { + world.beginProfiling("MTSRendering_Batch", false); + renderBuffers(partialTicks); + world.endProfiling(); + } + } + } + + private static void renderBuffers(float partialTicks) { + //Call order is CRITICAL and will lead to random JME faults with no stacktrace if modified! + for (Entry> renderEntry : queuedRenders.entrySet()) { + RenderType renderType = renderEntry.getKey(); + List datas = renderEntry.getValue(); + if (!datas.isEmpty() && ConfigSystem.client.renderingSettings.renderingMode.value == 0) { + //Setup common render states. + renderType.setupRenderState(); + BufferUploader.reset(); + + //Shader is set now, get it and set values. + ShaderInstance shaderInstance = RenderSystem.getShader(); + for (int k = 0; k < 12; ++k) { + int i = RenderSystem.getShaderTexture(k); + shaderInstance.setSampler("Sampler" + k, i); + } + if (shaderInstance.PROJECTION_MATRIX != null) { + shaderInstance.PROJECTION_MATRIX.set(projectionMatrix); + } + if (shaderInstance.INVERSE_VIEW_ROTATION_MATRIX != null) { + shaderInstance.INVERSE_VIEW_ROTATION_MATRIX.set(RenderSystem.getInverseViewRotationMatrix()); + } + if (shaderInstance.COLOR_MODULATOR != null) { + shaderInstance.COLOR_MODULATOR.set(RenderSystem.getShaderColor()); + } + if (shaderInstance.FOG_START != null) { + shaderInstance.FOG_START.set(RenderSystem.getShaderFogStart()); + } + if (shaderInstance.FOG_END != null) { + shaderInstance.FOG_END.set(RenderSystem.getShaderFogEnd()); + } + if (shaderInstance.FOG_COLOR != null) { + shaderInstance.FOG_COLOR.set(RenderSystem.getShaderFogColor()); + } + if (shaderInstance.FOG_SHAPE != null) { + shaderInstance.FOG_SHAPE.set(RenderSystem.getShaderFogShape().getIndex()); + } + RenderSystem.setupShaderLights(shaderInstance); + + //Now render vertices, setting only the states required each render. + for (RenderData data : datas) { + if (shaderInstance.MODEL_VIEW_MATRIX != null) { + shaderInstance.MODEL_VIEW_MATRIX.set(data.matrix); + } + shaderInstance.apply(); + data.bufferData.buffer.drawChunkLayer(); + } + shaderInstance.clear(); + renderType.clearRenderState(); + datas.clear(); + } + + if (!datas.isEmpty()) { + renderType.setupRenderState(); + for (RenderData data : datas) { + data.bufferData.buffer.drawWithShader(data.matrix, projectionMatrix, RenderSystem.getShader()); + } + + renderType.clearRenderState(); + datas.clear(); + } + } + + if (!removedRenders.isEmpty()) { + removedRenders.forEach(render -> render.buffer.close()); + removedRenders.clear(); + } + } + + private static class CustomRenderType extends RenderType { + private CustomRenderType(String name, VertexFormat fmt, Mode glMode, int size, boolean doCrumbling, boolean depthSorting, Runnable onEnable, Runnable onDisable) { + super(name, fmt, glMode, size, doCrumbling, depthSorting, onEnable, onDisable); + throw new IllegalStateException("This class must not be instantiated, this is only here to gain access to the rendering constants."); + } + + private static RenderType.CompositeState.CompositeStateBuilder createForObject(RenderableData data) { + //Create the state builder. Changed states are active, default states are commented to save processing but still show the state. + RenderType.CompositeState.CompositeStateBuilder stateBuilder = RenderType.CompositeState.builder(); + + //Set shader to use. + if (data.lightingMode.disableTextureShadows) { + //This shouldn't use OpenGL lighting, use shader that ignores this. + if (data.lightingMode.disableWorldLighting) { + stateBuilder.setShaderState(MTS_ENTITY_LIGHTS_SHADER); + } else { + stateBuilder.setShaderState(MTS_ENTITY_CUTOUT_NOSHADOWS_SHADER); + } + } else if (data.isTranslucent) { + stateBuilder.setShaderState(RENDERTYPE_ENTITY_SOLID_SHADER); + } else { + stateBuilder.setShaderState(RENDERTYPE_ENTITY_CUTOUT_SHADER); + } + + //Set transparency. + if (data.enableBrightBlending) { + stateBuilder.setTransparencyState(BRIGHTNESS_TRANSPARENCY); + } else if (data.isTranslucent) { + stateBuilder.setTransparencyState(PROPER_TRANSLUCENT_TRANSPARENCY); + } else { + stateBuilder.setTransparencyState(NO_TRANSPARENCY); + } + + //Texture is just based on what is specified. + stateBuilder.setTextureState(data.texture != null ? getTexture(data.texture) : NO_TEXTURE); + //Always need lightmaps for entities per their vertex format. + stateBuilder.setLightmapState(LIGHTMAP); + //Entities go to the main target. + stateBuilder.setOutputState(MAIN_TARGET); + //All entity shaders require overlays, so we use those here. + stateBuilder.setOverlayState(OVERLAY); + //Culling is required for textured surfaces since they have normals and this saves FPS. + stateBuilder.setCullState(CULL); + //Depth test is fine, it ensures translucent things don't render in front of everything. + //stateBuilder.setDepthTestState(LEQUAL_DEPTH_TEST); + //No layering is fine. + //stateBuilder.setLayeringState(NO_LAYERING); + //Default texture application is fine. + //stateBuilder.setTexturingState(DEFAULT_TEXTURING); + //Not sure what this does, but it should be fine as-is? I think this is for the Z-buffer. + //stateBuilder.setWriteMaskState(COLOR_DEPTH_WRITE); + //Don't need to poke lines, there's only one state and we don't change lines for non-line renders. + //stateBuilder.setLineState(DEFAULT_LINE); + + //Return. + return stateBuilder; + } + } + + @Override + public void deleteVertices(RenderableData data) { + if (data.vertexObject.cacheVertices) { + //Add to removed render list, we should only remove renders AFTER they are rendered. + //This ensures they are un-bound, if the were bound prior. + //Make sure we actually bound a buffer; just because the main system asks for a bound buffer, + //doesn't mean we actually can give it one. GUI models are one such case, as they don't work right + //with bound buffers due to matrix differences. + BufferData buffer = buffers.remove(data); + if (buffer != null) { + removedRenders.add(buffer); + } + } + } + + @Override + public int getLightingAtPosition(Point3D position) { + BlockPos pos = new BlockPos(position.x, position.y, position.z); + return LightTexture.pack(Minecraft.getInstance().level.getBrightness(LightLayer.BLOCK, pos), Minecraft.getInstance().level.getBrightness(LightLayer.SKY, pos)); + } + + @Override + public boolean shouldRenderBoundingBoxes() { + return Minecraft.getInstance().getEntityRenderDispatcher().shouldRenderHitBoxes(); + } + + @Override + public boolean bindURLTexture(String textureURL, InputStream stream) { + if (stream != null) { + try { + NativeImage image = NativeImage.read(NativeImage.Format.RGB, stream); + DynamicTexture texture = new DynamicTexture(image); + ResourceLocation textureLocation = Minecraft.getInstance().textureManager.register("mts-url", texture); + onlineTextures.put(textureURL, textureLocation); + return true; + } catch (Exception e) { + return false; + } + } else { + onlineTextures.put(textureURL, null); + return true; + } + } + + @Override + public boolean bindURLGIF(String textureURL, ParsedGIF gif) { + Map gifFrameIndexes = new HashMap<>(); + for (GIFImageFrame frame : gif.frames.values()) { + try { + BufferedImage frameBuffer = frame.getImage(); + ByteArrayOutputStream frameArrayStream = new ByteArrayOutputStream(); + ImageIO.write(frameBuffer, "gif", frameArrayStream); + InputStream frameStream = new ByteArrayInputStream(frameArrayStream.toByteArray()); + + NativeImage image = NativeImage.read(NativeImage.Format.RGB, frameStream); + DynamicTexture texture = new DynamicTexture(image); + ResourceLocation textureLocation = Minecraft.getInstance().textureManager.register("mts-gif", texture); + gifFrameIndexes.put(frame, textureLocation); + } catch (Exception e) { + return false; + } + } + animatedGIFs.put(textureURL, gif); + animatedGIFFrames.put(gif, gifFrameIndexes); + return true; + } + + /** + * Helper function to create a new texture state for the specified texture location. + */ + private static RenderStateShard.TextureStateShard getTexture(String textureLocation) { + //Check to make sure textures exist. We delay creating because some mods screw up this stuff in boot. Cray, looking at you buddy. + if (MISSING_STATE == null) { + MISSING_STATE = new RenderStateShard.TextureStateShard(new ResourceLocation("mts:textures/rendering/missing.png"), false, false); + BLOCK_STATE = new RenderStateShard.TextureStateShard(BLOCK_TEXTURE_LOCATION, false, false); + } + + if (animatedGIFs.containsKey(textureLocation)) { + //Special case for GIFs. + ParsedGIF parsedGIF = animatedGIFs.get(textureLocation); + return new RenderStateShard.TextureStateShard(animatedGIFFrames.get(parsedGIF).get(parsedGIF.getCurrentFrame()), false, false); + } else if (onlineTextures.containsKey(textureLocation)) { + //Online texture. + ResourceLocation onlineTexture = onlineTextures.get(textureLocation); + return onlineTexture != null ? new RenderStateShard.TextureStateShard(onlineTextures.get(textureLocation), false, false) : MISSING_STATE; + } else if (textureLocation.equals(RenderableData.GLOBAL_TEXTURE_NAME)) { + //Default texture. + return BLOCK_STATE; + } else { + //If the texture has a colon, it's a short-hand form that needs to be converted. + String formattedLocation = textureLocation; + if (textureLocation.contains(":")) { + formattedLocation = "/assets/" + textureLocation.replace(":", "/"); + } + + //Check if the texture exists. + if (InterfaceManager.coreInterface.getPackResource(formattedLocation) != null) { + //Convert the classpath-location to a domain-location path for MC. + String domain = formattedLocation.substring("/assets/".length(), formattedLocation.indexOf("/", "/assets/".length())); + String location = formattedLocation.substring("/assets/".length() + domain.length() + 1); + return new RenderStateShard.TextureStateShard(new ResourceLocation(domain, location), false, false); + } else { + InterfaceManager.coreInterface.logError("Could not find texture: " + formattedLocation + " Reverting to fallback texture."); + return MISSING_STATE; + } + } + } + + /** + * Renders the main GUI, setting up any transforms or operations as required. + */ + protected static void renderGUI(PoseStack stack, int mouseX, int mouseY, int screenWidth, int screenHeight, float partialTicks, boolean updateGUIs) { + //Get the buffer for GUI rendering. + matrixStack = stack; + matrixStack.pushPose(); + renderingGUI = true; + MultiBufferSource.BufferSource guiBuffer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); + renderBuffer = guiBuffer; + + //Render GUIs, re-creating their components if needed. + //Set Y-axis to inverted to have correct orientation. + matrixStack.scale(1.0F, -1.0F, 1.0F); + + //Render main pass, then blended pass. + int displayGUIIndex = 0; + for (AGUIBase gui : AGUIBase.activeGUIs) { + if (updateGUIs || gui.components.isEmpty()) { + gui.setupComponentsInit(screenWidth, screenHeight); + } + matrixStack.pushPose(); + if (gui.capturesPlayer()) { + //Translate in front of the main GUI components. + matrixStack.translate(0, 0, 250); + } else { + //Translate far enough to render behind the chat window. + matrixStack.translate(0, 0, -500 + 250 * displayGUIIndex++); + } + gui.render(mouseX, mouseY, false, partialTicks); + guiBuffer.endBatch(); + //Not needed, since we can't draw to custom buffers with GUIs. + //renderBuffers(); + + //Need to use RenderSystem here, since this is a direct buffer. + RenderSystem.enableBlend(); + gui.render(mouseX, mouseY, true, partialTicks); + guiBuffer.endBatch(); + //renderBuffers(); + RenderSystem.disableBlend(); + + //Render all stacks. These have to be in the standard GUI reference frame or they won't render. + matrixStack.scale(1.0F, -1.0F, 1.0F); + + for (GUIComponentItem component : stacksToRender) { + //Double-check the stack is still present, it might have been un-set since this call. + if ((WrapperItemStack) component.stackToRender != null) { + //Apply existing transform. + //Need to translate the z-offset to our value, which includes a -100 for the default added value. + //Blit starts at 200 though, plus 32 for the default item render. + float zOffset = Minecraft.getInstance().getItemRenderer().blitOffset; + Minecraft.getInstance().getItemRenderer().blitOffset = (float) (200 + component.translation.z - 100); + if (component.scale != 1.0) { + //Need to use RenderSystem here, since we can't access the stack directly for rendering scaling. + PoseStack posestack = RenderSystem.getModelViewStack(); + posestack.pushPose(); + posestack.scale(component.scale, component.scale, 1.0F); + RenderSystem.applyModelViewMatrix(); + Minecraft.getInstance().getItemRenderer().renderGuiItem(((WrapperItemStack) component.stackToRender).stack, (int) (component.translation.x / component.scale), (int) (-component.translation.y / component.scale) + 1); + posestack.popPose(); + RenderSystem.applyModelViewMatrix(); + } else { + Minecraft.getInstance().getItemRenderer().renderGuiItem(((WrapperItemStack) component.stackToRender).stack, (int) component.translation.x, (int) -component.translation.y); + } + Minecraft.getInstance().getItemRenderer().blitOffset = zOffset; + } + } + stacksToRender.clear(); + + matrixStack.popPose(); + } + matrixStack.popPose(); + renderingGUI = false; + } + + /** + * Converts internal matrix to new external matrix. + */ + public static Matrix4f convertMatrix4f(TransformationMatrix transform) { + matrixConvertArray[0] = (float) transform.m00; + matrixConvertArray[1] = (float) transform.m01; + matrixConvertArray[2] = (float) transform.m02; + matrixConvertArray[3] = (float) transform.m03; + matrixConvertArray[4] = (float) transform.m10; + matrixConvertArray[5] = (float) transform.m11; + matrixConvertArray[6] = (float) transform.m12; + matrixConvertArray[7] = (float) transform.m13; + matrixConvertArray[8] = (float) transform.m20; + matrixConvertArray[9] = (float) transform.m21; + matrixConvertArray[10] = (float) transform.m22; + matrixConvertArray[11] = (float) transform.m23; + matrixConvertArray[12] = (float) transform.m30; + matrixConvertArray[13] = (float) transform.m31; + matrixConvertArray[14] = (float) transform.m32; + matrixConvertArray[15] = (float) transform.m33; + return new Matrix4f(matrixConvertArray); + } + + + /** Blank render class used to bypass rendering for all other builders.**/ + private static class BlankRender extends EntityRenderer { + + protected BlankRender(EntityRendererProvider.Context p_i46179_1_) { + super(p_i46179_1_); + } + + @Override + public ResourceLocation getTextureLocation(T pEntity) { + return null; + } + + @Override + public boolean shouldRender(T builder, Frustum camera, double camX, double camY, double camZ) { + return false; + } + } + + private static class RenderData { + private final Matrix4f matrix; + private final BufferData bufferData; + + private RenderData(Matrix4f matrix, BufferData bufferData) { + this.matrix = new Matrix4f(matrix); + this.bufferData = bufferData; + } + } + + private static class BufferData { + final BufferBuilder builder; + final VertexBuffer buffer; + boolean isReady; + + private BufferData() { + builder = null; + buffer = null; + } + + private BufferData(RenderType type, RenderableData data) { + int vertices = data.vertexObject.vertices.limit() / 8; + //Convert verts to faces, then back to quad-verts for MC rendering. + //Add one face extra, since MC will want to increase the buffer if sees it can't handle another vert. + vertices = ((vertices / 3) + 1) * 3; + this.builder = new BufferBuilder(type.format().getIntegerSize() * vertices); + this.buffer = new VertexBuffer(); + } + } + + /** + * Proper translucent transparency. MC's one has the wrong blending function. + */ + private static final RenderStateShard.TransparencyStateShard PROPER_TRANSLUCENT_TRANSPARENCY = new RenderStateShard.TransparencyStateShard("proper_translucent_transparency", () -> { + RenderSystem.enableBlend(); + RenderSystem.depthMask(false); + RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + }, () -> { + RenderSystem.disableBlend(); + RenderSystem.depthMask(true); + RenderSystem.defaultBlendFunc(); + }); + + /** + * Brightness transparency. Does a special blending operation to make things behind + * the object brighter based on the object's alpha value. More alpha means more bright. + */ + private static final RenderStateShard.TransparencyStateShard BRIGHTNESS_TRANSPARENCY = new RenderStateShard.TransparencyStateShard("brightness_transparency", () -> { + RenderSystem.enableBlend(); + RenderSystem.depthMask(false); + RenderSystem.blendFunc(GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_ALPHA); + }, () -> { + RenderSystem.disableBlend(); + RenderSystem.depthMask(true); + RenderSystem.defaultBlendFunc(); + }); +} diff --git a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java index 83989c083..c642bf5df 100644 --- a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java +++ b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java @@ -1,153 +1,153 @@ -package mcinterface1192; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.nio.FloatBuffer; - -import minecrafttransportsimulator.baseclasses.Point3D; -import minecrafttransportsimulator.baseclasses.RotationMatrix; -import minecrafttransportsimulator.entities.instances.EntityFluidTank; -import minecrafttransportsimulator.guis.components.AGUIBase; -import minecrafttransportsimulator.guis.instances.GUIPackMissing; -import minecrafttransportsimulator.mcinterface.AWrapperWorld; -import minecrafttransportsimulator.mcinterface.IInterfaceClient; -import minecrafttransportsimulator.mcinterface.IWrapperItemStack; -import minecrafttransportsimulator.mcinterface.IWrapperPlayer; -import minecrafttransportsimulator.mcinterface.InterfaceManager; -import minecrafttransportsimulator.packloading.PackParser; -import minecrafttransportsimulator.rendering.RenderText; -import minecrafttransportsimulator.systems.CameraSystem.CameraMode; -import minecrafttransportsimulator.systems.ConfigSystem; -import minecrafttransportsimulator.systems.ControlSystem; -import minecrafttransportsimulator.systems.LanguageSystem; -import net.minecraft.ChatFormatting; -import net.minecraft.client.CameraType; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.ChatScreen; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.Style; -import net.minecraft.network.chat.TextColor; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.item.TooltipFlag; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.phys.Vec3; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.event.TickEvent.Phase; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber; -import net.minecraftforge.registries.ForgeRegistries; - -@EventBusSubscriber(Dist.CLIENT) -public class InterfaceClient implements IInterfaceClient { - private static CameraMode actualCameraMode; - private static CameraMode cameraModeRequest; - private static int ticksToCullingWarning = 200; - - @Override - public boolean isGamePaused() { - return Minecraft.getInstance().isPaused(); - } - - @Override - public String getLanguageName() { - if (Minecraft.getInstance().getLanguageManager() != null) { - return Minecraft.getInstance().getLanguageManager().getSelected().getCode(); - } else { - return "en_us"; - } - } - - @Override - public List getAllLanguages() { - List list = new ArrayList<>(); - Minecraft.getInstance().getLanguageManager().getLanguages().forEach(language -> list.add(language.getCode())); - return list; - } - - @Override - public String getFluidName(String fluidID, String fluidMod) { - for (Entry, Fluid> fluidEntry : ForgeRegistries.FLUIDS.getEntries()) { - ResourceLocation fluidLocation = fluidEntry.getKey().location(); - if ((fluidMod.equals(EntityFluidTank.WILDCARD_FLUID_MOD) || fluidLocation.getNamespace().equals(fluidMod)) && fluidLocation.getPath().equals(fluidID)) { - return fluidEntry.getValue().getFluidType().getDescription().getString(); - } - } - return "INVALID"; - } - - @Override - public Map getAllFluidNames() { - Map fluidIDsToNames = new HashMap<>(); - for (Fluid fluid : ForgeRegistries.FLUIDS.getValues()) { - fluidIDsToNames.put(ForgeRegistries.FLUIDS.getKey(fluid).getPath(), new FluidStack(fluid, 1).getDisplayName().getString()); - } - return fluidIDsToNames; - } - - @Override - public boolean isChatOpen() { - return Minecraft.getInstance().screen instanceof ChatScreen; - } - - @Override - public boolean isGUIOpen() { - return Minecraft.getInstance().screen != null; - } - - @Override - public void displayOverlayMessage(String message) { - Minecraft.getInstance().gui.setOverlayMessage(Component.literal(message), false); - } - - @Override - public CameraMode getCameraMode() { - return actualCameraMode; - } - - @Override - public void setCameraMode(CameraMode mode) { - cameraModeRequest = mode; - } - - @Override - public int getCameraDefaultZoom() { - return 0; - } - - @Override - public long getPackedDisplaySize() { - return (((long) Minecraft.getInstance().getWindow().getGuiScaledWidth()) << Integer.SIZE) | (Minecraft.getInstance().getWindow().getGuiScaledHeight() & 0xffffffffL); - } - - @Override - public float getFOV() { - return Minecraft.getInstance().options.fov().get(); - } - - @Override - public void setFOV(float setting) { - ((Ifov) ((Object) Minecraft.getInstance().options.fov())).setManual((int) setting); - } - - //Linked to the OptionInstanceMixin so we can implement a common interface. - public static interface Ifov { - public void setManual(Integer value); - }; - - @Override - public float getMouseSensitivity() { - return Minecraft.getInstance().options.sensitivity().get().floatValue(); - } - +package mcinterface1192; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.nio.FloatBuffer; + +import minecrafttransportsimulator.baseclasses.Point3D; +import minecrafttransportsimulator.baseclasses.RotationMatrix; +import minecrafttransportsimulator.entities.instances.EntityFluidTank; +import minecrafttransportsimulator.guis.components.AGUIBase; +import minecrafttransportsimulator.guis.instances.GUIPackMissing; +import minecrafttransportsimulator.mcinterface.AWrapperWorld; +import minecrafttransportsimulator.mcinterface.IInterfaceClient; +import minecrafttransportsimulator.mcinterface.IWrapperItemStack; +import minecrafttransportsimulator.mcinterface.IWrapperPlayer; +import minecrafttransportsimulator.mcinterface.InterfaceManager; +import minecrafttransportsimulator.packloading.PackParser; +import minecrafttransportsimulator.rendering.RenderText; +import minecrafttransportsimulator.systems.CameraSystem.CameraMode; +import minecrafttransportsimulator.systems.ConfigSystem; +import minecrafttransportsimulator.systems.ControlSystem; +import minecrafttransportsimulator.systems.LanguageSystem; +import net.minecraft.ChatFormatting; +import net.minecraft.client.CameraType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.ChatScreen; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.network.chat.TextColor; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.TickEvent.Phase; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import net.minecraftforge.registries.ForgeRegistries; + +@EventBusSubscriber(Dist.CLIENT) +public class InterfaceClient implements IInterfaceClient { + private static CameraMode actualCameraMode; + private static CameraMode cameraModeRequest; + private static int ticksToCullingWarning = 200; + + @Override + public boolean isGamePaused() { + return Minecraft.getInstance().isPaused(); + } + + @Override + public String getLanguageName() { + if (Minecraft.getInstance().getLanguageManager() != null) { + return Minecraft.getInstance().getLanguageManager().getSelected().getCode(); + } else { + return "en_us"; + } + } + + @Override + public List getAllLanguages() { + List list = new ArrayList<>(); + Minecraft.getInstance().getLanguageManager().getLanguages().forEach(language -> list.add(language.getCode())); + return list; + } + + @Override + public String getFluidName(String fluidID, String fluidMod) { + for (Entry, Fluid> fluidEntry : ForgeRegistries.FLUIDS.getEntries()) { + ResourceLocation fluidLocation = fluidEntry.getKey().location(); + if ((fluidMod.equals(EntityFluidTank.WILDCARD_FLUID_MOD) || fluidLocation.getNamespace().equals(fluidMod)) && fluidLocation.getPath().equals(fluidID)) { + return fluidEntry.getValue().getFluidType().getDescription().getString(); + } + } + return "INVALID"; + } + + @Override + public Map getAllFluidNames() { + Map fluidIDsToNames = new HashMap<>(); + for (Fluid fluid : ForgeRegistries.FLUIDS.getValues()) { + fluidIDsToNames.put(ForgeRegistries.FLUIDS.getKey(fluid).getPath(), new FluidStack(fluid, 1).getDisplayName().getString()); + } + return fluidIDsToNames; + } + + @Override + public boolean isChatOpen() { + return Minecraft.getInstance().screen instanceof ChatScreen; + } + + @Override + public boolean isGUIOpen() { + return Minecraft.getInstance().screen != null; + } + + @Override + public void displayOverlayMessage(String message) { + Minecraft.getInstance().gui.setOverlayMessage(Component.literal(message), false); + } + + @Override + public CameraMode getCameraMode() { + return actualCameraMode; + } + + @Override + public void setCameraMode(CameraMode mode) { + cameraModeRequest = mode; + } + + @Override + public int getCameraDefaultZoom() { + return 0; + } + + @Override + public long getPackedDisplaySize() { + return (((long) Minecraft.getInstance().getWindow().getGuiScaledWidth()) << Integer.SIZE) | (Minecraft.getInstance().getWindow().getGuiScaledHeight() & 0xffffffffL); + } + + @Override + public float getFOV() { + return Minecraft.getInstance().options.fov().get(); + } + + @Override + public void setFOV(float setting) { + ((Ifov) ((Object) Minecraft.getInstance().options.fov())).setManual((int) setting); + } + + //Linked to the OptionInstanceMixin so we can implement a common interface. + public static interface Ifov { + public void setManual(Integer value); + }; + + @Override + public float getMouseSensitivity() { + return Minecraft.getInstance().options.sensitivity().get().floatValue(); + } + @Override public void setMouseSensitivity(float setting) { Minecraft.getInstance().options.sensitivity().set((double) setting); @@ -162,235 +162,235 @@ public boolean isGUIHidden() { public void closeGUI() { Minecraft.getInstance().setScreen(null); } - - @Override - public void setActiveGUI(AGUIBase gui) { - Minecraft.getInstance().setScreen(new BuilderGUI(gui)); - } - - @Override - public WrapperWorld getClientWorld() { - return WrapperWorld.getWrapperFor(Minecraft.getInstance().level); - } - - @Override - public WrapperPlayer getClientPlayer() { - return WrapperPlayer.getWrapperFor(Minecraft.getInstance().player); - } - - @Override - public Point3D getCameraPosition() { - Vec3 cameraOffset = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); - mutablePosition.set(cameraOffset.x, cameraOffset.y, cameraOffset.z); - return mutablePosition; - } - - private static final Point3D mutablePosition = new Point3D(); - private static final RotationMatrix cameraProjectionOrientation = new RotationMatrix(); - private static final FloatBuffer projectionMatrixBuffer = FloatBuffer.allocate(16); - - @Override - public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight) { - double camX, camY, camZ; - double fwdX, fwdY, fwdZ; - double upX, upY, upZ; - double rgtX, rgtY, rgtZ; - - if (InterfaceEventsEntityRendering.adjustedCamera) { - camX = InterfaceEventsEntityRendering.cameraAdjustedPosition.x; - camY = InterfaceEventsEntityRendering.cameraAdjustedPosition.y; - camZ = InterfaceEventsEntityRendering.cameraAdjustedPosition.z; - RotationMatrix ori = getCameraProjectionOrientation(InterfaceEventsEntityRendering.cameraAdjustedOrientation); - fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; - upX = ori.m01; upY = ori.m11; upZ = ori.m21; - // MTS (1,0,0) rotated = camera LEFT (not right); negate to get camera right. - rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; - } else { - net.minecraft.client.Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - net.minecraft.world.phys.Vec3 camPos = camera.getPosition(); - camX = camPos.x; camY = camPos.y; camZ = camPos.z; - com.mojang.math.Vector3f look = camera.getLookVector(); - com.mojang.math.Vector3f up = camera.getUpVector(); - com.mojang.math.Vector3f left = camera.getLeftVector(); - fwdX = look.x(); fwdY = look.y(); fwdZ = look.z(); - upX = up.x(); upY = up.y(); upZ = up.z(); - rgtX = -left.x(); rgtY = -left.y(); rgtZ = -left.z(); - } - - double dx = worldPos.x - camX; - double dy = worldPos.y - camY; - double dz = worldPos.z - camZ; - - double depth = dx * fwdX + dy * fwdY + dz * fwdZ; - if (depth <= 0.001) return null; - - double xView = dx * rgtX + dy * rgtY + dz * rgtZ; - double yView = dx * upX + dy * upY + dz * upZ; - - double fovRad = Math.toRadians(getFOV()); - double tanHalfFov = Math.tan(fovRad / 2.0); - double aspect = (double) screenWidth / screenHeight; - double ndcX = xView / (depth * tanHalfFov * aspect); - double ndcY = yView / (depth * tanHalfFov); - if (InterfaceRender.projectionMatrix != null) { - projectionMatrixBuffer.clear(); - InterfaceRender.projectionMatrix.store(projectionMatrixBuffer); - double projectionScaleX = Math.abs(projectionMatrixBuffer.get(0)); - double projectionScaleY = Math.abs(projectionMatrixBuffer.get(5)); - if (projectionScaleX > 0 && projectionScaleY > 0) { - ndcX = xView * projectionScaleX / depth; - ndcY = yView * projectionScaleY / depth; - } - } - - if (ndcX < -1.1 || ndcX > 1.1 || ndcY < -1.1 || ndcY > 1.1) return null; - - screenProjectionResult.set( - (ndcX + 1.0) / 2.0 * screenWidth, - (1.0 - ndcY) / 2.0 * screenHeight, - depth); - return screenProjectionResult; - } - - private static RotationMatrix getCameraProjectionOrientation(RotationMatrix cameraOrientation) { - if (actualCameraMode == CameraMode.THIRD_PERSON_INVERTED) { - cameraProjectionOrientation.angles.set(-cameraOrientation.angles.x, cameraOrientation.angles.y - 180, -cameraOrientation.angles.z); - cameraProjectionOrientation.updateToAngles(); - return cameraProjectionOrientation; - } else { - return cameraOrientation; - } - } - - private static final Point3D screenProjectionResult = new Point3D(); - - @Override - public void playBlockBreakSound(Point3D position) { - BlockPos pos = new BlockPos(position.x, position.y, position.z); - if (!Minecraft.getInstance().level.isEmptyBlock(pos)) { - SoundType soundType = Minecraft.getInstance().level.getBlockState(pos).getBlock().getSoundType(Minecraft.getInstance().level.getBlockState(pos), Minecraft.getInstance().player.level, pos, null); - Minecraft.getInstance().level.playSound(Minecraft.getInstance().player, pos, soundType.getBreakSound(), SoundSource.BLOCKS, soundType.getVolume(), soundType.getPitch()); - } - } - - @Override - public List getTooltipLines(IWrapperItemStack stack) { - List tooltipText = new ArrayList<>(); - List tooltipLines = ((WrapperItemStack) stack).stack.getTooltipLines(Minecraft.getInstance().player, Minecraft.getInstance().options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL); - //Add grey formatting text to non-first line tooltips. - for (int i = 0; i < tooltipLines.size(); ++i) { - Component component = tooltipLines.get(i); - Style style = component.getStyle(); - String stringToAdd = ""; - if (style.isBold()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.BOLD_FORMATTING_CHAR; - } - if (style.isItalic()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.ITALIC_FORMATTING_CHAR; - } - if (style.isUnderlined()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.UNDERLINE_FORMATTING_CHAR; - } - if (style.isStrikethrough()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.STRIKETHROUGH_FORMATTING_CHAR; - } - if (style.isObfuscated()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.RANDOM_FORMATTING_CHAR; - } - if (style.getColor() != null) { - ChatFormatting legacyColor = null; - for (ChatFormatting format : ChatFormatting.values()) { - if (format.isColor()) { - if (style.getColor().equals(TextColor.fromLegacyFormat(format))) { - legacyColor = format; - break; - } - } - } - if (legacyColor != null) { - stringToAdd += RenderText.FORMATTING_CHAR + Integer.toHexString(legacyColor.ordinal()); - } - } - tooltipText.add(stringToAdd + tooltipLines.get(i).getString()); - } - return tooltipText; - } - - /** - * Tick client-side entities like bullets and particles. - * These don't get ticked normally due to the world tick event - * not being called on clients. - */ - @SubscribeEvent - public static void onIVClientTick(TickEvent.ClientTickEvent event) { - IWrapperPlayer player = InterfaceManager.clientInterface.getClientPlayer(); - if (!InterfaceManager.clientInterface.isGamePaused() && player != null) { - AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); - if (world != null) { - if (event.phase.equals(Phase.START)) { - if (!player.isSpectator()) { - //Handle controls. This has to happen prior to vehicle updates to ensure click handling is based on current position of the player. - ControlSystem.controlGlobal(player); - if (((WrapperPlayer) player).player.tickCount % 100 == 0) { - if (!InterfaceManager.clientInterface.isGUIOpen() && !PackParser.arePacksPresent()) { - new GUIPackMissing(); - } - } - } - - //Need to update world brightness since sky darken isn't calculated normally on clients. - ((WrapperWorld) world).world.updateSkyBrightness(); - - world.tickAll(true); - - //Complain about Entity Culling mod at 10 second mark. - if(ConfigSystem.settings.general.performModCompatFunctions.value && InterfaceManager.coreInterface.isModPresent("entityculling")) { - if(ticksToCullingWarning > 0) { - if(--ticksToCullingWarning == 0) { - player.displayChatMessage(LanguageSystem.SYSTEM_DEBUG, "IV HAS DETECTED THAT ENTITY CULLING MOD IS PRESENT. THIS MOD CULLS ALL IV VEHICLES UNLESS \"mts:builder_existing\", \"mts:builder_rendering\", AND \"mts:builder_seat\" ARE ADDED TO THE WHITELIST."); - } - } - } - } else { - world.tickAll(false); - - //Handle camera requests. - if(cameraModeRequest != null) { - switch(cameraModeRequest) { - case FIRST_PERSON:{ - Minecraft.getInstance().options.setCameraType(CameraType.FIRST_PERSON); - break; - } - case THIRD_PERSON:{ - Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_BACK); - break; - } - case THIRD_PERSON_INVERTED:{ - Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_FRONT); - break; - } - } - cameraModeRequest = null; - } - - //Update camera state, since this can change depending on tick if we check during renders. - CameraType cameraModeEnum = Minecraft.getInstance().options.getCameraType(); - switch(cameraModeEnum) { - case FIRST_PERSON:{ - actualCameraMode = CameraMode.FIRST_PERSON; - break; - } - case THIRD_PERSON_BACK:{ - actualCameraMode = CameraMode.THIRD_PERSON; - break; - } - case THIRD_PERSON_FRONT:{ - actualCameraMode = CameraMode.THIRD_PERSON_INVERTED; - break; - } - } - } - } - } - } -} + + @Override + public void setActiveGUI(AGUIBase gui) { + Minecraft.getInstance().setScreen(new BuilderGUI(gui)); + } + + @Override + public WrapperWorld getClientWorld() { + return WrapperWorld.getWrapperFor(Minecraft.getInstance().level); + } + + @Override + public WrapperPlayer getClientPlayer() { + return WrapperPlayer.getWrapperFor(Minecraft.getInstance().player); + } + + @Override + public Point3D getCameraPosition() { + Vec3 cameraOffset = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); + mutablePosition.set(cameraOffset.x, cameraOffset.y, cameraOffset.z); + return mutablePosition; + } + + private static final Point3D mutablePosition = new Point3D(); + private static final RotationMatrix cameraProjectionOrientation = new RotationMatrix(); + private static final FloatBuffer projectionMatrixBuffer = FloatBuffer.allocate(16); + + @Override + public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight) { + double camX, camY, camZ; + double fwdX, fwdY, fwdZ; + double upX, upY, upZ; + double rgtX, rgtY, rgtZ; + + if (InterfaceEventsEntityRendering.adjustedCamera) { + camX = InterfaceEventsEntityRendering.cameraAdjustedPosition.x; + camY = InterfaceEventsEntityRendering.cameraAdjustedPosition.y; + camZ = InterfaceEventsEntityRendering.cameraAdjustedPosition.z; + RotationMatrix ori = getCameraProjectionOrientation(InterfaceEventsEntityRendering.cameraAdjustedOrientation); + fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; + upX = ori.m01; upY = ori.m11; upZ = ori.m21; + // MTS (1,0,0) rotated = camera LEFT (not right); negate to get camera right. + rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; + } else { + net.minecraft.client.Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + net.minecraft.world.phys.Vec3 camPos = camera.getPosition(); + camX = camPos.x; camY = camPos.y; camZ = camPos.z; + com.mojang.math.Vector3f look = camera.getLookVector(); + com.mojang.math.Vector3f up = camera.getUpVector(); + com.mojang.math.Vector3f left = camera.getLeftVector(); + fwdX = look.x(); fwdY = look.y(); fwdZ = look.z(); + upX = up.x(); upY = up.y(); upZ = up.z(); + rgtX = -left.x(); rgtY = -left.y(); rgtZ = -left.z(); + } + + double dx = worldPos.x - camX; + double dy = worldPos.y - camY; + double dz = worldPos.z - camZ; + + double depth = dx * fwdX + dy * fwdY + dz * fwdZ; + if (depth <= 0.001) return null; + + double xView = dx * rgtX + dy * rgtY + dz * rgtZ; + double yView = dx * upX + dy * upY + dz * upZ; + + double fovRad = Math.toRadians(getFOV()); + double tanHalfFov = Math.tan(fovRad / 2.0); + double aspect = (double) screenWidth / screenHeight; + double ndcX = xView / (depth * tanHalfFov * aspect); + double ndcY = yView / (depth * tanHalfFov); + if (InterfaceRender.projectionMatrix != null) { + projectionMatrixBuffer.clear(); + InterfaceRender.projectionMatrix.store(projectionMatrixBuffer); + double projectionScaleX = Math.abs(projectionMatrixBuffer.get(0)); + double projectionScaleY = Math.abs(projectionMatrixBuffer.get(5)); + if (projectionScaleX > 0 && projectionScaleY > 0) { + ndcX = xView * projectionScaleX / depth; + ndcY = yView * projectionScaleY / depth; + } + } + + if (ndcX < -1.1 || ndcX > 1.1 || ndcY < -1.1 || ndcY > 1.1) return null; + + screenProjectionResult.set( + (ndcX + 1.0) / 2.0 * screenWidth, + (1.0 - ndcY) / 2.0 * screenHeight, + depth); + return screenProjectionResult; + } + + private static RotationMatrix getCameraProjectionOrientation(RotationMatrix cameraOrientation) { + if (actualCameraMode == CameraMode.THIRD_PERSON_INVERTED) { + cameraProjectionOrientation.angles.set(-cameraOrientation.angles.x, cameraOrientation.angles.y - 180, -cameraOrientation.angles.z); + cameraProjectionOrientation.updateToAngles(); + return cameraProjectionOrientation; + } else { + return cameraOrientation; + } + } + + private static final Point3D screenProjectionResult = new Point3D(); + + @Override + public void playBlockBreakSound(Point3D position) { + BlockPos pos = new BlockPos(position.x, position.y, position.z); + if (!Minecraft.getInstance().level.isEmptyBlock(pos)) { + SoundType soundType = Minecraft.getInstance().level.getBlockState(pos).getBlock().getSoundType(Minecraft.getInstance().level.getBlockState(pos), Minecraft.getInstance().player.level, pos, null); + Minecraft.getInstance().level.playSound(Minecraft.getInstance().player, pos, soundType.getBreakSound(), SoundSource.BLOCKS, soundType.getVolume(), soundType.getPitch()); + } + } + + @Override + public List getTooltipLines(IWrapperItemStack stack) { + List tooltipText = new ArrayList<>(); + List tooltipLines = ((WrapperItemStack) stack).stack.getTooltipLines(Minecraft.getInstance().player, Minecraft.getInstance().options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL); + //Add grey formatting text to non-first line tooltips. + for (int i = 0; i < tooltipLines.size(); ++i) { + Component component = tooltipLines.get(i); + Style style = component.getStyle(); + String stringToAdd = ""; + if (style.isBold()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.BOLD_FORMATTING_CHAR; + } + if (style.isItalic()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.ITALIC_FORMATTING_CHAR; + } + if (style.isUnderlined()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.UNDERLINE_FORMATTING_CHAR; + } + if (style.isStrikethrough()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.STRIKETHROUGH_FORMATTING_CHAR; + } + if (style.isObfuscated()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.RANDOM_FORMATTING_CHAR; + } + if (style.getColor() != null) { + ChatFormatting legacyColor = null; + for (ChatFormatting format : ChatFormatting.values()) { + if (format.isColor()) { + if (style.getColor().equals(TextColor.fromLegacyFormat(format))) { + legacyColor = format; + break; + } + } + } + if (legacyColor != null) { + stringToAdd += RenderText.FORMATTING_CHAR + Integer.toHexString(legacyColor.ordinal()); + } + } + tooltipText.add(stringToAdd + tooltipLines.get(i).getString()); + } + return tooltipText; + } + + /** + * Tick client-side entities like bullets and particles. + * These don't get ticked normally due to the world tick event + * not being called on clients. + */ + @SubscribeEvent + public static void onIVClientTick(TickEvent.ClientTickEvent event) { + IWrapperPlayer player = InterfaceManager.clientInterface.getClientPlayer(); + if (!InterfaceManager.clientInterface.isGamePaused() && player != null) { + AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); + if (world != null) { + if (event.phase.equals(Phase.START)) { + if (!player.isSpectator()) { + //Handle controls. This has to happen prior to vehicle updates to ensure click handling is based on current position of the player. + ControlSystem.controlGlobal(player); + if (((WrapperPlayer) player).player.tickCount % 100 == 0) { + if (!InterfaceManager.clientInterface.isGUIOpen() && !PackParser.arePacksPresent()) { + new GUIPackMissing(); + } + } + } + + //Need to update world brightness since sky darken isn't calculated normally on clients. + ((WrapperWorld) world).world.updateSkyBrightness(); + + world.tickAll(true); + + //Complain about Entity Culling mod at 10 second mark. + if(ConfigSystem.settings.general.performModCompatFunctions.value && InterfaceManager.coreInterface.isModPresent("entityculling")) { + if(ticksToCullingWarning > 0) { + if(--ticksToCullingWarning == 0) { + player.displayChatMessage(LanguageSystem.SYSTEM_DEBUG, "IV HAS DETECTED THAT ENTITY CULLING MOD IS PRESENT. THIS MOD CULLS ALL IV VEHICLES UNLESS \"mts:builder_existing\", \"mts:builder_rendering\", AND \"mts:builder_seat\" ARE ADDED TO THE WHITELIST."); + } + } + } + } else { + world.tickAll(false); + + //Handle camera requests. + if(cameraModeRequest != null) { + switch(cameraModeRequest) { + case FIRST_PERSON:{ + Minecraft.getInstance().options.setCameraType(CameraType.FIRST_PERSON); + break; + } + case THIRD_PERSON:{ + Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_BACK); + break; + } + case THIRD_PERSON_INVERTED:{ + Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_FRONT); + break; + } + } + cameraModeRequest = null; + } + + //Update camera state, since this can change depending on tick if we check during renders. + CameraType cameraModeEnum = Minecraft.getInstance().options.getCameraType(); + switch(cameraModeEnum) { + case FIRST_PERSON:{ + actualCameraMode = CameraMode.FIRST_PERSON; + break; + } + case THIRD_PERSON_BACK:{ + actualCameraMode = CameraMode.THIRD_PERSON; + break; + } + case THIRD_PERSON_FRONT:{ + actualCameraMode = CameraMode.THIRD_PERSON_INVERTED; + break; + } + } + } + } + } + } +} diff --git a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java index cd99d933e..729f141bb 100644 --- a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java +++ b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java @@ -1,193 +1,193 @@ -package mcinterface1192; - -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentLinkedQueue; - -import javax.imageio.ImageIO; - -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.platform.NativeImage; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.Tesselator; -import com.mojang.blaze3d.vertex.VertexBuffer; -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.blaze3d.vertex.VertexFormat; -import com.mojang.blaze3d.vertex.VertexFormat.Mode; -import com.mojang.math.Matrix3f; -import com.mojang.math.Matrix4f; - -import minecrafttransportsimulator.baseclasses.Point3D; -import minecrafttransportsimulator.baseclasses.TransformationMatrix; -import minecrafttransportsimulator.entities.components.AEntityC_Renderable; -import minecrafttransportsimulator.guis.components.AGUIBase; -import minecrafttransportsimulator.guis.components.GUIComponentItem; -import minecrafttransportsimulator.mcinterface.AWrapperWorld; -import minecrafttransportsimulator.mcinterface.IInterfaceRender; -import minecrafttransportsimulator.mcinterface.InterfaceManager; -import minecrafttransportsimulator.rendering.GIFParser.GIFImageFrame; -import minecrafttransportsimulator.rendering.GIFParser.ParsedGIF; -import minecrafttransportsimulator.rendering.RenderableData; -import minecrafttransportsimulator.systems.ConfigSystem; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.LightTexture; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderStateShard; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.ShaderInstance; -import net.minecraft.client.renderer.culling.Frustum; -import net.minecraft.client.renderer.entity.EntityRenderer; -import net.minecraft.client.renderer.entity.EntityRendererProvider; -import net.minecraft.client.renderer.texture.DynamicTexture; -import net.minecraft.client.renderer.texture.OverlayTexture; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.Mth; -import net.minecraft.world.level.LightLayer; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.client.event.EntityRenderersEvent.RegisterRenderers; -import net.minecraftforge.client.event.RegisterShadersEvent; - -/** - * Interface for the various MC rendering engines. This class has functions for - * binding textures, changing lightmap statuses, etc. - * - * @author don_bruce - */ -public class InterfaceRender implements IInterfaceRender { - private static final Map onlineTextures = new HashMap<>(); - private static final Map animatedGIFs = new HashMap<>(); - private static final Map> animatedGIFFrames = new LinkedHashMap<>(); - - private static final List stacksToRender = new ArrayList<>(); - - private static final Map renderTypes = new HashMap<>(); - private static final Map buffers = new HashMap<>(); - private static final Map> queuedRenders = new HashMap<>(); - private static final ConcurrentLinkedQueue removedRenders = new ConcurrentLinkedQueue<>(); - - @SuppressWarnings("deprecation") - private static final ResourceLocation BLOCK_TEXTURE_LOCATION = TextureAtlas.LOCATION_BLOCKS; - private static RenderStateShard.TextureStateShard MISSING_STATE; - private static RenderStateShard.TextureStateShard BLOCK_STATE; - public static PoseStack matrixStack; - public static Matrix4f projectionMatrix; - public static MultiBufferSource renderBuffer; - public static Point3D renderCameraOffset = new Point3D(); - private static boolean renderingGUI; - private static float[] matrixConvertArray = new float[16]; - - private static ShaderInstance entityLightsShader; - private static ShaderInstance entityCutoutNoshadowsShader; - private static final RenderStateShard.ShaderStateShard MTS_ENTITY_LIGHTS_SHADER = new RenderStateShard.ShaderStateShard(() -> entityLightsShader); - private static final RenderStateShard.ShaderStateShard MTS_ENTITY_CUTOUT_NOSHADOWS_SHADER = new RenderStateShard.ShaderStateShard(() -> entityCutoutNoshadowsShader); - - public static void onIVRegisterShadersEvent(RegisterShadersEvent event) { - try { - event.registerShader(new ShaderInstance(event.getResourceManager(), new ResourceLocation(InterfaceManager.coreModID, "mts_entity_lights"), DefaultVertexFormat.NEW_ENTITY), (createdShader) -> entityLightsShader = createdShader); - event.registerShader(new ShaderInstance(event.getResourceManager(), new ResourceLocation(InterfaceManager.coreModID, "mts_entity_cutout_noshadows"), DefaultVertexFormat.NEW_ENTITY), (createdShader) -> entityCutoutNoshadowsShader = createdShader); - } catch (IOException e) { - InterfaceManager.coreInterface.logError("COULD NOT LOAD SHADER FOR LIGHTS! THIS WILL END BADLY FOR RENDERING!"); - e.printStackTrace(); - } - } - - /** - * Event that's called to setup the client. We register our render wrapper - * class here. - */ - public static void onIVRegisterRenderersEvent(RegisterRenderers event) { - //Register the global entity rendering class. - event.registerEntityRenderer(BuilderEntityRenderForwarder.E_TYPE4.get(), manager -> new EntityRenderer(manager) { - @Override - public ResourceLocation getTextureLocation(BuilderEntityRenderForwarder builder) { - return null; - } - - @Override - public boolean shouldRender(BuilderEntityRenderForwarder builder, Frustum camera, double camX, double camY, double camZ) { - //Always render the forwarder, no matter where the camera is. - return true; - } - - @Override - public void render(BuilderEntityRenderForwarder builder, float entityYaw, float partialTicks, PoseStack stack, MultiBufferSource buffer, int packedLight) { - if (builder.playerFollowing == Minecraft.getInstance().player && !ConfigSystem.settings.general.forceRenderLastSolid.value) { - //Set camera offset point for later. - renderCameraOffset.set(Mth.lerp(partialTicks, builder.xOld, builder.getX()), Mth.lerp(partialTicks, builder.yOld, builder.getY()), Mth.lerp(partialTicks, builder.zOld, builder.getZ())); - - //Set the stack variables and render. - matrixStack = stack; - renderBuffer = buffer; - doRenderCall(false, partialTicks); - } - } - }); - - //Register blank classes for the other builders. - //If we don't, the game crashes when trying to render them. - event.registerEntityRenderer(BuilderEntityExisting.E_TYPE2.get(), manager -> new BlankRender(manager)); - event.registerEntityRenderer(BuilderEntityLinkedSeat.E_TYPE3.get(), manager -> new BlankRender(manager)); - } - - @Override - public float[] getBlockBreakTexture(AWrapperWorld world, Point3D position) { - //Get normal model. - BlockPos pos = new BlockPos(position.x, position.y, position.z); - BlockState state = ((WrapperWorld) world).world.getBlockState(pos); - TextureAtlasSprite sprite = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getTexture(state, ((WrapperWorld) world).world, pos); - return new float[] { sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() }; - } - - @Override - public float[] getDefaultBlockTexture(String name) { - TextureAtlasSprite sprite = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getModelManager().getAtlas(BLOCK_TEXTURE_LOCATION).getSprite(new ResourceLocation(name.replace(":", ":blocks/"))); - return new float[] { sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() }; - } - - @Override - public String getDefaultFontTextureFolder() { - return "/assets/minecraft/textures/font"; - } - - @Override - public InputStream getTextureStream(String name) { - try { - String domain = name.substring("/assets/".length(), name.indexOf("/", "/assets/".length())); - String location = name.substring("/assets/".length() + domain.length() + 1); - return Minecraft.getInstance().getResourceManager().getResource(new ResourceLocation(domain, location)).get().open(); - } catch (Exception e) { - return null; - } - } - - @Override - public void renderItemModel(GUIComponentItem component) { - stacksToRender.add(component); - } - - @Override - public void renderVertices(RenderableData data, boolean changedSinceLastRender) { - matrixStack.pushPose(); - Matrix4f matrix4f = convertMatrix4f(data.transform); - Matrix3f matrix3f = new Matrix3f(matrix4f); - PoseStack.Pose stackEntry = matrixStack.last(); - stackEntry.pose().multiply(matrix4f); - +package mcinterface1192; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentLinkedQueue; + +import javax.imageio.ImageIO; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.blaze3d.vertex.VertexBuffer; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat.Mode; +import com.mojang.math.Matrix3f; +import com.mojang.math.Matrix4f; + +import minecrafttransportsimulator.baseclasses.Point3D; +import minecrafttransportsimulator.baseclasses.TransformationMatrix; +import minecrafttransportsimulator.entities.components.AEntityC_Renderable; +import minecrafttransportsimulator.guis.components.AGUIBase; +import minecrafttransportsimulator.guis.components.GUIComponentItem; +import minecrafttransportsimulator.mcinterface.AWrapperWorld; +import minecrafttransportsimulator.mcinterface.IInterfaceRender; +import minecrafttransportsimulator.mcinterface.InterfaceManager; +import minecrafttransportsimulator.rendering.GIFParser.GIFImageFrame; +import minecrafttransportsimulator.rendering.GIFParser.ParsedGIF; +import minecrafttransportsimulator.rendering.RenderableData; +import minecrafttransportsimulator.systems.ConfigSystem; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderStateShard; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.client.event.EntityRenderersEvent.RegisterRenderers; +import net.minecraftforge.client.event.RegisterShadersEvent; + +/** + * Interface for the various MC rendering engines. This class has functions for + * binding textures, changing lightmap statuses, etc. + * + * @author don_bruce + */ +public class InterfaceRender implements IInterfaceRender { + private static final Map onlineTextures = new HashMap<>(); + private static final Map animatedGIFs = new HashMap<>(); + private static final Map> animatedGIFFrames = new LinkedHashMap<>(); + + private static final List stacksToRender = new ArrayList<>(); + + private static final Map renderTypes = new HashMap<>(); + private static final Map buffers = new HashMap<>(); + private static final Map> queuedRenders = new HashMap<>(); + private static final ConcurrentLinkedQueue removedRenders = new ConcurrentLinkedQueue<>(); + + @SuppressWarnings("deprecation") + private static final ResourceLocation BLOCK_TEXTURE_LOCATION = TextureAtlas.LOCATION_BLOCKS; + private static RenderStateShard.TextureStateShard MISSING_STATE; + private static RenderStateShard.TextureStateShard BLOCK_STATE; + public static PoseStack matrixStack; + public static Matrix4f projectionMatrix; + public static MultiBufferSource renderBuffer; + public static Point3D renderCameraOffset = new Point3D(); + private static boolean renderingGUI; + private static float[] matrixConvertArray = new float[16]; + + private static ShaderInstance entityLightsShader; + private static ShaderInstance entityCutoutNoshadowsShader; + private static final RenderStateShard.ShaderStateShard MTS_ENTITY_LIGHTS_SHADER = new RenderStateShard.ShaderStateShard(() -> entityLightsShader); + private static final RenderStateShard.ShaderStateShard MTS_ENTITY_CUTOUT_NOSHADOWS_SHADER = new RenderStateShard.ShaderStateShard(() -> entityCutoutNoshadowsShader); + + public static void onIVRegisterShadersEvent(RegisterShadersEvent event) { + try { + event.registerShader(new ShaderInstance(event.getResourceManager(), new ResourceLocation(InterfaceManager.coreModID, "mts_entity_lights"), DefaultVertexFormat.NEW_ENTITY), (createdShader) -> entityLightsShader = createdShader); + event.registerShader(new ShaderInstance(event.getResourceManager(), new ResourceLocation(InterfaceManager.coreModID, "mts_entity_cutout_noshadows"), DefaultVertexFormat.NEW_ENTITY), (createdShader) -> entityCutoutNoshadowsShader = createdShader); + } catch (IOException e) { + InterfaceManager.coreInterface.logError("COULD NOT LOAD SHADER FOR LIGHTS! THIS WILL END BADLY FOR RENDERING!"); + e.printStackTrace(); + } + } + + /** + * Event that's called to setup the client. We register our render wrapper + * class here. + */ + public static void onIVRegisterRenderersEvent(RegisterRenderers event) { + //Register the global entity rendering class. + event.registerEntityRenderer(BuilderEntityRenderForwarder.E_TYPE4.get(), manager -> new EntityRenderer(manager) { + @Override + public ResourceLocation getTextureLocation(BuilderEntityRenderForwarder builder) { + return null; + } + + @Override + public boolean shouldRender(BuilderEntityRenderForwarder builder, Frustum camera, double camX, double camY, double camZ) { + //Always render the forwarder, no matter where the camera is. + return true; + } + + @Override + public void render(BuilderEntityRenderForwarder builder, float entityYaw, float partialTicks, PoseStack stack, MultiBufferSource buffer, int packedLight) { + if (builder.playerFollowing == Minecraft.getInstance().player && !ConfigSystem.settings.general.forceRenderLastSolid.value) { + //Set camera offset point for later. + renderCameraOffset.set(Mth.lerp(partialTicks, builder.xOld, builder.getX()), Mth.lerp(partialTicks, builder.yOld, builder.getY()), Mth.lerp(partialTicks, builder.zOld, builder.getZ())); + + //Set the stack variables and render. + matrixStack = stack; + renderBuffer = buffer; + doRenderCall(false, partialTicks); + } + } + }); + + //Register blank classes for the other builders. + //If we don't, the game crashes when trying to render them. + event.registerEntityRenderer(BuilderEntityExisting.E_TYPE2.get(), manager -> new BlankRender(manager)); + event.registerEntityRenderer(BuilderEntityLinkedSeat.E_TYPE3.get(), manager -> new BlankRender(manager)); + } + + @Override + public float[] getBlockBreakTexture(AWrapperWorld world, Point3D position) { + //Get normal model. + BlockPos pos = new BlockPos(position.x, position.y, position.z); + BlockState state = ((WrapperWorld) world).world.getBlockState(pos); + TextureAtlasSprite sprite = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getTexture(state, ((WrapperWorld) world).world, pos); + return new float[] { sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() }; + } + + @Override + public float[] getDefaultBlockTexture(String name) { + TextureAtlasSprite sprite = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getModelManager().getAtlas(BLOCK_TEXTURE_LOCATION).getSprite(new ResourceLocation(name.replace(":", ":blocks/"))); + return new float[] { sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() }; + } + + @Override + public String getDefaultFontTextureFolder() { + return "/assets/minecraft/textures/font"; + } + + @Override + public InputStream getTextureStream(String name) { + try { + String domain = name.substring("/assets/".length(), name.indexOf("/", "/assets/".length())); + String location = name.substring("/assets/".length() + domain.length() + 1); + return Minecraft.getInstance().getResourceManager().getResource(new ResourceLocation(domain, location)).get().open(); + } catch (Exception e) { + return null; + } + } + + @Override + public void renderItemModel(GUIComponentItem component) { + stacksToRender.add(component); + } + + @Override + public void renderVertices(RenderableData data, boolean changedSinceLastRender) { + matrixStack.pushPose(); + Matrix4f matrix4f = convertMatrix4f(data.transform); + Matrix3f matrix3f = new Matrix3f(matrix4f); + PoseStack.Pose stackEntry = matrixStack.last(); + stackEntry.pose().multiply(matrix4f); + if (data.vertexObject.isLines) { stackEntry.normal().mul(matrix3f); VertexConsumer buffer = renderBuffer.getBuffer(RenderType.lines()); @@ -223,512 +223,512 @@ public void renderVertices(RenderableData data, boolean changedSinceLastRender) //Rewind buffer for next read. data.vertexObject.vertices.rewind(); } else { - String typeID = data.texture + data.isTranslucent + data.lightingMode + data.enableBrightBlending; - final RenderType renderType; - if (data.vertexObject.cacheVertices && !renderingGUI && ConfigSystem.client.renderingSettings.renderingMode.value != 2) { - //Get the render type and data buffer for this entity. - renderType = renderTypes.computeIfAbsent(typeID, k -> CustomRenderType.create("mts_entity", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.TRIANGLES, 2097152, true, data.isTranslucent, CustomRenderType.createForObject(data).createCompositeState(false))); - BufferData bufferData = buffers.computeIfAbsent(data, k -> new BufferData(renderType, data)); - - //Reset buffer if it's not ready. - if (changedSinceLastRender) { - bufferData.builder.clear(); - bufferData.isReady = false; - } - if (!bufferData.isReady) { - bufferData.builder.begin(VertexFormat.Mode.TRIANGLES, renderType.format()); - while (data.vertexObject.vertices.hasRemaining()) { - //Need to parse these out first since our order differs. - float normalX = data.vertexObject.vertices.get(); - float normalY = data.vertexObject.vertices.get(); - float normalZ = data.vertexObject.vertices.get(); - float texU = data.vertexObject.vertices.get(); - float texV = data.vertexObject.vertices.get(); - float posX = data.vertexObject.vertices.get(); - float posY = data.vertexObject.vertices.get(); - float posZ = data.vertexObject.vertices.get(); - bufferData.builder.vertex(posX, posY, posZ, data.color.red, data.color.green, data.color.blue, data.alpha, texU, texV, OverlayTexture.NO_OVERLAY, data.worldLightValue, normalX, normalY, normalZ); - } - bufferData.isReady = true; - bufferData.buffer.bind(); - bufferData.buffer.upload(bufferData.builder.end()); - data.vertexObject.vertices.rewind(); - VertexBuffer.unbind(); - } - - //Add this buffer to the list to render later. - List renders = queuedRenders.get(renderType); - if (renders == null) { - renders = new ArrayList<>(); - queuedRenders.put(renderType, renders); - } - renders.add(new RenderData(stackEntry.pose(), bufferData)); - } else { - stackEntry.normal().mul(matrix3f); - renderType = renderTypes.computeIfAbsent(typeID, k -> CustomRenderType.create("mts_entity_" + typeID, DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.TRIANGLES, 256, true, data.isTranslucent, CustomRenderType.createForObject(data).createCompositeState(false))); - VertexConsumer buffer = renderBuffer.getBuffer(renderType); - //System.out.println(renderType); - while (data.vertexObject.vertices.hasRemaining()) { - //Need to parse these out first since our order differs. - float normalX = data.vertexObject.vertices.get(); - float normalY = data.vertexObject.vertices.get(); - float normalZ = data.vertexObject.vertices.get(); - float texU = data.vertexObject.vertices.get(); - float texV = data.vertexObject.vertices.get(); - float posX = data.vertexObject.vertices.get(); - float posY = data.vertexObject.vertices.get(); - float posZ = data.vertexObject.vertices.get(); - - //Add the vertex. Yes, we have to multiply this here on the CPU. Yes, it's retarded because the GPU should be doing the matrix math. - //Blaze3d my ass, this is SLOWER than DisplayLists! - buffer.vertex(stackEntry.pose(), posX, posY, posZ); - buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); - buffer.uv(texU, texV); - buffer.overlayCoords(OverlayTexture.NO_OVERLAY); - buffer.uv2(data.worldLightValue); - buffer.normal(stackEntry.normal(), normalX, normalY, normalZ); - buffer.endVertex(); - } - //Rewind buffer for next read. - data.vertexObject.vertices.rewind(); - } - } - matrixStack.popPose(); - } - - public static void doRenderCall(boolean blendingEnabled, float partialTicks) { - AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); - ConcurrentLinkedQueue allEntities = world.renderableEntities; - if (allEntities != null) { - world.beginProfiling("MTSRendering_Setup", true); - - //NOTE: this operation occurs on a ConcurrentLinkedQueue. Therefore, updates will - //not occur one after another. Sanitize your inputs! - for (AEntityC_Renderable entity : allEntities) { - matrixStack.pushPose(); - matrixStack.translate(entity.position.x - renderCameraOffset.x, entity.position.y - renderCameraOffset.y, entity.position.z - renderCameraOffset.z); - entity.render(blendingEnabled, partialTicks); - matrixStack.popPose(); - } - - //Now do the actual render. - if (!queuedRenders.isEmpty()) { - world.beginProfiling("MTSRendering_Batch", false); - renderBuffers(partialTicks); - world.endProfiling(); - } - } - } - - private static void renderBuffers(float partialTicks) { - //Call order is CRITICAL and will lead to random JME faults with no stacktrace if modified! - for (Entry> renderEntry : queuedRenders.entrySet()) { - RenderType renderType = renderEntry.getKey(); - List datas = renderEntry.getValue(); - if (!datas.isEmpty() && ConfigSystem.client.renderingSettings.renderingMode.value == 0) { - //Setup common render states. - renderType.setupRenderState(); - - //Shader is set now, get it and set values. - ShaderInstance shaderInstance = RenderSystem.getShader(); - for (int k = 0; k < 12; ++k) { - int i = RenderSystem.getShaderTexture(k); - shaderInstance.setSampler("Sampler" + k, i); - } - if (shaderInstance.PROJECTION_MATRIX != null) { - shaderInstance.PROJECTION_MATRIX.set(projectionMatrix); - } - if (shaderInstance.INVERSE_VIEW_ROTATION_MATRIX != null) { - shaderInstance.INVERSE_VIEW_ROTATION_MATRIX.set(RenderSystem.getInverseViewRotationMatrix()); - } - if (shaderInstance.COLOR_MODULATOR != null) { - shaderInstance.COLOR_MODULATOR.set(RenderSystem.getShaderColor()); - } - if (shaderInstance.FOG_START != null) { - shaderInstance.FOG_START.set(RenderSystem.getShaderFogStart()); - } - if (shaderInstance.FOG_END != null) { - shaderInstance.FOG_END.set(RenderSystem.getShaderFogEnd()); - } - if (shaderInstance.FOG_COLOR != null) { - shaderInstance.FOG_COLOR.set(RenderSystem.getShaderFogColor()); - } - if (shaderInstance.FOG_SHAPE != null) { - shaderInstance.FOG_SHAPE.set(RenderSystem.getShaderFogShape().getIndex()); - } - RenderSystem.setupShaderLights(shaderInstance); - - //Now render vertices, setting only the states required each render. - for (RenderData data : datas) { - if (shaderInstance.MODEL_VIEW_MATRIX != null) { - shaderInstance.MODEL_VIEW_MATRIX.set(data.matrix); - } - shaderInstance.apply(); - data.bufferData.buffer.bind(); - data.bufferData.buffer.draw(); - } - VertexBuffer.unbind(); - shaderInstance.clear(); - renderType.clearRenderState(); - datas.clear(); - } - - if (!datas.isEmpty()) { - renderType.setupRenderState(); - for (RenderData data : datas) { - data.bufferData.buffer.bind(); - data.bufferData.buffer.drawWithShader(data.matrix, projectionMatrix, RenderSystem.getShader()); - } - VertexBuffer.unbind(); - renderType.clearRenderState(); - datas.clear(); - } - } - - if (!removedRenders.isEmpty()) { - removedRenders.forEach(render -> render.buffer.close()); - removedRenders.clear(); - } - } - - private static class CustomRenderType extends RenderType { - private CustomRenderType(String name, VertexFormat fmt, Mode glMode, int size, boolean doCrumbling, boolean depthSorting, Runnable onEnable, Runnable onDisable) { - super(name, fmt, glMode, size, doCrumbling, depthSorting, onEnable, onDisable); - throw new IllegalStateException("This class must not be instantiated, this is only here to gain access to the rendering constants."); - } - - private static RenderType.CompositeState.CompositeStateBuilder createForObject(RenderableData data) { - //Create the state builder. Changed states are active, default states are commented to save processing but still show the state. - RenderType.CompositeState.CompositeStateBuilder stateBuilder = RenderType.CompositeState.builder(); - - //Set shader to use. - if (data.lightingMode.disableTextureShadows) { - //This shouldn't use OpenGL lighting, use shader that ignores this. - if (data.lightingMode.disableWorldLighting) { - stateBuilder.setShaderState(MTS_ENTITY_LIGHTS_SHADER); - } else { - stateBuilder.setShaderState(MTS_ENTITY_CUTOUT_NOSHADOWS_SHADER); - } - } else if (data.isTranslucent) { - stateBuilder.setShaderState(RENDERTYPE_ENTITY_SOLID_SHADER); - } else { - stateBuilder.setShaderState(RENDERTYPE_ENTITY_CUTOUT_SHADER); - } - - //Set transparency. - if (data.enableBrightBlending) { - stateBuilder.setTransparencyState(BRIGHTNESS_TRANSPARENCY); - } else if (data.isTranslucent) { - stateBuilder.setTransparencyState(PROPER_TRANSLUCENT_TRANSPARENCY); - } else { - stateBuilder.setTransparencyState(NO_TRANSPARENCY); - } - - //Texture is just based on what is specified. - stateBuilder.setTextureState(data.texture != null ? getTexture(data.texture) : NO_TEXTURE); - //Always need lightmaps for entities per their vertex format. - stateBuilder.setLightmapState(LIGHTMAP); - //Entities go to the main target. - stateBuilder.setOutputState(MAIN_TARGET); - //All entity shaders require overlays, so we use those here. - stateBuilder.setOverlayState(OVERLAY); - //Culling is required for textured surfaces since they have normals and this saves FPS. - stateBuilder.setCullState(CULL); - //Depth test is fine, it ensures translucent things don't render in front of everything. - //stateBuilder.setDepthTestState(LEQUAL_DEPTH_TEST); - //No layering is fine. - //stateBuilder.setLayeringState(NO_LAYERING); - //Default texture application is fine. - //stateBuilder.setTexturingState(DEFAULT_TEXTURING); - //Not sure what this does, but it should be fine as-is? I think this is for the Z-buffer. - //stateBuilder.setWriteMaskState(COLOR_DEPTH_WRITE); - //Don't need to poke lines, there's only one state and we don't change lines for non-line renders. - //stateBuilder.setLineState(DEFAULT_LINE); - - //Return. - return stateBuilder; - } - } - - @Override - public void deleteVertices(RenderableData data) { - if (data.vertexObject.cacheVertices) { - //Add to removed render list, we should only remove renders AFTER they are rendered. - //This ensures they are un-bound, if the were bound prior. - //Make sure we actually bound a buffer; just because the main system asks for a bound buffer, - //doesn't mean we actually can give it one. GUI models are one such case, as they don't work right - //with bound buffers due to matrix differences. - BufferData buffer = buffers.remove(data); - if (buffer != null) { - removedRenders.add(buffer); - } - } - } - - @Override - public int getLightingAtPosition(Point3D position) { - BlockPos pos = new BlockPos(position.x, position.y, position.z); - return LightTexture.pack(Minecraft.getInstance().level.getBrightness(LightLayer.BLOCK, pos), Minecraft.getInstance().level.getBrightness(LightLayer.SKY, pos)); - } - - @Override - public boolean shouldRenderBoundingBoxes() { - return Minecraft.getInstance().getEntityRenderDispatcher().shouldRenderHitBoxes(); - } - - @Override - public boolean bindURLTexture(String textureURL, InputStream stream) { - if (stream != null) { - try { - NativeImage image = NativeImage.read(NativeImage.Format.RGB, stream); - DynamicTexture texture = new DynamicTexture(image); - ResourceLocation textureLocation = Minecraft.getInstance().textureManager.register("mts-url", texture); - onlineTextures.put(textureURL, textureLocation); - return true; - } catch (Exception e) { - return false; - } - } else { - onlineTextures.put(textureURL, null); - return true; - } - } - - @Override - public boolean bindURLGIF(String textureURL, ParsedGIF gif) { - Map gifFrameIndexes = new HashMap<>(); - for (GIFImageFrame frame : gif.frames.values()) { - try { - BufferedImage frameBuffer = frame.getImage(); - ByteArrayOutputStream frameArrayStream = new ByteArrayOutputStream(); - ImageIO.write(frameBuffer, "gif", frameArrayStream); - InputStream frameStream = new ByteArrayInputStream(frameArrayStream.toByteArray()); - - NativeImage image = NativeImage.read(NativeImage.Format.RGB, frameStream); - DynamicTexture texture = new DynamicTexture(image); - ResourceLocation textureLocation = Minecraft.getInstance().textureManager.register("mts-gif", texture); - gifFrameIndexes.put(frame, textureLocation); - } catch (Exception e) { - return false; - } - } - animatedGIFs.put(textureURL, gif); - animatedGIFFrames.put(gif, gifFrameIndexes); - return true; - } - - /** - * Helper function to create a new texture state for the specified texture location. - */ - private static RenderStateShard.TextureStateShard getTexture(String textureLocation) { - //Check to make sure textures exist. We delay creating because some mods screw up this stuff in boot. Cray, looking at you buddy. - if (MISSING_STATE == null) { - MISSING_STATE = new RenderStateShard.TextureStateShard(new ResourceLocation("mts:textures/rendering/missing.png"), false, false); - BLOCK_STATE = new RenderStateShard.TextureStateShard(BLOCK_TEXTURE_LOCATION, false, false); - } - - if (animatedGIFs.containsKey(textureLocation)) { - //Special case for GIFs. - ParsedGIF parsedGIF = animatedGIFs.get(textureLocation); - return new RenderStateShard.TextureStateShard(animatedGIFFrames.get(parsedGIF).get(parsedGIF.getCurrentFrame()), false, false); - } else if (onlineTextures.containsKey(textureLocation)) { - //Online texture. - ResourceLocation onlineTexture = onlineTextures.get(textureLocation); - return onlineTexture != null ? new RenderStateShard.TextureStateShard(onlineTextures.get(textureLocation), false, false) : MISSING_STATE; - } else if (textureLocation.equals(RenderableData.GLOBAL_TEXTURE_NAME)) { - //Default texture. - return BLOCK_STATE; - } else { - //If the texture has a colon, it's a short-hand form that needs to be converted. - String formattedLocation = textureLocation; - if (textureLocation.contains(":")) { - formattedLocation = "/assets/" + textureLocation.replace(":", "/"); - } - - //Check if the texture exists. - if (InterfaceManager.coreInterface.getPackResource(formattedLocation) != null) { - //Convert the classpath-location to a domain-location path for MC. - String domain = formattedLocation.substring("/assets/".length(), formattedLocation.indexOf("/", "/assets/".length())); - String location = formattedLocation.substring("/assets/".length() + domain.length() + 1); - return new RenderStateShard.TextureStateShard(new ResourceLocation(domain, location), false, false); - } else { - InterfaceManager.coreInterface.logError("Could not find texture: " + formattedLocation + " Reverting to fallback texture."); - return MISSING_STATE; - } - } - } - - /** - * Renders the main GUI, setting up any transforms or operations as required. - */ - protected static void renderGUI(PoseStack stack, int mouseX, int mouseY, int screenWidth, int screenHeight, float partialTicks, boolean updateGUIs) { - //Get the buffer for GUI rendering. - matrixStack = stack; - matrixStack.pushPose(); - renderingGUI = true; - MultiBufferSource.BufferSource guiBuffer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); - renderBuffer = guiBuffer; - - //Render GUIs, re-creating their components if needed. - //Set Y-axis to inverted to have correct orientation. - matrixStack.scale(1.0F, -1.0F, 1.0F); - - //Render main pass, then blended pass. - int displayGUIIndex = 0; - for (AGUIBase gui : AGUIBase.activeGUIs) { - if (updateGUIs || gui.components.isEmpty()) { - gui.setupComponentsInit(screenWidth, screenHeight); - } - matrixStack.pushPose(); - if (gui.capturesPlayer()) { - //Translate in front of the main GUI components. - matrixStack.translate(0, 0, 250); - } else { - //Translate far enough to render behind the chat window. - matrixStack.translate(0, 0, -500 + 250 * displayGUIIndex++); - } - gui.render(mouseX, mouseY, false, partialTicks); - guiBuffer.endBatch(); - //Not needed, since we can't draw to custom buffers with GUIs. - //renderBuffers(); - - //Need to use RenderSystem here, since this is a direct buffer. - RenderSystem.enableBlend(); - gui.render(mouseX, mouseY, true, partialTicks); - guiBuffer.endBatch(); - //renderBuffers(); - RenderSystem.disableBlend(); - - //Render all stacks. These have to be in the standard GUI reference frame or they won't render. - matrixStack.scale(1.0F, -1.0F, 1.0F); - - for (GUIComponentItem component : stacksToRender) { - //Double-check the stack is still present, it might have been un-set since this call. - if ((WrapperItemStack) component.stackToRender != null) { - //Apply existing transform. - //Need to translate the z-offset to our value, which includes a -100 for the default added value. - //Blit starts at 200 though, plus 32 for the default item render. - float zOffset = Minecraft.getInstance().getItemRenderer().blitOffset; - Minecraft.getInstance().getItemRenderer().blitOffset = (float) (200 + component.translation.z - 100); - if (component.scale != 1.0) { - //Need to use RenderSystem here, since we can't access the stack directly for rendering scaling. - PoseStack posestack = RenderSystem.getModelViewStack(); - posestack.pushPose(); - posestack.scale(component.scale, component.scale, 1.0F); - RenderSystem.applyModelViewMatrix(); - Minecraft.getInstance().getItemRenderer().renderGuiItem(((WrapperItemStack) component.stackToRender).stack, (int) (component.translation.x / component.scale), (int) (-component.translation.y / component.scale) + 1); - posestack.popPose(); - RenderSystem.applyModelViewMatrix(); - } else { - Minecraft.getInstance().getItemRenderer().renderGuiItem(((WrapperItemStack) component.stackToRender).stack, (int) component.translation.x, (int) -component.translation.y); - } - Minecraft.getInstance().getItemRenderer().blitOffset = zOffset; - } - } - stacksToRender.clear(); - - matrixStack.popPose(); - } - matrixStack.popPose(); - renderingGUI = false; - } - - /** - * Converts internal matrix to new external matrix. - */ - public static Matrix4f convertMatrix4f(TransformationMatrix transform) { - matrixConvertArray[0] = (float) transform.m00; - matrixConvertArray[1] = (float) transform.m01; - matrixConvertArray[2] = (float) transform.m02; - matrixConvertArray[3] = (float) transform.m03; - matrixConvertArray[4] = (float) transform.m10; - matrixConvertArray[5] = (float) transform.m11; - matrixConvertArray[6] = (float) transform.m12; - matrixConvertArray[7] = (float) transform.m13; - matrixConvertArray[8] = (float) transform.m20; - matrixConvertArray[9] = (float) transform.m21; - matrixConvertArray[10] = (float) transform.m22; - matrixConvertArray[11] = (float) transform.m23; - matrixConvertArray[12] = (float) transform.m30; - matrixConvertArray[13] = (float) transform.m31; - matrixConvertArray[14] = (float) transform.m32; - matrixConvertArray[15] = (float) transform.m33; - return new Matrix4f(matrixConvertArray); - } - - - /** Blank render class used to bypass rendering for all other builders.**/ - private static class BlankRender extends EntityRenderer { - - protected BlankRender(EntityRendererProvider.Context p_i46179_1_) { - super(p_i46179_1_); - } - - @Override - public ResourceLocation getTextureLocation(T pEntity) { - return null; - } - - @Override - public boolean shouldRender(T builder, Frustum camera, double camX, double camY, double camZ) { - return false; - } - } - - private static class RenderData { - private final Matrix4f matrix; - private final BufferData bufferData; - - private RenderData(Matrix4f matrix, BufferData bufferData) { - this.matrix = new Matrix4f(matrix); - this.bufferData = bufferData; - } - } - - private static class BufferData { - final BufferBuilder builder; - final VertexBuffer buffer; - boolean isReady; - - private BufferData() { - builder = null; - buffer = null; - } - - private BufferData(RenderType type, RenderableData data) { - int vertices = data.vertexObject.vertices.limit() / 8; - //Convert verts to faces, then back to quad-verts for MC rendering. - //Add one face extra, since MC will want to increase the buffer if sees it can't handle another vert. - vertices = ((vertices / 3) + 1) * 3; - this.builder = new BufferBuilder(type.format().getIntegerSize() * vertices); - this.buffer = new VertexBuffer(); - } - } - - /** - * Proper translucent transparency. MC's one has the wrong blending function. - */ - private static final RenderStateShard.TransparencyStateShard PROPER_TRANSLUCENT_TRANSPARENCY = new RenderStateShard.TransparencyStateShard("proper_translucent_transparency", () -> { - RenderSystem.enableBlend(); - RenderSystem.depthMask(false); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - }, () -> { - RenderSystem.disableBlend(); - RenderSystem.depthMask(true); - RenderSystem.defaultBlendFunc(); - }); - - /** - * Brightness transparency. Does a special blending operation to make things behind - * the object brighter based on the object's alpha value. More alpha means more bright. - */ - private static final RenderStateShard.TransparencyStateShard BRIGHTNESS_TRANSPARENCY = new RenderStateShard.TransparencyStateShard("brightness_transparency", () -> { - RenderSystem.enableBlend(); - RenderSystem.depthMask(false); - RenderSystem.blendFunc(GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_ALPHA); - }, () -> { - RenderSystem.disableBlend(); - RenderSystem.depthMask(true); - RenderSystem.defaultBlendFunc(); - }); -} + String typeID = data.texture + data.isTranslucent + data.lightingMode + data.enableBrightBlending; + final RenderType renderType; + if (data.vertexObject.cacheVertices && !renderingGUI && ConfigSystem.client.renderingSettings.renderingMode.value != 2) { + //Get the render type and data buffer for this entity. + renderType = renderTypes.computeIfAbsent(typeID, k -> CustomRenderType.create("mts_entity", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.TRIANGLES, 2097152, true, data.isTranslucent, CustomRenderType.createForObject(data).createCompositeState(false))); + BufferData bufferData = buffers.computeIfAbsent(data, k -> new BufferData(renderType, data)); + + //Reset buffer if it's not ready. + if (changedSinceLastRender) { + bufferData.builder.clear(); + bufferData.isReady = false; + } + if (!bufferData.isReady) { + bufferData.builder.begin(VertexFormat.Mode.TRIANGLES, renderType.format()); + while (data.vertexObject.vertices.hasRemaining()) { + //Need to parse these out first since our order differs. + float normalX = data.vertexObject.vertices.get(); + float normalY = data.vertexObject.vertices.get(); + float normalZ = data.vertexObject.vertices.get(); + float texU = data.vertexObject.vertices.get(); + float texV = data.vertexObject.vertices.get(); + float posX = data.vertexObject.vertices.get(); + float posY = data.vertexObject.vertices.get(); + float posZ = data.vertexObject.vertices.get(); + bufferData.builder.vertex(posX, posY, posZ, data.color.red, data.color.green, data.color.blue, data.alpha, texU, texV, OverlayTexture.NO_OVERLAY, data.worldLightValue, normalX, normalY, normalZ); + } + bufferData.isReady = true; + bufferData.buffer.bind(); + bufferData.buffer.upload(bufferData.builder.end()); + data.vertexObject.vertices.rewind(); + VertexBuffer.unbind(); + } + + //Add this buffer to the list to render later. + List renders = queuedRenders.get(renderType); + if (renders == null) { + renders = new ArrayList<>(); + queuedRenders.put(renderType, renders); + } + renders.add(new RenderData(stackEntry.pose(), bufferData)); + } else { + stackEntry.normal().mul(matrix3f); + renderType = renderTypes.computeIfAbsent(typeID, k -> CustomRenderType.create("mts_entity_" + typeID, DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.TRIANGLES, 256, true, data.isTranslucent, CustomRenderType.createForObject(data).createCompositeState(false))); + VertexConsumer buffer = renderBuffer.getBuffer(renderType); + //System.out.println(renderType); + while (data.vertexObject.vertices.hasRemaining()) { + //Need to parse these out first since our order differs. + float normalX = data.vertexObject.vertices.get(); + float normalY = data.vertexObject.vertices.get(); + float normalZ = data.vertexObject.vertices.get(); + float texU = data.vertexObject.vertices.get(); + float texV = data.vertexObject.vertices.get(); + float posX = data.vertexObject.vertices.get(); + float posY = data.vertexObject.vertices.get(); + float posZ = data.vertexObject.vertices.get(); + + //Add the vertex. Yes, we have to multiply this here on the CPU. Yes, it's retarded because the GPU should be doing the matrix math. + //Blaze3d my ass, this is SLOWER than DisplayLists! + buffer.vertex(stackEntry.pose(), posX, posY, posZ); + buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); + buffer.uv(texU, texV); + buffer.overlayCoords(OverlayTexture.NO_OVERLAY); + buffer.uv2(data.worldLightValue); + buffer.normal(stackEntry.normal(), normalX, normalY, normalZ); + buffer.endVertex(); + } + //Rewind buffer for next read. + data.vertexObject.vertices.rewind(); + } + } + matrixStack.popPose(); + } + + public static void doRenderCall(boolean blendingEnabled, float partialTicks) { + AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); + ConcurrentLinkedQueue allEntities = world.renderableEntities; + if (allEntities != null) { + world.beginProfiling("MTSRendering_Setup", true); + + //NOTE: this operation occurs on a ConcurrentLinkedQueue. Therefore, updates will + //not occur one after another. Sanitize your inputs! + for (AEntityC_Renderable entity : allEntities) { + matrixStack.pushPose(); + matrixStack.translate(entity.position.x - renderCameraOffset.x, entity.position.y - renderCameraOffset.y, entity.position.z - renderCameraOffset.z); + entity.render(blendingEnabled, partialTicks); + matrixStack.popPose(); + } + + //Now do the actual render. + if (!queuedRenders.isEmpty()) { + world.beginProfiling("MTSRendering_Batch", false); + renderBuffers(partialTicks); + world.endProfiling(); + } + } + } + + private static void renderBuffers(float partialTicks) { + //Call order is CRITICAL and will lead to random JME faults with no stacktrace if modified! + for (Entry> renderEntry : queuedRenders.entrySet()) { + RenderType renderType = renderEntry.getKey(); + List datas = renderEntry.getValue(); + if (!datas.isEmpty() && ConfigSystem.client.renderingSettings.renderingMode.value == 0) { + //Setup common render states. + renderType.setupRenderState(); + + //Shader is set now, get it and set values. + ShaderInstance shaderInstance = RenderSystem.getShader(); + for (int k = 0; k < 12; ++k) { + int i = RenderSystem.getShaderTexture(k); + shaderInstance.setSampler("Sampler" + k, i); + } + if (shaderInstance.PROJECTION_MATRIX != null) { + shaderInstance.PROJECTION_MATRIX.set(projectionMatrix); + } + if (shaderInstance.INVERSE_VIEW_ROTATION_MATRIX != null) { + shaderInstance.INVERSE_VIEW_ROTATION_MATRIX.set(RenderSystem.getInverseViewRotationMatrix()); + } + if (shaderInstance.COLOR_MODULATOR != null) { + shaderInstance.COLOR_MODULATOR.set(RenderSystem.getShaderColor()); + } + if (shaderInstance.FOG_START != null) { + shaderInstance.FOG_START.set(RenderSystem.getShaderFogStart()); + } + if (shaderInstance.FOG_END != null) { + shaderInstance.FOG_END.set(RenderSystem.getShaderFogEnd()); + } + if (shaderInstance.FOG_COLOR != null) { + shaderInstance.FOG_COLOR.set(RenderSystem.getShaderFogColor()); + } + if (shaderInstance.FOG_SHAPE != null) { + shaderInstance.FOG_SHAPE.set(RenderSystem.getShaderFogShape().getIndex()); + } + RenderSystem.setupShaderLights(shaderInstance); + + //Now render vertices, setting only the states required each render. + for (RenderData data : datas) { + if (shaderInstance.MODEL_VIEW_MATRIX != null) { + shaderInstance.MODEL_VIEW_MATRIX.set(data.matrix); + } + shaderInstance.apply(); + data.bufferData.buffer.bind(); + data.bufferData.buffer.draw(); + } + VertexBuffer.unbind(); + shaderInstance.clear(); + renderType.clearRenderState(); + datas.clear(); + } + + if (!datas.isEmpty()) { + renderType.setupRenderState(); + for (RenderData data : datas) { + data.bufferData.buffer.bind(); + data.bufferData.buffer.drawWithShader(data.matrix, projectionMatrix, RenderSystem.getShader()); + } + VertexBuffer.unbind(); + renderType.clearRenderState(); + datas.clear(); + } + } + + if (!removedRenders.isEmpty()) { + removedRenders.forEach(render -> render.buffer.close()); + removedRenders.clear(); + } + } + + private static class CustomRenderType extends RenderType { + private CustomRenderType(String name, VertexFormat fmt, Mode glMode, int size, boolean doCrumbling, boolean depthSorting, Runnable onEnable, Runnable onDisable) { + super(name, fmt, glMode, size, doCrumbling, depthSorting, onEnable, onDisable); + throw new IllegalStateException("This class must not be instantiated, this is only here to gain access to the rendering constants."); + } + + private static RenderType.CompositeState.CompositeStateBuilder createForObject(RenderableData data) { + //Create the state builder. Changed states are active, default states are commented to save processing but still show the state. + RenderType.CompositeState.CompositeStateBuilder stateBuilder = RenderType.CompositeState.builder(); + + //Set shader to use. + if (data.lightingMode.disableTextureShadows) { + //This shouldn't use OpenGL lighting, use shader that ignores this. + if (data.lightingMode.disableWorldLighting) { + stateBuilder.setShaderState(MTS_ENTITY_LIGHTS_SHADER); + } else { + stateBuilder.setShaderState(MTS_ENTITY_CUTOUT_NOSHADOWS_SHADER); + } + } else if (data.isTranslucent) { + stateBuilder.setShaderState(RENDERTYPE_ENTITY_SOLID_SHADER); + } else { + stateBuilder.setShaderState(RENDERTYPE_ENTITY_CUTOUT_SHADER); + } + + //Set transparency. + if (data.enableBrightBlending) { + stateBuilder.setTransparencyState(BRIGHTNESS_TRANSPARENCY); + } else if (data.isTranslucent) { + stateBuilder.setTransparencyState(PROPER_TRANSLUCENT_TRANSPARENCY); + } else { + stateBuilder.setTransparencyState(NO_TRANSPARENCY); + } + + //Texture is just based on what is specified. + stateBuilder.setTextureState(data.texture != null ? getTexture(data.texture) : NO_TEXTURE); + //Always need lightmaps for entities per their vertex format. + stateBuilder.setLightmapState(LIGHTMAP); + //Entities go to the main target. + stateBuilder.setOutputState(MAIN_TARGET); + //All entity shaders require overlays, so we use those here. + stateBuilder.setOverlayState(OVERLAY); + //Culling is required for textured surfaces since they have normals and this saves FPS. + stateBuilder.setCullState(CULL); + //Depth test is fine, it ensures translucent things don't render in front of everything. + //stateBuilder.setDepthTestState(LEQUAL_DEPTH_TEST); + //No layering is fine. + //stateBuilder.setLayeringState(NO_LAYERING); + //Default texture application is fine. + //stateBuilder.setTexturingState(DEFAULT_TEXTURING); + //Not sure what this does, but it should be fine as-is? I think this is for the Z-buffer. + //stateBuilder.setWriteMaskState(COLOR_DEPTH_WRITE); + //Don't need to poke lines, there's only one state and we don't change lines for non-line renders. + //stateBuilder.setLineState(DEFAULT_LINE); + + //Return. + return stateBuilder; + } + } + + @Override + public void deleteVertices(RenderableData data) { + if (data.vertexObject.cacheVertices) { + //Add to removed render list, we should only remove renders AFTER they are rendered. + //This ensures they are un-bound, if the were bound prior. + //Make sure we actually bound a buffer; just because the main system asks for a bound buffer, + //doesn't mean we actually can give it one. GUI models are one such case, as they don't work right + //with bound buffers due to matrix differences. + BufferData buffer = buffers.remove(data); + if (buffer != null) { + removedRenders.add(buffer); + } + } + } + + @Override + public int getLightingAtPosition(Point3D position) { + BlockPos pos = new BlockPos(position.x, position.y, position.z); + return LightTexture.pack(Minecraft.getInstance().level.getBrightness(LightLayer.BLOCK, pos), Minecraft.getInstance().level.getBrightness(LightLayer.SKY, pos)); + } + + @Override + public boolean shouldRenderBoundingBoxes() { + return Minecraft.getInstance().getEntityRenderDispatcher().shouldRenderHitBoxes(); + } + + @Override + public boolean bindURLTexture(String textureURL, InputStream stream) { + if (stream != null) { + try { + NativeImage image = NativeImage.read(NativeImage.Format.RGB, stream); + DynamicTexture texture = new DynamicTexture(image); + ResourceLocation textureLocation = Minecraft.getInstance().textureManager.register("mts-url", texture); + onlineTextures.put(textureURL, textureLocation); + return true; + } catch (Exception e) { + return false; + } + } else { + onlineTextures.put(textureURL, null); + return true; + } + } + + @Override + public boolean bindURLGIF(String textureURL, ParsedGIF gif) { + Map gifFrameIndexes = new HashMap<>(); + for (GIFImageFrame frame : gif.frames.values()) { + try { + BufferedImage frameBuffer = frame.getImage(); + ByteArrayOutputStream frameArrayStream = new ByteArrayOutputStream(); + ImageIO.write(frameBuffer, "gif", frameArrayStream); + InputStream frameStream = new ByteArrayInputStream(frameArrayStream.toByteArray()); + + NativeImage image = NativeImage.read(NativeImage.Format.RGB, frameStream); + DynamicTexture texture = new DynamicTexture(image); + ResourceLocation textureLocation = Minecraft.getInstance().textureManager.register("mts-gif", texture); + gifFrameIndexes.put(frame, textureLocation); + } catch (Exception e) { + return false; + } + } + animatedGIFs.put(textureURL, gif); + animatedGIFFrames.put(gif, gifFrameIndexes); + return true; + } + + /** + * Helper function to create a new texture state for the specified texture location. + */ + private static RenderStateShard.TextureStateShard getTexture(String textureLocation) { + //Check to make sure textures exist. We delay creating because some mods screw up this stuff in boot. Cray, looking at you buddy. + if (MISSING_STATE == null) { + MISSING_STATE = new RenderStateShard.TextureStateShard(new ResourceLocation("mts:textures/rendering/missing.png"), false, false); + BLOCK_STATE = new RenderStateShard.TextureStateShard(BLOCK_TEXTURE_LOCATION, false, false); + } + + if (animatedGIFs.containsKey(textureLocation)) { + //Special case for GIFs. + ParsedGIF parsedGIF = animatedGIFs.get(textureLocation); + return new RenderStateShard.TextureStateShard(animatedGIFFrames.get(parsedGIF).get(parsedGIF.getCurrentFrame()), false, false); + } else if (onlineTextures.containsKey(textureLocation)) { + //Online texture. + ResourceLocation onlineTexture = onlineTextures.get(textureLocation); + return onlineTexture != null ? new RenderStateShard.TextureStateShard(onlineTextures.get(textureLocation), false, false) : MISSING_STATE; + } else if (textureLocation.equals(RenderableData.GLOBAL_TEXTURE_NAME)) { + //Default texture. + return BLOCK_STATE; + } else { + //If the texture has a colon, it's a short-hand form that needs to be converted. + String formattedLocation = textureLocation; + if (textureLocation.contains(":")) { + formattedLocation = "/assets/" + textureLocation.replace(":", "/"); + } + + //Check if the texture exists. + if (InterfaceManager.coreInterface.getPackResource(formattedLocation) != null) { + //Convert the classpath-location to a domain-location path for MC. + String domain = formattedLocation.substring("/assets/".length(), formattedLocation.indexOf("/", "/assets/".length())); + String location = formattedLocation.substring("/assets/".length() + domain.length() + 1); + return new RenderStateShard.TextureStateShard(new ResourceLocation(domain, location), false, false); + } else { + InterfaceManager.coreInterface.logError("Could not find texture: " + formattedLocation + " Reverting to fallback texture."); + return MISSING_STATE; + } + } + } + + /** + * Renders the main GUI, setting up any transforms or operations as required. + */ + protected static void renderGUI(PoseStack stack, int mouseX, int mouseY, int screenWidth, int screenHeight, float partialTicks, boolean updateGUIs) { + //Get the buffer for GUI rendering. + matrixStack = stack; + matrixStack.pushPose(); + renderingGUI = true; + MultiBufferSource.BufferSource guiBuffer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); + renderBuffer = guiBuffer; + + //Render GUIs, re-creating their components if needed. + //Set Y-axis to inverted to have correct orientation. + matrixStack.scale(1.0F, -1.0F, 1.0F); + + //Render main pass, then blended pass. + int displayGUIIndex = 0; + for (AGUIBase gui : AGUIBase.activeGUIs) { + if (updateGUIs || gui.components.isEmpty()) { + gui.setupComponentsInit(screenWidth, screenHeight); + } + matrixStack.pushPose(); + if (gui.capturesPlayer()) { + //Translate in front of the main GUI components. + matrixStack.translate(0, 0, 250); + } else { + //Translate far enough to render behind the chat window. + matrixStack.translate(0, 0, -500 + 250 * displayGUIIndex++); + } + gui.render(mouseX, mouseY, false, partialTicks); + guiBuffer.endBatch(); + //Not needed, since we can't draw to custom buffers with GUIs. + //renderBuffers(); + + //Need to use RenderSystem here, since this is a direct buffer. + RenderSystem.enableBlend(); + gui.render(mouseX, mouseY, true, partialTicks); + guiBuffer.endBatch(); + //renderBuffers(); + RenderSystem.disableBlend(); + + //Render all stacks. These have to be in the standard GUI reference frame or they won't render. + matrixStack.scale(1.0F, -1.0F, 1.0F); + + for (GUIComponentItem component : stacksToRender) { + //Double-check the stack is still present, it might have been un-set since this call. + if ((WrapperItemStack) component.stackToRender != null) { + //Apply existing transform. + //Need to translate the z-offset to our value, which includes a -100 for the default added value. + //Blit starts at 200 though, plus 32 for the default item render. + float zOffset = Minecraft.getInstance().getItemRenderer().blitOffset; + Minecraft.getInstance().getItemRenderer().blitOffset = (float) (200 + component.translation.z - 100); + if (component.scale != 1.0) { + //Need to use RenderSystem here, since we can't access the stack directly for rendering scaling. + PoseStack posestack = RenderSystem.getModelViewStack(); + posestack.pushPose(); + posestack.scale(component.scale, component.scale, 1.0F); + RenderSystem.applyModelViewMatrix(); + Minecraft.getInstance().getItemRenderer().renderGuiItem(((WrapperItemStack) component.stackToRender).stack, (int) (component.translation.x / component.scale), (int) (-component.translation.y / component.scale) + 1); + posestack.popPose(); + RenderSystem.applyModelViewMatrix(); + } else { + Minecraft.getInstance().getItemRenderer().renderGuiItem(((WrapperItemStack) component.stackToRender).stack, (int) component.translation.x, (int) -component.translation.y); + } + Minecraft.getInstance().getItemRenderer().blitOffset = zOffset; + } + } + stacksToRender.clear(); + + matrixStack.popPose(); + } + matrixStack.popPose(); + renderingGUI = false; + } + + /** + * Converts internal matrix to new external matrix. + */ + public static Matrix4f convertMatrix4f(TransformationMatrix transform) { + matrixConvertArray[0] = (float) transform.m00; + matrixConvertArray[1] = (float) transform.m01; + matrixConvertArray[2] = (float) transform.m02; + matrixConvertArray[3] = (float) transform.m03; + matrixConvertArray[4] = (float) transform.m10; + matrixConvertArray[5] = (float) transform.m11; + matrixConvertArray[6] = (float) transform.m12; + matrixConvertArray[7] = (float) transform.m13; + matrixConvertArray[8] = (float) transform.m20; + matrixConvertArray[9] = (float) transform.m21; + matrixConvertArray[10] = (float) transform.m22; + matrixConvertArray[11] = (float) transform.m23; + matrixConvertArray[12] = (float) transform.m30; + matrixConvertArray[13] = (float) transform.m31; + matrixConvertArray[14] = (float) transform.m32; + matrixConvertArray[15] = (float) transform.m33; + return new Matrix4f(matrixConvertArray); + } + + + /** Blank render class used to bypass rendering for all other builders.**/ + private static class BlankRender extends EntityRenderer { + + protected BlankRender(EntityRendererProvider.Context p_i46179_1_) { + super(p_i46179_1_); + } + + @Override + public ResourceLocation getTextureLocation(T pEntity) { + return null; + } + + @Override + public boolean shouldRender(T builder, Frustum camera, double camX, double camY, double camZ) { + return false; + } + } + + private static class RenderData { + private final Matrix4f matrix; + private final BufferData bufferData; + + private RenderData(Matrix4f matrix, BufferData bufferData) { + this.matrix = new Matrix4f(matrix); + this.bufferData = bufferData; + } + } + + private static class BufferData { + final BufferBuilder builder; + final VertexBuffer buffer; + boolean isReady; + + private BufferData() { + builder = null; + buffer = null; + } + + private BufferData(RenderType type, RenderableData data) { + int vertices = data.vertexObject.vertices.limit() / 8; + //Convert verts to faces, then back to quad-verts for MC rendering. + //Add one face extra, since MC will want to increase the buffer if sees it can't handle another vert. + vertices = ((vertices / 3) + 1) * 3; + this.builder = new BufferBuilder(type.format().getIntegerSize() * vertices); + this.buffer = new VertexBuffer(); + } + } + + /** + * Proper translucent transparency. MC's one has the wrong blending function. + */ + private static final RenderStateShard.TransparencyStateShard PROPER_TRANSLUCENT_TRANSPARENCY = new RenderStateShard.TransparencyStateShard("proper_translucent_transparency", () -> { + RenderSystem.enableBlend(); + RenderSystem.depthMask(false); + RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + }, () -> { + RenderSystem.disableBlend(); + RenderSystem.depthMask(true); + RenderSystem.defaultBlendFunc(); + }); + + /** + * Brightness transparency. Does a special blending operation to make things behind + * the object brighter based on the object's alpha value. More alpha means more bright. + */ + private static final RenderStateShard.TransparencyStateShard BRIGHTNESS_TRANSPARENCY = new RenderStateShard.TransparencyStateShard("brightness_transparency", () -> { + RenderSystem.enableBlend(); + RenderSystem.depthMask(false); + RenderSystem.blendFunc(GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_ALPHA); + }, () -> { + RenderSystem.disableBlend(); + RenderSystem.depthMask(true); + RenderSystem.defaultBlendFunc(); + }); +} diff --git a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java index 294415b06..d83abd6bf 100644 --- a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java +++ b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java @@ -1,152 +1,152 @@ -package mcinterface1201; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import minecrafttransportsimulator.baseclasses.Point3D; -import minecrafttransportsimulator.baseclasses.RotationMatrix; -import minecrafttransportsimulator.entities.instances.EntityFluidTank; -import minecrafttransportsimulator.guis.components.AGUIBase; -import minecrafttransportsimulator.guis.instances.GUIPackMissing; -import minecrafttransportsimulator.mcinterface.AWrapperWorld; -import minecrafttransportsimulator.mcinterface.IInterfaceClient; -import minecrafttransportsimulator.mcinterface.IWrapperItemStack; -import minecrafttransportsimulator.mcinterface.IWrapperPlayer; -import minecrafttransportsimulator.mcinterface.InterfaceManager; -import minecrafttransportsimulator.packloading.PackParser; -import minecrafttransportsimulator.rendering.RenderText; -import minecrafttransportsimulator.systems.CameraSystem.CameraMode; -import minecrafttransportsimulator.systems.ConfigSystem; -import minecrafttransportsimulator.systems.ControlSystem; -import minecrafttransportsimulator.systems.LanguageSystem; -import net.minecraft.ChatFormatting; -import net.minecraft.client.CameraType; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.ChatScreen; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.Style; -import net.minecraft.network.chat.TextColor; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.item.TooltipFlag; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.phys.Vec3; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.event.TickEvent.Phase; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber; -import net.minecraftforge.registries.ForgeRegistries; - -@EventBusSubscriber(Dist.CLIENT) -public class InterfaceClient implements IInterfaceClient { - private static CameraMode actualCameraMode; - private static CameraMode cameraModeRequest; - private static int ticksToCullingWarning = 200; - - @Override - public boolean isGamePaused() { - return Minecraft.getInstance().isPaused(); - } - - @Override - public String getLanguageName() { - if (Minecraft.getInstance().getLanguageManager() != null) { - return Minecraft.getInstance().getLanguageManager().getSelected(); - } else { - return "en_us"; - } - } - - @Override - public List getAllLanguages() { - List list = new ArrayList<>(); - Minecraft.getInstance().getLanguageManager().getLanguages().forEach((languageCode, languageInfo) -> list.add(languageCode)); - return list; - } - - @Override - public String getFluidName(String fluidID, String fluidMod) { - for (Entry, Fluid> fluidEntry : ForgeRegistries.FLUIDS.getEntries()) { - ResourceLocation fluidLocation = fluidEntry.getKey().location(); - if ((fluidMod.equals(EntityFluidTank.WILDCARD_FLUID_MOD) || fluidLocation.getNamespace().equals(fluidMod)) && fluidLocation.getPath().equals(fluidID)) { - return fluidEntry.getValue().getFluidType().getDescription().getString(); - } - } - return "INVALID"; - } - - @Override - public Map getAllFluidNames() { - Map fluidIDsToNames = new HashMap<>(); - for (Fluid fluid : ForgeRegistries.FLUIDS.getValues()) { - fluidIDsToNames.put(ForgeRegistries.FLUIDS.getKey(fluid).getPath(), new FluidStack(fluid, 1).getDisplayName().getString()); - } - return fluidIDsToNames; - } - - @Override - public boolean isChatOpen() { - return Minecraft.getInstance().screen instanceof ChatScreen; - } - - @Override - public boolean isGUIOpen() { - return Minecraft.getInstance().screen != null; - } - - @Override - public void displayOverlayMessage(String message) { - Minecraft.getInstance().gui.setOverlayMessage(Component.literal(message), false); - } - - @Override - public CameraMode getCameraMode() { - return actualCameraMode; - } - - @Override - public void setCameraMode(CameraMode mode) { - cameraModeRequest = mode; - } - - @Override - public int getCameraDefaultZoom() { - return 0; - } - - @Override - public long getPackedDisplaySize() { - return (((long) Minecraft.getInstance().getWindow().getGuiScaledWidth()) << Integer.SIZE) | (Minecraft.getInstance().getWindow().getGuiScaledHeight() & 0xffffffffL); - } - - @Override - public float getFOV() { - return Minecraft.getInstance().options.fov().get(); - } - - @Override - public void setFOV(float setting) { - ((Ifov) ((Object) Minecraft.getInstance().options.fov())).setManual((int) setting); - } - - //Linked to the OptionInstanceMixin so we can implement a common interface. - public static interface Ifov { - public void setManual(Integer value); - }; - - @Override - public float getMouseSensitivity() { - return Minecraft.getInstance().options.sensitivity().get().floatValue(); - } - +package mcinterface1201; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import minecrafttransportsimulator.baseclasses.Point3D; +import minecrafttransportsimulator.baseclasses.RotationMatrix; +import minecrafttransportsimulator.entities.instances.EntityFluidTank; +import minecrafttransportsimulator.guis.components.AGUIBase; +import minecrafttransportsimulator.guis.instances.GUIPackMissing; +import minecrafttransportsimulator.mcinterface.AWrapperWorld; +import minecrafttransportsimulator.mcinterface.IInterfaceClient; +import minecrafttransportsimulator.mcinterface.IWrapperItemStack; +import minecrafttransportsimulator.mcinterface.IWrapperPlayer; +import minecrafttransportsimulator.mcinterface.InterfaceManager; +import minecrafttransportsimulator.packloading.PackParser; +import minecrafttransportsimulator.rendering.RenderText; +import minecrafttransportsimulator.systems.CameraSystem.CameraMode; +import minecrafttransportsimulator.systems.ConfigSystem; +import minecrafttransportsimulator.systems.ControlSystem; +import minecrafttransportsimulator.systems.LanguageSystem; +import net.minecraft.ChatFormatting; +import net.minecraft.client.CameraType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.ChatScreen; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.network.chat.TextColor; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.TickEvent.Phase; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import net.minecraftforge.registries.ForgeRegistries; + +@EventBusSubscriber(Dist.CLIENT) +public class InterfaceClient implements IInterfaceClient { + private static CameraMode actualCameraMode; + private static CameraMode cameraModeRequest; + private static int ticksToCullingWarning = 200; + + @Override + public boolean isGamePaused() { + return Minecraft.getInstance().isPaused(); + } + + @Override + public String getLanguageName() { + if (Minecraft.getInstance().getLanguageManager() != null) { + return Minecraft.getInstance().getLanguageManager().getSelected(); + } else { + return "en_us"; + } + } + + @Override + public List getAllLanguages() { + List list = new ArrayList<>(); + Minecraft.getInstance().getLanguageManager().getLanguages().forEach((languageCode, languageInfo) -> list.add(languageCode)); + return list; + } + + @Override + public String getFluidName(String fluidID, String fluidMod) { + for (Entry, Fluid> fluidEntry : ForgeRegistries.FLUIDS.getEntries()) { + ResourceLocation fluidLocation = fluidEntry.getKey().location(); + if ((fluidMod.equals(EntityFluidTank.WILDCARD_FLUID_MOD) || fluidLocation.getNamespace().equals(fluidMod)) && fluidLocation.getPath().equals(fluidID)) { + return fluidEntry.getValue().getFluidType().getDescription().getString(); + } + } + return "INVALID"; + } + + @Override + public Map getAllFluidNames() { + Map fluidIDsToNames = new HashMap<>(); + for (Fluid fluid : ForgeRegistries.FLUIDS.getValues()) { + fluidIDsToNames.put(ForgeRegistries.FLUIDS.getKey(fluid).getPath(), new FluidStack(fluid, 1).getDisplayName().getString()); + } + return fluidIDsToNames; + } + + @Override + public boolean isChatOpen() { + return Minecraft.getInstance().screen instanceof ChatScreen; + } + + @Override + public boolean isGUIOpen() { + return Minecraft.getInstance().screen != null; + } + + @Override + public void displayOverlayMessage(String message) { + Minecraft.getInstance().gui.setOverlayMessage(Component.literal(message), false); + } + + @Override + public CameraMode getCameraMode() { + return actualCameraMode; + } + + @Override + public void setCameraMode(CameraMode mode) { + cameraModeRequest = mode; + } + + @Override + public int getCameraDefaultZoom() { + return 0; + } + + @Override + public long getPackedDisplaySize() { + return (((long) Minecraft.getInstance().getWindow().getGuiScaledWidth()) << Integer.SIZE) | (Minecraft.getInstance().getWindow().getGuiScaledHeight() & 0xffffffffL); + } + + @Override + public float getFOV() { + return Minecraft.getInstance().options.fov().get(); + } + + @Override + public void setFOV(float setting) { + ((Ifov) ((Object) Minecraft.getInstance().options.fov())).setManual((int) setting); + } + + //Linked to the OptionInstanceMixin so we can implement a common interface. + public static interface Ifov { + public void setManual(Integer value); + }; + + @Override + public float getMouseSensitivity() { + return Minecraft.getInstance().options.sensitivity().get().floatValue(); + } + @Override public void setMouseSensitivity(float setting) { Minecraft.getInstance().options.sensitivity().set((double) setting); @@ -161,232 +161,232 @@ public boolean isGUIHidden() { public void closeGUI() { Minecraft.getInstance().setScreen(null); } - - @Override - public void setActiveGUI(AGUIBase gui) { - Minecraft.getInstance().setScreen(new BuilderGUI(gui)); - } - - @Override - public WrapperWorld getClientWorld() { - return WrapperWorld.getWrapperFor(Minecraft.getInstance().level); - } - - @Override - public WrapperPlayer getClientPlayer() { - return WrapperPlayer.getWrapperFor(Minecraft.getInstance().player); - } - - @Override - public Point3D getCameraPosition() { - Vec3 cameraOffset = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); - mutablePosition.set(cameraOffset.x, cameraOffset.y, cameraOffset.z); - return mutablePosition; - } - - private static final Point3D mutablePosition = new Point3D(); - private static final RotationMatrix cameraProjectionOrientation = new RotationMatrix(); - - @Override - public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight) { - double camX, camY, camZ; - double fwdX, fwdY, fwdZ; - double upX, upY, upZ; - double rgtX, rgtY, rgtZ; - - if (InterfaceEventsEntityRendering.adjustedCamera) { - camX = InterfaceEventsEntityRendering.cameraAdjustedPosition.x; - camY = InterfaceEventsEntityRendering.cameraAdjustedPosition.y; - camZ = InterfaceEventsEntityRendering.cameraAdjustedPosition.z; - RotationMatrix ori = getCameraProjectionOrientation(InterfaceEventsEntityRendering.cameraAdjustedOrientation); - fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; - upX = ori.m01; upY = ori.m11; upZ = ori.m21; - // MTS (1,0,0) rotated = camera LEFT (not right); negate to get camera right. - rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; - } else { - net.minecraft.client.Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - net.minecraft.world.phys.Vec3 camPos = camera.getPosition(); - camX = camPos.x; camY = camPos.y; camZ = camPos.z; - org.joml.Vector3f look = camera.getLookVector(); - org.joml.Vector3f up = camera.getUpVector(); - org.joml.Vector3f left = camera.getLeftVector(); - fwdX = look.x(); fwdY = look.y(); fwdZ = look.z(); - upX = up.x(); upY = up.y(); upZ = up.z(); - rgtX = -left.x(); rgtY = -left.y(); rgtZ = -left.z(); - } - - double dx = worldPos.x - camX; - double dy = worldPos.y - camY; - double dz = worldPos.z - camZ; - - double depth = dx * fwdX + dy * fwdY + dz * fwdZ; - if (depth <= 0.001) return null; - - double xView = dx * rgtX + dy * rgtY + dz * rgtZ; - double yView = dx * upX + dy * upY + dz * upZ; - - double fovRad = Math.toRadians(getFOV()); - double tanHalfFov = Math.tan(fovRad / 2.0); - double aspect = (double) screenWidth / screenHeight; - double ndcX = xView / (depth * tanHalfFov * aspect); - double ndcY = yView / (depth * tanHalfFov); - if (InterfaceRender.projectionMatrix != null) { - double projectionScaleX = Math.abs(InterfaceRender.projectionMatrix.m00()); - double projectionScaleY = Math.abs(InterfaceRender.projectionMatrix.m11()); - if (projectionScaleX > 0 && projectionScaleY > 0) { - ndcX = xView * projectionScaleX / depth; - ndcY = yView * projectionScaleY / depth; - } - } - - if (ndcX < -1.1 || ndcX > 1.1 || ndcY < -1.1 || ndcY > 1.1) return null; - - screenProjectionResult.set( - (ndcX + 1.0) / 2.0 * screenWidth, - (1.0 - ndcY) / 2.0 * screenHeight, - depth); - return screenProjectionResult; - } - - private static RotationMatrix getCameraProjectionOrientation(RotationMatrix cameraOrientation) { - if (actualCameraMode == CameraMode.THIRD_PERSON_INVERTED) { - cameraProjectionOrientation.angles.set(-cameraOrientation.angles.x, cameraOrientation.angles.y - 180, -cameraOrientation.angles.z); - cameraProjectionOrientation.updateToAngles(); - return cameraProjectionOrientation; - } else { - return cameraOrientation; - } - } - - private static final Point3D screenProjectionResult = new Point3D(); - - @Override - public void playBlockBreakSound(Point3D position) { - BlockPos pos = BlockPos.containing(position.x, position.y, position.z); - if (!Minecraft.getInstance().level.isEmptyBlock(pos)) { - SoundType soundType = Minecraft.getInstance().level.getBlockState(pos).getBlock().getSoundType(Minecraft.getInstance().level.getBlockState(pos), Minecraft.getInstance().player.level(), pos, null); - Minecraft.getInstance().level.playSound(Minecraft.getInstance().player, pos, soundType.getBreakSound(), SoundSource.BLOCKS, soundType.getVolume(), soundType.getPitch()); - } - } - - @Override - public List getTooltipLines(IWrapperItemStack stack) { - List tooltipText = new ArrayList<>(); - List tooltipLines = ((WrapperItemStack) stack).stack.getTooltipLines(Minecraft.getInstance().player, Minecraft.getInstance().options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL); - //Add grey formatting text to non-first line tooltips. - for (int i = 0; i < tooltipLines.size(); ++i) { - Component component = tooltipLines.get(i); - Style style = component.getStyle(); - String stringToAdd = ""; - if (style.isBold()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.BOLD_FORMATTING_CHAR; - } - if (style.isItalic()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.ITALIC_FORMATTING_CHAR; - } - if (style.isUnderlined()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.UNDERLINE_FORMATTING_CHAR; - } - if (style.isStrikethrough()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.STRIKETHROUGH_FORMATTING_CHAR; - } - if (style.isObfuscated()) { - stringToAdd += RenderText.FORMATTING_CHAR + RenderText.RANDOM_FORMATTING_CHAR; - } - if (style.getColor() != null) { - ChatFormatting legacyColor = null; - for (ChatFormatting format : ChatFormatting.values()) { - if (format.isColor()) { - if (style.getColor().equals(TextColor.fromLegacyFormat(format))) { - legacyColor = format; - break; - } - } - } - if (legacyColor != null) { - stringToAdd += RenderText.FORMATTING_CHAR + Integer.toHexString(legacyColor.ordinal()); - } - } - tooltipText.add(stringToAdd + tooltipLines.get(i).getString()); - } - return tooltipText; - } - - /** - * Tick client-side entities like bullets and particles. - * These don't get ticked normally due to the world tick event - * not being called on clients. - */ - @SubscribeEvent - public static void onIVClientTick(TickEvent.ClientTickEvent event) { - IWrapperPlayer player = InterfaceManager.clientInterface.getClientPlayer(); - if (!InterfaceManager.clientInterface.isGamePaused() && player != null) { - AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); - if (world != null) { - if (event.phase.equals(Phase.START)) { - if (!player.isSpectator()) { - //Handle controls. This has to happen prior to vehicle updates to ensure click handling is based on current position of the player. - ControlSystem.controlGlobal(player); - if (((WrapperPlayer) player).player.tickCount % 100 == 0) { - if (!InterfaceManager.clientInterface.isGUIOpen() && !PackParser.arePacksPresent()) { - new GUIPackMissing(); - } - } - } - - //Need to update world brightness since sky darken isn't calculated normally on clients. - ((WrapperWorld) world).world.updateSkyBrightness(); - - world.tickAll(true); - - //Complain about Entity Culling mod at 10 second mark. - if(ConfigSystem.settings.general.performModCompatFunctions.value && InterfaceManager.coreInterface.isModPresent("entityculling")) { - if(ticksToCullingWarning > 0) { - if(--ticksToCullingWarning == 0) { - player.displayChatMessage(LanguageSystem.SYSTEM_DEBUG, "IV HAS DETECTED THAT ENTITY CULLING MOD IS PRESENT. THIS MOD CULLS ALL IV VEHICLES UNLESS \"mts:builder_existing\", \"mts:builder_rendering\", AND \"mts:builder_seat\" ARE ADDED TO THE WHITELIST."); - } - } - } - } else { - world.tickAll(false); - - //Handle camera requests. - if(cameraModeRequest != null) { - switch(cameraModeRequest) { - case FIRST_PERSON:{ - Minecraft.getInstance().options.setCameraType(CameraType.FIRST_PERSON); - break; - } - case THIRD_PERSON:{ - Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_BACK); - break; - } - case THIRD_PERSON_INVERTED:{ - Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_FRONT); - break; - } - } - cameraModeRequest = null; - } - - //Update camera state, since this can change depending on tick if we check during renders. - CameraType cameraModeEnum = Minecraft.getInstance().options.getCameraType(); - switch(cameraModeEnum) { - case FIRST_PERSON:{ - actualCameraMode = CameraMode.FIRST_PERSON; - break; - } - case THIRD_PERSON_BACK:{ - actualCameraMode = CameraMode.THIRD_PERSON; - break; - } - case THIRD_PERSON_FRONT:{ - actualCameraMode = CameraMode.THIRD_PERSON_INVERTED; - break; - } - } - } - } - } - } -} + + @Override + public void setActiveGUI(AGUIBase gui) { + Minecraft.getInstance().setScreen(new BuilderGUI(gui)); + } + + @Override + public WrapperWorld getClientWorld() { + return WrapperWorld.getWrapperFor(Minecraft.getInstance().level); + } + + @Override + public WrapperPlayer getClientPlayer() { + return WrapperPlayer.getWrapperFor(Minecraft.getInstance().player); + } + + @Override + public Point3D getCameraPosition() { + Vec3 cameraOffset = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); + mutablePosition.set(cameraOffset.x, cameraOffset.y, cameraOffset.z); + return mutablePosition; + } + + private static final Point3D mutablePosition = new Point3D(); + private static final RotationMatrix cameraProjectionOrientation = new RotationMatrix(); + + @Override + public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight) { + double camX, camY, camZ; + double fwdX, fwdY, fwdZ; + double upX, upY, upZ; + double rgtX, rgtY, rgtZ; + + if (InterfaceEventsEntityRendering.adjustedCamera) { + camX = InterfaceEventsEntityRendering.cameraAdjustedPosition.x; + camY = InterfaceEventsEntityRendering.cameraAdjustedPosition.y; + camZ = InterfaceEventsEntityRendering.cameraAdjustedPosition.z; + RotationMatrix ori = getCameraProjectionOrientation(InterfaceEventsEntityRendering.cameraAdjustedOrientation); + fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; + upX = ori.m01; upY = ori.m11; upZ = ori.m21; + // MTS (1,0,0) rotated = camera LEFT (not right); negate to get camera right. + rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; + } else { + net.minecraft.client.Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + net.minecraft.world.phys.Vec3 camPos = camera.getPosition(); + camX = camPos.x; camY = camPos.y; camZ = camPos.z; + org.joml.Vector3f look = camera.getLookVector(); + org.joml.Vector3f up = camera.getUpVector(); + org.joml.Vector3f left = camera.getLeftVector(); + fwdX = look.x(); fwdY = look.y(); fwdZ = look.z(); + upX = up.x(); upY = up.y(); upZ = up.z(); + rgtX = -left.x(); rgtY = -left.y(); rgtZ = -left.z(); + } + + double dx = worldPos.x - camX; + double dy = worldPos.y - camY; + double dz = worldPos.z - camZ; + + double depth = dx * fwdX + dy * fwdY + dz * fwdZ; + if (depth <= 0.001) return null; + + double xView = dx * rgtX + dy * rgtY + dz * rgtZ; + double yView = dx * upX + dy * upY + dz * upZ; + + double fovRad = Math.toRadians(getFOV()); + double tanHalfFov = Math.tan(fovRad / 2.0); + double aspect = (double) screenWidth / screenHeight; + double ndcX = xView / (depth * tanHalfFov * aspect); + double ndcY = yView / (depth * tanHalfFov); + if (InterfaceRender.projectionMatrix != null) { + double projectionScaleX = Math.abs(InterfaceRender.projectionMatrix.m00()); + double projectionScaleY = Math.abs(InterfaceRender.projectionMatrix.m11()); + if (projectionScaleX > 0 && projectionScaleY > 0) { + ndcX = xView * projectionScaleX / depth; + ndcY = yView * projectionScaleY / depth; + } + } + + if (ndcX < -1.1 || ndcX > 1.1 || ndcY < -1.1 || ndcY > 1.1) return null; + + screenProjectionResult.set( + (ndcX + 1.0) / 2.0 * screenWidth, + (1.0 - ndcY) / 2.0 * screenHeight, + depth); + return screenProjectionResult; + } + + private static RotationMatrix getCameraProjectionOrientation(RotationMatrix cameraOrientation) { + if (actualCameraMode == CameraMode.THIRD_PERSON_INVERTED) { + cameraProjectionOrientation.angles.set(-cameraOrientation.angles.x, cameraOrientation.angles.y - 180, -cameraOrientation.angles.z); + cameraProjectionOrientation.updateToAngles(); + return cameraProjectionOrientation; + } else { + return cameraOrientation; + } + } + + private static final Point3D screenProjectionResult = new Point3D(); + + @Override + public void playBlockBreakSound(Point3D position) { + BlockPos pos = BlockPos.containing(position.x, position.y, position.z); + if (!Minecraft.getInstance().level.isEmptyBlock(pos)) { + SoundType soundType = Minecraft.getInstance().level.getBlockState(pos).getBlock().getSoundType(Minecraft.getInstance().level.getBlockState(pos), Minecraft.getInstance().player.level(), pos, null); + Minecraft.getInstance().level.playSound(Minecraft.getInstance().player, pos, soundType.getBreakSound(), SoundSource.BLOCKS, soundType.getVolume(), soundType.getPitch()); + } + } + + @Override + public List getTooltipLines(IWrapperItemStack stack) { + List tooltipText = new ArrayList<>(); + List tooltipLines = ((WrapperItemStack) stack).stack.getTooltipLines(Minecraft.getInstance().player, Minecraft.getInstance().options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL); + //Add grey formatting text to non-first line tooltips. + for (int i = 0; i < tooltipLines.size(); ++i) { + Component component = tooltipLines.get(i); + Style style = component.getStyle(); + String stringToAdd = ""; + if (style.isBold()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.BOLD_FORMATTING_CHAR; + } + if (style.isItalic()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.ITALIC_FORMATTING_CHAR; + } + if (style.isUnderlined()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.UNDERLINE_FORMATTING_CHAR; + } + if (style.isStrikethrough()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.STRIKETHROUGH_FORMATTING_CHAR; + } + if (style.isObfuscated()) { + stringToAdd += RenderText.FORMATTING_CHAR + RenderText.RANDOM_FORMATTING_CHAR; + } + if (style.getColor() != null) { + ChatFormatting legacyColor = null; + for (ChatFormatting format : ChatFormatting.values()) { + if (format.isColor()) { + if (style.getColor().equals(TextColor.fromLegacyFormat(format))) { + legacyColor = format; + break; + } + } + } + if (legacyColor != null) { + stringToAdd += RenderText.FORMATTING_CHAR + Integer.toHexString(legacyColor.ordinal()); + } + } + tooltipText.add(stringToAdd + tooltipLines.get(i).getString()); + } + return tooltipText; + } + + /** + * Tick client-side entities like bullets and particles. + * These don't get ticked normally due to the world tick event + * not being called on clients. + */ + @SubscribeEvent + public static void onIVClientTick(TickEvent.ClientTickEvent event) { + IWrapperPlayer player = InterfaceManager.clientInterface.getClientPlayer(); + if (!InterfaceManager.clientInterface.isGamePaused() && player != null) { + AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); + if (world != null) { + if (event.phase.equals(Phase.START)) { + if (!player.isSpectator()) { + //Handle controls. This has to happen prior to vehicle updates to ensure click handling is based on current position of the player. + ControlSystem.controlGlobal(player); + if (((WrapperPlayer) player).player.tickCount % 100 == 0) { + if (!InterfaceManager.clientInterface.isGUIOpen() && !PackParser.arePacksPresent()) { + new GUIPackMissing(); + } + } + } + + //Need to update world brightness since sky darken isn't calculated normally on clients. + ((WrapperWorld) world).world.updateSkyBrightness(); + + world.tickAll(true); + + //Complain about Entity Culling mod at 10 second mark. + if(ConfigSystem.settings.general.performModCompatFunctions.value && InterfaceManager.coreInterface.isModPresent("entityculling")) { + if(ticksToCullingWarning > 0) { + if(--ticksToCullingWarning == 0) { + player.displayChatMessage(LanguageSystem.SYSTEM_DEBUG, "IV HAS DETECTED THAT ENTITY CULLING MOD IS PRESENT. THIS MOD CULLS ALL IV VEHICLES UNLESS \"mts:builder_existing\", \"mts:builder_rendering\", AND \"mts:builder_seat\" ARE ADDED TO THE WHITELIST."); + } + } + } + } else { + world.tickAll(false); + + //Handle camera requests. + if(cameraModeRequest != null) { + switch(cameraModeRequest) { + case FIRST_PERSON:{ + Minecraft.getInstance().options.setCameraType(CameraType.FIRST_PERSON); + break; + } + case THIRD_PERSON:{ + Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_BACK); + break; + } + case THIRD_PERSON_INVERTED:{ + Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_FRONT); + break; + } + } + cameraModeRequest = null; + } + + //Update camera state, since this can change depending on tick if we check during renders. + CameraType cameraModeEnum = Minecraft.getInstance().options.getCameraType(); + switch(cameraModeEnum) { + case FIRST_PERSON:{ + actualCameraMode = CameraMode.FIRST_PERSON; + break; + } + case THIRD_PERSON_BACK:{ + actualCameraMode = CameraMode.THIRD_PERSON; + break; + } + case THIRD_PERSON_FRONT:{ + actualCameraMode = CameraMode.THIRD_PERSON_INVERTED; + break; + } + } + } + } + } + } +} diff --git a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java index 1d39cb3e6..a50445cf3 100644 --- a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java +++ b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java @@ -1,217 +1,217 @@ -package mcinterface1201; - -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; - -import javax.imageio.ImageIO; - -import org.joml.Matrix3f; -import org.joml.Matrix4f; - -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.platform.NativeImage; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.Tesselator; -import com.mojang.blaze3d.vertex.VertexBuffer; -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.blaze3d.vertex.VertexFormat; -import com.mojang.blaze3d.vertex.VertexFormat.Mode; - -import minecrafttransportsimulator.baseclasses.Point3D; -import minecrafttransportsimulator.baseclasses.TransformationMatrix; -import minecrafttransportsimulator.entities.components.AEntityC_Renderable; -import minecrafttransportsimulator.guis.components.AGUIBase; -import minecrafttransportsimulator.guis.components.GUIComponentItem; -import minecrafttransportsimulator.mcinterface.AWrapperWorld; -import minecrafttransportsimulator.mcinterface.IInterfaceRender; -import minecrafttransportsimulator.mcinterface.InterfaceManager; -import minecrafttransportsimulator.rendering.GIFParser.GIFImageFrame; -import minecrafttransportsimulator.rendering.GIFParser.ParsedGIF; -import minecrafttransportsimulator.rendering.RenderableData; -import minecrafttransportsimulator.systems.ConfigSystem; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.renderer.LightTexture; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderStateShard; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.ShaderInstance; -import net.minecraft.client.renderer.culling.Frustum; -import net.minecraft.client.renderer.entity.EntityRenderer; -import net.minecraft.client.renderer.entity.EntityRendererProvider; -import net.minecraft.client.renderer.texture.DynamicTexture; -import net.minecraft.client.renderer.texture.OverlayTexture; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.Mth; -import net.minecraft.world.level.LightLayer; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.client.event.EntityRenderersEvent.RegisterRenderers; -import net.minecraftforge.client.event.RegisterShadersEvent; - -/** - * Interface for the various MC rendering engines. This class has functions for - * binding textures, changing lightmap statuses, etc. - * - * @author don_bruce - */ -public class InterfaceRender implements IInterfaceRender { - private static final Map onlineTextures = new HashMap<>(); - private static final Map animatedGIFs = new HashMap<>(); - private static final Map> animatedGIFFrames = new LinkedHashMap<>(); - - private static final List stacksToRender = new ArrayList<>(); - - private static final ConcurrentHashMap renderTypes = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap buffers = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap> queuedRenders = new ConcurrentHashMap<>(); - private static final ConcurrentLinkedQueue removedRenders = new ConcurrentLinkedQueue<>(); - - @SuppressWarnings("deprecation") - private static final ResourceLocation BLOCK_TEXTURE_LOCATION = TextureAtlas.LOCATION_BLOCKS; - private static RenderStateShard.TextureStateShard MISSING_STATE; - private static RenderStateShard.TextureStateShard BLOCK_STATE; - public static PoseStack matrixStack; - public static Matrix4f projectionMatrix; - public static MultiBufferSource renderBuffer; - public static Point3D renderCameraOffset = new Point3D(); - private static boolean renderingGUI; - - private static ShaderInstance entityLightsShader; - private static ShaderInstance entityCutoutNoshadowsShader; - private static final RenderStateShard.ShaderStateShard MTS_ENTITY_LIGHTS_SHADER = new RenderStateShard.ShaderStateShard(() -> entityLightsShader); - private static final RenderStateShard.ShaderStateShard MTS_ENTITY_CUTOUT_NOSHADOWS_SHADER = new RenderStateShard.ShaderStateShard(() -> entityCutoutNoshadowsShader); - - public static void onIVRegisterShadersEvent(RegisterShadersEvent event) { - try { - event.registerShader(new ShaderInstance(event.getResourceProvider(), new ResourceLocation(InterfaceLoader.MODID, "mts_entity_lights"), DefaultVertexFormat.NEW_ENTITY), (createdShader) -> entityLightsShader = createdShader); - event.registerShader(new ShaderInstance(event.getResourceProvider(), new ResourceLocation(InterfaceLoader.MODID, "mts_entity_cutout_noshadows"), DefaultVertexFormat.NEW_ENTITY), (createdShader) -> entityCutoutNoshadowsShader = createdShader); - } catch (IOException e) { - InterfaceManager.coreInterface.logError("COULD NOT LOAD SHADER FOR LIGHTS! THIS WILL END BADLY FOR RENDERING!"); - e.printStackTrace(); - } - } - - /** - * Event that's called to setup the client. We register our render wrapper - * class here. - */ - public static void onIVRegisterRenderersEvent(RegisterRenderers event) { - //Register the global entity rendering class. - event.registerEntityRenderer(BuilderEntityRenderForwarder.E_TYPE4.get(), manager -> new EntityRenderer(manager) { - @Override - public ResourceLocation getTextureLocation(BuilderEntityRenderForwarder builder) { - return null; - } - - @Override - public boolean shouldRender(BuilderEntityRenderForwarder builder, Frustum camera, double camX, double camY, double camZ) { - //Always render the forwarder, no matter where the camera is. - return true; - } - - @Override - public void render(BuilderEntityRenderForwarder builder, float entityYaw, float partialTicks, PoseStack stack, MultiBufferSource buffer, int packedLight) { - if (builder.playerFollowing == Minecraft.getInstance().player && !ConfigSystem.settings.general.forceRenderLastSolid.value) { - //Set camera offset point for later. - renderCameraOffset.set(Mth.lerp(partialTicks, builder.xOld, builder.getX()), Mth.lerp(partialTicks, builder.yOld, builder.getY()), Mth.lerp(partialTicks, builder.zOld, builder.getZ())); - - //Set the stack variables and render. - matrixStack = stack; - renderBuffer = buffer; - doRenderCall(false, partialTicks); - } - } - }); - - //Register blank classes for the other builders. - //If we don't, the game crashes when trying to render them. - event.registerEntityRenderer(BuilderEntityExisting.E_TYPE2.get(), manager -> new BlankRender(manager)); - event.registerEntityRenderer(BuilderEntityLinkedSeat.E_TYPE3.get(), manager -> new BlankRender(manager)); - } - - @Override - public float[] getBlockBreakTexture(AWrapperWorld world, Point3D position) { - //Get normal model. - BlockPos pos = BlockPos.containing(position.x, position.y, position.z); - BlockState state = ((WrapperWorld) world).world.getBlockState(pos); - TextureAtlasSprite sprite = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getTexture(state, ((WrapperWorld) world).world, pos); - return new float[] { sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() }; - } - - @Override - public float[] getDefaultBlockTexture(String name) { - TextureAtlasSprite sprite = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getModelManager().getAtlas(BLOCK_TEXTURE_LOCATION).getSprite(new ResourceLocation(name.replace(":", ":blocks/"))); - return new float[] { sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() }; - } - - @Override - public String getDefaultFontTextureFolder() { - return "/assets/mts/textures/mcfont"; - } - - @Override - public InputStream getTextureStream(String name) { - try { - String domain = name.substring("/assets/".length(), name.indexOf("/", "/assets/".length())); - String location = name.substring("/assets/".length() + domain.length() + 1); - return Minecraft.getInstance().getResourceManager().getResource(new ResourceLocation(domain, location)).get().open(); - } catch (Exception e) { - return null; - } - } - - @Override - public void renderItemModel(GUIComponentItem component) { - stacksToRender.add(component); - } - - @Override - public void renderVertices(RenderableData data, boolean changedSinceLastRender) { - matrixStack.pushPose(); - Matrix4f matrix4f = convertMatrix4f(data.transform); - Matrix3f matrix3f = new Matrix3f(matrix4f); - PoseStack.Pose stackEntry = matrixStack.last(); - stackEntry.pose().mul(matrix4f); - - if (data.vertexObject.isLines) { - stackEntry.normal().mul(matrix3f); - VertexConsumer buffer = renderBuffer.getBuffer(RenderType.lines()); - while (data.vertexObject.vertices.hasRemaining()) { - //Read both endpoints of the line segment to compute the direction normal. - //RenderType.lines() uses the normal to determine screen-space line expansion, - //so it must be the normalized direction of each line segment, not a fixed value. - float x1 = data.vertexObject.vertices.get(); - float y1 = data.vertexObject.vertices.get(); - float z1 = data.vertexObject.vertices.get(); - float x2 = data.vertexObject.vertices.get(); - float y2 = data.vertexObject.vertices.get(); - float z2 = data.vertexObject.vertices.get(); - float dx = x2 - x1; - float dy = y2 - y1; - float dz = z2 - z1; - float len = (float) Math.sqrt(dx * dx + dy * dy + dz * dz); - if (len > 0.0F) { - dx /= len; - dy /= len; - dz /= len; - } +package mcinterface1201; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +import javax.imageio.ImageIO; + +import org.joml.Matrix3f; +import org.joml.Matrix4f; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.blaze3d.vertex.VertexBuffer; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat.Mode; + +import minecrafttransportsimulator.baseclasses.Point3D; +import minecrafttransportsimulator.baseclasses.TransformationMatrix; +import minecrafttransportsimulator.entities.components.AEntityC_Renderable; +import minecrafttransportsimulator.guis.components.AGUIBase; +import minecrafttransportsimulator.guis.components.GUIComponentItem; +import minecrafttransportsimulator.mcinterface.AWrapperWorld; +import minecrafttransportsimulator.mcinterface.IInterfaceRender; +import minecrafttransportsimulator.mcinterface.InterfaceManager; +import minecrafttransportsimulator.rendering.GIFParser.GIFImageFrame; +import minecrafttransportsimulator.rendering.GIFParser.ParsedGIF; +import minecrafttransportsimulator.rendering.RenderableData; +import minecrafttransportsimulator.systems.ConfigSystem; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderStateShard; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.client.event.EntityRenderersEvent.RegisterRenderers; +import net.minecraftforge.client.event.RegisterShadersEvent; + +/** + * Interface for the various MC rendering engines. This class has functions for + * binding textures, changing lightmap statuses, etc. + * + * @author don_bruce + */ +public class InterfaceRender implements IInterfaceRender { + private static final Map onlineTextures = new HashMap<>(); + private static final Map animatedGIFs = new HashMap<>(); + private static final Map> animatedGIFFrames = new LinkedHashMap<>(); + + private static final List stacksToRender = new ArrayList<>(); + + private static final ConcurrentHashMap renderTypes = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap buffers = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap> queuedRenders = new ConcurrentHashMap<>(); + private static final ConcurrentLinkedQueue removedRenders = new ConcurrentLinkedQueue<>(); + + @SuppressWarnings("deprecation") + private static final ResourceLocation BLOCK_TEXTURE_LOCATION = TextureAtlas.LOCATION_BLOCKS; + private static RenderStateShard.TextureStateShard MISSING_STATE; + private static RenderStateShard.TextureStateShard BLOCK_STATE; + public static PoseStack matrixStack; + public static Matrix4f projectionMatrix; + public static MultiBufferSource renderBuffer; + public static Point3D renderCameraOffset = new Point3D(); + private static boolean renderingGUI; + + private static ShaderInstance entityLightsShader; + private static ShaderInstance entityCutoutNoshadowsShader; + private static final RenderStateShard.ShaderStateShard MTS_ENTITY_LIGHTS_SHADER = new RenderStateShard.ShaderStateShard(() -> entityLightsShader); + private static final RenderStateShard.ShaderStateShard MTS_ENTITY_CUTOUT_NOSHADOWS_SHADER = new RenderStateShard.ShaderStateShard(() -> entityCutoutNoshadowsShader); + + public static void onIVRegisterShadersEvent(RegisterShadersEvent event) { + try { + event.registerShader(new ShaderInstance(event.getResourceProvider(), new ResourceLocation(InterfaceLoader.MODID, "mts_entity_lights"), DefaultVertexFormat.NEW_ENTITY), (createdShader) -> entityLightsShader = createdShader); + event.registerShader(new ShaderInstance(event.getResourceProvider(), new ResourceLocation(InterfaceLoader.MODID, "mts_entity_cutout_noshadows"), DefaultVertexFormat.NEW_ENTITY), (createdShader) -> entityCutoutNoshadowsShader = createdShader); + } catch (IOException e) { + InterfaceManager.coreInterface.logError("COULD NOT LOAD SHADER FOR LIGHTS! THIS WILL END BADLY FOR RENDERING!"); + e.printStackTrace(); + } + } + + /** + * Event that's called to setup the client. We register our render wrapper + * class here. + */ + public static void onIVRegisterRenderersEvent(RegisterRenderers event) { + //Register the global entity rendering class. + event.registerEntityRenderer(BuilderEntityRenderForwarder.E_TYPE4.get(), manager -> new EntityRenderer(manager) { + @Override + public ResourceLocation getTextureLocation(BuilderEntityRenderForwarder builder) { + return null; + } + + @Override + public boolean shouldRender(BuilderEntityRenderForwarder builder, Frustum camera, double camX, double camY, double camZ) { + //Always render the forwarder, no matter where the camera is. + return true; + } + + @Override + public void render(BuilderEntityRenderForwarder builder, float entityYaw, float partialTicks, PoseStack stack, MultiBufferSource buffer, int packedLight) { + if (builder.playerFollowing == Minecraft.getInstance().player && !ConfigSystem.settings.general.forceRenderLastSolid.value) { + //Set camera offset point for later. + renderCameraOffset.set(Mth.lerp(partialTicks, builder.xOld, builder.getX()), Mth.lerp(partialTicks, builder.yOld, builder.getY()), Mth.lerp(partialTicks, builder.zOld, builder.getZ())); + + //Set the stack variables and render. + matrixStack = stack; + renderBuffer = buffer; + doRenderCall(false, partialTicks); + } + } + }); + + //Register blank classes for the other builders. + //If we don't, the game crashes when trying to render them. + event.registerEntityRenderer(BuilderEntityExisting.E_TYPE2.get(), manager -> new BlankRender(manager)); + event.registerEntityRenderer(BuilderEntityLinkedSeat.E_TYPE3.get(), manager -> new BlankRender(manager)); + } + + @Override + public float[] getBlockBreakTexture(AWrapperWorld world, Point3D position) { + //Get normal model. + BlockPos pos = BlockPos.containing(position.x, position.y, position.z); + BlockState state = ((WrapperWorld) world).world.getBlockState(pos); + TextureAtlasSprite sprite = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getTexture(state, ((WrapperWorld) world).world, pos); + return new float[] { sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() }; + } + + @Override + public float[] getDefaultBlockTexture(String name) { + TextureAtlasSprite sprite = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getModelManager().getAtlas(BLOCK_TEXTURE_LOCATION).getSprite(new ResourceLocation(name.replace(":", ":blocks/"))); + return new float[] { sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() }; + } + + @Override + public String getDefaultFontTextureFolder() { + return "/assets/mts/textures/mcfont"; + } + + @Override + public InputStream getTextureStream(String name) { + try { + String domain = name.substring("/assets/".length(), name.indexOf("/", "/assets/".length())); + String location = name.substring("/assets/".length() + domain.length() + 1); + return Minecraft.getInstance().getResourceManager().getResource(new ResourceLocation(domain, location)).get().open(); + } catch (Exception e) { + return null; + } + } + + @Override + public void renderItemModel(GUIComponentItem component) { + stacksToRender.add(component); + } + + @Override + public void renderVertices(RenderableData data, boolean changedSinceLastRender) { + matrixStack.pushPose(); + Matrix4f matrix4f = convertMatrix4f(data.transform); + Matrix3f matrix3f = new Matrix3f(matrix4f); + PoseStack.Pose stackEntry = matrixStack.last(); + stackEntry.pose().mul(matrix4f); + + if (data.vertexObject.isLines) { + stackEntry.normal().mul(matrix3f); + VertexConsumer buffer = renderBuffer.getBuffer(RenderType.lines()); + while (data.vertexObject.vertices.hasRemaining()) { + //Read both endpoints of the line segment to compute the direction normal. + //RenderType.lines() uses the normal to determine screen-space line expansion, + //so it must be the normalized direction of each line segment, not a fixed value. + float x1 = data.vertexObject.vertices.get(); + float y1 = data.vertexObject.vertices.get(); + float z1 = data.vertexObject.vertices.get(); + float x2 = data.vertexObject.vertices.get(); + float y2 = data.vertexObject.vertices.get(); + float z2 = data.vertexObject.vertices.get(); + float dx = x2 - x1; + float dy = y2 - y1; + float dz = z2 - z1; + float len = (float) Math.sqrt(dx * dx + dy * dy + dz * dz); + if (len > 0.0F) { + dx /= len; + dy /= len; + dz /= len; + } buffer.vertex(stackEntry.pose(), x1, y1, z1); buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); buffer.normal(stackEntry.normal(), dx, dy, dz); @@ -221,500 +221,500 @@ public void renderVertices(RenderableData data, boolean changedSinceLastRender) //Although we don't use them, normals are required for proper rendering. Stupid shaders... buffer.normal(stackEntry.normal(), dx, dy, dz); buffer.endVertex(); - } - //Rewind buffer for next read. - data.vertexObject.vertices.rewind(); - } else { - String typeID = data.texture + data.isTranslucent + data.lightingMode + data.enableBrightBlending; - final RenderType renderType; - if (data.vertexObject.cacheVertices && !renderingGUI && ConfigSystem.client.renderingSettings.renderingMode.value != 2) { - //Get the render type and data buffer for this entity. - renderType = renderTypes.computeIfAbsent(typeID, k -> CustomRenderType.create("mts_entity", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.TRIANGLES, 2097152, true, data.isTranslucent, CustomRenderType.createForObject(data).createCompositeState(false))); - BufferData bufferData = buffers.computeIfAbsent(data, k -> new BufferData(renderType, data)); - - //Reset buffer if it's not ready. - if (changedSinceLastRender) { - bufferData.builder.clear(); - bufferData.isReady = false; - } - if (!bufferData.isReady) { - bufferData.builder.begin(VertexFormat.Mode.TRIANGLES, renderType.format()); - while (data.vertexObject.vertices.hasRemaining()) { - //Need to parse these out first since our order differs. - float normalX = data.vertexObject.vertices.get(); - float normalY = data.vertexObject.vertices.get(); - float normalZ = data.vertexObject.vertices.get(); - float texU = data.vertexObject.vertices.get(); - float texV = data.vertexObject.vertices.get(); - float posX = data.vertexObject.vertices.get(); - float posY = data.vertexObject.vertices.get(); - float posZ = data.vertexObject.vertices.get(); - bufferData.builder.vertex(posX, posY, posZ, data.color.red, data.color.green, data.color.blue, data.alpha, texU, texV, OverlayTexture.NO_OVERLAY, data.worldLightValue, normalX, normalY, normalZ); - } - bufferData.isReady = true; - bufferData.buffer.bind(); - bufferData.buffer.upload(bufferData.builder.end()); - data.vertexObject.vertices.rewind(); - VertexBuffer.unbind(); - } - - //Add this buffer to the list to render later. - List renders = queuedRenders.get(renderType); - if (renders == null) { - renders = new ArrayList<>(); - queuedRenders.put(renderType, renders); - } - renders.add(new RenderData(stackEntry.pose(), bufferData)); - } else { - stackEntry.normal().mul(matrix3f); - renderType = renderTypes.computeIfAbsent(typeID, k -> CustomRenderType.create("mts_entity_" + typeID, DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.TRIANGLES, 256, true, data.isTranslucent, CustomRenderType.createForObject(data).createCompositeState(false))); - VertexConsumer buffer = renderBuffer.getBuffer(renderType); - //System.out.println(renderType); - while (data.vertexObject.vertices.hasRemaining()) { - //Need to parse these out first since our order differs. - float normalX = data.vertexObject.vertices.get(); - float normalY = data.vertexObject.vertices.get(); - float normalZ = data.vertexObject.vertices.get(); - float texU = data.vertexObject.vertices.get(); - float texV = data.vertexObject.vertices.get(); - float posX = data.vertexObject.vertices.get(); - float posY = data.vertexObject.vertices.get(); - float posZ = data.vertexObject.vertices.get(); - - //Add the vertex. Yes, we have to multiply this here on the CPU. Yes, it's retarded because the GPU should be doing the matrix math. - //Blaze3d my ass, this is SLOWER than DisplayLists! - buffer.vertex(stackEntry.pose(), posX, posY, posZ); - buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); - buffer.uv(texU, texV); - buffer.overlayCoords(OverlayTexture.NO_OVERLAY); - buffer.uv2(data.worldLightValue); - buffer.normal(stackEntry.normal(), normalX, normalY, normalZ); - buffer.endVertex(); - } - //Rewind buffer for next read. - data.vertexObject.vertices.rewind(); - } - } - matrixStack.popPose(); - } - - public static void doRenderCall(boolean blendingEnabled, float partialTicks) { - AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); - ConcurrentLinkedQueue allEntities = world.renderableEntities; - if (allEntities != null) { - world.beginProfiling("MTSRendering_Setup", true); - - //NOTE: this operation occurs on a ConcurrentLinkedQueue. Therefore, updates will - //not occur one after another. Sanitize your inputs! - for (AEntityC_Renderable entity : allEntities) { - matrixStack.pushPose(); - matrixStack.translate(entity.position.x - renderCameraOffset.x, entity.position.y - renderCameraOffset.y, entity.position.z - renderCameraOffset.z); - entity.render(blendingEnabled, partialTicks); - matrixStack.popPose(); - } - - //Now do the actual render. - if (!queuedRenders.isEmpty()) { - world.beginProfiling("MTSRendering_Batch", false); - renderBuffers(partialTicks); - world.endProfiling(); - } - } - } - - private static void renderBuffers(float partialTicks) { - //Call order is CRITICAL and will lead to random JME faults with no stacktrace if modified! - for (Entry> renderEntry : queuedRenders.entrySet()) { - RenderType renderType = renderEntry.getKey(); - List datas = renderEntry.getValue(); - if (!datas.isEmpty() && ConfigSystem.client.renderingSettings.renderingMode.value == 0) { - //Setup common render states. - renderType.setupRenderState(); - - //Shader is set now, get it and set values. - ShaderInstance shaderInstance = RenderSystem.getShader(); - for (int k = 0; k < 12; ++k) { - int i = RenderSystem.getShaderTexture(k); - shaderInstance.setSampler("Sampler" + k, i); - } - if (shaderInstance.PROJECTION_MATRIX != null) { - shaderInstance.PROJECTION_MATRIX.set(projectionMatrix); - } - if (shaderInstance.INVERSE_VIEW_ROTATION_MATRIX != null) { - shaderInstance.INVERSE_VIEW_ROTATION_MATRIX.set(RenderSystem.getInverseViewRotationMatrix()); - } - if (shaderInstance.COLOR_MODULATOR != null) { - shaderInstance.COLOR_MODULATOR.set(RenderSystem.getShaderColor()); - } - if (shaderInstance.FOG_START != null) { - shaderInstance.FOG_START.set(RenderSystem.getShaderFogStart()); - } - if (shaderInstance.FOG_END != null) { - shaderInstance.FOG_END.set(RenderSystem.getShaderFogEnd()); - } - if (shaderInstance.FOG_COLOR != null) { - shaderInstance.FOG_COLOR.set(RenderSystem.getShaderFogColor()); - } - if (shaderInstance.FOG_SHAPE != null) { - shaderInstance.FOG_SHAPE.set(RenderSystem.getShaderFogShape().getIndex()); - } - RenderSystem.setupShaderLights(shaderInstance); - - //Now render vertices, setting only the states required each render. - for (RenderData data : datas) { - if (shaderInstance.MODEL_VIEW_MATRIX != null) { - shaderInstance.MODEL_VIEW_MATRIX.set(data.matrix); - } - shaderInstance.apply(); - data.bufferData.buffer.bind(); - data.bufferData.buffer.draw(); - } - VertexBuffer.unbind(); - shaderInstance.clear(); - renderType.clearRenderState(); - datas.clear(); - } - - if (!datas.isEmpty()) { - renderType.setupRenderState(); - for (RenderData data : datas) { - data.bufferData.buffer.bind(); - data.bufferData.buffer.drawWithShader(data.matrix, projectionMatrix, RenderSystem.getShader()); - } - VertexBuffer.unbind(); - renderType.clearRenderState(); - datas.clear(); - } - } - - if (!removedRenders.isEmpty()) { - removedRenders.forEach(render -> render.buffer.close()); - removedRenders.clear(); - } - } - - private static class CustomRenderType extends RenderType { - private CustomRenderType(String name, VertexFormat fmt, Mode glMode, int size, boolean doCrumbling, boolean depthSorting, Runnable onEnable, Runnable onDisable) { - super(name, fmt, glMode, size, doCrumbling, depthSorting, onEnable, onDisable); - throw new IllegalStateException("This class must not be instantiated, this is only here to gain access to the rendering constants."); - } - - private static RenderType.CompositeState.CompositeStateBuilder createForObject(RenderableData data) { - //Create the state builder. Changed states are active, default states are commented to save processing but still show the state. - RenderType.CompositeState.CompositeStateBuilder stateBuilder = RenderType.CompositeState.builder(); - - //Set shader to use. - if (data.lightingMode.disableTextureShadows) { - //This shouldn't use OpenGL lighting, use shader that ignores this. - if (data.lightingMode.disableWorldLighting) { - stateBuilder.setShaderState(MTS_ENTITY_LIGHTS_SHADER); - } else { - stateBuilder.setShaderState(MTS_ENTITY_CUTOUT_NOSHADOWS_SHADER); - } - } else if (data.isTranslucent) { - stateBuilder.setShaderState(RENDERTYPE_ENTITY_SOLID_SHADER); - } else { - stateBuilder.setShaderState(RENDERTYPE_ENTITY_CUTOUT_SHADER); - } - - //Set transparency. - if (data.enableBrightBlending) { - stateBuilder.setTransparencyState(BRIGHTNESS_TRANSPARENCY); - } else if (data.isTranslucent) { - stateBuilder.setTransparencyState(PROPER_TRANSLUCENT_TRANSPARENCY); - } else { - stateBuilder.setTransparencyState(NO_TRANSPARENCY); - } - - //Texture is just based on what is specified. - stateBuilder.setTextureState(data.texture != null ? getTexture(data.texture) : NO_TEXTURE); - //Always need lightmaps for entities per their vertex format. - stateBuilder.setLightmapState(LIGHTMAP); - //Entities go to the main target. - stateBuilder.setOutputState(MAIN_TARGET); - //All entity shaders require overlays, so we use those here. - stateBuilder.setOverlayState(OVERLAY); - //Culling is required for textured surfaces since they have normals and this saves FPS. - stateBuilder.setCullState(CULL); - //Depth test is fine, it ensures translucent things don't render in front of everything. - //stateBuilder.setDepthTestState(LEQUAL_DEPTH_TEST); - //No layering is fine. - //stateBuilder.setLayeringState(NO_LAYERING); - //Default texture application is fine. - //stateBuilder.setTexturingState(DEFAULT_TEXTURING); - //Not sure what this does, but it should be fine as-is? I think this is for the Z-buffer. - //stateBuilder.setWriteMaskState(COLOR_DEPTH_WRITE); - //Don't need to poke lines, there's only one state and we don't change lines for non-line renders. - //stateBuilder.setLineState(DEFAULT_LINE); - - //Return. - return stateBuilder; - } - } - - @Override - public void deleteVertices(RenderableData data) { - if (data.vertexObject.cacheVertices) { - //Add to removed render list, we should only remove renders AFTER they are rendered. - //This ensures they are un-bound, if the were bound prior. - //Make sure we actually bound a buffer; just because the main system asks for a bound buffer, - //doesn't mean we actually can give it one. GUI models are one such case, as they don't work right - //with bound buffers due to matrix differences. - BufferData buffer = buffers.remove(data); - if (buffer != null) { - removedRenders.add(buffer); - } - } - } - - @Override - public int getLightingAtPosition(Point3D position) { - BlockPos pos = BlockPos.containing(position.x, position.y, position.z); - return LightTexture.pack(Minecraft.getInstance().level.getBrightness(LightLayer.BLOCK, pos), Minecraft.getInstance().level.getBrightness(LightLayer.SKY, pos)); - } - - @Override - public boolean shouldRenderBoundingBoxes() { - return Minecraft.getInstance().getEntityRenderDispatcher().shouldRenderHitBoxes(); - } - - @Override - public boolean bindURLTexture(String textureURL, InputStream stream) { - if (stream != null) { - try { - NativeImage image = NativeImage.read(NativeImage.Format.RGB, stream); - DynamicTexture texture = new DynamicTexture(image); - ResourceLocation textureLocation = Minecraft.getInstance().textureManager.register("mts-url", texture); - onlineTextures.put(textureURL, textureLocation); - return true; - } catch (Exception e) { - return false; - } - } else { - onlineTextures.put(textureURL, null); - return true; - } - } - - @Override - public boolean bindURLGIF(String textureURL, ParsedGIF gif) { - Map gifFrameIndexes = new HashMap<>(); - for (GIFImageFrame frame : gif.frames.values()) { - try { - BufferedImage frameBuffer = frame.getImage(); - ByteArrayOutputStream frameArrayStream = new ByteArrayOutputStream(); - ImageIO.write(frameBuffer, "gif", frameArrayStream); - InputStream frameStream = new ByteArrayInputStream(frameArrayStream.toByteArray()); - - NativeImage image = NativeImage.read(NativeImage.Format.RGB, frameStream); - DynamicTexture texture = new DynamicTexture(image); - ResourceLocation textureLocation = Minecraft.getInstance().textureManager.register("mts-gif", texture); - gifFrameIndexes.put(frame, textureLocation); - } catch (Exception e) { - return false; - } - } - animatedGIFs.put(textureURL, gif); - animatedGIFFrames.put(gif, gifFrameIndexes); - return true; - } - - /** - * Helper function to create a new texture state for the specified texture location. - */ - private static RenderStateShard.TextureStateShard getTexture(String textureLocation) { - //Check to make sure textures exist. We delay creating because some mods screw up this stuff in boot. Cray, looking at you buddy. - if (MISSING_STATE == null) { - MISSING_STATE = new RenderStateShard.TextureStateShard(new ResourceLocation("mts:textures/rendering/missing.png"), false, false); - BLOCK_STATE = new RenderStateShard.TextureStateShard(BLOCK_TEXTURE_LOCATION, false, false); - } - - if (animatedGIFs.containsKey(textureLocation)) { - //Special case for GIFs. - ParsedGIF parsedGIF = animatedGIFs.get(textureLocation); - return new RenderStateShard.TextureStateShard(animatedGIFFrames.get(parsedGIF).get(parsedGIF.getCurrentFrame()), false, false); - } else if (onlineTextures.containsKey(textureLocation)) { - //Online texture. - ResourceLocation onlineTexture = onlineTextures.get(textureLocation); - return onlineTexture != null ? new RenderStateShard.TextureStateShard(onlineTextures.get(textureLocation), false, false) : MISSING_STATE; - } else if (textureLocation.equals(RenderableData.GLOBAL_TEXTURE_NAME)) { - //Default texture. - return BLOCK_STATE; - } else { - //If the texture has a colon, it's a short-hand form that needs to be converted. - String formattedLocation = textureLocation; - if (textureLocation.contains(":")) { - formattedLocation = "/assets/" + textureLocation.replace(":", "/"); - } - - //Check if the texture exists. - if (InterfaceManager.coreInterface.getPackResource(formattedLocation) != null) { - //Convert the classpath-location to a domain-location path for MC. - String domain = formattedLocation.substring("/assets/".length(), formattedLocation.indexOf("/", "/assets/".length())); - String location = formattedLocation.substring("/assets/".length() + domain.length() + 1); - return new RenderStateShard.TextureStateShard(new ResourceLocation(domain, location), false, false); - } else { - InterfaceManager.coreInterface.logError("Could not find texture: " + formattedLocation + " Reverting to fallback texture."); - return MISSING_STATE; - } - } - } - - /** - * Renders the main GUI, setting up any transforms or operations as required. - */ - protected static void renderGUI(GuiGraphics mcGUI, int mouseX, int mouseY, int screenWidth, int screenHeight, float partialTicks, boolean updateGUIs) { - //Get the buffer for GUI rendering. - matrixStack = mcGUI.pose(); - matrixStack.pushPose(); - renderingGUI = true; - MultiBufferSource.BufferSource guiBuffer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); - renderBuffer = guiBuffer; - - //Render GUIs, re-creating their components if needed. - //Set Y-axis to inverted to have correct orientation. - matrixStack.scale(1.0F, -1.0F, 1.0F); - - //Render main pass, then blended pass. - int displayGUIIndex = 0; - for (AGUIBase gui : AGUIBase.activeGUIs) { - if (updateGUIs || gui.components.isEmpty()) { - gui.setupComponentsInit(screenWidth, screenHeight); - } - matrixStack.pushPose(); - if (gui.capturesPlayer()) { - //Translate in front of the main GUI components. - matrixStack.translate(0, 0, 250); - } else { - //Translate far enough to render behind the chat window. - matrixStack.translate(0, 0, -500 + 250 * displayGUIIndex++); - } - gui.render(mouseX, mouseY, false, partialTicks); - guiBuffer.endBatch(); - //Not needed, since we can't draw to custom buffers with GUIs. - //renderBuffers(); - - //Need to use RenderSystem here, since this is a direct buffer. - RenderSystem.enableBlend(); - gui.render(mouseX, mouseY, true, partialTicks); - guiBuffer.endBatch(); - //renderBuffers(); - RenderSystem.disableBlend(); - - //Render all stacks. These have to be in the standard GUI reference frame or they won't render. - matrixStack.scale(1.0F, -1.0F, 1.0F); - - for (GUIComponentItem component : stacksToRender) { - //Double-check the stack is still present, it might have been un-set since this call. - if ((WrapperItemStack) component.stackToRender != null) { - //Apply existing transform. - //Need to use RenderSystem here, since we can't access the stack directly for rendering scaling. - PoseStack posestack = RenderSystem.getModelViewStack(); - posestack.pushPose(); - //Need to translate the z-offset to our value, which includes a -100 for the default added value. - posestack.translate(0, 0, (float) (component.translation.z - 100)); - if (component.scale != 1.0) { - posestack.scale(component.scale, component.scale, 1.0F); - RenderSystem.applyModelViewMatrix(); - mcGUI.renderItem(((WrapperItemStack) component.stackToRender).stack, (int) (component.translation.x / component.scale), (int) (-component.translation.y / component.scale) + 1); - } else { - RenderSystem.applyModelViewMatrix(); - mcGUI.renderItem(((WrapperItemStack) component.stackToRender).stack, (int) component.translation.x, (int) -component.translation.y); - } - posestack.popPose(); - RenderSystem.applyModelViewMatrix(); - } - } - stacksToRender.clear(); - - matrixStack.popPose(); - } - matrixStack.popPose(); - renderingGUI = false; - } - - /** - * Converts internal matrix to new external matrix. - * Note that MC does multiplication operations backwards of us in this version of the game - * so this is actually the transpose of the passed-in matrix. - */ - public static Matrix4f convertMatrix4f(TransformationMatrix transform) { - return new Matrix4f((float) transform.m00, (float) transform.m10, (float) transform.m20, (float) transform.m30, (float) transform.m01, (float) transform.m11, (float) transform.m21, (float) transform.m31, (float) transform.m02, (float) transform.m12, (float) transform.m22, (float) transform.m32, (float) transform.m03, (float) transform.m13, (float) transform.m23, (float) transform.m33); - } - - - /** Blank render class used to bypass rendering for all other builders.**/ - private static class BlankRender extends EntityRenderer { - - protected BlankRender(EntityRendererProvider.Context p_i46179_1_) { - super(p_i46179_1_); - } - - @Override - public ResourceLocation getTextureLocation(T pEntity) { - return null; - } - - @Override - public boolean shouldRender(T builder, Frustum camera, double camX, double camY, double camZ) { - return false; - } - } - - private static class RenderData { - private final Matrix4f matrix; - private final BufferData bufferData; - - private RenderData(Matrix4f matrix, BufferData bufferData) { - this.matrix = new Matrix4f(matrix); - this.bufferData = bufferData; - } - } - - private static class BufferData { - final BufferBuilder builder; - final VertexBuffer buffer; - boolean isReady; - - private BufferData() { - builder = null; - buffer = null; - } - - private BufferData(RenderType type, RenderableData data) { - int vertices = data.vertexObject.vertices.limit() / 8; - //Convert verts to faces, then back to quad-verts for MC rendering. - //Add one face extra, since MC will want to increase the buffer if sees it can't handle another vert. - vertices = ((vertices / 3) + 1) * 3; - this.builder = new BufferBuilder(type.format().getIntegerSize() * vertices); - this.buffer = new VertexBuffer(VertexBuffer.Usage.STATIC); - } - } - - /** - * Proper translucent transparency. MC's one has the wrong blending function. - */ - private static final RenderStateShard.TransparencyStateShard PROPER_TRANSLUCENT_TRANSPARENCY = new RenderStateShard.TransparencyStateShard("proper_translucent_transparency", () -> { - RenderSystem.enableBlend(); - RenderSystem.depthMask(false); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - }, () -> { - RenderSystem.disableBlend(); - RenderSystem.depthMask(true); - RenderSystem.defaultBlendFunc(); - }); - - /** - * Brightness transparency. Does a special blending operation to make things behind - * the object brighter based on the object's alpha value. More alpha means more bright. - */ - private static final RenderStateShard.TransparencyStateShard BRIGHTNESS_TRANSPARENCY = new RenderStateShard.TransparencyStateShard("brightness_transparency", () -> { - RenderSystem.enableBlend(); - RenderSystem.depthMask(false); - RenderSystem.blendFunc(GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_ALPHA); - }, () -> { - RenderSystem.disableBlend(); - RenderSystem.depthMask(true); - RenderSystem.defaultBlendFunc(); - }); -} + } + //Rewind buffer for next read. + data.vertexObject.vertices.rewind(); + } else { + String typeID = data.texture + data.isTranslucent + data.lightingMode + data.enableBrightBlending; + final RenderType renderType; + if (data.vertexObject.cacheVertices && !renderingGUI && ConfigSystem.client.renderingSettings.renderingMode.value != 2) { + //Get the render type and data buffer for this entity. + renderType = renderTypes.computeIfAbsent(typeID, k -> CustomRenderType.create("mts_entity", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.TRIANGLES, 2097152, true, data.isTranslucent, CustomRenderType.createForObject(data).createCompositeState(false))); + BufferData bufferData = buffers.computeIfAbsent(data, k -> new BufferData(renderType, data)); + + //Reset buffer if it's not ready. + if (changedSinceLastRender) { + bufferData.builder.clear(); + bufferData.isReady = false; + } + if (!bufferData.isReady) { + bufferData.builder.begin(VertexFormat.Mode.TRIANGLES, renderType.format()); + while (data.vertexObject.vertices.hasRemaining()) { + //Need to parse these out first since our order differs. + float normalX = data.vertexObject.vertices.get(); + float normalY = data.vertexObject.vertices.get(); + float normalZ = data.vertexObject.vertices.get(); + float texU = data.vertexObject.vertices.get(); + float texV = data.vertexObject.vertices.get(); + float posX = data.vertexObject.vertices.get(); + float posY = data.vertexObject.vertices.get(); + float posZ = data.vertexObject.vertices.get(); + bufferData.builder.vertex(posX, posY, posZ, data.color.red, data.color.green, data.color.blue, data.alpha, texU, texV, OverlayTexture.NO_OVERLAY, data.worldLightValue, normalX, normalY, normalZ); + } + bufferData.isReady = true; + bufferData.buffer.bind(); + bufferData.buffer.upload(bufferData.builder.end()); + data.vertexObject.vertices.rewind(); + VertexBuffer.unbind(); + } + + //Add this buffer to the list to render later. + List renders = queuedRenders.get(renderType); + if (renders == null) { + renders = new ArrayList<>(); + queuedRenders.put(renderType, renders); + } + renders.add(new RenderData(stackEntry.pose(), bufferData)); + } else { + stackEntry.normal().mul(matrix3f); + renderType = renderTypes.computeIfAbsent(typeID, k -> CustomRenderType.create("mts_entity_" + typeID, DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.TRIANGLES, 256, true, data.isTranslucent, CustomRenderType.createForObject(data).createCompositeState(false))); + VertexConsumer buffer = renderBuffer.getBuffer(renderType); + //System.out.println(renderType); + while (data.vertexObject.vertices.hasRemaining()) { + //Need to parse these out first since our order differs. + float normalX = data.vertexObject.vertices.get(); + float normalY = data.vertexObject.vertices.get(); + float normalZ = data.vertexObject.vertices.get(); + float texU = data.vertexObject.vertices.get(); + float texV = data.vertexObject.vertices.get(); + float posX = data.vertexObject.vertices.get(); + float posY = data.vertexObject.vertices.get(); + float posZ = data.vertexObject.vertices.get(); + + //Add the vertex. Yes, we have to multiply this here on the CPU. Yes, it's retarded because the GPU should be doing the matrix math. + //Blaze3d my ass, this is SLOWER than DisplayLists! + buffer.vertex(stackEntry.pose(), posX, posY, posZ); + buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); + buffer.uv(texU, texV); + buffer.overlayCoords(OverlayTexture.NO_OVERLAY); + buffer.uv2(data.worldLightValue); + buffer.normal(stackEntry.normal(), normalX, normalY, normalZ); + buffer.endVertex(); + } + //Rewind buffer for next read. + data.vertexObject.vertices.rewind(); + } + } + matrixStack.popPose(); + } + + public static void doRenderCall(boolean blendingEnabled, float partialTicks) { + AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); + ConcurrentLinkedQueue allEntities = world.renderableEntities; + if (allEntities != null) { + world.beginProfiling("MTSRendering_Setup", true); + + //NOTE: this operation occurs on a ConcurrentLinkedQueue. Therefore, updates will + //not occur one after another. Sanitize your inputs! + for (AEntityC_Renderable entity : allEntities) { + matrixStack.pushPose(); + matrixStack.translate(entity.position.x - renderCameraOffset.x, entity.position.y - renderCameraOffset.y, entity.position.z - renderCameraOffset.z); + entity.render(blendingEnabled, partialTicks); + matrixStack.popPose(); + } + + //Now do the actual render. + if (!queuedRenders.isEmpty()) { + world.beginProfiling("MTSRendering_Batch", false); + renderBuffers(partialTicks); + world.endProfiling(); + } + } + } + + private static void renderBuffers(float partialTicks) { + //Call order is CRITICAL and will lead to random JME faults with no stacktrace if modified! + for (Entry> renderEntry : queuedRenders.entrySet()) { + RenderType renderType = renderEntry.getKey(); + List datas = renderEntry.getValue(); + if (!datas.isEmpty() && ConfigSystem.client.renderingSettings.renderingMode.value == 0) { + //Setup common render states. + renderType.setupRenderState(); + + //Shader is set now, get it and set values. + ShaderInstance shaderInstance = RenderSystem.getShader(); + for (int k = 0; k < 12; ++k) { + int i = RenderSystem.getShaderTexture(k); + shaderInstance.setSampler("Sampler" + k, i); + } + if (shaderInstance.PROJECTION_MATRIX != null) { + shaderInstance.PROJECTION_MATRIX.set(projectionMatrix); + } + if (shaderInstance.INVERSE_VIEW_ROTATION_MATRIX != null) { + shaderInstance.INVERSE_VIEW_ROTATION_MATRIX.set(RenderSystem.getInverseViewRotationMatrix()); + } + if (shaderInstance.COLOR_MODULATOR != null) { + shaderInstance.COLOR_MODULATOR.set(RenderSystem.getShaderColor()); + } + if (shaderInstance.FOG_START != null) { + shaderInstance.FOG_START.set(RenderSystem.getShaderFogStart()); + } + if (shaderInstance.FOG_END != null) { + shaderInstance.FOG_END.set(RenderSystem.getShaderFogEnd()); + } + if (shaderInstance.FOG_COLOR != null) { + shaderInstance.FOG_COLOR.set(RenderSystem.getShaderFogColor()); + } + if (shaderInstance.FOG_SHAPE != null) { + shaderInstance.FOG_SHAPE.set(RenderSystem.getShaderFogShape().getIndex()); + } + RenderSystem.setupShaderLights(shaderInstance); + + //Now render vertices, setting only the states required each render. + for (RenderData data : datas) { + if (shaderInstance.MODEL_VIEW_MATRIX != null) { + shaderInstance.MODEL_VIEW_MATRIX.set(data.matrix); + } + shaderInstance.apply(); + data.bufferData.buffer.bind(); + data.bufferData.buffer.draw(); + } + VertexBuffer.unbind(); + shaderInstance.clear(); + renderType.clearRenderState(); + datas.clear(); + } + + if (!datas.isEmpty()) { + renderType.setupRenderState(); + for (RenderData data : datas) { + data.bufferData.buffer.bind(); + data.bufferData.buffer.drawWithShader(data.matrix, projectionMatrix, RenderSystem.getShader()); + } + VertexBuffer.unbind(); + renderType.clearRenderState(); + datas.clear(); + } + } + + if (!removedRenders.isEmpty()) { + removedRenders.forEach(render -> render.buffer.close()); + removedRenders.clear(); + } + } + + private static class CustomRenderType extends RenderType { + private CustomRenderType(String name, VertexFormat fmt, Mode glMode, int size, boolean doCrumbling, boolean depthSorting, Runnable onEnable, Runnable onDisable) { + super(name, fmt, glMode, size, doCrumbling, depthSorting, onEnable, onDisable); + throw new IllegalStateException("This class must not be instantiated, this is only here to gain access to the rendering constants."); + } + + private static RenderType.CompositeState.CompositeStateBuilder createForObject(RenderableData data) { + //Create the state builder. Changed states are active, default states are commented to save processing but still show the state. + RenderType.CompositeState.CompositeStateBuilder stateBuilder = RenderType.CompositeState.builder(); + + //Set shader to use. + if (data.lightingMode.disableTextureShadows) { + //This shouldn't use OpenGL lighting, use shader that ignores this. + if (data.lightingMode.disableWorldLighting) { + stateBuilder.setShaderState(MTS_ENTITY_LIGHTS_SHADER); + } else { + stateBuilder.setShaderState(MTS_ENTITY_CUTOUT_NOSHADOWS_SHADER); + } + } else if (data.isTranslucent) { + stateBuilder.setShaderState(RENDERTYPE_ENTITY_SOLID_SHADER); + } else { + stateBuilder.setShaderState(RENDERTYPE_ENTITY_CUTOUT_SHADER); + } + + //Set transparency. + if (data.enableBrightBlending) { + stateBuilder.setTransparencyState(BRIGHTNESS_TRANSPARENCY); + } else if (data.isTranslucent) { + stateBuilder.setTransparencyState(PROPER_TRANSLUCENT_TRANSPARENCY); + } else { + stateBuilder.setTransparencyState(NO_TRANSPARENCY); + } + + //Texture is just based on what is specified. + stateBuilder.setTextureState(data.texture != null ? getTexture(data.texture) : NO_TEXTURE); + //Always need lightmaps for entities per their vertex format. + stateBuilder.setLightmapState(LIGHTMAP); + //Entities go to the main target. + stateBuilder.setOutputState(MAIN_TARGET); + //All entity shaders require overlays, so we use those here. + stateBuilder.setOverlayState(OVERLAY); + //Culling is required for textured surfaces since they have normals and this saves FPS. + stateBuilder.setCullState(CULL); + //Depth test is fine, it ensures translucent things don't render in front of everything. + //stateBuilder.setDepthTestState(LEQUAL_DEPTH_TEST); + //No layering is fine. + //stateBuilder.setLayeringState(NO_LAYERING); + //Default texture application is fine. + //stateBuilder.setTexturingState(DEFAULT_TEXTURING); + //Not sure what this does, but it should be fine as-is? I think this is for the Z-buffer. + //stateBuilder.setWriteMaskState(COLOR_DEPTH_WRITE); + //Don't need to poke lines, there's only one state and we don't change lines for non-line renders. + //stateBuilder.setLineState(DEFAULT_LINE); + + //Return. + return stateBuilder; + } + } + + @Override + public void deleteVertices(RenderableData data) { + if (data.vertexObject.cacheVertices) { + //Add to removed render list, we should only remove renders AFTER they are rendered. + //This ensures they are un-bound, if the were bound prior. + //Make sure we actually bound a buffer; just because the main system asks for a bound buffer, + //doesn't mean we actually can give it one. GUI models are one such case, as they don't work right + //with bound buffers due to matrix differences. + BufferData buffer = buffers.remove(data); + if (buffer != null) { + removedRenders.add(buffer); + } + } + } + + @Override + public int getLightingAtPosition(Point3D position) { + BlockPos pos = BlockPos.containing(position.x, position.y, position.z); + return LightTexture.pack(Minecraft.getInstance().level.getBrightness(LightLayer.BLOCK, pos), Minecraft.getInstance().level.getBrightness(LightLayer.SKY, pos)); + } + + @Override + public boolean shouldRenderBoundingBoxes() { + return Minecraft.getInstance().getEntityRenderDispatcher().shouldRenderHitBoxes(); + } + + @Override + public boolean bindURLTexture(String textureURL, InputStream stream) { + if (stream != null) { + try { + NativeImage image = NativeImage.read(NativeImage.Format.RGB, stream); + DynamicTexture texture = new DynamicTexture(image); + ResourceLocation textureLocation = Minecraft.getInstance().textureManager.register("mts-url", texture); + onlineTextures.put(textureURL, textureLocation); + return true; + } catch (Exception e) { + return false; + } + } else { + onlineTextures.put(textureURL, null); + return true; + } + } + + @Override + public boolean bindURLGIF(String textureURL, ParsedGIF gif) { + Map gifFrameIndexes = new HashMap<>(); + for (GIFImageFrame frame : gif.frames.values()) { + try { + BufferedImage frameBuffer = frame.getImage(); + ByteArrayOutputStream frameArrayStream = new ByteArrayOutputStream(); + ImageIO.write(frameBuffer, "gif", frameArrayStream); + InputStream frameStream = new ByteArrayInputStream(frameArrayStream.toByteArray()); + + NativeImage image = NativeImage.read(NativeImage.Format.RGB, frameStream); + DynamicTexture texture = new DynamicTexture(image); + ResourceLocation textureLocation = Minecraft.getInstance().textureManager.register("mts-gif", texture); + gifFrameIndexes.put(frame, textureLocation); + } catch (Exception e) { + return false; + } + } + animatedGIFs.put(textureURL, gif); + animatedGIFFrames.put(gif, gifFrameIndexes); + return true; + } + + /** + * Helper function to create a new texture state for the specified texture location. + */ + private static RenderStateShard.TextureStateShard getTexture(String textureLocation) { + //Check to make sure textures exist. We delay creating because some mods screw up this stuff in boot. Cray, looking at you buddy. + if (MISSING_STATE == null) { + MISSING_STATE = new RenderStateShard.TextureStateShard(new ResourceLocation("mts:textures/rendering/missing.png"), false, false); + BLOCK_STATE = new RenderStateShard.TextureStateShard(BLOCK_TEXTURE_LOCATION, false, false); + } + + if (animatedGIFs.containsKey(textureLocation)) { + //Special case for GIFs. + ParsedGIF parsedGIF = animatedGIFs.get(textureLocation); + return new RenderStateShard.TextureStateShard(animatedGIFFrames.get(parsedGIF).get(parsedGIF.getCurrentFrame()), false, false); + } else if (onlineTextures.containsKey(textureLocation)) { + //Online texture. + ResourceLocation onlineTexture = onlineTextures.get(textureLocation); + return onlineTexture != null ? new RenderStateShard.TextureStateShard(onlineTextures.get(textureLocation), false, false) : MISSING_STATE; + } else if (textureLocation.equals(RenderableData.GLOBAL_TEXTURE_NAME)) { + //Default texture. + return BLOCK_STATE; + } else { + //If the texture has a colon, it's a short-hand form that needs to be converted. + String formattedLocation = textureLocation; + if (textureLocation.contains(":")) { + formattedLocation = "/assets/" + textureLocation.replace(":", "/"); + } + + //Check if the texture exists. + if (InterfaceManager.coreInterface.getPackResource(formattedLocation) != null) { + //Convert the classpath-location to a domain-location path for MC. + String domain = formattedLocation.substring("/assets/".length(), formattedLocation.indexOf("/", "/assets/".length())); + String location = formattedLocation.substring("/assets/".length() + domain.length() + 1); + return new RenderStateShard.TextureStateShard(new ResourceLocation(domain, location), false, false); + } else { + InterfaceManager.coreInterface.logError("Could not find texture: " + formattedLocation + " Reverting to fallback texture."); + return MISSING_STATE; + } + } + } + + /** + * Renders the main GUI, setting up any transforms or operations as required. + */ + protected static void renderGUI(GuiGraphics mcGUI, int mouseX, int mouseY, int screenWidth, int screenHeight, float partialTicks, boolean updateGUIs) { + //Get the buffer for GUI rendering. + matrixStack = mcGUI.pose(); + matrixStack.pushPose(); + renderingGUI = true; + MultiBufferSource.BufferSource guiBuffer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); + renderBuffer = guiBuffer; + + //Render GUIs, re-creating their components if needed. + //Set Y-axis to inverted to have correct orientation. + matrixStack.scale(1.0F, -1.0F, 1.0F); + + //Render main pass, then blended pass. + int displayGUIIndex = 0; + for (AGUIBase gui : AGUIBase.activeGUIs) { + if (updateGUIs || gui.components.isEmpty()) { + gui.setupComponentsInit(screenWidth, screenHeight); + } + matrixStack.pushPose(); + if (gui.capturesPlayer()) { + //Translate in front of the main GUI components. + matrixStack.translate(0, 0, 250); + } else { + //Translate far enough to render behind the chat window. + matrixStack.translate(0, 0, -500 + 250 * displayGUIIndex++); + } + gui.render(mouseX, mouseY, false, partialTicks); + guiBuffer.endBatch(); + //Not needed, since we can't draw to custom buffers with GUIs. + //renderBuffers(); + + //Need to use RenderSystem here, since this is a direct buffer. + RenderSystem.enableBlend(); + gui.render(mouseX, mouseY, true, partialTicks); + guiBuffer.endBatch(); + //renderBuffers(); + RenderSystem.disableBlend(); + + //Render all stacks. These have to be in the standard GUI reference frame or they won't render. + matrixStack.scale(1.0F, -1.0F, 1.0F); + + for (GUIComponentItem component : stacksToRender) { + //Double-check the stack is still present, it might have been un-set since this call. + if ((WrapperItemStack) component.stackToRender != null) { + //Apply existing transform. + //Need to use RenderSystem here, since we can't access the stack directly for rendering scaling. + PoseStack posestack = RenderSystem.getModelViewStack(); + posestack.pushPose(); + //Need to translate the z-offset to our value, which includes a -100 for the default added value. + posestack.translate(0, 0, (float) (component.translation.z - 100)); + if (component.scale != 1.0) { + posestack.scale(component.scale, component.scale, 1.0F); + RenderSystem.applyModelViewMatrix(); + mcGUI.renderItem(((WrapperItemStack) component.stackToRender).stack, (int) (component.translation.x / component.scale), (int) (-component.translation.y / component.scale) + 1); + } else { + RenderSystem.applyModelViewMatrix(); + mcGUI.renderItem(((WrapperItemStack) component.stackToRender).stack, (int) component.translation.x, (int) -component.translation.y); + } + posestack.popPose(); + RenderSystem.applyModelViewMatrix(); + } + } + stacksToRender.clear(); + + matrixStack.popPose(); + } + matrixStack.popPose(); + renderingGUI = false; + } + + /** + * Converts internal matrix to new external matrix. + * Note that MC does multiplication operations backwards of us in this version of the game + * so this is actually the transpose of the passed-in matrix. + */ + public static Matrix4f convertMatrix4f(TransformationMatrix transform) { + return new Matrix4f((float) transform.m00, (float) transform.m10, (float) transform.m20, (float) transform.m30, (float) transform.m01, (float) transform.m11, (float) transform.m21, (float) transform.m31, (float) transform.m02, (float) transform.m12, (float) transform.m22, (float) transform.m32, (float) transform.m03, (float) transform.m13, (float) transform.m23, (float) transform.m33); + } + + + /** Blank render class used to bypass rendering for all other builders.**/ + private static class BlankRender extends EntityRenderer { + + protected BlankRender(EntityRendererProvider.Context p_i46179_1_) { + super(p_i46179_1_); + } + + @Override + public ResourceLocation getTextureLocation(T pEntity) { + return null; + } + + @Override + public boolean shouldRender(T builder, Frustum camera, double camX, double camY, double camZ) { + return false; + } + } + + private static class RenderData { + private final Matrix4f matrix; + private final BufferData bufferData; + + private RenderData(Matrix4f matrix, BufferData bufferData) { + this.matrix = new Matrix4f(matrix); + this.bufferData = bufferData; + } + } + + private static class BufferData { + final BufferBuilder builder; + final VertexBuffer buffer; + boolean isReady; + + private BufferData() { + builder = null; + buffer = null; + } + + private BufferData(RenderType type, RenderableData data) { + int vertices = data.vertexObject.vertices.limit() / 8; + //Convert verts to faces, then back to quad-verts for MC rendering. + //Add one face extra, since MC will want to increase the buffer if sees it can't handle another vert. + vertices = ((vertices / 3) + 1) * 3; + this.builder = new BufferBuilder(type.format().getIntegerSize() * vertices); + this.buffer = new VertexBuffer(VertexBuffer.Usage.STATIC); + } + } + + /** + * Proper translucent transparency. MC's one has the wrong blending function. + */ + private static final RenderStateShard.TransparencyStateShard PROPER_TRANSLUCENT_TRANSPARENCY = new RenderStateShard.TransparencyStateShard("proper_translucent_transparency", () -> { + RenderSystem.enableBlend(); + RenderSystem.depthMask(false); + RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + }, () -> { + RenderSystem.disableBlend(); + RenderSystem.depthMask(true); + RenderSystem.defaultBlendFunc(); + }); + + /** + * Brightness transparency. Does a special blending operation to make things behind + * the object brighter based on the object's alpha value. More alpha means more bright. + */ + private static final RenderStateShard.TransparencyStateShard BRIGHTNESS_TRANSPARENCY = new RenderStateShard.TransparencyStateShard("brightness_transparency", () -> { + RenderSystem.enableBlend(); + RenderSystem.depthMask(false); + RenderSystem.blendFunc(GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_ALPHA); + }, () -> { + RenderSystem.disableBlend(); + RenderSystem.depthMask(true); + RenderSystem.defaultBlendFunc(); + }); +}