Fix Rendering Glitches

This commit is contained in:
Kam committed 2026-02-23 23:19:33 -05:00
1 parent 08728efcdd
commit abc34b3f6f
7 files changed
+71 -42

No files matched your search

@@ -59,12 +59,17 @@ public class RenderAbilityDisc extends RenderEnergyAbility {
double motionX = entity.motionX;
double motionY = entity.motionY;
double motionZ = entity.motionZ;
Vec3 motionVec = Vec3.createVectorHelper(motionX, motionY, motionZ);
double speedSq = motionX * motionX + motionY * motionY + motionZ * motionZ;
float motionYaw = MathUtil.getYaw(motionVec);
float motionPitch = MathUtil.getPitch(motionVec);
GL11.glRotatef(motionYaw, 0, 1, 0);
GL11.glRotatef(motionPitch, 1, 0, 0);
// Only apply motion-based facing when speed is meaningful.
// Near-zero motion makes atan2 unstable, producing jittery angles.
if (speedSq > 0.0001) {
Vec3 motionVec = Vec3.createVectorHelper(motionX, motionY, motionZ);
float motionYaw = MathUtil.getYaw(motionVec);
float motionPitch = MathUtil.getPitch(motionVec);
GL11.glRotatef(motionYaw, 0, 1, 0);
GL11.glRotatef(motionPitch, 1, 0, 0);
}
// Apply Y-axis rotation for spin
GL11.glRotatef(disc.getInterpolatedRotationY(partialTicks), 0.0f, 1.0f, 0.0f);
@@ -23,9 +23,6 @@ public class RenderAbilityLaser extends RenderEnergyAbility {
@Override
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
if (shouldSkipInitialActiveRender(entity)) {
return;
}
EntityAbilityLaser laser = (EntityAbilityLaser) entity;
setupRenderState();
@@ -46,12 +46,17 @@ public class RenderAbilityOrb extends RenderEnergyAbility {
double motionX = entity.motionX;
double motionY = entity.motionY;
double motionZ = entity.motionZ;
Vec3 motionVec = Vec3.createVectorHelper(motionX, motionY, motionZ);
double speedSq = motionX * motionX + motionY * motionY + motionZ * motionZ;
float motionYaw = MathUtil.getYaw(motionVec);
float motionPitch = MathUtil.getPitch(motionVec);
GL11.glRotatef(motionYaw, 0, 1, 0);
GL11.glRotatef(motionPitch, 1, 0, 0);
// Only apply motion-based facing when speed is meaningful.
// Near-zero motion makes atan2 unstable, producing jittery angles.
if (speedSq > 0.0001) {
Vec3 motionVec = Vec3.createVectorHelper(motionX, motionY, motionZ);
float motionYaw = MathUtil.getYaw(motionVec);
float motionPitch = MathUtil.getPitch(motionVec);
GL11.glRotatef(motionYaw, 0, 1, 0);
GL11.glRotatef(motionPitch, 1, 0, 0);
}
// Use entity's interpolated rotation for smooth spinning
GL11.glRotatef(orb.getInterpolatedRotationX(partialTicks), 1.0f, 0.0f, 0.0f);
@@ -39,6 +39,9 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
private int returnOwnerNullTicks = 0;
private boolean vertical = false; // false = horizontal (flat), true = vertical (thin edge forward)
// Cached travel direction for smooth rendering when motion is near zero
private float lastTravelYaw = 0.0f;
// Charging state (disc-specific target sizes)
private float targetDiscRadius = 1.0f; // Full radius to grow to during charging
private float targetDiscThickness = 0.2f; // Full thickness to grow to during charging
@@ -60,7 +63,7 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
super(world);
// Initialize base properties
initProjectile(owner, target, x, y, z, discRadius, display, combat, lightning, lifespan, trajectory);
initProjectile(owner, target, x, y, z, 1.0f, display, combat, lightning, lifespan, trajectory);
this.homingData = homing;
@@ -316,7 +319,7 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
float progress = getChargeProgress();
this.discRadius = targetDiscRadius * progress;
this.discThickness = targetDiscThickness * progress;
this.size = this.discRadius; // Base size for interpolation
this.size = 1.0f; // Size acts as a scaling factor; discRadius handles actual dimensions
Entity owner = getOwnerEntity();
if (owner instanceof EntityLivingBase) {
@@ -382,14 +385,15 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
if (isCharging()) {
Entity owner = previewMode ? previewOwner : getOwnerEntity();
if (owner != null) {
return owner.rotationYaw;
lastTravelYaw = owner.rotationYaw;
return lastTravelYaw;
}
}
double speed = Math.sqrt(motionX * motionX + motionZ * motionZ);
if (speed > 0.001) {
return (float) (Math.atan2(-motionX, motionZ) * 180.0 / Math.PI);
lastTravelYaw = (float) (Math.atan2(-motionX, motionZ) * 180.0 / Math.PI);
}
return 0.0f;
return lastTravelYaw;
}
// ==================== NBT ====================
@@ -406,6 +410,12 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
readChargingNBT(nbt);
this.targetDiscRadius = nbt.hasKey("TargetDiscRadius") ? nbt.getFloat("TargetDiscRadius") : this.discRadius;
this.targetDiscThickness = nbt.hasKey("TargetDiscThickness") ? nbt.getFloat("TargetDiscThickness") : this.discThickness;
// Normalize size to 1.0 (scaling factor) — discRadius handles actual dimensions.
// Old saves may have size = discRadius baked in from the quadratic scaling bug.
this.size = 1.0f;
this.renderCurrentSize = 1.0f;
this.prevRenderSize = 1.0f;
}
@Override
@@ -6,6 +6,8 @@ import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLifespanData;
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData;
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyTrajectoryData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
@@ -103,6 +105,13 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
this.endZ = z;
}
@Override
@SideOnly(Side.CLIENT)
public boolean isInRangeToRenderDist(double distance) {
double range = Math.max(128.0D, currentLength * 2.0D + 64.0D);
return distance < range * range;
}
@Override
protected void updateRotation() {
// Laser doesn't rotate
@@ -179,7 +188,13 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
*/
private void updateLaserOriginAndDirection() {
Entity owner = getOwnerEntity();
if (owner == null || !(owner instanceof EntityLivingBase)) return;
if (owner == null || !(owner instanceof EntityLivingBase)) {
// On client, fall back to entity tracker interpolation when owner not loaded
if (worldObj != null && worldObj.isRemote) {
handleClientInterpolation();
}
return;
}
EntityLivingBase livingOwner = (EntityLivingBase) owner;
// Use owner look vector (head/eye aim) for direction updates so hit logic matches visuals.
@@ -801,19 +801,21 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility {
/**
* Handle client-side position interpolation.
* Only moves position during active interpolation (interpSteps > 0).
* Does NOT predict/drift between server updates — the motion values are
* derived from correction deltas, not actual entity velocity, so applying
* them between updates causes entities to fly off in wrong directions.
* This matches vanilla Minecraft behavior for non-player entities.
*/
protected void handleClientInterpolation() {
if (this.interpSteps > 0) {
// For very slow / stationary projectiles, snap directly to avoid jitter
double dx = this.interpTargetX - this.posX;
double dy = this.interpTargetY - this.posY;
double dz = this.interpTargetZ - this.posZ;
double distSq = dx * dx + dy * dy + dz * dz;
if (distSq < 0.04) {
if (distSq < 0.0001) {
this.setPosition(this.interpTargetX, this.interpTargetY, this.interpTargetZ);
this.motionX = this.interpTargetMotionX;
this.motionY = this.interpTargetMotionY;
this.motionZ = this.interpTargetMotionZ;
this.interpSteps = 0;
return;
}
@@ -828,16 +830,6 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility {
this.setPosition(newX, newY, newZ);
this.interpSteps--;
} else {
// Skip motion application for nearly-stationary projectiles to prevent drift
double speedSq = this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ;
if (speedSq < 0.0001) {
return;
}
this.posX += this.motionX;
this.posY += this.motionY;
this.posZ += this.motionZ;
this.setPosition(this.posX, this.posY, this.posZ);
}
}
@@ -1542,9 +1534,7 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility {
protected void setChargeOrigin(Vec3 pos) {
if (pos == null) return;
this.setPosition(pos.xCoord, pos.yCoord, pos.zCoord);
this.prevPosX = pos.xCoord;
this.prevPosY = pos.yCoord;
this.prevPosZ = pos.zCoord;
syncPositionState(pos.xCoord, pos.yCoord, pos.zCoord, true);
this.startX = pos.xCoord;
this.startY = pos.yCoord;
this.startZ = pos.zCoord;
@@ -24,6 +24,10 @@ public class EntityEnergySlicer extends EntityEnergyProjectile {
private float sliceWidth = 3.0f;
private float sliceThickness = 0.15f;
// Cached travel direction for smooth rendering when motion is near zero
private float lastTravelYaw = 0.0f;
private float lastTravelPitch = 0.0f;
public EntityEnergySlicer(World world) {
super(world);
}
@@ -166,13 +170,16 @@ public class EntityEnergySlicer extends EntityEnergyProjectile {
public float getTravelYaw() {
if (isCharging()) {
Entity owner = previewMode ? previewOwner : getOwnerEntity();
if (owner != null) return owner.rotationYaw;
if (owner != null) {
lastTravelYaw = owner.rotationYaw;
return lastTravelYaw;
}
}
double speed = Math.sqrt(motionX * motionX + motionZ * motionZ);
if (speed > 0.001) {
return (float) (Math.atan2(-motionX, motionZ) * 180.0 / Math.PI);
lastTravelYaw = (float) (Math.atan2(-motionX, motionZ) * 180.0 / Math.PI);
}
return 0.0f;
return lastTravelYaw;
}
/**
@@ -181,9 +188,9 @@ public class EntityEnergySlicer extends EntityEnergyProjectile {
public float getTravelPitch() {
double horizSpeed = Math.sqrt(motionX * motionX + motionZ * motionZ);
if (horizSpeed > 0.001 || Math.abs(motionY) > 0.001) {
return (float) (-Math.atan2(motionY, horizSpeed) * 180.0 / Math.PI);
lastTravelPitch = (float) (-Math.atan2(motionY, horizSpeed) * 180.0 / Math.PI);
}
return 0.0f;
return lastTravelPitch;
}
// ==================== GETTERS ====================