From 816397d4cde6e621a478e5b40173bfe4bb656dd3 Mon Sep 17 00:00:00 2001 From: dldev32 Date: Thu, 9 Apr 2026 19:03:04 +0300 Subject: [PATCH 1/8] Minor rework of bullets in the mod --- .../components/AEntityF_Multipart.java | 110 +++++++++++++++++- .../entities/instances/EntityBullet.java | 3 +- .../jsondefs/JSONBullet.java | 24 +++- 3 files changed, 132 insertions(+), 5 deletions(-) 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 890b10653..bd805db76 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; @@ -357,9 +359,18 @@ 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); - bullet.armorPenetrated += armorThickness; + boolean bulletIsHeat = bullet.definition.bullet.types.contains(BulletType.HEAT) || bullet.definition.bullet.isHeat; + 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. + if (!bulletIsHeat && armorThickness > 0 && penetrationPotential > 0) { + double ratio = armorThickness / penetrationPotential; + double penetrationLoss = armorThickness * (1.0 + ratio); + bullet.armorPenetrated += penetrationLoss; + } else { + bullet.armorPenetrated += armorThickness; + } bullet.displayDebugMessage("HIT ARMOR OF: " + (int) armorThickness); if (bullet.armorPenetrated > penetrationPotential) { @@ -373,6 +384,99 @@ public EntityBullet.HitType attackProjectile(Damage damage, EntityBullet bullet, 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) { + 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 = hitEntry.position.distanceTo(part.position); + if (distToPart <= coneRange) { + Point3D toPartVector = part.position.copy().subtract(hitEntry.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) { + 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 = hitEntry.position.distanceTo(partRider.getPosition()); + if (distToRider <= coneRange) { + Point3D toRiderVector = partRider.getPosition().copy().subtract(hitEntry.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) { + 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); + } + } + } + } + } + } + } + } + + //HEAT shells detonate on armor contact and do not continue flying. + //If penetration was successful, apply post-penetration damage to the vehicle and crew + //proportional to the residual penetration potential. + if (bulletIsHeat) { + double residualRatio = (penetrationPotential - bullet.armorPenetrated) / penetrationPotential; + if (residualRatio > 0) { + double postPenDamage = bullet.definition.bullet.damage * residualRatio; + Damage heatDamage = new Damage(bullet.gun, hitEntry.box, postPenDamage); + if (world.isClient()) { + InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitEntity(bullet.gun, hitEntity, heatDamage)); + for (APart part : allParts) { + if (part.rider != null) { + InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitExternalEntity(part.rider, heatDamage)); + } + } + } else { + EntityBullet.performEntityHitLogic(hitEntity, heatDamage); + for (APart part : allParts) { + if (part.rider != null) { + EntityBullet.performExternalEntityHitLogic(part.rider, heatDamage); + } + } + } + bullet.displayDebugMessage("HEAT POST-PEN DAMAGE: " + (int) postPenDamage); + } + + if (world.isClient()) { + InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitGeneric(bullet.gun, bullet.bulletNumber, hitEntry.position, hitEntry.side, HitType.ARMOR)); + bullet.waitingOnActionPacket = true; + } else { + EntityBullet.performGenericHitLogic(bullet.gun, bullet.bulletNumber, hitEntry.position, hitEntry.side, HitType.ARMOR); + } + return EntityBullet.HitType.ARMOR; + } } else { //Not a bullet, but hit armor, 100% stopping power with no damage. return EntityBullet.HitType.ARMOR; diff --git a/mccore/src/main/java/minecrafttransportsimulator/entities/instances/EntityBullet.java b/mccore/src/main/java/minecrafttransportsimulator/entities/instances/EntityBullet.java index b4060595b..7678a5d7e 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/entities/instances/EntityBullet.java +++ b/mccore/src/main/java/minecrafttransportsimulator/entities/instances/EntityBullet.java @@ -314,7 +314,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) || definition.bullet.isHeat; + 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. diff --git a/mccore/src/main/java/minecrafttransportsimulator/jsondefs/JSONBullet.java b/mccore/src/main/java/minecrafttransportsimulator/jsondefs/JSONBullet.java index 889d5a7d7..263f267fb 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/jsondefs/JSONBullet.java +++ b/mccore/src/main/java/minecrafttransportsimulator/jsondefs/JSONBullet.java @@ -29,9 +29,21 @@ 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.") + @Deprecated 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; @@ -120,6 +132,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 } From ce94bef496845533538bfbab592deea8590b6c12 Mon Sep 17 00:00:00 2001 From: dldev32 Date: Sun, 12 Apr 2026 23:04:22 +0300 Subject: [PATCH 2/8] Fixed bullet position when hitting entities/vehicles --- .../components/AEntityF_Multipart.java | 20 +++++++++---------- .../entities/instances/EntityBullet.java | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) 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 bd805db76..329ef7b29 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/entities/components/AEntityF_Multipart.java +++ b/mccore/src/main/java/minecrafttransportsimulator/entities/components/AEntityF_Multipart.java @@ -376,10 +376,10 @@ public EntityBullet.HitType attackProjectile(Damage damage, EntityBullet bullet, 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, 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; @@ -395,9 +395,9 @@ public EntityBullet.HitType attackProjectile(Damage damage, EntityBullet bullet, //Check all parts on this entity for fragmentation hits within the cone. for (APart part : allParts) { - double distToPart = hitEntry.position.distanceTo(part.position); + double distToPart = bullet.position.distanceTo(part.position); if (distToPart <= coneRange) { - Point3D toPartVector = part.position.copy().subtract(hitEntry.position); + 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())); @@ -420,9 +420,9 @@ public EntityBullet.HitType attackProjectile(Damage damage, EntityBullet bullet, for (APart part : allParts) { if (part.rider != null) { IWrapperEntity partRider = part.rider; - double distToRider = hitEntry.position.distanceTo(partRider.getPosition()); + double distToRider = bullet.position.distanceTo(partRider.getPosition()); if (distToRider <= coneRange) { - Point3D toRiderVector = partRider.getPosition().copy().subtract(hitEntry.position); + 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())); @@ -470,10 +470,10 @@ public EntityBullet.HitType attackProjectile(Damage damage, EntityBullet bullet, } 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, hitEntry.position, hitEntry.side, HitType.ARMOR); + EntityBullet.performGenericHitLogic(bullet.gun, bullet.bulletNumber, bullet.position, hitEntry.side, HitType.ARMOR); } return EntityBullet.HitType.ARMOR; } @@ -507,14 +507,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 7678a5d7e..4aed8feb4 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/entities/instances/EntityBullet.java +++ b/mccore/src/main/java/minecrafttransportsimulator/entities/instances/EntityBullet.java @@ -417,11 +417,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; From 758ada2abf48f543d135b4a53dc0f39eefa377c0 Mon Sep 17 00:00:00 2001 From: dldev32 Date: Mon, 13 Apr 2026 19:47:54 +0300 Subject: [PATCH 3/8] Fixed a couple of bugs and expanded bullet debugging --- .../components/AEntityF_Multipart.java | 47 ++++++------------- 1 file changed, 14 insertions(+), 33 deletions(-) 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 329ef7b29..893387a93 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/entities/components/AEntityF_Multipart.java +++ b/mccore/src/main/java/minecrafttransportsimulator/entities/components/AEntityF_Multipart.java @@ -364,14 +364,9 @@ public EntityBullet.HitType attackProjectile(Damage damage, EntityBullet bullet, 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. - if (!bulletIsHeat && armorThickness > 0 && penetrationPotential > 0) { - double ratio = armorThickness / penetrationPotential; - double penetrationLoss = armorThickness * (1.0 + ratio); - bullet.armorPenetrated += penetrationLoss; - } else { - bullet.armorPenetrated += armorThickness; - } - bullet.displayDebugMessage("HIT ARMOR OF: " + (int) armorThickness); + //Heat projectiles use their base penetration directly without scaling. + bullet.armorPenetrated += 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. @@ -381,12 +376,18 @@ public EntityBullet.HitType attackProjectile(Damage damage, EntityBullet bullet, } else { EntityBullet.performGenericHitLogic(bullet.gun, bullet.bulletNumber, bullet.position, hitEntry.side, HitType.ARMOR); } - bullet.displayDebugMessage("HIT TOO MUCH ARMOR. MAX PEN: " + (int) penetrationPotential); + 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; @@ -403,6 +404,7 @@ public EntityBullet.HitType attackProjectile(Damage damage, EntityBullet bullet, 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()) { @@ -428,6 +430,7 @@ public EntityBullet.HitType attackProjectile(Damage damage, EntityBullet bullet, 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()) { @@ -441,34 +444,12 @@ public EntityBullet.HitType attackProjectile(Damage damage, EntityBullet bullet, } } } + bullet.displayDebugMessage("FRAG HIT " + fragPartsHit + " PARTS AND " + fragEntitiesHit + " ENTITIES"); } //HEAT shells detonate on armor contact and do not continue flying. - //If penetration was successful, apply post-penetration damage to the vehicle and crew - //proportional to the residual penetration potential. if (bulletIsHeat) { - double residualRatio = (penetrationPotential - bullet.armorPenetrated) / penetrationPotential; - if (residualRatio > 0) { - double postPenDamage = bullet.definition.bullet.damage * residualRatio; - Damage heatDamage = new Damage(bullet.gun, hitEntry.box, postPenDamage); - if (world.isClient()) { - InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitEntity(bullet.gun, hitEntity, heatDamage)); - for (APart part : allParts) { - if (part.rider != null) { - InterfaceManager.packetInterface.sendToServer(new PacketEntityBulletHitExternalEntity(part.rider, heatDamage)); - } - } - } else { - EntityBullet.performEntityHitLogic(hitEntity, heatDamage); - for (APart part : allParts) { - if (part.rider != null) { - EntityBullet.performExternalEntityHitLogic(part.rider, heatDamage); - } - } - } - bullet.displayDebugMessage("HEAT POST-PEN DAMAGE: " + (int) postPenDamage); - } - + 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; From 4d09556439458d647fa8138aa29afe2e853e56cb Mon Sep 17 00:00:00 2001 From: dldev32 Date: Fri, 17 Apr 2026 22:17:38 +0300 Subject: [PATCH 4/8] Added a debug panel above the vehicle, which shows how much HP the vehicle and parts have+ --- .../components/GUIComponentVehicleDebug.java | 207 ++++++++++++++++++ .../guis/instances/GUIOverlay.java | 2 + .../mcinterface/IInterfaceClient.java | 36 +-- .../mcinterface/IInterfaceRender.java | 7 + .../java/mcinterface1122/InterfaceClient.java | 93 ++++++-- .../InterfaceEventsEntityRendering.java | 7 + .../java/mcinterface1122/InterfaceRender.java | 19 ++ .../java/mcinterface1165/InterfaceClient.java | 94 ++++++-- .../java/mcinterface1165/InterfaceRender.java | 18 ++ .../java/mcinterface1182/InterfaceClient.java | 94 ++++++-- .../java/mcinterface1182/InterfaceRender.java | 89 +++++--- .../java/mcinterface1192/InterfaceClient.java | 84 +++++-- .../java/mcinterface1192/InterfaceRender.java | 89 +++++--- .../java/mcinterface1201/InterfaceClient.java | 84 +++++-- .../java/mcinterface1201/InterfaceRender.java | 37 +++- .../java/mcinterface1211/InterfaceClient.java | 58 +++++ .../java/mcinterface1211/InterfaceRender.java | 19 ++ 17 files changed, 869 insertions(+), 168 deletions(-) create mode 100644 mccore/src/main/java/minecrafttransportsimulator/guis/components/GUIComponentVehicleDebug.java diff --git a/mccore/src/main/java/minecrafttransportsimulator/guis/components/GUIComponentVehicleDebug.java b/mccore/src/main/java/minecrafttransportsimulator/guis/components/GUIComponentVehicleDebug.java new file mode 100644 index 000000000..42158015a --- /dev/null +++ b/mccore/src/main/java/minecrafttransportsimulator/guis/components/GUIComponentVehicleDebug.java @@ -0,0 +1,207 @@ +package minecrafttransportsimulator.guis.components; + +import java.util.ArrayList; +import java.util.List; + +import minecrafttransportsimulator.baseclasses.ColorRGB; +import minecrafttransportsimulator.baseclasses.Point3D; +import minecrafttransportsimulator.entities.instances.APart; +import minecrafttransportsimulator.entities.instances.EntityVehicleF_Physics; +import minecrafttransportsimulator.mcinterface.AWrapperWorld; +import minecrafttransportsimulator.mcinterface.IWrapperPlayer; +import minecrafttransportsimulator.mcinterface.InterfaceManager; +import minecrafttransportsimulator.rendering.RenderText.TextAlignment; +import minecrafttransportsimulator.rendering.RenderableData; +import minecrafttransportsimulator.rendering.RenderableData.LightingMode; +import minecrafttransportsimulator.rendering.RenderableVertices; +import minecrafttransportsimulator.systems.ConfigSystem; + +public class GUIComponentVehicleDebug extends AGUIComponent { + public static float panelScale = 0.5F; + + private static final int BASE_PANEL_WIDTH = 400; + private static final int BASE_BAR_WIDTH = 150; + private static final int BASE_BAR_HEIGHT = 12; + private static final int BASE_MARGIN = 8; + private static final double MAX_DISTANCE = 10.0; + + private final RenderableData bgRenderable; + private final RenderableData barBgRenderable; + private final RenderableData barFgRenderable; + + public GUIComponentVehicleDebug() { + super(0, 0, 0, 0); + + // Use 3 quads for "slightly rounded" corners (top, middle, bottom) + RenderableVertices bgVertices = RenderableVertices.createSprite(3, null, null); + bgRenderable = new RenderableData(bgVertices, AGUIBase.STANDARD_TEXTURE_NAME); + bgRenderable.setColor(ColorRGB.BLACK); + bgRenderable.setAlpha(0.6F); + bgRenderable.setLightMode(LightingMode.IGNORE_ALL_LIGHTING); + bgRenderable.isTranslucent = true; + + RenderableVertices barBgVertices = RenderableVertices.createSprite(3, null, null); + barBgRenderable = new RenderableData(barBgVertices, AGUIBase.STANDARD_TEXTURE_NAME); + barBgRenderable.setColor(ColorRGB.GRAY); + barBgRenderable.setAlpha(0.8F); + barBgRenderable.setLightMode(LightingMode.IGNORE_ALL_LIGHTING); + barBgRenderable.isTranslucent = true; + + RenderableVertices barFgVertices = RenderableVertices.createSprite(3, null, null); + barFgRenderable = new RenderableData(barFgVertices, AGUIBase.STANDARD_TEXTURE_NAME); + barFgRenderable.setColor(ColorRGB.GREEN); + barFgRenderable.setAlpha(0.9F); + barFgRenderable.setLightMode(LightingMode.IGNORE_ALL_LIGHTING); + barFgRenderable.isTranslucent = true; + } + + @Override + public void render(AGUIBase gui, int mouseX, int mouseY, boolean renderBright, boolean renderLitTexture, boolean blendingEnabled, float partialTicks) { + if (!ConfigSystem.settings.general.devMode.value || !ConfigSystem.client.renderingSettings.vehicleDebugPanel.value) { + return; + } + + if (blendingEnabled) { + return; + } + + AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); + if (world == null) return; + + IWrapperPlayer player = InterfaceManager.clientInterface.getClientPlayer(); + if (player == null) return; + + int panelWidth = (int) (BASE_PANEL_WIDTH * panelScale); + int barWidth = (int) (BASE_BAR_WIDTH * panelScale); + int barHeight = (int) (BASE_BAR_HEIGHT * panelScale); + int margin = (int) (BASE_MARGIN * panelScale); + int bgRadius = (int) (4 * panelScale); + int barRadius = (int) (2 * panelScale); + + if (bgRadius < 1) bgRadius = 1; + if (barRadius < 1) barRadius = 1; + float textScale = 0.8F * panelScale; + float titleScale = 1.0F * panelScale; + float percentScale = 0.7F * panelScale; + + for (EntityVehicleF_Physics vehicle : world.getEntitiesExtendingType(EntityVehicleF_Physics.class)) { + if (vehicle.position.distanceTo(player.getPosition()) > MAX_DISTANCE) { + continue; + } + + Point3D topPos = vehicle.position.copy(); + topPos.y += vehicle.encompassingBox.heightRadius + 2.5; + + Point3D screen = InterfaceManager.clientInterface.projectToScreen(topPos, gui.screenWidth, gui.screenHeight); + if (screen == null) continue; + + double screenX = screen.x; + double screenY = screen.y; + + List parts = new ArrayList<>(); + for (APart part : vehicle.allParts) { + if (part.definition.general.health > 0 && !part.definition.generic.type.toLowerCase().startsWith("seat")) { + parts.add(part); + } + } + + int numBars = (vehicle.definition.general.health > 0 ? 1 : 0) + parts.size(); + if (numBars == 0) continue; + + int panelHeight = margin * 2 + (int)(15 * titleScale) + (barHeight + margin) * numBars; + + int startX = (int) screenX - panelWidth / 2; + int startY = (int) screenY - panelHeight / 2; + + float u = 7 / 256F; + float v = 7 / 256F; + float u2 = 8 / 256F; + float v2 = 8 / 256F; + + setRoundedBoxSolid(bgRenderable.vertexObject, panelWidth, panelHeight, bgRadius, u, v, u2, v2); + bgRenderable.transform.resetTransforms(); + bgRenderable.transform.setTranslation(startX, -startY, 300); + bgRenderable.render(); + + int currentY = startY + margin; + + String vehicleName = vehicle.cachedItem != null ? vehicle.cachedItem.getItemName() : vehicle.definition.systemName; + InterfaceManager.renderingInterface.drawVanillaText(vehicleName, (int) screenX, -(currentY), ColorRGB.WHITE, TextAlignment.CENTERED, titleScale); + currentY += (int)(15 * titleScale) + margin; + + int barX = startX + panelWidth - barWidth - margin; + + if (vehicle.definition.general.health > 0) { + InterfaceManager.renderingInterface.drawVanillaText("VEHICLE: " + vehicleName, startX + margin, -(currentY + (int)(2 * panelScale)), ColorRGB.WHITE, TextAlignment.LEFT_ALIGNED, textScale); + + drawBar(vehicle.damageVar.currentValue, vehicle.definition.general.health, barX, currentY, barWidth, barHeight, barRadius, percentScale, u, v, u2, v2); + currentY += barHeight + margin; + } + + for (APart part : parts) { + String partType = getTypeName(part.definition.generic.type).toUpperCase(); + String partName = part.cachedItem != null ? part.cachedItem.getItemName() : part.definition.systemName; + + InterfaceManager.renderingInterface.drawVanillaText(partType + ": " + partName, startX + margin, -(currentY + (int)(2 * panelScale)), ColorRGB.WHITE, TextAlignment.LEFT_ALIGNED, textScale); + + drawBar(part.damageVar.currentValue, part.definition.general.health, barX, currentY, barWidth, barHeight, barRadius, percentScale, u, v, u2, v2); + currentY += barHeight + margin; + } + } + } + + private void drawBar(double currentDamage, double maxHealth, int x, int y, int barWidth, int barHeight, int r, float textScale, float u, float v, float u2, float v2) { + double healthRatio = 1.0 - (currentDamage / maxHealth); + if (healthRatio < 0) healthRatio = 0; + + int fillWidth = (int) (barWidth * healthRatio); + + setRoundedBoxSolid(barBgRenderable.vertexObject, barWidth, barHeight, r, u, v, u2, v2); + barBgRenderable.transform.resetTransforms(); + barBgRenderable.transform.setTranslation(x, -y, 301); + barBgRenderable.render(); + + if (fillWidth > 0) { + if (healthRatio > 0.5) barFgRenderable.setColor(ColorRGB.GREEN); + else if (healthRatio > 0.2) barFgRenderable.setColor(ColorRGB.YELLOW); + else barFgRenderable.setColor(ColorRGB.RED); + + int activeR = Math.min(r, fillWidth / 2); + setRoundedBoxSolid(barFgRenderable.vertexObject, fillWidth, barHeight, activeR, u, v, u2, v2); + barFgRenderable.transform.resetTransforms(); + barFgRenderable.transform.setTranslation(x, -y, 302); + barFgRenderable.render(); + } + + int percent = (int) (healthRatio * 100); + InterfaceManager.renderingInterface.drawVanillaText(percent + "%", x + barWidth / 2, -(y + (int)(barHeight / 2 - 4 * textScale)), ColorRGB.WHITE, TextAlignment.CENTERED, textScale); + } + + private void setRoundedBoxSolid(RenderableVertices vertices, int width, int height, int r, float u, float v, float u2, float v2) { + if (r <= 0) { + vertices.setSpriteProperties(0, 0, 0, width, height, u, v, u2, v2); + vertices.setSpriteProperties(1, 0, 0, 0, 0, u, v, u2, v2); + vertices.setSpriteProperties(2, 0, 0, 0, 0, u, v, u2, v2); + return; + } + vertices.setSpriteProperties(0, r, 0, width - 2*r, r, u, v, u2, v2); + vertices.setSpriteProperties(1, 0, -r, width, height - 2*r, u, v, u2, v2); + vertices.setSpriteProperties(2, r, -(height - r), width - 2*r, r, u, v, u2, v2); + } + + private String getTypeName(String type) { + if (type == null) return "Unknown"; + String t = type.toLowerCase(); + if (t.startsWith("engine")) return "Engine"; + if (t.startsWith("ground")) return "Ground device"; + if (t.startsWith("propeller")) return "Propeller"; + if (t.startsWith("seat")) return "Seat"; + if (t.startsWith("gun")) return "Gun"; + if (t.startsWith("interactable")) return "Interactable"; + if (t.startsWith("effector")) return "Effector"; + if (t.startsWith("generic")) return "Generic"; + if (t.startsWith("custom")) return "Custom"; + + return type.substring(0, 1).toUpperCase() + type.substring(1).toLowerCase().replace('_', ' '); + } +} \ No newline at end of file diff --git a/mccore/src/main/java/minecrafttransportsimulator/guis/instances/GUIOverlay.java b/mccore/src/main/java/minecrafttransportsimulator/guis/instances/GUIOverlay.java index a629d5321..7b96f9dc0 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/guis/instances/GUIOverlay.java +++ b/mccore/src/main/java/minecrafttransportsimulator/guis/instances/GUIOverlay.java @@ -17,6 +17,7 @@ import minecrafttransportsimulator.guis.components.AGUIBase; import minecrafttransportsimulator.guis.components.GUIComponentItem; import minecrafttransportsimulator.guis.components.GUIComponentLabel; +import minecrafttransportsimulator.guis.components.GUIComponentVehicleDebug; import minecrafttransportsimulator.items.components.AItemPack; import minecrafttransportsimulator.items.components.AItemPart; import minecrafttransportsimulator.jsondefs.JSONItem.ItemComponentType; @@ -67,6 +68,7 @@ public List getTooltipText() { return tooltipText; } }); + addComponent(new GUIComponentVehicleDebug()); } @Override diff --git a/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceClient.java b/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceClient.java index e1a29f709..c33aa26b2 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceClient.java +++ b/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceClient.java @@ -52,20 +52,20 @@ public interface IInterfaceClient { */ boolean isChatOpen(); - /** - * Returns true if a GUI is open. - */ - boolean isGUIOpen(); - - /** - * Displays a short-lived overlay message above the hotbar. - */ - void displayOverlayMessage(String message); - - /** - * Returns the camera mode. - */ - CameraMode getCameraMode(); + /** + * Returns true if a GUI is open. + */ + boolean isGUIOpen(); + + /** + * Displays a short-lived overlay message above the hotbar. + */ + void displayOverlayMessage(String message); + + /** + * Returns the camera mode. + */ + CameraMode getCameraMode(); /** * Sets the camera mode. @@ -137,6 +137,14 @@ public interface IInterfaceClient { */ Point3D getCameraPosition(); + /** + * Projects a 3D world position into 2D screen GUI coordinates for overlay rendering. + * Returns null if the point is behind the camera or outside a reasonable screen margin. + * The returned Point3D is a shared mutable instance — copy it if you need to keep the value. + * Coordinates: x = pixel from left, y = pixel from top, z = depth (positive = in front of camera). + */ + Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight); + /** * Plays the block breaking sound for the block at the passed-in position. */ diff --git a/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceRender.java b/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceRender.java index 0c589ddd4..407f047c9 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceRender.java +++ b/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceRender.java @@ -2,9 +2,11 @@ import java.io.InputStream; +import minecrafttransportsimulator.baseclasses.ColorRGB; import minecrafttransportsimulator.baseclasses.Point3D; import minecrafttransportsimulator.guis.components.GUIComponentItem; import minecrafttransportsimulator.rendering.GIFParser.ParsedGIF; +import minecrafttransportsimulator.rendering.RenderText; import minecrafttransportsimulator.rendering.RenderableData; import minecrafttransportsimulator.rendering.RenderableVertices; @@ -43,6 +45,11 @@ public interface IInterfaceRender { */ void renderItemModel(GUIComponentItem component); + /** + * Draws vanilla text. + */ + void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale); + /** * Renders according to the set data. * If the object is ever deleted, and {@link RenderableVertices#cacheVertices} is true, diff --git a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceClient.java b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceClient.java index 20a9b5ef6..67d82969b 100644 --- a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceClient.java +++ b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceClient.java @@ -82,19 +82,19 @@ public boolean isChatOpen() { } @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; - } + 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) { @@ -163,6 +163,73 @@ public Point3D getCameraPosition() { private static final Point3D mutablePosition = new Point3D(); + @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; + minecrafttransportsimulator.baseclasses.RotationMatrix ori = 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. + EntityPlayer player = Minecraft.getMinecraft().player; + Vec3d camOffset = 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; + } + + 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 final Point3D screenProjectionResult = new Point3D(); + @Override public void playBlockBreakSound(Point3D position) { BlockPos pos = new BlockPos(position.x, position.y, position.z); diff --git a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceEventsEntityRendering.java b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceEventsEntityRendering.java index 2849b7ed8..e3fbee092 100644 --- a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceEventsEntityRendering.java +++ b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceEventsEntityRendering.java @@ -64,6 +64,9 @@ public class InterfaceEventsEntityRendering { private static final Point3D playerPositionHelper = new Point3D(); private static final Point3D cameraAdjustedPosition = new Point3D(); private static final RotationMatrix cameraAdjustedOrientation = new RotationMatrix(); + public static boolean adjustedCamera; + public static final Point3D projectionCameraPosition = new Point3D(); + public static final RotationMatrix projectionCameraOrientation = new RotationMatrix(); private static final TransformationMatrix cameraAdjustments = new TransformationMatrix(); private static int lastScreenWidth; private static int lastScreenHeight; @@ -82,7 +85,11 @@ public static void onIVCameraSetup(CameraSetup event) { IWrapperPlayer player = WrapperPlayer.getWrapperFor((EntityPlayer) event.getEntity()); cameraAdjustedPosition.set(0, 0, 0); cameraAdjustedOrientation.setToZero(); + adjustedCamera = false; if (CameraSystem.adjustCamera(player, cameraAdjustedPosition, cameraAdjustedOrientation, (float) event.getRenderPartialTicks())) { + projectionCameraPosition.set(cameraAdjustedPosition); + projectionCameraOrientation.set(cameraAdjustedOrientation); + adjustedCamera = true; //Set helper to the current camera position. EntityPlayer mcPlayer = ((WrapperPlayer) player).player; playerPosition.set(mcPlayer.posX, mcPlayer.posY, mcPlayer.posZ); diff --git a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceRender.java b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceRender.java index f173180a8..cf6074cb5 100644 --- a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceRender.java +++ b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceRender.java @@ -14,6 +14,9 @@ import java.util.Map; import java.util.Set; +import minecrafttransportsimulator.baseclasses.ColorRGB; +import minecrafttransportsimulator.rendering.RenderText; +import net.minecraft.client.gui.FontRenderer; import org.lwjgl.opengl.GL11; import minecrafttransportsimulator.baseclasses.Point3D; @@ -488,4 +491,20 @@ private static void setBlendBright(boolean enabled) { GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); } } + + @Override + public void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale) { + FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer; + float width = fontRenderer.getStringWidth(text) * scale; + float finalX = x; + if (alignment == RenderText.TextAlignment.CENTERED) finalX -= width / 2.0F; + else if (alignment == RenderText.TextAlignment.RIGHT_ALIGNED) finalX -= width; + + GlStateManager.pushMatrix(); + GlStateManager.translate(finalX, y, 0); + GlStateManager.scale(scale, -scale, 1.0F); + fontRenderer.drawStringWithShadow(text, 0, 0, color.rgbInt | 0xFF000000); + GlStateManager.popMatrix(); + } + } diff --git a/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java b/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java index ad51680ea..040f66aa4 100644 --- a/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java +++ b/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java @@ -24,6 +24,7 @@ import net.minecraft.block.SoundType; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.ChatScreen; +import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.client.settings.PointOfView; import net.minecraft.client.util.ITooltipFlag; import net.minecraft.fluid.Fluid; @@ -32,12 +33,13 @@ import net.minecraft.util.SoundCategory; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.util.math.vector.Vector3f; 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.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; @@ -99,19 +101,19 @@ public boolean isChatOpen() { } @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; - } + 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) { @@ -177,6 +179,62 @@ public Point3D getCameraPosition() { private static final Point3D mutablePosition = new Point3D(); + @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; + minecrafttransportsimulator.baseclasses.RotationMatrix ori = InterfaceEventsEntityRendering.cameraAdjustedOrientation; + fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; + upX = ori.m01; upY = ori.m11; upZ = ori.m21; + rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; + } else { + ActiveRenderInfo camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + Vector3d camPos = camera.getPosition(); + camX = camPos.x; camY = camPos.y; camZ = camPos.z; + Vector3f look = camera.getLookVector(); + Vector3f up = camera.getUpVector(); + fwdX = look.x(); fwdY = look.y(); fwdZ = look.z(); + upX = up.x(); upY = up.y(); upZ = up.z(); + 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 (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 final Point3D screenProjectionResult = new Point3D(); + @Override public void playBlockBreakSound(Point3D position) { BlockPos pos = new BlockPos(position.x, position.y, position.z); diff --git a/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceRender.java b/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceRender.java index d214252c9..a57a61f7e 100644 --- a/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceRender.java +++ b/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceRender.java @@ -14,6 +14,9 @@ import javax.imageio.ImageIO; +import minecrafttransportsimulator.baseclasses.ColorRGB; +import minecrafttransportsimulator.rendering.RenderText; +import net.minecraft.client.gui.FontRenderer; import org.lwjgl.opengl.GL11; import com.mojang.blaze3d.matrix.MatrixStack; @@ -647,4 +650,19 @@ private BufferData(RenderType type, RenderableData data) { RenderSystem.depthMask(true); RenderSystem.defaultBlendFunc(); }); + + @Override + public void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale) { + FontRenderer fontRenderer = Minecraft.getInstance().font; + float width = fontRenderer.width(text) * scale; + float finalX = x; + if (alignment == RenderText.TextAlignment.CENTERED) finalX -= width / 2.0F; + else if (alignment == RenderText.TextAlignment.RIGHT_ALIGNED) finalX -= width; + + matrixStack.pushPose(); + matrixStack.translate(finalX, y, 0); + matrixStack.scale(scale, -scale, 1.0F); + fontRenderer.drawInBatch(text, 0, 0, color.rgbInt | 0xFF000000, true, matrixStack.last().pose(), renderBuffer, false, 0, 15728880); + matrixStack.popPose(); + } } diff --git a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java index 5a8ffbe9a..48ba727d2 100644 --- a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java +++ b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java @@ -6,7 +6,9 @@ import java.util.Map; import java.util.Map.Entry; +import com.mojang.math.Vector3f; import minecrafttransportsimulator.baseclasses.Point3D; +import minecrafttransportsimulator.baseclasses.RotationMatrix; import minecrafttransportsimulator.entities.instances.EntityFluidTank; import minecrafttransportsimulator.guis.components.AGUIBase; import minecrafttransportsimulator.guis.instances.GUIPackMissing; @@ -22,15 +24,16 @@ import minecrafttransportsimulator.systems.ControlSystem; import minecrafttransportsimulator.systems.LanguageSystem; import net.minecraft.ChatFormatting; +import net.minecraft.client.Camera; 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.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; @@ -99,19 +102,19 @@ public boolean isChatOpen() { } @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; - } + 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) { @@ -177,6 +180,61 @@ public Point3D getCameraPosition() { private static final Point3D mutablePosition = new Point3D(); + @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 = InterfaceEventsEntityRendering.cameraAdjustedOrientation; + fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; + upX = ori.m01; upY = ori.m11; upZ = ori.m21; + rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; + } else { + Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + Vec3 camPos = camera.getPosition(); + camX = camPos.x(); camY = camPos.y(); camZ = camPos.z(); + Vector3f look = camera.getLookVector(); + Vector3f up = camera.getUpVector(); + 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 (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 final Point3D screenProjectionResult = new Point3D(); + @Override public void playBlockBreakSound(Point3D position) { BlockPos pos = new BlockPos(position.x, position.y, position.z); diff --git a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java index 00ec24a3a..cd9a82cea 100644 --- a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java +++ b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java @@ -30,6 +30,7 @@ import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; +import minecrafttransportsimulator.baseclasses.ColorRGB; import minecrafttransportsimulator.baseclasses.Point3D; import minecrafttransportsimulator.baseclasses.TransformationMatrix; import minecrafttransportsimulator.entities.components.AEntityC_Renderable; @@ -40,9 +41,11 @@ import minecrafttransportsimulator.mcinterface.InterfaceManager; import minecrafttransportsimulator.rendering.GIFParser.GIFImageFrame; import minecrafttransportsimulator.rendering.GIFParser.ParsedGIF; +import minecrafttransportsimulator.rendering.RenderText; import minecrafttransportsimulator.rendering.RenderableData; import minecrafttransportsimulator.systems.ConfigSystem; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderStateShard; @@ -195,41 +198,41 @@ public void renderVertices(RenderableData data, boolean changedSinceLastRender) PoseStack.Pose stackEntry = matrixStack.last(); stackEntry.pose().multiply(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); - buffer.endVertex(); - buffer.vertex(stackEntry.pose(), x2, y2, z2); - buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); - //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 { + 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); + buffer.endVertex(); + buffer.vertex(stackEntry.pose(), x2, y2, z2); + buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); + //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) { @@ -735,4 +738,20 @@ private BufferData(RenderType type, RenderableData data) { RenderSystem.depthMask(true); RenderSystem.defaultBlendFunc(); }); + + + @Override + public void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale) { + Font fontRenderer = Minecraft.getInstance().font; + float width = fontRenderer.width(text) * scale; + float finalX = x; + if (alignment == RenderText.TextAlignment.CENTERED) finalX -= width / 2.0F; + else if (alignment == RenderText.TextAlignment.RIGHT_ALIGNED) finalX -= width; + + matrixStack.pushPose(); + matrixStack.translate(finalX, y, 0); + matrixStack.scale(scale, -scale, 1.0F); + fontRenderer.drawInBatch(text, 0, 0, color.rgbInt | 0xFF000000, true, matrixStack.last().pose(), renderBuffer, false, 0, 15728880); + matrixStack.popPose(); + } } diff --git a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java index 49c60905e..3f3cf39f6 100644 --- a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java +++ b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java @@ -6,7 +6,9 @@ import java.util.Map; import java.util.Map.Entry; +import com.mojang.math.Vector3f; import minecrafttransportsimulator.baseclasses.Point3D; +import minecrafttransportsimulator.baseclasses.RotationMatrix; import minecrafttransportsimulator.entities.instances.EntityFluidTank; import minecrafttransportsimulator.guis.components.AGUIBase; import minecrafttransportsimulator.guis.instances.GUIPackMissing; @@ -22,6 +24,7 @@ import minecrafttransportsimulator.systems.ControlSystem; import minecrafttransportsimulator.systems.LanguageSystem; import net.minecraft.ChatFormatting; +import net.minecraft.client.Camera; import net.minecraft.client.CameraType; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.ChatScreen; @@ -97,19 +100,19 @@ public boolean isChatOpen() { } @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; - } + 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) { @@ -180,6 +183,61 @@ public Point3D getCameraPosition() { private static final Point3D mutablePosition = new Point3D(); + @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 = InterfaceEventsEntityRendering.cameraAdjustedOrientation; + fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; + upX = ori.m01; upY = ori.m11; upZ = ori.m21; + rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; + } else { + Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + Vec3 camPos = camera.getPosition(); + camX = camPos.x; camY = camPos.y; camZ = camPos.z; + Vector3f look = camera.getLookVector(); + Vector3f up = camera.getUpVector(); + 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 (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 final Point3D screenProjectionResult = new Point3D(); + @Override public void playBlockBreakSound(Point3D position) { BlockPos pos = new BlockPos(position.x, position.y, position.z); diff --git a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java index cd99d933e..d892ad51d 100644 --- a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java +++ b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java @@ -29,6 +29,7 @@ import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; +import minecrafttransportsimulator.baseclasses.ColorRGB; import minecrafttransportsimulator.baseclasses.Point3D; import minecrafttransportsimulator.baseclasses.TransformationMatrix; import minecrafttransportsimulator.entities.components.AEntityC_Renderable; @@ -39,9 +40,11 @@ import minecrafttransportsimulator.mcinterface.InterfaceManager; import minecrafttransportsimulator.rendering.GIFParser.GIFImageFrame; import minecrafttransportsimulator.rendering.GIFParser.ParsedGIF; +import minecrafttransportsimulator.rendering.RenderText; import minecrafttransportsimulator.rendering.RenderableData; import minecrafttransportsimulator.systems.ConfigSystem; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderStateShard; @@ -188,41 +191,41 @@ public void renderVertices(RenderableData data, boolean changedSinceLastRender) PoseStack.Pose stackEntry = matrixStack.last(); stackEntry.pose().multiply(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); - buffer.endVertex(); - buffer.vertex(stackEntry.pose(), x2, y2, z2); - buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); - //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 { + 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); + buffer.endVertex(); + buffer.vertex(stackEntry.pose(), x2, y2, z2); + buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); + //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) { @@ -731,4 +734,20 @@ private BufferData(RenderType type, RenderableData data) { RenderSystem.depthMask(true); RenderSystem.defaultBlendFunc(); }); + + + @Override + public void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale) { + Font fontRenderer = Minecraft.getInstance().font; + float width = fontRenderer.width(text) * scale; + float finalX = x; + if (alignment == RenderText.TextAlignment.CENTERED) finalX -= width / 2.0F; + else if (alignment == RenderText.TextAlignment.RIGHT_ALIGNED) finalX -= width; + + matrixStack.pushPose(); + matrixStack.translate(finalX, y, 0); + matrixStack.scale(scale, -scale, 1.0F); + fontRenderer.drawInBatch(text, 0, 0, color.rgbInt | 0xFF000000, true, matrixStack.last().pose(), renderBuffer, false, 0, 15728880); + matrixStack.popPose(); + } } diff --git a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java index 2ce8c8f06..729624e35 100644 --- a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java +++ b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java @@ -7,6 +7,7 @@ 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; @@ -22,6 +23,7 @@ import minecrafttransportsimulator.systems.ControlSystem; import minecrafttransportsimulator.systems.LanguageSystem; import net.minecraft.ChatFormatting; +import net.minecraft.client.Camera; import net.minecraft.client.CameraType; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.ChatScreen; @@ -43,6 +45,7 @@ import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; import net.minecraftforge.registries.ForgeRegistries; +import org.joml.Vector3f; @EventBusSubscriber(Dist.CLIENT) public class InterfaceClient implements IInterfaceClient { @@ -97,19 +100,19 @@ public boolean isChatOpen() { } @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; - } + 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) { @@ -180,6 +183,61 @@ public Point3D getCameraPosition() { private static final Point3D mutablePosition = new Point3D(); + @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 = InterfaceEventsEntityRendering.cameraAdjustedOrientation; + fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; + upX = ori.m01; upY = ori.m11; upZ = ori.m21; + rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; + } else { + Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + Vec3 camPos = camera.getPosition(); + camX = camPos.x; camY = camPos.y; camZ = camPos.z; + Vector3f look = camera.getLookVector(); + Vector3f up = camera.getUpVector(); + 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 (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 final Point3D screenProjectionResult = new Point3D(); + @Override public void playBlockBreakSound(Point3D position) { BlockPos pos = BlockPos.containing(position.x, position.y, position.z); diff --git a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java index 1d39cb3e6..b38b083d0 100644 --- a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java +++ b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java @@ -16,6 +16,9 @@ import javax.imageio.ImageIO; +import minecrafttransportsimulator.baseclasses.ColorRGB; +import minecrafttransportsimulator.rendering.RenderText; +import net.minecraft.client.gui.Font; import org.joml.Matrix3f; import org.joml.Matrix4f; @@ -212,15 +215,15 @@ public void renderVertices(RenderableData data, boolean changedSinceLastRender) 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); - buffer.endVertex(); - buffer.vertex(stackEntry.pose(), x2, y2, z2); - buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); - //Although we don't use them, normals are required for proper rendering. Stupid shaders... - buffer.normal(stackEntry.normal(), dx, dy, dz); - buffer.endVertex(); + 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); + buffer.endVertex(); + buffer.vertex(stackEntry.pose(), x2, y2, z2); + buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); + //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(); @@ -717,4 +720,20 @@ private BufferData(RenderType type, RenderableData data) { RenderSystem.depthMask(true); RenderSystem.defaultBlendFunc(); }); + + @Override + public void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale) { + Font fontRenderer = Minecraft.getInstance().font; + float width = fontRenderer.width(text) * scale; + float finalX = x; + if (alignment == RenderText.TextAlignment.CENTERED) finalX -= width / 2.0F; + else if (alignment == RenderText.TextAlignment.RIGHT_ALIGNED) finalX -= width; + + matrixStack.pushPose(); + matrixStack.translate(finalX, y, 0); + matrixStack.scale(scale, -scale, 1.0F); + fontRenderer.drawInBatch(text, 0, 0, color.rgbInt | 0xFF000000, true, matrixStack.last().pose(), renderBuffer, Font.DisplayMode.NORMAL, 0, 15728880); + matrixStack.popPose(); + } + } diff --git a/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceClient.java b/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceClient.java index b2539e574..6505864b3 100644 --- a/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceClient.java +++ b/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceClient.java @@ -9,6 +9,7 @@ import java.util.Set; import minecrafttransportsimulator.baseclasses.Point3D; +import minecrafttransportsimulator.baseclasses.RotationMatrix; import minecrafttransportsimulator.entities.instances.EntityFluidTank; import minecrafttransportsimulator.guis.components.AGUIBase; import minecrafttransportsimulator.guis.instances.GUIPackMissing; @@ -28,6 +29,7 @@ import minecrafttransportsimulator.systems.ControlSystem; import minecrafttransportsimulator.systems.LanguageSystem; import net.minecraft.ChatFormatting; +import net.minecraft.client.Camera; import net.minecraft.client.CameraType; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.ChatScreen; @@ -48,6 +50,7 @@ import net.neoforged.neoforge.fluids.FluidStack; import net.neoforged.fml.common.EventBusSubscriber; import net.minecraft.core.registries.BuiltInRegistries; +import org.joml.Vector3f; @EventBusSubscriber(modid = InterfaceLoader.MODID, value = Dist.CLIENT) public class InterfaceClient implements IInterfaceClient { @@ -238,6 +241,61 @@ public Point3D getCameraPosition() { private static final Point3D mutablePosition = new Point3D(); + @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 = InterfaceEventsEntityRendering.cameraAdjustedOrientation; + fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; + upX = ori.m01; upY = ori.m11; upZ = ori.m21; + rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; + } else { + Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + Vec3 camPos = camera.getPosition(); + camX = camPos.x; camY = camPos.y; camZ = camPos.z; + Vector3f look = camera.getLookVector(); + Vector3f up = camera.getUpVector(); + 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 (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 final Point3D screenProjectionResult = new Point3D(); + @Override public void playBlockBreakSound(Point3D position) { BlockPos pos = BlockPos.containing(position.x, position.y, position.z); diff --git a/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceRender.java b/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceRender.java index 627e61196..ac3b442b0 100644 --- a/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceRender.java +++ b/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceRender.java @@ -16,6 +16,9 @@ import javax.imageio.ImageIO; +import minecrafttransportsimulator.baseclasses.ColorRGB; +import minecrafttransportsimulator.rendering.RenderText; +import net.minecraft.client.gui.Font; import org.joml.Matrix3f; import org.joml.Matrix4f; import org.joml.Matrix4fStack; @@ -742,4 +745,20 @@ private void close() { RenderSystem.depthMask(true); RenderSystem.defaultBlendFunc(); }); + + @Override + public void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale) { + Font fontRenderer = Minecraft.getInstance().font; + float width = fontRenderer.width(text) * scale; + float finalX = x; + if (alignment == RenderText.TextAlignment.CENTERED) finalX -= width / 2.0F; + else if (alignment == RenderText.TextAlignment.RIGHT_ALIGNED) finalX -= width; + + matrixStack.pushPose(); + matrixStack.translate(finalX, y, 0); + matrixStack.scale(scale, -scale, 1.0F); + fontRenderer.drawInBatch(text, 0, 0, color.rgbInt | 0xFF000000, true, matrixStack.last().pose(), renderBuffer, Font.DisplayMode.NORMAL, 0, 15728880); + matrixStack.popPose(); + } + } From 0afaf939be04ad23cc085d1e62f8bb8895ffc8a5 Mon Sep 17 00:00:00 2001 From: dldev32 Date: Sun, 10 May 2026 21:53:23 +0300 Subject: [PATCH 5/8] Fixed projectToScreen --- .../java/mcinterface1211/InterfaceClient.java | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceClient.java b/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceClient.java index 6505864b3..93cfe294f 100644 --- a/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceClient.java +++ b/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceClient.java @@ -29,11 +29,11 @@ import minecrafttransportsimulator.systems.ControlSystem; import minecrafttransportsimulator.systems.LanguageSystem; import net.minecraft.ChatFormatting; -import net.minecraft.client.Camera; 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.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Style; import net.minecraft.network.chat.TextColor; @@ -49,8 +49,6 @@ import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.fluids.FluidStack; import net.neoforged.fml.common.EventBusSubscriber; -import net.minecraft.core.registries.BuiltInRegistries; -import org.joml.Vector3f; @EventBusSubscriber(modid = InterfaceLoader.MODID, value = Dist.CLIENT) public class InterfaceClient implements IInterfaceClient { @@ -240,6 +238,7 @@ public Point3D getCameraPosition() { } private static final Point3D mutablePosition = new Point3D(); + private static final RotationMatrix cameraProjectionOrientation = new RotationMatrix(); @Override public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight) { @@ -252,17 +251,18 @@ public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeig camX = InterfaceEventsEntityRendering.cameraAdjustedPosition.x; camY = InterfaceEventsEntityRendering.cameraAdjustedPosition.y; camZ = InterfaceEventsEntityRendering.cameraAdjustedPosition.z; - RotationMatrix ori = InterfaceEventsEntityRendering.cameraAdjustedOrientation; + 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 { - Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - Vec3 camPos = camera.getPosition(); + 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; - Vector3f look = camera.getLookVector(); - Vector3f up = camera.getUpVector(); - Vector3f left = camera.getLeftVector(); + 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(); @@ -281,9 +281,16 @@ public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeig 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; @@ -294,6 +301,16 @@ public Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeig 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 From cf5d38d31cd9ebf7dea3a28ab550a3b543086405 Mon Sep 17 00:00:00 2001 From: dldev32 Date: Mon, 11 May 2026 23:16:04 +0300 Subject: [PATCH 6/8] Removed unused code --- .../components/GUIComponentVehicleDebug.java | 207 ------------------ .../guis/instances/GUIOverlay.java | 2 - .../mcinterface/IInterfaceClient.java | 8 - .../mcinterface/IInterfaceRender.java | 7 - .../java/mcinterface1122/InterfaceClient.java | 67 ------ .../InterfaceEventsEntityRendering.java | 7 - .../java/mcinterface1122/InterfaceRender.java | 19 -- .../java/mcinterface1165/InterfaceClient.java | 58 ----- .../java/mcinterface1165/InterfaceRender.java | 18 -- .../java/mcinterface1182/InterfaceClient.java | 58 ----- .../java/mcinterface1182/InterfaceRender.java | 19 -- .../java/mcinterface1192/InterfaceClient.java | 58 ----- .../java/mcinterface1192/InterfaceRender.java | 19 -- .../java/mcinterface1201/InterfaceClient.java | 58 ----- .../java/mcinterface1201/InterfaceRender.java | 19 -- .../java/mcinterface1211/InterfaceClient.java | 77 +------ .../java/mcinterface1211/InterfaceRender.java | 19 -- 17 files changed, 1 insertion(+), 719 deletions(-) delete mode 100644 mccore/src/main/java/minecrafttransportsimulator/guis/components/GUIComponentVehicleDebug.java diff --git a/mccore/src/main/java/minecrafttransportsimulator/guis/components/GUIComponentVehicleDebug.java b/mccore/src/main/java/minecrafttransportsimulator/guis/components/GUIComponentVehicleDebug.java deleted file mode 100644 index 42158015a..000000000 --- a/mccore/src/main/java/minecrafttransportsimulator/guis/components/GUIComponentVehicleDebug.java +++ /dev/null @@ -1,207 +0,0 @@ -package minecrafttransportsimulator.guis.components; - -import java.util.ArrayList; -import java.util.List; - -import minecrafttransportsimulator.baseclasses.ColorRGB; -import minecrafttransportsimulator.baseclasses.Point3D; -import minecrafttransportsimulator.entities.instances.APart; -import minecrafttransportsimulator.entities.instances.EntityVehicleF_Physics; -import minecrafttransportsimulator.mcinterface.AWrapperWorld; -import minecrafttransportsimulator.mcinterface.IWrapperPlayer; -import minecrafttransportsimulator.mcinterface.InterfaceManager; -import minecrafttransportsimulator.rendering.RenderText.TextAlignment; -import minecrafttransportsimulator.rendering.RenderableData; -import minecrafttransportsimulator.rendering.RenderableData.LightingMode; -import minecrafttransportsimulator.rendering.RenderableVertices; -import minecrafttransportsimulator.systems.ConfigSystem; - -public class GUIComponentVehicleDebug extends AGUIComponent { - public static float panelScale = 0.5F; - - private static final int BASE_PANEL_WIDTH = 400; - private static final int BASE_BAR_WIDTH = 150; - private static final int BASE_BAR_HEIGHT = 12; - private static final int BASE_MARGIN = 8; - private static final double MAX_DISTANCE = 10.0; - - private final RenderableData bgRenderable; - private final RenderableData barBgRenderable; - private final RenderableData barFgRenderable; - - public GUIComponentVehicleDebug() { - super(0, 0, 0, 0); - - // Use 3 quads for "slightly rounded" corners (top, middle, bottom) - RenderableVertices bgVertices = RenderableVertices.createSprite(3, null, null); - bgRenderable = new RenderableData(bgVertices, AGUIBase.STANDARD_TEXTURE_NAME); - bgRenderable.setColor(ColorRGB.BLACK); - bgRenderable.setAlpha(0.6F); - bgRenderable.setLightMode(LightingMode.IGNORE_ALL_LIGHTING); - bgRenderable.isTranslucent = true; - - RenderableVertices barBgVertices = RenderableVertices.createSprite(3, null, null); - barBgRenderable = new RenderableData(barBgVertices, AGUIBase.STANDARD_TEXTURE_NAME); - barBgRenderable.setColor(ColorRGB.GRAY); - barBgRenderable.setAlpha(0.8F); - barBgRenderable.setLightMode(LightingMode.IGNORE_ALL_LIGHTING); - barBgRenderable.isTranslucent = true; - - RenderableVertices barFgVertices = RenderableVertices.createSprite(3, null, null); - barFgRenderable = new RenderableData(barFgVertices, AGUIBase.STANDARD_TEXTURE_NAME); - barFgRenderable.setColor(ColorRGB.GREEN); - barFgRenderable.setAlpha(0.9F); - barFgRenderable.setLightMode(LightingMode.IGNORE_ALL_LIGHTING); - barFgRenderable.isTranslucent = true; - } - - @Override - public void render(AGUIBase gui, int mouseX, int mouseY, boolean renderBright, boolean renderLitTexture, boolean blendingEnabled, float partialTicks) { - if (!ConfigSystem.settings.general.devMode.value || !ConfigSystem.client.renderingSettings.vehicleDebugPanel.value) { - return; - } - - if (blendingEnabled) { - return; - } - - AWrapperWorld world = InterfaceManager.clientInterface.getClientWorld(); - if (world == null) return; - - IWrapperPlayer player = InterfaceManager.clientInterface.getClientPlayer(); - if (player == null) return; - - int panelWidth = (int) (BASE_PANEL_WIDTH * panelScale); - int barWidth = (int) (BASE_BAR_WIDTH * panelScale); - int barHeight = (int) (BASE_BAR_HEIGHT * panelScale); - int margin = (int) (BASE_MARGIN * panelScale); - int bgRadius = (int) (4 * panelScale); - int barRadius = (int) (2 * panelScale); - - if (bgRadius < 1) bgRadius = 1; - if (barRadius < 1) barRadius = 1; - float textScale = 0.8F * panelScale; - float titleScale = 1.0F * panelScale; - float percentScale = 0.7F * panelScale; - - for (EntityVehicleF_Physics vehicle : world.getEntitiesExtendingType(EntityVehicleF_Physics.class)) { - if (vehicle.position.distanceTo(player.getPosition()) > MAX_DISTANCE) { - continue; - } - - Point3D topPos = vehicle.position.copy(); - topPos.y += vehicle.encompassingBox.heightRadius + 2.5; - - Point3D screen = InterfaceManager.clientInterface.projectToScreen(topPos, gui.screenWidth, gui.screenHeight); - if (screen == null) continue; - - double screenX = screen.x; - double screenY = screen.y; - - List parts = new ArrayList<>(); - for (APart part : vehicle.allParts) { - if (part.definition.general.health > 0 && !part.definition.generic.type.toLowerCase().startsWith("seat")) { - parts.add(part); - } - } - - int numBars = (vehicle.definition.general.health > 0 ? 1 : 0) + parts.size(); - if (numBars == 0) continue; - - int panelHeight = margin * 2 + (int)(15 * titleScale) + (barHeight + margin) * numBars; - - int startX = (int) screenX - panelWidth / 2; - int startY = (int) screenY - panelHeight / 2; - - float u = 7 / 256F; - float v = 7 / 256F; - float u2 = 8 / 256F; - float v2 = 8 / 256F; - - setRoundedBoxSolid(bgRenderable.vertexObject, panelWidth, panelHeight, bgRadius, u, v, u2, v2); - bgRenderable.transform.resetTransforms(); - bgRenderable.transform.setTranslation(startX, -startY, 300); - bgRenderable.render(); - - int currentY = startY + margin; - - String vehicleName = vehicle.cachedItem != null ? vehicle.cachedItem.getItemName() : vehicle.definition.systemName; - InterfaceManager.renderingInterface.drawVanillaText(vehicleName, (int) screenX, -(currentY), ColorRGB.WHITE, TextAlignment.CENTERED, titleScale); - currentY += (int)(15 * titleScale) + margin; - - int barX = startX + panelWidth - barWidth - margin; - - if (vehicle.definition.general.health > 0) { - InterfaceManager.renderingInterface.drawVanillaText("VEHICLE: " + vehicleName, startX + margin, -(currentY + (int)(2 * panelScale)), ColorRGB.WHITE, TextAlignment.LEFT_ALIGNED, textScale); - - drawBar(vehicle.damageVar.currentValue, vehicle.definition.general.health, barX, currentY, barWidth, barHeight, barRadius, percentScale, u, v, u2, v2); - currentY += barHeight + margin; - } - - for (APart part : parts) { - String partType = getTypeName(part.definition.generic.type).toUpperCase(); - String partName = part.cachedItem != null ? part.cachedItem.getItemName() : part.definition.systemName; - - InterfaceManager.renderingInterface.drawVanillaText(partType + ": " + partName, startX + margin, -(currentY + (int)(2 * panelScale)), ColorRGB.WHITE, TextAlignment.LEFT_ALIGNED, textScale); - - drawBar(part.damageVar.currentValue, part.definition.general.health, barX, currentY, barWidth, barHeight, barRadius, percentScale, u, v, u2, v2); - currentY += barHeight + margin; - } - } - } - - private void drawBar(double currentDamage, double maxHealth, int x, int y, int barWidth, int barHeight, int r, float textScale, float u, float v, float u2, float v2) { - double healthRatio = 1.0 - (currentDamage / maxHealth); - if (healthRatio < 0) healthRatio = 0; - - int fillWidth = (int) (barWidth * healthRatio); - - setRoundedBoxSolid(barBgRenderable.vertexObject, barWidth, barHeight, r, u, v, u2, v2); - barBgRenderable.transform.resetTransforms(); - barBgRenderable.transform.setTranslation(x, -y, 301); - barBgRenderable.render(); - - if (fillWidth > 0) { - if (healthRatio > 0.5) barFgRenderable.setColor(ColorRGB.GREEN); - else if (healthRatio > 0.2) barFgRenderable.setColor(ColorRGB.YELLOW); - else barFgRenderable.setColor(ColorRGB.RED); - - int activeR = Math.min(r, fillWidth / 2); - setRoundedBoxSolid(barFgRenderable.vertexObject, fillWidth, barHeight, activeR, u, v, u2, v2); - barFgRenderable.transform.resetTransforms(); - barFgRenderable.transform.setTranslation(x, -y, 302); - barFgRenderable.render(); - } - - int percent = (int) (healthRatio * 100); - InterfaceManager.renderingInterface.drawVanillaText(percent + "%", x + barWidth / 2, -(y + (int)(barHeight / 2 - 4 * textScale)), ColorRGB.WHITE, TextAlignment.CENTERED, textScale); - } - - private void setRoundedBoxSolid(RenderableVertices vertices, int width, int height, int r, float u, float v, float u2, float v2) { - if (r <= 0) { - vertices.setSpriteProperties(0, 0, 0, width, height, u, v, u2, v2); - vertices.setSpriteProperties(1, 0, 0, 0, 0, u, v, u2, v2); - vertices.setSpriteProperties(2, 0, 0, 0, 0, u, v, u2, v2); - return; - } - vertices.setSpriteProperties(0, r, 0, width - 2*r, r, u, v, u2, v2); - vertices.setSpriteProperties(1, 0, -r, width, height - 2*r, u, v, u2, v2); - vertices.setSpriteProperties(2, r, -(height - r), width - 2*r, r, u, v, u2, v2); - } - - private String getTypeName(String type) { - if (type == null) return "Unknown"; - String t = type.toLowerCase(); - if (t.startsWith("engine")) return "Engine"; - if (t.startsWith("ground")) return "Ground device"; - if (t.startsWith("propeller")) return "Propeller"; - if (t.startsWith("seat")) return "Seat"; - if (t.startsWith("gun")) return "Gun"; - if (t.startsWith("interactable")) return "Interactable"; - if (t.startsWith("effector")) return "Effector"; - if (t.startsWith("generic")) return "Generic"; - if (t.startsWith("custom")) return "Custom"; - - return type.substring(0, 1).toUpperCase() + type.substring(1).toLowerCase().replace('_', ' '); - } -} \ No newline at end of file diff --git a/mccore/src/main/java/minecrafttransportsimulator/guis/instances/GUIOverlay.java b/mccore/src/main/java/minecrafttransportsimulator/guis/instances/GUIOverlay.java index 7b96f9dc0..a629d5321 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/guis/instances/GUIOverlay.java +++ b/mccore/src/main/java/minecrafttransportsimulator/guis/instances/GUIOverlay.java @@ -17,7 +17,6 @@ import minecrafttransportsimulator.guis.components.AGUIBase; import minecrafttransportsimulator.guis.components.GUIComponentItem; import minecrafttransportsimulator.guis.components.GUIComponentLabel; -import minecrafttransportsimulator.guis.components.GUIComponentVehicleDebug; import minecrafttransportsimulator.items.components.AItemPack; import minecrafttransportsimulator.items.components.AItemPart; import minecrafttransportsimulator.jsondefs.JSONItem.ItemComponentType; @@ -68,7 +67,6 @@ public List getTooltipText() { return tooltipText; } }); - addComponent(new GUIComponentVehicleDebug()); } @Override diff --git a/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceClient.java b/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceClient.java index c33aa26b2..27eecf784 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceClient.java +++ b/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceClient.java @@ -137,14 +137,6 @@ public interface IInterfaceClient { */ Point3D getCameraPosition(); - /** - * Projects a 3D world position into 2D screen GUI coordinates for overlay rendering. - * Returns null if the point is behind the camera or outside a reasonable screen margin. - * The returned Point3D is a shared mutable instance — copy it if you need to keep the value. - * Coordinates: x = pixel from left, y = pixel from top, z = depth (positive = in front of camera). - */ - Point3D projectToScreen(Point3D worldPos, int screenWidth, int screenHeight); - /** * Plays the block breaking sound for the block at the passed-in position. */ diff --git a/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceRender.java b/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceRender.java index 407f047c9..0c589ddd4 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceRender.java +++ b/mccore/src/main/java/minecrafttransportsimulator/mcinterface/IInterfaceRender.java @@ -2,11 +2,9 @@ import java.io.InputStream; -import minecrafttransportsimulator.baseclasses.ColorRGB; import minecrafttransportsimulator.baseclasses.Point3D; import minecrafttransportsimulator.guis.components.GUIComponentItem; import minecrafttransportsimulator.rendering.GIFParser.ParsedGIF; -import minecrafttransportsimulator.rendering.RenderText; import minecrafttransportsimulator.rendering.RenderableData; import minecrafttransportsimulator.rendering.RenderableVertices; @@ -45,11 +43,6 @@ public interface IInterfaceRender { */ void renderItemModel(GUIComponentItem component); - /** - * Draws vanilla text. - */ - void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale); - /** * Renders according to the set data. * If the object is ever deleted, and {@link RenderableVertices#cacheVertices} is true, diff --git a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceClient.java b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceClient.java index 67d82969b..677836c18 100644 --- a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceClient.java +++ b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceClient.java @@ -163,73 +163,6 @@ public Point3D getCameraPosition() { private static final Point3D mutablePosition = new Point3D(); - @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; - minecrafttransportsimulator.baseclasses.RotationMatrix ori = 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. - EntityPlayer player = Minecraft.getMinecraft().player; - Vec3d camOffset = 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; - } - - 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 final Point3D screenProjectionResult = new Point3D(); - @Override public void playBlockBreakSound(Point3D position) { BlockPos pos = new BlockPos(position.x, position.y, position.z); diff --git a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceEventsEntityRendering.java b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceEventsEntityRendering.java index e3fbee092..2849b7ed8 100644 --- a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceEventsEntityRendering.java +++ b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceEventsEntityRendering.java @@ -64,9 +64,6 @@ public class InterfaceEventsEntityRendering { private static final Point3D playerPositionHelper = new Point3D(); private static final Point3D cameraAdjustedPosition = new Point3D(); private static final RotationMatrix cameraAdjustedOrientation = new RotationMatrix(); - public static boolean adjustedCamera; - public static final Point3D projectionCameraPosition = new Point3D(); - public static final RotationMatrix projectionCameraOrientation = new RotationMatrix(); private static final TransformationMatrix cameraAdjustments = new TransformationMatrix(); private static int lastScreenWidth; private static int lastScreenHeight; @@ -85,11 +82,7 @@ public static void onIVCameraSetup(CameraSetup event) { IWrapperPlayer player = WrapperPlayer.getWrapperFor((EntityPlayer) event.getEntity()); cameraAdjustedPosition.set(0, 0, 0); cameraAdjustedOrientation.setToZero(); - adjustedCamera = false; if (CameraSystem.adjustCamera(player, cameraAdjustedPosition, cameraAdjustedOrientation, (float) event.getRenderPartialTicks())) { - projectionCameraPosition.set(cameraAdjustedPosition); - projectionCameraOrientation.set(cameraAdjustedOrientation); - adjustedCamera = true; //Set helper to the current camera position. EntityPlayer mcPlayer = ((WrapperPlayer) player).player; playerPosition.set(mcPlayer.posX, mcPlayer.posY, mcPlayer.posZ); diff --git a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceRender.java b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceRender.java index cf6074cb5..f173180a8 100644 --- a/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceRender.java +++ b/mcinterfaceforge1122/src/main/java/mcinterface1122/InterfaceRender.java @@ -14,9 +14,6 @@ import java.util.Map; import java.util.Set; -import minecrafttransportsimulator.baseclasses.ColorRGB; -import minecrafttransportsimulator.rendering.RenderText; -import net.minecraft.client.gui.FontRenderer; import org.lwjgl.opengl.GL11; import minecrafttransportsimulator.baseclasses.Point3D; @@ -491,20 +488,4 @@ private static void setBlendBright(boolean enabled) { GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); } } - - @Override - public void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale) { - FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer; - float width = fontRenderer.getStringWidth(text) * scale; - float finalX = x; - if (alignment == RenderText.TextAlignment.CENTERED) finalX -= width / 2.0F; - else if (alignment == RenderText.TextAlignment.RIGHT_ALIGNED) finalX -= width; - - GlStateManager.pushMatrix(); - GlStateManager.translate(finalX, y, 0); - GlStateManager.scale(scale, -scale, 1.0F); - fontRenderer.drawStringWithShadow(text, 0, 0, color.rgbInt | 0xFF000000); - GlStateManager.popMatrix(); - } - } diff --git a/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java b/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java index 040f66aa4..e2b78ca87 100644 --- a/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java +++ b/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java @@ -24,7 +24,6 @@ import net.minecraft.block.SoundType; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.ChatScreen; -import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.client.settings.PointOfView; import net.minecraft.client.util.ITooltipFlag; import net.minecraft.fluid.Fluid; @@ -33,7 +32,6 @@ import net.minecraft.util.SoundCategory; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.vector.Vector3d; -import net.minecraft.util.math.vector.Vector3f; import net.minecraft.util.text.Color; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.Style; @@ -179,62 +177,6 @@ public Point3D getCameraPosition() { private static final Point3D mutablePosition = new Point3D(); - @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; - minecrafttransportsimulator.baseclasses.RotationMatrix ori = InterfaceEventsEntityRendering.cameraAdjustedOrientation; - fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; - upX = ori.m01; upY = ori.m11; upZ = ori.m21; - rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; - } else { - ActiveRenderInfo camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - Vector3d camPos = camera.getPosition(); - camX = camPos.x; camY = camPos.y; camZ = camPos.z; - Vector3f look = camera.getLookVector(); - Vector3f up = camera.getUpVector(); - fwdX = look.x(); fwdY = look.y(); fwdZ = look.z(); - upX = up.x(); upY = up.y(); upZ = up.z(); - 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 (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 final Point3D screenProjectionResult = new Point3D(); - @Override public void playBlockBreakSound(Point3D position) { BlockPos pos = new BlockPos(position.x, position.y, position.z); diff --git a/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceRender.java b/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceRender.java index a57a61f7e..d214252c9 100644 --- a/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceRender.java +++ b/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceRender.java @@ -14,9 +14,6 @@ import javax.imageio.ImageIO; -import minecrafttransportsimulator.baseclasses.ColorRGB; -import minecrafttransportsimulator.rendering.RenderText; -import net.minecraft.client.gui.FontRenderer; import org.lwjgl.opengl.GL11; import com.mojang.blaze3d.matrix.MatrixStack; @@ -650,19 +647,4 @@ private BufferData(RenderType type, RenderableData data) { RenderSystem.depthMask(true); RenderSystem.defaultBlendFunc(); }); - - @Override - public void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale) { - FontRenderer fontRenderer = Minecraft.getInstance().font; - float width = fontRenderer.width(text) * scale; - float finalX = x; - if (alignment == RenderText.TextAlignment.CENTERED) finalX -= width / 2.0F; - else if (alignment == RenderText.TextAlignment.RIGHT_ALIGNED) finalX -= width; - - matrixStack.pushPose(); - matrixStack.translate(finalX, y, 0); - matrixStack.scale(scale, -scale, 1.0F); - fontRenderer.drawInBatch(text, 0, 0, color.rgbInt | 0xFF000000, true, matrixStack.last().pose(), renderBuffer, false, 0, 15728880); - matrixStack.popPose(); - } } diff --git a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java index 48ba727d2..d0cd25871 100644 --- a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java +++ b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java @@ -6,9 +6,7 @@ import java.util.Map; import java.util.Map.Entry; -import com.mojang.math.Vector3f; import minecrafttransportsimulator.baseclasses.Point3D; -import minecrafttransportsimulator.baseclasses.RotationMatrix; import minecrafttransportsimulator.entities.instances.EntityFluidTank; import minecrafttransportsimulator.guis.components.AGUIBase; import minecrafttransportsimulator.guis.instances.GUIPackMissing; @@ -24,7 +22,6 @@ import minecrafttransportsimulator.systems.ControlSystem; import minecrafttransportsimulator.systems.LanguageSystem; import net.minecraft.ChatFormatting; -import net.minecraft.client.Camera; import net.minecraft.client.CameraType; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.ChatScreen; @@ -180,61 +177,6 @@ public Point3D getCameraPosition() { private static final Point3D mutablePosition = new Point3D(); - @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 = InterfaceEventsEntityRendering.cameraAdjustedOrientation; - fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; - upX = ori.m01; upY = ori.m11; upZ = ori.m21; - rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; - } else { - Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - Vec3 camPos = camera.getPosition(); - camX = camPos.x(); camY = camPos.y(); camZ = camPos.z(); - Vector3f look = camera.getLookVector(); - Vector3f up = camera.getUpVector(); - 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 (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 final Point3D screenProjectionResult = new Point3D(); - @Override public void playBlockBreakSound(Point3D position) { BlockPos pos = new BlockPos(position.x, position.y, position.z); diff --git a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java index cd9a82cea..73695d63c 100644 --- a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java +++ b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java @@ -30,7 +30,6 @@ import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; -import minecrafttransportsimulator.baseclasses.ColorRGB; import minecrafttransportsimulator.baseclasses.Point3D; import minecrafttransportsimulator.baseclasses.TransformationMatrix; import minecrafttransportsimulator.entities.components.AEntityC_Renderable; @@ -41,11 +40,9 @@ import minecrafttransportsimulator.mcinterface.InterfaceManager; import minecrafttransportsimulator.rendering.GIFParser.GIFImageFrame; import minecrafttransportsimulator.rendering.GIFParser.ParsedGIF; -import minecrafttransportsimulator.rendering.RenderText; import minecrafttransportsimulator.rendering.RenderableData; import minecrafttransportsimulator.systems.ConfigSystem; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderStateShard; @@ -738,20 +735,4 @@ private BufferData(RenderType type, RenderableData data) { RenderSystem.depthMask(true); RenderSystem.defaultBlendFunc(); }); - - - @Override - public void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale) { - Font fontRenderer = Minecraft.getInstance().font; - float width = fontRenderer.width(text) * scale; - float finalX = x; - if (alignment == RenderText.TextAlignment.CENTERED) finalX -= width / 2.0F; - else if (alignment == RenderText.TextAlignment.RIGHT_ALIGNED) finalX -= width; - - matrixStack.pushPose(); - matrixStack.translate(finalX, y, 0); - matrixStack.scale(scale, -scale, 1.0F); - fontRenderer.drawInBatch(text, 0, 0, color.rgbInt | 0xFF000000, true, matrixStack.last().pose(), renderBuffer, false, 0, 15728880); - matrixStack.popPose(); - } } diff --git a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java index 3f3cf39f6..016481e80 100644 --- a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java +++ b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java @@ -6,9 +6,7 @@ import java.util.Map; import java.util.Map.Entry; -import com.mojang.math.Vector3f; import minecrafttransportsimulator.baseclasses.Point3D; -import minecrafttransportsimulator.baseclasses.RotationMatrix; import minecrafttransportsimulator.entities.instances.EntityFluidTank; import minecrafttransportsimulator.guis.components.AGUIBase; import minecrafttransportsimulator.guis.instances.GUIPackMissing; @@ -24,7 +22,6 @@ import minecrafttransportsimulator.systems.ControlSystem; import minecrafttransportsimulator.systems.LanguageSystem; import net.minecraft.ChatFormatting; -import net.minecraft.client.Camera; import net.minecraft.client.CameraType; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.ChatScreen; @@ -183,61 +180,6 @@ public Point3D getCameraPosition() { private static final Point3D mutablePosition = new Point3D(); - @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 = InterfaceEventsEntityRendering.cameraAdjustedOrientation; - fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; - upX = ori.m01; upY = ori.m11; upZ = ori.m21; - rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; - } else { - Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - Vec3 camPos = camera.getPosition(); - camX = camPos.x; camY = camPos.y; camZ = camPos.z; - Vector3f look = camera.getLookVector(); - Vector3f up = camera.getUpVector(); - 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 (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 final Point3D screenProjectionResult = new Point3D(); - @Override public void playBlockBreakSound(Point3D position) { BlockPos pos = new BlockPos(position.x, position.y, position.z); diff --git a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java index d892ad51d..17af9ffb7 100644 --- a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java +++ b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java @@ -29,7 +29,6 @@ import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; -import minecrafttransportsimulator.baseclasses.ColorRGB; import minecrafttransportsimulator.baseclasses.Point3D; import minecrafttransportsimulator.baseclasses.TransformationMatrix; import minecrafttransportsimulator.entities.components.AEntityC_Renderable; @@ -40,11 +39,9 @@ import minecrafttransportsimulator.mcinterface.InterfaceManager; import minecrafttransportsimulator.rendering.GIFParser.GIFImageFrame; import minecrafttransportsimulator.rendering.GIFParser.ParsedGIF; -import minecrafttransportsimulator.rendering.RenderText; import minecrafttransportsimulator.rendering.RenderableData; import minecrafttransportsimulator.systems.ConfigSystem; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderStateShard; @@ -734,20 +731,4 @@ private BufferData(RenderType type, RenderableData data) { RenderSystem.depthMask(true); RenderSystem.defaultBlendFunc(); }); - - - @Override - public void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale) { - Font fontRenderer = Minecraft.getInstance().font; - float width = fontRenderer.width(text) * scale; - float finalX = x; - if (alignment == RenderText.TextAlignment.CENTERED) finalX -= width / 2.0F; - else if (alignment == RenderText.TextAlignment.RIGHT_ALIGNED) finalX -= width; - - matrixStack.pushPose(); - matrixStack.translate(finalX, y, 0); - matrixStack.scale(scale, -scale, 1.0F); - fontRenderer.drawInBatch(text, 0, 0, color.rgbInt | 0xFF000000, true, matrixStack.last().pose(), renderBuffer, false, 0, 15728880); - matrixStack.popPose(); - } } diff --git a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java index 729624e35..97800bd57 100644 --- a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java +++ b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java @@ -7,7 +7,6 @@ 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; @@ -23,7 +22,6 @@ import minecrafttransportsimulator.systems.ControlSystem; import minecrafttransportsimulator.systems.LanguageSystem; import net.minecraft.ChatFormatting; -import net.minecraft.client.Camera; import net.minecraft.client.CameraType; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.ChatScreen; @@ -45,7 +43,6 @@ import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; import net.minecraftforge.registries.ForgeRegistries; -import org.joml.Vector3f; @EventBusSubscriber(Dist.CLIENT) public class InterfaceClient implements IInterfaceClient { @@ -183,61 +180,6 @@ public Point3D getCameraPosition() { private static final Point3D mutablePosition = new Point3D(); - @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 = InterfaceEventsEntityRendering.cameraAdjustedOrientation; - fwdX = ori.m02; fwdY = ori.m12; fwdZ = ori.m22; - upX = ori.m01; upY = ori.m11; upZ = ori.m21; - rgtX = -ori.m00; rgtY = -ori.m10; rgtZ = -ori.m20; - } else { - Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - Vec3 camPos = camera.getPosition(); - camX = camPos.x; camY = camPos.y; camZ = camPos.z; - Vector3f look = camera.getLookVector(); - Vector3f up = camera.getUpVector(); - 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 (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 final Point3D screenProjectionResult = new Point3D(); - @Override public void playBlockBreakSound(Point3D position) { BlockPos pos = BlockPos.containing(position.x, position.y, position.z); diff --git a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java index b38b083d0..1ab2e82b9 100644 --- a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java +++ b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java @@ -16,9 +16,6 @@ import javax.imageio.ImageIO; -import minecrafttransportsimulator.baseclasses.ColorRGB; -import minecrafttransportsimulator.rendering.RenderText; -import net.minecraft.client.gui.Font; import org.joml.Matrix3f; import org.joml.Matrix4f; @@ -720,20 +717,4 @@ private BufferData(RenderType type, RenderableData data) { RenderSystem.depthMask(true); RenderSystem.defaultBlendFunc(); }); - - @Override - public void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale) { - Font fontRenderer = Minecraft.getInstance().font; - float width = fontRenderer.width(text) * scale; - float finalX = x; - if (alignment == RenderText.TextAlignment.CENTERED) finalX -= width / 2.0F; - else if (alignment == RenderText.TextAlignment.RIGHT_ALIGNED) finalX -= width; - - matrixStack.pushPose(); - matrixStack.translate(finalX, y, 0); - matrixStack.scale(scale, -scale, 1.0F); - fontRenderer.drawInBatch(text, 0, 0, color.rgbInt | 0xFF000000, true, matrixStack.last().pose(), renderBuffer, Font.DisplayMode.NORMAL, 0, 15728880); - matrixStack.popPose(); - } - } diff --git a/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceClient.java b/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceClient.java index 93cfe294f..b2539e574 100644 --- a/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceClient.java +++ b/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceClient.java @@ -9,7 +9,6 @@ import java.util.Set; import minecrafttransportsimulator.baseclasses.Point3D; -import minecrafttransportsimulator.baseclasses.RotationMatrix; import minecrafttransportsimulator.entities.instances.EntityFluidTank; import minecrafttransportsimulator.guis.components.AGUIBase; import minecrafttransportsimulator.guis.instances.GUIPackMissing; @@ -33,7 +32,6 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.ChatScreen; import net.minecraft.core.BlockPos; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Style; import net.minecraft.network.chat.TextColor; @@ -49,6 +47,7 @@ import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.fluids.FluidStack; import net.neoforged.fml.common.EventBusSubscriber; +import net.minecraft.core.registries.BuiltInRegistries; @EventBusSubscriber(modid = InterfaceLoader.MODID, value = Dist.CLIENT) public class InterfaceClient implements IInterfaceClient { @@ -238,80 +237,6 @@ public Point3D getCameraPosition() { } 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) { diff --git a/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceRender.java b/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceRender.java index ac3b442b0..627e61196 100644 --- a/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceRender.java +++ b/mcinterfaceneoforge1211/src/main/java/mcinterface1211/InterfaceRender.java @@ -16,9 +16,6 @@ import javax.imageio.ImageIO; -import minecrafttransportsimulator.baseclasses.ColorRGB; -import minecrafttransportsimulator.rendering.RenderText; -import net.minecraft.client.gui.Font; import org.joml.Matrix3f; import org.joml.Matrix4f; import org.joml.Matrix4fStack; @@ -745,20 +742,4 @@ private void close() { RenderSystem.depthMask(true); RenderSystem.defaultBlendFunc(); }); - - @Override - public void drawVanillaText(String text, int x, int y, ColorRGB color, RenderText.TextAlignment alignment, float scale) { - Font fontRenderer = Minecraft.getInstance().font; - float width = fontRenderer.width(text) * scale; - float finalX = x; - if (alignment == RenderText.TextAlignment.CENTERED) finalX -= width / 2.0F; - else if (alignment == RenderText.TextAlignment.RIGHT_ALIGNED) finalX -= width; - - matrixStack.pushPose(); - matrixStack.translate(finalX, y, 0); - matrixStack.scale(scale, -scale, 1.0F); - fontRenderer.drawInBatch(text, 0, 0, color.rgbInt | 0xFF000000, true, matrixStack.last().pose(), renderBuffer, Font.DisplayMode.NORMAL, 0, 15728880); - matrixStack.popPose(); - } - } From 1d7ad3487d029c683ae3d5e782c52b74cd514d23 Mon Sep 17 00:00:00 2001 From: dldev32 Date: Tue, 12 May 2026 21:51:37 +0300 Subject: [PATCH 7/8] Added LegacyCompat for isHeat --- .../entities/components/AEntityF_Multipart.java | 2 +- .../entities/instances/EntityBullet.java | 2 +- .../jsondefs/JSONBullet.java | 6 +++--- .../packloading/LegacyCompatSystem.java | 10 ++++++++++ 4 files changed, 15 insertions(+), 5 deletions(-) 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 efd94b6ae..238fd7704 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/entities/components/AEntityF_Multipart.java +++ b/mccore/src/main/java/minecrafttransportsimulator/entities/components/AEntityF_Multipart.java @@ -360,7 +360,7 @@ 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) { - boolean bulletIsHeat = bullet.definition.bullet.types.contains(BulletType.HEAT) || bullet.definition.bullet.isHeat; + 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); diff --git a/mccore/src/main/java/minecrafttransportsimulator/entities/instances/EntityBullet.java b/mccore/src/main/java/minecrafttransportsimulator/entities/instances/EntityBullet.java index 7916788fe..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,7 @@ 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. - boolean bulletIsHeat = definition.bullet.types.contains(BulletType.HEAT) || definition.bullet.isHeat; + 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); diff --git a/mccore/src/main/java/minecrafttransportsimulator/jsondefs/JSONBullet.java b/mccore/src/main/java/minecrafttransportsimulator/jsondefs/JSONBullet.java index 749950726..192dbff63 100644 --- a/mccore/src/main/java/minecrafttransportsimulator/jsondefs/JSONBullet.java +++ b/mccore/src/main/java/minecrafttransportsimulator/jsondefs/JSONBullet.java @@ -29,9 +29,6 @@ public static class Bullet { @JSONDescription("Set this to true to make this bullet not spawn, but consume ammo.") public boolean isBlank; - @Deprecated - 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; @@ -166,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 { 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; From 09073798dbdec2a2b3dfbb57eaf971e892db6e74 Mon Sep 17 00:00:00 2001 From: dldev32 Date: Wed, 13 May 2026 00:18:27 +0300 Subject: [PATCH 8/8] Fixed empty changes --- .gitattributes | Bin 0 -> 64 bytes .../java/mcinterface1122/InterfaceClient.java | 546 +++--- .../java/mcinterface1165/InterfaceClient.java | 610 +++---- .../java/mcinterface1182/InterfaceClient.java | 600 +++---- .../java/mcinterface1182/InterfaceRender.java | 1476 ++++++++--------- .../java/mcinterface1192/InterfaceClient.java | 606 +++---- .../java/mcinterface1192/InterfaceRender.java | 1468 ++++++++-------- .../java/mcinterface1201/InterfaceClient.java | 606 +++---- .../java/mcinterface1201/InterfaceRender.java | 1440 ++++++++-------- 9 files changed, 3676 insertions(+), 3676 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..7dafd27580b03d296f542f2ef5f165bcda0ad256 GIT binary patch literal 64 zcmezWPm4i;p@boop#n(SG9)sT0_l7PUIs1(Ee1V?ETBjk5F^wm0M+I* 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.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; @@ -141,147 +141,147 @@ 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(); - - @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(); + + @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 b23669ab0..7d2e2e109 100644 --- a/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java +++ b/mcinterfaceforge1165/src/main/java/mcinterface1165/InterfaceClient.java @@ -1,148 +1,148 @@ -package mcinterface1165; - -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.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 minecrafttransportsimulator.baseclasses.Point3D; +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; @@ -157,163 +157,163 @@ 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(); - - @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(); + + @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 1ec142e53..33ff8c04c 100644 --- a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java +++ b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceClient.java @@ -1,148 +1,148 @@ -package mcinterface1182; - -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.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 minecrafttransportsimulator.baseclasses.Point3D; +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; @@ -157,158 +157,158 @@ 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(); - - @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(); + + @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 73695d63c..d7ed09515 100644 --- a/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java +++ b/mcinterfaceforge1182/src/main/java/mcinterface1182/InterfaceRender.java @@ -1,738 +1,738 @@ -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()); - 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); - buffer.endVertex(); - buffer.vertex(stackEntry.pose(), x2, y2, z2); - buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); - //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.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(); - }); -} +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()); + 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); + buffer.endVertex(); + buffer.vertex(stackEntry.pose(), x2, y2, z2); + buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); + //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.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 27de4e2de..c823df4bf 100644 --- a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java +++ b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceClient.java @@ -1,151 +1,151 @@ -package mcinterface1192; - -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.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 minecrafttransportsimulator.baseclasses.Point3D; +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); @@ -160,158 +160,158 @@ 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(); - - @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(); + + @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 17af9ffb7..729f141bb 100644 --- a/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java +++ b/mcinterfaceforge1192/src/main/java/mcinterface1192/InterfaceRender.java @@ -1,734 +1,734 @@ -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()); - 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); - buffer.endVertex(); - buffer.vertex(stackEntry.pose(), x2, y2, z2); - buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); - //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 = 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(); - }); -} +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()); + 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); + buffer.endVertex(); + buffer.vertex(stackEntry.pose(), x2, y2, z2); + buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); + //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 = 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 e9399769d..35148f27e 100644 --- a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java +++ b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceClient.java @@ -1,151 +1,151 @@ -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.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.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); @@ -160,158 +160,158 @@ 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(); - - @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(); + + @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 1ab2e82b9..a50445cf3 100644 --- a/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java +++ b/mcinterfaceforge1201/src/main/java/mcinterface1201/InterfaceRender.java @@ -1,720 +1,720 @@ -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); - buffer.endVertex(); - buffer.vertex(stackEntry.pose(), x2, y2, z2); - buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); - //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(); - }); -} +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); + buffer.endVertex(); + buffer.vertex(stackEntry.pose(), x2, y2, z2); + buffer.color(data.color.red, data.color.green, data.color.blue, data.alpha); + //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(); + }); +}