Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions src/main/java/gregtech/api/entities/particle/GTParticle.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package gregtech.api.entities.particle;

import net.minecraft.client.particle.Particle;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.entity.Entity;
import net.minecraft.world.World;

/**
* Created with IntelliJ IDEA.
*
* @Author: KilaBash
* @Date: 2021/08/31
* @Description:
*/
public abstract class GTParticle extends Particle {
protected float texturesPerRow = 16F;
protected boolean motionless = false;

public GTParticle(World worldIn, double posXIn, double posYIn, double posZIn) {
super(worldIn, posXIn, posYIn, posZIn);
}

public GTParticle(World worldIn, double xCoordIn, double yCoordIn, double zCoordIn, double xSpeedIn, double ySpeedIn, double zSpeedIn) {
super(worldIn, xCoordIn, yCoordIn, zCoordIn, xSpeedIn, ySpeedIn, zSpeedIn);
}

public void setImmortal() {
this.particleAge = -1;
}

public void setMotionless(boolean motionless) {
this.motionless = motionless;
}

public void setColor(int color) {
this.setRBGColorF((color >> 16 & 255) / 255.0F,
(color >> 8 & 255) / 255.0F,
(color & 255) / 255.0F);
this.setAlphaF((color >> 24 & 255) / 255.0F);
}

public void setScale(float scale) {
this.particleScale = scale;
}

public void setGravity(float gravity) {
this.particleGravity = gravity;
}

public void setTexturesIndex(int particleTextureIndexX, int particleTextureIndexY) {
this.particleTextureIndexX = particleTextureIndexX;
this.particleTextureIndexY = particleTextureIndexY;
}

public void setTexturesPerRow(float texturesPerRow) {
this.texturesPerRow = texturesPerRow;
}

@Override
public void onUpdate() {
if (this.particleAge >= 0 && this.particleAge++ >= this.particleMaxAge) {
this.setExpired();
}

if (!motionless) {
this.prevPosX = this.posX;
this.prevPosY = this.posY;
this.prevPosZ = this.posZ;

this.motionY -= 0.04D * (double)this.particleGravity;
this.move(this.motionX, this.motionY, this.motionZ);
this.motionX *= 0.9800000190734863D;
this.motionY *= 0.9800000190734863D;
this.motionZ *= 0.9800000190734863D;

if (this.onGround) {
this.motionX *= 0.699999988079071D;
this.motionZ *= 0.699999988079071D;
}
}
}

@Override
public void renderParticle(BufferBuilder buffer, Entity entityIn, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ) {
float minU = (float) this.particleTextureIndexX / texturesPerRow;
float maxU = minU + 1F / texturesPerRow;//0.0624375F;
float minV = (float) this.particleTextureIndexY / texturesPerRow;
float maxV = minV + 1F / texturesPerRow;//0.0624375F;
float scale = 0.1F * this.particleScale;

if (this.particleTexture != null) {
minU = this.particleTexture.getMinU();
maxU = this.particleTexture.getMaxU();
minV = this.particleTexture.getMinV();
maxV = this.particleTexture.getMaxV();
}

float renderX = (float) (this.prevPosX + (this.posX - this.prevPosX) * (double) partialTicks - interpPosX);
float renderY = (float) (this.prevPosY + (this.posY - this.prevPosY) * (double) partialTicks - interpPosY);
float renderZ = (float) (this.prevPosZ + (this.posZ - this.prevPosZ) * (double) partialTicks - interpPosZ);
int brightnessForRender = this.getBrightnessForRender(partialTicks);
int j = brightnessForRender >> 16 & 65535;
int k = brightnessForRender & 65535;
buffer.pos(renderX - rotationX * scale - rotationXY * scale, renderY - rotationZ * scale, (renderZ - rotationYZ * scale - rotationXZ * scale)).tex(maxU, maxV).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
buffer.pos(renderX - rotationX * scale + rotationXY * scale, renderY + rotationZ * scale, (renderZ - rotationYZ * scale + rotationXZ * scale)).tex(maxU, minV).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
buffer.pos(renderX + rotationX * scale + rotationXY * scale, (renderY + rotationZ * scale), (renderZ + rotationYZ * scale + rotationXZ * scale)).tex(minU, minV).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
buffer.pos(renderX + rotationX * scale - rotationXY * scale, (renderY - rotationZ * scale), (renderZ + rotationYZ * scale - rotationXZ * scale)).tex(minU, maxV).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
}

/***
* Do not create an instance here; use a static instance plz
*/
public IGTParticleHandler getGLHandler() {
return IGTParticleHandler.DEFAULT_FX_HANDLER;
}
}
218 changes: 218 additions & 0 deletions src/main/java/gregtech/api/entities/particle/GTParticleManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package gregtech.api.entities.particle;

import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.ParticleManager;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.entity.Entity;
import net.minecraft.util.ReportedException;
import net.minecraft.util.Tuple;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.lwjgl.opengl.GL11;

import java.util.*;
import java.util.concurrent.Callable;

/**
* Created with IntelliJ IDEA.
*
* @Author: KilaBash
* @Date: 2021/08/31
* @Description: ParticleManger register, spawn, efficient rendering, update our custom particles.
*/
@SideOnly(Side.CLIENT)
@Mod.EventBusSubscriber(Side.CLIENT)
public class GTParticleManager {
public static GTParticleManager instance = new GTParticleManager();
private static World currentWorld = null;
private static ParticleManager particleManager = Minecraft.getMinecraft().effectRenderer;

private final Map<IGTParticleHandler, ArrayDeque<GTParticle>[]> renderQueue = new HashMap<>();
private final Queue<Tuple<IGTParticleHandler, GTParticle>> newParticleQueue = new ArrayDeque<>();

private GTParticleManager() { }

public void addEffect(Particle... particles) {
if (particleManager == null) {
particleManager = Minecraft.getMinecraft().effectRenderer;
}
for (Particle particle : particles) {
if (particle instanceof GTParticle && ((GTParticle) particle).getGLHandler() != null) {
newParticleQueue.add(new Tuple<>(((GTParticle) particle).getGLHandler(), (GTParticle)particle));
} else {
particleManager.addEffect(particle);
}
}
}

public void updateEffects() {
for (int i = 0; i < 4; ++i) {
updateEffectLayer(i);
}
if (!newParticleQueue.isEmpty()) {
for (Tuple<IGTParticleHandler, GTParticle> handlerParticle = newParticleQueue.poll(); handlerParticle != null; handlerParticle = newParticleQueue.poll()) {
IGTParticleHandler handler = handlerParticle.getFirst();
GTParticle particle = handlerParticle.getSecond();
if (!renderQueue.containsKey(handler)) {
renderQueue.put(handler, new ArrayDeque[]{new ArrayDeque(), new ArrayDeque(), new ArrayDeque(), new ArrayDeque()});
}
ArrayDeque<GTParticle>[] arrayDeques = renderQueue.get(handler);
int layer = particle.getFXLayer();
if (arrayDeques[layer].size() > 6000) {
arrayDeques[layer].removeFirst().setExpired();
}
arrayDeques[layer].add(particle);
}
}
}

private void updateEffectLayer(int layer) {
for (ArrayDeque<GTParticle>[] array : renderQueue.values()) {
ArrayDeque<GTParticle> queue = array[layer];
if (!queue.isEmpty()) {
Iterator<GTParticle> iterator = queue.iterator();
while (iterator.hasNext()) {
Particle particle = iterator.next();
tickParticle(particle);
if (!particle.isAlive()) {
iterator.remove();
}
}
}
}
}

public void clearAllEffects() {
renderQueue.clear();
newParticleQueue.clear();
}

private void tickParticle(final Particle particle) {
try {
particle.onUpdate();
}
catch (Throwable throwable) {
CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Ticking Particle");
CrashReportCategory crashreportcategory = crashreport.makeCategory("Particle being ticked");
final int i = particle.getFXLayer();
crashreportcategory.addCrashSection("Particle", new Callable<String>() {
public String call() {
return particle.toString();
}
});
crashreportcategory.addCrashSection("Particle Type", new Callable<String>() {
public String call() {
return i == 0 ? "MISC_TEXTURE" : (i == 1 ? "TERRAIN_TEXTURE" : (i == 3 ? "ENTITY_PARTICLE_TEXTURE" : "Unknown - " + i));
}
});
throw new ReportedException(crashreport);
}
}

public void renderParticles(Entity entityIn, float partialTicks) {
float rotationX = ActiveRenderInfo.getRotationX();
float rotationZ = ActiveRenderInfo.getRotationZ();
float rotationYZ = ActiveRenderInfo.getRotationYZ();
float rotationXY = ActiveRenderInfo.getRotationXY();
float rotationXZ = ActiveRenderInfo.getRotationXZ();
Particle.interpPosX = entityIn.lastTickPosX + (entityIn.posX - entityIn.lastTickPosX) * (double) partialTicks;
Particle.interpPosY = entityIn.lastTickPosY + (entityIn.posY - entityIn.lastTickPosY) * (double) partialTicks;
Particle.interpPosZ = entityIn.lastTickPosZ + (entityIn.posZ - entityIn.lastTickPosZ) * (double) partialTicks;
Particle.cameraViewDir = entityIn.getLook(partialTicks);
GlStateManager.enableBlend();
GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
GlStateManager.alphaFunc(GL11.GL_GREATER, 0);

Tessellator tessellator = Tessellator.getInstance();
GlStateManager.disableLighting();

renderGlParticlesInLayer(tessellator, entityIn, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ);



GlStateManager.depthMask(true);
GlStateManager.disableBlend();
GlStateManager.alphaFunc(GL11.GL_GREATER, 0.1F);
}

private void renderGlParticlesInLayer(Tessellator tessellator, Entity entityIn, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ) {
for (int layer = 0; layer < 4; layer++) {
for (IGTParticleHandler handler : renderQueue.keySet()) {
ArrayDeque<GTParticle> particles = renderQueue.get(handler)[layer];
if (particles.isEmpty()) continue;
BufferBuilder buffer = tessellator.getBuffer();
handler.preDraw(layer, buffer, entityIn, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ);
for (final Particle particle : particles) {
try {
particle.renderParticle(buffer, entityIn, partialTicks, rotationX, rotationXZ, rotationZ, rotationYZ, rotationXY);
}
catch (Throwable throwable) {
CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Rendering Particle");
CrashReportCategory crashreportcategory = crashreport.makeCategory("Particle being rendered");
crashreportcategory.addCrashSection("Particle", new Callable<String>() {
public String call() {
return particle.toString();
}
});
throw new ReportedException(crashreport);
}
}
handler.postDraw(layer, buffer, tessellator);
}
}
}

public String getStatistics() {
int g = 0;
for (ArrayDeque<GTParticle>[] array : renderQueue.values()) {
for (ArrayDeque<GTParticle> queue : array) {
g += queue.size();
}
}
return " GLFX: " + g;
}

@SubscribeEvent
public static void clientTick(TickEvent.ClientTickEvent event) {
Minecraft mc = Minecraft.getMinecraft();
if (event.phase != TickEvent.Phase.END || mc.isGamePaused()) {
return;
}

if (currentWorld != mc.world) {
currentWorld = mc.world;
instance.clearAllEffects();
}

if (mc.world != null) {
instance.updateEffects();
}
}

@SubscribeEvent
public static void renderWorld(RenderWorldLastEvent event) {
instance.renderParticles(Minecraft.getMinecraft().player, event.getPartialTicks());
}

@SubscribeEvent
public static void debugOverlay(RenderGameOverlayEvent.Text event) {
if (event.getLeft().size() >= 5 && instance != null) {
String particleTxt = event.getLeft().get(4);
particleTxt += "." + TextFormatting.GOLD + " BC-P: " + instance.getStatistics();
event.getLeft().set(4, particleTxt);
}
}
}
Loading