package game.renderer.particle; import java.util.List; import java.util.Map; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import game.block.Block; import game.entity.Entity; import game.material.Material; import game.renderer.ActiveRenderInfo; import game.renderer.DefaultVertexFormats; import game.renderer.GlState; import game.renderer.RenderBuffer; import game.renderer.Tessellator; import game.renderer.texture.TextureManager; import game.renderer.texture.TextureMap; import game.rng.Random; import game.util.ExtMath; import game.world.BlockPos; import game.world.Facing; import game.world.State; import game.world.World; public class EffectRenderer { public static final String particleTextures = "textures/world/particles.png"; /** Reference to the World object. */ protected World worldObj; private List[][] fxLayers = new List[4][]; private List particleEmitters = Lists.newArrayList(); private TextureManager renderer; /** RNG. */ private Random rand = new Random(); private Map particleTypes = Maps.newHashMap(); public EffectRenderer(World worldIn, TextureManager rendererIn) { this.worldObj = worldIn; this.renderer = rendererIn; for (int i = 0; i < 4; ++i) { this.fxLayers[i] = new List[2]; for (int j = 0; j < 2; ++j) { this.fxLayers[i][j] = Lists.newArrayList(); } } this.registerVanillaParticles(); } private void registerVanillaParticles() { this.registerParticle(ParticleType.EXPLOSION_NORMAL.getParticleID(), new EntityExplodeFX.Factory()); this.registerParticle(ParticleType.WATER_BUBBLE.getParticleID(), new EntityBubbleFX.Factory()); this.registerParticle(ParticleType.WATER_SPLASH.getParticleID(), new EntitySplashFX.Factory()); this.registerParticle(ParticleType.WATER_WAKE.getParticleID(), new EntityFishWakeFX.Factory()); this.registerParticle(ParticleType.WATER_DROP.getParticleID(), new EntityDownfallFX.RainFactory()); this.registerParticle(ParticleType.SUSPENDED.getParticleID(), new EntitySuspendFX.Factory()); this.registerParticle(ParticleType.SUSPENDED_DEPTH.getParticleID(), new EntityAuraFX.SuspendFactory()); this.registerParticle(ParticleType.CRIT.getParticleID(), new EntityCrit2FX.Factory()); this.registerParticle(ParticleType.CRIT_MAGIC.getParticleID(), new EntityCrit2FX.MagicFactory()); this.registerParticle(ParticleType.SMOKE_NORMAL.getParticleID(), new EntitySmokeFX.Factory()); this.registerParticle(ParticleType.SMOKE_LARGE.getParticleID(), new EntityCritFX.Factory()); this.registerParticle(ParticleType.SPELL.getParticleID(), new EntitySpellParticleFX.Factory()); this.registerParticle(ParticleType.SPELL_INSTANT.getParticleID(), new EntitySpellParticleFX.InstantFactory()); this.registerParticle(ParticleType.SPELL_MOB.getParticleID(), new EntitySpellParticleFX.MobFactory()); this.registerParticle(ParticleType.SPELL_MOB_AMBIENT.getParticleID(), new EntitySpellParticleFX.AmbientMobFactory()); this.registerParticle(ParticleType.SPELL_WITCH.getParticleID(), new EntitySpellParticleFX.WitchFactory()); this.registerParticle(ParticleType.DRIP_WATER.getParticleID(), new EntityDropParticleFX.WaterFactory()); this.registerParticle(ParticleType.DRIP_LAVA.getParticleID(), new EntityDropParticleFX.LavaFactory()); // this.registerParticle(EnumParticleTypes.VILLAGER_ANGRY.getParticleID(), new EntityHeartFX.AngryVillagerFactory()); this.registerParticle(ParticleType.GROW.getParticleID(), new EntityAuraFX.GrowFactory()); this.registerParticle(ParticleType.TOWN_AURA.getParticleID(), new EntityAuraFX.Factory()); this.registerParticle(ParticleType.NOTE.getParticleID(), new EntityNoteFX.Factory()); this.registerParticle(ParticleType.PORTAL.getParticleID(), new EntityPortalFX.Factory()); this.registerParticle(ParticleType.ENCHANTMENT_TABLE.getParticleID(), new EntityEnchantmentTableParticleFX.EnchantmentTable()); this.registerParticle(ParticleType.FLAME.getParticleID(), new EntityFlameFX.Factory()); this.registerParticle(ParticleType.LAVA.getParticleID(), new EntityLavaFX.Factory()); this.registerParticle(ParticleType.FOOTSTEP.getParticleID(), new EntityFootStepFX.Factory()); this.registerParticle(ParticleType.CLOUD.getParticleID(), new EntityCloudFX.Factory()); this.registerParticle(ParticleType.REDSTONE.getParticleID(), new EntityReddustFX.Factory()); this.registerParticle(ParticleType.SNOWBALL.getParticleID(), new EntityBreakingFX.SnowballFactory()); this.registerParticle(ParticleType.SNOW_SHOVEL.getParticleID(), new EntitySnowShovelFX.Factory()); this.registerParticle(ParticleType.SLIME.getParticleID(), new EntityBreakingFX.SlimeFactory()); this.registerParticle(ParticleType.HEART.getParticleID(), new EntityHeartFX.Factory()); // this.registerParticle(EnumParticleTypes.BARRIER.getParticleID(), new Barrier.Factory()); this.registerParticle(ParticleType.ITEM_CRACK.getParticleID(), new EntityBreakingFX.Factory()); this.registerParticle(ParticleType.BLOCK_CRACK.getParticleID(), new EntityDiggingFX.Factory()); this.registerParticle(ParticleType.BLOCK_DUST.getParticleID(), new EntityBlockDustFX.Factory()); this.registerParticle(ParticleType.EXPLOSION_HUGE.getParticleID(), new EntityHugeExplodeFX.Factory()); this.registerParticle(ParticleType.EXPLOSION_LARGE.getParticleID(), new EntityLargeExplodeFX.Factory()); this.registerParticle(ParticleType.FIREWORKS_SPARK.getParticleID(), new EntityFirework.Factory()); this.registerParticle(ParticleType.HAIL_CORN.getParticleID(), new EntityDownfallFX.HailFactory()); } public void registerParticle(int id, IParticleFactory particleFactory) { this.particleTypes.put(Integer.valueOf(id), particleFactory); } public void emitParticleAtEntity(Entity entityIn, ParticleType particleTypes) { this.particleEmitters.add(new EntityParticleEmitter(this.worldObj, entityIn, particleTypes)); } /** * Spawns the relevant particle according to the particle id. * * @param xCoord X position of the particle * @param yCoord Y position of the particle * @param zCoord Z position of the particle * @param xSpeed X speed of the particle * @param ySpeed Y speed of the particle * @param zSpeed Z speed of the particle * @param parameters Parameters for the particle (color for redstone, ...) */ public EntityFX spawnEffectParticle(int particleId, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int[] parameters) { IParticleFactory iparticlefactory = (IParticleFactory)this.particleTypes.get(Integer.valueOf(particleId)); if (iparticlefactory != null) { EntityFX entityfx = iparticlefactory.getEntityFX(particleId, this.worldObj, xCoord, yCoord, zCoord, xSpeed, ySpeed, zSpeed, parameters); if (entityfx != null) { this.addEffect(entityfx); return entityfx; } } return null; } public void addEffect(EntityFX effect) { int i = effect.getFXLayer(); int j = effect.getAlpha() != 1.0F ? 0 : 1; if (this.fxLayers[i][j].size() >= 4000) { this.fxLayers[i][j].remove(0); } this.fxLayers[i][j].add(effect); } public void updateEffects() { for (int i = 0; i < 4; ++i) { this.updateEffectLayer(i); } List list = Lists.newArrayList(); for (EntityParticleEmitter entityparticleemitter : this.particleEmitters) { entityparticleemitter.onUpdate(); if (entityparticleemitter.dead) { list.add(entityparticleemitter); } } this.particleEmitters.removeAll(list); } private void updateEffectLayer(int layer) { for (int i = 0; i < 2; ++i) { this.updateEffectAlphaLayer(this.fxLayers[layer][i]); } } private void updateEffectAlphaLayer(List entitiesFX) { List list = Lists.newArrayList(); for (int i = 0; i < entitiesFX.size(); ++i) { EntityFX entityfx = (EntityFX)entitiesFX.get(i); this.tickParticle(entityfx); if (entityfx.dead) { list.add(entityfx); } } entitiesFX.removeAll(list); } private void tickParticle(final EntityFX particle) { particle.onUpdate(); } /** * Renders all current particles. Args player, partialTickTime */ public void renderParticles(Entity entityIn, float partialTicks) { float f = ActiveRenderInfo.getRotationX(); float f1 = ActiveRenderInfo.getRotationZ(); float f2 = ActiveRenderInfo.getRotationYZ(); float f3 = ActiveRenderInfo.getRotationXY(); float f4 = ActiveRenderInfo.getRotationXZ(); EntityFX.interpPosX = entityIn.lastTickPosX + (entityIn.posX - entityIn.lastTickPosX) * (double)partialTicks; EntityFX.interpPosY = entityIn.lastTickPosY + (entityIn.posY - entityIn.lastTickPosY) * (double)partialTicks; EntityFX.interpPosZ = entityIn.lastTickPosZ + (entityIn.posZ - entityIn.lastTickPosZ) * (double)partialTicks; GlState.enableBlend(); GlState.blendFunc(770, 771); GlState.alphaFunc(516, 0.003921569F); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 2; ++j) { final int i_f = i; if (!this.fxLayers[i][j].isEmpty()) { switch (j) { case 0: GlState.depthMask(false); break; case 1: GlState.depthMask(true); } switch (i) { case 0: default: this.renderer.bindTexture(particleTextures); break; case 1: this.renderer.bindTexture(TextureMap.locationBlocksTexture); } GlState.color(1.0F, 1.0F, 1.0F, 1.0F); // Tessellator tessellator = Tessellator.getInstance(); RenderBuffer worldrenderer = Tessellator.getBuffer(); worldrenderer.begin(7, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP); for (int k = 0; k < this.fxLayers[i][j].size(); ++k) { final EntityFX entityfx = (EntityFX)this.fxLayers[i][j].get(k); entityfx.renderParticle(worldrenderer, entityIn, partialTicks, f, f4, f1, f2, f3); } Tessellator.draw(); } } } GlState.depthMask(true); GlState.disableBlend(); GlState.alphaFunc(516, 0.1F); } public void renderLitParticles(Entity entityIn, float partialTick) { float f = 0.017453292F; float f1 = ExtMath.cos(entityIn.rotYaw * 0.017453292F); float f2 = ExtMath.sin(entityIn.rotYaw * 0.017453292F); float f3 = -f2 * ExtMath.sin(entityIn.rotPitch * 0.017453292F); float f4 = f1 * ExtMath.sin(entityIn.rotPitch * 0.017453292F); float f5 = ExtMath.cos(entityIn.rotPitch * 0.017453292F); for (int i = 0; i < 2; ++i) { List list = this.fxLayers[3][i]; if (!list.isEmpty()) { // Tessellator tessellator = Tessellator.getInstance(); RenderBuffer worldrenderer = Tessellator.getBuffer(); for (int j = 0; j < list.size(); ++j) { EntityFX entityfx = (EntityFX)list.get(j); entityfx.renderParticle(worldrenderer, entityIn, partialTick, f1, f5, f2, f3, f4); } } } } public void clearEffects(World worldIn) { this.worldObj = worldIn; for (int i = 0; i < 4; ++i) { for (int j = 0; j < 2; ++j) { this.fxLayers[i][j].clear(); } } this.particleEmitters.clear(); } public void addBlockDestroyEffects(BlockPos pos, State state) { if (state.getBlock().getMaterial() != Material.air) { state = state.getBlock().getActualState(state, this.worldObj, pos); int i = 4; for (int j = 0; j < i; ++j) { for (int k = 0; k < i; ++k) { for (int l = 0; l < i; ++l) { double d0 = (double)pos.getX() + ((double)j + 0.5D) / (double)i; double d1 = (double)pos.getY() + ((double)k + 0.5D) / (double)i; double d2 = (double)pos.getZ() + ((double)l + 0.5D) / (double)i; this.addEffect((new EntityDiggingFX(this.worldObj, d0, d1, d2, d0 - (double)pos.getX() - 0.5D, d1 - (double)pos.getY() - 0.5D, d2 - (double)pos.getZ() - 0.5D, state)).setBlockPos(pos)); } } } } } /** * Adds block hit particles for the specified block */ public void addBlockHitEffects(BlockPos pos, Facing side) { State iblockstate = this.worldObj.getState(pos); Block block = iblockstate.getBlock(); if (block.getRenderType() != -1) { int i = pos.getX(); int j = pos.getY(); int k = pos.getZ(); float f = 0.1F; double d0 = (double)i + this.rand.doublev() * (block.getBlockBoundsMaxX() - block.getBlockBoundsMinX() - (double)(f * 2.0F)) + (double)f + block.getBlockBoundsMinX(); double d1 = (double)j + this.rand.doublev() * (block.getBlockBoundsMaxY() - block.getBlockBoundsMinY() - (double)(f * 2.0F)) + (double)f + block.getBlockBoundsMinY(); double d2 = (double)k + this.rand.doublev() * (block.getBlockBoundsMaxZ() - block.getBlockBoundsMinZ() - (double)(f * 2.0F)) + (double)f + block.getBlockBoundsMinZ(); if (side == Facing.DOWN) { d1 = (double)j + block.getBlockBoundsMinY() - (double)f; } if (side == Facing.UP) { d1 = (double)j + block.getBlockBoundsMaxY() + (double)f; } if (side == Facing.NORTH) { d2 = (double)k + block.getBlockBoundsMinZ() - (double)f; } if (side == Facing.SOUTH) { d2 = (double)k + block.getBlockBoundsMaxZ() + (double)f; } if (side == Facing.WEST) { d0 = (double)i + block.getBlockBoundsMinX() - (double)f; } if (side == Facing.EAST) { d0 = (double)i + block.getBlockBoundsMaxX() + (double)f; } this.addEffect((new EntityDiggingFX(this.worldObj, d0, d1, d2, 0.0D, 0.0D, 0.0D, iblockstate)).setBlockPos(pos).multiplyVelocity(0.2F).multipleParticleScaleBy(0.6F)); } } public void moveToAlphaLayer(EntityFX effect) { this.moveToLayer(effect, 1, 0); } public void moveToNoAlphaLayer(EntityFX effect) { this.moveToLayer(effect, 0, 1); } private void moveToLayer(EntityFX effect, int layerFrom, int layerTo) { for (int i = 0; i < 4; ++i) { if (this.fxLayers[i][layerFrom].contains(effect)) { this.fxLayers[i][layerFrom].remove(effect); this.fxLayers[i][layerTo].add(effect); } } } public String getStatistics() { int i = 0; for (int j = 0; j < 4; ++j) { for (int k = 0; k < 2; ++k) { i += this.fxLayers[j][k].size(); } } return "" + i; } }