package electroblob.wizardry.client.particle; import org.lwjgl.opengl.GL11; import net.minecraft.client.Minecraft; import net.minecraft.client.particle.Particle; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.entity.Entity; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; /** * Abstract superclass for all particles that use custom textures. This is intended to centralise as much code as * possible; all subclasses need to do is to define the texture to use, how the frames are arranged (and which to * choose), and any properties like gravity and collisions. * * @author Electroblob * @since Wizardry 1.2 */ @SideOnly(Side.CLIENT) public abstract class ParticleCustomTexture extends Particle { /** True if the particle always renders at full brightness. Defaults to false. */ protected boolean fullBrightness = false; public ParticleCustomTexture(World world, double x, double y, double z, double vx, double vy, double vz){ super(world, x, y, z, vx, vy, vz); this.motionX = vx; this.motionY = vy; this.motionZ = vz; this.init(); } public ParticleCustomTexture(World world, double x, double y, double z, double vx, double vy, double vz, int maxAge){ this(world, x, y, z, vx, vy, vz); this.particleMaxAge = maxAge; } /** * Called from both constructors to set constants, avoiding duplicate code. Common fields to set here include: * particleScale, particleGravity, canCollide, fullBrightness and setting the texture index. */ public abstract void init(); /** * Returns a ResourceLocation for the particle's texture sheet. Do not create a new ResourceLocation in this method, * only return a constant. */ public abstract ResourceLocation getTexture(); /** Returns how many 'frames' there are in the x direction on the texture. */ protected abstract int getXFrames(); /** Returns how many 'frames' there are in the y direction on the texture. */ protected abstract int getYFrames(); /* There are 4 layers of particles, specified as 0-3 by the method below. - Layer 0 causes the normal particles.png * to be bound to the render engine for normal particles. - Layer 1 causes the block textures to be bound to the * render engine for digging fx and falling fx. - Layer 2 causes the item textures to be bound to the render engine * for tool breaking fx, snowballpoofs, slime particles, etc. - Layer 3 is not used in vanilla minecraft and was * presumably added by forge for exactly this reason. This means no texture is bound by vanilla minecraft, meaning * you are free to do as you wish without possibly overwriting vanilla particles. Mod particles won't be overwritten * anyway since they bind their own textures. It is of course important to bind the texture every time you render a * custom particle, but I don't see how you could do it any other way, since you don't have access to * EffectRenderer. */ @Override public int getFXLayer(){ // This can only be 0-3 or it will cause an ArrayIndexOutOfBoundsException in EffectRenderer. return 3; } @Override public void setParticleTextureIndex(int index){ this.particleTextureIndexX = index % getXFrames(); this.particleTextureIndexY = index / getYFrames(); } // Overridden to fix the bug with vanilla that makes particles frictionless. (y != y... seriously, Mojang?) // TESTME: Probably no longer necessary. // @Override // public void move(double x, double y, double z){ // // double d0 = y; // // if (this.canCollide) // { // List list = this.world.getCollisionBoxes((Entity)null, this.getBoundingBox().addCoord(x, y, z)); // // for (AxisAlignedBB axisalignedbb : list) // { // y = axisalignedbb.calculateYOffset(this.getBoundingBox(), y); // } // // this.setBoundingBox(this.getBoundingBox().offset(0.0D, y, 0.0D)); // // for (AxisAlignedBB axisalignedbb1 : list) // { // x = axisalignedbb1.calculateXOffset(this.getBoundingBox(), x); // } // // this.setBoundingBox(this.getBoundingBox().offset(x, 0.0D, 0.0D)); // // for (AxisAlignedBB axisalignedbb2 : list) // { // z = axisalignedbb2.calculateZOffset(this.getBoundingBox(), z); // } // // this.setBoundingBox(this.getBoundingBox().offset(0.0D, 0.0D, z)); // } // else // { // this.setBoundingBox(this.getBoundingBox().offset(x, y, z)); // } // // this.resetPositionToBB(); // this.onGround = d0 != y && d0 < 0.0D; // // /* Can never be true! - But this doesn't seem to make any difference anyway. // if (x != x) // { // this.motionX = 0.0D; // } // // if (z != z) // { // this.motionZ = 0.0D; // } // */ // } // Overridden to bind the new texture. I think this can be done with TextureAtlasSprite, but this works as it is // so I'm not changing it for the time being. @Override public void renderParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ){ GlStateManager.pushMatrix(); GlStateManager.pushAttrib(); this.applyGLStateChanges(); // This stuff does the shading. It vanilla does this later on for each point, but this also seems to work. int brightness = this.getBrightnessForRender(partialTicks); int lightmapX = brightness % 65536; int lightmapY = brightness / 65536; OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)lightmapX / 1.0F, (float)lightmapY / 1.0F); RenderHelper.disableStandardItemLighting(); Minecraft.getMinecraft().getTextureManager().bindTexture(getTexture()); buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); float u1 = (float)this.particleTextureIndexX / (float)getXFrames(); float u2 = u1 + 1.0f / getXFrames(); float v1 = (float)this.particleTextureIndexY / (float)getYFrames(); float v2 = v1 + 1.0f / getYFrames(); float scale = 0.1F * this.particleScale; // I'm pretty sure these were always static. Particle.interpPosX = viewer.lastTickPosX + (viewer.posX - viewer.lastTickPosX) * (double)partialTicks; Particle.interpPosY = viewer.lastTickPosY + (viewer.posY - viewer.lastTickPosY) * (double)partialTicks; Particle.interpPosZ = viewer.lastTickPosZ + (viewer.posZ - viewer.lastTickPosZ) * (double)partialTicks; float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX); float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY); float z = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ); buffer.pos((double)(x - rotationX * scale - rotationXY * scale), (double)(y - rotationZ * scale), (double)(z - rotationYZ * scale - rotationXZ * scale)).tex(u2, v2) .color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex(); buffer.pos((double)(x - rotationX * scale + rotationXY * scale), (double)(y + rotationZ * scale), (double)(z - rotationYZ * scale + rotationXZ * scale)).tex(u2, v1) .color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex(); buffer.pos((double)(x + rotationX * scale + rotationXY * scale), (double)(y + rotationZ * scale), (double)(z + rotationYZ * scale + rotationXZ * scale)).tex(u1, v1) .color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex(); buffer.pos((double)(x + rotationX * scale - rotationXY * scale), (double)(y - rotationZ * scale), (double)(z + rotationYZ * scale - rotationXZ * scale)).tex(u1, v2) .color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex(); ; Tessellator.getInstance().draw(); this.undoGLStateChanges(); GlStateManager.popAttrib(); GlStateManager.popMatrix(); } /** * Override to add any GL state changes, like blending. Does nothing by default. State changes should be done * using GLStateManager, not using GL11 directly (as is the case with all rendering code now). */ public void applyGLStateChanges(){ } /** * Override to undo any GL state changes, like blending. Does nothing by default. State changes should be done * using GLStateManager, not using GL11 directly (as is the case with all rendering code now). */ public void undoGLStateChanges(){ } @Override public int getBrightnessForRender(float partialTick){ return fullBrightness ? 15728880 : super.getBrightnessForRender(partialTick); } }