mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-11 23:07:37 +00:00
332 lines
12 KiB
Java
332 lines
12 KiB
Java
package kamkeel.npcs.client.renderer;
|
|
|
|
import cpw.mods.fml.relauncher.Side;
|
|
import cpw.mods.fml.relauncher.SideOnly;
|
|
import kamkeel.npcs.client.renderer.lightning.AttachedLightningRenderer;
|
|
import kamkeel.npcs.entity.EntityAbilityLaser;
|
|
import net.minecraft.client.renderer.Tessellator;
|
|
import net.minecraft.entity.Entity;
|
|
import org.lwjgl.opengl.GL11;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* Renders the AbilityLaser entity as a rectangular beam with clear inner/outer colors.
|
|
* <p>
|
|
* Design inspired by LouisXIV's energy rendering system.
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
public class RenderAbilityLaser extends RenderEnergyAbility {
|
|
|
|
private final Map<Integer, AttachedLightningRenderer.LightningState> tipLightningStates = new HashMap<>();
|
|
|
|
@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();
|
|
|
|
if (laser.isCharging()) {
|
|
renderChargingOrb(laser, x, y, z, partialTicks);
|
|
restoreRenderState();
|
|
return;
|
|
}
|
|
|
|
// Get laser parameters
|
|
double startX = laser.getStartX();
|
|
double startY = laser.getStartY();
|
|
double startZ = laser.getStartZ();
|
|
double endX = laser.getEndX();
|
|
double endY = laser.getEndY();
|
|
double endZ = laser.getEndZ();
|
|
float width = laser.getLaserWidth();
|
|
float alpha = laser.getLingerAlpha();
|
|
|
|
// Calculate render offset (entity position is used as reference)
|
|
double offsetX = x - laser.posX;
|
|
double offsetY = y - laser.posY;
|
|
double offsetZ = z - laser.posZ;
|
|
|
|
// Render start relative to camera
|
|
double renderStartX = startX + offsetX;
|
|
double renderStartY = startY + offsetY;
|
|
double renderStartZ = startZ + offsetZ;
|
|
double renderEndX = endX + offsetX;
|
|
double renderEndY = endY + offsetY;
|
|
double renderEndZ = endZ + offsetZ;
|
|
|
|
// Calculate perpendicular vectors for the beam rectangle
|
|
double dx = renderEndX - renderStartX;
|
|
double dy = renderEndY - renderStartY;
|
|
double dz = renderEndZ - renderStartZ;
|
|
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
if (len < 0.001) {
|
|
restoreRenderState();
|
|
return;
|
|
}
|
|
|
|
// Normalize direction
|
|
dx /= len;
|
|
dy /= len;
|
|
dz /= len;
|
|
|
|
// Calculate perpendicular vectors (one horizontal, one vertical relative to beam)
|
|
double upX = 0, upY = 1, upZ = 0;
|
|
if (Math.abs(dy) > 0.9) {
|
|
upX = 1;
|
|
upY = 0;
|
|
upZ = 0;
|
|
}
|
|
|
|
// Cross product for horizontal perpendicular
|
|
double horzX = dy * upZ - dz * upY;
|
|
double horzY = dz * upX - dx * upZ;
|
|
double horzZ = dx * upY - dy * upX;
|
|
double horzLen = Math.sqrt(horzX * horzX + horzY * horzY + horzZ * horzZ);
|
|
if (horzLen > 0) {
|
|
horzX /= horzLen;
|
|
horzY /= horzLen;
|
|
horzZ /= horzLen;
|
|
}
|
|
|
|
// Cross product for vertical perpendicular
|
|
double vertX = dy * horzZ - dz * horzY;
|
|
double vertY = dz * horzX - dx * horzZ;
|
|
double vertZ = dx * horzY - dy * horzX;
|
|
|
|
// Inner scale defines the core width
|
|
float innerScale = 0.6f;
|
|
float innerWidth = width * innerScale;
|
|
|
|
// Render outer rectangle (larger, outer color, translucent) - only if enabled
|
|
// outerColorWidth is an additive offset from inner width
|
|
if (laser.isOuterColorEnabled()) {
|
|
float outerWidth = innerWidth + laser.getOuterColorWidth() * width;
|
|
GL11.glDepthMask(false);
|
|
renderBeamRectangle(renderStartX, renderStartY, renderStartZ,
|
|
renderEndX, renderEndY, renderEndZ,
|
|
horzX, horzY, horzZ, vertX, vertY, vertZ,
|
|
outerWidth, laser.getOuterColor(), alpha * 0.4f);
|
|
|
|
// Render middle layer (halfway between inner and outer)
|
|
float midWidth = innerWidth + (outerWidth - innerWidth) * 0.5f;
|
|
renderBeamRectangle(renderStartX, renderStartY, renderStartZ,
|
|
renderEndX, renderEndY, renderEndZ,
|
|
horzX, horzY, horzZ, vertX, vertY, vertZ,
|
|
midWidth, laser.getOuterColor(), alpha * 0.7f);
|
|
GL11.glDepthMask(true);
|
|
}
|
|
|
|
// Render inner core (solid)
|
|
renderBeamRectangle(renderStartX, renderStartY, renderStartZ,
|
|
renderEndX, renderEndY, renderEndZ,
|
|
horzX, horzY, horzZ, vertX, vertY, vertZ,
|
|
innerWidth, laser.getInnerColor(), alpha * laser.getInnerAlpha());
|
|
|
|
// Render lightning along the laser beam
|
|
if (laser.hasLightningEffect() && laser.getCurrentLength() > 0.1f) {
|
|
renderLaserLightning(laser, renderStartX, renderStartY, renderStartZ,
|
|
renderEndX, renderEndY, renderEndZ, width);
|
|
}
|
|
|
|
restoreRenderState();
|
|
}
|
|
|
|
private void renderChargingOrb(EntityAbilityLaser laser, double x, double y, double z, float partialTicks) {
|
|
float headSize = laser.getLaserWidth() * 1.5f;
|
|
float chargeProgress = laser.getInterpolatedChargeProgress(partialTicks);
|
|
float size = headSize * chargeProgress;
|
|
|
|
if (size <= 0.01f) return;
|
|
|
|
int innerColor = laser.getInnerColor();
|
|
int outerColor = laser.getOuterColor();
|
|
|
|
GL11.glPushMatrix();
|
|
GL11.glTranslated(x, y, z);
|
|
|
|
// Pulsing effect
|
|
float pulseTime = laser.ticksExisted + partialTicks;
|
|
float scaleModifier = (float) Math.sin(pulseTime * 0.2f) * 0.08f;
|
|
float scale = size * (1.0f + scaleModifier);
|
|
|
|
// Render lightning during charging if enabled
|
|
if (laser.hasLightningEffect()) {
|
|
renderAttachedLightning(laser, 0.6f * 0.5f, scale);
|
|
}
|
|
|
|
GL11.glPushMatrix();
|
|
GL11.glScalef(scale, scale, scale);
|
|
|
|
// Inner scale defines the core size
|
|
float innerScale = 0.6f;
|
|
|
|
// Render outer glow only if enabled
|
|
if (laser.isOuterColorEnabled()) {
|
|
float outerScale = innerScale + laser.getOuterColorWidth();
|
|
float outerAlpha = laser.getOuterColorAlpha();
|
|
GL11.glDepthMask(false);
|
|
GL11.glPushMatrix();
|
|
GL11.glScalef(outerScale, outerScale, outerScale);
|
|
renderCube(outerColor, outerAlpha, 0.5f);
|
|
GL11.glPopMatrix();
|
|
GL11.glDepthMask(true);
|
|
}
|
|
|
|
// Render inner core
|
|
GL11.glScalef(innerScale, innerScale, innerScale);
|
|
renderCube(innerColor, laser.getInnerAlpha(), 0.5f);
|
|
|
|
GL11.glPopMatrix();
|
|
GL11.glPopMatrix();
|
|
}
|
|
|
|
/**
|
|
* Render lightning effects along the laser beam at multiple points.
|
|
*/
|
|
private void renderLaserLightning(EntityAbilityLaser laser,
|
|
double startX, double startY, double startZ,
|
|
double endX, double endY, double endZ, float width) {
|
|
AttachedLightningRenderer.LightningState state = getLightningState(laser);
|
|
|
|
float density = laser.getLightningDensity();
|
|
// Lightning radius extends outward from inner width
|
|
float innerRadius = width * 0.6f * 0.5f;
|
|
float radius = innerRadius + laser.getLightningRadius() * width;
|
|
int outerColor = laser.getOuterColor();
|
|
int innerColor = laser.getInnerColor();
|
|
int fadeTime = laser.getLightningFadeTime();
|
|
|
|
// Render lightning at multiple points along the laser
|
|
// Start, middle, and end points
|
|
double midX = (startX + endX) * 0.5;
|
|
double midY = (startY + endY) * 0.5;
|
|
double midZ = (startZ + endZ) * 0.5;
|
|
|
|
GL11.glPushMatrix();
|
|
GL11.glTranslated(midX, midY, midZ);
|
|
state.update(density, radius, outerColor, innerColor, fadeTime);
|
|
state.render();
|
|
GL11.glPopMatrix();
|
|
|
|
// Also render at the tip
|
|
GL11.glPushMatrix();
|
|
GL11.glTranslated(endX, endY, endZ);
|
|
// Use a secondary state for the tip
|
|
AttachedLightningRenderer.LightningState tipState = getTipLightningState(laser);
|
|
tipState.update(density * 0.7f, radius * 0.8f, outerColor, innerColor, fadeTime);
|
|
tipState.render();
|
|
GL11.glPopMatrix();
|
|
}
|
|
|
|
|
|
/**
|
|
* Get or create the tip lightning state for the laser.
|
|
* Persisted per entity ID so arcs accumulate and fade properly across frames.
|
|
*/
|
|
private AttachedLightningRenderer.LightningState getTipLightningState(EntityAbilityLaser laser) {
|
|
int id = laser.getEntityId();
|
|
AttachedLightningRenderer.LightningState state = tipLightningStates.get(id);
|
|
if (state == null) {
|
|
state = new AttachedLightningRenderer.LightningState();
|
|
tipLightningStates.put(id, state);
|
|
}
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* Render a beam as a 3D rectangular prism (box along a line).
|
|
*/
|
|
private void renderBeamRectangle(double x1, double y1, double z1,
|
|
double x2, double y2, double z2,
|
|
double horzX, double horzY, double horzZ,
|
|
double vertX, double vertY, double vertZ,
|
|
float width, int color, float alpha) {
|
|
float[] rgb = extractRGB(color);
|
|
float r = rgb[0], g = rgb[1], b = rgb[2];
|
|
|
|
float halfWidth = width * 0.5f;
|
|
|
|
// Calculate 8 corners of the box
|
|
// Start corners
|
|
double s1x = x1 - horzX * halfWidth - vertX * halfWidth;
|
|
double s1y = y1 - horzY * halfWidth - vertY * halfWidth;
|
|
double s1z = z1 - horzZ * halfWidth - vertZ * halfWidth;
|
|
|
|
double s2x = x1 + horzX * halfWidth - vertX * halfWidth;
|
|
double s2y = y1 + horzY * halfWidth - vertY * halfWidth;
|
|
double s2z = z1 + horzZ * halfWidth - vertZ * halfWidth;
|
|
|
|
double s3x = x1 + horzX * halfWidth + vertX * halfWidth;
|
|
double s3y = y1 + horzY * halfWidth + vertY * halfWidth;
|
|
double s3z = z1 + horzZ * halfWidth + vertZ * halfWidth;
|
|
|
|
double s4x = x1 - horzX * halfWidth + vertX * halfWidth;
|
|
double s4y = y1 - horzY * halfWidth + vertY * halfWidth;
|
|
double s4z = z1 - horzZ * halfWidth + vertZ * halfWidth;
|
|
|
|
// End corners
|
|
double e1x = x2 - horzX * halfWidth - vertX * halfWidth;
|
|
double e1y = y2 - horzY * halfWidth - vertY * halfWidth;
|
|
double e1z = z2 - horzZ * halfWidth - vertZ * halfWidth;
|
|
|
|
double e2x = x2 + horzX * halfWidth - vertX * halfWidth;
|
|
double e2y = y2 + horzY * halfWidth - vertY * halfWidth;
|
|
double e2z = z2 + horzZ * halfWidth - vertZ * halfWidth;
|
|
|
|
double e3x = x2 + horzX * halfWidth + vertX * halfWidth;
|
|
double e3y = y2 + horzY * halfWidth + vertY * halfWidth;
|
|
double e3z = z2 + horzZ * halfWidth + vertZ * halfWidth;
|
|
|
|
double e4x = x2 - horzX * halfWidth + vertX * halfWidth;
|
|
double e4y = y2 - horzY * halfWidth + vertY * halfWidth;
|
|
double e4z = z2 - horzZ * halfWidth + vertZ * halfWidth;
|
|
|
|
Tessellator tess = Tessellator.instance;
|
|
tess.startDrawingQuads();
|
|
tess.setColorRGBA_F(r, g, b, alpha);
|
|
|
|
// Bottom face
|
|
tess.addVertex(s1x, s1y, s1z);
|
|
tess.addVertex(s2x, s2y, s2z);
|
|
tess.addVertex(e2x, e2y, e2z);
|
|
tess.addVertex(e1x, e1y, e1z);
|
|
|
|
// Top face
|
|
tess.addVertex(s4x, s4y, s4z);
|
|
tess.addVertex(e4x, e4y, e4z);
|
|
tess.addVertex(e3x, e3y, e3z);
|
|
tess.addVertex(s3x, s3y, s3z);
|
|
|
|
// Left face
|
|
tess.addVertex(s1x, s1y, s1z);
|
|
tess.addVertex(e1x, e1y, e1z);
|
|
tess.addVertex(e4x, e4y, e4z);
|
|
tess.addVertex(s4x, s4y, s4z);
|
|
|
|
// Right face
|
|
tess.addVertex(s2x, s2y, s2z);
|
|
tess.addVertex(s3x, s3y, s3z);
|
|
tess.addVertex(e3x, e3y, e3z);
|
|
tess.addVertex(e2x, e2y, e2z);
|
|
|
|
// Start cap
|
|
tess.addVertex(s1x, s1y, s1z);
|
|
tess.addVertex(s4x, s4y, s4z);
|
|
tess.addVertex(s3x, s3y, s3z);
|
|
tess.addVertex(s2x, s2y, s2z);
|
|
|
|
// End cap
|
|
tess.addVertex(e1x, e1y, e1z);
|
|
tess.addVertex(e2x, e2y, e2z);
|
|
tess.addVertex(e3x, e3y, e3z);
|
|
tess.addVertex(e4x, e4y, e4z);
|
|
|
|
tess.draw();
|
|
}
|
|
}
|