Java tutorial
/******************************************************************************* * HellFirePvP / Astral Sorcery 2017 * * This project is licensed under GNU GENERAL PUBLIC LICENSE Version 3. * The source code is available on github: https://github.com/HellFirePvP/AstralSorcery * For further details, see the License file there. ******************************************************************************/ package hellfirepvp.astralsorcery.client.effect; import hellfirepvp.astralsorcery.AstralSorcery; import hellfirepvp.astralsorcery.client.effect.block.EffectTranslucentFallingBlock; import hellfirepvp.astralsorcery.client.effect.compound.CompoundObjectEffect; import hellfirepvp.astralsorcery.client.effect.controller.OrbitalEffectController; import hellfirepvp.astralsorcery.client.effect.fx.EntityFXFacingParticle; import hellfirepvp.astralsorcery.client.effect.light.EffectLightbeam; import hellfirepvp.astralsorcery.client.effect.light.EffectLightning; import hellfirepvp.astralsorcery.client.effect.texture.TexturePlane; import hellfirepvp.astralsorcery.client.effect.texture.TextureSpritePlane; import hellfirepvp.astralsorcery.client.event.ClientGatewayHandler; import hellfirepvp.astralsorcery.client.render.tile.TESRMapDrawingTable; import hellfirepvp.astralsorcery.client.render.tile.TESRPrismLens; import hellfirepvp.astralsorcery.client.render.tile.TESRTranslucentBlock; import hellfirepvp.astralsorcery.client.util.Blending; import hellfirepvp.astralsorcery.client.util.ClientScreenshotCache; import hellfirepvp.astralsorcery.client.util.RenderingUtils; import hellfirepvp.astralsorcery.client.util.UIGateway; import hellfirepvp.astralsorcery.client.util.resource.AssetLibrary; import hellfirepvp.astralsorcery.client.util.resource.BindableResource; import hellfirepvp.astralsorcery.client.util.resource.SpriteSheetResource; import hellfirepvp.astralsorcery.common.data.config.Config; import hellfirepvp.astralsorcery.common.util.Counter; import hellfirepvp.astralsorcery.common.util.data.Vector3; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderWorldLastEvent; import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import org.lwjgl.opengl.GL11; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; /** * This class is part of the Astral Sorcery Mod * The complete source code for this mod can be found on github. * Class: EffectHandler * Created by HellFirePvP * Date: 12.05.2016 / 17:44 */ public final class EffectHandler { public static final Random STATIC_EFFECT_RAND = new Random(); public static final EffectHandler instance = new EffectHandler(); private static boolean acceptsNewParticles = true, cleanRequested = false; private static List<IComplexEffect> toAddBuffer = new LinkedList<>(); private UIGateway uiGateway = null; private int gatewayUITicks = 0; public boolean renderGateway = true; public static final Map<IComplexEffect.RenderTarget, Map<Integer, List<IComplexEffect>>> complexEffects = new HashMap<>(); public static final List<EntityFXFacingParticle> fastRenderParticles = new LinkedList<>(); public static final List<EffectLightning> fastRenderLightnings = new LinkedList<>(); public static final Map<CompoundObjectEffect.ObjectGroup, List<CompoundObjectEffect>> objects = new HashMap<>(); private EffectHandler() { } public static EffectHandler getInstance() { return instance; } public void requestGatewayUIFor(World world, Vector3 pos, double sphereRadius) { if (uiGateway == null || !uiGateway.getPos().equals(pos)) { uiGateway = UIGateway.initialize(world, pos, sphereRadius); } gatewayUITicks = 20; } @Nullable public UIGateway getUiGateway() { return uiGateway; } public static int getDebugEffectCount() { final Counter c = new Counter(0); for (Map<Integer, List<IComplexEffect>> effects : complexEffects.values()) { for (List<IComplexEffect> eff : effects.values()) { c.value += eff.size(); } } c.value += fastRenderParticles.size(); c.value += fastRenderLightnings.size(); objects.values().stream().forEach((l) -> c.value += l.size()); return c.value; } @SubscribeEvent public void onOverlay(RenderGameOverlayEvent.Post event) { if (event.getType() == RenderGameOverlayEvent.ElementType.ALL) { acceptsNewParticles = false; Map<Integer, List<IComplexEffect>> layeredEffects = complexEffects .get(IComplexEffect.RenderTarget.OVERLAY_TEXT); for (int i = 0; i <= 2; i++) { for (IComplexEffect effect : layeredEffects.get(i)) { GL11.glPushMatrix(); GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); effect.render(event.getPartialTicks()); GL11.glPopAttrib(); GL11.glPopMatrix(); } } acceptsNewParticles = true; } } @SubscribeEvent public void onDebugText(RenderGameOverlayEvent.Text event) { if (Minecraft.getMinecraft().gameSettings.showDebugInfo) { event.getLeft().add(""); event.getLeft().add("9[AstralSorcery]r EffectHandler:"); event.getLeft().add("9[AstralSorcery]r > Complex effects: " + getDebugEffectCount()); } } @SubscribeEvent(priority = EventPriority.LOW) public void onRender(RenderWorldLastEvent event) { TESRPrismLens.renderColoredPrismsLast(); float pTicks = event.getPartialTicks(); acceptsNewParticles = false; for (CompoundObjectEffect.ObjectGroup og : objects.keySet()) { og.prepareGLContext(); for (CompoundObjectEffect effect : objects.get(og)) { effect.render(pTicks); } og.revertGLContext(); } if (uiGateway != null) { if (renderGateway) { uiGateway.renderIntoWorld(pTicks); } if (ClientGatewayHandler.focusingEntry != null) { renderGatewayTarget(pTicks); } } EntityFXFacingParticle.renderFast(pTicks, fastRenderParticles); EffectLightning.renderFast(pTicks, fastRenderLightnings); Map<Integer, List<IComplexEffect>> layeredEffects = complexEffects .get(IComplexEffect.RenderTarget.RENDERLOOP); for (int i = 0; i <= 2; i++) { for (IComplexEffect effect : layeredEffects.get(i)) { GL11.glPushMatrix(); GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); effect.render(pTicks); GL11.glPopAttrib(); GL11.glPopMatrix(); } } acceptsNewParticles = true; TESRMapDrawingTable.renderRemainingGlasses(pTicks); TESRTranslucentBlock.renderTranslucentBlocks(); } private void renderGatewayTarget(float pTicks) { int focusTicks = ClientGatewayHandler.focusTicks; UIGateway.GatewayEntry focusingEntry = ClientGatewayHandler.focusingEntry; float perc = (Math.min(40F, focusTicks) / 40F) * 0.3F; if (focusTicks > 70) { perc = ((float) (focusTicks - 70)) / 25F; perc = MathHelper.clamp(perc, 0.3F, 1F); } ResourceLocation screenshot = ClientScreenshotCache.tryQueryTextureFor(focusingEntry.originalDimId, focusingEntry.originalBlockPos); if (screenshot != null) { GlStateManager.pushMatrix(); Minecraft.getMinecraft().getTextureManager().bindTexture(screenshot); GlStateManager.enableBlend(); Blending.DEFAULT.applyStateManager(); GlStateManager.disableAlpha(); Tessellator tes = Tessellator.getInstance(); VertexBuffer vb = tes.getBuffer(); vb.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); Vector3 pos = focusingEntry.relativePos.clone().multiply(0.85).add(uiGateway.getPos()); RenderingUtils.renderFacingFullQuadVB(vb, pos.getX(), pos.getY(), pos.getZ(), pTicks, 0.4F, 0, 1F, 1F, 1F, perc); tes.draw(); GlStateManager.enableAlpha(); GlStateManager.popMatrix(); } } @SubscribeEvent public void onClTick(TickEvent.ClientTickEvent event) { if (event.phase != TickEvent.Phase.END) return; tick(); /*if(Minecraft.getMinecraft().player == null) return; if(ClientScheduler.getClientTick() % 10 != 0) return; ItemStack main = Minecraft.getMinecraft().player.getHeldItem(EnumHand.MAIN_HAND); if(main != null && main.getItem() instanceof ItemIlluminationWand) { RayTraceResult res = MiscUtils.rayTraceLook(Minecraft.getMinecraft().player, 60); if(res != null && res.typeOfHit == RayTraceResult.Type.BLOCK) { EffectLightning.buildAndRegisterLightning(new Vector3(res.getBlockPos()).addY(7), new Vector3(res.getBlockPos())); } }*/ } public EntityComplexFX registerFX(EntityComplexFX entityComplexFX) { register(entityComplexFX); return entityComplexFX; } public EffectTranslucentFallingBlock translucentFallingBlock(Vector3 position, IBlockState state) { EffectTranslucentFallingBlock block = new EffectTranslucentFallingBlock(state); block.setPosition(position.clone().add(-0.5, -0.5, -0.5)); register(block); return block; } public EffectLightning lightning(Vector3 from, Vector3 to) { return EffectLightning.buildAndRegisterLightning(from, to); } public OrbitalEffectController orbital(OrbitalEffectController.OrbitPointEffect pointEffect, @Nullable OrbitalEffectController.OrbitPersistence persistence, @Nullable OrbitalEffectController.OrbitTickModifier tickModifier) { OrbitalEffectController ctrl = new OrbitalEffectController(pointEffect, persistence, tickModifier); register(ctrl); return ctrl; } public TextureSpritePlane textureSpritePlane(SpriteSheetResource sheetResource, Vector3 rotationAxis) { TextureSpritePlane plane = new TextureSpritePlane(sheetResource, rotationAxis); register(plane); return plane; } public TexturePlane texturePlane(BindableResource texture, Vector3 rotationAxis) { TexturePlane plane = new TexturePlane(texture, rotationAxis); register(plane); return plane; } public EffectLightbeam lightbeam(Vector3 from, Vector3 to, double beamRadSize) { EffectLightbeam beam = new EffectLightbeam(from, to, beamRadSize); register(beam); return beam; } public EffectLightbeam lightbeam(Vector3 from, Vector3 to, double fromBeamSize, double toBeamSize) { EffectLightbeam beam = new EffectLightbeam(from, to, fromBeamSize, toBeamSize); register(beam); return beam; } private void register(final IComplexEffect effect) { if (AssetLibrary.reloading || effect == null || Minecraft.getMinecraft().isGamePaused()) return; //instead of getEffeciveSide - neither is pretty, but this at least prevents async editing. if (!Thread.currentThread().getName().contains("Client thread")) { AstralSorcery.proxy.scheduleClientside(() -> register(effect)); return; } if (acceptsNewParticles) { registerUnsafe(effect); } else { toAddBuffer.add(effect); } } private void registerUnsafe(IComplexEffect effect) { if (!mayAcceptParticle(effect)) return; if (effect instanceof EffectLightning) { fastRenderLightnings.add((EffectLightning) effect); } else if (effect instanceof EntityFXFacingParticle) { fastRenderParticles.add((EntityFXFacingParticle) effect); } else if (effect instanceof CompoundObjectEffect) { CompoundObjectEffect.ObjectGroup group = ((CompoundObjectEffect) effect).getGroup(); if (!objects.containsKey(group)) objects.put(group, new LinkedList<>()); objects.get(group).add((CompoundObjectEffect) effect); } else { complexEffects.get(effect.getRenderTarget()).get(effect.getLayer()).add(effect); } effect.clearRemoveFlag(); } public void tick() { if (cleanRequested) { for (IComplexEffect.RenderTarget t : IComplexEffect.RenderTarget.values()) { for (int i = 0; i <= 2; i++) { complexEffects.get(t).get(i).clear(); } } fastRenderParticles.clear(); fastRenderLightnings.clear(); objects.clear(); toAddBuffer.clear(); uiGateway = null; gatewayUITicks = 0; cleanRequested = false; } if (Minecraft.getMinecraft().player == null) { return; } if (gatewayUITicks > 0) { gatewayUITicks--; if (gatewayUITicks <= 0) { uiGateway = null; } } acceptsNewParticles = false; for (IComplexEffect.RenderTarget target : complexEffects.keySet()) { Map<Integer, List<IComplexEffect>> layeredEffects = complexEffects.get(target); for (int i = 0; i <= 2; i++) { Iterator<IComplexEffect> iterator = layeredEffects.get(i).iterator(); while (iterator.hasNext()) { IComplexEffect effect = iterator.next(); effect.tick(); if (effect.canRemove()) { effect.flagAsRemoved(); iterator.remove(); } } } } Vector3 playerPos = new Vector3(Minecraft.getMinecraft().player); for (EntityFXFacingParticle effect : new ArrayList<>(fastRenderParticles)) { if (effect == null) { fastRenderParticles.remove(null); continue; } effect.tick(); if (effect.canRemove() || effect.getPosition().distanceSquared(playerPos) >= Config.maxEffectRenderDistanceSq) { effect.flagAsRemoved(); fastRenderParticles.remove(effect); } } for (EffectLightning effect : new ArrayList<>(fastRenderLightnings)) { if (effect == null) { fastRenderLightnings.remove(null); continue; } effect.tick(); if (effect.canRemove()) { effect.flagAsRemoved(); fastRenderLightnings.remove(effect); } } Iterator<CompoundObjectEffect.ObjectGroup> itGroups = objects.keySet().iterator(); while (itGroups.hasNext()) { CompoundObjectEffect.ObjectGroup group = itGroups.next(); List<CompoundObjectEffect> effects = objects.get(group); if (effects == null || effects.isEmpty()) { itGroups.remove(); continue; } Iterator<CompoundObjectEffect> itObjects = effects.iterator(); while (itObjects.hasNext()) { CompoundObjectEffect effect = itObjects.next(); if (effect == null) { itObjects.remove(); continue; } effect.tick(); if (effect.canRemove()) { effect.flagAsRemoved(); itObjects.remove(); } } } acceptsNewParticles = true; List<IComplexEffect> effects = new LinkedList<>(toAddBuffer); toAddBuffer.clear(); for (IComplexEffect eff : effects) { registerUnsafe(eff); } } public static boolean mayAcceptParticle(IComplexEffect effect) { int cfg = Config.particleAmount; if (cfg > 1 && !Minecraft.isFancyGraphicsEnabled()) { cfg = 1; } if (effect instanceof IComplexEffect.PreventRemoval || cfg == 2) return true; return cfg == 1 && STATIC_EFFECT_RAND.nextInt(3) == 0; } static { for (IComplexEffect.RenderTarget target : IComplexEffect.RenderTarget.values()) { Map<Integer, List<IComplexEffect>> layeredEffects = new HashMap<>(); for (int i = 0; i <= 2; i++) { layeredEffects.put(i, new LinkedList<>()); } complexEffects.put(target, layeredEffects); } } public static void cleanUp() { cleanRequested = true; } }